Repository: zhanghe06/flask_project Branch: master Commit: 41796f4cd2a4 Files: 2856 Total size: 32.0 MB Directory structure: gitextract_dou5xjo6/ ├── .gitignore ├── README.md ├── app_api/ │ ├── README.md │ └── __init__.py ├── app_backend/ │ ├── README.md │ ├── __init__.py │ ├── api/ │ │ ├── __init__.py │ │ ├── active.py │ │ ├── active_item.py │ │ ├── admin.py │ │ ├── admin_role.py │ │ ├── apply_get.py │ │ ├── apply_put.py │ │ ├── author.py │ │ ├── blog.py │ │ ├── complaint.py │ │ ├── message.py │ │ ├── order.py │ │ ├── scheduling.py │ │ ├── scheduling_item.py │ │ ├── score.py │ │ ├── user.py │ │ ├── user_auth.py │ │ ├── user_bank.py │ │ ├── user_config.py │ │ ├── user_profile.py │ │ └── wallet.py │ ├── database.py │ ├── filters.py │ ├── forms/ │ │ ├── __init__.py │ │ ├── active.py │ │ ├── admin.py │ │ ├── apply_get.py │ │ ├── apply_put.py │ │ ├── blog.py │ │ ├── complaint.py │ │ ├── login.py │ │ ├── order.py │ │ ├── pay.py │ │ ├── scheduling.py │ │ ├── score.py │ │ ├── settings.py │ │ ├── user.py │ │ └── wallet.py │ ├── lib/ │ │ ├── __init__.py │ │ ├── captcha.py │ │ ├── cart.py │ │ ├── container.py │ │ ├── counter.py │ │ ├── mongo.py │ │ ├── qiniu_store.py │ │ ├── rabbit_mq.py │ │ ├── redis_session.py │ │ ├── sendcloud.py │ │ ├── session.py │ │ ├── sms_chuanglan.py │ │ ├── sms_chuanglan_iso.py │ │ └── token.py │ ├── login.py │ ├── models.py │ ├── pages/ │ │ ├── blank.html │ │ ├── buttons.html │ │ ├── flot.html │ │ ├── forms.html │ │ ├── grid.html │ │ ├── icons.html │ │ ├── index.html │ │ ├── login.html │ │ ├── morris.html │ │ ├── notifications.html │ │ ├── panels-wells.html │ │ ├── tables.html │ │ └── typography.html │ ├── permissions.py │ ├── static/ │ │ ├── css/ │ │ │ ├── bootstrap-select.css │ │ │ ├── bootstrap-theme.css │ │ │ ├── bootstrap.css │ │ │ ├── custom.css │ │ │ ├── lightbox.css │ │ │ ├── sb-admin-2.css │ │ │ └── slideout.css │ │ ├── csv/ │ │ │ └── flare.csv │ │ ├── js/ │ │ │ ├── bootstrap-select.js │ │ │ ├── bootstrap.js │ │ │ ├── custom.js │ │ │ ├── i18n/ │ │ │ │ ├── defaults-ar_AR.js │ │ │ │ ├── defaults-bg_BG.js │ │ │ │ ├── defaults-cro_CRO.js │ │ │ │ ├── defaults-cs_CZ.js │ │ │ │ ├── defaults-da_DK.js │ │ │ │ ├── defaults-de_DE.js │ │ │ │ ├── defaults-en_US.js │ │ │ │ ├── defaults-es_CL.js │ │ │ │ ├── defaults-es_ES.js │ │ │ │ ├── defaults-eu.js │ │ │ │ ├── defaults-fa_IR.js │ │ │ │ ├── defaults-fi_FI.js │ │ │ │ ├── defaults-fr_FR.js │ │ │ │ ├── defaults-hu_HU.js │ │ │ │ ├── defaults-id_ID.js │ │ │ │ ├── defaults-it_IT.js │ │ │ │ ├── defaults-ko_KR.js │ │ │ │ ├── defaults-lt_LT.js │ │ │ │ ├── defaults-nb_NO.js │ │ │ │ ├── defaults-nl_NL.js │ │ │ │ ├── defaults-pl_PL.js │ │ │ │ ├── defaults-pt_BR.js │ │ │ │ ├── defaults-pt_PT.js │ │ │ │ ├── defaults-ro_RO.js │ │ │ │ ├── defaults-ru_RU.js │ │ │ │ ├── defaults-sk_SK.js │ │ │ │ ├── defaults-sl_SI.js │ │ │ │ ├── defaults-sv_SE.js │ │ │ │ ├── defaults-tr_TR.js │ │ │ │ ├── defaults-ua_UA.js │ │ │ │ ├── defaults-zh_CN.js │ │ │ │ └── defaults-zh_TW.js │ │ │ ├── npm.js │ │ │ └── sb-admin-2.js │ │ ├── plugin/ │ │ │ ├── Chart.js-2.6.0/ │ │ │ │ ├── .codeclimate.yml │ │ │ │ ├── .editorconfig │ │ │ │ ├── .eslintignore │ │ │ │ ├── .eslintrc │ │ │ │ ├── .github/ │ │ │ │ │ ├── ISSUE_TEMPLATE.md │ │ │ │ │ └── PULL_REQUEST_TEMPLATE.md │ │ │ │ ├── .gitignore │ │ │ │ ├── .npmignore │ │ │ │ ├── .travis.yml │ │ │ │ ├── LICENSE.md │ │ │ │ ├── MAINTAINING.md │ │ │ │ ├── README.md │ │ │ │ ├── book.json │ │ │ │ ├── composer.json │ │ │ │ ├── dist/ │ │ │ │ │ ├── Chart.bundle.js │ │ │ │ │ └── Chart.js │ │ │ │ ├── docs/ │ │ │ │ │ ├── README.md │ │ │ │ │ ├── SUMMARY.md │ │ │ │ │ ├── axes/ │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ ├── cartesian/ │ │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ │ ├── category.md │ │ │ │ │ │ │ ├── linear.md │ │ │ │ │ │ │ ├── logarithmic.md │ │ │ │ │ │ │ └── time.md │ │ │ │ │ │ ├── labelling.md │ │ │ │ │ │ ├── radial/ │ │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ │ └── linear.md │ │ │ │ │ │ └── styling.md │ │ │ │ │ ├── charts/ │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ ├── area.md │ │ │ │ │ │ ├── bar.md │ │ │ │ │ │ ├── bubble.md │ │ │ │ │ │ ├── doughnut.md │ │ │ │ │ │ ├── line.md │ │ │ │ │ │ ├── mixed.md │ │ │ │ │ │ ├── polar.md │ │ │ │ │ │ ├── radar.md │ │ │ │ │ │ └── scatter.md │ │ │ │ │ ├── configuration/ │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ ├── animations.md │ │ │ │ │ │ ├── elements.md │ │ │ │ │ │ ├── layout.md │ │ │ │ │ │ ├── legend.md │ │ │ │ │ │ ├── title.md │ │ │ │ │ │ └── tooltip.md │ │ │ │ │ ├── developers/ │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ ├── api.md │ │ │ │ │ │ ├── axes.md │ │ │ │ │ │ ├── charts.md │ │ │ │ │ │ ├── contributing.md │ │ │ │ │ │ ├── plugins.md │ │ │ │ │ │ └── updates.md │ │ │ │ │ ├── general/ │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ ├── colors.md │ │ │ │ │ │ ├── fonts.md │ │ │ │ │ │ ├── interactions/ │ │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ │ ├── events.md │ │ │ │ │ │ │ └── modes.md │ │ │ │ │ │ └── responsive.md │ │ │ │ │ ├── getting-started/ │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ ├── installation.md │ │ │ │ │ │ ├── integration.md │ │ │ │ │ │ └── usage.md │ │ │ │ │ ├── notes/ │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ ├── comparison.md │ │ │ │ │ │ ├── extensions.md │ │ │ │ │ │ └── license.md │ │ │ │ │ └── style.css │ │ │ │ ├── gulpfile.js │ │ │ │ ├── karma.conf.js │ │ │ │ ├── package.json │ │ │ │ ├── samples/ │ │ │ │ │ ├── advanced/ │ │ │ │ │ │ ├── data-labelling.html │ │ │ │ │ │ └── progress-bar.html │ │ │ │ │ ├── charts/ │ │ │ │ │ │ ├── area/ │ │ │ │ │ │ │ ├── analyser.js │ │ │ │ │ │ │ ├── line-boundaries.html │ │ │ │ │ │ │ ├── line-datasets.html │ │ │ │ │ │ │ ├── line-stacked.html │ │ │ │ │ │ │ └── radar.html │ │ │ │ │ │ ├── bar/ │ │ │ │ │ │ │ ├── horizontal.html │ │ │ │ │ │ │ ├── multi-axis.html │ │ │ │ │ │ │ ├── stacked-group.html │ │ │ │ │ │ │ ├── stacked.html │ │ │ │ │ │ │ └── vertical.html │ │ │ │ │ │ ├── bubble.html │ │ │ │ │ │ ├── combo-bar-line.html │ │ │ │ │ │ ├── doughnut.html │ │ │ │ │ │ ├── line/ │ │ │ │ │ │ │ ├── basic.html │ │ │ │ │ │ │ ├── interpolation-modes.html │ │ │ │ │ │ │ ├── line-styles.html │ │ │ │ │ │ │ ├── multi-axis.html │ │ │ │ │ │ │ ├── point-sizes.html │ │ │ │ │ │ │ ├── point-styles.html │ │ │ │ │ │ │ ├── skip-points.html │ │ │ │ │ │ │ └── stepped.html │ │ │ │ │ │ ├── pie.html │ │ │ │ │ │ ├── polar-area.html │ │ │ │ │ │ ├── radar-skip-points.html │ │ │ │ │ │ ├── radar.html │ │ │ │ │ │ └── scatter/ │ │ │ │ │ │ ├── basic.html │ │ │ │ │ │ └── multi-axis.html │ │ │ │ │ ├── index.html │ │ │ │ │ ├── legend/ │ │ │ │ │ │ ├── point-style.html │ │ │ │ │ │ └── positioning.html │ │ │ │ │ ├── samples.js │ │ │ │ │ ├── scales/ │ │ │ │ │ │ ├── filtering-labels.html │ │ │ │ │ │ ├── gridlines-display.html │ │ │ │ │ │ ├── gridlines-style.html │ │ │ │ │ │ ├── linear/ │ │ │ │ │ │ │ ├── min-max-suggested.html │ │ │ │ │ │ │ ├── min-max.html │ │ │ │ │ │ │ └── step-size.html │ │ │ │ │ │ ├── logarithmic/ │ │ │ │ │ │ │ ├── line.html │ │ │ │ │ │ │ └── scatter.html │ │ │ │ │ │ ├── multiline-labels.html │ │ │ │ │ │ ├── non-numeric-y.html │ │ │ │ │ │ └── time/ │ │ │ │ │ │ ├── combo.html │ │ │ │ │ │ ├── line-point-data.html │ │ │ │ │ │ └── line.html │ │ │ │ │ ├── style.css │ │ │ │ │ ├── tooltips/ │ │ │ │ │ │ ├── border.html │ │ │ │ │ │ ├── callbacks.html │ │ │ │ │ │ ├── custom-line.html │ │ │ │ │ │ ├── custom-pie.html │ │ │ │ │ │ ├── custom-points.html │ │ │ │ │ │ ├── interactions.html │ │ │ │ │ │ └── positioning.html │ │ │ │ │ └── utils.js │ │ │ │ ├── scripts/ │ │ │ │ │ ├── deploy.sh │ │ │ │ │ └── release.sh │ │ │ │ ├── src/ │ │ │ │ │ ├── chart.js │ │ │ │ │ ├── charts/ │ │ │ │ │ │ ├── Chart.Bar.js │ │ │ │ │ │ ├── Chart.Bubble.js │ │ │ │ │ │ ├── Chart.Doughnut.js │ │ │ │ │ │ ├── Chart.Line.js │ │ │ │ │ │ ├── Chart.PolarArea.js │ │ │ │ │ │ ├── Chart.Radar.js │ │ │ │ │ │ └── Chart.Scatter.js │ │ │ │ │ ├── controllers/ │ │ │ │ │ │ ├── controller.bar.js │ │ │ │ │ │ ├── controller.bubble.js │ │ │ │ │ │ ├── controller.doughnut.js │ │ │ │ │ │ ├── controller.line.js │ │ │ │ │ │ ├── controller.polarArea.js │ │ │ │ │ │ └── controller.radar.js │ │ │ │ │ ├── core/ │ │ │ │ │ │ ├── core.animation.js │ │ │ │ │ │ ├── core.canvasHelpers.js │ │ │ │ │ │ ├── core.controller.js │ │ │ │ │ │ ├── core.datasetController.js │ │ │ │ │ │ ├── core.element.js │ │ │ │ │ │ ├── core.helpers.js │ │ │ │ │ │ ├── core.interaction.js │ │ │ │ │ │ ├── core.js │ │ │ │ │ │ ├── core.layoutService.js │ │ │ │ │ │ ├── core.plugin.js │ │ │ │ │ │ ├── core.scale.js │ │ │ │ │ │ ├── core.scaleService.js │ │ │ │ │ │ ├── core.ticks.js │ │ │ │ │ │ └── core.tooltip.js │ │ │ │ │ ├── elements/ │ │ │ │ │ │ ├── element.arc.js │ │ │ │ │ │ ├── element.line.js │ │ │ │ │ │ ├── element.point.js │ │ │ │ │ │ └── element.rectangle.js │ │ │ │ │ ├── platforms/ │ │ │ │ │ │ ├── platform.dom.js │ │ │ │ │ │ └── platform.js │ │ │ │ │ ├── plugins/ │ │ │ │ │ │ ├── plugin.filler.js │ │ │ │ │ │ ├── plugin.legend.js │ │ │ │ │ │ └── plugin.title.js │ │ │ │ │ └── scales/ │ │ │ │ │ ├── scale.category.js │ │ │ │ │ ├── scale.linear.js │ │ │ │ │ ├── scale.linearbase.js │ │ │ │ │ ├── scale.logarithmic.js │ │ │ │ │ ├── scale.radialLinear.js │ │ │ │ │ └── scale.time.js │ │ │ │ └── test/ │ │ │ │ ├── fixtures/ │ │ │ │ │ └── plugin.filler/ │ │ │ │ │ ├── fill-line-boundary-end-span.json │ │ │ │ │ ├── fill-line-boundary-end.json │ │ │ │ │ ├── fill-line-boundary-origin-span.json │ │ │ │ │ ├── fill-line-boundary-origin-spline-span.json │ │ │ │ │ ├── fill-line-boundary-origin-spline.json │ │ │ │ │ ├── fill-line-boundary-origin-stepped-span.json │ │ │ │ │ ├── fill-line-boundary-origin-stepped.json │ │ │ │ │ ├── fill-line-boundary-origin.json │ │ │ │ │ ├── fill-line-boundary-start-span.json │ │ │ │ │ ├── fill-line-boundary-start.json │ │ │ │ │ ├── fill-line-dataset-span.json │ │ │ │ │ ├── fill-line-dataset-spline-span.json │ │ │ │ │ ├── fill-line-dataset-spline.json │ │ │ │ │ ├── fill-line-dataset.json │ │ │ │ │ ├── fill-radar-boundary-origin-spline.json │ │ │ │ │ └── fill-radar-boundary-origin.json │ │ │ │ ├── jasmine.context.js │ │ │ │ ├── jasmine.index.js │ │ │ │ ├── jasmine.matchers.js │ │ │ │ ├── jasmine.utils.js │ │ │ │ └── specs/ │ │ │ │ ├── controller.bar.tests.js │ │ │ │ ├── controller.bubble.tests.js │ │ │ │ ├── controller.doughnut.tests.js │ │ │ │ ├── controller.line.tests.js │ │ │ │ ├── controller.polarArea.tests.js │ │ │ │ ├── controller.radar.tests.js │ │ │ │ ├── core.controller.tests.js │ │ │ │ ├── core.datasetController.tests.js │ │ │ │ ├── core.element.tests.js │ │ │ │ ├── core.helpers.tests.js │ │ │ │ ├── core.interaction.tests.js │ │ │ │ ├── core.layoutService.tests.js │ │ │ │ ├── core.plugin.tests.js │ │ │ │ ├── core.scaleService.tests.js │ │ │ │ ├── core.tooltip.tests.js │ │ │ │ ├── element.arc.tests.js │ │ │ │ ├── element.line.tests.js │ │ │ │ ├── element.point.tests.js │ │ │ │ ├── element.rectangle.tests.js │ │ │ │ ├── global.defaults.tests.js │ │ │ │ ├── global.deprecations.tests.js │ │ │ │ ├── platform.dom.tests.js │ │ │ │ ├── plugin.filler.tests.js │ │ │ │ ├── plugin.legend.tests.js │ │ │ │ ├── plugin.title.tests.js │ │ │ │ ├── scale.category.tests.js │ │ │ │ ├── scale.linear.tests.js │ │ │ │ ├── scale.logarithmic.tests.js │ │ │ │ ├── scale.radialLinear.tests.js │ │ │ │ └── scale.time.tests.js │ │ │ ├── Gallery-2.25.0/ │ │ │ │ ├── .gitignore │ │ │ │ ├── .npmignore │ │ │ │ ├── LICENSE.txt │ │ │ │ ├── README.md │ │ │ │ ├── css/ │ │ │ │ │ ├── blueimp-gallery-indicator.css │ │ │ │ │ ├── blueimp-gallery-video.css │ │ │ │ │ ├── blueimp-gallery.css │ │ │ │ │ └── demo/ │ │ │ │ │ └── demo.css │ │ │ │ ├── index.html │ │ │ │ ├── js/ │ │ │ │ │ ├── blueimp-gallery-fullscreen.js │ │ │ │ │ ├── blueimp-gallery-indicator.js │ │ │ │ │ ├── blueimp-gallery-video.js │ │ │ │ │ ├── blueimp-gallery-vimeo.js │ │ │ │ │ ├── blueimp-gallery-youtube.js │ │ │ │ │ ├── blueimp-gallery.js │ │ │ │ │ ├── blueimp-helper.js │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── demo.js │ │ │ │ │ ├── jquery.blueimp-gallery.js │ │ │ │ │ └── vendor/ │ │ │ │ │ └── jquery.js │ │ │ │ └── package.json │ │ │ ├── JavaScript-Canvas-to-Blob-3.7.0/ │ │ │ │ ├── .gitignore │ │ │ │ ├── .npmignore │ │ │ │ ├── .travis.yml │ │ │ │ ├── README.md │ │ │ │ ├── js/ │ │ │ │ │ └── canvas-to-blob.js │ │ │ │ ├── package.json │ │ │ │ └── test/ │ │ │ │ ├── index.html │ │ │ │ ├── test.js │ │ │ │ └── vendor/ │ │ │ │ ├── chai.js │ │ │ │ ├── load-image.js │ │ │ │ ├── mocha.css │ │ │ │ └── mocha.js │ │ │ ├── JavaScript-Load-Image-2.12.2/ │ │ │ │ ├── .gitignore │ │ │ │ ├── .npmignore │ │ │ │ ├── .travis.yml │ │ │ │ ├── README.md │ │ │ │ ├── css/ │ │ │ │ │ ├── demo.css │ │ │ │ │ └── vendor/ │ │ │ │ │ └── jquery.Jcrop.css │ │ │ │ ├── index.html │ │ │ │ ├── js/ │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── demo.js │ │ │ │ │ ├── index.js │ │ │ │ │ ├── load-image-exif-map.js │ │ │ │ │ ├── load-image-exif.js │ │ │ │ │ ├── load-image-fetch.js │ │ │ │ │ ├── load-image-meta.js │ │ │ │ │ ├── load-image-orientation.js │ │ │ │ │ ├── load-image-scale.js │ │ │ │ │ ├── load-image.js │ │ │ │ │ └── vendor/ │ │ │ │ │ ├── jquery.Jcrop.js │ │ │ │ │ └── jquery.js │ │ │ │ ├── package.json │ │ │ │ └── test/ │ │ │ │ ├── index.html │ │ │ │ ├── test.js │ │ │ │ └── vendor/ │ │ │ │ ├── canvas-to-blob.js │ │ │ │ ├── chai.js │ │ │ │ ├── mocha.css │ │ │ │ └── mocha.js │ │ │ ├── JavaScript-Templates/ │ │ │ │ ├── .gitignore │ │ │ │ ├── .travis.yml │ │ │ │ ├── README.md │ │ │ │ ├── css/ │ │ │ │ │ └── demo.css │ │ │ │ ├── index.html │ │ │ │ ├── js/ │ │ │ │ │ ├── compile.js │ │ │ │ │ ├── demo.js │ │ │ │ │ ├── runtime.js │ │ │ │ │ └── tmpl.js │ │ │ │ ├── package.json │ │ │ │ └── test/ │ │ │ │ ├── index.html │ │ │ │ ├── test.js │ │ │ │ └── vendor/ │ │ │ │ ├── expect.js │ │ │ │ ├── mocha.css │ │ │ │ └── mocha.js │ │ │ ├── JavaScript-Templates-3.8.0/ │ │ │ │ ├── .gitignore │ │ │ │ ├── .npmignore │ │ │ │ ├── .travis.yml │ │ │ │ ├── README.md │ │ │ │ ├── css/ │ │ │ │ │ └── demo.css │ │ │ │ ├── index.html │ │ │ │ ├── js/ │ │ │ │ │ ├── compile.js │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── demo.js │ │ │ │ │ ├── runtime.js │ │ │ │ │ └── tmpl.js │ │ │ │ ├── package.json │ │ │ │ └── test/ │ │ │ │ ├── index.html │ │ │ │ ├── test.js │ │ │ │ └── vendor/ │ │ │ │ ├── chai.js │ │ │ │ ├── mocha.css │ │ │ │ └── mocha.js │ │ │ ├── Lightbox/ │ │ │ │ └── lightbox.js │ │ │ ├── Slideout/ │ │ │ │ └── slideout.js │ │ │ ├── Swiper-3.3.1/ │ │ │ │ ├── .gitignore │ │ │ │ ├── .jshintrc │ │ │ │ ├── .travis.yml │ │ │ │ ├── CHANGELOG.md │ │ │ │ ├── LICENSE │ │ │ │ ├── README.md │ │ │ │ ├── bower.json │ │ │ │ ├── component.json │ │ │ │ ├── demos/ │ │ │ │ │ ├── 01-default.html │ │ │ │ │ ├── 02-responsive.html │ │ │ │ │ ├── 03-vertical.html │ │ │ │ │ ├── 04-space-between.html │ │ │ │ │ ├── 05-slides-per-view.html │ │ │ │ │ ├── 06-slides-per-view-auto.html │ │ │ │ │ ├── 07-centered.html │ │ │ │ │ ├── 08-centered-auto.html │ │ │ │ │ ├── 09-freemode.html │ │ │ │ │ ├── 10-slides-per-column.html │ │ │ │ │ ├── 11-nested.html │ │ │ │ │ ├── 12-grab-cursor.html │ │ │ │ │ ├── 13-scrollbar.html │ │ │ │ │ ├── 14-nav-arrows.html │ │ │ │ │ ├── 15-infinite-loop.html │ │ │ │ │ ├── 16-effect-fade.html │ │ │ │ │ ├── 17-effect-cube.html │ │ │ │ │ ├── 18-effect-coverflow.html │ │ │ │ │ ├── 19-keyboard-control.html │ │ │ │ │ ├── 20-mousewheel-control.html │ │ │ │ │ ├── 21-autoplay.html │ │ │ │ │ ├── 22-dynamic-slides.html │ │ │ │ │ ├── 23-thumbs-gallery-loop.html │ │ │ │ │ ├── 23-thumbs-gallery.html │ │ │ │ │ ├── 24-multiple-swipers.html │ │ │ │ │ ├── 25-hash-navigation.html │ │ │ │ │ ├── 26-rtl.html │ │ │ │ │ ├── 27-jquery.html │ │ │ │ │ ├── 28-parallax.html │ │ │ │ │ ├── 29-custom-pagination.html │ │ │ │ │ ├── 30-lazy-load-images.html │ │ │ │ │ ├── 31-custom-plugin.html │ │ │ │ │ ├── 32-scroll-container.html │ │ │ │ │ ├── 33-responsive-breakpoints.html │ │ │ │ │ ├── 34-autoheight.html │ │ │ │ │ ├── 35-effect-flip.html │ │ │ │ │ ├── 36-pagination-fraction.html │ │ │ │ │ └── 37-pagination-progress.html │ │ │ │ ├── dist/ │ │ │ │ │ ├── css/ │ │ │ │ │ │ └── swiper.css │ │ │ │ │ └── js/ │ │ │ │ │ ├── swiper.jquery.js │ │ │ │ │ ├── swiper.jquery.umd.js │ │ │ │ │ └── swiper.js │ │ │ │ ├── gulpfile.js │ │ │ │ ├── package.js │ │ │ │ ├── package.json │ │ │ │ ├── playground/ │ │ │ │ │ └── index.html │ │ │ │ └── src/ │ │ │ │ ├── js/ │ │ │ │ │ ├── a11y.js │ │ │ │ │ ├── amd.js │ │ │ │ │ ├── controller.js │ │ │ │ │ ├── core.js │ │ │ │ │ ├── dom-plugins.js │ │ │ │ │ ├── dom.js │ │ │ │ │ ├── effects.js │ │ │ │ │ ├── emitter.js │ │ │ │ │ ├── get-dom-lib.js │ │ │ │ │ ├── get-jquery.js │ │ │ │ │ ├── hashnav.js │ │ │ │ │ ├── init.js │ │ │ │ │ ├── keyboard.js │ │ │ │ │ ├── lazy-load.js │ │ │ │ │ ├── mousewheel.js │ │ │ │ │ ├── parallax.js │ │ │ │ │ ├── plugins.js │ │ │ │ │ ├── scrollbar.js │ │ │ │ │ ├── swiper-intro-f7.js │ │ │ │ │ ├── swiper-intro.js │ │ │ │ │ ├── swiper-outro.js │ │ │ │ │ ├── swiper-proto.js │ │ │ │ │ ├── wrap-end-umd.js │ │ │ │ │ ├── wrap-end.js │ │ │ │ │ ├── wrap-start-umd.js │ │ │ │ │ └── wrap-start.js │ │ │ │ └── less/ │ │ │ │ ├── core.less │ │ │ │ ├── effects.less │ │ │ │ ├── mixins.less │ │ │ │ ├── navigation-f7.less │ │ │ │ ├── navigation.less │ │ │ │ ├── preloader-f7.less │ │ │ │ ├── preloader.less │ │ │ │ ├── scrollbar.less │ │ │ │ └── swiper.less │ │ │ ├── bootstrap-datepicker-1.6.4/ │ │ │ │ ├── css/ │ │ │ │ │ ├── bootstrap-datepicker.css │ │ │ │ │ ├── bootstrap-datepicker.standalone.css │ │ │ │ │ ├── bootstrap-datepicker3.css │ │ │ │ │ └── bootstrap-datepicker3.standalone.css │ │ │ │ └── js/ │ │ │ │ └── bootstrap-datepicker.js │ │ │ ├── bootstrap-select-1.12.2/ │ │ │ │ ├── .github/ │ │ │ │ │ └── ISSUE_TEMPLATE.md │ │ │ │ ├── .gitignore │ │ │ │ ├── CHANGELOG.md │ │ │ │ ├── CONTRIBUTING.md │ │ │ │ ├── Gruntfile.js │ │ │ │ ├── LICENSE │ │ │ │ ├── README.md │ │ │ │ ├── bower.json │ │ │ │ ├── composer.json │ │ │ │ ├── dist/ │ │ │ │ │ ├── css/ │ │ │ │ │ │ └── bootstrap-select.css │ │ │ │ │ └── js/ │ │ │ │ │ ├── bootstrap-select.js │ │ │ │ │ └── i18n/ │ │ │ │ │ ├── defaults-ar_AR.js │ │ │ │ │ ├── defaults-bg_BG.js │ │ │ │ │ ├── defaults-cro_CRO.js │ │ │ │ │ ├── defaults-cs_CZ.js │ │ │ │ │ ├── defaults-da_DK.js │ │ │ │ │ ├── defaults-de_DE.js │ │ │ │ │ ├── defaults-en_US.js │ │ │ │ │ ├── defaults-es_CL.js │ │ │ │ │ ├── defaults-es_ES.js │ │ │ │ │ ├── defaults-eu.js │ │ │ │ │ ├── defaults-fa_IR.js │ │ │ │ │ ├── defaults-fi_FI.js │ │ │ │ │ ├── defaults-fr_FR.js │ │ │ │ │ ├── defaults-hu_HU.js │ │ │ │ │ ├── defaults-id_ID.js │ │ │ │ │ ├── defaults-it_IT.js │ │ │ │ │ ├── defaults-ko_KR.js │ │ │ │ │ ├── defaults-lt_LT.js │ │ │ │ │ ├── defaults-nb_NO.js │ │ │ │ │ ├── defaults-nl_NL.js │ │ │ │ │ ├── defaults-pl_PL.js │ │ │ │ │ ├── defaults-pt_BR.js │ │ │ │ │ ├── defaults-pt_PT.js │ │ │ │ │ ├── defaults-ro_RO.js │ │ │ │ │ ├── defaults-ru_RU.js │ │ │ │ │ ├── defaults-sk_SK.js │ │ │ │ │ ├── defaults-sl_SI.js │ │ │ │ │ ├── defaults-sv_SE.js │ │ │ │ │ ├── defaults-tr_TR.js │ │ │ │ │ ├── defaults-ua_UA.js │ │ │ │ │ ├── defaults-zh_CN.js │ │ │ │ │ └── defaults-zh_TW.js │ │ │ │ ├── docs/ │ │ │ │ │ ├── custom_theme/ │ │ │ │ │ │ ├── base.html │ │ │ │ │ │ ├── css/ │ │ │ │ │ │ │ └── base.css │ │ │ │ │ │ ├── js/ │ │ │ │ │ │ │ └── base.js │ │ │ │ │ │ ├── nav.html │ │ │ │ │ │ └── toc.html │ │ │ │ │ ├── docs/ │ │ │ │ │ │ ├── css/ │ │ │ │ │ │ │ └── custom.css │ │ │ │ │ │ ├── dist/ │ │ │ │ │ │ │ ├── css/ │ │ │ │ │ │ │ │ └── bootstrap-select.css │ │ │ │ │ │ │ └── js/ │ │ │ │ │ │ │ ├── bootstrap-select.js │ │ │ │ │ │ │ └── i18n/ │ │ │ │ │ │ │ ├── defaults-ar_AR.js │ │ │ │ │ │ │ ├── defaults-bg_BG.js │ │ │ │ │ │ │ ├── defaults-cro_CRO.js │ │ │ │ │ │ │ ├── defaults-cs_CZ.js │ │ │ │ │ │ │ ├── defaults-da_DK.js │ │ │ │ │ │ │ ├── defaults-de_DE.js │ │ │ │ │ │ │ ├── defaults-en_US.js │ │ │ │ │ │ │ ├── defaults-es_CL.js │ │ │ │ │ │ │ ├── defaults-es_ES.js │ │ │ │ │ │ │ ├── defaults-eu.js │ │ │ │ │ │ │ ├── defaults-fa_IR.js │ │ │ │ │ │ │ ├── defaults-fi_FI.js │ │ │ │ │ │ │ ├── defaults-fr_FR.js │ │ │ │ │ │ │ ├── defaults-hu_HU.js │ │ │ │ │ │ │ ├── defaults-id_ID.js │ │ │ │ │ │ │ ├── defaults-it_IT.js │ │ │ │ │ │ │ ├── defaults-ko_KR.js │ │ │ │ │ │ │ ├── defaults-lt_LT.js │ │ │ │ │ │ │ ├── defaults-nb_NO.js │ │ │ │ │ │ │ ├── defaults-nl_NL.js │ │ │ │ │ │ │ ├── defaults-pl_PL.js │ │ │ │ │ │ │ ├── defaults-pt_BR.js │ │ │ │ │ │ │ ├── defaults-pt_PT.js │ │ │ │ │ │ │ ├── defaults-ro_RO.js │ │ │ │ │ │ │ ├── defaults-ru_RU.js │ │ │ │ │ │ │ ├── defaults-sk_SK.js │ │ │ │ │ │ │ ├── defaults-sl_SI.js │ │ │ │ │ │ │ ├── defaults-sv_SE.js │ │ │ │ │ │ │ ├── defaults-tr_TR.js │ │ │ │ │ │ │ ├── defaults-ua_UA.js │ │ │ │ │ │ │ ├── defaults-zh_CN.js │ │ │ │ │ │ │ └── defaults-zh_TW.js │ │ │ │ │ │ ├── examples.md │ │ │ │ │ │ ├── index.md │ │ │ │ │ │ ├── methods.md │ │ │ │ │ │ ├── options.md │ │ │ │ │ │ └── playground/ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ ├── plnkrOpener.js │ │ │ │ │ │ └── test.html │ │ │ │ │ └── mkdocs.yml │ │ │ │ ├── js/ │ │ │ │ │ ├── .jshintrc │ │ │ │ │ ├── bootstrap-select.js │ │ │ │ │ └── i18n/ │ │ │ │ │ ├── defaults-ar_AR.js │ │ │ │ │ ├── defaults-bg_BG.js │ │ │ │ │ ├── defaults-cro_CRO.js │ │ │ │ │ ├── defaults-cs_CZ.js │ │ │ │ │ ├── defaults-da_DK.js │ │ │ │ │ ├── defaults-de_DE.js │ │ │ │ │ ├── defaults-en_US.js │ │ │ │ │ ├── defaults-es_CL.js │ │ │ │ │ ├── defaults-es_ES.js │ │ │ │ │ ├── defaults-eu.js │ │ │ │ │ ├── defaults-fa_IR.js │ │ │ │ │ ├── defaults-fi_FI.js │ │ │ │ │ ├── defaults-fr_FR.js │ │ │ │ │ ├── defaults-hu_HU.js │ │ │ │ │ ├── defaults-id_ID.js │ │ │ │ │ ├── defaults-it_IT.js │ │ │ │ │ ├── defaults-ko_KR.js │ │ │ │ │ ├── defaults-lt_LT.js │ │ │ │ │ ├── defaults-nb_NO.js │ │ │ │ │ ├── defaults-nl_NL.js │ │ │ │ │ ├── defaults-pl_PL.js │ │ │ │ │ ├── defaults-pt_BR.js │ │ │ │ │ ├── defaults-pt_PT.js │ │ │ │ │ ├── defaults-ro_RO.js │ │ │ │ │ ├── defaults-ru_RU.js │ │ │ │ │ ├── defaults-sk_SK.js │ │ │ │ │ ├── defaults-sl_SI.js │ │ │ │ │ ├── defaults-sv_SE.js │ │ │ │ │ ├── defaults-tr_TR.js │ │ │ │ │ ├── defaults-ua_UA.js │ │ │ │ │ ├── defaults-zh_CN.js │ │ │ │ │ └── defaults-zh_TW.js │ │ │ │ ├── less/ │ │ │ │ │ ├── bootstrap-select.less │ │ │ │ │ └── variables.less │ │ │ │ ├── nuget/ │ │ │ │ │ ├── MyGet.ps1 │ │ │ │ │ └── bootstrap-select.nuspec │ │ │ │ ├── package.json │ │ │ │ ├── sass/ │ │ │ │ │ ├── bootstrap-select.scss │ │ │ │ │ └── variables.scss │ │ │ │ └── test.html │ │ │ ├── d3/ │ │ │ │ ├── API.md │ │ │ │ ├── CHANGES.md │ │ │ │ ├── LICENSE │ │ │ │ ├── README.md │ │ │ │ └── d3.js │ │ │ ├── font-awesome/ │ │ │ │ ├── HELP-US-OUT.txt │ │ │ │ ├── css/ │ │ │ │ │ └── font-awesome.css │ │ │ │ ├── fonts/ │ │ │ │ │ └── FontAwesome.otf │ │ │ │ ├── less/ │ │ │ │ │ ├── animated.less │ │ │ │ │ ├── bordered-pulled.less │ │ │ │ │ ├── core.less │ │ │ │ │ ├── extras.less │ │ │ │ │ ├── fixed-width.less │ │ │ │ │ ├── font-awesome.less │ │ │ │ │ ├── icons.less │ │ │ │ │ ├── larger.less │ │ │ │ │ ├── list.less │ │ │ │ │ ├── mixins.less │ │ │ │ │ ├── path.less │ │ │ │ │ ├── rotated-flipped.less │ │ │ │ │ ├── screen-reader.less │ │ │ │ │ ├── spinning.less │ │ │ │ │ ├── stacked.less │ │ │ │ │ └── variables.less │ │ │ │ └── scss/ │ │ │ │ ├── _animated.scss │ │ │ │ ├── _bordered-pulled.scss │ │ │ │ ├── _core.scss │ │ │ │ ├── _extras.scss │ │ │ │ ├── _fixed-width.scss │ │ │ │ ├── _icons.scss │ │ │ │ ├── _larger.scss │ │ │ │ ├── _list.scss │ │ │ │ ├── _mixins.scss │ │ │ │ ├── _path.scss │ │ │ │ ├── _rotated-flipped.scss │ │ │ │ ├── _screen-reader.scss │ │ │ │ ├── _spinning.scss │ │ │ │ ├── _stacked.scss │ │ │ │ ├── _variables.scss │ │ │ │ └── font-awesome.scss │ │ │ ├── jQuery-File-Upload-9.18.0/ │ │ │ │ ├── .gitignore │ │ │ │ ├── .jshintrc │ │ │ │ ├── .npmignore │ │ │ │ ├── CONTRIBUTING.md │ │ │ │ ├── LICENSE │ │ │ │ ├── README.md │ │ │ │ ├── angularjs.html │ │ │ │ ├── basic-plus.html │ │ │ │ ├── basic.html │ │ │ │ ├── bower-version-update.js │ │ │ │ ├── bower.json │ │ │ │ ├── cors/ │ │ │ │ │ ├── postmessage.html │ │ │ │ │ └── result.html │ │ │ │ ├── css/ │ │ │ │ │ ├── demo-ie8.css │ │ │ │ │ ├── demo.css │ │ │ │ │ ├── jquery.fileupload-noscript.css │ │ │ │ │ ├── jquery.fileupload-ui-noscript.css │ │ │ │ │ ├── jquery.fileupload-ui.css │ │ │ │ │ ├── jquery.fileupload.css │ │ │ │ │ └── style.css │ │ │ │ ├── index.html │ │ │ │ ├── jquery-ui.html │ │ │ │ ├── js/ │ │ │ │ │ ├── app.js │ │ │ │ │ ├── cors/ │ │ │ │ │ │ ├── jquery.postmessage-transport.js │ │ │ │ │ │ └── jquery.xdr-transport.js │ │ │ │ │ ├── jquery.fileupload-angular.js │ │ │ │ │ ├── jquery.fileupload-audio.js │ │ │ │ │ ├── jquery.fileupload-image.js │ │ │ │ │ ├── jquery.fileupload-jquery-ui.js │ │ │ │ │ ├── jquery.fileupload-process.js │ │ │ │ │ ├── jquery.fileupload-ui.js │ │ │ │ │ ├── jquery.fileupload-validate.js │ │ │ │ │ ├── jquery.fileupload-video.js │ │ │ │ │ ├── jquery.fileupload.js │ │ │ │ │ ├── jquery.iframe-transport.js │ │ │ │ │ ├── main.js │ │ │ │ │ └── vendor/ │ │ │ │ │ └── jquery.ui.widget.js │ │ │ │ ├── package.json │ │ │ │ ├── server/ │ │ │ │ │ ├── gae-go/ │ │ │ │ │ │ ├── app/ │ │ │ │ │ │ │ └── main.go │ │ │ │ │ │ ├── app.yaml │ │ │ │ │ │ └── static/ │ │ │ │ │ │ └── robots.txt │ │ │ │ │ ├── gae-python/ │ │ │ │ │ │ ├── app.yaml │ │ │ │ │ │ ├── main.py │ │ │ │ │ │ └── static/ │ │ │ │ │ │ └── robots.txt │ │ │ │ │ └── php/ │ │ │ │ │ ├── Dockerfile │ │ │ │ │ ├── UploadHandler.php │ │ │ │ │ ├── docker-compose.yml │ │ │ │ │ ├── files/ │ │ │ │ │ │ ├── .gitignore │ │ │ │ │ │ └── .htaccess │ │ │ │ │ └── index.php │ │ │ │ └── test/ │ │ │ │ ├── index.html │ │ │ │ └── test.js │ │ │ ├── metisMenu/ │ │ │ │ ├── metisMenu.css │ │ │ │ └── metisMenu.js │ │ │ └── moment-2.18.1/ │ │ │ ├── .editorconfig │ │ │ ├── .gitattributes │ │ │ ├── .gitignore │ │ │ ├── .jscs.json │ │ │ ├── .jshintrc │ │ │ ├── .npmignore │ │ │ ├── .spmignore │ │ │ ├── .travis.yml │ │ │ ├── CHANGELOG.md │ │ │ ├── CONTRIBUTING.md │ │ │ ├── Gruntfile.js │ │ │ ├── ISSUE_TEMPLATE.md │ │ │ ├── LICENSE │ │ │ ├── Moment.js.nuspec │ │ │ ├── README.md │ │ │ ├── benchmarks/ │ │ │ │ ├── add.js │ │ │ │ ├── clone.js │ │ │ │ ├── endOf.js │ │ │ │ ├── fromDate.js │ │ │ │ ├── fromDateUtc.js │ │ │ │ ├── makeDuration.js │ │ │ │ ├── query.js │ │ │ │ ├── startOf.js │ │ │ │ ├── subtract.js │ │ │ │ └── zeroFill.js │ │ │ ├── bower.json │ │ │ ├── component.json │ │ │ ├── composer.json │ │ │ ├── ender.js │ │ │ ├── locale/ │ │ │ │ ├── af.js │ │ │ │ ├── ar-dz.js │ │ │ │ ├── ar-kw.js │ │ │ │ ├── ar-ly.js │ │ │ │ ├── ar-ma.js │ │ │ │ ├── ar-sa.js │ │ │ │ ├── ar-tn.js │ │ │ │ ├── ar.js │ │ │ │ ├── az.js │ │ │ │ ├── be.js │ │ │ │ ├── bg.js │ │ │ │ ├── bn.js │ │ │ │ ├── bo.js │ │ │ │ ├── br.js │ │ │ │ ├── bs.js │ │ │ │ ├── ca.js │ │ │ │ ├── cs.js │ │ │ │ ├── cv.js │ │ │ │ ├── cy.js │ │ │ │ ├── da.js │ │ │ │ ├── de-at.js │ │ │ │ ├── de-ch.js │ │ │ │ ├── de.js │ │ │ │ ├── dv.js │ │ │ │ ├── el.js │ │ │ │ ├── en-au.js │ │ │ │ ├── en-ca.js │ │ │ │ ├── en-gb.js │ │ │ │ ├── en-ie.js │ │ │ │ ├── en-nz.js │ │ │ │ ├── eo.js │ │ │ │ ├── es-do.js │ │ │ │ ├── es.js │ │ │ │ ├── et.js │ │ │ │ ├── eu.js │ │ │ │ ├── fa.js │ │ │ │ ├── fi.js │ │ │ │ ├── fo.js │ │ │ │ ├── fr-ca.js │ │ │ │ ├── fr-ch.js │ │ │ │ ├── fr.js │ │ │ │ ├── fy.js │ │ │ │ ├── gd.js │ │ │ │ ├── gl.js │ │ │ │ ├── gom-latn.js │ │ │ │ ├── he.js │ │ │ │ ├── hi.js │ │ │ │ ├── hr.js │ │ │ │ ├── hu.js │ │ │ │ ├── hy-am.js │ │ │ │ ├── id.js │ │ │ │ ├── is.js │ │ │ │ ├── it.js │ │ │ │ ├── ja.js │ │ │ │ ├── jv.js │ │ │ │ ├── ka.js │ │ │ │ ├── kk.js │ │ │ │ ├── km.js │ │ │ │ ├── kn.js │ │ │ │ ├── ko.js │ │ │ │ ├── ky.js │ │ │ │ ├── lb.js │ │ │ │ ├── lo.js │ │ │ │ ├── lt.js │ │ │ │ ├── lv.js │ │ │ │ ├── me.js │ │ │ │ ├── mi.js │ │ │ │ ├── mk.js │ │ │ │ ├── ml.js │ │ │ │ ├── mr.js │ │ │ │ ├── ms-my.js │ │ │ │ ├── ms.js │ │ │ │ ├── my.js │ │ │ │ ├── nb.js │ │ │ │ ├── ne.js │ │ │ │ ├── nl-be.js │ │ │ │ ├── nl.js │ │ │ │ ├── nn.js │ │ │ │ ├── pa-in.js │ │ │ │ ├── pl.js │ │ │ │ ├── pt-br.js │ │ │ │ ├── pt.js │ │ │ │ ├── ro.js │ │ │ │ ├── ru.js │ │ │ │ ├── sd.js │ │ │ │ ├── se.js │ │ │ │ ├── si.js │ │ │ │ ├── sk.js │ │ │ │ ├── sl.js │ │ │ │ ├── sq.js │ │ │ │ ├── sr-cyrl.js │ │ │ │ ├── sr.js │ │ │ │ ├── ss.js │ │ │ │ ├── sv.js │ │ │ │ ├── sw.js │ │ │ │ ├── ta.js │ │ │ │ ├── te.js │ │ │ │ ├── tet.js │ │ │ │ ├── th.js │ │ │ │ ├── tl-ph.js │ │ │ │ ├── tlh.js │ │ │ │ ├── tr.js │ │ │ │ ├── tzl.js │ │ │ │ ├── tzm-latn.js │ │ │ │ ├── tzm.js │ │ │ │ ├── uk.js │ │ │ │ ├── ur.js │ │ │ │ ├── uz-latn.js │ │ │ │ ├── uz.js │ │ │ │ ├── vi.js │ │ │ │ ├── x-pseudo.js │ │ │ │ ├── yo.js │ │ │ │ ├── zh-cn.js │ │ │ │ ├── zh-hk.js │ │ │ │ └── zh-tw.js │ │ │ ├── meteor/ │ │ │ │ ├── README.md │ │ │ │ ├── export.js │ │ │ │ ├── moment.js │ │ │ │ ├── package.js │ │ │ │ └── test.js │ │ │ ├── min/ │ │ │ │ ├── locales.js │ │ │ │ ├── moment-with-locales.js │ │ │ │ └── tests.js │ │ │ ├── moment.d.ts │ │ │ ├── moment.js │ │ │ ├── package.js │ │ │ ├── package.json │ │ │ ├── scripts/ │ │ │ │ ├── locales.js │ │ │ │ └── npm_prepublish.sh │ │ │ ├── src/ │ │ │ │ ├── lib/ │ │ │ │ │ ├── create/ │ │ │ │ │ │ ├── check-overflow.js │ │ │ │ │ │ ├── date-from-array.js │ │ │ │ │ │ ├── from-anything.js │ │ │ │ │ │ ├── from-array.js │ │ │ │ │ │ ├── from-object.js │ │ │ │ │ │ ├── from-string-and-array.js │ │ │ │ │ │ ├── from-string-and-format.js │ │ │ │ │ │ ├── from-string.js │ │ │ │ │ │ ├── local.js │ │ │ │ │ │ ├── parsing-flags.js │ │ │ │ │ │ ├── utc.js │ │ │ │ │ │ └── valid.js │ │ │ │ │ ├── duration/ │ │ │ │ │ │ ├── abs.js │ │ │ │ │ │ ├── add-subtract.js │ │ │ │ │ │ ├── as.js │ │ │ │ │ │ ├── bubble.js │ │ │ │ │ │ ├── constructor.js │ │ │ │ │ │ ├── create.js │ │ │ │ │ │ ├── duration.js │ │ │ │ │ │ ├── get.js │ │ │ │ │ │ ├── humanize.js │ │ │ │ │ │ ├── iso-string.js │ │ │ │ │ │ ├── prototype.js │ │ │ │ │ │ └── valid.js │ │ │ │ │ ├── format/ │ │ │ │ │ │ └── format.js │ │ │ │ │ ├── locale/ │ │ │ │ │ │ ├── base-config.js │ │ │ │ │ │ ├── calendar.js │ │ │ │ │ │ ├── constructor.js │ │ │ │ │ │ ├── en.js │ │ │ │ │ │ ├── formats.js │ │ │ │ │ │ ├── invalid.js │ │ │ │ │ │ ├── lists.js │ │ │ │ │ │ ├── locale.js │ │ │ │ │ │ ├── locales.js │ │ │ │ │ │ ├── ordinal.js │ │ │ │ │ │ ├── pre-post-format.js │ │ │ │ │ │ ├── prototype.js │ │ │ │ │ │ ├── relative.js │ │ │ │ │ │ └── set.js │ │ │ │ │ ├── moment/ │ │ │ │ │ │ ├── add-subtract.js │ │ │ │ │ │ ├── calendar.js │ │ │ │ │ │ ├── clone.js │ │ │ │ │ │ ├── compare.js │ │ │ │ │ │ ├── constructor.js │ │ │ │ │ │ ├── creation-data.js │ │ │ │ │ │ ├── diff.js │ │ │ │ │ │ ├── format.js │ │ │ │ │ │ ├── from.js │ │ │ │ │ │ ├── get-set.js │ │ │ │ │ │ ├── locale.js │ │ │ │ │ │ ├── min-max.js │ │ │ │ │ │ ├── moment.js │ │ │ │ │ │ ├── now.js │ │ │ │ │ │ ├── prototype.js │ │ │ │ │ │ ├── start-end-of.js │ │ │ │ │ │ ├── to-type.js │ │ │ │ │ │ ├── to.js │ │ │ │ │ │ └── valid.js │ │ │ │ │ ├── parse/ │ │ │ │ │ │ ├── regex.js │ │ │ │ │ │ └── token.js │ │ │ │ │ ├── units/ │ │ │ │ │ │ ├── aliases.js │ │ │ │ │ │ ├── constants.js │ │ │ │ │ │ ├── day-of-month.js │ │ │ │ │ │ ├── day-of-week.js │ │ │ │ │ │ ├── day-of-year.js │ │ │ │ │ │ ├── hour.js │ │ │ │ │ │ ├── millisecond.js │ │ │ │ │ │ ├── minute.js │ │ │ │ │ │ ├── month.js │ │ │ │ │ │ ├── offset.js │ │ │ │ │ │ ├── priorities.js │ │ │ │ │ │ ├── quarter.js │ │ │ │ │ │ ├── second.js │ │ │ │ │ │ ├── timestamp.js │ │ │ │ │ │ ├── timezone.js │ │ │ │ │ │ ├── units.js │ │ │ │ │ │ ├── week-calendar-utils.js │ │ │ │ │ │ ├── week-year.js │ │ │ │ │ │ ├── week.js │ │ │ │ │ │ └── year.js │ │ │ │ │ └── utils/ │ │ │ │ │ ├── abs-ceil.js │ │ │ │ │ ├── abs-floor.js │ │ │ │ │ ├── abs-round.js │ │ │ │ │ ├── compare-arrays.js │ │ │ │ │ ├── defaults.js │ │ │ │ │ ├── deprecate.js │ │ │ │ │ ├── extend.js │ │ │ │ │ ├── has-own-prop.js │ │ │ │ │ ├── hooks.js │ │ │ │ │ ├── index-of.js │ │ │ │ │ ├── is-array.js │ │ │ │ │ ├── is-date.js │ │ │ │ │ ├── is-function.js │ │ │ │ │ ├── is-number.js │ │ │ │ │ ├── is-object-empty.js │ │ │ │ │ ├── is-object.js │ │ │ │ │ ├── is-undefined.js │ │ │ │ │ ├── keys.js │ │ │ │ │ ├── map.js │ │ │ │ │ ├── some.js │ │ │ │ │ ├── to-int.js │ │ │ │ │ └── zero-fill.js │ │ │ │ ├── locale/ │ │ │ │ │ ├── af.js │ │ │ │ │ ├── ar-dz.js │ │ │ │ │ ├── ar-kw.js │ │ │ │ │ ├── ar-ly.js │ │ │ │ │ ├── ar-ma.js │ │ │ │ │ ├── ar-sa.js │ │ │ │ │ ├── ar-tn.js │ │ │ │ │ ├── ar.js │ │ │ │ │ ├── az.js │ │ │ │ │ ├── be.js │ │ │ │ │ ├── bg.js │ │ │ │ │ ├── bn.js │ │ │ │ │ ├── bo.js │ │ │ │ │ ├── br.js │ │ │ │ │ ├── bs.js │ │ │ │ │ ├── ca.js │ │ │ │ │ ├── cs.js │ │ │ │ │ ├── cv.js │ │ │ │ │ ├── cy.js │ │ │ │ │ ├── da.js │ │ │ │ │ ├── de-at.js │ │ │ │ │ ├── de-ch.js │ │ │ │ │ ├── de.js │ │ │ │ │ ├── dv.js │ │ │ │ │ ├── el.js │ │ │ │ │ ├── en-au.js │ │ │ │ │ ├── en-ca.js │ │ │ │ │ ├── en-gb.js │ │ │ │ │ ├── en-ie.js │ │ │ │ │ ├── en-nz.js │ │ │ │ │ ├── eo.js │ │ │ │ │ ├── es-do.js │ │ │ │ │ ├── es.js │ │ │ │ │ ├── et.js │ │ │ │ │ ├── eu.js │ │ │ │ │ ├── fa.js │ │ │ │ │ ├── fi.js │ │ │ │ │ ├── fo.js │ │ │ │ │ ├── fr-ca.js │ │ │ │ │ ├── fr-ch.js │ │ │ │ │ ├── fr.js │ │ │ │ │ ├── fy.js │ │ │ │ │ ├── gd.js │ │ │ │ │ ├── gl.js │ │ │ │ │ ├── gom-latn.js │ │ │ │ │ ├── he.js │ │ │ │ │ ├── hi.js │ │ │ │ │ ├── hr.js │ │ │ │ │ ├── hu.js │ │ │ │ │ ├── hy-am.js │ │ │ │ │ ├── id.js │ │ │ │ │ ├── is.js │ │ │ │ │ ├── it.js │ │ │ │ │ ├── ja.js │ │ │ │ │ ├── jv.js │ │ │ │ │ ├── ka.js │ │ │ │ │ ├── kk.js │ │ │ │ │ ├── km.js │ │ │ │ │ ├── kn.js │ │ │ │ │ ├── ko.js │ │ │ │ │ ├── ky.js │ │ │ │ │ ├── lb.js │ │ │ │ │ ├── lo.js │ │ │ │ │ ├── lt.js │ │ │ │ │ ├── lv.js │ │ │ │ │ ├── me.js │ │ │ │ │ ├── mi.js │ │ │ │ │ ├── mk.js │ │ │ │ │ ├── ml.js │ │ │ │ │ ├── mr.js │ │ │ │ │ ├── ms-my.js │ │ │ │ │ ├── ms.js │ │ │ │ │ ├── my.js │ │ │ │ │ ├── nb.js │ │ │ │ │ ├── ne.js │ │ │ │ │ ├── nl-be.js │ │ │ │ │ ├── nl.js │ │ │ │ │ ├── nn.js │ │ │ │ │ ├── pa-in.js │ │ │ │ │ ├── pl.js │ │ │ │ │ ├── pt-br.js │ │ │ │ │ ├── pt.js │ │ │ │ │ ├── ro.js │ │ │ │ │ ├── ru.js │ │ │ │ │ ├── sd.js │ │ │ │ │ ├── se.js │ │ │ │ │ ├── si.js │ │ │ │ │ ├── sk.js │ │ │ │ │ ├── sl.js │ │ │ │ │ ├── sq.js │ │ │ │ │ ├── sr-cyrl.js │ │ │ │ │ ├── sr.js │ │ │ │ │ ├── ss.js │ │ │ │ │ ├── sv.js │ │ │ │ │ ├── sw.js │ │ │ │ │ ├── ta.js │ │ │ │ │ ├── te.js │ │ │ │ │ ├── tet.js │ │ │ │ │ ├── th.js │ │ │ │ │ ├── tl-ph.js │ │ │ │ │ ├── tlh.js │ │ │ │ │ ├── tr.js │ │ │ │ │ ├── tzl.js │ │ │ │ │ ├── tzm-latn.js │ │ │ │ │ ├── tzm.js │ │ │ │ │ ├── uk.js │ │ │ │ │ ├── ur.js │ │ │ │ │ ├── uz-latn.js │ │ │ │ │ ├── uz.js │ │ │ │ │ ├── vi.js │ │ │ │ │ ├── x-pseudo.js │ │ │ │ │ ├── yo.js │ │ │ │ │ ├── zh-cn.js │ │ │ │ │ ├── zh-hk.js │ │ │ │ │ └── zh-tw.js │ │ │ │ ├── moment.js │ │ │ │ └── test/ │ │ │ │ ├── helpers/ │ │ │ │ │ ├── common-locale.js │ │ │ │ │ ├── deprecation-handler.js │ │ │ │ │ ├── dst.js │ │ │ │ │ ├── each.js │ │ │ │ │ └── object-keys.js │ │ │ │ ├── locale/ │ │ │ │ │ ├── af.js │ │ │ │ │ ├── ar-dz.js │ │ │ │ │ ├── ar-kw.js │ │ │ │ │ ├── ar-ly.js │ │ │ │ │ ├── ar-ma.js │ │ │ │ │ ├── ar-sa.js │ │ │ │ │ ├── ar-tn.js │ │ │ │ │ ├── ar.js │ │ │ │ │ ├── az.js │ │ │ │ │ ├── be.js │ │ │ │ │ ├── bg.js │ │ │ │ │ ├── bn.js │ │ │ │ │ ├── bo.js │ │ │ │ │ ├── br.js │ │ │ │ │ ├── bs.js │ │ │ │ │ ├── ca.js │ │ │ │ │ ├── cs.js │ │ │ │ │ ├── cv.js │ │ │ │ │ ├── cy.js │ │ │ │ │ ├── da.js │ │ │ │ │ ├── de-at.js │ │ │ │ │ ├── de-ch.js │ │ │ │ │ ├── de.js │ │ │ │ │ ├── dv.js │ │ │ │ │ ├── el.js │ │ │ │ │ ├── en-au.js │ │ │ │ │ ├── en-ca.js │ │ │ │ │ ├── en-gb.js │ │ │ │ │ ├── en-ie.js │ │ │ │ │ ├── en-nz.js │ │ │ │ │ ├── en.js │ │ │ │ │ ├── eo.js │ │ │ │ │ ├── es-do.js │ │ │ │ │ ├── es.js │ │ │ │ │ ├── et.js │ │ │ │ │ ├── eu.js │ │ │ │ │ ├── fa.js │ │ │ │ │ ├── fi.js │ │ │ │ │ ├── fo.js │ │ │ │ │ ├── fr-ca.js │ │ │ │ │ ├── fr-ch.js │ │ │ │ │ ├── fr.js │ │ │ │ │ ├── fy.js │ │ │ │ │ ├── gd.js │ │ │ │ │ ├── gl.js │ │ │ │ │ ├── gom-latn.js │ │ │ │ │ ├── he.js │ │ │ │ │ ├── hi.js │ │ │ │ │ ├── hr.js │ │ │ │ │ ├── hu.js │ │ │ │ │ ├── hy-am.js │ │ │ │ │ ├── id.js │ │ │ │ │ ├── is.js │ │ │ │ │ ├── it.js │ │ │ │ │ ├── ja.js │ │ │ │ │ ├── jv.js │ │ │ │ │ ├── ka.js │ │ │ │ │ ├── kk.js │ │ │ │ │ ├── km.js │ │ │ │ │ ├── kn.js │ │ │ │ │ ├── ko.js │ │ │ │ │ ├── ky.js │ │ │ │ │ ├── lb.js │ │ │ │ │ ├── lo.js │ │ │ │ │ ├── lt.js │ │ │ │ │ ├── lv.js │ │ │ │ │ ├── me.js │ │ │ │ │ ├── mi.js │ │ │ │ │ ├── mk.js │ │ │ │ │ ├── ml.js │ │ │ │ │ ├── mr.js │ │ │ │ │ ├── ms-my.js │ │ │ │ │ ├── ms.js │ │ │ │ │ ├── my.js │ │ │ │ │ ├── nb.js │ │ │ │ │ ├── ne.js │ │ │ │ │ ├── nl-be.js │ │ │ │ │ ├── nl.js │ │ │ │ │ ├── nn.js │ │ │ │ │ ├── pa-in.js │ │ │ │ │ ├── pl.js │ │ │ │ │ ├── pt-br.js │ │ │ │ │ ├── pt.js │ │ │ │ │ ├── ro.js │ │ │ │ │ ├── ru.js │ │ │ │ │ ├── sd.js │ │ │ │ │ ├── se.js │ │ │ │ │ ├── si.js │ │ │ │ │ ├── sk.js │ │ │ │ │ ├── sl.js │ │ │ │ │ ├── sq.js │ │ │ │ │ ├── sr-cyrl.js │ │ │ │ │ ├── sr.js │ │ │ │ │ ├── ss.js │ │ │ │ │ ├── sv.js │ │ │ │ │ ├── sw.js │ │ │ │ │ ├── ta.js │ │ │ │ │ ├── te.js │ │ │ │ │ ├── tet.js │ │ │ │ │ ├── th.js │ │ │ │ │ ├── tl-ph.js │ │ │ │ │ ├── tlh.js │ │ │ │ │ ├── tr.js │ │ │ │ │ ├── tzl.js │ │ │ │ │ ├── tzm-latn.js │ │ │ │ │ ├── tzm.js │ │ │ │ │ ├── uk.js │ │ │ │ │ ├── ur.js │ │ │ │ │ ├── uz-latn.js │ │ │ │ │ ├── uz.js │ │ │ │ │ ├── vi.js │ │ │ │ │ ├── x-pseudo.js │ │ │ │ │ ├── yo.js │ │ │ │ │ ├── zh-cn.js │ │ │ │ │ ├── zh-hk.js │ │ │ │ │ └── zh-tw.js │ │ │ │ ├── moment/ │ │ │ │ │ ├── add_subtract.js │ │ │ │ │ ├── calendar.js │ │ │ │ │ ├── create.js │ │ │ │ │ ├── creation-data.js │ │ │ │ │ ├── days_in_month.js │ │ │ │ │ ├── days_in_year.js │ │ │ │ │ ├── deprecate.js │ │ │ │ │ ├── diff.js │ │ │ │ │ ├── duration.js │ │ │ │ │ ├── duration_from_moments.js │ │ │ │ │ ├── duration_invalid.js │ │ │ │ │ ├── format.js │ │ │ │ │ ├── from_to.js │ │ │ │ │ ├── getters_setters.js │ │ │ │ │ ├── instanceof.js │ │ │ │ │ ├── invalid.js │ │ │ │ │ ├── is_after.js │ │ │ │ │ ├── is_array.js │ │ │ │ │ ├── is_before.js │ │ │ │ │ ├── is_between.js │ │ │ │ │ ├── is_date.js │ │ │ │ │ ├── is_moment.js │ │ │ │ │ ├── is_number.js │ │ │ │ │ ├── is_same.js │ │ │ │ │ ├── is_same_or_after.js │ │ │ │ │ ├── is_same_or_before.js │ │ │ │ │ ├── is_valid.js │ │ │ │ │ ├── leapyear.js │ │ │ │ │ ├── listers.js │ │ │ │ │ ├── locale.js │ │ │ │ │ ├── locale_inheritance.js │ │ │ │ │ ├── locale_update.js │ │ │ │ │ ├── min_max.js │ │ │ │ │ ├── mutable.js │ │ │ │ │ ├── normalize_units.js │ │ │ │ │ ├── now.js │ │ │ │ │ ├── parsing_flags.js │ │ │ │ │ ├── preparse_postformat.js │ │ │ │ │ ├── quarter.js │ │ │ │ │ ├── relative_time.js │ │ │ │ │ ├── start_end_of.js │ │ │ │ │ ├── string_prototype.js │ │ │ │ │ ├── to_type.js │ │ │ │ │ ├── utc.js │ │ │ │ │ ├── utc_offset.js │ │ │ │ │ ├── week_year.js │ │ │ │ │ ├── weekday.js │ │ │ │ │ ├── weeks.js │ │ │ │ │ ├── weeks_in_year.js │ │ │ │ │ ├── zone_switching.js │ │ │ │ │ └── zones.js │ │ │ │ └── qunit.js │ │ │ ├── tasks/ │ │ │ │ ├── bump_version.js │ │ │ │ ├── check_sauce_creds.js │ │ │ │ ├── component.js │ │ │ │ ├── nuget.js │ │ │ │ ├── qtest.js │ │ │ │ ├── transpile.js │ │ │ │ └── update_index.js │ │ │ ├── templates/ │ │ │ │ ├── default.js │ │ │ │ ├── locale-header.js │ │ │ │ └── test-header.js │ │ │ └── typing-tests/ │ │ │ ├── moment-tests.ts │ │ │ └── tsconfig.json │ │ └── robots.txt │ ├── tasks/ │ │ ├── README.md │ │ ├── __init__.py │ │ ├── run_stats.py │ │ └── run_task.py │ ├── templates/ │ │ ├── 404.html │ │ ├── 500.html │ │ ├── _left.html │ │ ├── _top.html │ │ ├── active/ │ │ │ ├── add.html │ │ │ └── list.html │ │ ├── admin/ │ │ │ ├── add.html │ │ │ ├── edit.html │ │ │ ├── list.html │ │ │ ├── profile.html │ │ │ └── role.html │ │ ├── apply_get/ │ │ │ ├── add.html │ │ │ ├── info.html │ │ │ ├── list.html │ │ │ └── stats.html │ │ ├── apply_put/ │ │ │ ├── add.html │ │ │ ├── info.html │ │ │ ├── list.html │ │ │ └── stats.html │ │ ├── auth/ │ │ │ ├── email.html │ │ │ ├── index.html │ │ │ └── phone.html │ │ ├── complaint/ │ │ │ ├── list.html │ │ │ └── reply.html │ │ ├── index.html │ │ ├── index_.html │ │ ├── layout.html │ │ ├── login.html │ │ ├── macros.html │ │ ├── main.html │ │ ├── message/ │ │ │ └── list.html │ │ ├── order/ │ │ │ ├── list.html │ │ │ └── stats.html │ │ ├── scheduling/ │ │ │ ├── add.html │ │ │ └── list.html │ │ ├── score/ │ │ │ ├── list.html │ │ │ └── stats.html │ │ ├── settings/ │ │ │ ├── apply_get.html │ │ │ ├── apply_put.html │ │ │ ├── bonus.html │ │ │ ├── interest.html │ │ │ ├── order.html │ │ │ ├── score.html │ │ │ ├── switch.html │ │ │ ├── user.html │ │ │ └── wallet.html │ │ ├── stats/ │ │ │ ├── apply_get.html │ │ │ ├── apply_put.html │ │ │ ├── order.html │ │ │ └── user.html │ │ ├── user/ │ │ │ ├── _give_active.html │ │ │ ├── _team_tree.html │ │ │ ├── add.html │ │ │ ├── auth.html │ │ │ ├── bank.html │ │ │ ├── config.html │ │ │ ├── list.html │ │ │ ├── profile.html │ │ │ ├── relationship.html │ │ │ └── stats.html │ │ └── wallet/ │ │ ├── list.html │ │ └── stats.html │ ├── tests/ │ │ ├── __init__.py │ │ └── test_user_profile.py │ ├── tools/ │ │ ├── __init__.py │ │ ├── config_manage.py │ │ ├── db.py │ │ ├── db_test.py │ │ ├── decorators.py │ │ ├── file.py │ │ ├── send_sms.py │ │ ├── session_manage.py │ │ ├── stat.py │ │ ├── system.py │ │ └── url.py │ ├── vendor/ │ │ ├── bootstrap/ │ │ │ ├── css/ │ │ │ │ └── bootstrap.css │ │ │ └── js/ │ │ │ └── bootstrap.js │ │ ├── bootstrap-social/ │ │ │ ├── bootstrap-social.css │ │ │ ├── bootstrap-social.less │ │ │ └── bootstrap-social.scss │ │ ├── datatables/ │ │ │ ├── css/ │ │ │ │ ├── dataTables.bootstrap.css │ │ │ │ ├── dataTables.bootstrap4.css │ │ │ │ ├── dataTables.foundation.css │ │ │ │ ├── dataTables.jqueryui.css │ │ │ │ ├── dataTables.material.css │ │ │ │ ├── dataTables.semanticui.css │ │ │ │ ├── dataTables.uikit.css │ │ │ │ ├── jquery.dataTables.css │ │ │ │ └── jquery.dataTables_themeroller.css │ │ │ ├── images/ │ │ │ │ └── Sorting icons.psd │ │ │ └── js/ │ │ │ ├── dataTables.bootstrap.js │ │ │ ├── dataTables.bootstrap4.js │ │ │ ├── dataTables.foundation.js │ │ │ ├── dataTables.jqueryui.js │ │ │ ├── dataTables.material.js │ │ │ ├── dataTables.semanticui.js │ │ │ ├── dataTables.uikit.js │ │ │ ├── jquery.dataTables.js │ │ │ └── jquery.js │ │ ├── datatables-plugins/ │ │ │ ├── dataTables.bootstrap.css │ │ │ ├── dataTables.bootstrap.js │ │ │ └── index.html │ │ ├── datatables-responsive/ │ │ │ ├── dataTables.responsive.css │ │ │ ├── dataTables.responsive.js │ │ │ └── dataTables.responsive.scss │ │ ├── flot/ │ │ │ ├── excanvas.js │ │ │ ├── jquery.colorhelpers.js │ │ │ ├── jquery.flot.canvas.js │ │ │ ├── jquery.flot.categories.js │ │ │ ├── jquery.flot.crosshair.js │ │ │ ├── jquery.flot.errorbars.js │ │ │ ├── jquery.flot.fillbetween.js │ │ │ ├── jquery.flot.image.js │ │ │ ├── jquery.flot.js │ │ │ ├── jquery.flot.navigate.js │ │ │ ├── jquery.flot.pie.js │ │ │ ├── jquery.flot.resize.js │ │ │ ├── jquery.flot.selection.js │ │ │ ├── jquery.flot.stack.js │ │ │ ├── jquery.flot.symbol.js │ │ │ ├── jquery.flot.threshold.js │ │ │ ├── jquery.flot.time.js │ │ │ └── jquery.js │ │ ├── flot-tooltip/ │ │ │ ├── jquery.flot.tooltip.js │ │ │ └── jquery.flot.tooltip.source.js │ │ ├── font-awesome/ │ │ │ ├── HELP-US-OUT.txt │ │ │ ├── css/ │ │ │ │ └── font-awesome.css │ │ │ ├── fonts/ │ │ │ │ └── FontAwesome.otf │ │ │ ├── less/ │ │ │ │ ├── animated.less │ │ │ │ ├── bordered-pulled.less │ │ │ │ ├── core.less │ │ │ │ ├── extras.less │ │ │ │ ├── fixed-width.less │ │ │ │ ├── font-awesome.less │ │ │ │ ├── icons.less │ │ │ │ ├── larger.less │ │ │ │ ├── list.less │ │ │ │ ├── mixins.less │ │ │ │ ├── path.less │ │ │ │ ├── rotated-flipped.less │ │ │ │ ├── screen-reader.less │ │ │ │ ├── spinning.less │ │ │ │ ├── stacked.less │ │ │ │ └── variables.less │ │ │ └── scss/ │ │ │ ├── _animated.scss │ │ │ ├── _bordered-pulled.scss │ │ │ ├── _core.scss │ │ │ ├── _extras.scss │ │ │ ├── _fixed-width.scss │ │ │ ├── _icons.scss │ │ │ ├── _larger.scss │ │ │ ├── _list.scss │ │ │ ├── _mixins.scss │ │ │ ├── _path.scss │ │ │ ├── _rotated-flipped.scss │ │ │ ├── _screen-reader.scss │ │ │ ├── _spinning.scss │ │ │ ├── _stacked.scss │ │ │ ├── _variables.scss │ │ │ └── font-awesome.scss │ │ ├── jquery/ │ │ │ └── jquery.js │ │ ├── metisMenu/ │ │ │ ├── metisMenu.css │ │ │ └── metisMenu.js │ │ ├── morrisjs/ │ │ │ ├── morris.css │ │ │ └── morris.js │ │ └── raphael/ │ │ └── raphael.js │ └── views/ │ ├── __init__.py │ ├── active.py │ ├── admin.py │ ├── apply_get.py │ ├── apply_put.py │ ├── complaint.py │ ├── message.py │ ├── order.py │ ├── scheduling.py │ ├── score.py │ ├── settings.py │ ├── stats.py │ ├── user.py │ └── wallet.py ├── app_common/ │ ├── README.md │ ├── __init__.py │ ├── maps/ │ │ ├── __init__.py │ │ ├── output.py │ │ ├── role_admin.py │ │ ├── status_active.py │ │ ├── status_apply.py │ │ ├── status_audit.py │ │ ├── status_delete.py │ │ ├── status_flow.py │ │ ├── status_lock.py │ │ ├── status_order.py │ │ ├── status_pay.py │ │ ├── status_rec.py │ │ ├── status_reply.py │ │ ├── type_active.py │ │ ├── type_apply.py │ │ ├── type_auth.py │ │ ├── type_level.py │ │ ├── type_order.py │ │ ├── type_pay.py │ │ ├── type_payment.py │ │ ├── type_scheduling.py │ │ ├── type_score.py │ │ ├── type_withdraw.py │ │ └── 全球区号.xlsx │ └── tools/ │ ├── __init__.py │ ├── date_time.py │ ├── file.py │ ├── ip.py │ └── tree.py ├── app_frontend/ │ ├── README.md │ ├── __init__.py │ ├── api/ │ │ ├── __init__.py │ │ ├── active.py │ │ ├── active_item.py │ │ ├── apply_get.py │ │ ├── apply_put.py │ │ ├── author.py │ │ ├── bit_coin.py │ │ ├── bit_coin_item.py │ │ ├── blog.py │ │ ├── bonus.py │ │ ├── bonus_item.py │ │ ├── complaint.py │ │ ├── credit.py │ │ ├── message.py │ │ ├── order.py │ │ ├── order_bill.py │ │ ├── scheduling.py │ │ ├── scheduling_item.py │ │ ├── score.py │ │ ├── score_charity.py │ │ ├── score_charity_item.py │ │ ├── score_digital.py │ │ ├── score_digital_item.py │ │ ├── score_expense.py │ │ ├── score_expense_item.py │ │ ├── user.py │ │ ├── user_auth.py │ │ ├── user_bank.py │ │ ├── user_config.py │ │ ├── user_profile.py │ │ ├── wallet.py │ │ └── wallet_item.py │ ├── celery_worker.py │ ├── database.py │ ├── emails.py │ ├── filters.py │ ├── forms/ │ │ ├── __init__.py │ │ ├── active.py │ │ ├── apply_get.py │ │ ├── apply_put.py │ │ ├── blog.py │ │ ├── complaint.py │ │ ├── login.py │ │ ├── message.py │ │ ├── order.py │ │ ├── pay.py │ │ ├── reg.py │ │ ├── scheduling.py │ │ └── user.py │ ├── lib/ │ │ ├── __init__.py │ │ ├── captcha.py │ │ ├── cart.py │ │ ├── container.py │ │ ├── counter.py │ │ ├── mongo.py │ │ ├── qiniu_store.py │ │ ├── rabbit_mq.py │ │ ├── redis_session.py │ │ ├── sendcloud.py │ │ ├── session.py │ │ ├── sms_chuanglan.py │ │ ├── sms_chuanglan_iso.py │ │ └── token.py │ ├── log_test.py │ ├── login.py │ ├── middlewares.py │ ├── models.py │ ├── models_sqlite.py │ ├── static/ │ │ ├── css/ │ │ │ ├── bootstrap-select.css │ │ │ ├── bootstrap-theme.css │ │ │ ├── bootstrap.css │ │ │ ├── custom.css │ │ │ ├── lightbox.css │ │ │ └── slideout.css │ │ ├── js/ │ │ │ ├── bootstrap-select.js │ │ │ ├── bootstrap.js │ │ │ ├── custom.js │ │ │ ├── i18n/ │ │ │ │ ├── defaults-ar_AR.js │ │ │ │ ├── defaults-bg_BG.js │ │ │ │ ├── defaults-cro_CRO.js │ │ │ │ ├── defaults-cs_CZ.js │ │ │ │ ├── defaults-da_DK.js │ │ │ │ ├── defaults-de_DE.js │ │ │ │ ├── defaults-en_US.js │ │ │ │ ├── defaults-es_CL.js │ │ │ │ ├── defaults-es_ES.js │ │ │ │ ├── defaults-eu.js │ │ │ │ ├── defaults-fa_IR.js │ │ │ │ ├── defaults-fi_FI.js │ │ │ │ ├── defaults-fr_FR.js │ │ │ │ ├── defaults-hu_HU.js │ │ │ │ ├── defaults-id_ID.js │ │ │ │ ├── defaults-it_IT.js │ │ │ │ ├── defaults-ko_KR.js │ │ │ │ ├── defaults-lt_LT.js │ │ │ │ ├── defaults-nb_NO.js │ │ │ │ ├── defaults-nl_NL.js │ │ │ │ ├── defaults-pl_PL.js │ │ │ │ ├── defaults-pt_BR.js │ │ │ │ ├── defaults-pt_PT.js │ │ │ │ ├── defaults-ro_RO.js │ │ │ │ ├── defaults-ru_RU.js │ │ │ │ ├── defaults-sk_SK.js │ │ │ │ ├── defaults-sl_SI.js │ │ │ │ ├── defaults-sv_SE.js │ │ │ │ ├── defaults-tr_TR.js │ │ │ │ ├── defaults-ua_UA.js │ │ │ │ ├── defaults-zh_CN.js │ │ │ │ └── defaults-zh_TW.js │ │ │ └── npm.js │ │ ├── plugin/ │ │ │ ├── Chart/ │ │ │ │ └── Chart.js │ │ │ ├── Gallery-2.25.0/ │ │ │ │ ├── .gitignore │ │ │ │ ├── .npmignore │ │ │ │ ├── LICENSE.txt │ │ │ │ ├── README.md │ │ │ │ ├── css/ │ │ │ │ │ ├── blueimp-gallery-indicator.css │ │ │ │ │ ├── blueimp-gallery-video.css │ │ │ │ │ ├── blueimp-gallery.css │ │ │ │ │ └── demo/ │ │ │ │ │ └── demo.css │ │ │ │ ├── index.html │ │ │ │ ├── js/ │ │ │ │ │ ├── blueimp-gallery-fullscreen.js │ │ │ │ │ ├── blueimp-gallery-indicator.js │ │ │ │ │ ├── blueimp-gallery-video.js │ │ │ │ │ ├── blueimp-gallery-vimeo.js │ │ │ │ │ ├── blueimp-gallery-youtube.js │ │ │ │ │ ├── blueimp-gallery.js │ │ │ │ │ ├── blueimp-helper.js │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── demo.js │ │ │ │ │ ├── jquery.blueimp-gallery.js │ │ │ │ │ └── vendor/ │ │ │ │ │ └── jquery.js │ │ │ │ └── package.json │ │ │ ├── JavaScript-Canvas-to-Blob-3.7.0/ │ │ │ │ ├── .gitignore │ │ │ │ ├── .npmignore │ │ │ │ ├── .travis.yml │ │ │ │ ├── README.md │ │ │ │ ├── js/ │ │ │ │ │ └── canvas-to-blob.js │ │ │ │ ├── package.json │ │ │ │ └── test/ │ │ │ │ ├── index.html │ │ │ │ ├── test.js │ │ │ │ └── vendor/ │ │ │ │ ├── chai.js │ │ │ │ ├── load-image.js │ │ │ │ ├── mocha.css │ │ │ │ └── mocha.js │ │ │ ├── JavaScript-Load-Image-2.12.2/ │ │ │ │ ├── .gitignore │ │ │ │ ├── .npmignore │ │ │ │ ├── .travis.yml │ │ │ │ ├── README.md │ │ │ │ ├── css/ │ │ │ │ │ ├── demo.css │ │ │ │ │ └── vendor/ │ │ │ │ │ └── jquery.Jcrop.css │ │ │ │ ├── index.html │ │ │ │ ├── js/ │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── demo.js │ │ │ │ │ ├── index.js │ │ │ │ │ ├── load-image-exif-map.js │ │ │ │ │ ├── load-image-exif.js │ │ │ │ │ ├── load-image-fetch.js │ │ │ │ │ ├── load-image-meta.js │ │ │ │ │ ├── load-image-orientation.js │ │ │ │ │ ├── load-image-scale.js │ │ │ │ │ ├── load-image.js │ │ │ │ │ └── vendor/ │ │ │ │ │ ├── jquery.Jcrop.js │ │ │ │ │ └── jquery.js │ │ │ │ ├── package.json │ │ │ │ └── test/ │ │ │ │ ├── index.html │ │ │ │ ├── test.js │ │ │ │ └── vendor/ │ │ │ │ ├── canvas-to-blob.js │ │ │ │ ├── chai.js │ │ │ │ ├── mocha.css │ │ │ │ └── mocha.js │ │ │ ├── JavaScript-Templates/ │ │ │ │ ├── .gitignore │ │ │ │ ├── .travis.yml │ │ │ │ ├── README.md │ │ │ │ ├── css/ │ │ │ │ │ └── demo.css │ │ │ │ ├── index.html │ │ │ │ ├── js/ │ │ │ │ │ ├── compile.js │ │ │ │ │ ├── demo.js │ │ │ │ │ ├── runtime.js │ │ │ │ │ └── tmpl.js │ │ │ │ ├── package.json │ │ │ │ └── test/ │ │ │ │ ├── index.html │ │ │ │ ├── test.js │ │ │ │ └── vendor/ │ │ │ │ ├── expect.js │ │ │ │ ├── mocha.css │ │ │ │ └── mocha.js │ │ │ ├── JavaScript-Templates-3.8.0/ │ │ │ │ ├── .gitignore │ │ │ │ ├── .npmignore │ │ │ │ ├── .travis.yml │ │ │ │ ├── README.md │ │ │ │ ├── css/ │ │ │ │ │ └── demo.css │ │ │ │ ├── index.html │ │ │ │ ├── js/ │ │ │ │ │ ├── compile.js │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── demo.js │ │ │ │ │ ├── runtime.js │ │ │ │ │ └── tmpl.js │ │ │ │ ├── package.json │ │ │ │ └── test/ │ │ │ │ ├── index.html │ │ │ │ ├── test.js │ │ │ │ └── vendor/ │ │ │ │ ├── chai.js │ │ │ │ ├── mocha.css │ │ │ │ └── mocha.js │ │ │ ├── Lightbox/ │ │ │ │ └── lightbox.js │ │ │ ├── Slideout/ │ │ │ │ └── slideout.js │ │ │ ├── Swiper-3.3.1/ │ │ │ │ ├── .gitignore │ │ │ │ ├── .jshintrc │ │ │ │ ├── .travis.yml │ │ │ │ ├── CHANGELOG.md │ │ │ │ ├── LICENSE │ │ │ │ ├── README.md │ │ │ │ ├── bower.json │ │ │ │ ├── component.json │ │ │ │ ├── demos/ │ │ │ │ │ ├── 01-default.html │ │ │ │ │ ├── 02-responsive.html │ │ │ │ │ ├── 03-vertical.html │ │ │ │ │ ├── 04-space-between.html │ │ │ │ │ ├── 05-slides-per-view.html │ │ │ │ │ ├── 06-slides-per-view-auto.html │ │ │ │ │ ├── 07-centered.html │ │ │ │ │ ├── 08-centered-auto.html │ │ │ │ │ ├── 09-freemode.html │ │ │ │ │ ├── 10-slides-per-column.html │ │ │ │ │ ├── 11-nested.html │ │ │ │ │ ├── 12-grab-cursor.html │ │ │ │ │ ├── 13-scrollbar.html │ │ │ │ │ ├── 14-nav-arrows.html │ │ │ │ │ ├── 15-infinite-loop.html │ │ │ │ │ ├── 16-effect-fade.html │ │ │ │ │ ├── 17-effect-cube.html │ │ │ │ │ ├── 18-effect-coverflow.html │ │ │ │ │ ├── 19-keyboard-control.html │ │ │ │ │ ├── 20-mousewheel-control.html │ │ │ │ │ ├── 21-autoplay.html │ │ │ │ │ ├── 22-dynamic-slides.html │ │ │ │ │ ├── 23-thumbs-gallery-loop.html │ │ │ │ │ ├── 23-thumbs-gallery.html │ │ │ │ │ ├── 24-multiple-swipers.html │ │ │ │ │ ├── 25-hash-navigation.html │ │ │ │ │ ├── 26-rtl.html │ │ │ │ │ ├── 27-jquery.html │ │ │ │ │ ├── 28-parallax.html │ │ │ │ │ ├── 29-custom-pagination.html │ │ │ │ │ ├── 30-lazy-load-images.html │ │ │ │ │ ├── 31-custom-plugin.html │ │ │ │ │ ├── 32-scroll-container.html │ │ │ │ │ ├── 33-responsive-breakpoints.html │ │ │ │ │ ├── 34-autoheight.html │ │ │ │ │ ├── 35-effect-flip.html │ │ │ │ │ ├── 36-pagination-fraction.html │ │ │ │ │ └── 37-pagination-progress.html │ │ │ │ ├── dist/ │ │ │ │ │ ├── css/ │ │ │ │ │ │ └── swiper.css │ │ │ │ │ └── js/ │ │ │ │ │ ├── swiper.jquery.js │ │ │ │ │ ├── swiper.jquery.umd.js │ │ │ │ │ └── swiper.js │ │ │ │ ├── gulpfile.js │ │ │ │ ├── package.js │ │ │ │ ├── package.json │ │ │ │ ├── playground/ │ │ │ │ │ └── index.html │ │ │ │ └── src/ │ │ │ │ ├── js/ │ │ │ │ │ ├── a11y.js │ │ │ │ │ ├── amd.js │ │ │ │ │ ├── controller.js │ │ │ │ │ ├── core.js │ │ │ │ │ ├── dom-plugins.js │ │ │ │ │ ├── dom.js │ │ │ │ │ ├── effects.js │ │ │ │ │ ├── emitter.js │ │ │ │ │ ├── get-dom-lib.js │ │ │ │ │ ├── get-jquery.js │ │ │ │ │ ├── hashnav.js │ │ │ │ │ ├── init.js │ │ │ │ │ ├── keyboard.js │ │ │ │ │ ├── lazy-load.js │ │ │ │ │ ├── mousewheel.js │ │ │ │ │ ├── parallax.js │ │ │ │ │ ├── plugins.js │ │ │ │ │ ├── scrollbar.js │ │ │ │ │ ├── swiper-intro-f7.js │ │ │ │ │ ├── swiper-intro.js │ │ │ │ │ ├── swiper-outro.js │ │ │ │ │ ├── swiper-proto.js │ │ │ │ │ ├── wrap-end-umd.js │ │ │ │ │ ├── wrap-end.js │ │ │ │ │ ├── wrap-start-umd.js │ │ │ │ │ └── wrap-start.js │ │ │ │ └── less/ │ │ │ │ ├── core.less │ │ │ │ ├── effects.less │ │ │ │ ├── mixins.less │ │ │ │ ├── navigation-f7.less │ │ │ │ ├── navigation.less │ │ │ │ ├── preloader-f7.less │ │ │ │ ├── preloader.less │ │ │ │ ├── scrollbar.less │ │ │ │ └── swiper.less │ │ │ ├── bootstrap-select-1.12.2/ │ │ │ │ ├── .github/ │ │ │ │ │ └── ISSUE_TEMPLATE.md │ │ │ │ ├── .gitignore │ │ │ │ ├── CHANGELOG.md │ │ │ │ ├── CONTRIBUTING.md │ │ │ │ ├── Gruntfile.js │ │ │ │ ├── LICENSE │ │ │ │ ├── README.md │ │ │ │ ├── bower.json │ │ │ │ ├── composer.json │ │ │ │ ├── dist/ │ │ │ │ │ ├── css/ │ │ │ │ │ │ └── bootstrap-select.css │ │ │ │ │ └── js/ │ │ │ │ │ ├── bootstrap-select.js │ │ │ │ │ └── i18n/ │ │ │ │ │ ├── defaults-ar_AR.js │ │ │ │ │ ├── defaults-bg_BG.js │ │ │ │ │ ├── defaults-cro_CRO.js │ │ │ │ │ ├── defaults-cs_CZ.js │ │ │ │ │ ├── defaults-da_DK.js │ │ │ │ │ ├── defaults-de_DE.js │ │ │ │ │ ├── defaults-en_US.js │ │ │ │ │ ├── defaults-es_CL.js │ │ │ │ │ ├── defaults-es_ES.js │ │ │ │ │ ├── defaults-eu.js │ │ │ │ │ ├── defaults-fa_IR.js │ │ │ │ │ ├── defaults-fi_FI.js │ │ │ │ │ ├── defaults-fr_FR.js │ │ │ │ │ ├── defaults-hu_HU.js │ │ │ │ │ ├── defaults-id_ID.js │ │ │ │ │ ├── defaults-it_IT.js │ │ │ │ │ ├── defaults-ko_KR.js │ │ │ │ │ ├── defaults-lt_LT.js │ │ │ │ │ ├── defaults-nb_NO.js │ │ │ │ │ ├── defaults-nl_NL.js │ │ │ │ │ ├── defaults-pl_PL.js │ │ │ │ │ ├── defaults-pt_BR.js │ │ │ │ │ ├── defaults-pt_PT.js │ │ │ │ │ ├── defaults-ro_RO.js │ │ │ │ │ ├── defaults-ru_RU.js │ │ │ │ │ ├── defaults-sk_SK.js │ │ │ │ │ ├── defaults-sl_SI.js │ │ │ │ │ ├── defaults-sv_SE.js │ │ │ │ │ ├── defaults-tr_TR.js │ │ │ │ │ ├── defaults-ua_UA.js │ │ │ │ │ ├── defaults-zh_CN.js │ │ │ │ │ └── defaults-zh_TW.js │ │ │ │ ├── docs/ │ │ │ │ │ ├── custom_theme/ │ │ │ │ │ │ ├── base.html │ │ │ │ │ │ ├── css/ │ │ │ │ │ │ │ └── base.css │ │ │ │ │ │ ├── js/ │ │ │ │ │ │ │ └── base.js │ │ │ │ │ │ ├── nav.html │ │ │ │ │ │ └── toc.html │ │ │ │ │ ├── docs/ │ │ │ │ │ │ ├── css/ │ │ │ │ │ │ │ └── custom.css │ │ │ │ │ │ ├── dist/ │ │ │ │ │ │ │ ├── css/ │ │ │ │ │ │ │ │ └── bootstrap-select.css │ │ │ │ │ │ │ └── js/ │ │ │ │ │ │ │ ├── bootstrap-select.js │ │ │ │ │ │ │ └── i18n/ │ │ │ │ │ │ │ ├── defaults-ar_AR.js │ │ │ │ │ │ │ ├── defaults-bg_BG.js │ │ │ │ │ │ │ ├── defaults-cro_CRO.js │ │ │ │ │ │ │ ├── defaults-cs_CZ.js │ │ │ │ │ │ │ ├── defaults-da_DK.js │ │ │ │ │ │ │ ├── defaults-de_DE.js │ │ │ │ │ │ │ ├── defaults-en_US.js │ │ │ │ │ │ │ ├── defaults-es_CL.js │ │ │ │ │ │ │ ├── defaults-es_ES.js │ │ │ │ │ │ │ ├── defaults-eu.js │ │ │ │ │ │ │ ├── defaults-fa_IR.js │ │ │ │ │ │ │ ├── defaults-fi_FI.js │ │ │ │ │ │ │ ├── defaults-fr_FR.js │ │ │ │ │ │ │ ├── defaults-hu_HU.js │ │ │ │ │ │ │ ├── defaults-id_ID.js │ │ │ │ │ │ │ ├── defaults-it_IT.js │ │ │ │ │ │ │ ├── defaults-ko_KR.js │ │ │ │ │ │ │ ├── defaults-lt_LT.js │ │ │ │ │ │ │ ├── defaults-nb_NO.js │ │ │ │ │ │ │ ├── defaults-nl_NL.js │ │ │ │ │ │ │ ├── defaults-pl_PL.js │ │ │ │ │ │ │ ├── defaults-pt_BR.js │ │ │ │ │ │ │ ├── defaults-pt_PT.js │ │ │ │ │ │ │ ├── defaults-ro_RO.js │ │ │ │ │ │ │ ├── defaults-ru_RU.js │ │ │ │ │ │ │ ├── defaults-sk_SK.js │ │ │ │ │ │ │ ├── defaults-sl_SI.js │ │ │ │ │ │ │ ├── defaults-sv_SE.js │ │ │ │ │ │ │ ├── defaults-tr_TR.js │ │ │ │ │ │ │ ├── defaults-ua_UA.js │ │ │ │ │ │ │ ├── defaults-zh_CN.js │ │ │ │ │ │ │ └── defaults-zh_TW.js │ │ │ │ │ │ ├── examples.md │ │ │ │ │ │ ├── index.md │ │ │ │ │ │ ├── methods.md │ │ │ │ │ │ ├── options.md │ │ │ │ │ │ └── playground/ │ │ │ │ │ │ ├── index.html │ │ │ │ │ │ ├── plnkrOpener.js │ │ │ │ │ │ └── test.html │ │ │ │ │ └── mkdocs.yml │ │ │ │ ├── js/ │ │ │ │ │ ├── .jshintrc │ │ │ │ │ ├── bootstrap-select.js │ │ │ │ │ └── i18n/ │ │ │ │ │ ├── defaults-ar_AR.js │ │ │ │ │ ├── defaults-bg_BG.js │ │ │ │ │ ├── defaults-cro_CRO.js │ │ │ │ │ ├── defaults-cs_CZ.js │ │ │ │ │ ├── defaults-da_DK.js │ │ │ │ │ ├── defaults-de_DE.js │ │ │ │ │ ├── defaults-en_US.js │ │ │ │ │ ├── defaults-es_CL.js │ │ │ │ │ ├── defaults-es_ES.js │ │ │ │ │ ├── defaults-eu.js │ │ │ │ │ ├── defaults-fa_IR.js │ │ │ │ │ ├── defaults-fi_FI.js │ │ │ │ │ ├── defaults-fr_FR.js │ │ │ │ │ ├── defaults-hu_HU.js │ │ │ │ │ ├── defaults-id_ID.js │ │ │ │ │ ├── defaults-it_IT.js │ │ │ │ │ ├── defaults-ko_KR.js │ │ │ │ │ ├── defaults-lt_LT.js │ │ │ │ │ ├── defaults-nb_NO.js │ │ │ │ │ ├── defaults-nl_NL.js │ │ │ │ │ ├── defaults-pl_PL.js │ │ │ │ │ ├── defaults-pt_BR.js │ │ │ │ │ ├── defaults-pt_PT.js │ │ │ │ │ ├── defaults-ro_RO.js │ │ │ │ │ ├── defaults-ru_RU.js │ │ │ │ │ ├── defaults-sk_SK.js │ │ │ │ │ ├── defaults-sl_SI.js │ │ │ │ │ ├── defaults-sv_SE.js │ │ │ │ │ ├── defaults-tr_TR.js │ │ │ │ │ ├── defaults-ua_UA.js │ │ │ │ │ ├── defaults-zh_CN.js │ │ │ │ │ └── defaults-zh_TW.js │ │ │ │ ├── less/ │ │ │ │ │ ├── bootstrap-select.less │ │ │ │ │ └── variables.less │ │ │ │ ├── nuget/ │ │ │ │ │ ├── MyGet.ps1 │ │ │ │ │ └── bootstrap-select.nuspec │ │ │ │ ├── package.json │ │ │ │ ├── sass/ │ │ │ │ │ ├── bootstrap-select.scss │ │ │ │ │ └── variables.scss │ │ │ │ └── test.html │ │ │ ├── clipboard.js-1.6.1/ │ │ │ │ ├── .babelrc │ │ │ │ ├── .banner │ │ │ │ ├── .editorconfig │ │ │ │ ├── .github/ │ │ │ │ │ └── issue_template.md │ │ │ │ ├── .gitignore │ │ │ │ ├── .npmignore │ │ │ │ ├── .travis.yml │ │ │ │ ├── bower.json │ │ │ │ ├── contributing.md │ │ │ │ ├── demo/ │ │ │ │ │ ├── constructor-node.html │ │ │ │ │ ├── constructor-nodelist.html │ │ │ │ │ ├── constructor-selector.html │ │ │ │ │ ├── function-target.html │ │ │ │ │ ├── function-text.html │ │ │ │ │ ├── target-div.html │ │ │ │ │ ├── target-input.html │ │ │ │ │ └── target-textarea.html │ │ │ │ ├── dist/ │ │ │ │ │ └── clipboard.js │ │ │ │ ├── karma.conf.js │ │ │ │ ├── package.js │ │ │ │ ├── package.json │ │ │ │ ├── readme.md │ │ │ │ ├── src/ │ │ │ │ │ ├── clipboard-action.js │ │ │ │ │ └── clipboard.js │ │ │ │ └── test/ │ │ │ │ ├── clipboard-action.js │ │ │ │ └── clipboard.js │ │ │ ├── jQuery-File-Upload-9.18.0/ │ │ │ │ ├── .gitignore │ │ │ │ ├── .jshintrc │ │ │ │ ├── .npmignore │ │ │ │ ├── CONTRIBUTING.md │ │ │ │ ├── LICENSE │ │ │ │ ├── README.md │ │ │ │ ├── angularjs.html │ │ │ │ ├── basic-plus.html │ │ │ │ ├── basic.html │ │ │ │ ├── bower-version-update.js │ │ │ │ ├── bower.json │ │ │ │ ├── cors/ │ │ │ │ │ ├── postmessage.html │ │ │ │ │ └── result.html │ │ │ │ ├── css/ │ │ │ │ │ ├── demo-ie8.css │ │ │ │ │ ├── demo.css │ │ │ │ │ ├── jquery.fileupload-noscript.css │ │ │ │ │ ├── jquery.fileupload-ui-noscript.css │ │ │ │ │ ├── jquery.fileupload-ui.css │ │ │ │ │ ├── jquery.fileupload.css │ │ │ │ │ └── style.css │ │ │ │ ├── index.html │ │ │ │ ├── jquery-ui.html │ │ │ │ ├── js/ │ │ │ │ │ ├── app.js │ │ │ │ │ ├── cors/ │ │ │ │ │ │ ├── jquery.postmessage-transport.js │ │ │ │ │ │ └── jquery.xdr-transport.js │ │ │ │ │ ├── jquery.fileupload-angular.js │ │ │ │ │ ├── jquery.fileupload-audio.js │ │ │ │ │ ├── jquery.fileupload-image.js │ │ │ │ │ ├── jquery.fileupload-jquery-ui.js │ │ │ │ │ ├── jquery.fileupload-process.js │ │ │ │ │ ├── jquery.fileupload-ui.js │ │ │ │ │ ├── jquery.fileupload-validate.js │ │ │ │ │ ├── jquery.fileupload-video.js │ │ │ │ │ ├── jquery.fileupload.js │ │ │ │ │ ├── jquery.iframe-transport.js │ │ │ │ │ ├── main.js │ │ │ │ │ └── vendor/ │ │ │ │ │ └── jquery.ui.widget.js │ │ │ │ ├── package.json │ │ │ │ ├── server/ │ │ │ │ │ ├── gae-go/ │ │ │ │ │ │ ├── app/ │ │ │ │ │ │ │ └── main.go │ │ │ │ │ │ ├── app.yaml │ │ │ │ │ │ └── static/ │ │ │ │ │ │ └── robots.txt │ │ │ │ │ ├── gae-python/ │ │ │ │ │ │ ├── app.yaml │ │ │ │ │ │ ├── main.py │ │ │ │ │ │ └── static/ │ │ │ │ │ │ └── robots.txt │ │ │ │ │ └── php/ │ │ │ │ │ ├── Dockerfile │ │ │ │ │ ├── UploadHandler.php │ │ │ │ │ ├── docker-compose.yml │ │ │ │ │ ├── files/ │ │ │ │ │ │ ├── .gitignore │ │ │ │ │ │ └── .htaccess │ │ │ │ │ └── index.php │ │ │ │ └── test/ │ │ │ │ ├── index.html │ │ │ │ └── test.js │ │ │ └── moment-2.18.1/ │ │ │ ├── .editorconfig │ │ │ ├── .gitattributes │ │ │ ├── .gitignore │ │ │ ├── .jscs.json │ │ │ ├── .jshintrc │ │ │ ├── .npmignore │ │ │ ├── .spmignore │ │ │ ├── .travis.yml │ │ │ ├── CHANGELOG.md │ │ │ ├── CONTRIBUTING.md │ │ │ ├── Gruntfile.js │ │ │ ├── ISSUE_TEMPLATE.md │ │ │ ├── LICENSE │ │ │ ├── Moment.js.nuspec │ │ │ ├── README.md │ │ │ ├── benchmarks/ │ │ │ │ ├── add.js │ │ │ │ ├── clone.js │ │ │ │ ├── endOf.js │ │ │ │ ├── fromDate.js │ │ │ │ ├── fromDateUtc.js │ │ │ │ ├── makeDuration.js │ │ │ │ ├── query.js │ │ │ │ ├── startOf.js │ │ │ │ ├── subtract.js │ │ │ │ └── zeroFill.js │ │ │ ├── bower.json │ │ │ ├── component.json │ │ │ ├── composer.json │ │ │ ├── ender.js │ │ │ ├── locale/ │ │ │ │ ├── af.js │ │ │ │ ├── ar-dz.js │ │ │ │ ├── ar-kw.js │ │ │ │ ├── ar-ly.js │ │ │ │ ├── ar-ma.js │ │ │ │ ├── ar-sa.js │ │ │ │ ├── ar-tn.js │ │ │ │ ├── ar.js │ │ │ │ ├── az.js │ │ │ │ ├── be.js │ │ │ │ ├── bg.js │ │ │ │ ├── bn.js │ │ │ │ ├── bo.js │ │ │ │ ├── br.js │ │ │ │ ├── bs.js │ │ │ │ ├── ca.js │ │ │ │ ├── cs.js │ │ │ │ ├── cv.js │ │ │ │ ├── cy.js │ │ │ │ ├── da.js │ │ │ │ ├── de-at.js │ │ │ │ ├── de-ch.js │ │ │ │ ├── de.js │ │ │ │ ├── dv.js │ │ │ │ ├── el.js │ │ │ │ ├── en-au.js │ │ │ │ ├── en-ca.js │ │ │ │ ├── en-gb.js │ │ │ │ ├── en-ie.js │ │ │ │ ├── en-nz.js │ │ │ │ ├── eo.js │ │ │ │ ├── es-do.js │ │ │ │ ├── es.js │ │ │ │ ├── et.js │ │ │ │ ├── eu.js │ │ │ │ ├── fa.js │ │ │ │ ├── fi.js │ │ │ │ ├── fo.js │ │ │ │ ├── fr-ca.js │ │ │ │ ├── fr-ch.js │ │ │ │ ├── fr.js │ │ │ │ ├── fy.js │ │ │ │ ├── gd.js │ │ │ │ ├── gl.js │ │ │ │ ├── gom-latn.js │ │ │ │ ├── he.js │ │ │ │ ├── hi.js │ │ │ │ ├── hr.js │ │ │ │ ├── hu.js │ │ │ │ ├── hy-am.js │ │ │ │ ├── id.js │ │ │ │ ├── is.js │ │ │ │ ├── it.js │ │ │ │ ├── ja.js │ │ │ │ ├── jv.js │ │ │ │ ├── ka.js │ │ │ │ ├── kk.js │ │ │ │ ├── km.js │ │ │ │ ├── kn.js │ │ │ │ ├── ko.js │ │ │ │ ├── ky.js │ │ │ │ ├── lb.js │ │ │ │ ├── lo.js │ │ │ │ ├── lt.js │ │ │ │ ├── lv.js │ │ │ │ ├── me.js │ │ │ │ ├── mi.js │ │ │ │ ├── mk.js │ │ │ │ ├── ml.js │ │ │ │ ├── mr.js │ │ │ │ ├── ms-my.js │ │ │ │ ├── ms.js │ │ │ │ ├── my.js │ │ │ │ ├── nb.js │ │ │ │ ├── ne.js │ │ │ │ ├── nl-be.js │ │ │ │ ├── nl.js │ │ │ │ ├── nn.js │ │ │ │ ├── pa-in.js │ │ │ │ ├── pl.js │ │ │ │ ├── pt-br.js │ │ │ │ ├── pt.js │ │ │ │ ├── ro.js │ │ │ │ ├── ru.js │ │ │ │ ├── sd.js │ │ │ │ ├── se.js │ │ │ │ ├── si.js │ │ │ │ ├── sk.js │ │ │ │ ├── sl.js │ │ │ │ ├── sq.js │ │ │ │ ├── sr-cyrl.js │ │ │ │ ├── sr.js │ │ │ │ ├── ss.js │ │ │ │ ├── sv.js │ │ │ │ ├── sw.js │ │ │ │ ├── ta.js │ │ │ │ ├── te.js │ │ │ │ ├── tet.js │ │ │ │ ├── th.js │ │ │ │ ├── tl-ph.js │ │ │ │ ├── tlh.js │ │ │ │ ├── tr.js │ │ │ │ ├── tzl.js │ │ │ │ ├── tzm-latn.js │ │ │ │ ├── tzm.js │ │ │ │ ├── uk.js │ │ │ │ ├── ur.js │ │ │ │ ├── uz-latn.js │ │ │ │ ├── uz.js │ │ │ │ ├── vi.js │ │ │ │ ├── x-pseudo.js │ │ │ │ ├── yo.js │ │ │ │ ├── zh-cn.js │ │ │ │ ├── zh-hk.js │ │ │ │ └── zh-tw.js │ │ │ ├── meteor/ │ │ │ │ ├── README.md │ │ │ │ ├── export.js │ │ │ │ ├── moment.js │ │ │ │ ├── package.js │ │ │ │ └── test.js │ │ │ ├── min/ │ │ │ │ ├── locales.js │ │ │ │ ├── moment-with-locales.js │ │ │ │ └── tests.js │ │ │ ├── moment.d.ts │ │ │ ├── moment.js │ │ │ ├── package.js │ │ │ ├── package.json │ │ │ ├── scripts/ │ │ │ │ ├── locales.js │ │ │ │ └── npm_prepublish.sh │ │ │ ├── src/ │ │ │ │ ├── lib/ │ │ │ │ │ ├── create/ │ │ │ │ │ │ ├── check-overflow.js │ │ │ │ │ │ ├── date-from-array.js │ │ │ │ │ │ ├── from-anything.js │ │ │ │ │ │ ├── from-array.js │ │ │ │ │ │ ├── from-object.js │ │ │ │ │ │ ├── from-string-and-array.js │ │ │ │ │ │ ├── from-string-and-format.js │ │ │ │ │ │ ├── from-string.js │ │ │ │ │ │ ├── local.js │ │ │ │ │ │ ├── parsing-flags.js │ │ │ │ │ │ ├── utc.js │ │ │ │ │ │ └── valid.js │ │ │ │ │ ├── duration/ │ │ │ │ │ │ ├── abs.js │ │ │ │ │ │ ├── add-subtract.js │ │ │ │ │ │ ├── as.js │ │ │ │ │ │ ├── bubble.js │ │ │ │ │ │ ├── constructor.js │ │ │ │ │ │ ├── create.js │ │ │ │ │ │ ├── duration.js │ │ │ │ │ │ ├── get.js │ │ │ │ │ │ ├── humanize.js │ │ │ │ │ │ ├── iso-string.js │ │ │ │ │ │ ├── prototype.js │ │ │ │ │ │ └── valid.js │ │ │ │ │ ├── format/ │ │ │ │ │ │ └── format.js │ │ │ │ │ ├── locale/ │ │ │ │ │ │ ├── base-config.js │ │ │ │ │ │ ├── calendar.js │ │ │ │ │ │ ├── constructor.js │ │ │ │ │ │ ├── en.js │ │ │ │ │ │ ├── formats.js │ │ │ │ │ │ ├── invalid.js │ │ │ │ │ │ ├── lists.js │ │ │ │ │ │ ├── locale.js │ │ │ │ │ │ ├── locales.js │ │ │ │ │ │ ├── ordinal.js │ │ │ │ │ │ ├── pre-post-format.js │ │ │ │ │ │ ├── prototype.js │ │ │ │ │ │ ├── relative.js │ │ │ │ │ │ └── set.js │ │ │ │ │ ├── moment/ │ │ │ │ │ │ ├── add-subtract.js │ │ │ │ │ │ ├── calendar.js │ │ │ │ │ │ ├── clone.js │ │ │ │ │ │ ├── compare.js │ │ │ │ │ │ ├── constructor.js │ │ │ │ │ │ ├── creation-data.js │ │ │ │ │ │ ├── diff.js │ │ │ │ │ │ ├── format.js │ │ │ │ │ │ ├── from.js │ │ │ │ │ │ ├── get-set.js │ │ │ │ │ │ ├── locale.js │ │ │ │ │ │ ├── min-max.js │ │ │ │ │ │ ├── moment.js │ │ │ │ │ │ ├── now.js │ │ │ │ │ │ ├── prototype.js │ │ │ │ │ │ ├── start-end-of.js │ │ │ │ │ │ ├── to-type.js │ │ │ │ │ │ ├── to.js │ │ │ │ │ │ └── valid.js │ │ │ │ │ ├── parse/ │ │ │ │ │ │ ├── regex.js │ │ │ │ │ │ └── token.js │ │ │ │ │ ├── units/ │ │ │ │ │ │ ├── aliases.js │ │ │ │ │ │ ├── constants.js │ │ │ │ │ │ ├── day-of-month.js │ │ │ │ │ │ ├── day-of-week.js │ │ │ │ │ │ ├── day-of-year.js │ │ │ │ │ │ ├── hour.js │ │ │ │ │ │ ├── millisecond.js │ │ │ │ │ │ ├── minute.js │ │ │ │ │ │ ├── month.js │ │ │ │ │ │ ├── offset.js │ │ │ │ │ │ ├── priorities.js │ │ │ │ │ │ ├── quarter.js │ │ │ │ │ │ ├── second.js │ │ │ │ │ │ ├── timestamp.js │ │ │ │ │ │ ├── timezone.js │ │ │ │ │ │ ├── units.js │ │ │ │ │ │ ├── week-calendar-utils.js │ │ │ │ │ │ ├── week-year.js │ │ │ │ │ │ ├── week.js │ │ │ │ │ │ └── year.js │ │ │ │ │ └── utils/ │ │ │ │ │ ├── abs-ceil.js │ │ │ │ │ ├── abs-floor.js │ │ │ │ │ ├── abs-round.js │ │ │ │ │ ├── compare-arrays.js │ │ │ │ │ ├── defaults.js │ │ │ │ │ ├── deprecate.js │ │ │ │ │ ├── extend.js │ │ │ │ │ ├── has-own-prop.js │ │ │ │ │ ├── hooks.js │ │ │ │ │ ├── index-of.js │ │ │ │ │ ├── is-array.js │ │ │ │ │ ├── is-date.js │ │ │ │ │ ├── is-function.js │ │ │ │ │ ├── is-number.js │ │ │ │ │ ├── is-object-empty.js │ │ │ │ │ ├── is-object.js │ │ │ │ │ ├── is-undefined.js │ │ │ │ │ ├── keys.js │ │ │ │ │ ├── map.js │ │ │ │ │ ├── some.js │ │ │ │ │ ├── to-int.js │ │ │ │ │ └── zero-fill.js │ │ │ │ ├── locale/ │ │ │ │ │ ├── af.js │ │ │ │ │ ├── ar-dz.js │ │ │ │ │ ├── ar-kw.js │ │ │ │ │ ├── ar-ly.js │ │ │ │ │ ├── ar-ma.js │ │ │ │ │ ├── ar-sa.js │ │ │ │ │ ├── ar-tn.js │ │ │ │ │ ├── ar.js │ │ │ │ │ ├── az.js │ │ │ │ │ ├── be.js │ │ │ │ │ ├── bg.js │ │ │ │ │ ├── bn.js │ │ │ │ │ ├── bo.js │ │ │ │ │ ├── br.js │ │ │ │ │ ├── bs.js │ │ │ │ │ ├── ca.js │ │ │ │ │ ├── cs.js │ │ │ │ │ ├── cv.js │ │ │ │ │ ├── cy.js │ │ │ │ │ ├── da.js │ │ │ │ │ ├── de-at.js │ │ │ │ │ ├── de-ch.js │ │ │ │ │ ├── de.js │ │ │ │ │ ├── dv.js │ │ │ │ │ ├── el.js │ │ │ │ │ ├── en-au.js │ │ │ │ │ ├── en-ca.js │ │ │ │ │ ├── en-gb.js │ │ │ │ │ ├── en-ie.js │ │ │ │ │ ├── en-nz.js │ │ │ │ │ ├── eo.js │ │ │ │ │ ├── es-do.js │ │ │ │ │ ├── es.js │ │ │ │ │ ├── et.js │ │ │ │ │ ├── eu.js │ │ │ │ │ ├── fa.js │ │ │ │ │ ├── fi.js │ │ │ │ │ ├── fo.js │ │ │ │ │ ├── fr-ca.js │ │ │ │ │ ├── fr-ch.js │ │ │ │ │ ├── fr.js │ │ │ │ │ ├── fy.js │ │ │ │ │ ├── gd.js │ │ │ │ │ ├── gl.js │ │ │ │ │ ├── gom-latn.js │ │ │ │ │ ├── he.js │ │ │ │ │ ├── hi.js │ │ │ │ │ ├── hr.js │ │ │ │ │ ├── hu.js │ │ │ │ │ ├── hy-am.js │ │ │ │ │ ├── id.js │ │ │ │ │ ├── is.js │ │ │ │ │ ├── it.js │ │ │ │ │ ├── ja.js │ │ │ │ │ ├── jv.js │ │ │ │ │ ├── ka.js │ │ │ │ │ ├── kk.js │ │ │ │ │ ├── km.js │ │ │ │ │ ├── kn.js │ │ │ │ │ ├── ko.js │ │ │ │ │ ├── ky.js │ │ │ │ │ ├── lb.js │ │ │ │ │ ├── lo.js │ │ │ │ │ ├── lt.js │ │ │ │ │ ├── lv.js │ │ │ │ │ ├── me.js │ │ │ │ │ ├── mi.js │ │ │ │ │ ├── mk.js │ │ │ │ │ ├── ml.js │ │ │ │ │ ├── mr.js │ │ │ │ │ ├── ms-my.js │ │ │ │ │ ├── ms.js │ │ │ │ │ ├── my.js │ │ │ │ │ ├── nb.js │ │ │ │ │ ├── ne.js │ │ │ │ │ ├── nl-be.js │ │ │ │ │ ├── nl.js │ │ │ │ │ ├── nn.js │ │ │ │ │ ├── pa-in.js │ │ │ │ │ ├── pl.js │ │ │ │ │ ├── pt-br.js │ │ │ │ │ ├── pt.js │ │ │ │ │ ├── ro.js │ │ │ │ │ ├── ru.js │ │ │ │ │ ├── sd.js │ │ │ │ │ ├── se.js │ │ │ │ │ ├── si.js │ │ │ │ │ ├── sk.js │ │ │ │ │ ├── sl.js │ │ │ │ │ ├── sq.js │ │ │ │ │ ├── sr-cyrl.js │ │ │ │ │ ├── sr.js │ │ │ │ │ ├── ss.js │ │ │ │ │ ├── sv.js │ │ │ │ │ ├── sw.js │ │ │ │ │ ├── ta.js │ │ │ │ │ ├── te.js │ │ │ │ │ ├── tet.js │ │ │ │ │ ├── th.js │ │ │ │ │ ├── tl-ph.js │ │ │ │ │ ├── tlh.js │ │ │ │ │ ├── tr.js │ │ │ │ │ ├── tzl.js │ │ │ │ │ ├── tzm-latn.js │ │ │ │ │ ├── tzm.js │ │ │ │ │ ├── uk.js │ │ │ │ │ ├── ur.js │ │ │ │ │ ├── uz-latn.js │ │ │ │ │ ├── uz.js │ │ │ │ │ ├── vi.js │ │ │ │ │ ├── x-pseudo.js │ │ │ │ │ ├── yo.js │ │ │ │ │ ├── zh-cn.js │ │ │ │ │ ├── zh-hk.js │ │ │ │ │ └── zh-tw.js │ │ │ │ ├── moment.js │ │ │ │ └── test/ │ │ │ │ ├── helpers/ │ │ │ │ │ ├── common-locale.js │ │ │ │ │ ├── deprecation-handler.js │ │ │ │ │ ├── dst.js │ │ │ │ │ ├── each.js │ │ │ │ │ └── object-keys.js │ │ │ │ ├── locale/ │ │ │ │ │ ├── af.js │ │ │ │ │ ├── ar-dz.js │ │ │ │ │ ├── ar-kw.js │ │ │ │ │ ├── ar-ly.js │ │ │ │ │ ├── ar-ma.js │ │ │ │ │ ├── ar-sa.js │ │ │ │ │ ├── ar-tn.js │ │ │ │ │ ├── ar.js │ │ │ │ │ ├── az.js │ │ │ │ │ ├── be.js │ │ │ │ │ ├── bg.js │ │ │ │ │ ├── bn.js │ │ │ │ │ ├── bo.js │ │ │ │ │ ├── br.js │ │ │ │ │ ├── bs.js │ │ │ │ │ ├── ca.js │ │ │ │ │ ├── cs.js │ │ │ │ │ ├── cv.js │ │ │ │ │ ├── cy.js │ │ │ │ │ ├── da.js │ │ │ │ │ ├── de-at.js │ │ │ │ │ ├── de-ch.js │ │ │ │ │ ├── de.js │ │ │ │ │ ├── dv.js │ │ │ │ │ ├── el.js │ │ │ │ │ ├── en-au.js │ │ │ │ │ ├── en-ca.js │ │ │ │ │ ├── en-gb.js │ │ │ │ │ ├── en-ie.js │ │ │ │ │ ├── en-nz.js │ │ │ │ │ ├── en.js │ │ │ │ │ ├── eo.js │ │ │ │ │ ├── es-do.js │ │ │ │ │ ├── es.js │ │ │ │ │ ├── et.js │ │ │ │ │ ├── eu.js │ │ │ │ │ ├── fa.js │ │ │ │ │ ├── fi.js │ │ │ │ │ ├── fo.js │ │ │ │ │ ├── fr-ca.js │ │ │ │ │ ├── fr-ch.js │ │ │ │ │ ├── fr.js │ │ │ │ │ ├── fy.js │ │ │ │ │ ├── gd.js │ │ │ │ │ ├── gl.js │ │ │ │ │ ├── gom-latn.js │ │ │ │ │ ├── he.js │ │ │ │ │ ├── hi.js │ │ │ │ │ ├── hr.js │ │ │ │ │ ├── hu.js │ │ │ │ │ ├── hy-am.js │ │ │ │ │ ├── id.js │ │ │ │ │ ├── is.js │ │ │ │ │ ├── it.js │ │ │ │ │ ├── ja.js │ │ │ │ │ ├── jv.js │ │ │ │ │ ├── ka.js │ │ │ │ │ ├── kk.js │ │ │ │ │ ├── km.js │ │ │ │ │ ├── kn.js │ │ │ │ │ ├── ko.js │ │ │ │ │ ├── ky.js │ │ │ │ │ ├── lb.js │ │ │ │ │ ├── lo.js │ │ │ │ │ ├── lt.js │ │ │ │ │ ├── lv.js │ │ │ │ │ ├── me.js │ │ │ │ │ ├── mi.js │ │ │ │ │ ├── mk.js │ │ │ │ │ ├── ml.js │ │ │ │ │ ├── mr.js │ │ │ │ │ ├── ms-my.js │ │ │ │ │ ├── ms.js │ │ │ │ │ ├── my.js │ │ │ │ │ ├── nb.js │ │ │ │ │ ├── ne.js │ │ │ │ │ ├── nl-be.js │ │ │ │ │ ├── nl.js │ │ │ │ │ ├── nn.js │ │ │ │ │ ├── pa-in.js │ │ │ │ │ ├── pl.js │ │ │ │ │ ├── pt-br.js │ │ │ │ │ ├── pt.js │ │ │ │ │ ├── ro.js │ │ │ │ │ ├── ru.js │ │ │ │ │ ├── sd.js │ │ │ │ │ ├── se.js │ │ │ │ │ ├── si.js │ │ │ │ │ ├── sk.js │ │ │ │ │ ├── sl.js │ │ │ │ │ ├── sq.js │ │ │ │ │ ├── sr-cyrl.js │ │ │ │ │ ├── sr.js │ │ │ │ │ ├── ss.js │ │ │ │ │ ├── sv.js │ │ │ │ │ ├── sw.js │ │ │ │ │ ├── ta.js │ │ │ │ │ ├── te.js │ │ │ │ │ ├── tet.js │ │ │ │ │ ├── th.js │ │ │ │ │ ├── tl-ph.js │ │ │ │ │ ├── tlh.js │ │ │ │ │ ├── tr.js │ │ │ │ │ ├── tzl.js │ │ │ │ │ ├── tzm-latn.js │ │ │ │ │ ├── tzm.js │ │ │ │ │ ├── uk.js │ │ │ │ │ ├── ur.js │ │ │ │ │ ├── uz-latn.js │ │ │ │ │ ├── uz.js │ │ │ │ │ ├── vi.js │ │ │ │ │ ├── x-pseudo.js │ │ │ │ │ ├── yo.js │ │ │ │ │ ├── zh-cn.js │ │ │ │ │ ├── zh-hk.js │ │ │ │ │ └── zh-tw.js │ │ │ │ ├── moment/ │ │ │ │ │ ├── add_subtract.js │ │ │ │ │ ├── calendar.js │ │ │ │ │ ├── create.js │ │ │ │ │ ├── creation-data.js │ │ │ │ │ ├── days_in_month.js │ │ │ │ │ ├── days_in_year.js │ │ │ │ │ ├── deprecate.js │ │ │ │ │ ├── diff.js │ │ │ │ │ ├── duration.js │ │ │ │ │ ├── duration_from_moments.js │ │ │ │ │ ├── duration_invalid.js │ │ │ │ │ ├── format.js │ │ │ │ │ ├── from_to.js │ │ │ │ │ ├── getters_setters.js │ │ │ │ │ ├── instanceof.js │ │ │ │ │ ├── invalid.js │ │ │ │ │ ├── is_after.js │ │ │ │ │ ├── is_array.js │ │ │ │ │ ├── is_before.js │ │ │ │ │ ├── is_between.js │ │ │ │ │ ├── is_date.js │ │ │ │ │ ├── is_moment.js │ │ │ │ │ ├── is_number.js │ │ │ │ │ ├── is_same.js │ │ │ │ │ ├── is_same_or_after.js │ │ │ │ │ ├── is_same_or_before.js │ │ │ │ │ ├── is_valid.js │ │ │ │ │ ├── leapyear.js │ │ │ │ │ ├── listers.js │ │ │ │ │ ├── locale.js │ │ │ │ │ ├── locale_inheritance.js │ │ │ │ │ ├── locale_update.js │ │ │ │ │ ├── min_max.js │ │ │ │ │ ├── mutable.js │ │ │ │ │ ├── normalize_units.js │ │ │ │ │ ├── now.js │ │ │ │ │ ├── parsing_flags.js │ │ │ │ │ ├── preparse_postformat.js │ │ │ │ │ ├── quarter.js │ │ │ │ │ ├── relative_time.js │ │ │ │ │ ├── start_end_of.js │ │ │ │ │ ├── string_prototype.js │ │ │ │ │ ├── to_type.js │ │ │ │ │ ├── utc.js │ │ │ │ │ ├── utc_offset.js │ │ │ │ │ ├── week_year.js │ │ │ │ │ ├── weekday.js │ │ │ │ │ ├── weeks.js │ │ │ │ │ ├── weeks_in_year.js │ │ │ │ │ ├── zone_switching.js │ │ │ │ │ └── zones.js │ │ │ │ └── qunit.js │ │ │ ├── tasks/ │ │ │ │ ├── bump_version.js │ │ │ │ ├── check_sauce_creds.js │ │ │ │ ├── component.js │ │ │ │ ├── nuget.js │ │ │ │ ├── qtest.js │ │ │ │ ├── transpile.js │ │ │ │ └── update_index.js │ │ │ ├── templates/ │ │ │ │ ├── default.js │ │ │ │ ├── locale-header.js │ │ │ │ └── test-header.js │ │ │ └── typing-tests/ │ │ │ ├── moment-tests.ts │ │ │ └── tsconfig.json │ │ ├── robots.txt │ │ ├── theme/ │ │ │ └── default/ │ │ │ ├── css/ │ │ │ │ ├── animate.css │ │ │ │ ├── footer-1.css │ │ │ │ ├── header-1.css │ │ │ │ ├── magnific-popup.css │ │ │ │ └── style.css │ │ │ ├── fonts/ │ │ │ │ └── FontAwesome.otf │ │ │ ├── js/ │ │ │ │ ├── cubeportfolio/ │ │ │ │ │ ├── blog-grid-3col.js │ │ │ │ │ ├── portfolio-masonry-4col.js │ │ │ │ │ ├── team.js │ │ │ │ │ └── testimonials.js │ │ │ │ ├── custom.js │ │ │ │ ├── google-map.js │ │ │ │ ├── index.js │ │ │ │ └── navigation.js │ │ │ └── plugins/ │ │ │ ├── FlexSlider/ │ │ │ │ ├── jquery.flexslider-min.js │ │ │ │ └── jquery.flexslider.css │ │ │ └── owl-carousel/ │ │ │ ├── owl.carousel.css │ │ │ └── owl.theme.css │ │ └── uploads/ │ │ └── index.html │ ├── tasks/ │ │ ├── README.md │ │ ├── __init__.py │ │ ├── cron_send_sms.py │ │ ├── run_apply_put_interest_on_principal.py │ │ ├── run_lock_active_not_put.py │ │ ├── run_lock_order_not_pay.py │ │ ├── run_lock_pay_not_rec.py │ │ ├── run_lock_reg_not_active.py │ │ ├── run_send_sms.py │ │ └── run_send_sms_priority.py │ ├── templates/ │ │ ├── 404.html │ │ ├── 500.html │ │ ├── _footer.html │ │ ├── _top.html │ │ ├── about.html │ │ ├── about_bak.html │ │ ├── active/ │ │ │ ├── add.html │ │ │ └── list.html │ │ ├── apply/ │ │ │ ├── get_add.html │ │ │ ├── get_edit.html │ │ │ ├── get_list.html │ │ │ ├── put_add.html │ │ │ ├── put_edit.html │ │ │ └── put_list.html │ │ ├── auth/ │ │ │ ├── _forget_password.html │ │ │ ├── _login_three_part.html │ │ │ ├── email.html │ │ │ ├── index.html │ │ │ └── phone.html │ │ ├── bit_coin/ │ │ │ └── list.html │ │ ├── blog/ │ │ │ ├── add.html │ │ │ ├── edit.html │ │ │ ├── hot.html │ │ │ ├── index.html │ │ │ ├── list_edit.html │ │ │ └── new.html │ │ ├── complaint/ │ │ │ ├── add.html │ │ │ └── list.html │ │ ├── contact.html │ │ ├── credit/ │ │ │ └── index.html │ │ ├── email.html │ │ ├── index.html │ │ ├── layout.html │ │ ├── logout.html │ │ ├── macros.html │ │ ├── message/ │ │ │ ├── add.html │ │ │ └── list.html │ │ ├── order/ │ │ │ ├── get_info.html │ │ │ ├── get_list.html │ │ │ ├── pay.html │ │ │ ├── put_info.html │ │ │ └── put_list.html │ │ ├── reg/ │ │ │ ├── _agreement.html │ │ │ ├── _agreement_en.html │ │ │ ├── email.html │ │ │ ├── index.html │ │ │ └── phone.html │ │ ├── scheduling/ │ │ │ └── list.html │ │ ├── score/ │ │ │ ├── charity_list.html │ │ │ ├── digital_list.html │ │ │ └── expense_list.html │ │ ├── setting.html │ │ ├── site/ │ │ │ └── index.html │ │ ├── theme/ │ │ │ └── default/ │ │ │ ├── _footer.html │ │ │ ├── _header.html │ │ │ ├── index.html │ │ │ └── layout.html │ │ ├── uploads.html │ │ ├── user/ │ │ │ ├── _account_info.html │ │ │ ├── _invite_link.html │ │ │ ├── _status.html │ │ │ ├── _team_tree.html │ │ │ ├── account.html │ │ │ ├── auth.html │ │ │ ├── bank.html │ │ │ ├── notification.html │ │ │ ├── profile.html │ │ │ └── team.html │ │ └── wallet/ │ │ └── list.html │ ├── tools/ │ │ ├── __init__.py │ │ ├── config_manage.py │ │ ├── db.py │ │ ├── db_test_mysql.py │ │ ├── db_test_sqlite.py │ │ ├── decorators.py │ │ ├── exception.py │ │ ├── file.py │ │ ├── send_sms.py │ │ ├── session_manage.py │ │ ├── stat.py │ │ ├── system.py │ │ └── url.py │ └── views/ │ ├── __init__.py │ ├── active.py │ ├── apply.py │ ├── auth.py │ ├── bit_coin.py │ ├── blog.py │ ├── captcha.py │ ├── complaint.py │ ├── credit.py │ ├── file.py │ ├── get.py │ ├── invite.py │ ├── message.py │ ├── order.py │ ├── pay.py │ ├── penetration.py │ ├── put.py │ ├── reg.py │ ├── scheduling.py │ ├── score.py │ ├── site.py │ ├── test.py │ ├── user.py │ ├── wallet.py │ └── wechat.py ├── backup/ │ ├── batch_import.sh │ ├── data_test.sql │ ├── db_dump.sh │ ├── db_init.sh │ ├── db_restore.sh │ ├── model_create.sh │ ├── run.sh │ ├── schema.dump.sql │ ├── schema.sql │ ├── test.py │ └── views_bak.py ├── changelog/ │ └── 2017-07-03/ │ └── mysql.sql ├── config/ │ ├── __init__.py │ ├── develop/ │ │ ├── __init__.py │ │ └── settings/ │ │ ├── __init__.py │ │ ├── apply.py │ │ ├── interest.py │ │ ├── order.py │ │ ├── sms.py │ │ └── user.py │ └── product/ │ ├── __init__.py │ └── settings/ │ ├── __init__.py │ ├── apply.py │ ├── interest.py │ ├── order.py │ ├── sms.py │ └── user.py ├── db/ │ ├── data/ │ │ └── mysql.sql │ ├── mysql.py │ ├── postgresql.py │ ├── schema/ │ │ ├── mysql.sql │ │ ├── postgresql.sql │ │ └── sqlite.sql │ └── sqlite.py ├── doc/ │ ├── BluePrint.md │ ├── Celery.md │ ├── Docker.md │ ├── DockerHub.md │ ├── ElasticSearch.md │ ├── Flask-SQLAlchemy.md │ ├── Flask-SSE.md │ ├── Flask-WTF.md │ ├── Flower.md │ ├── Git.md │ ├── Gunicorn.md │ ├── LightBox.md │ ├── MariaDB.md │ ├── MongoDB.md │ ├── OAuth.md │ ├── OpenPay.md │ ├── Redis.md │ ├── Siege.md │ ├── SqlAlchemy.md │ ├── UbuntuServer.md │ ├── WeiXin.md │ ├── bootstrap-select.md │ ├── cache.md │ ├── fabric.md │ ├── https.md │ ├── jQuery-File-Upload.md │ └── mirrors.md ├── docker/ │ ├── Dockerfile │ ├── README.md │ ├── docker_build.sh │ ├── docker_login.sh │ ├── docker_run.sh │ ├── mariadb/ │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── cli.sh │ │ ├── db_dump.sh │ │ ├── db_init.sh │ │ ├── docker_run_develop.sh │ │ ├── docker_run_product.sh │ │ └── env.sh │ ├── mongodb/ │ │ ├── README.md │ │ ├── cli.sh │ │ ├── docker_run.sh │ │ └── env.sh │ ├── nginx/ │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── conf/ │ │ │ ├── conf.d/ │ │ │ │ ├── default.conf │ │ │ │ ├── project.conf │ │ │ │ └── project_ssl.conf │ │ │ ├── nginx.conf │ │ │ └── ssl/ │ │ │ ├── ca.key │ │ │ ├── server.crt │ │ │ ├── server.csr │ │ │ ├── server.key │ │ │ └── v3.ext │ │ ├── create_ca.sh │ │ ├── create_crt.sh │ │ ├── create_csr.sh │ │ ├── create_key.sh │ │ └── docker_run.sh │ ├── rabbitmq/ │ │ ├── README.md │ │ ├── docker_run.sh │ │ └── docker_run_management.sh │ ├── redis/ │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── cli.sh │ │ ├── docker_run_aof.sh │ │ └── docker_run_rdb.sh │ └── requirements.txt ├── docker-compose.yml ├── env_develop.sh ├── env_product.sh ├── etc/ │ ├── app_backend.ini │ ├── app_frontend.ini │ ├── app_wechat.ini │ ├── cron_send_sms.ini │ ├── nginx.conf │ ├── nginx_ssl.conf │ ├── supervisord.conf │ ├── task_lock.ini │ └── task_sms.ini ├── gen.py ├── project/ │ ├── doc.md │ ├── run_develop.md │ └── run_product.md ├── requirements.txt ├── run_backend.py ├── run_frontend.py ├── service_start.md └── ssl/ ├── server.crt ├── server.csr ├── server.key └── server.key.org ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Created by .ignore support plugin (hsz.mobi) ### Python template *.env *.pyc #*.log .idea/ *.DS_Store app_frontend/static/uploads/* !app_frontend/static/uploads/index.html # 数据 docker/mariadb/backup/* # 日志 app_wechat/ db/backup/ docker/mariadb/backup/ docker/mariadb/data/ docker/mongodb/data/ docker/nginx/log/ docker/rabbitmq/data/ docker/redis/data/ logs/ ================================================ FILE: README.md ================================================ ## Flask 项目模拟 - product 生产环境 - develop 开发环境 ### 项目演示步骤 虚拟环境 部署方式 ``` $ cd flask_project # 进入项目目录 $ virtualenv flask.env # 新建虚拟环境 $ source flask.env/bin/activate # 进入虚拟环境 $ pip install -r requirements.txt # 安装环境依赖 $ python run_frontend.py # 开启前台服务 $ python run_backend.py # 开启后台服务 ``` 也可以 docker 方式部署 ``` $ cd flask_project/docker # 进入Docker脚本目录 $ sh docker/docker_build.sh # 创建本地Docker镜像 $ sh docker/docker_run.sh # 运行本地Docker容器 ``` ### 安装 flask 以及扩展 创建并进入虚拟环境 ``` $ virtualenv flask.env $ source flask.env/bin/activate ``` 项目服务依赖 ``` Nginx Redis MariaDB RabbitMQ MongoDB ``` 项目扩展依赖 ``` $ pip install Flask $ pip install Flask-Login $ pip install Flask-Mail $ pip install Flask-SQLAlchemy $ pip install Flask-WTF $ pip install Flask-OAuthlib $ pip install Flask-Excel $ pip install Flask-Moment $ pip install Flask-Uploads $ pip install Flask-Principal $ pip install Flask-Babel $ pip install sqlacodegen $ pip install gunicorn $ pip install schedule $ pip install supervisor $ pip install redis $ pip install requests $ pip install celery $ pip install librabbitmq $ pip install pika $ pip install Pillow $ pip install sshtunnel $ pip install MySQL-python $ pip freeze > requirements.txt ``` 服务部署安装方式 ``` $ pip install -r requirements.txt ``` ### 创建目录结构 ``` $ mkdir app $ mkdir app/static $ mkdir app/templates $ mkdir tmp ``` 在 Pycharm 中设置 templates 目录 ``` app/templates 目录右键 >> Mark Directory As >> Template Folder File >> Settings >> Languages & Frameworks >> Python Template Languages >> Template Language: jinja2 ``` ### 引入 Bootstrap Bootstrap 是最受欢迎的 HTML、CSS 和 JS 框架,用于开发响应式布局、移动设备优先的 WEB 项目。 项目地址:https://github.com/twbs/bootstrap Bootstrap 英文官网:http://getbootstrap.com/ Bootstrap 中文网:http://www.bootcss.com/ Bootstrap CDN ``` ``` ### 启动 web 服务 ``` $ chmod a+x run.py $ ./run.py # 可以 Ctrl-C 来终止服务 ``` 浏览器访问: [http://localhost:5000](http://localhost:5000) [http://localhost:5000/index](http://localhost:5000/index) ### 如何生成强壮的密钥 ``` In [1]: import os In [2]: os.urandom(24) Out[2]: '\x03\xabjR\xbbg\x82\x0b{\x96f\xca\xa8\xbdM\xb0x\xdbK%\xf2\x07\r\x8c' ``` ### 创建数据库 模拟数据 ``` INSERT INTO author(name, email) VALUES('Mark', 'mark@gmail.com'); INSERT INTO author(name, email) VALUES('Jacob', 'jacob@gmail.com'); INSERT INTO author(name, email) VALUES('Larry', 'larry@gmail.com'); INSERT INTO author(name, email) VALUES('Tom', 'tom@gmail.com'); INSERT INTO author(name, email) VALUES('Lily', 'lily@gmail.com'); ``` ``` INSERT INTO blog(author, title, pub_date) VALUES('Mark', 'The old man and the sea', '2016-01-11 11:01:05'); INSERT INTO blog(author, title, pub_date) VALUES('Jacob', 'The fault in our stars', '2016-01-11 20:23:27'); INSERT INTO blog(author, title, pub_date) VALUES('Larry', 'The Great Gatsby', '2016-01-11 23:15:18'); INSERT INTO blog(author, title, pub_date) VALUES('Tom', 'Sense and Sensibility', '2016-01-12 12:25:34'); INSERT INTO blog(author, title, pub_date) VALUES('Tom', 'Pride and Prejudice', '2016-01-12 13:17:25'); INSERT INTO blog(author, title, pub_date) VALUES('Lily', 'Game of Thrones', '2016-01-12 14:53:01'); INSERT INTO blog(author, title, pub_date) VALUES('Mark', 'Charlie and the Chocolate Factory', '2016-01-12 15:13:17'); INSERT INTO blog(author, title, pub_date) VALUES('Larry', 'Harry Potter and the Sorcerer''s Stone', '2016-01-12 19:32:15'); INSERT INTO blog(author, title, pub_date) VALUES('Larry', 'The house on mango street', '2016-01-12 01:43:42'); INSERT INTO blog(author, title, pub_date) VALUES('Jacob', 'And then there were none', '2016-01-13 16:17:32'); ``` 注意:插入内容中如果存在半角单引号('),需要替换为2个单引号('') 前提是插入内容在两个单引号之间,存入数据库会自动转义为1个单引号 生成建表语句(备份) ``` $ sqlite3 flask.db ".dump" > schema.sql ``` 创建数据库(恢复): ``` $ sqlite3 flask.db < schema.sql ``` etc 目录下已经创建好脚本(初始化数据,备份数据) ``` $ chmod a+x db_init.sh $ chmod a+x db_dump.sh $ ./db_init.sh ``` ### SQLAlchemy python 的一种 ORM 框架 ORM:Object-Relational Mapping 把关系数据库的表结构映射到对象上 使用 Flask-SQLAlchemy 进行数据库管理 ``` Database engine URL --------------- --- MySQL mysql://username:password@hostname/database Postgres postgresql://username:password@hostname/database SQLite (Unix) sqlite:////absolute/path/to/database SQLite (Windows) sqlite:///c:/absolute/path/to/database ``` 最常用的 SQLAlchemy 列类型如下: ``` Type name Python Type Python type Description --------- ------- ----------------------- Integer int Integerint Regular integer, typically 32 bits SmallInteger int Short-range integer, typically 16 bits BigInteger int or long Unlimited precision integer Float float Floating-point number Numeric decimal.Decimal Fixed-point number String str Variable-length string Text str Variable-length string, optimized for large or unbound length Unicode unicode Variable-length Unicode string UnicodeText unicode Variable-length Unicode string, optimized for large or unbound length Boolean bool Boolean value Date datetime.date Date value Time datetime.time Time value DateTime datetime.datetime Date and time value Interval datetime.timedelta Time interval Enum str List of string values PickleType Any Python object Automatic Pickle serialization LargeBinary str Binary blob ``` 模型可以(没有强制要求)定义 __repr()__ 方法,返回一个具有可读性的字符串表示模型,可在调试和测试时使用。 最常见的 SQLAlchemy 选项: ``` Option name Description ----------- ----------- primary_key If set to True , the column is the table’s primary key. unique If set to True , do not allow duplicate values for this column. index If set to True , create an index for this column, so that queries are more efficient. nullable If set to True , allow empty values for this column. If set to False , the column will not allow null values. default Define a default value for the column. ``` 官方文档: [SQLAlchemy 1.0 Documentation](http://docs.sqlalchemy.org/en/rel_1_0/orm/tutorial.html) [常见的过滤器运算符 Common Filter Operators](http://docs.sqlalchemy.org/en/rel_1_0/orm/tutorial.html#common-filter-operators) [动态获取 Model 对象的主键](http://docs.sqlalchemy.org/en/latest/orm/internals.html#sqlalchemy.orm.state.InstanceState.identity) 打开自动提交 ``` from flask.ext.sqlalchemy import SQLAlchemy db = SQLAlchemy(session_options={'autocommit': True}) 后者: SQLALCHEMY_COMMIT_ON_TEARDOWN = True # 打开自动提交 ``` 联表(内联)分页查询 ``` paginate = User.query.join(UserProfile, User.id == UserProfile.user_id).add_entity(UserProfile).order_by(User.id.desc()).paginate(1, 10, False) print paginate.items for (user, user_profile) in paginate.items: print user.id, user_profile.user_id ``` 如果需要外联,join 替换为 outerjoin 即可 ### sqlacodegen 安装 ``` $ pip install sqlacodegen ``` 生成 model ``` $ sqlacodegen sqlite:///flask.db --outfile app/models.py ``` 参考例子: ``` $ sqlacodegen postgresql:///some_local_db $ sqlacodegen mysql+oursql://user:password@localhost/db_name $ sqlacodegen sqlite:///database.db $ sqlacodegen mysql://root:root@127.0.0.1:3306/db_name > models.py $ sqlacodegen mysql://root:root@127.0.0.1:3306/db_name --outfile models.py ``` 修改 models 文件: from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() 替换为: from flask.ext.sqlalchemy import SQLAlchemy from app import app db = SQLAlchemy(app) Base = db.Model [http://docs.sqlalchemy.org/en/latest/core/engines.html](http://docs.sqlalchemy.org/en/latest/core/engines.html) 注意: 添加选项 --noinflect 否则工具默认会将表名后缀s去掉 ### 存入数据库中文乱码 SQLALCHEMY_DATABASE_URI = 'mysql://[用户]:[密码]@[IP]/[库名]?charset=utf8' 注意是 utf8 ,不是 utf-8 > show variables like 'character%'; mysql 里的 charset 是 utf8 ### 关于分页 错误写法: ``` rows = db_session.query(Author).filter_by(**kwargs).paginate(page, per_page, False) ``` 报错如下: ``` AttributeError: 'Query' object has no attribute 'paginate' ``` 失败原因: ``` "Query" refers to the SQLAlchemy Query object. "BaseQuery" refers to the Flask-SQLALchemy BaseQuery object, which is a subclass of Query. This subclass includes helpers such as first_or_404() and paginate(). However, this means that a Query object does NOT have the paginate() function. How you actually build the object you are calling your "Query" object depends on whether you are dealing with a Query or BaseQuery object. ``` 参考:[http://stackoverflow.com/questions/18468887/flask-sqlalchemy-pagination-error](http://stackoverflow.com/questions/18468887/flask-sqlalchemy-pagination-error) 正确写法: ``` rows = Author.query.filter_by(**kwargs).paginate(page, per_page, False) ``` 模板中遍历 item,单页 item 序号用 loop.index 表示 ### 表单 WTForms 标准 HTML 表单域 ``` 表单域类型 描述 --------- ---- StringField 文本框 TextAreaField 多行文本框 PasswordField 密码输入框 HiddenField 隐藏文本框 DateField 接收给定格式datetime.date型的文本框 DateTimeField 接收给定格式datetime.datetime型的文本框 IntegerField 接收整型的文本框 DecimalField 接收decimal.Decimal型的文本框 FloadField 接收浮点型的文本框 BooleanField 带有True和False的复选框 RadioField 一组单选框 SelectField 下拉选择框 SelectMultipleField 下拉多选框 FieldField 文件上传框 SubmitField 表单提交按钮 FormField 将一个表单作为表单域嵌入到容器表单中 FieldList 给定类型的表单域列表 ``` WTForms 验证 ``` 验证程序 描述 ------- ---- Email 验证邮箱地址 EqualTo 比较两个域的值;在要求输入两次密码进行确认的时候非常有用 IPAddress 验证IPv4网络地址 Length 验证输入字符串的长度 NumberRange 验证输入的值在数值范围内 Optional 允许输入为空;忽略额外的验证 Required 验证表单域包含数据 Regexp 验证输入的正则表达式 URL 验证一个URL AnyOf 验证输入是一组可能值中的一个 NoneOf 验证输入不是一组可能值中的一个 ``` 自定义表单验证 参考官方文档:[http://wtforms.readthedocs.org/en/latest/validators.html#custom-validators](http://wtforms.readthedocs.org/en/latest/validators.html#custom-validators) 注意事项: NumberRange 校验只对数值类型(IntegerField,FloadField,DecimalField)生效;字符串类型(StringField)不生效 DataRequired, InputRequired 的区别 - DataRequired 检查类型转换后的值,是否为真(即 if field.data) - InputRequired 检查原始输入的值,是否为真 ### CSRF 跨站请求伪造 攻击方式: 利用用户身份执行非法请求(在用户浏览器端发起特定请求) 防御策略: 通常使用csrf_token防御 http://flask-wtf.readthedocs.io/en/stable/csrf.html Any view using FlaskForm to process the request is already getting CSRF protection. 也就是使用FlaskForm的表单默认都会受到保护 HTML Forms ```html
{{ form.csrf_token }}
``` ```html
``` JavaScript Requests ```html ``` ### Message Flashing 闪现消息 定义4种类型 success info warning danger 参考:[http://flask.pocoo.org/docs/0.10/patterns/flashing/#flashing-with-categories](http://flask.pocoo.org/docs/0.10/patterns/flashing/#flashing-with-categories) 如果需要自动关闭 ``` $("#success-alert").fadeTo(2000, 500).slideUp(500, function(){ $("#success-alert").slideUp(500); }); ``` ### 关于时间 python 中: timestamp=datetime.datetime.utcnow() sql 模式下: CURRENT_TIMESTAMP 均为 UTC 时间(非系统本地时间) 把时间设置为 UTC 时区,所有的存储在数据库里的时间将是 UTC 格式,用户可能在世界各地写微博,因此我们需要使用统一的时间单位。 另外,sqlite 并不支持像 mysql 有这种 DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 触发器的简单语法。 其实更新时间完全可以在程序中控制。 若缺省值是 CURRENT_TIME、CURRENT_DATE 或 CURRENT_TIMESTAMP,则当前 UTC 日期和/或时间被插入字段。 CURRENT_TIME 的格式为 “HH:MM:SS” CURRENT_DATE 为 “YYYY-MM-DD” 而 CURRENT_TIMESTAMP 是 “YYYY-MM-DD HH:MM:SS” 注意: 数据库表中的创建时间字段一般会设置缺省当前时间。 保存数据时,如果程序中,创建时间为空,创建时间字段存储的是数据库服务器的当前时间; 如果程序中直接设置好创建时间为当前时间,则保存的创建时间字段是代码服务器的当前时间。 生产环境通常 web 与 db 是分开的,时钟往往不同步。 不同开发人员程序中如果不统一的话,会造成麻烦。 Flask-Moment 本地化日期和时间 https://github.com/miguelgrinberg/Flask-Moment [http://momentjs.com/](http://momentjs.com/) 例子 ``` {{ moment().fromNow(refresh=True).format('LLLL') }} ``` 页面配置 本地加载: ``` ``` cdn方案: ``` {{ moment.include_moment() }} {{ moment.locale('zh-cn') }} ``` ### 用户登陆 Flask-Login 扩展需要在我们 model 的 User 类里实现一些方法。: def is_authenticated(self): return True def is_active(self): return True def is_anonymous(self): return False def get_id(self): try: return unicode(self.id) # python 2 except NameError: return str(self.id) # python 3 [https://flask-login.readthedocs.io/en/latest/](https://flask-login.readthedocs.io/en/latest/) ### 用 jQuery 实现 Ajax [http://www.pythondoc.com/flask/patterns/jquery.html](http://www.pythondoc.com/flask/patterns/jquery.html) ### Bootstrap 轮播(Carousel)插件 [http://www.runoob.com/bootstrap/bootstrap-carousel-plugin.html](http://www.runoob.com/bootstrap/bootstrap-carousel-plugin.html) ### 轮播大图样式 - 选择 宽度1920 高度610 的高画质图片 - 主要图像内容信息集中在中间1024的区域 ### Swiper 移动端触摸滑动插件 [http://www.swiper.com.cn/](http://www.swiper.com.cn/) ### Slideout.js 侧滑插件 [https://github.com/mango/slideout](https://github.com/mango/slideout) ### LightBox 插件 参考: [LightBox.md](doc/LightBox.md) ### Chart.js 图表插件 [http://www.bootcss.com/p/chart.js/docs/](http://www.bootcss.com/p/chart.js/docs/) 响应式辅助参数: responsive: true ## clipboard.js 剪贴板插件 https://github.com/zenorocha/clipboard.js/ ## Flask-Excel http://flask-excel.readthedocs.io/en/latest/ 支持 csv tsv 导出 如果需要导出 xls 格式,需要安装 pyexcel-xls ## Flask-Uploads https://pythonhosted.org/Flask-Uploads/ ## Flask-Principal http://pythonhosted.org/Flask-Principal/ http://flask-principal-cn.readthedocs.io/zh_CN/latest/ ## Flask-DebugToolbar https://flask-debugtoolbar.readthedocs.io/en/latest/ ## 部署方案( Nginx + Gunicorn + Supervisor ) 生产环境下,flask 自带的 服务器,无法满足性能要求。我们这里采用 gunicorn 做 wsgi 容器,用来部署 python。 Gunicorn 官网:[http://gunicorn.org/](http://gunicorn.org/) 参考:[virtualenv 环境下 Flask + Nginx + Gunicorn+ Supervisor 搭建 Python Web](http://www.ituring.com.cn/article/201045?utm_source=tuicool) Supervisor 重启 Gunicorn 时,有时会碰到 Gunicorn 起不来的情况 查看端口占用情况 ``` # netstat -tulpn Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 1722/python tcp 0 0 127.0.0.1:9001 0.0.0.0:* LISTEN 1/python tcp 0 0 0.0.0.0:8010 0.0.0.0:* LISTEN 647/python # kill -9 PID ``` 安装 ``` $ pip install gunicorn ``` 用 gunicorn 启动 flask ``` $ gunicorn -w4 -b0.0.0.0:8000 app:app [2016-03-02 15:22:44 +0000] [14362] [INFO] Starting gunicorn 19.4.5 [2016-03-02 15:22:44 +0000] [14362] [INFO] Listening at: http://0.0.0.0:8000 (14362) [2016-03-02 15:22:44 +0000] [14362] [INFO] Using worker: sync [2016-03-02 15:22:44 +0000] [14367] [INFO] Booting worker with pid: 14367 [2016-03-02 15:22:44 +0000] [14370] [INFO] Booting worker with pid: 14370 [2016-03-02 15:22:45 +0000] [14373] [INFO] Booting worker with pid: 14373 [2016-03-02 15:22:45 +0000] [14374] [INFO] Booting worker with pid: 14374 ``` 此时,我们需要用 8000 的端口进行访问,原先的5000并没有启用。 进程数的推荐配置: workers = multiprocessing.cpu_count() * 2 + 1 安装 supervisor ``` $ pip install supervisor $ echo_supervisord_conf > etc/supervisord.conf ``` 新增如下配置 ``` [program:app] command="gunicorn -w 4 -b 0.0.0.0:8000 app:app" startsecs=0 stopwaitsecs=0 autostart=false autorestart=false stdout_logfile=log/gunicorn.log stderr_logfile=log/gunicorn.err ``` 启动 supervisord 服务 ``` $ supervisord -c etc/supervisord.conf ``` 常用命令 ``` supervisord -c etc/supervisord.conf supervisorctl -c etc/supervisord.conf status 查看supervisor的状态 supervisorctl -c etc/supervisord.conf reload 重新载入 配置文件 supervisorctl -c etc/supervisord.conf start [all]|[appname] 启动指定/所有 supervisor 管理的程序进程 supervisorctl -c etc/supervisord.conf stop [all]|[appname] 关闭指定/所有 supervisor 管理的程序进程 supervisorctl -c etc/supervisord.conf restart [all]|[appname] 重启指定/所有 supervisor 管理的程序进程 ``` supervisor 还有一个 web 的管理界面,可以激活。更改下配置: ``` [inet_http_server] ; inet (TCP) server disabled by default port=127.0.0.1:9001 ; (ip_address:port specifier, *:port for all iface) username=admin ; (default is no username (open server)) password=123456 ; (default is no password (open server)) ``` ``` [supervisorctl] serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket username=admin ; should be same as http_username if set password=123456 ; should be same as http_password if set ;prompt=mysupervisor ; cmd line prompt (default "supervisor") ;history_file=~/.sc_history ; use readline history if available ``` 用户名 和 密码 必须一致,也可以同时注释掉。 重新加载配置 并 重启服务 ``` $ supervisorctl -c etc/supervisord.conf reload $ supervisorctl -c etc/supervisord.conf restart all ``` 浏览器访问 [http://127.0.0.1:9001](http://127.0.0.1:9001) 可以得到 supervisor 的 web 管理界面, 访问 [http://127.0.0.1:8000](http://127.0.0.1:8000) 可以看见 gunicorn 启动的返回的页面。 nginx 服务 ``` sudo service nginx start sudo service nginx stop sudo service nginx restart ``` ``` 进入项目目录 $ sudo ln -s `pwd`/etc/nginx.conf /etc/nginx/conf.d/flask_app_nginx.conf $ sudo nginx -s reload # 平滑重启 $ sudo subl /etc/hosts # 127.0.0.1 www.flask-app.com ``` 查看 ip 地址 ``` $ ifconfig eth0 | grep 'inet ' | awk '{print $2}' ``` nginx 静态资源目录配置 ``` $ sudo mkdir -p /data/static $ sudo chown nginx. /data -R $ sudo ln -s /data/static /home/zhanghe/code/flask_project/app/static ``` 为了保证网站安全(避免上传漏洞),需要正确权限配置(目录755,静态文件644权限) ``` $ cd /data/ $ find ./ -type d -print | xargs chmod 755 $ find ./ -type f -print | xargs chmod 644 ``` 不建议以下为方便而设置的777权限 ``` $ sudo chmod 777 -R /data/static/ ``` 新增 nginx 配置信息 ``` location ~ ^/static/ { root /data/; #过期30天,静态文件不怎么更新,过期可以设大一点,如果频繁更新,则可以设置得小一点。 expires 30d; } ``` Nginx https 部署 参考: [https.md](doc/https.md) ## 上传目录权限设置 ``` $ chmod 777 /data/uploads ``` ## 统计代码行数(包含注释) 查看项目python代码行数 ``` $ find ./app -type f -name "*.py" | xargs wc -l ``` 查看项目模板文件行数 ``` $ find ./app -type f -name "*.html" | xargs wc -l ``` ## 调试 在 Python 应用程序中设置断点 ``` import pdb; pdb.set_trace() ``` ## 远程调试 ``` $ pip install remote-pdb ``` 远程调试配置代码(添加web服务启动之前): ``` import signal import os def handle_pdb(sig, frame): from remote_pdb import RemotePdb print sig, frame RemotePdb('0.0.0.0', 48110).set_trace() # 如将ip地址设置为127.0.0.1,只能本机调试 signal.signal(signal.SIGUSR1, handle_pdb) print('pid:%s' % os.getpid()) ``` 调试过程: 一、服务启动 ``` $ ./run.py pid:7892 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) 10 CRITICAL:root:RemotePdb session open at 127.0.0.1:48110, waiting for connection ... RemotePdb session open at 0.0.0.0:48110, waiting for connection ... CRITICAL:root:RemotePdb accepted connection from ('127.0.0.1', 41497). RemotePdb accepted connection from ('127.0.0.1', 41497). ``` 终端输出的进程id 为 7892 二、给进程7892 发送终止信号 ``` $ kill -10 7892 ``` 三、远程登陆 ``` $ telnet 127.0.0.1 48110 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. --Return-- > /home/zhanghe/code/flask_project/run.py(21)handle_pdb()->None -> RemotePdb('127.0.0.1', 48110).set_trace() (Pdb) ``` 四、退出调试(2步): Ctrl + ], q ``` (Pdb) ^] telnet> q Connection closed. ``` 此时,程序继续运行(web继续提供服务) pdb 可以执行命令: 直接回车是重复前一条命令! p(print) 查看一个变量值 n(next) 下一步 s(step) 单步,可进入函数 c(continue)继续前进 l(list)看源代码 查看系统支持的信号列表 ``` $ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX ``` 以上程序使用的 SIGUSR1 是终止进程信号,为用户定义信号1 参考文档 [https://pypi.python.org/pypi/remote-pdb](https://pypi.python.org/pypi/remote-pdb) ## 参考资料: [Flask 代码模式](http://docs.jinkan.org/docs/flask/patterns/index.html) ## 安全设置 - 路由参数 校验参数类型,格式 例如: ``` @app.route('/blog/edit//', methods=['GET', 'POST']) ``` 防止 XSS 漏洞 - 主键设置为整型(为方便底层操作,统一命名为 id) 好处:安全(防止因代码疏忽而导致的 XSS 漏洞),效率 - 链接中的重定向参数(next)不能为外链 防止 URL 重定向/跳转漏洞 redirect= ## GitHub 操作 …or create a new repository on the command line ``` echo "# flask_project" >> README.md git init git add README.md git commit -m "first commit" git remote add origin git@github.com:zhanghe06/flask_project.git git push -u origin master ``` …or push an existing repository from the command line ``` git remote add origin git@github.com:zhanghe06/flask_project.git git push -u origin master ``` ## Fix SNIMissingWarning 机器环境: ``` $ python --version Python 2.7.6 ``` 虚拟环境: ``` $ pip freeze | grep requests requests==2.9.1 requests-oauthlib==0.6.1 ``` 执行 pip freeze 命令后有报错信息: ``` /home/zhanghe/code/flask_project/flask.env/local/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py:315: SNIMissingWarning: An HTTPS request has been made, but the SNI (Subject Name Indication) extension to TLS is not available on this platform. This may cause the server to present an incorrect TLS certificate, which can cause validation failures. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#snimissingwarning. ``` 原因: [requests 2.6 以后的版本, pyopenssl.inject_into_urllib3()](https://github.com/kennethreitz/requests/blob/a57c87a459d51c5b17d20285109e901b8aa95752/requests/__init__.py#L54) python-2.7.9 以前的版本都会有这个问题 如果不想升级 python 版本, 可以选择降级 requests 版本, 例如: requests==2.5.3 最佳解决方案: ``` $ sudo apt-get install python-dev libffi-dev libssl-dev $ pip install 'requests[security]' ``` 相应的依赖都会被装上: ``` Installing collected packages: idna, pyasn1, six, enum34, ipaddress, pycparser, cffi, cryptography, pyOpenSSL, ndg-httpsclient Successfully installed cffi-1.5.2 cryptography-1.3.1 enum34-1.1.3 idna-2.1 ipaddress-1.0.16 ndg-httpsclient-0.4.0 pyOpenSSL-16.0.0 pyasn1-0.1.9 pycparser-2.14 six-1.10.0 ``` ## UUID [https://docs.python.org/2/library/uuid.html#example](https://docs.python.org/2/library/uuid.html#example) ## SendCloud [http://sendcloud.sohu.com/](http://sendcloud.sohu.com/) ## QiNiu 安装 ``` $ pip install qiniu ``` [http://www.qiniu.com/](http://www.qiniu.com/) [python SDK 官方版](https://github.com/qiniu/python-sdk) [python SDK 社区版](https://github.com/yueyoum/seven-cow) [Flask 扩展](https://github.com/csuzhangxc/Flask-QiniuStorage) ## 注册邮箱验证 主要验证两点: - 邮箱是否可达 - 是否本人操作 验证方式: - 一、验证 token:https://www.***.com/email/signup/token - 二、验证签名:https://www.***.com/email/signup/sign 显然第二种方式更好,不需要生成一个一次性的 token 并把它们存到数据库中 参考:[http://itsdangerous.readthedocs.io/en/latest/](http://itsdangerous.readthedocs.io/en/latest/) ## 签名模块 中文文档:[http://itsdangerous.readthedocs.io/en/latest/](http://itsdangerous.readthedocs.io/en/latest/) ## 常用链接的处理 添加锚点: url_for('end_points', _anchor='part_id') _external=True 绝对路径 ## 编码规范(建议) 新增表单 ``` @app.route('/add/', methods=['GET', 'POST']) @login_required def add(): """ 添加 """ form = AddForm(request.form) if request.method == 'POST': if form.validate_on_submit(): from datetime import datetime current_time = datetime.utcnow() info = { 'author': form.author.data, 'title': form.title.data, 'pub_date': form.pub_date.data, 'add_time': current_time, 'edit_time': current_time, } result = add(info) if result: flash(u'Add Success', 'success') else: flash(u'Add Failed', 'warning') return render_template('add.html', form=form) ``` 编辑表单 ``` @app.route('/edit//', methods=['GET', 'POST']) @login_required def edit(id): """ 编辑 """ form = EditForm(request.form) if request.method == 'GET': info = get_info(id) if blog_info: form.author.data = info.author form.title.data = info.title form.pub_date.data = info.pub_date else: return redirect(url_for('index')) if request.method == 'POST': if form.validate_on_submit(): from datetime import datetime info = { 'author': form.author.data, 'title': form.title.data, 'pub_date': form.pub_date.data, 'edit_time': datetime.utcnow(), } result = edit(id, info) if result: flash(u'Edit Success', 'success') else: flash(u'Edit Failed', 'warning') return render_template('edit.html', id=id, form=form) ``` ## 模板引擎 [https://github.com/aui/artTemplate](https://github.com/aui/artTemplate) [https://github.com/wycats/handlebars.js](https://github.com/wycats/handlebars.js) 模板引擎与jinja结合 ``` {% raw %} ... {% endraw %} ``` ## 模块导入更新 旧模块 | 新模块 | --- | --- | flask.ext.login | flask_login flask.ext.wtf | flask_wtf flask.ext.sqlalchemy | flask_sqlalchemy flask.ext.mail | flask_mail 表单里,用 FlaskForm 替代历史版本 Form ``` from flask.ext.wtf import Form 替换为 from flask_wtf import FlaskForm ``` ## 过滤器 注册自定义过滤器 ``` 方式一: @app.template_filter('reverse') def reverse_filter(s): return s[::-1] 方式二: def reverse_filter(s): return s[::-1] app.jinja_env.filters['reverse'] = reverse_filter ``` 过滤器使用 ``` {% for x in mylist | reverse %} {% endfor %} ``` ## 用户注册 注册方式 | 校验方式 | --- | --- | 手机号码 | 短信验证码 邮箱 | 发送带身份信息加密的链接,点击校验通过后即确认身份 第三方 | oauth 注意:如果需要兼容国际手机号码注册,需要添加国家区号 平台内先注册,后绑定第三方账号 ## 手机注册 检查手机号码 检查图形码 获取短信验证码 1、前台页面60S倒计时,60S后恢复 2、后台生成短信码 session存储 3、发送短信 提交注册(校验 表单与session 短信验证码) ## 忘记密码 ## 页面全选功能设计 ``` $("#check_all").change(function(){ $('.check_children').prop("checked",this.checked); }); ``` ## 业务逻辑确认外键还是数据库设置外键 效率与一致性的权衡 主外键只是一种保持数据一致性的手段之一,如果有别的方式来保持数据一致性(业务逻辑保持数据一致性),那么数据库不设置主外键也是可以的。 ## 数据库设计 NOT NULL 与 DEFAULT(默认非null值) 1、NOT NULL DEFAULT 同时设置,可以不插入字段,但是不能插入 null 值 2、仅仅设置 DEFAULT,可以不插入字段,也可插入 null 值 3、仅仅设置 NOT NULL,可以不插入字段,不可插入 null 值 通常,DEFAULT 的设置会简化代码的设计, 同时考虑索引效率,会将需要建立索引的字段添加 DEFAULT,因为 null是无法比较的 ``` MariaDB [(none)]> select 2=2, 'a'='a', NULL=NULL; +-----+---------+-----------+ | 2=2 | 'a'='a' | NULL=NULL | +-----+---------+-----------+ | 1 | 1 | NULL | +-----+---------+-----------+ 1 row in set (0.00 sec) ``` ## 关于数据库索引优化 索引的类型 索引名称 | 定义 | 描述 | --- | --- | --- | 普通索引 | key | 唯一的作用就是加快查询的速度 主键索引 | primary key | 字段具备唯一性 一张数据表中只有一个 唯一索引 | unique key | 保证数据唯一性 联合索引 | key(a,b,c) | 实际上是建立3个索引(a,b,c;a,b;a),每个索引都包含a 外键索引 | - | 就是用来维护数据表之间的相关性的,并且会导致数据的写入等操作的速度过慢,所以好像没啥用(对于较大的项目) 全文索引 | FULLTEXT(column1, column2) | mysql5.6以前的InnoDB表不支持。使用:WHERE MATCH(column) AGAINST('search_content') 能否用到索引 | 操作符 | --- | --- | 可以用到 | <, <=, =, >, >=, BETWEEN, IN, LIKE _% 不能用到 | <>, !=, not in, LIKE %_ 对于比较长的字符串比如varchar(255)类型字段,可为该字段的前n个值建立索引,可用以下语句确定索引的长度 ``` select count(distinct substring(字段,1,结束位置)) from 表 ``` EXPLAIN 测试SQL执行计划 ## 基于 redisde 会话管理 登陆成功后,即可看到session信息 ``` 127.0.0.1:6379> keys * 1) "session:.eJw9kF1vgjAYhf_K0msvsOASTbww1jkMbwmk1bU3RD5caWUoyNAa__uIS8y5PMnz5Jw7Sg5N0So0uzRdMUJJmaPZHb2laIbAZhMgEYZqY8CKnla8D5nxQGc3uhYerMEBZjDV2VhYvwcilcDcBcxxSGJDdV5KkpchiVzBVi7stlpqH0vmX0PiO0C4pYxWYI0n7JAq1oPHEXp1kyxWIVGGDsxwxz2h1VGyaHBFPeBoAhZcsaNHqmHosjl6jFDWNofkUpvi5zVBEo6BDWjsD6rMk-x7TK1UgGMDjA_z-I3q1QS070itNCXGkYv5E3c-J_W-u6imyMvmRdx_xk5G6t_ALjpYen2gF-8hWfSwnLYpzk9pOVX5V1yn7uZUVFsTuB_XoP8ndm3RPA9GLnr8AWDyey8.C58cvw._6DL2RmlQ9oqsw8IlGuX0FvLGts" 127.0.0.1:6379> type "session:.eJw9kF1vgjAYhf_K0msvsOASTbww1jkMbwmk1bU3RD5caWUoyNAa__uIS8y5PMnz5Jw7Sg5N0So0uzRdMUJJmaPZHb2laIbAZhMgEYZqY8CKnla8D5nxQGc3uhYerMEBZjDV2VhYvwcilcDcBcxxSGJDdV5KkpchiVzBVi7stlpqH0vmX0PiO0C4pYxWYI0n7JAq1oPHEXp1kyxWIVGGDsxwxz2h1VGyaHBFPeBoAhZcsaNHqmHosjl6jFDWNofkUpvi5zVBEo6BDWjsD6rMk-x7TK1UgGMDjA_z-I3q1QS070itNCXGkYv5E3c-J_W-u6imyMvmRdx_xk5G6t_ALjpYen2gF-8hWfSwnLYpzk9pOVX5V1yn7uZUVFsTuB_XoP8ndm3RPA9GLnr8AWDyey8.C58cvw._6DL2RmlQ9oqsw8IlGuX0FvLGts" string 127.0.0.1:6379> get "session:.eJw9kF1vgjAYhf_K0msvsOASTbww1jkMbwmk1bU3RD5caWUoyNAa__uIS8y5PMnz5Jw7Sg5N0So0uzRdMUJJmaPZHb2laIbAZhMgEYZqY8CKnla8D5nxQGc3uhYerMEBZjDV2VhYvwcilcDcBcxxSGJDdV5KkpchiVzBVi7stlpqH0vmX0PiO0C4pYxWYI0n7JAq1oPHEXp1kyxWIVGGDsxwxz2h1VGyaHBFPeBoAhZcsaNHqmHosjl6jFDWNofkUpvi5zVBEo6BDWjsD6rMk-x7TK1UgGMDjA_z-I3q1QS070itNCXGkYv5E3c-J_W-u6imyMvmRdx_xk5G6t_ALjpYen2gF-8hWfSwnLYpzk9pOVX5V1yn7uZUVFsTuB_XoP8ndm3RPA9GLnr8AWDyey8.C58cvw._6DL2RmlQ9oqsw8IlGuX0FvLGts" "(dp0\nS'csrf_token'\np1\nS'c44590d3e1d7be4250ff8c88e1b807832939b8ab'\np2\nsS'_fresh'\np3\nI01\nsS'user_id'\np4\nV3\np5\nsS'_id'\np6\nS'3790462bd3606e09982724f80c4196675c2006ace73e684d67bd7b847a171ecf26e2182405353f398c63bdc364b12e4a88d46a9e8b8ee466403d9337ace638b7'\np7\ns." 127.0.0.1:6379> ``` ## 并发测试 flask 自带的 BaseHTTPServer 性能太差,测试过程中,连续刷新10次左右竟然报错 ``` IOError: [Errno 32] Broken pipe ``` 测试图形验证码并发 ``` ✗ ab -n 1000 -c 100 http://127.0.0.1:8000/captcha/get_code/reg/ ``` ## 事务一致性 分布式架构下,无法做到跨服务事务,只能通过逻辑事务,回滚采用补偿方案处理 ## ajax 前后端规范 后端返回结构 ``` json.dumps({'result': True}) json.dumps({'result': False, 'msg': u'错误消息'}) json.dumps({'result': False, 'msg': u'错误消息', 'location': 'http://www.baidu.com'}) ``` 前端处理逻辑 ``` ``` ## sqlalchemy.exc.OperationalError OperationalError: (_mysql_exceptions.OperationalError) (2006, 'MySQL server has gone away') ## sqlalchemy.exc.InvalidRequestError StatementError: (sqlalchemy.exc.InvalidRequestError) Can't reconnect until invalid transaction is rolled back 检查配置 ``` MariaDB [flask_project]> show global variables like "%timeout%"; +-----------------------------+----------+ | Variable_name | Value | +-----------------------------+----------+ | connect_timeout | 5 | | deadlock_timeout_long | 50000000 | | deadlock_timeout_short | 10000 | | delayed_insert_timeout | 300 | | innodb_flush_log_at_timeout | 1 | | innodb_lock_wait_timeout | 50 | | innodb_rollback_on_timeout | OFF | | interactive_timeout | 28800 | | lock_wait_timeout | 31536000 | | net_read_timeout | 30 | | net_write_timeout | 60 | | slave_net_timeout | 3600 | | thread_pool_idle_timeout | 60 | | wait_timeout | 600 | +-----------------------------+----------+ 14 rows in set (0.00 sec) ``` 查看连接情况 ``` MariaDB [flask_project]> show processlist; +-----+------+------------------+---------------+---------+------+-------+------------------+----------+ | Id | User | Host | db | Command | Time | State | Info | Progress | +-----+------+------------------+---------------+---------+------+-------+------------------+----------+ | 176 | root | 172.17.0.5:46672 | flask_project | Query | 0 | init | show processlist | 0.000 | | 180 | root | 172.17.0.1:39592 | flask_project | Sleep | 284 | | NULL | 0.000 | +-----+------+------------------+---------------+---------+------+-------+------------------+----------+ 2 rows in set (0.00 sec) ``` 查看当前打开的连接的数量 ``` MariaDB [flask_project]> show status like '%Threads_connected%'; +-------------------+-------+ | Variable_name | Value | +-------------------+-------+ | Threads_connected | 2 | +-------------------+-------+ 1 row in set (0.01 sec) ``` 修改连接超时时间(测试) ``` MariaDB [flask_project]> set global wait_timeout=6; ``` ``` MariaDB [flask_project]> show global variables like 'wait_timeout'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | wait_timeout | 600 | +---------------+-------+ 1 row in set (0.00 sec) MariaDB [flask_project]> show variables like 'wait_timeout'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | wait_timeout | 28800 | +---------------+-------+ 1 row in set (0.00 sec) MariaDB [flask_project]> show global status like 'uptime'; +---------------+--------+ | Variable_name | Value | +---------------+--------+ | Uptime | 204519 | +---------------+--------+ 1 row in set (0.00 sec) MariaDB [flask_project]> show global variables like 'max_allowed_packet'; +--------------------+----------+ | Variable_name | Value | +--------------------+----------+ | max_allowed_packet | 16777216 | +--------------------+----------+ 1 row in set (0.00 sec) ``` 错误重现: 当有事务没有提交或者没有回滚时,sqlalchemy 的连接回收时间(SQLALCHEMY_POOL_RECYCLE)大于数据库关闭等待连接时间(global wait_timeout); 一旦数据库连接超时自动断开,此时 sqlalchemy 尝试连接数据库,会出现 OperationalError: (_mysql_exceptions.OperationalError) (2006, 'MySQL server has gone away') 处理方案: 1. sqlalchemy 的超时时间 小于 数据库的超时时间 2. 代码操作数据库,立即提交事务,错误回滚 官方说明: http://flask-sqlalchemy.pocoo.org/2.2/config/#timeouts ## gunicorn [CRITICAL] WORKER TIMEOUT ## jinja 模板递归 关键词 recursive ``` {% for item in [8,[[10,11,34],2],[3,4,5,[6,7,8]]] recursive %} {% if loop.first %}
{{ '----'*loop.depth }}开始{{ loop.depth }}
{% endif %} {{ '----'*loop.depth }}Depth: {{ loop.depth }} {% if item[0] %} {{ loop(item) }} {% else %} Number: {{ item }} ;
{% endif %} {% if loop.last %} {{ '----'*loop.depth }}结束{{ loop.depth }}
{% endif %} {% endfor %} ``` 递归循环结果: ``` ----开始1 ----Depth: 1 Number: 8 ; ----Depth: 1 --------开始2 --------Depth: 2 ------------开始3 ------------Depth: 3 Number: 10 ; ------------Depth: 3 Number: 11 ; ------------Depth: 3 Number: 34 ; ------------结束3 --------Depth: 2 Number: 2 ; --------结束2 ----Depth: 1 --------开始2 --------Depth: 2 Number: 3 ; --------Depth: 2 Number: 4 ; --------Depth: 2 Number: 5 ; --------Depth: 2 ------------开始3 ------------Depth: 3 Number: 6 ; ------------Depth: 3 Number: 7 ; ------------Depth: 3 Number: 8 ; ------------结束3 --------结束2 ----结束1 ``` ## 在 jinja2 macro 使用 current_user 需要带使用上下文参数(with context) ``` {% from "macros/comments.html" import comment_form with context %} ``` ## 银行卡验证接口 http://www.apistore.cn/ http://v.gotoway.com/api/v4/bankCard?key=0341749f1adc9e4f47f6a936e7ed9b46&bankcard=123456 ``` { "error_code": 0, "reason": "Succes", "result": { "abbreviation": "ABC", "bankimage": "http://auth.apis.la/bank/3_ABC.png", "bankname": "农业银行", "banknum": "1030000", "bankurl": "http://www.abchina.com/", "cardlength": "19", "cardname": "金穗通宝卡(银联卡)", "cardprefixlength": "6", "cardprefixnum": "622848", "cardtype": "银联借记卡", "enbankname": "Agricultural Bank of China", "isLuhn": true, "iscreditcard": 1, "servicephone": "95599" }, "ordersign": "20170621091044215952509756" } ``` 认证类型: - 实名认证 (姓名、身份证号码) - 银行卡三要素认证 (银行卡姓名/卡号/身份证) - 银行卡四要素认证 (银行卡姓名/卡号/身份证/手机号) ## 参数优化 连接池大小 == web进程数 == CPU核心数 SQLALCHEMY_POOL_RECYCLE < wait_timeout ``` MariaDB [flask_project]> show global variables like 'max_connections'; +-----------------+-------+ | Variable_name | Value | +-----------------+-------+ | max_connections | 100 | +-----------------+-------+ 1 row in set (0.00 sec) MariaDB [flask_project]> set global max_connections=1000; ``` set global max_connections=1000; ## 流式处理的几种方式 - HTML5 Server-Sent Events - Flask-SSE http://dormousehole.readthedocs.io/en/latest/patterns/streaming.html http://flask.pocoo.org/snippets/118/ http://www.python-requests.org/en/master/user/advanced/#streaming-requests http://flask-sse.readthedocs.io/en/latest/quickstart.html ## 日志 使用 supervisor 管理进程,终端输出的日志信息,被supervisor截获后,写入stderr_logfile,一直想不通 查看 logging 模块源码 ``` class StreamHandler(Handler): """ A handler class which writes logging records, appropriately formatted, to a stream. Note that this class does not close the stream, as sys.stdout or sys.stderr may be used. """ def __init__(self, stream=None): """ Initialize the handler. If stream is not specified, sys.stderr is used. """ Handler.__init__(self) if stream is None: stream = sys.stderr self.stream = stream ``` StreamHandler 默认是按照标准错误输出处理 print 则使用的是标准输出 ## 分库分表 垂直分库 水平分表 - 分库 http://flask-sqlalchemy.pocoo.org/2.1/binds/ ``` SQLALCHEMY_DATABASE_URI = 'postgres://localhost/main' SQLALCHEMY_BINDS = { 'users': 'mysqldb://localhost/users', 'appmeta': 'sqlite:////path/to/appmeta.db' } ``` ``` class User(db.Model): __bind_key__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True) ``` 使用 SQLALCHEMY_BINDS() 和 **\_\_bind_key__** - 分表 网友方案,先拿来 ``` class GoodsDesc(object): _mapper = {} @staticmethod def model(goods_id): table_index = goods_id%100 class_name = 'GoodsDesc_%d' % table_index ModelClass = GoodsDesc._mapper.get(class_name, None) if ModelClass is None: ModelClass = type(class_name, (db.Model,), { '__module__' : __name__, '__name__' : class_name, '__tablename__' : 'goods_desc_%d' % table_index, 'goods_id' : db.Column(db.Integer, primary_key=True), 'goods_desc' : db.Column(db.Text, default=None), }) GoodsDesc._mapper[class_name] = ModelClass cls = ModelClass() cls.goods_id = goods_id return cls # 外部代码调用如例如下: # ----------------------- # 新增插入 gdm = GoodsDesc.model(goods_id) gdm.goods_desc = 'desc' db.session.add(gd) # 查询 gdm = GoodsDesc.model(goods_id) gd = gdm.query.filter_by(goods_id=goods_id).first() ``` 待改进 ``` _mapper = {} def shard(self, pk_id): """ 分表 :param self: :param pk_id: :return: """ table_index = pk_id % 16 class_name = '%s_%02d' % (self.name, table_index) model_class = _mapper.get(class_name, None) if model_class is None: model_class = type(class_name, (Base,), { '__module__': __name__, '__name__': class_name, '__tablename__': '%s_%d' % (self.__table__.name, table_index), 'goods_id': db.Column(db.Integer, primary_key=True), 'goods_desc': db.Column(db.Text, default=None), }) _mapper[class_name] = model_class cls = model_class() cls.id = pk_id return cls ``` ## 单一架构 VS 微服务架构 ## 信息修改 账号修改 密码修改 ## Todo: - 第三方登陆 - 第三方支付 - 邮件列表的绑定和解绑 - 找回密码安全设置 参考:[密码找回功能可能存在的问题](http://drops.wooyun.org/web/3295) - 接口参数签名校验的必要性 参考:[在线支付逻辑漏洞总结](http://drops.wooyun.org/papers/345) - 检查数据重复, 处理效率问题 - 页面导出csv - 各大搜索引擎提交入口 - 用户推广,注册邀请 ================================================ FILE: app_api/README.md ================================================ ## App 专用 ================================================ FILE: app_api/__init__.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: __init__.py.py @time: 2017/4/19 下午2:24 """ def func(): pass class Main(object): def __init__(self): pass if __name__ == '__main__': pass ================================================ FILE: app_backend/README.md ================================================ ## 运营后台 ================================================ FILE: app_backend/__init__.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: __init__.py @time: 2017/4/9 上午10:11 """ from logging.config import dictConfig from config import current_config from flask import Flask from flask_login import LoginManager from flask_moment import Moment from flask_oauthlib.client import OAuth from flask_principal import Principal from app_backend.lib.qiniu_store import QiNiuClient from app_backend.lib.redis_session import RedisSessionInterface from app_backend.lib.sendcloud import SendCloudClient from app_backend.lib.sms_chuanglan_iso import SmsChuangLanIsoApi app = Flask(__name__) app.config.from_object(current_config) app.config['REMEMBER_COOKIE_NAME'] = 'r_a' app.session_cookie_name = 's_a' app.session_interface = RedisSessionInterface(prefix='s:a:', **app.config['REDIS']) login_manager = LoginManager() login_manager.init_app(app) # setup_app 方法已淘汰 login_manager.login_view = 'login' login_manager.login_message = u'请登录后操作' # 设置登录提示消息 login_manager.login_message_category = 'info' # 设置消息分类 # 权限管理插件 principals = Principal(app) # Moment 时间插件 moment = Moment(app) # 短信通道 sms_client = SmsChuangLanIsoApi(app.config['SMS']['UN'], app.config['SMS']['PW']) # SendCloud 邮件 send_cloud_client = SendCloudClient(app) # 七牛云存储 qi_niu_client = QiNiuClient(app) # 第三方开放授权登录 oauth = OAuth(app) # GitHub oauth_github = oauth.remote_app( 'github', **app.config['GITHUB_OAUTH'] ) # QQ oauth_qq = oauth.remote_app( 'qq', **app.config['QQ_OAUTH'] ) # WeiBo oauth_weibo = oauth.remote_app( 'weibo', **app.config['WEIBO_OAUTH'] ) # Google # 要银子,妹的 # 配置日志 dictConfig(app.config['LOG_CONFIG']) if not app.config['DEBUG']: import logging from logging.handlers import SMTPHandler credentials = None if app.config['MAIL_USERNAME'] or app.config['MAIL_PASSWORD']: credentials = (app.config['MAIL_USERNAME'], app.config['MAIL_PASSWORD']) mail_handler = SMTPHandler( (app.config['MAIL_SERVER'], app.config['MAIL_PORT']), app.config['MAIL_DEFAULT_SENDER'][1], app.config['ADMINS'], 'App Error Message', credentials ) mail_handler.setLevel(logging.DEBUG) app.logger.addHandler(mail_handler) # 这个 import 语句放在这里, 防止views, models import发生循环import from app_backend import models, tasks # 导入视图(不使用蓝图的单模式方式) from app_backend import views # 导入视图(不使用蓝图的多模块方式) # from application.views import auth # from application.views import blog # from application.views import reg # from application.views import site # from application.views import user # 导入蓝图(使用蓝图的多模块方式) from app_backend.views.admin import bp_admin from app_backend.views.apply_get import bp_apply_get from app_backend.views.apply_put import bp_apply_put from app_backend.views.order import bp_order from app_backend.views.score import bp_score from app_backend.views.wallet import bp_wallet # from app_backend.views.blog import bp_blog # from app_backend.views.file import bp_file # from app_backend.views.reg import bp_reg from app_backend.views.user import bp_user from app_backend.views.settings import bp_settings from app_backend.views.complaint import bp_complaint from app_backend.views.message import bp_message from app_backend.views.stats import bp_stats from app_backend.views.active import bp_active from app_backend.views.scheduling import bp_scheduling # 注册蓝图 app.register_blueprint(bp_admin) app.register_blueprint(bp_apply_get) app.register_blueprint(bp_apply_put) app.register_blueprint(bp_order) app.register_blueprint(bp_score) app.register_blueprint(bp_wallet) # app.register_blueprint(bp_blog) # app.register_blueprint(bp_file) # app.register_blueprint(bp_reg) app.register_blueprint(bp_user) app.register_blueprint(bp_settings) app.register_blueprint(bp_complaint) app.register_blueprint(bp_message) app.register_blueprint(bp_stats) app.register_blueprint(bp_active) app.register_blueprint(bp_scheduling) # 导入自定义过滤器 from app_backend import filters ================================================ FILE: app_backend/api/__init__.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: __init__.py.py @time: 2017/3/10 下午11:32 """ def func(): pass class Main(object): def __init__(self): pass if __name__ == '__main__': pass ================================================ FILE: app_backend/api/active.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: active.py @time: 2017/5/14 上午1:42 """ from datetime import datetime from app_backend.database import db from app_backend.models import Active, ActiveItem, User from app_backend.tools.db import get_row, get_rows, get_row_by_id, add, edit, delete from app_common.maps.status_audit import * from app_common.maps.type_active import * from app_common.maps.status_active import * def get_active_row_by_id(active_id): """ 通过 id 获取激活信息 :param active_id: :return: None/object """ return get_row_by_id(Active, active_id) def get_active_row(*args, **kwargs): """ 获取激活信息 :param args: :param kwargs: :return: None/object """ return get_row(Active, *args, **kwargs) def add_active(active_data): """ 添加激活信息 :param active_data: :return: None/Value of active.id """ return add(Active, active_data) def edit_active(active_id, active_data): """ 修改激活信息 :param active_id: :param active_data: :return: Number of affected rows (Example: 0/1) """ return edit(Active, active_id, active_data) def delete_active(active_id): """ 删除激活信息 :param active_id: :return: Number of affected rows (Example: 0/1) """ return delete(Active, active_id) def get_active_rows(page=1, per_page=10, *args, **kwargs): """ 获取激活列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(Active, page, per_page, *args, **kwargs) return rows def give_active(user_id_active, amount=1): """ 赠送激活数量 :param user_id_active: :param amount: :return: """ try: current_time = datetime.utcnow() # 添加激活码消费明细 active_item_data = { 'user_id': 0, 'type': TYPE_ACTIVE_GIVE, 'amount': amount, 'sc_id': user_id_active, 'status_audit': STATUS_AUDIT_SUCCESS, 'audit_time': current_time, 'create_time': current_time, 'update_time': current_time, } db.session.add(ActiveItem(**active_item_data)) # 新增接收用户激活码总数量 active_obj = db.session.query(Active).filter(Active.user_id == user_id_active) active_info = active_obj.first() if active_info: active_data = { 'user_id': user_id_active, 'amount': active_info.amount + amount, 'update_time': current_time, } active_obj.update(active_data) else: active_data = { 'user_id': user_id_active, 'amount': amount, 'create_time': current_time, 'update_time': current_time, } db.session.add(Active(**active_data)) db.session.commit() return True except Exception as e: db.session.rollback() raise e ================================================ FILE: app_backend/api/active_item.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: active_item.py @time: 2017/5/20 下午6:29 """ from app_backend.models import ActiveItem from app_backend.tools.db import get_row, get_rows, get_row_by_id, add, edit, delete def get_active_item_row_by_id(active_item_id): """ 通过 id 获取激活信息 :param active_item_id: :return: None/object """ return get_row_by_id(ActiveItem, active_item_id) def get_active_item_row(*args, **kwargs): """ 获取激活信息 :param args: :param kwargs: :return: None/object """ return get_row(ActiveItem, *args, **kwargs) def add_active_item(active_item_data): """ 添加激活信息 :param active_item_data: :return: None/Value of active.id """ return add(ActiveItem, active_item_data) def edit_active_item(active_item_id, active_item_data): """ 修改激活信息 :param active_item_id: :param active_item_data: :return: Number of affected rows (Example: 0/1) """ return edit(ActiveItem, active_item_id, active_item_data) def delete_active_item(active_item_id): """ 删除激活信息 :param active_item_id: :return: Number of affected rows (Example: 0/1) """ return delete(ActiveItem, active_item_id) def get_active_item_rows(page=1, per_page=10, *args, **kwargs): """ 获取激活列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(ActiveItem, page, per_page, *args, **kwargs) return rows ================================================ FILE: app_backend/api/admin.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: admin.py @time: 17-4-20 下午10:04 """ from app_backend.login import LoginUser from app_backend.tools.db import get_row, get_rows, get_row_by_id, add, edit, delete, update_rows def get_admin_row_by_id(user_auth_id): """ 通过 id 获取用户信息 :param user_auth_id: :return: None/object """ return get_row_by_id(LoginUser, user_auth_id) def get_admin_row(*args, **kwargs): """ 获取用户信息 :param args: :param kwargs: :return: None/object """ return get_row(LoginUser, *args, **kwargs) def add_admin(user_auth_data): """ 添加用户信息 :param user_auth_data: :return: None/Value of user.id """ return add(LoginUser, user_auth_data) def edit_admin(user_auth_id, user_auth_data): """ 修改用户信息 :param user_auth_id: :param user_auth_data: :return: Number of affected rows (Example: 0/1) """ return edit(LoginUser, user_auth_id, user_auth_data) def delete_admin(user_auth_id): """ 删除用户信息 :param user_auth_id: :return: Number of affected rows (Example: 0/1) """ return delete(LoginUser, user_auth_id) def get_admin_rows(page=1, per_page=10, *args, **kwargs): """ 获取用户列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(LoginUser, page, per_page, *args, **kwargs) return rows def update_admin_rows(data, *args, **kwargs): """ 批量更新用户信息 """ return update_rows(LoginUser, data, *args, **kwargs) ================================================ FILE: app_backend/api/admin_role.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: admin_role.py @time: 2017/6/14 上午11:07 """ from app_backend.models import AdminRole from app_backend.tools.db import get_row, get_rows, get_row_by_id, add, edit, delete, get_lists def get_admin_role_row_by_id(admin_role_id): """ 通过 id 获取角色信息 :param admin_role_id: :return: None/object """ return get_row_by_id(AdminRole, admin_role_id) def get_admin_role_row(*args, **kwargs): """ 获取角色信息 :param args: :param kwargs: :return: None/object """ return get_row(AdminRole, *args, **kwargs) def add_admin_role(admin_role_data): """ 添加角色信息 :param admin_role_data: :return: None/Value of author.id """ return add(AdminRole, admin_role_data) def edit_admin_role(admin_role_id, admin_role_data): """ 修改角色信息 :param admin_role_id: :param admin_role_data: :return: Number of affected rows (Example: 0/1) """ return edit(AdminRole, admin_role_id, admin_role_data) def delete_admin_role(admin_role_id): """ 删除角色信息 :param admin_role_id: :return: Number of affected rows (Example: 0/1) """ return delete(AdminRole, admin_role_id) def get_admin_role_rows(page=1, per_page=10, *args, **kwargs): """ 获取角色列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(AdminRole, page, per_page, *args, **kwargs) return rows def get_admin_role_lists(*args, **kwargs): """ 获取角色列表 :param args: :param kwargs: :return: None/list """ return get_lists(AdminRole, *args, **kwargs) ================================================ FILE: app_backend/api/apply_get.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: apply_get.py @time: 2017/4/13 上午11:27 """ from datetime import datetime import traceback from sqlalchemy import func from app_backend.database import db from app_backend.models import ApplyGet, Order, ApplyPut from app_backend.tools.db import get_row, get_rows_by_ids, get_lists, get_rows, get_row_by_id, add, edit, delete from app_common.maps.status_audit import STATUS_AUDIT_SUCCESS from app_common.maps.status_order import STATUS_ORDER_COMPLETED, STATUS_ORDER_PROCESSING from app_common.tools.date_time import get_current_day_time_ends, get_hours, time_local_to_utc, \ get_current_month_time_ends, get_days, get_current_year_time_ends, get_months def get_apply_get_row_by_id(apply_get_id): """ 通过 id 获取提现申请信息 :param apply_get_id: :return: None/object """ return get_row_by_id(ApplyGet, apply_get_id) def get_apply_get_row(*args, **kwargs): """ 获取提现申请信息 :param args: :param kwargs: :return: None/object """ return get_row(ApplyGet, *args, **kwargs) def add_apply_get(apply_get_data): """ 添加提现申请信息 :param apply_get_data: :return: None/Value of apply_get.id """ return add(ApplyGet, apply_get_data) def edit_apply_get(apply_get_id, apply_get_data): """ 修改提现申请信息 :param apply_get_id: :param apply_get_data: :return: Number of affected rows (Example: 0/1) """ return edit(ApplyGet, apply_get_id, apply_get_data) def delete_apply_get(apply_get_id): """ 删除提现申请信息 :param apply_get_id: :return: Number of affected rows (Example: 0/1) """ return delete(ApplyGet, apply_get_id) def get_apply_get_rows_by_ids(ids): """ 获取提现申请列表通过主键列表 :param ids: :return: list """ return get_rows_by_ids(ApplyGet, ids) def get_apply_get_lists(*args, **kwargs): """ 获取提现申请列表 :param args: :param kwargs: :return: None/list """ return get_lists(ApplyGet, *args, **kwargs) def get_apply_get_rows(page=1, per_page=10, *args, **kwargs): """ 获取提现申请列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(ApplyGet, page, per_page, *args, **kwargs) return rows def apply_get_match(apply_get_id, apply_put_ids, accept_split=0): """ 提现申请匹配 :param apply_get_id: :param apply_put_ids: :param accept_split: :return: """ from app_backend.api.apply_put import get_apply_put_rows_by_ids apply_get_info = get_apply_get_row_by_id(apply_get_id) # 判断是否已经匹配 if apply_get_info.status_order == int(STATUS_ORDER_COMPLETED): raise Exception(u'不能重复匹配') apply_put_list = get_apply_put_rows_by_ids(apply_put_ids) # 判断是否同一个人 apply_put_user_ids = [apply_put_item.user_id for apply_put_item in apply_put_list] if apply_get_info.user_id in apply_put_user_ids: raise Exception(u'不能匹配给自己') # 判断金额 money_need_match = apply_get_info.money_apply - apply_get_info.money_order apply_put_amount = sum([apply_put_item.money_apply-apply_put_item.money_order for apply_put_item in apply_put_list]) # 如果接收拆分,判断被拆金额是否大于被匹配金额 if accept_split: if money_need_match < apply_put_amount: raise Exception(u'选择金额太大') elif money_need_match != apply_put_amount: raise Exception(u'金额不匹配') # 循环投资申请,生成对应的订单 try: money_order = 0 for apply_put_item in apply_put_list: money_match = apply_put_item.money_apply - apply_put_item.money_order current_time = datetime.utcnow() order_data = { 'apply_put_id': apply_put_item.id, 'apply_get_id': apply_get_id, 'apply_put_uid': apply_put_item.user_id, 'apply_get_uid': apply_get_info.user_id, 'money': money_match, 'status_audit': STATUS_AUDIT_SUCCESS, 'audit_time': current_time, 'create_time': current_time, 'update_time': current_time, } db.session.add(Order(**order_data)) # 更新投资申请状态 apply_put_update_info = { 'money_order': apply_put_item.money_order + money_match, 'status_order': STATUS_ORDER_COMPLETED, 'update_time': current_time, } apply_put_obj = db.session.query(ApplyPut).filter(ApplyPut.id == apply_put_item.id) apply_put_obj.update(apply_put_update_info) money_order += money_match # 更新提现订单金额和申请状态 current_time = datetime.utcnow() apply_get_update_data = { 'money_order': ApplyGet.money_order + money_order, 'status_order': STATUS_ORDER_COMPLETED if money_order == money_need_match else STATUS_ORDER_PROCESSING, 'update_time': current_time, } # result = edit_apply_get(apply_get_id, apply_get_update_info) apply_get_obj = db.session.query(ApplyGet).filter(ApplyGet.id == apply_get_id) result = apply_get_obj.update(apply_get_update_data) db.session.commit() return result except Exception as e: print traceback.print_exc() db.session.rollback() raise e def apply_get_stats(time_based='hour'): """ 提现申请统计 :return: """ # 按小时统计 if time_based == 'hour': start_time, end_time = get_current_day_time_ends() hours = get_hours(False) hours_zerofill = get_hours() result = dict(zip(hours, [0] * len(hours))) rows = db.session \ .query(func.hour(ApplyGet.create_time).label('hour'), func.sum(ApplyGet.money_apply)) \ .filter(ApplyGet.create_time >= time_local_to_utc(start_time), ApplyGet.create_time <= time_local_to_utc(end_time)) \ .group_by('hour') \ .limit(len(hours)) \ .all() result.update(dict(rows)) return [(hours_zerofill[i], result[hour]) for i, hour in enumerate(hours)] # 按日期统计 if time_based == 'date': start_time, end_time = get_current_month_time_ends() today = datetime.today() days = get_days(year=today.year, month=today.month, zerofill=False) days_zerofill = get_days(year=today.year, month=today.month) result = dict(zip(days, [0] * len(days))) rows = db.session \ .query(func.day(ApplyGet.create_time).label('date'), func.sum(ApplyGet.money_apply)) \ .filter(ApplyGet.create_time >= time_local_to_utc(start_time), ApplyGet.create_time <= time_local_to_utc(end_time)) \ .group_by('date') \ .limit(len(days)) \ .all() result.update(dict(rows)) return [(days_zerofill[i], result[day]) for i, day in enumerate(days)] # 按月份统计 if time_based == 'month': start_time, end_time = get_current_year_time_ends() months = get_months(False) months_zerofill = get_months() result = dict(zip(months, [0] * len(months))) rows = db.session \ .query(func.month(ApplyGet.create_time).label('month'), func.sum(ApplyGet.money_apply)) \ .filter(ApplyGet.create_time >= time_local_to_utc(start_time), ApplyGet.create_time <= time_local_to_utc(end_time)) \ .group_by('month') \ .limit(len(months)) \ .all() result.update(dict(rows)) return [(months_zerofill[i], result[month]) for i, month in enumerate(months)] ================================================ FILE: app_backend/api/apply_put.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: apply_put.py @time: 2017/4/13 下午9:45 """ from datetime import datetime import traceback from sqlalchemy import func from app_backend.models import ApplyGet, Order, ApplyPut from app_backend.tools.db import get_row, get_rows_by_ids, get_lists, get_rows, get_row_by_id, add, edit, delete from app_backend.database import db from app_common.maps.status_audit import STATUS_AUDIT_SUCCESS from app_common.maps.status_order import STATUS_ORDER_COMPLETED, STATUS_ORDER_PROCESSING from app_common.tools.date_time import get_current_day_time_ends, get_hours, time_local_to_utc, \ get_current_month_time_ends, get_days, get_current_year_time_ends, get_months def get_apply_put_row_by_id(apply_put_id): """ 通过 id 获取投资申请信息 :param apply_put_id: :return: None/object """ return get_row_by_id(ApplyPut, apply_put_id) def get_apply_put_row(*args, **kwargs): """ 获取投资申请信息 :param args: :param kwargs: :return: None/object """ return get_row(ApplyPut, *args, **kwargs) def add_apply_put(apply_put_data): """ 添加投资申请信息 :param apply_put_data: :return: None/Value of apply_put.id """ return add(ApplyPut, apply_put_data) def edit_apply_put(apply_put_id, apply_put_data): """ 修改投资申请信息 :param apply_put_id: :param apply_put_data: :return: Number of affected rows (Example: 0/1) """ return edit(ApplyPut, apply_put_id, apply_put_data) def delete_apply_put(apply_put_id): """ 删除投资申请信息 :param apply_put_id: :return: Number of affected rows (Example: 0/1) """ return delete(ApplyPut, apply_put_id) def get_apply_put_rows_by_ids(ids): """ 获取投资申请列表 :param ids: :return: list """ return get_rows_by_ids(ApplyPut, ids) def get_apply_put_lists(*args, **kwargs): """ 获取投资申请列表 :param args: :param kwargs: :return: None/list """ return get_lists(ApplyPut, *args, **kwargs) def get_apply_put_rows(page=1, per_page=10, *args, **kwargs): """ 获取投资申请列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(ApplyPut, page, per_page, *args, **kwargs) return rows def apply_put_match(apply_put_id, apply_get_ids, accept_split=0): """ 投资申请匹配 :param apply_put_id: :param apply_get_ids: :param accept_split: :return: """ from app_backend.api.apply_get import get_apply_get_rows_by_ids apply_put_info = get_apply_put_row_by_id(apply_put_id) # 判断是否已经匹配 if apply_put_info.status_order == int(STATUS_ORDER_COMPLETED): raise Exception(u'不能重复匹配') apply_get_list = get_apply_get_rows_by_ids(apply_get_ids) # 判断是否同一个人 apply_get_user_ids = [apply_get_item.user_id for apply_get_item in apply_get_list] if apply_put_info.user_id in apply_get_user_ids: raise Exception(u'不能匹配给自己') # 判断金额 money_need_match = apply_put_info.money_apply - apply_put_info.money_order apply_get_amount = sum( [apply_get_item.money_apply - apply_get_item.money_order for apply_get_item in apply_get_list]) # 如果接收拆分,判断被拆金额是否大于被匹配金额 if accept_split: if money_need_match < apply_get_amount: raise Exception(u'选择金额太大') elif money_need_match != apply_get_amount: raise Exception(u'金额不匹配') # 循环提现申请,生成对应的订单 try: money_order = 0 for apply_get_item in apply_get_list: money_match = apply_get_item.money_apply - apply_get_item.money_order current_time = datetime.utcnow() order_data = { 'apply_put_id': apply_put_id, 'apply_get_id': apply_get_item.id, 'apply_put_uid': apply_put_info.user_id, 'apply_get_uid': apply_get_item.user_id, 'money': money_match, 'status_audit': STATUS_AUDIT_SUCCESS, 'audit_time': current_time, 'create_time': current_time, 'update_time': current_time, } db.session.add(Order(**order_data)) # 更新提现申请状态 apply_get_update_info = { 'money_order': apply_get_item.money_order + money_match, 'status_order': STATUS_ORDER_COMPLETED, 'update_time': current_time, } apply_get_obj = db.session.query(ApplyGet).filter(ApplyGet.id == apply_get_item.id) apply_get_obj.update(apply_get_update_info) money_order += money_match # 更新投资订单金额和申请状态 current_time = datetime.utcnow() apply_put_update_data = { 'money_order': ApplyPut.money_order + money_order, 'status_order': STATUS_ORDER_COMPLETED if money_order == money_need_match else STATUS_ORDER_PROCESSING, 'update_time': current_time, } # result = edit_apply_get(apply_get_id, apply_get_update_info) apply_put_obj = db.session.query(ApplyPut).filter(ApplyPut.id == apply_put_id) result = apply_put_obj.update(apply_put_update_data) db.session.commit() return result except Exception as e: print traceback.print_exc() db.session.rollback() raise e def apply_put_stats(time_based='hour'): """ 投资申请统计 :return: """ # 按小时统计 if time_based == 'hour': start_time, end_time = get_current_day_time_ends() hours = get_hours(False) hours_zerofill = get_hours() result = dict(zip(hours, [0] * len(hours))) rows = db.session \ .query(func.hour(ApplyPut.create_time).label('hour'), func.sum(ApplyPut.money_apply)) \ .filter(ApplyPut.create_time >= time_local_to_utc(start_time), ApplyPut.create_time <= time_local_to_utc(end_time)) \ .group_by('hour') \ .limit(len(hours)) \ .all() result.update(dict(rows)) return [(hours_zerofill[i], result[hour]) for i, hour in enumerate(hours)] # 按日期统计 if time_based == 'date': start_time, end_time = get_current_month_time_ends() today = datetime.today() days = get_days(year=today.year, month=today.month, zerofill=False) days_zerofill = get_days(year=today.year, month=today.month) result = dict(zip(days, [0] * len(days))) rows = db.session \ .query(func.day(ApplyPut.create_time).label('date'), func.sum(ApplyPut.money_apply)) \ .filter(ApplyPut.create_time >= time_local_to_utc(start_time), ApplyPut.create_time <= time_local_to_utc(end_time)) \ .group_by('date') \ .limit(len(days)) \ .all() result.update(dict(rows)) return [(days_zerofill[i], result[day]) for i, day in enumerate(days)] # 按月份统计 if time_based == 'month': start_time, end_time = get_current_year_time_ends() months = get_months(False) months_zerofill = get_months() result = dict(zip(months, [0] * len(months))) rows = db.session \ .query(func.month(ApplyPut.create_time).label('month'), func.sum(ApplyPut.money_apply)) \ .filter(ApplyPut.create_time >= time_local_to_utc(start_time), ApplyPut.create_time <= time_local_to_utc(end_time)) \ .group_by('month') \ .limit(len(months)) \ .all() result.update(dict(rows)) return [(months_zerofill[i], result[month]) for i, month in enumerate(months)] ================================================ FILE: app_backend/api/author.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: author.py @time: 16-1-17 上午12:05 """ from app_backend.models import Author from app_backend.tools.db import get_row, get_rows, get_row_by_id, add, edit, delete def get_author_row_by_id(author_id): """ 通过 id 获取博客信息 :param author_id: :return: None/object """ return get_row_by_id(Author, author_id) def get_author_row(*args, **kwargs): """ 获取博客信息 :param args: :param kwargs: :return: None/object """ return get_row(Author, *args, **kwargs) def add_author(author_data): """ 添加博客信息 :param author_data: :return: None/Value of author.id """ return add(Author, author_data) def edit_author(author_id, author_data): """ 修改博客信息 :param author_id: :param author_data: :return: Number of affected rows (Example: 0/1) """ return edit(Author, author_id, author_data) def delete_author(author_id): """ 删除博客信息 :param author_id: :return: Number of affected rows (Example: 0/1) """ return delete(Author, author_id) def get_author_rows(page=1, per_page=10, *args, **kwargs): """ 获取博客列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(Author, page, per_page, *args, **kwargs) return rows ================================================ FILE: app_backend/api/blog.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @blog: zhanghe @software: PyCharm @file: blog.py @time: 16-1-21 上午10:24 """ from app_backend.models import Blog from app_backend.tools.db import get_row, get_rows, get_row_by_id, add, edit, delete from app_backend.lib.counter import Counter from app_backend.lib.container import Container def get_blog_row_by_id(blog_id): """ 通过 id 获取博客信息 :param blog_id: :return: None/object """ return get_row_by_id(Blog, blog_id) def get_blog_row(*args, **kwargs): """ 获取博客信息 :param args: :param kwargs: :return: None/object """ return get_row(Blog, *args, **kwargs) def add_blog(blog_data): """ 添加博客信息 :param blog_data: :return: None/Value of blog.id """ return add(Blog, blog_data) def edit_blog(blog_id, blog_data): """ 修改博客信息 :param blog_id: :param blog_data: :return: Number of affected rows (Example: 0/1) """ return edit(Blog, blog_id, blog_data) def delete_blog(blog_id): """ 删除博客信息 :param blog_id: :return: Number of affected rows (Example: 0/1) """ return delete(Blog, blog_id) def get_blog_rows(page=1, per_page=10, *args, **kwargs): """ 获取博客列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(Blog, page, per_page, *args, **kwargs) return rows def get_blog_counter(blog_id): """ 获取blog计数器 :param blog_id: :return: """ blog_cnt_obj = Counter('blog') return blog_cnt_obj.counter_blog_item(blog_id) def set_blog_counter(blog_id, stat_type, num): """ 设置blog计数器 :param blog_id: :param stat_type: :param num: :return: """ blog_cnt_obj = Counter('blog') return blog_cnt_obj.set_blog_counter(blog_id, stat_type, num) def get_blog_list_counter(blog_id_list): """ 获取blog计数器 :param blog_id_list: :return: """ blog_cnt_obj = Counter('blog') return blog_cnt_obj.counter_blog_list(blog_id_list) def get_blog_container_status(blog_id, uid): """ 获取blog容器状态 :param blog_id: :param uid: :return: """ blog_container_obj = Container('blog') return blog_container_obj.get_item_container_status(blog_id, uid) def get_blog_list_container_status(blog_id_list, uid): """ 获取blog容器状态 :param blog_id_list: :param uid: :return: """ blog_container_obj = Container('blog') return blog_container_obj.get_item_list_container_status(blog_id_list, uid) def add_blog_stat_item(stat_type, blog_id, uid): """ 添加blog统计明细 :param stat_type: :param blog_id: :param uid: :return: """ blog_container_obj = Container('blog') return blog_container_obj.add_item(stat_type, blog_id, uid) if __name__ == '__main__': blog_rows = get_blog_rows(1, 10) if blog_rows: for item in blog_rows.items: print item.id, item.author, item.title, item.pub_date import json print json.dumps(get_blog_counter(['1', '2', '3']), indent=4, ensure_ascii=False) ================================================ FILE: app_backend/api/complaint.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: complaint.py @time: 2017/4/29 下午2:30 """ from app_backend.models import Complaint from app_backend.tools.db import get_row, get_rows, get_row_by_id, add, edit, delete def get_complaint_row_by_id(complaint_id): """ 通过 id 获取投诉信息 :param complaint_id: :return: None/object """ return get_row_by_id(Complaint, complaint_id) def get_complaint_row(*args, **kwargs): """ 获取投诉信息 :param args: :param kwargs: :return: None/object """ return get_row(Complaint, *args, **kwargs) def add_complaint(complaint_data): """ 添加投诉信息 :param complaint_data: :return: None/Value of complaint.id """ return add(Complaint, complaint_data) def edit_complaint(complaint_id, complaint_data): """ 修改投诉信息 :param complaint_id: :param complaint_data: :return: Number of affected rows (Example: 0/1) """ return edit(Complaint, complaint_id, complaint_data) def delete_complaint(complaint_id): """ 删除投诉信息 :param complaint_id: :return: Number of affected rows (Example: 0/1) """ return delete(Complaint, complaint_id) def get_complaint_rows(page=1, per_page=10, *args, **kwargs): """ 获取投诉列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(Complaint, page, per_page, *args, **kwargs) return rows ================================================ FILE: app_backend/api/message.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: message.py @time: 2017/6/24 下午10:20 """ from app_backend.models import Message from app_backend.tools.db import get_row, get_rows, get_row_by_id, add, edit, delete def get_message_row_by_id(message_id): """ 通过 id 获取留言信息 :param message_id: :return: None/object """ return get_row_by_id(Message, message_id) def get_message_row(*args, **kwargs): """ 获取留言信息 :param args: :param kwargs: :return: None/object """ return get_row(Message, *args, **kwargs) def add_message(message_data): """ 添加留言信息 :param message_data: :return: None/Value of message.id """ return add(Message, message_data) def edit_message(message_id, message_data): """ 修改留言信息 :param message_id: :param message_data: :return: Number of affected rows (Example: 0/1) """ return edit(Message, message_id, message_data) def delete_message(message_id): """ 删除留言信息 :param message_id: :return: Number of affected rows (Example: 0/1) """ return delete(Message, message_id) def get_message_rows(page=1, per_page=10, *args, **kwargs): """ 获取留言列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(Message, page, per_page, *args, **kwargs) return rows ================================================ FILE: app_backend/api/order.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: order.py @time: 2017/4/13 下午9:45 """ from datetime import datetime from sqlalchemy import func from app_backend.models import Order, ApplyGet, ApplyPut from app_backend.tools.db import get_row, get_lists, get_rows, get_row_by_id, add, edit, delete from app_backend.database import db from app_common.maps.status_audit import STATUS_AUDIT_SUCCESS from app_common.maps.status_order import STATUS_ORDER_COMPLETED, STATUS_ORDER_PROCESSING from app_common.tools.date_time import get_hours, get_current_day_time_ends, time_local_to_utc, \ get_current_month_time_ends, get_days, get_current_year_time_ends, get_months def get_order_row_by_id(order_id): """ 通过 id 获取订单信息 :param order_id: :return: None/object """ return get_row_by_id(Order, order_id) def get_order_row(*args, **kwargs): """ 获取订单信息 :param args: :param kwargs: :return: None/object """ return get_row(Order, *args, **kwargs) def add_order(order_data): """ 添加订单信息 :param order_data: :return: None/Value of order.id """ return add(Order, order_data) def edit_order(order_id, order_data): """ 修改订单信息 :param order_id: :param order_data: :return: Number of affected rows (Example: 0/1) """ return edit(Order, order_id, order_data) def delete_order(order_id): """ 删除订单信息 :param order_id: :return: Number of affected rows (Example: 0/1) """ return delete(Order, order_id) def get_order_lists(*args, **kwargs): """ 获取订单列表 :param args: :param kwargs: :return: None/list """ return get_lists(Order, *args, **kwargs) def get_order_rows(page=1, per_page=10, *args, **kwargs): """ 获取订单列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(Order, page, per_page, *args, **kwargs) return rows def get_put_match_order_rows(apply_put_id): """ 获取投资匹配订单列表 :param apply_put_id: :return: list """ try: rows = db.session.query(Order, ApplyGet). \ filter(Order.apply_put_id == apply_put_id, ApplyGet.id == Order.apply_get_id). \ all() db.session.commit() return rows except Exception as e: db.session.rollback() raise e def get_get_match_order_rows(apply_get_id): """ 获取提现匹配订单列表 :param apply_get_id: :return: list """ try: rows = db.session.query(Order, ApplyPut). \ filter(Order.apply_get_id == apply_get_id, ApplyPut.id == Order.apply_put_id). \ all() db.session.commit() return rows except Exception as e: db.session.rollback() raise e def flow(order_id, next_uid): """ 订单流转 :param order_id: :param next_uid: :return: """ # 获取订单信息 # 新增投资 # 新增匹配订单(流转类型) # 修改原始订单(流转状态) # 新增订单流转记录 def order_stats(time_based='hour'): """ 订单金额统计 :return: """ # 按小时统计 if time_based == 'hour': start_time, end_time = get_current_day_time_ends() hours = get_hours(False) hours_zerofill = get_hours() result = dict(zip(hours, [0] * len(hours))) rows = db.session \ .query(func.hour(Order.create_time).label('hour'), func.sum(Order.money)) \ .filter(Order.create_time >= time_local_to_utc(start_time), Order.create_time <= time_local_to_utc(end_time)) \ .group_by('hour') \ .limit(len(hours)) \ .all() result.update(dict(rows)) return [(hours_zerofill[i], result[hour]) for i, hour in enumerate(hours)] # 按日期统计 if time_based == 'date': start_time, end_time = get_current_month_time_ends() today = datetime.today() days = get_days(year=today.year, month=today.month, zerofill=False) days_zerofill = get_days(year=today.year, month=today.month) result = dict(zip(days, [0] * len(days))) rows = db.session \ .query(func.day(Order.create_time).label('date'), func.sum(Order.money)) \ .filter(Order.create_time >= time_local_to_utc(start_time), Order.create_time <= time_local_to_utc(end_time)) \ .group_by('date') \ .limit(len(days)) \ .all() result.update(dict(rows)) return [(days_zerofill[i], result[day]) for i, day in enumerate(days)] # 按月份统计 if time_based == 'month': start_time, end_time = get_current_year_time_ends() months = get_months(False) months_zerofill = get_months() result = dict(zip(months, [0] * len(months))) rows = db.session \ .query(func.month(Order.create_time).label('month'), func.sum(Order.money)) \ .filter(Order.create_time >= time_local_to_utc(start_time), Order.create_time <= time_local_to_utc(end_time)) \ .group_by('month') \ .limit(len(months)) \ .all() result.update(dict(rows)) return [(months_zerofill[i], result[month]) for i, month in enumerate(months)] ================================================ FILE: app_backend/api/scheduling.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: scheduling.py @time: 2017/6/29 下午8:46 """ from datetime import datetime from app_backend.database import db from app_backend.models import Scheduling, SchedulingItem from app_backend.tools.db import get_row, get_rows, get_row_by_id, add, edit, delete from app_common.maps.status_audit import STATUS_AUDIT_SUCCESS from app_common.maps.type_scheduling import TYPE_SCHEDULING_GIVE def get_scheduling_row_by_id(scheduling_id): """ 通过 id 获取排单信息 :param scheduling_id: :return: None/object """ return get_row_by_id(Scheduling, scheduling_id) def get_scheduling_row(*args, **kwargs): """ 获取排单信息 :param args: :param kwargs: :return: None/object """ return get_row(Scheduling, *args, **kwargs) def add_scheduling(scheduling_data): """ 添加排单信息 :param scheduling_data: :return: None/Value of score.id """ return add(Scheduling, scheduling_data) def edit_scheduling(scheduling_id, scheduling_data): """ 修改排单信息 :param scheduling_id: :param scheduling_data: :return: Number of affected rows (Example: 0/1) """ return edit(Scheduling, scheduling_id, scheduling_data) def delete_scheduling(scheduling_id): """ 删除排单信息 :param scheduling_id: :return: Number of affected rows (Example: 0/1) """ return delete(Scheduling, scheduling_id) def get_scheduling_rows(page=1, per_page=10, *args, **kwargs): """ 获取排单列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(Scheduling, page, per_page, *args, **kwargs) return rows def give_scheduling(user_id_scheduling, amount=1): """ 赠送排单数量 :param user_id_scheduling: :param amount: :return: """ try: current_time = datetime.utcnow() # 添加排单币消费明细 scheduling_item_data = { 'user_id': 0, 'type': TYPE_SCHEDULING_GIVE, 'amount': amount, 'sc_id': user_id_scheduling, 'status_audit': STATUS_AUDIT_SUCCESS, 'audit_time': current_time, 'create_time': current_time, 'update_time': current_time, } db.session.add(SchedulingItem(**scheduling_item_data)) # 新增接收用户排单币总数量 scheduling_obj = db.session.query(Scheduling).filter(Scheduling.user_id == user_id_scheduling) scheduling_info = scheduling_obj.first() if scheduling_info: scheduling_data = { 'user_id': user_id_scheduling, 'amount': scheduling_info.amount + amount, 'update_time': current_time, } scheduling_obj.update(scheduling_data) else: scheduling_data = { 'user_id': user_id_scheduling, 'amount': amount, 'create_time': current_time, 'update_time': current_time, } db.session.add(Scheduling(**scheduling_data)) db.session.commit() return True except Exception as e: db.session.rollback() raise e ================================================ FILE: app_backend/api/scheduling_item.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: scheduling_item.py @time: 2017/6/29 下午8:46 """ from app_backend.models import SchedulingItem from app_backend.tools.db import get_row, get_rows, get_row_by_id, add, edit, delete def get_scheduling_item_item_row_by_id(scheduling_item_item_id): """ 通过 id 获取排单明细信息 :param scheduling_item_item_id: :return: None/object """ return get_row_by_id(SchedulingItem, scheduling_item_item_id) def get_scheduling_item_item_row(*args, **kwargs): """ 获取排单明细信息 :param args: :param kwargs: :return: None/object """ return get_row(SchedulingItem, *args, **kwargs) def add_scheduling_item(scheduling_item_item_data): """ 添加排单明细信息 :param scheduling_item_item_data: :return: None/Value of score.id """ return add(SchedulingItem, scheduling_item_item_data) def edit_scheduling_item(scheduling_item_item_id, scheduling_item_item_data): """ 修改排单明细信息 :param scheduling_item_item_id: :param scheduling_item_item_data: :return: Number of affected rows (Example: 0/1) """ return edit(SchedulingItem, scheduling_item_item_id, scheduling_item_item_data) def delete_scheduling_item(scheduling_item_item_id): """ 删除排单明细信息 :param scheduling_item_item_id: :return: Number of affected rows (Example: 0/1) """ return delete(SchedulingItem, scheduling_item_item_id) def get_scheduling_item_item_rows(page=1, per_page=10, *args, **kwargs): """ 获取排单明细列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(SchedulingItem, page, per_page, *args, **kwargs) return rows ================================================ FILE: app_backend/api/score.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: score.py @time: 2017/4/25 下午1:29 """ from app_backend.models import Score from app_backend.tools.db import get_row, get_rows, get_row_by_id, add, edit, delete def get_score_row_by_id(score_id): """ 通过 id 获取积分信息 :param score_id: :return: None/object """ return get_row_by_id(Score, score_id) def get_score_row(*args, **kwargs): """ 获取积分信息 :param args: :param kwargs: :return: None/object """ return get_row(Score, *args, **kwargs) def add_score(score_data): """ 添加积分信息 :param score_data: :return: None/Value of score.id """ return add(Score, score_data) def edit_score(score_id, score_data): """ 修改积分信息 :param score_id: :param score_data: :return: Number of affected rows (Example: 0/1) """ return edit(Score, score_id, score_data) def delete_score(score_id): """ 删除积分信息 :param score_id: :return: Number of affected rows (Example: 0/1) """ return delete(Score, score_id) def get_score_rows(page=1, per_page=10, *args, **kwargs): """ 获取积分列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(Score, page, per_page, *args, **kwargs) return rows ================================================ FILE: app_backend/api/user.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @user: zhanghe @software: PyCharm @file: user.py @time: 17-4-21 下午10:42 """ from datetime import datetime from sqlalchemy.sql import func from app_backend.database import db from app_backend import app from app_backend.models import User from app_backend.models import UserBank from app_backend.models import UserProfile from app_backend.tools.db import get_row, get_rows, get_row_by_id, add, edit, delete from app_common.maps.status_lock import * from app_common.maps.status_active import * from app_common.tools.date_time import get_hours, get_days, get_months, time_local_to_utc from app_common.tools.date_time import get_current_day_time_ends from app_common.tools.date_time import get_current_month_time_ends from app_common.tools.date_time import get_current_year_time_ends PER_PAGE_BACKEND = app.config['PER_PAGE_BACKEND'] def get_user_row_by_id(user_id): """ 通过 id 获取用户信息 :param user_id: :return: None/object """ return get_row_by_id(User, user_id) def get_user_row(*args, **kwargs): """ 获取用户信息 :param args: :param kwargs: :return: None/object """ return get_row(User, *args, **kwargs) def add_user(user_data): """ 添加用户信息 :param user_data: :return: None/Value of user.id """ return add(User, user_data) def edit_user(user_id, user_data): """ 修改用户信息 :param user_id: :param user_data: :return: Number of affected rows (Example: 0/1) """ return edit(User, user_id, user_data) def delete_user(user_id): """ 删除用户信息 :param user_id: :return: Number of affected rows (Example: 0/1) """ return delete(User, user_id) def get_user_rows(page=1, per_page=10, *args, **kwargs): """ 获取用户列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(User, page, per_page, *args, **kwargs) return rows def get_user_detail_rows(page=1, per_page=10, *args, **kwargs): """ 获取用户详细信息列表(分页) User UserProfile Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ try: rows = User.query. \ outerjoin(UserProfile, User.id == UserProfile.user_id). \ outerjoin(UserBank, User.id == UserBank.user_id). \ add_entity(UserProfile). \ add_entity(UserBank). \ filter(*args). \ filter_by(**kwargs). \ paginate(page, PER_PAGE_BACKEND, False) db.session.commit() return rows except Exception as e: db.session.rollback() raise e def lock(user_id): """ 锁定用户 :param user_id: :return: Number of affected rows (Example: 0/1) """ current_time = datetime.utcnow() user_data = { 'status_lock': STATUS_LOCK_OK, 'lock_time': current_time, 'update_time': current_time } result = edit_user(user_id, user_data) return result def unlock(user_id): """ 解锁用户 :param user_id: :return: Number of affected rows (Example: 0/1) """ current_time = datetime.utcnow() user_data = { 'status_lock': STATUS_LOCK_NO, 'update_time': current_time } result = edit_user(user_id, user_data) return result def user_reg_stats(time_based='hour'): """ 用户注册统计 :return: """ # 按小时统计 if time_based == 'hour': start_time, end_time = get_current_day_time_ends() hours = get_hours(False) hours_zerofill = get_hours() result = dict(zip(hours, [0] * len(hours))) rows = db.session \ .query(func.hour(User.create_time).label('hour'), func.count(User.id)) \ .filter(User.create_time >= time_local_to_utc(start_time), User.create_time <= time_local_to_utc(end_time)) \ .group_by('hour') \ .limit(len(hours)) \ .all() result.update(dict(rows)) return [(hours_zerofill[i], result[hour]) for i, hour in enumerate(hours)] # 按日期统计 if time_based == 'date': start_time, end_time = get_current_month_time_ends() today = datetime.today() days = get_days(year=today.year, month=today.month, zerofill=False) days_zerofill = get_days(year=today.year, month=today.month) result = dict(zip(days, [0] * len(days))) rows = db.session \ .query(func.day(User.create_time).label('date'), func.count(User.id)) \ .filter(User.create_time >= time_local_to_utc(start_time), User.create_time <= time_local_to_utc(end_time)) \ .group_by('date') \ .limit(len(days)) \ .all() result.update(dict(rows)) return [(days_zerofill[i], result[day]) for i, day in enumerate(days)] # 按月份统计 if time_based == 'month': start_time, end_time = get_current_year_time_ends() months = get_months(False) months_zerofill = get_months() result = dict(zip(months, [0] * len(months))) rows = db.session \ .query(func.month(User.create_time).label('month'), func.count(User.id)) \ .filter(User.create_time >= time_local_to_utc(start_time), User.create_time <= time_local_to_utc(end_time)) \ .group_by('month') \ .limit(len(months)) \ .all() result.update(dict(rows)) return [(months_zerofill[i], result[month]) for i, month in enumerate(months)] def user_active_stats(time_based='hour'): """ 用户激活统计 :return: """ # 按小时统计 if time_based == 'hour': start_time, end_time = get_current_day_time_ends() hours = get_hours(False) hours_zerofill = get_hours() result = dict(zip(hours, [0] * len(hours))) rows = db.session \ .query(func.hour(User.create_time).label('hour'), func.count(User.id)) \ .filter(User.create_time >= time_local_to_utc(start_time), User.create_time <= time_local_to_utc(end_time), User.status_active == STATUS_ACTIVE_OK) \ .group_by('hour') \ .limit(len(hours)) \ .all() result.update(dict(rows)) return [(hours_zerofill[i], result[hour]) for i, hour in enumerate(hours)] # 按日期统计 if time_based == 'date': start_time, end_time = get_current_month_time_ends() today = datetime.today() days = get_days(year=today.year, month=today.month, zerofill=False) days_zerofill = get_days(year=today.year, month=today.month) result = dict(zip(days, [0] * len(days))) rows = db.session \ .query(func.day(User.create_time).label('date'), func.count(User.id)) \ .filter(User.create_time >= time_local_to_utc(start_time), User.create_time <= time_local_to_utc(end_time), User.status_active == STATUS_ACTIVE_OK) \ .group_by('date') \ .limit(len(days)) \ .all() result.update(dict(rows)) return [(days_zerofill[i], result[day]) for i, day in enumerate(days)] # 按月份统计 if time_based == 'month': start_time, end_time = get_current_year_time_ends() months = get_months(False) months_zerofill = get_months() result = dict(zip(months, [0] * len(months))) rows = db.session \ .query(func.month(User.create_time).label('month'), func.count(User.id)) \ .filter(User.create_time >= time_local_to_utc(start_time), User.create_time <= time_local_to_utc(end_time), User.status_active == STATUS_ACTIVE_OK) \ .group_by('month') \ .limit(len(months)) \ .all() result.update(dict(rows)) return [(months_zerofill[i], result[month]) for i, month in enumerate(months)] ================================================ FILE: app_backend/api/user_auth.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: user_auth.py @time: 16-4-28 上午1:04 """ from app_backend.models import UserAuth from app_backend.tools.db import get_row, get_rows, get_row_by_id, add, edit, delete, update_rows def get_user_auth_row_by_id(user_auth_id): """ 通过 id 获取用户信息 :param user_auth_id: :return: None/object """ return get_row_by_id(UserAuth, user_auth_id) def get_user_auth_row(*args, **kwargs): """ 获取用户信息 :param args: :param kwargs: :return: None/object """ return get_row(UserAuth, *args, **kwargs) def add_user_auth(user_auth_data): """ 添加用户信息 :param user_auth_data: :return: None/Value of user.id """ return add(UserAuth, user_auth_data) def edit_user_auth(user_auth_id, user_auth_data): """ 修改用户信息 :param user_auth_id: :param user_auth_data: :return: Number of affected rows (Example: 0/1) """ return edit(UserAuth, user_auth_id, user_auth_data) def delete_user_auth(user_auth_id): """ 删除用户信息 :param user_auth_id: :return: Number of affected rows (Example: 0/1) """ return delete(UserAuth, user_auth_id) def get_user_auth_rows(page=1, per_page=10, *args, **kwargs): """ 获取用户列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(UserAuth, page, per_page, *args, **kwargs) return rows def update_user_auth_rows(data, *args, **kwargs): """ 批量更新用户信息 """ return update_rows(UserAuth, data, *args, **kwargs) ================================================ FILE: app_backend/api/user_bank.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: user_bank.py @time: 2017/4/26 下午1:46 """ from app_backend.models import UserBank from app_backend.tools.db import get_row, get_rows, get_row_by_id, add, edit, delete, update_rows def get_user_bank_row_by_id(user_bank_id): """ 通过 id 获取用户银行信息 :param user_bank_id: :return: None/object """ return get_row_by_id(UserBank, user_bank_id) def get_user_bank_row(*args, **kwargs): """ 获取用户银行信息 :param args: :param kwargs: :return: None/object """ return get_row(UserBank, *args, **kwargs) def add_user_bank(user_bank_data): """ 添加用户银行信息 :param user_bank_data: :return: None/Value of user.id """ return add(UserBank, user_bank_data) def edit_user_bank(user_bank_id, user_bank_data): """ 修改用户银行信息 :param user_bank_id: :param user_bank_data: :return: Number of affected rows (Example: 0/1) """ return edit(UserBank, user_bank_id, user_bank_data) def delete_user_bank(user_bank_id): """ 删除用户银行信息 :param user_bank_id: :return: Number of affected rows (Example: 0/1) """ return delete(UserBank, user_bank_id) def get_user_bank_rows(page=1, per_page=10, *args, **kwargs): """ 获取用户银行列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(UserBank, page, per_page, *args, **kwargs) return rows def update_user_bank_rows(data, *args, **kwargs): """ 批量更新用户银行信息 """ return update_rows(UserBank, data, *args, **kwargs) ================================================ FILE: app_backend/api/user_config.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: user_config.py @time: 2017/6/29 上午11:41 """ from app_backend.models import UserConfig from app_backend.tools.db import get_row, get_rows, get_row_by_id, add, edit, merge, delete def get_user_config_row_by_id(user_config_id): """ 通过 id 获取用户配置信息 :param user_config_id: :return: None/object """ return get_row_by_id(UserConfig, user_config_id) def get_user_config_row(*args, **kwargs): """ 获取用户配置信息 :param args: :param kwargs: :return: None/object """ return get_row(UserConfig, *args, **kwargs) def add_user_config(user_config_data): """ 添加用户配置信息 :param user_config_data: :return: None/Value of wallet.id """ return add(UserConfig, user_config_data) def edit_user_config(user_config_id, user_config_data): """ 修改用户配置信息 :param user_config_id: :param user_config_data: :return: Number of affected rows (Example: 0/1) """ return edit(UserConfig, user_config_id, user_config_data) def merge_user_config(user_config_data): """ 填充用户配置信息 :param user_config_data: :return: Value of PK """ return merge(UserConfig, user_config_data) def delete_user_config(user_config_id): """ 删除用户配置信息 :param user_config_id: :return: Number of affected rows (Example: 0/1) """ return delete(UserConfig, user_config_id) def get_user_config_rows(page=1, per_page=10, *args, **kwargs): """ 获取用户配置列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(UserConfig, page, per_page, *args, **kwargs) return rows ================================================ FILE: app_backend/api/user_profile.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @user: zhanghe @software: PyCharm @file: user_profile.py @time: 17-4-29 下午16:36 """ from app_backend.models import UserProfile from app_backend.tools.db import get_row, get_rows, get_lists, get_row_by_id, add, edit, delete from app_common.tools.tree import tree def get_user_profile_row_by_id(user_id): """ 通过 id 获取用户信息 :param user_id: :return: None/object """ return get_row_by_id(UserProfile, user_id) def get_user_profile_row(*args, **kwargs): """ 获取用户信息 :param args: :param kwargs: :return: None/object """ return get_row(UserProfile, *args, **kwargs) def add_user_profile(user_data): """ 添加用户信息 :param user_data: :return: None/Value of user.id """ return add(UserProfile, user_data) def edit_user_profile(user_id, user_data): """ 修改用户信息 :param user_id: :param user_data: :return: Number of affected rows (Example: 0/1) """ return edit(UserProfile, user_id, user_data) def delete_user_profile(user_id): """ 删除用户信息 :param user_id: :return: Number of affected rows (Example: 0/1) """ return delete(UserProfile, user_id) def get_user_profile_rows(page=1, per_page=10, *args, **kwargs): """ 获取用户列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(UserProfile, page, per_page, *args, **kwargs) return rows def get_child_users(user_id): """ 获取子节点 :param user_id: :return: """ condition = { 'user_pid': user_id } rows = get_lists(UserProfile, **condition) return [(row.user_id, row.nickname, row.type_level) for row in rows] def get_team_tree_recursion(user_id, team=None, node=None): """ 递归获取用户团队树形结构(深度优先) :param user_id: :param team: :param node: :return: """ # print '-'*10, user_id, team if not team: team = tree() node = team child_users = get_child_users(user_id) for child_user in child_users: # 遍历当前所有子节点 node[child_user] = {} # 子节点加入树 user_id_next = child_user[0] get_team_tree_recursion(user_id_next, team, node[child_user]) # 递归下一个子节点 return team ================================================ FILE: app_backend/api/wallet.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: wallet.py @time: 2017/4/25 下午1:29 """ from app_backend.models import Wallet from app_backend.tools.db import get_row, get_rows, get_row_by_id, add, edit, delete def get_wallet_row_by_id(wallet_id): """ 通过 id 获取钱包信息 :param wallet_id: :return: None/object """ return get_row_by_id(Wallet, wallet_id) def get_wallet_row(*args, **kwargs): """ 获取钱包信息 :param args: :param kwargs: :return: None/object """ return get_row(Wallet, *args, **kwargs) def add_wallet(wallet_data): """ 添加钱包信息 :param wallet_data: :return: None/Value of wallet.id """ return add(Wallet, wallet_data) def edit_wallet(wallet_id, wallet_data): """ 修改钱包信息 :param wallet_id: :param wallet_data: :return: Number of affected rows (Example: 0/1) """ return edit(Wallet, wallet_id, wallet_data) def delete_wallet(wallet_id): """ 删除钱包信息 :param wallet_id: :return: Number of affected rows (Example: 0/1) """ return delete(Wallet, wallet_id) def get_wallet_rows(page=1, per_page=10, *args, **kwargs): """ 获取钱包列表(分页) Usage: items: 信息列表 has_next: 如果本页之后还有超过一个分页,则返回True has_prev: 如果本页之前还有超过一个分页,则返回True next_num: 返回下一页的页码 prev_num: 返回上一页的页码 iter_pages(): 页码列表 iter_pages(left_edge=2, left_current=2, right_current=5, right_edge=2) 页码列表默认参数 :param page: :param per_page: :param args: :param kwargs: :return: """ rows = get_rows(Wallet, page, per_page, *args, **kwargs) return rows ================================================ FILE: app_backend/database.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: database.py @time: 17-4-20 下午13:44 """ from flask_sqlalchemy import SQLAlchemy from app_backend import app db = SQLAlchemy(app) ================================================ FILE: app_backend/filters.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: filters.py @time: 2017/4/13 下午2:33 @desc: 自定义过滤器 """ import time from app_backend.api.active import get_active_row_by_id from app_backend.api.scheduling import get_scheduling_row_by_id from app_common.maps.role_admin import ROLE_ADMIN_DICT from app_common.maps.type_active import TYPE_ACTIVE_DICT from app_common.maps.type_apply import TYPE_APPLY_DICT from app_common.maps.type_auth import TYPE_AUTH_DICT from app_common.maps.type_scheduling import TYPE_SCHEDULING_DICT from app_common.maps.status_audit import STATUS_AUDIT_DICT from app_common.maps.status_apply import STATUS_APPLY_DICT from app_common.maps.status_order import STATUS_ORDER_DICT from app_common.maps.status_delete import STATUS_DEL_DICT from app_common.maps.status_pay import STATUS_PAY_DICT from app_common.maps.status_rec import STATUS_REC_DICT from app_common.maps.type_level import TYPE_LEVEL_DICT from app_common.maps.status_active import STATUS_ACTIVE_DICT from app_common.maps.status_lock import STATUS_LOCK_DICT from app_backend import app from app_backend.views.user import get_user_profile_row_by_id from app_common.maps.type_order import TYPE_ORDER_DICT @app.template_filter('reverse') def reverse_filter(s): return s[::-1] @app.template_filter('url_t') def url_t_filter(s): return '%s?t=%s' % (s, time.time()) @app.template_filter('time_diff_pretty') def time_diff_pretty_filter(delta_s): """ 时间差友好显示 {{ 1234 | time_diff_pretty }} >> 2分34秒 :param delta_s: :return: """ delta_s *= 1.00 result = u'' if delta_s >= (365 * 24 * 60 * 60): count = int(delta_s / (365 * 24 * 60 * 60)) result += u'%s年' % count delta_s -= count * 365 * 24 * 60 * 60 if delta_s >= (30 * 24 * 60 * 60): count = int(delta_s / (30 * 24 * 60 * 60)) result += u'%s月' % count delta_s -= count * 30 * 24 * 60 * 60 if delta_s >= (24 * 60 * 60): count = int(delta_s / (24 * 60 * 60)) result += u'%s天' % count delta_s -= count * 24 * 60 * 60 if delta_s >= (60 * 60): count = int(delta_s / (60 * 60)) result += u'%s小时' % count delta_s -= count * 60 * 60 if delta_s >= 60: count = int(delta_s / 60) result += u'%s分' % count delta_s -= count * 60 if delta_s > 0: count = int(delta_s) result += u'%s秒' % count return result @app.template_filter('nickname') def filter_nickname(user_id): """ 显示用户名称 :param user_id: :return: """ user_info = get_user_profile_row_by_id(user_id) return user_info.nickname if user_info else u'系统用户' @app.template_filter('user_active') def filter_user_active(user_id): """ 用户激活码量 :param user_id: :return: """ if not user_id: return 0 row = get_active_row_by_id(user_id) return row.amount if row else 0 @app.template_filter('role_admin') def filter_role_admin(role_admin_id): """ 管理后台显示管理账号角色 :param role_admin_id: :return: """ return ROLE_ADMIN_DICT.get(role_admin_id, u'') @app.template_filter('type_apply') def filter_type_apply(type_apply_id): """ 申请类型 :param type_apply_id: :return: """ return TYPE_APPLY_DICT.get(type_apply_id, u'') @app.template_filter('type_order') def filter_type_order(type_order_id): """ 订单类型 :param type_order_id: :return: """ return TYPE_ORDER_DICT.get(type_order_id, u'') @app.template_filter('type_auth') def filter_type_auth(type_auth_id): """ 认证类型 :param type_auth_id: :return: """ return TYPE_AUTH_DICT.get(type_auth_id, u'') @app.template_filter('type_active') def filter_type_active(type_active_id): """ 激活类型 :param type_active_id: :return: """ return TYPE_ACTIVE_DICT.get(type_active_id, u'') @app.template_filter('type_scheduling') def filter_type_scheduling(type_scheduling_id): """ 排单类型 :param type_scheduling_id: :return: """ return TYPE_SCHEDULING_DICT.get(type_scheduling_id, u'') @app.template_filter('status_apply') def filter_status_apply(status_apply_id): """ 申请状态 :param status_apply_id: :return: """ return STATUS_APPLY_DICT.get(status_apply_id, u'') @app.template_filter('status_audit') def filter_status_audit(status_audit_id): """ 审核状态 :param status_audit_id: :return: """ return STATUS_AUDIT_DICT.get(status_audit_id, u'') @app.template_filter('status_order') def filter_status_order(status_order_id): """ 订单状态 :param status_order_id: :return: """ return STATUS_ORDER_DICT.get(status_order_id, u'') @app.template_filter('status_delete') def filter_status_delete(status_delete_id): """ 删除状态 :param status_delete_id: :return: """ return STATUS_DEL_DICT.get(status_delete_id, u'') @app.template_filter('status_pay') def filter_status_pay(status_pay_id): """ 支付状态 :param status_pay_id: :return: """ return STATUS_PAY_DICT.get(status_pay_id, u'') @app.template_filter('status_rec') def filter_status_rec(status_rec_id): """ 收款状态 :param status_rec_id: :return: """ return STATUS_REC_DICT.get(status_rec_id, u'') @app.template_filter('type_level') def filter_type_level(type_level_id): """ 等级类型 :param type_level_id: :return: """ return TYPE_LEVEL_DICT.get(type_level_id, u'') @app.template_filter('status_active') def filter_status_active(status_active_id): """ 激活状态 :param status_active_id: :return: """ return STATUS_ACTIVE_DICT.get(status_active_id, u'') @app.template_filter('status_lock') def filter_status_lock(status_lock_id): """ 锁定状态 :param status_lock_id: :return: """ return STATUS_LOCK_DICT.get(status_lock_id, u'') @app.template_filter('scheduling_amount') def filter_scheduling_amount(user_id): """ 排单剩余次数 :param user_id: :return: """ if not user_id: return 0 row = get_scheduling_row_by_id(user_id) return row.amount if row else 0 ================================================ FILE: app_backend/forms/__init__.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: __init__.py.py @time: 2017/4/9 上午10:13 """ from wtforms import SelectField, BooleanField from wtforms.widgets import HTMLString from wtforms.compat import text_type, iteritems from wtforms.widgets import html_params def select_multi_checkbox(field, ul_class='', **kwargs): """ 多选框控件 :param field: :param ul_class: :param kwargs: :return: """ kwargs.setdefault('type', 'checkbox') field_id = kwargs.pop('id', field.id) html = [u'') return u''.join(html) class SelectBSWidget(object): """ 自定义选择组件 """ def __call__(self, field, **kwargs): params = { 'id': field.id, 'name': field.id, 'class': 'selectpicker show-tick', # 'data-live-search': 'true', 'title': kwargs.pop('placeholder', 'Choose one of the following...'), 'data-header': kwargs.pop('data_header', 'Select a condiment'), 'data-width': kwargs.pop('data_width', 'auto') } html = ['') return HTMLString('\n'.join(html)) class SelectBS(SelectField): """ 自定义选择表单控件 """ widget = SelectBSWidget() def pre_validate(self, form): """ 校验表单传值是否合法 """ for v, _ in self.choices: # print self.data, v, type(self.data), type(v) if str(self.data) == str(v): break else: raise ValueError(self.gettext('Not a valid choice')) class CheckBoxBSWidget(object): """ 自定义复选框组件 """ input_type = 'checkbox' def __call__(self, field, **kwargs): if getattr(field, 'checked', field.data): kwargs['checked'] = True kwargs.setdefault('id', field.id) kwargs.setdefault('type', self.input_type) if 'value' not in kwargs: kwargs['value'] = 1 html = [ '
', '', '
' ] return HTMLString('\n'.join(html)) class CheckBoxBS(BooleanField): """ 自定义复选框控件 """ widget = CheckBoxBSWidget() class SelectAreaCodeWidget(object): """ 自定义选择组件 - 区号 """ def __call__(self, field, **kwargs): params = { 'id': field.id, 'name': field.id, 'class': 'selectpicker show-tick', 'data-live-search': 'true', 'title': kwargs.pop('title', 'Choose one of the following...'), 'data-header': kwargs.pop('data-header', 'Select a condiment'), } html = ['') return HTMLString('\n'.join(html)) class SelectAreaCode(SelectField): """ 自定义选择表单控件 """ widget = SelectAreaCodeWidget() def pre_validate(self, form): """ 校验表单传值是否合法 """ is_find = False for _, area_data in self.choices: for area_list in area_data.values(): if self.data in [str(i['id']) for i in area_list]: is_find = True break if is_find: break else: raise ValueError(self.gettext('Not a valid choice')) ================================================ FILE: app_backend/forms/active.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: active.py @time: 2017/6/1 下午2:42 """ from flask_login import current_user from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, BooleanField, DateField, DateTimeField, DecimalField, IntegerField from wtforms.validators import DataRequired, Length, NumberRange, EqualTo, Email, ValidationError, IPAddress from app_common.maps.status_delete import * from app_common.maps.type_auth import * from app_backend.api.user import get_user_row_by_id from app_backend.api.user_auth import get_user_auth_row from app_backend.api.user_profile import get_user_profile_row_by_id class UserRightValidate(object): """ 用户权限校验 """ def __init__(self, message=None): self.message = message def __call__(self, form, field): # 用户异常处理 user_info = get_user_row_by_id(field.data) if not user_info: raise ValidationError(u'异常操作,此用户不存在') if user_info.status_delete == int(STATUS_DEL_OK): raise ValidationError(u'异常操作,此用户已删除') user_profile_info = get_user_profile_row_by_id(field.data) if not user_profile_info: raise ValidationError(u'异常操作,此用户不存在') class ActiveAddForm(FlaskForm): """ 激活添加表单 """ user_id = StringField(u'赠送用户ID', validators=[ DataRequired(message=u'赠送用户ID不能为空'), UserRightValidate() ]) amount = IntegerField(u'赠送数量', validators=[ DataRequired(message=u'赠送数量必须为整数'), NumberRange(min=1, message=u'赠送数量必须为整数') ]) ================================================ FILE: app_backend/forms/admin.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: user.py @time: 2017/3/17 下午11:49 """ from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, BooleanField, DateField, DateTimeField, HiddenField from wtforms.validators import DataRequired, Length, NumberRange, EqualTo, Email, ValidationError, IPAddress from app_common.maps import area_code_list, role_admin_list from app_backend.api.admin import get_admin_row from app_backend.forms import SelectAreaCode, SelectBS def reg_username_repeat(form, field): """ 登录账号重复校验 """ condition = { 'username': field.data } row = get_admin_row(**condition) if row: raise ValidationError(u'登录账号重复') def password_edit_validator(form, field): """ 密码修改表单校验 :param form: :param field: :return: """ field_data_length = len(field.data) if field.data else 0 if field_data_length > 0 and (field_data_length < 6 or field_data_length > 20): raise ValidationError(u'密码长度不符') class AdminProfileForm(FlaskForm): """ 管理员基本信息表单 """ id = HiddenField('Id') username = StringField(u'管理账号', validators=[DataRequired(), Length(min=2, max=20)]) password = StringField(u'登录密码', validators=[password_edit_validator]) area_code_choices = [] for m, n in enumerate(area_code_list): area_code_choices.append((m, n)) area_id = SelectAreaCode(u'手机区号', default='0', choices=area_code_choices, validators=[DataRequired()]) phone = StringField(u'手机号码') role_id = SelectBS(u'管理角色', default='', choices=role_admin_list, validators=[DataRequired(u'管理角色不能为空')]) login_ip = StringField(u'登录IP') login_time = DateTimeField(u'登录时间') create_time = DateTimeField(u'创建时间') update_time = DateTimeField(u'更新时间') class AdminAddForm(FlaskForm): """ 管理员添加表单 """ username = StringField(u'管理账号', validators=[DataRequired(), Length(min=2, max=20)]) password = StringField(u'登录密码', validators=[DataRequired(), Length(min=6, max=20)]) area_code_choices = [] for m, n in enumerate(area_code_list): area_code_choices.append((m, n)) area_id = SelectAreaCode(u'手机区号', default='0', choices=area_code_choices, validators=[DataRequired()]) phone = StringField(u'手机号码') role_id = SelectBS(u'管理角色', default='', choices=role_admin_list, validators=[DataRequired(u'管理角色不能为空')]) login_ip = StringField(u'登录IP') login_time = DateTimeField(u'Login Time') class AdminEditForm(FlaskForm): """ 管理员编辑表单 """ id = HiddenField('Id') username = StringField(u'管理账号', validators=[DataRequired(), Length(min=2, max=20)]) password = StringField(u'登录密码', validators=[password_edit_validator]) area_code_choices = [] for m, n in enumerate(area_code_list): area_code_choices.append((m, n)) area_id = SelectAreaCode(u'手机区号', default='0', choices=area_code_choices, validators=[DataRequired()]) phone = StringField(u'手机号码') role_id = SelectBS(u'管理角色', default='', choices=role_admin_list, validators=[DataRequired(u'管理角色不能为空')]) login_ip = StringField(u'登录IP') login_time = DateTimeField(u'登录时间') create_time = DateTimeField(u'创建时间') update_time = DateTimeField(u'更新时间') class AdminRoleForm(FlaskForm): """ 管理员角色表单 """ role_id = SelectBS(u'管理角色', default='', choices=role_admin_list, validators=[DataRequired(u'管理角色不能为空')]) name = StringField(u'模块权限') note = StringField(u'权限备注') module = StringField(u'模块权限') create_time = DateTimeField(u'创建时间') update_time = DateTimeField(u'更新时间') class EditPassword(FlaskForm): """ 修改用户密码 """ password = PasswordField('New Password', validators=[ DataRequired(), Length(min=6, max=40), EqualTo('confirm', message='Passwords must match') ]) confirm = PasswordField('Repeat Password', validators=[ DataRequired(), Length(min=6, max=40) ]) ================================================ FILE: app_backend/forms/apply_get.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: apply_get.py @time: 2017/4/13 下午9:31 """ from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, BooleanField, DateField, DateTimeField from wtforms.validators import DataRequired, Length, NumberRange, EqualTo, Email, ValidationError, IPAddress from app_backend.api.user_auth import get_user_auth_row from app_backend.forms import SelectBS from app_common.maps import type_apply_list from app_common.maps import status_apply_list from app_common.maps import status_order_list from app_common.maps import status_delete_list class ApplyGetSearchForm(FlaskForm): """ 提现申请搜索表单 """ apply_get_id = StringField('Apply Get Id') user_id = StringField('User Id') type_apply = SelectBS('Type Apply', default='', choices=type_apply_list) money_apply = StringField('Type Apply') status_apply = SelectBS('Type Apply', default='', choices=status_apply_list) status_order = SelectBS('Status Order', default='', choices=status_order_list) status_delete = SelectBS('Status Delete', default='', choices=status_delete_list) min_money = StringField(u'最小金额') max_money = StringField(u'最大金额') start_time = StringField(u'开始时间') end_time = StringField(u'结束时间') ================================================ FILE: app_backend/forms/apply_put.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: apply_put.py @time: 2017/4/13 下午9:31 """ from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, BooleanField, DateField, DateTimeField from wtforms.validators import DataRequired, Length, NumberRange, EqualTo, Email, ValidationError, IPAddress from app_backend.api.user_auth import get_user_auth_row from app_backend.forms import SelectBS from app_common.maps import type_apply_list from app_common.maps import status_apply_list from app_common.maps import status_order_list from app_common.maps import status_delete_list class ApplyPutSearchForm(FlaskForm): """ 投资申请搜索表单 """ apply_put_id = StringField('Apply Put Id') user_id = StringField('User Id') type_apply = SelectBS('Type Apply', default='', choices=type_apply_list) money_apply = StringField('Type Apply') status_apply = SelectBS('Type Apply', default='', choices=status_apply_list) status_order = SelectBS('Status Order', default='', choices=status_order_list) status_delete = SelectBS('Status Delete', default='', choices=status_delete_list) min_money = StringField(u'最小金额') max_money = StringField(u'最大金额') start_time = StringField(u'开始时间') end_time = StringField(u'结束时间') ================================================ FILE: app_backend/forms/blog.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: blog.py @time: 2017/3/10 下午11:00 """ from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, BooleanField, DateField, DateTimeField from wtforms.validators import DataRequired, Length, NumberRange, EqualTo, Email, ValidationError, IPAddress class BlogAddForm(FlaskForm): """ Blog 添加表单 """ author = StringField('Author', validators=[DataRequired()]) title = StringField('Title', validators=[DataRequired(), Length(max=40)]) pub_date = DateField('Pub Date', validators=[DataRequired()]) class BlogEditForm(FlaskForm): """ Blog 编辑表单 """ author = StringField('Author', validators=[DataRequired()]) title = StringField('Title', validators=[DataRequired(), Length(max=40)]) pub_date = DateField('Pub Date', validators=[DataRequired()]) ================================================ FILE: app_backend/forms/complaint.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: complaint.py @time: 2017/6/24 下午11:13 """ from flask_login import current_user from flask_wtf import FlaskForm from wtforms import StringField, TextAreaField, PasswordField, BooleanField, DateField, DateTimeField, DecimalField, IntegerField, HiddenField from wtforms.validators import DataRequired, Length, NumberRange, EqualTo, Email, ValidationError, IPAddress class ComplaintReplyForm(FlaskForm): """ 投诉回复表单 """ id = StringField(u'投诉明细ID') send_user_id = StringField(u'投诉用户') receive_user_id = StringField(u'被投诉用户') content = TextAreaField(u'投诉内容', validators=[ DataRequired(message=u'投诉内容不能为空'), Length(min=2, message=u'请输入有效的投诉内容'), Length(max=512, message=u'投诉内容超出长度限制'), ]) content_reply = TextAreaField(u'回复内容', validators=[ DataRequired(message=u'回复内容不能为空'), Length(min=2, message=u'请输入有效的回复内容'), Length(max=512, message=u'回复内容超出长度限制'), ]) ================================================ FILE: app_backend/forms/login.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: login.py @time: 2017/3/10 下午10:49 """ import re from flask import session from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, BooleanField, DateField, DateTimeField from wtforms.validators import DataRequired, Length, NumberRange, EqualTo, Email, ValidationError, IPAddress from app_backend.api.user_auth import get_user_auth_row from app_backend import app class SmsCodeValidate(object): """ 短信验证码校验 """ def __init__(self, message=None): self.message = message self._reg = re.compile(ur'^\d{6}$') def __call__(self, form, field): data = field.data if not self._reg.match(data): raise ValidationError(self.message or u"短信验证码格式错误") code_key = '%s:%s' % ('sms_code', 'login') # print session.get(code_key), type(session.get(code_key)), data, type(data) # 测试模式下,跳过验证 if not app.config.get('TEST') and session.get(code_key) != data: raise ValidationError(self.message or u"短信验证码校验错误") class LoginForm(FlaskForm): """ 账号登录表单 """ account = StringField(u'登录账号', validators=[ DataRequired(u'登录账号不能为空'), Length(min=2, max=20, message=u'登录账号长度不符') ]) password = PasswordField(u'登录密码', validators=[ DataRequired(u'密码不能为空'), Length(min=6, max=20, message=u'密码长度不符') ]) sms = StringField(u'短信验证码', validators=[ DataRequired(u'短信验证码不能为空'), Length(min=6, max=6, message=u'短信验证码长度不符'), SmsCodeValidate() ]) remember = BooleanField('Remember Me', default=False) ================================================ FILE: app_backend/forms/order.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: order.py @time: 2017/4/13 下午9:32 """ from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, BooleanField, DateField, DateTimeField from wtforms.validators import DataRequired, Length, NumberRange, EqualTo, Email, ValidationError, IPAddress from app_backend.api.user_auth import get_user_auth_row from app_backend.forms import SelectBS from app_common.maps import status_audit_list from app_common.maps import status_pay_list from app_common.maps import status_rec_list class OrderSearchForm(FlaskForm): """ 订单搜索表单 """ order_id = StringField(U'订单ID') apply_put_id = StringField(u'申请投资ID') # 申请投资Id apply_get_id = StringField(u'申请提现ID') # 申请提现Id apply_put_uid = StringField(u'申请投资用户ID') # 申请投资用户Id apply_get_uid = StringField(u'申请提现用户ID') # 申请提现用户Id status_audit = SelectBS('Status Audit', default='', choices=status_audit_list) # 审核状态:0:待审核,1:审核通过,2:审核失败 status_pay = SelectBS('Status Pay', default='', choices=status_pay_list) # 支付状态:0:待支付,1:支付成功,2:支付失败 status_rec = SelectBS('Status Rec', default='', choices=status_rec_list) # 收款状态:0:待收款,1:收款成功,2:收款失败 start_time = StringField('Start Time') end_time = StringField('End Time') ================================================ FILE: app_backend/forms/pay.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: pay.py @time: 2017/3/18 上午12:29 """ def func(): pass class Main(object): def __init__(self): pass if __name__ == '__main__': pass ================================================ FILE: app_backend/forms/scheduling.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: scheduling.py @time: 2017/6/29 下午8:46 """ from flask_login import current_user from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, BooleanField, DateField, DateTimeField, DecimalField, IntegerField from wtforms.validators import DataRequired, Length, NumberRange, EqualTo, Email, ValidationError, IPAddress from app_common.maps.status_delete import * from app_common.maps.type_auth import * from app_backend.api.user import get_user_row_by_id from app_backend.api.user_auth import get_user_auth_row from app_backend.api.user_profile import get_user_profile_row_by_id class UserRightValidate(object): """ 用户权限校验 """ def __init__(self, message=None): self.message = message def __call__(self, form, field): # 用户异常处理 user_info = get_user_row_by_id(field.data) if not user_info: raise ValidationError(u'异常操作,此用户不存在') if user_info.status_delete == int(STATUS_DEL_OK): raise ValidationError(u'异常操作,此用户已删除') user_profile_info = get_user_profile_row_by_id(field.data) if not user_profile_info: raise ValidationError(u'异常操作,此用户不存在') class SchedulingAddForm(FlaskForm): """ 排单币添加表单 """ user_id = StringField(u'用户ID', validators=[ DataRequired(message=u'用户ID不能为空'), UserRightValidate() ]) amount = IntegerField(u'添加数量', validators=[ DataRequired(message=u'添加数量必须为整数'), NumberRange(min=1, message=u'添加数量必须为整数') ]) ================================================ FILE: app_backend/forms/score.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: score.py @time: 2017/4/25 下午1:25 """ def func(): pass class Main(object): def __init__(self): pass if __name__ == '__main__': pass ================================================ FILE: app_backend/forms/settings.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: settings.py @time: 2017/6/4 上午11:53 """ from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, BooleanField, DateField, DateTimeField, HiddenField, IntegerField, DecimalField from wtforms.validators import DataRequired, InputRequired, Length, NumberRange, EqualTo, Email, ValidationError, IPAddress, AnyOf from app_backend.forms import SelectAreaCode, CheckBoxBS class SwitchForm(FlaskForm): """ 开关配置表单 SWITCH_EXPORT = OFF # 导出 SWITCH_REG_ACCOUNT = ON # 用户账号注册 SWITCH_REG_PHONE = OFF # 用户手机注册 SWITCH_REG_EMAIL = OFF # 用户邮箱注册 SWITCH_REG_THREE_PART = OFF # 第三方平台注册 SWITCH_LOGIN_ACCOUNT = ON # 用户账号登录 SWITCH_LOGIN_PHONE = ON # 用户手机登录 SWITCH_LOGIN_EMAIL = ON # 用户邮箱登录 SWITCH_LOGIN_THREE_PART = OFF # 第三方平台登录 """ SWITCH_EXPORT = CheckBoxBS(u'导出开关') SWITCH_REG_ACCOUNT = CheckBoxBS(u'用户账号注册') SWITCH_REG_PHONE = CheckBoxBS(u'用户手机注册') SWITCH_REG_EMAIL = CheckBoxBS(u'用户邮箱注册') SWITCH_REG_THREE_PART = CheckBoxBS(u'第三方平台注册') SWITCH_LOGIN_ACCOUNT = CheckBoxBS(u'用户账号登录') SWITCH_LOGIN_PHONE = CheckBoxBS(u'用户手机登录') SWITCH_LOGIN_EMAIL = CheckBoxBS(u'用户邮箱登录') SWITCH_LOGIN_THREE_PART = CheckBoxBS(u'第三方平台登录') class UserForm(FlaskForm): """ 用户配置表单 LOCK_REG_NOT_ACTIVE_TTL = 3600*24*3 # 注册后3天内未激活 LOCK_ACTIVE_NOT_PUT_TTL = 3600*24*3 # 激活后3天内未排单 LOCK_ORDER_NOT_PAY_TTL = 3600*48 # 匹配后超过48小时不打款 LOCK_PAY_NOT_REC_TTL = 3600*48 # 收款后48小时不确认 """ LOCK_REG_NOT_ACTIVE_TTL = IntegerField(u'注册未激活时间', validators=[ DataRequired(u'时间不能为空'), NumberRange(min=0, message=u'时间必须为正数') ]) LOCK_ACTIVE_NOT_PUT_TTL = IntegerField(u'激活未排单时间', validators=[ DataRequired(u'时间不能为空'), NumberRange(min=0, message=u'时间必须为正数') ]) LOCK_ORDER_NOT_PAY_TTL = IntegerField(u'匹配未付款时间', validators=[ DataRequired(u'时间不能为空'), NumberRange(min=0, message=u'时间必须为正数') ]) LOCK_PAY_NOT_REC_TTL = IntegerField(u'收款未确认时间', validators=[ DataRequired(u'时间不能为空'), NumberRange(min=0, message=u'时间必须为正数') ]) class OrderForm(FlaskForm): """ 订单配置 # 订单限制 ORDER_MAX_AMOUNT = 10000 # 订单最大金额 ORDER_MAX_COUNT = 100 # 订单最大数量 # 推广奖励 BONUS_DIRECT = 0.03 # 直接推荐奖励 BONUS_LEVEL_FIRST = 0.05 # 一级推荐奖励 BONUS_LEVEL_SECOND = 0.05 # 二级推荐奖励 BONUS_LEVEL_THIRD = 0.03 # 三级推荐奖励 """ # 订单限制 ORDER_MAX_AMOUNT = IntegerField(u'订单最大金额', validators=[ DataRequired(u'金额不能为空'), NumberRange(min=0, message=u'金额必须为正数') ]) ORDER_MAX_COUNT = IntegerField(u'订单最大数量', validators=[ DataRequired(u'数量不能为空'), NumberRange(min=0, message=u'数量必须为正数') ]) # 推广奖励 BONUS_DIRECT = DecimalField(u'直接推荐奖励', validators=[ DataRequired(u'利息不能为空'), NumberRange(min=0, message=u'利息必须为正数') ]) BONUS_LEVEL_FIRST = DecimalField(u'一级推荐奖励', validators=[ DataRequired(u'利息不能为空'), NumberRange(min=0, max=1, message=u'奖励利息范围:0-1') ]) BONUS_LEVEL_SECOND = DecimalField(u'二级推荐奖励', validators=[ DataRequired(u'利息不能为空'), NumberRange(min=0, max=1, message=u'奖励利息范围:0-1') ]) BONUS_LEVEL_THIRD = DecimalField(u'三级推荐奖励', validators=[ DataRequired(u'利息不能为空'), NumberRange(min=0, max=1, message=u'奖励利息范围:0-1') ]) class ApplyPutForm(FlaskForm): """ 投资配置 # 单次投资金额范围 APPLY_PUT_MIN_EACH = 2000 # 最小值 APPLY_PUT_MAX_EACH = 20000 # 最大值 APPLY_PUT_STEP = 1000 # 投资金额步长(基数) # 单个用户投资限制 APPLY_PUT_USER_MAX_AMOUNT = 30000 # 单个用户投资最大交易中金额 APPLY_PUT_USER_MAX_COUNT = 1 # 单个用户投资最大交易中单数(0 表示不限制) # 每日投资限制 APPLY_PUT_MAX_AMOUNT_DAILY = 1000000 # 最大金额 APPLY_PUT_MAX_COUNT_DAILY = 0 # 最大数量(0 表示不限制) # 每月投资限制 APPLY_PUT_MAX_AMOUNT_MONTHLY = 30000000 # 最大值 APPLY_PUT_MAX_COUNT_MONTHLY = 0 # 最大数量(0 表示不限制) # 投资时间配置 APPLY_PUT_TIME_START = '00:00:00' # 每天投资申请开始时间 APPLY_PUT_TIME_END = '59:00:00' # 每天投资申请结束时间 # 分红配置 APPLY_PUT_DAYS_BONUS = 15 # 分红计算天数 APPLY_PUT_DAYS_LOCK = 15 # 投资锁定天数、提现冻结天数 APPLY_PUT_INTEREST_ON_PRINCIPAL_TTL = 3600*24*15 # 投资申请后15天完成的订单执行回收本息 """ # 单次投资金额范围 APPLY_PUT_MIN_EACH = IntegerField(u'单次投资最小金额', validators=[ DataRequired(u'金额不能为空'), NumberRange(min=0, message=u'金额必须为正数') ]) APPLY_PUT_MAX_EACH = IntegerField(u'单次投资最大金额', validators=[ DataRequired(u'金额不能为空'), NumberRange(min=0, message=u'金额必须为正数') ]) APPLY_PUT_STEP = IntegerField(u'投资金额调整基数', validators=[ DataRequired(u'金额不能为空'), NumberRange(min=0, message=u'金额必须为正数') ]) # 单个用户投资限制 APPLY_PUT_USER_MAX_AMOUNT = IntegerField(u'单个用户投资最大金额', validators=[ DataRequired(u'金额不能为空'), NumberRange(min=0, message=u'金额必须为正数') ]) APPLY_PUT_USER_MAX_COUNT = IntegerField(u'单个用户投资最大单数', validators=[ InputRequired(u'数量不能为空'), NumberRange(min=0, message=u'数量必须为正数') ]) # 每日投资限制 APPLY_PUT_MAX_AMOUNT_DAILY = IntegerField(u'每日投资最大金额', validators=[ DataRequired(u'金额不能为空'), NumberRange(min=0, message=u'金额必须为正数') ]) APPLY_PUT_MAX_COUNT_DAILY = IntegerField(u'每日投资最大单数', validators=[ DataRequired(u'数量不能为空'), NumberRange(min=0, message=u'数量必须为正数') ]) # 每月投资限制 APPLY_PUT_MAX_AMOUNT_MONTHLY = IntegerField(u'每月投资最大金额', validators=[ DataRequired(u'金额不能为空'), NumberRange(min=0, message=u'金额必须为正数') ]) APPLY_PUT_MAX_COUNT_MONTHLY = IntegerField(u'每月投资最大单数', validators=[ DataRequired(u'数量不能为空'), NumberRange(min=0, message=u'数量必须为正数') ]) # 投资时间配置 APPLY_PUT_TIME_START = StringField(u'每天投资申请开始时间', validators=[ DataRequired(u'时间不能为空') ]) APPLY_PUT_TIME_END = StringField(u'每天投资申请结束时间', validators=[ DataRequired(u'时间不能为空') ]) class ApplyGetForm(FlaskForm): """ 提现配置 # 单次提现金额范围 APPLY_GET_MIN_EACH = 2000 # 最小值 APPLY_GET_MAX_EACH = 20000 # 最大值 APPLY_GET_STEP = 1000 # 投资金额步长(基数) # 单个用户提现限制 APPLY_GET_USER_MAX_AMOUNT = 30000 # 单个用户提现最大交易中金额 APPLY_GET_USER_MAX_COUNT = 1 # 单个用户提现最大交易中单数(0 表示不限制) # 每日提现限制 APPLY_GET_MAX_AMOUNT_DAILY = 1000000 # 最大金额 APPLY_GET_MAX_COUNT_DAILY = 0 # 最大数量(0 表示不限制) # 每月提现限制 APPLY_GET_MAX_AMOUNT_MONTHLY = 30000000 # 最大值 APPLY_GET_MAX_COUNT_MONTHLY = 0 # 最大数量(0 表示不限制) # 提现时间配置 APPLY_GET_TIME_START = '00:00:00' # 每天提现申请开始时间 APPLY_GET_TIME_END = '59:00:00' # 每天提现申请结束时间 """ # 单次投资金额范围 APPLY_GET_MIN_EACH = IntegerField(u'单次提现最小金额', validators=[ DataRequired(u'金额不能为空'), NumberRange(min=0, message=u'金额必须为正数') ]) APPLY_GET_MAX_EACH = IntegerField(u'单次提现最大金额', validators=[ DataRequired(u'金额不能为空'), NumberRange(min=0, message=u'金额必须为正数') ]) APPLY_GET_STEP = IntegerField(u'提现金额调整基数', validators=[ DataRequired(u'金额不能为空'), NumberRange(min=0, message=u'金额必须为正数') ]) # 单个用户提现限制 APPLY_GET_USER_MAX_AMOUNT = IntegerField(u'单个用户提现最大金额', validators=[ DataRequired(u'金额不能为空'), NumberRange(min=0, message=u'金额必须为正数') ]) APPLY_GET_USER_MAX_COUNT = IntegerField(u'单个用户提现最大单数', validators=[ InputRequired(u'数量不能为空'), NumberRange(min=0, message=u'数量必须为正数') ]) # 每日提现限制 APPLY_GET_MAX_AMOUNT_DAILY = IntegerField(u'每日提现最大金额', validators=[ DataRequired(u'金额不能为空'), NumberRange(min=0, message=u'金额必须为正数') ]) APPLY_GET_MAX_COUNT_DAILY = IntegerField(u'每日提现最大单数', validators=[ DataRequired(u'数量不能为空'), NumberRange(min=0, message=u'数量必须为正数') ]) # 每月提现限制 APPLY_GET_MAX_AMOUNT_MONTHLY = IntegerField(u'每月提现最大金额', validators=[ DataRequired(u'金额不能为空'), NumberRange(min=0, message=u'金额必须为正数') ]) APPLY_GET_MAX_COUNT_MONTHLY = IntegerField(u'每月提现最大单数', validators=[ DataRequired(u'数量不能为空'), NumberRange(min=0, message=u'数量必须为正数') ]) # 提现时间配置 APPLY_GET_TIME_START = StringField(u'每天提现申请开始时间', validators=[ DataRequired(u'时间不能为空') ]) APPLY_GET_TIME_END = StringField(u'每天提现申请结束时间', validators=[ DataRequired(u'时间不能为空') ]) class InterestForm(FlaskForm): """ 利息配置 INTEREST_PUT = 0.01 # 投资利息(日息) # 支付奖惩比例 INTEREST_PAY_AHEAD = 0.02 # 提前支付奖金比例 INTEREST_PAY_DELAY = 0.02 # 延迟支付罚金比例 # 支付时间差 DIFF_TIME_PAY_AHEAD = 60*60*1 # 提前支付奖金时间差 DIFF_TIME_PAY_DELAY = 60*60*24 # 延迟支付罚金时间差 # 确认奖惩比例 INTEREST_REC_AHEAD = 0.02 # 提前确认奖金比例 INTEREST_REC_DELAY = 0.02 # 延迟确认罚金比例 # 确认时间差 DIFF_TIME_REC_AHEAD = 60*60*1 # 提前确认奖金时间差 DIFF_TIME_REC_DELAY = 60*60*24 # 延迟确认罚金时间差 """ # 利息配置 INTEREST_PUT = DecimalField(u'投资利息(日息)', validators=[ DataRequired(u'利息不能为空'), NumberRange(min=0, message=u'利息必须为正数') ]) # 支付奖惩比例 INTEREST_PAY_AHEAD = DecimalField(u'提前支付奖金比例', validators=[ DataRequired(u'奖金比例不能为空'), NumberRange(min=0, message=u'奖金比例必须为正数') ]) INTEREST_PAY_DELAY = DecimalField(u'延迟支付罚金比例', validators=[ DataRequired(u'罚金比例不能为空'), NumberRange(min=0, message=u'罚金比例必须为正数') ]) # 支付时间差 DIFF_TIME_PAY_AHEAD = IntegerField(u'提前支付奖金时间', validators=[ DataRequired(u'时间不能为空'), NumberRange(min=0, message=u'时间必须为正数') ]) DIFF_TIME_PAY_DELAY = IntegerField(u'延迟支付罚金时间', validators=[ DataRequired(u'时间不能为空'), NumberRange(min=0, message=u'时间必须为正数') ]) # 确认奖惩比例 INTEREST_REC_AHEAD = DecimalField(u'提前确认奖金比例', validators=[ DataRequired(u'奖金比例不能为空'), NumberRange(min=0, message=u'奖金比例必须为正数') ]) INTEREST_REC_DELAY = DecimalField(u'延迟确认罚金比例', validators=[ DataRequired(u'罚金比例不能为空'), NumberRange(min=0, message=u'罚金比例必须为正数') ]) # 确认时间差 DIFF_TIME_REC_AHEAD = IntegerField(u'提前确认奖金时间', validators=[ DataRequired(u'时间不能为空'), NumberRange(min=0, message=u'时间必须为正数') ]) DIFF_TIME_REC_DELAY = IntegerField(u'延迟确认罚金时间', validators=[ DataRequired(u'时间不能为空'), NumberRange(min=0, message=u'时间必须为正数') ]) ================================================ FILE: app_backend/forms/user.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: user.py @time: 2017/3/17 下午11:49 """ from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, BooleanField, DateField, DateTimeField, HiddenField, IntegerField from wtforms.validators import DataRequired, Length, NumberRange, EqualTo, Email, ValidationError, IPAddress, \ InputRequired from flask_login import current_user from app_backend.api.user import get_user_row_by_id from app_backend.models import UserProfile, UserAuth from app_backend.forms import SelectBS, CheckBoxBS from app_common.maps import status_lock_list from app_common.maps import status_active_list from app_common.maps import area_code_list from app_backend.api.user_auth import get_user_auth_row from app_backend.api.user_profile import get_user_profile_row from app_backend.forms import SelectAreaCode, CheckBoxBS from app_common.maps.status_delete import STATUS_DEL_OK from app_common.maps.type_auth import TYPE_AUTH_ACCOUNT def reg_email_repeat(form, field): """ 邮箱重复校验 """ condition = { 'auth_type': 'email', 'auth_key': field.data } row = get_user_auth_row(**condition) if row: raise ValidationError(u'注册邮箱重复') class PhoneFormatValidate(object): """ 手机号码格式校验 """ def __init__(self, message=None): self.message = message def __call__(self, form, field): phone_len = len(field.data) if phone_len < 6 or phone_len > 11: raise ValidationError(u'手机号码长度不符') # 中国手机号码格式校验 if form.area_id.data == '0' and phone_len != 11: raise ValidationError(u'手机号码长度不符') if field.data.startswith('0'): raise ValidationError(u'手机号码格式不符') class PhoneRepeatValidate(object): """ 手机重复校验 (编辑重复校验排除当前用户) """ def __init__(self, message=None): self.message = message def __call__(self, form, field): condition = [ UserProfile.area_id == form.area_id.data, UserProfile.phone == field.data, UserProfile.user_id != form.user_id.data ] row = get_user_profile_row(*condition) if row: raise ValidationError(self.message or u'手机号码重复') class IdCardRepeatValidate(object): """ 身份证号重复校验 (编辑重复校验排除当前用户) """ def __init__(self, message=None): self.message = message def __call__(self, form, field): condition = [ UserProfile.area_id == form.area_id.data, UserProfile.id_card == field.data, UserProfile.user_id != form.user_id.data ] row = get_user_profile_row(*condition) if row: raise ValidationError(self.message or u'身份证号重复') class RegAccountRepeatValidate(object): """ 登录账号重复校验 """ def __init__(self, message=None): self.message = message def __call__(self, form, field): condition = [ UserAuth.type_auth == TYPE_AUTH_ACCOUNT, UserAuth.auth_key == field.data, UserAuth.user_id != form.user_id.data ] row = get_user_auth_row(*condition) if row: raise ValidationError(self.message or u'登录账号重复') class PasswordFormatValidate(object): """ 密码格式校验 """ def __init__(self, message=None): self.message = message def __call__(self, form, field): password_len = len(field.data) if password_len > 0 and (password_len < 6 or password_len > 20): raise ValidationError(self.message or u'密码长度不符') class UserProfileForm(FlaskForm): """ 用户基本信息表单 """ user_id = HiddenField('User Id', validators=[DataRequired()]) user_pid = StringField(u'推荐人ID', validators=[InputRequired()]) nickname = StringField(u'用户名称') avatar_url = StringField(u'用户头像') email = StringField(u'电子邮箱') area_code_choices = [] for m, n in enumerate(area_code_list): area_code_choices.append((m, n)) area_id = SelectAreaCode(u'手机区号', default='0', choices=area_code_choices, validators=[DataRequired()]) area_code = StringField('Area Code') phone = StringField(u'手机号码', validators=[ DataRequired(u'手机号码不能为空'), PhoneFormatValidate(), PhoneRepeatValidate() ]) birthday = DateField(u'出生日期') real_name = StringField(u'真实姓名', validators=[ DataRequired(u'真实姓名不能为空'), Length(min=2, max=20, message=u'真实姓名长度不符') ]) id_card = StringField(u'身份证号', validators=[ DataRequired(u'身份证号不能为空'), Length(min=18, max=18, message=u'身份证号长度不符'), IdCardRepeatValidate() ]) create_time = DateTimeField(u'创建时间') update_time = DateTimeField(u'修改时间') class UserAuthForm(FlaskForm): """ 用户登录认证信息表单 """ id = HiddenField('Id', validators=[DataRequired()]) user_id = HiddenField('User Id', validators=[DataRequired()]) type_auth = StringField(u'账号类型') auth_key = StringField(u'登录账号', validators=[ DataRequired(u'登录账号不能为空'), Length(min=2, max=20, message=u'登录账号长度不符'), RegAccountRepeatValidate() ]) auth_secret = PasswordField(u'登录密码', validators=[ PasswordFormatValidate() ]) status_verified = CheckBoxBS(u'认证状态') create_time = DateTimeField(u'创建时间') update_time = DateTimeField(u'更新时间') class UserBankForm(FlaskForm): """ 用户基本银行信息表单 """ user_id = HiddenField(u'用户ID', validators=[DataRequired()]) account_name = StringField(u'账户姓名', validators=[ DataRequired(u'账户姓名不能为空'), Length(min=2, max=20, message=u'账户姓名长度不符'), ]) bank_name = StringField(u'银行名称', validators=[DataRequired(u'银行名称不能为空')]) bank_address = StringField(u'支行名称', validators=[DataRequired(u'支行名称不能为空')]) bank_account = StringField(u'银行卡号', validators=[DataRequired(u'银行卡号不能为空')]) status_verified = CheckBoxBS(u'认证状态') status_delete = StringField(u'删除状态') create_time = DateTimeField(u'创建时间') update_time = DateTimeField(u'更新时间') class EditPassword(FlaskForm): """ 修改用户密码 """ password = PasswordField('New Password', validators=[ DataRequired(), Length(min=6, max=40), EqualTo('confirm', message='Passwords must match') ]) confirm = PasswordField('Repeat Password', validators=[ DataRequired(), Length(min=6, max=40) ]) class UserSearchForm(FlaskForm): """ 用户搜索表单 """ user_id = StringField(u'用户ID') user_name = StringField(u'用户名称') start_time = StringField('Start Time') end_time = StringField('End Time') status_active = SelectBS('Status Active', default='', choices=status_active_list) status_lock = SelectBS('Status Lock', default='', choices=status_lock_list) class UserRightValidate(object): """ 用户权限校验 """ def __init__(self, message=None): self.message = message def __call__(self, form, field): # 用户异常处理 user_info = get_user_row_by_id(field.data) if not user_info: raise ValidationError(u'异常操作,此用户不存在') if user_info.status_delete == int(STATUS_DEL_OK): raise ValidationError(u'异常操作,此用户已删除') class UserConfigForm(FlaskForm): """ 用户配置表单 """ user_id = StringField(u'用户ID', validators=[ DataRequired(message=u'用户ID不能为空'), UserRightValidate() ]) team_bonus = StringField(u'团队奖金配置') ================================================ FILE: app_backend/forms/wallet.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: wallet.py @time: 2017/4/25 下午1:26 """ def func(): pass class Main(object): def __init__(self): pass if __name__ == '__main__': pass ================================================ FILE: app_backend/lib/__init__.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: __init__.py @time: 16-1-27 上午10:26 """ def func(): pass class Main(object): def __init__(self): pass if __name__ == '__main__': pass ================================================ FILE: app_backend/lib/captcha.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: captcha.py @time: 16-4-11 下午9:23 """ import random from PIL import Image, ImageDraw, ImageFont, ImageFilter from config import BASE_DIR class Captcha(object): """ 验证码(生成) """ # map:将str函数作用于后面序列的每一个元素 _letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母, 去除可能干扰的i, l, o, z _upper_cases = _letter_cases.upper() # 大写字母 _numbers = ''.join(map(str, range(3, 10))) # 数字, 去除可能干扰的0, 1, 2 init_chars = ''.join((_upper_cases, _numbers)) def __init__(self, size=(120, 30), chars=init_chars, mode="RGB", bg_color=(255, 255, 255), fg_color=(255, 0, 0), line_color=(255, 0, 0), point_color=(255, 0, 0), font_type='%s/%s' % (BASE_DIR, 'app_backend/static/fonts/Ubuntu-B.ttf'), font_size=18, length=4, draw_lines=True, n_lines=(1, 2), draw_points=True, point_chance=2): """ :param size: 图片的大小,格式(宽,高),默认为(120, 30) :param chars: 允许的字符集合,格式字符串 :param mode: 图片模式,默认为RGB :param bg_color: 背景颜色,默认为白色 :param fg_color: 前景色,验证码字符颜色 :param font_type: 验证码字体 :param font_size: 验证码字体大小 :param length: 验证码字符个数 :param draw_lines: 是否划干扰线 :param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效 :param draw_points: 是否画干扰点 :param point_chance: 干扰点出现的概率,大小范围[0, 50] :return: """ self.size = size self.width, self.height = size self.chars = chars self.bg_color, self.fg_color, self.line_color, self.point_color = bg_color, fg_color, line_color, point_color self.font_type, self.font_size = font_type, font_size self.length = length self.draw_lines, self.n_lines, self.draw_points = draw_lines, n_lines, draw_points self.point_chance = point_chance self.img = Image.new(mode, size, bg_color) # 创建图形 self.draw = ImageDraw.Draw(self.img) # 创建画笔 def _get_chars(self): """ 生成给定长度的字符串,返回列表格式 """ return random.sample(self.chars, self.length) def _create_lines(self): """ 绘制干扰线 """ line_num = random.randint(*self.n_lines) # 干扰线条数 for i in range(line_num): # 起始点 begin = (random.randint(0, self.size[0]), random.randint(0, self.size[1])) # 结束点 end = (random.randint(0, self.size[0]), random.randint(0, self.size[1])) self.draw.line([begin, end], fill=self.line_color) def _create_points(self): """ 绘制干扰点 """ chance = min(50, max(0, int(self.point_chance))) # 大小限制在[0, 50] for w in xrange(self.width): for h in xrange(self.height): tmp = random.randint(0, 50) if tmp > 50 - chance: self.draw.point((w, h), fill=self.point_color) def _create_code_str(self): """ 绘制验证码字符 """ c_chars = self._get_chars() c_str = '%s' % ''.join(c_chars) font = ImageFont.truetype(self.font_type, self.font_size) font_width, font_height = font.getsize(c_str) self.draw.text(((self.width - font_width) / 3, (self.height - font_height) / 4), c_str, font=font, fill=self.fg_color) return c_str def get(self): if self.draw_lines: self._create_lines() if self.draw_points: self._create_points() code_str = self._create_code_str() # 图形扭曲参数 params = [1 - float(random.randint(1, 2)) / 100, 0, 0, 0, 1 - float(random.randint(1, 10)) / 100, float(random.randint(1, 2)) / 500, 0.001, float(random.randint(1, 2)) / 500 ] img = self.img.transform(self.size, Image.PERSPECTIVE, params) # 创建扭曲 img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 滤镜,边界加强(阈值更大) return img, code_str if __name__ == '__main__': pass ================================================ FILE: app_backend/lib/cart.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: cart.py @time: 16-1-27 上午10:27 """ import redis redis_client = redis.StrictRedis(host='localhost', port=6379, db=0) class Cart(object): """ 购物车 """ uid = '' prefix = '' def __init__(self, uid, prefix='cart'): self.uid = uid self.prefix = prefix def add_item(self, pid, num=1): """ 添加物品 :param pid: :param num: :return: """ key = "%s:%s:%s" % (self.prefix, self.uid, pid) # 判断物品是否存在 if redis_client.exists(key): redis_client.hincrby(key, 'num', num) else: # 如果不存在,添加物品至购物车 redis_client.hmset(key, {'pid': pid, 'num': num}) return True def del_item(self, pid): """ 删除物品 :param pid: :return: """ key = "%s:%s:%s" % (self.prefix, self.uid, pid) # 判断物品是否存在 if redis_client.exists(key): redis_client.delete(key) return True def edit_item(self, pid, num): """ 编辑物品 :param pid: :param num: :return: True/False """ key = "%s:%s:%s" % (self.prefix, self.uid, pid) # 判断物品是否存在 if redis_client.exists(key): redis_client.hmset(key, {'pid': pid, 'num': num}) return True return False def increase(self, pid, num=1): """ 增加物品数量 :param pid: :param num: :return: """ key = "%s:%s:%s" % (self.prefix, self.uid, pid) # 判断物品是否存在 if redis_client.exists(key): redis_client.hincrby(key, 'num', num) else: # 如果不存在,添加物品至购物车 redis_client.hmset(key, {'pid': pid, 'num': num}) return True def decrease(self, pid, num=1): """ 减少物品数量 :param pid: :param num: :return: """ key = "%s:%s:%s" % (self.prefix, self.uid, pid) # 判断物品是否存在 if redis_client.exists(key): if num >= int(redis_client.hget(key, 'num')): # 如果超过,设置默认最小数量 redis_client.hmset(key, {'num': 1}) else: redis_client.hincrby(key, 'num', -num) return True def cart_list(self): """ 显示购物车 :return: list """ key = "%s:%s:*" % (self.prefix, self.uid) car_key_list = redis_client.keys(key) return [redis_client.hgetall(item) for item in car_key_list] def clean(self): """ 清空购物车 :return: 0/int """ key = "%s:%s:*" % (self.prefix, self.uid) car_key_list = redis_client.keys(key) return redis_client.delete(*car_key_list) if car_key_list else 0 def test(): obj = Cart('02') print obj.cart_list() obj.add_item('3') print obj.cart_list() obj.del_item('4') print obj.cart_list() obj.increase('3') print obj.cart_list() obj.decrease('3') print obj.cart_list() obj.add_item('4') print obj.cart_list() obj.add_item('5', 10) print obj.cart_list() obj.edit_item('5', 9) print obj.cart_list() obj.decrease('4', 10) print obj.cart_list() if __name__ == '__main__': test() """ 测试结果 [] [{'num': '1', 'pid': '3'}] [{'num': '1', 'pid': '3'}] [{'num': '2', 'pid': '3'}] [{'num': '1', 'pid': '3'}] [{'num': '1', 'pid': '4'}, {'num': '1', 'pid': '3'}] [{'num': '10', 'pid': '5'}, {'num': '1', 'pid': '4'}, {'num': '1', 'pid': '3'}] [{'num': '10', 'pid': '5'}, {'num': '1', 'pid': '4'}, {'num': '1', 'pid': '3'}] """ ================================================ FILE: app_backend/lib/container.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: container.py @time: 16-2-17 下午3:46 """ import redis import time from copy import deepcopy redis_client = redis.StrictRedis(host='localhost', port=6379, db=0) class Container(object): """ 容器(数据结构:有序集合) """ # 定义支持的实体类型 entity_name_list = ['user', 'blog', 'topic', 'subject', 'product'] # 定义支持的统计类型 stat_type_list = [ 'favor', # 支持数 'decry', # 反对数 'follow', # 关注数 'fans', # 粉丝数 'view', # 点击数 'collect', # 收藏数 'flag' # 举报数 ] # 定义项目容器状态结构 container_status_dict = { 'favor': False, # 支持 'flag': False, # 举报 'collect': False # 收藏 } def __init__(self, entity_name, prefix='container'): if entity_name not in self.entity_name_list: raise TypeError(u'类型错误') self.entity_name = entity_name self.prefix = prefix def add_item(self, stat_type, key_id, item_id): """ 添加统计项目 :param stat_type: :param key_id: :param item_id: :return:0/1 """ key = "%s:%s:%s:%s" % (self.prefix, self.entity_name, stat_type, key_id) return redis_client.zadd(key, time.time(), item_id) def del_item(self, stat_type, key_id, item_id): """ 删除统计项目 :param stat_type: :param key_id: :param item_id: :return:0/1 """ key = "%s:%s:%s:%s" % (self.prefix, self.entity_name, stat_type, key_id) return redis_client.zrem(key, item_id) def count_item(self, stat_type, key_id): """ 统计相关项目的总数 :param stat_type: :param key_id: :return: """ key = "%s:%s:%s:%s" % (self.prefix, self.entity_name, stat_type, key_id) return redis_client.zcount(key, 0, time.time()) def get_items(self, stat_type, key_id, page=1, pagesize=10): """ 分页获取相关项目列表(根据时间降序) :param stat_type: :param key_id: :param page: :param pagesize: :return:[] """ offset = 0 if page > 1: offset = (page - 1) * pagesize max_count = (page * pagesize) - 1 key = "%s:%s:%s:%s" % (self.prefix, self.entity_name, stat_type, key_id) item_ids = redis_client.zrevrange(key, offset, max_count) # 倒序取值 # item_ids = redis_client.zrange(key, offset, max_count) # 顺序取值 return item_ids def get_all_items(self, stat_type, key_id): """ 获取所有相关项目列表 :param stat_type: :param key_id: :return:[] """ key = "%s:%s:%s:%s" % (self.prefix, self.entity_name, stat_type, key_id) total = redis_client.zcard(key) item_ids = redis_client.zrevrange(key, 0, total - 1, True) return item_ids def get_item_container_status(self, key_id, item_id): """ 获取容器状态 :param key_id: :param item_id: :return: """ container_status = deepcopy(self.container_status_dict) for stat_type in container_status.keys(): key = "%s:%s:%s:%s" % (self.prefix, self.entity_name, stat_type, key_id) # 无序集合sismember; 有序用 zscore 返回值:None if redis_client.zscore(key, item_id): container_status[stat_type] = True return container_status def get_item_list_container_status(self, key_ids, item_id): """ 显示列表容器状态 按原 item_ids 列表顺序返回结果 :param key_ids: :param item_id: :return: """ container_list = [] for key_id in key_ids: container_status = self.get_item_container_status(key_id, item_id) container_list.append(container_status) return container_list def test_blog_favor(): """ 测试 blog favor """ obj = Container('blog') print obj.add_item('favor', 2, 5) # 1 print obj.add_item('favor', 2, 5) # 0 print obj.add_item('favor', 2, 6) # 1 print obj.get_items('favor', 2) # ['6', '5'] print obj.get_items('favor', 3) # [] print obj.count_item('favor', 2) # 2 print obj.del_item('favor', 3, 5) # 0 print obj.count_item('favor', 3) # 0 print obj.del_item('favor', 2, 5) # 1 print obj.count_item('favor', 2) # 1 print obj.del_item('favor', 2, 6) # 1 print obj.count_item('favor', 2) # 0 if __name__ == '__main__': test_blog_favor() ================================================ FILE: app_backend/lib/counter.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: count.py @time: 16-1-27 下午3:03 """ import redis from copy import deepcopy redis_client = redis.StrictRedis(host='localhost', port=6379, db=0) redis_pipeline = redis_client.pipeline() class Counter(object): """ 计数器 """ # 定义支持的实体类型 entity_name_list = ['user', 'blog', 'topic', 'subject', 'product'] # 定义支持的统计类型 stat_type_list = [ 'favor', # 支持 'decry', # 反对 'follow', # 关注 'fans', # 粉丝 'view', # 点击 'collect', # 收藏 'flag' # 举报 ] # 定义用户计数器结构 user_counter_dict = { 'favor': '0', # 支持数 'decry': '0', # 反对数 'follow': '0', # 关注数 'fans': '0' # 粉丝数 } # 定义话题计数器结构 topic_counter_dict = { 'favor': '0', # 支持数 'decry': '0', # 反对数 'view': '0', # 点击数 'collect': '0' # 收藏数 } # 定义博客计数器结构 blog_counter_dict = { 'favor': '0', # 支持数 'flag': '0', # 举报数 'view': '0', # 点击数 'collect': '0' # 收藏数 } def __init__(self, entity_name, prefix='counter'): if entity_name not in self.entity_name_list: raise TypeError(u'类型错误') self.entity_name = entity_name self.prefix = prefix def increase(self, item_id, stat_type, num=1): """ 增加次数 :param item_id: :param stat_type: :param num: :return: """ if stat_type not in self.stat_type_list: raise TypeError(u'类型错误') key = "%s:%s:%s" % (self.prefix, self.entity_name, item_id) # 判断物品是否存在 if redis_client.exists(key): redis_client.hincrby(key, stat_type, num) else: # 如果不存在,添加物品至购物车 redis_client.hmset(key, {stat_type: num}) return True def decrease(self, item_id, stat_type, num=1): """ 减少次数 :param item_id: :param stat_type: :param num: :return: """ key = "%s:%s:%s" % (self.prefix, self.entity_name, item_id) # 判断物品是否存在 if redis_client.exists(key): if num >= int(redis_client.hget(key, stat_type)): # 如果超过,设置默认最小数量 redis_client.hmset(key, {stat_type: 1}) else: redis_client.hincrby(key, stat_type, -num) return True def del_item(self, item_id): """ 删除物品 :param item_id: :return: """ key = "%s:%s:%s" % (self.prefix, self.entity_name, item_id) # 判断物品是否存在 if redis_client.exists(key): redis_client.delete(key) return True def counter_blog_item(self, blog_id): """ 显示 blog 计数器 :param blog_id: :return: """ key = "%s:%s:%s" % (self.prefix, self.entity_name, blog_id) # 判断物品是否存在 blog_dict = deepcopy(self.blog_counter_dict) if redis_client.exists(key): redis_result = redis_client.hgetall(key) blog_dict.update(redis_result) return blog_dict def counter_user_list_all(self): """ 显示全部用户计数器 :return: """ key = "%s:%s:*" % (self.prefix, self.entity_name) cnt_key_list = redis_client.keys(key) cnt_list = [] for item in cnt_key_list: user_dict = deepcopy(self.user_counter_dict) # 注意是深度复制,不能写成 user_dict = self.user_counter_dict user_dict.update(redis_client.hgetall(item)) cnt_list.append(user_dict) return cnt_list def counter_user_list(self, uid_list): """ 显示用户计数器 按原 uid_list 列表顺序返回结果 :param uid_list: :return: """ cnt_list = [] for uid in uid_list: key = "%s:%s:%s" % (self.prefix, self.entity_name, uid) if redis_client.exists(key): user_dict = deepcopy(self.user_counter_dict) redis_result = redis_client.hgetall(key) user_dict.update(redis_result) cnt_list.append(user_dict) return cnt_list def counter_topic_list(self, topic_list): """ 显示话题计数器 按原 uid_list 列表顺序返回结果 :param topic_list: :return: """ cnt_list = [] for topic in topic_list: key = "%s:%s:%s" % (self.prefix, self.entity_name, topic) user_dict = deepcopy(self.topic_counter_dict) if redis_client.exists(key): redis_result = redis_client.hgetall(key) user_dict.update(redis_result) cnt_list.append(user_dict) return cnt_list def counter_blog_list(self, blog_list): """ 显示 blog 计数器 按原 blog_list 列表顺序返回结果 :param blog_list: :return: """ cnt_list = [] for blog in blog_list: key = "%s:%s:%s" % (self.prefix, self.entity_name, blog) blog_dict = deepcopy(self.blog_counter_dict) if redis_client.exists(key): redis_result = redis_client.hgetall(key) blog_dict.update(redis_result) cnt_list.append(blog_dict) return cnt_list def set_blog_counter(self, blog_id, stat_type, num=1): """ 设置 blog 计数器 :param blog_id: :param stat_type: :param num: :return: """ self.increase(blog_id, stat_type, num) return self.counter_blog_item(blog_id) def test_user(): import json user_cnt_obj = Counter('user') user_cnt_obj.del_item(12) user_cnt_obj.del_item(13) user_cnt_obj.increase(12, 'favor') user_cnt_obj.increase(13, 'fans', 5) user_cnt_obj.increase(14, 'fans', 4) user_cnt_obj.increase(14, 'follow', 3) print json.dumps(user_cnt_obj.counter_user_list(['12', '13']), indent=4, ensure_ascii=False) print json.dumps(user_cnt_obj.counter_user_list_all(), indent=4, ensure_ascii=False) def test_topic(): import json topic_cnt_obj = Counter('topic') print json.dumps(topic_cnt_obj.counter_topic_list(['1', '2', '3']), indent=4, ensure_ascii=False) topic_cnt_obj.increase(4, 'fans', 4) print topic_cnt_obj.counter_blog_item(4) print topic_cnt_obj.set_blog_counter(4, 'flag') if __name__ == '__main__': # test_user() test_topic() ================================================ FILE: app_backend/lib/mongo.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: mongo.py @time: 2017/4/10 下午11:44 """ from pymongo import MongoClient from pymongo import errors import json from datetime import date, datetime import logging logger = logging.getLogger(__name__) class Mongodb(object): """ 自定义mongodb工具 """ def __init__(self, db_config, db_name=None): self.db_config = db_config if db_name is not None: self.db_config['database'] = db_name try: # 实例化mongodb self.conn = MongoClient(self.db_config['host'], self.db_config['port']) # 获取数据库对象(选择/切换) self.db = self.conn.get_database(self.db_config['database']) except errors.ServerSelectionTimeoutError, e: logger.error('连接超时:%s' % e) except Exception, e: logger.error(e) @staticmethod def __default(obj): """ 支持datetime的json encode TypeError: datetime.datetime(2015, 10, 21, 8, 42, 54) is not JSON serializable :param obj: :return: """ if isinstance(obj, datetime): return obj.strftime('%Y-%m-%d %H:%M:%S') elif isinstance(obj, date): return obj.strftime('%Y-%m-%d') else: raise TypeError('%r is not JSON serializable' % obj) def close_conn(self): """ 关闭连接 关闭所有套接字的连接池和停止监控线程。 如果这个实例再次使用它将自动重启和重新启动线程 """ self.conn.close() def find_one(self, table_name, condition=None): """ 查询单条记录 :param table_name: :param condition: :return: """ return self.db.get_collection(table_name).find_one(condition) def find_all(self, table_name, condition=None): """ 查询多条记录 :param table_name: :param condition: :return: """ return self.db.get_collection(table_name).find(condition) def count(self, table_name, condition=None): """ 查询记录总数 :param table_name: :param condition: :return: """ return self.db.get_collection(table_name).count(condition) def distinct(self, table_name, field_name): """ 查询某字段去重后值的范围 :param table_name: :param field_name: :return: """ return self.db.get_collection(table_name).distinct(field_name) def insert(self, table_name, data): """ 插入数据 :param table_name: :param data: :return: """ try: ids = self.db.get_collection(table_name).insert(data) return ids except Exception, e: logger.error('插入失败:%s' % e) return None def update(self, table_name, condition, update_data, update_type='set'): """ 批量更新数据 upsert : 如果不存在update的记录,是否插入;true为插入,默认是false,不插入。 :param table_name: :param condition: :param update_data: :param update_type: 范围:['inc', 'set', 'unset', 'push', 'pushAll', 'addToSet', 'pop', 'pull', 'pullAll', 'rename'] :return: """ if update_type not in ['inc', 'set', 'unset', 'push', 'pushAll', 'addToSet', 'pop', 'pull', 'pullAll', 'rename']: logger.error('更新失败,类型错误:%s' % update_type) return None try: result = self.db.get_collection(table_name).update_many(condition, {'$%s' % update_type: update_data}) logger.info('更新成功,匹配数量:%s;更新数量:%s' % (result.matched_count, result.modified_count)) return result.modified_count # 返回更新数量,仅支持MongoDB 2.6及以上版本 except Exception, e: logger.error('更新失败:%s' % e) return None def remove(self, table_name, condition=None): """ 删除文档记录 :param table_name: :param condition: :return: """ result = self.db.get_collection(table_name).remove(condition) if result.get('err') is None: logger.info('删除成功,删除行数%s' % result.get('n', 0)) return result.get('n', 0) else: logger.error('删除失败:%s' % result.get('err')) return None def output_row(self, table_name, condition=None, style=0): """ 格式化输出单个记录 style=0 键值对齐风格 style=1 JSON缩进风格 :param table_name: :param condition: :param style: :return: """ row = self.find_one(table_name, condition) if style == 0: # 获取KEY最大的长度作为缩进依据 max_len_key = max([len(each_key) for each_key in row.keys()]) str_format = '{0: >%s}' % max_len_key keys = [str_format.format(each_key) for each_key in row.keys()] result = dict(zip(keys, row.values())) print '********** 表名[%s] **********' % table_name for key, item in result.items(): print key, ':', item else: print json.dumps(row, indent=4, ensure_ascii=False, default=self.__default) def output_rows(self, table_name, condition=None, style=0): """ 格式化输出批量记录 style=0 键值对齐风格 style=1 JSON缩进风格 :param table_name: :param condition: :param style: :return: """ rows = self.find_all(table_name, condition) total = self.count(table_name, condition) if style == 0: count = 0 for row in rows: # 获取KEY最大的长度作为缩进依据 max_len_key = max([len(each_key) for each_key in row.keys()]) str_format = '{0: >%s}' % max_len_key keys = [str_format.format(each_key) for each_key in row.keys()] result = dict(zip(keys, row.values())) count += 1 print '********** 表名[%s] [%d/%d] **********' % (table_name, count, total) for key, item in result.items(): print key, ':', item else: for row in rows: print json.dumps(row, indent=4, ensure_ascii=False, default=self.__default) ================================================ FILE: app_backend/lib/qiniu_store.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: qiniu.py @time: 16-5-2 下午1:41 """ from urlparse import urljoin import qiniu class QiNiuClient(object): """ 七牛云存储 """ def __init__(self, app=None): """ 初始化应用 """ if app is not None: self._access_key = app.config.get('QINIU_ACCESS_KEY', '') self._secret_key = app.config.get('QINIU_SECRET_KEY', '') self._bucket_name = app.config.get('QINIU_BUCKET_NAME', '') domain = app.config.get('QINIU_BUCKET_DOMAIN') if not domain: self._base_url = 'http://' + self._bucket_name + '.qiniudn.com' else: self._base_url = 'http://' + domain def save(self, data, filename=None): """ 保存 """ auth = qiniu.Auth(self._access_key, self._secret_key) token = auth.upload_token(self._bucket_name) return qiniu.put_data(token, filename, data) def delete(self, filename): """ 删除 """ auth = qiniu.Auth(self._access_key, self._secret_key) bucket = qiniu.BucketManager(auth) return bucket.delete(self._bucket_name, filename) def url(self, filename): """ 链接 """ return urljoin(self._base_url, filename) if __name__ == '__main__': pass ================================================ FILE: app_backend/lib/rabbit_mq.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: rabbit_mq.py @time: 2017/4/1 上午9:55 """ import pika import json import traceback from config import current_config RABBIT_MQ = current_config.RABBIT_MQ _client_conn = {'conn': None} def get_conn(): """ 获取连接 :return: """ if not _client_conn.get('conn'): conn_mq = pika.BlockingConnection( pika.ConnectionParameters( host=RABBIT_MQ.get('host', '127.0.0.1'), port=RABBIT_MQ.get('port', 5672), virtual_host=RABBIT_MQ.get('virtual_host', '/'), heartbeat_interval=RABBIT_MQ.get('heartbeat_interval', 0), retry_delay=RABBIT_MQ.get('retry_delay', 3) ) ) _client_conn['conn'] = conn_mq return conn_mq else: return _client_conn['conn'] class RabbitQueue(object): """ 队列 """ def __init__(self, exchange, queue_name, exchange_type='direct', durable=True, **arguments): self.exchange = exchange self.queue_name = queue_name self.exchange_type = exchange_type self.durable = durable self.arguments = arguments # print u'实例化附加参数:', arguments self.conn = get_conn() self.channel = self.conn.channel() self.declare() def close_conn(self): """ 关闭连接 :return: """ if _client_conn.get('conn'): self.conn.close() _client_conn.pop('conn') def declare(self): """ 声明队列 """ self.channel.exchange_declare(exchange=self.exchange, exchange_type=self.exchange_type, durable=self.durable) self.channel.queue_declare(queue=self.queue_name, durable=self.durable, arguments=self.arguments) self.channel.queue_bind(exchange=self.exchange, queue=self.queue_name, routing_key=self.queue_name) self.channel.basic_qos(prefetch_count=1) def put(self, message): """ 推送队列消息 :param message: :return: """ if isinstance(message, dict): message = json.dumps(message) self.channel.basic_publish(exchange=self.exchange, routing_key=self.queue_name, body=message, properties=pika.BasicProperties( delivery_mode=2 if self.durable else 1, # make message persistent )) print " [x] Sent %r" % (message,) def get(self): """ 获取队列消息 :return: """ # data = self.channel.basic_get(self.queue_name) # print data method_frame, header_frame, body = self.channel.basic_get(self.queue_name) if method_frame: print " [x] Get %r" % (body,) print method_frame, header_frame, body self.channel.basic_ack(method_frame.delivery_tag) else: print('No message returned') def get_block(self): """ 获取队列消息(阻塞) direct 模式下多进程消费,进程轮流获取单个消息 :return: """ def callback(ch, method, properties, body): try: print " [x] Get %r" % (body,) # raise Exception('test') ch.basic_ack(delivery_tag=method.delivery_tag) except Exception as e: print traceback.print_exc() raise e self.consume(callback) def consume(self, callback): """ 消费 """ # 处理队列 self.channel.basic_consume(consumer_callback=callback, queue=self.queue_name) try: self.channel.start_consuming() except KeyboardInterrupt: self.channel.stop_consuming() self.close_conn() class RabbitPubSub(object): """ 订阅 """ def __init__(self, exchange, exchange_type='fanout', durable=True, **arguments): self.exchange = exchange self.exchange_type = exchange_type self.durable = durable self.arguments = arguments # print u'实例化附加参数:', arguments self.conn = get_conn() self.channel = self.conn.channel() self.channel.exchange_declare(exchange=self.exchange, exchange_type=self.exchange_type, durable=self.durable) def close_conn(self): """ 关闭连接 :return: """ if _client_conn.get('conn'): self.conn.close() _client_conn.pop('conn') def pub(self, message): """ 推送队列消息 :param message: :return: """ if isinstance(message, dict): message = json.dumps(message) self.channel.basic_publish(exchange=self.exchange, body=message, routing_key='', properties=pika.BasicProperties( delivery_mode=2 if self.durable else 1, # make message persistent )) print " [x] Pub %r" % (message,) def sub(self): """ 订阅队列消息 exchange_type='fanout' fanout 模式下多进程消费,进程同时同步获取消息 :return: """ result = self.channel.queue_declare(exclusive=True) queue_name = result.method.queue self.channel.queue_bind(exchange=self.exchange, queue=queue_name) print ' [*] Waiting for logs. To exit press CTRL+C' def callback(ch, method, properties, body): print " [x] Sub %r" % (body,) self.channel.basic_consume(callback, queue=queue_name, no_ack=True ) self.channel.start_consuming() class RabbitDelayQueue(object): """ 延时队列 q_d_client = RabbitDelayQueue('amq.direct', q_name, ttl=3600*24) """ def __init__(self, exchange, queue_name, exchange_type='direct', durable=True, **arguments): self.exchange = exchange self.queue_name = queue_name self.delay_queue_name = '%s_delay' % queue_name self.exchange_type = exchange_type self.durable = durable self.arguments = arguments # print u'实例化附加参数:', arguments self.conn = get_conn() self.channel = self.conn.channel() self.channel.confirm_delivery() self.channel.queue_declare(queue=queue_name, durable=durable) # We need to bind this channel to an exchange, that will be used to transfer # messages from our delay queue. self.channel.queue_bind(exchange=self.exchange, queue=queue_name) # 延时队列定义 self.delay_channel = self.conn.channel() self.delay_channel.confirm_delivery() # This is where we declare the delay, and routing for our delay channel. self.delay_channel.queue_declare(queue=self.delay_queue_name, durable=durable, arguments={ 'x-message-ttl': arguments.get('ttl', 5)*1000, # Delay until the message is transferred in milliseconds. 'x-dead-letter-exchange': self.exchange, # Exchange used to transfer the message from A to B. 'x-dead-letter-routing-key': self.queue_name # Name of the queue we want the message transferred to. }) def close_conn(self): """ 关闭连接 :return: """ if _client_conn.get('conn'): self.conn.close() _client_conn.pop('conn') def put(self, message): """ 推送队列消息 :param message: :return: """ if isinstance(message, dict): message = json.dumps(message) self.delay_channel.basic_publish(exchange='', routing_key=self.delay_queue_name, body=message, properties=pika.BasicProperties( delivery_mode=2 if self.durable else 1, # make message persistent )) print " [x] Sent %r" % (message,) def get(self): """ 获取队列消息 :return: """ # data = self.channel.basic_get(self.queue_name) # print data method_frame, header_frame, body = self.channel.basic_get(self.queue_name) if method_frame: print " [x] Get %r" % (body,) print method_frame, header_frame, body self.channel.basic_ack(method_frame.delivery_tag) else: print('No message returned') def get_block(self): """ 获取队列消息(阻塞) direct 模式下多进程消费,进程轮流获取单个消息 :return: """ def callback(ch, method, properties, body): try: print " [x] Get %r" % (body,) # raise Exception('test') ch.basic_ack(delivery_tag=method.delivery_tag) except Exception as e: print traceback.print_exc() raise e self.consume(callback) def consume(self, callback): """ 消费 """ # 处理队列 self.channel.basic_consume(consumer_callback=callback, queue=self.queue_name) try: self.channel.start_consuming() except KeyboardInterrupt: self.channel.stop_consuming() self.close_conn() class RabbitPriorityQueue(RabbitQueue): """ 优先级队列 max_priority=255 """ def __init__(self, exchange, queue_name, exchange_type='direct', durable=True, **arguments): super(RabbitPriorityQueue, self).__init__(exchange, queue_name, exchange_type, durable, **arguments) def declare(self): """ 声明队列 """ self.channel.exchange_declare(exchange=self.exchange, exchange_type=self.exchange_type, durable=self.durable) self.channel.queue_declare( queue=self.queue_name, durable=self.durable, arguments={ 'x-max-priority': self.arguments.get('max_priority', 255) } ) self.channel.queue_bind(exchange=self.exchange, queue=self.queue_name, routing_key=self.queue_name) self.channel.basic_qos(prefetch_count=1) def put(self, message, priority=0): """ 推送队列消息 :param message: :param priority: :return: """ print '--priority:', priority if isinstance(message, dict): message = json.dumps(message) self.channel.basic_publish(exchange=self.exchange, routing_key=self.queue_name, body=message, properties=pika.BasicProperties( delivery_mode=2 if self.durable else 1, # make message persistent priority=priority )) print " [x] Sent %r" % (message,) def test_queue(): """ 队列测试 参数:方法 队列名称 消息 :return: """ import sys print sys.argv if len(sys.argv) < 3: print u'参数:方法 队列名称 消息' print u'python rabbit_mq.py put q_task 123456' print u'python rabbit_mq.py get q_task' return method, q_name, msg = sys.argv[1], sys.argv[2], ''.join(sys.argv[3:4]) q_client = RabbitQueue('e_test', q_name) print u'连接id:%s' % id(q_client.conn) # 获取消息 if method == 'get': q_client.get() # 推送消息 if method == 'put': q_client.put(msg) # 阻塞获取消息 if method == 'get_block': q_client.get_block() q_client.close_conn() def test_pub_sub(): """ 队列pub/sub 参数:方法 队列名称 消息 :return: """ import sys print sys.argv if len(sys.argv) < 3: print u'参数:方法 队列名称 消息' print u'python rabbit_mq.py pub q_task 123456' print u'python rabbit_mq.py sub q_task' return method, q_name, msg = sys.argv[1], sys.argv[2], ''.join(sys.argv[3:4]) q_client = RabbitPubSub(q_name) print u'连接id:%s' % id(q_client.conn) # 获取消息 if method == 'sub': q_client.sub() # 推送消息 if method == 'pub': q_client.pub(msg) q_client.close_conn() def test_delay_queue(): """ 延时队列测试 参数:方法 队列名称 消息 :return: """ import sys print sys.argv if len(sys.argv) < 3: print u'参数:方法 队列名称 消息' print u'python rabbit_mq.py put q_delay_task 123456' print u'python rabbit_mq.py get q_delay_task' return method, q_name, msg = sys.argv[1], sys.argv[2], ''.join(sys.argv[3:4]) q_client = RabbitDelayQueue('amq.direct', q_name) print u'连接id:%s' % id(q_client.conn) # 获取消息 if method == 'get': q_client.get() # 推送消息 if method == 'put': q_client.put(msg) # 阻塞获取消息 if method == 'get_block': q_client.get_block() q_client.close_conn() def test_priority_queue(): """ 优先级队列测试 参数:方法 队列名称 消息 :return: """ import sys print sys.argv if len(sys.argv) < 3: print u'参数:方法 队列名称 消息' print u'python rabbit_mq.py put q_priority_task 111 100' print u'python rabbit_mq.py put q_priority_task 222 200' print u'python rabbit_mq.py put q_priority_task 333 150' print u'python rabbit_mq.py get q_priority_task' # 222 print u'python rabbit_mq.py get q_priority_task' # 333 print u'python rabbit_mq.py get q_priority_task' # 111 return method, q_name, msg, priority = sys.argv[1], sys.argv[2], ''.join(sys.argv[3:4]), ''.join(sys.argv[4:5]) q_client = RabbitPriorityQueue('amq.direct', q_name) print u'连接id:%s' % id(q_client.conn) # 获取消息 if method == 'get': q_client.get() # 推送消息 if method == 'put': q_client.put(msg, int(priority) if priority else 0) # 阻塞获取消息 if method == 'get_block': q_client.get_block() q_client.close_conn() if __name__ == '__main__': # test_queue() # test_pub_sub() # test_delay_queue() test_priority_queue() ================================================ FILE: app_backend/lib/redis_session.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: redis_session.py @time: 2017/3/7 上午12:41 """ # import pickle import json from datetime import timedelta from uuid import uuid4 from redis import Redis from werkzeug.datastructures import CallbackDict from flask.sessions import SessionInterface, SessionMixin class RedisSession(CallbackDict, SessionMixin): def __init__(self, initial=None, sid=None, new=False): def on_update(self): self.modified = True CallbackDict.__init__(self, initial, on_update) self.sid = sid self.new = new self.modified = False class RedisSessionInterface(SessionInterface): # serializer = pickle serializer = json session_class = RedisSession def __init__(self, redis=None, prefix='session:', **kwargs): if redis is None: redis = Redis(**kwargs) self.redis = redis self.prefix = prefix @staticmethod def generate_sid(): return str(uuid4()) @staticmethod def get_redis_expiration_time(app, session): if session.permanent: return app.permanent_session_lifetime # return timedelta(days=1) return timedelta(minutes=20) def open_session(self, app, request): sid = request.cookies.get(app.session_cookie_name) if not sid: sid = self.generate_sid() return self.session_class(sid=sid, new=True) val = self.redis.get(self.prefix + sid) if val is not None: data = self.serializer.loads(val) return self.session_class(data, sid=sid) return self.session_class(sid=sid, new=True) def save_session(self, app, session, response): domain = self.get_cookie_domain(app) if not session: self.redis.delete(self.prefix + session.sid) if session.modified: response.delete_cookie(app.session_cookie_name, domain=domain) return redis_exp = self.get_redis_expiration_time(app, session) cookie_exp = self.get_expiration_time(app, session) val = self.serializer.dumps(dict(session)) self.redis.setex(self.prefix + session.sid, val, int(redis_exp.total_seconds())) response.set_cookie(app.session_cookie_name, session.sid, expires=cookie_exp, httponly=True, domain=domain) if __name__ == '__main__': pass ================================================ FILE: app_backend/lib/sendcloud.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: sendcloud.py @time: 16-5-2 下午12:30 """ import requests import json class SendCloudClient(object): """ SendCloud 邮件发送平台 以下方法采用 API v2 (区别:请求地址,参数命名规则) link: http://sendcloud.sohu.com/doc/email_v2/ 仅列出常用接口,实际使用中定制 """ def __init__(self, app=None): """ 初始化应用 """ if app is not None: self._api_key = app.config.get('SENDCLOUD_API_KEY', '') self._api_user = app.config.get('SENDCLOUD_API_USER', '') def userinfo_get(self): """ 用户信息 查询 """ api_url = 'http://api.sendcloud.net/apiv2/userinfo/get' params = { 'apiUser': self._api_user, 'apiKey': self._api_key } return requests.get(api_url, params=params).json() def mail_send(self, mail_from, mail_to, mail_subject, mail_html): """ 普通发送 """ api_url = 'http://api.sendcloud.net/apiv2/mail/send' params = { 'apiUser': self._api_user, # API_USER 'apiKey': self._api_key, # API_KEY 'from': mail_from, # 发件人地址 'to': mail_to, # 收件人地址. 多个地址使用';'分隔 'subject': mail_subject, # 邮件标题 'html': mail_html, # 邮件的内容. 邮件格式为 text/html } return requests.post(api_url, data=params).json() def mail_sendtemplate(self, mail_from, xsmtpapi, mail_subject, template_name): """ 模板发送 不用地址列表 xsmtpapi = { 'to': ['test1@ifaxin.com', 'test2@ifaxin.com'], 'sub': { '%name%': ['user1', 'user2'], '%money%': ['1000', '2000'], } } """ api_url = 'http://api.sendcloud.net/apiv2/mail/sendtemplate' params = { 'apiUser': self._api_user, # API_USER 'apiKey': self._api_key, # API_KEY 'from': mail_from, # 发件人地址 'xsmtpapi': json.dumps(xsmtpapi), # SMTP 扩展字段 'subject': mail_subject, # 邮件标题 'templateInvokeName': template_name, # 邮件模板调用名称 } return requests.post(api_url, data=params).json() def label_list(self, query, start=0, limit=100): """ 邮件标签 查询 ( 批量查询 ) """ api_url = 'http://api.sendcloud.net/apiv2/label/list' params = { 'apiUser': self._api_user, 'apiKey': self._api_key, 'query': query, 'start': start, 'limit': limit } return requests.get(api_url, params=params).json() def addresslist_list(self, address, start=0, limit=100): """ 查询地址列表 ( 批量查询 ) """ api_url = 'http://api.sendcloud.net/apiv2/addresslist/list' params = { 'apiUser': self._api_user, 'apiKey': self._api_key, 'address': address, 'start': start, 'limit': limit } return requests.get(api_url, params=params).json() if __name__ == '__main__': pass ================================================ FILE: app_backend/lib/session.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: session.py @time: 16-8-3 下午5:56 """ import redis import json from datetime import timedelta from itsdangerous import TimestampSigner, SignatureExpired, BadTimeSignature redis_client = redis.StrictRedis(host='localhost', port=6379, db=0) class Session(object): """ 基于redis的会话管理 数据结构:字符串(用户信息序列化) """ # 定义支持的实体类型 entity_name_list = ['web', 'app'] # 设置 key 生命周期 time_out = 7200 ttl = timedelta(seconds=time_out) # 设置签名密钥 _sign_key = '121e65bfbc1012625643b21b0a5cecdd' def __init__(self, entity_name, prefix='session'): if entity_name not in self.entity_name_list: raise TypeError(u'类型错误') self.entity_name = entity_name self.prefix = prefix @staticmethod def generate_sign_key(length=32): """ 生成签名密钥 121e65bfbc1012625643b21b0a5cecdd """ import os import binascii sk = os.urandom(length/2) # print sk # print binascii.b2a_hex(sk) return binascii.b2a_hex(sk) def sign(self, session_id): """ 签名 session_id :param session_id: 通常是uuid :return: """ s = TimestampSigner(self._sign_key) return s.sign(session_id) def un_sign(self, sign_session_id): """ 校验签名 session_id :param sign_session_id: :return: """ s = TimestampSigner(self._sign_key) try: session_id = s.unsign(sign_session_id, max_age=self.time_out) return session_id except SignatureExpired as e: # 处理签名超时 raise Exception(e.message) except BadTimeSignature as e: # 处理签名错误 raise Exception(e.message) def add_item(self, key_id, item): """ 添加 item :param key_id: :param item: :return:0/1 """ key = "%s:%s:%s" % (self.prefix, self.entity_name, key_id) return redis_client.set(key, item, ex=self.ttl) def get_item(self, key_id): """ 获取 item :param key_id: :return:None/String """ key = "%s:%s:%s" % (self.prefix, self.entity_name, key_id) return redis_client.get(key) def del_item(self, key_id): """ 删除 item :param key_id: :return:0/1 """ key = "%s:%s:%s" % (self.prefix, self.entity_name, key_id) return redis_client.delete(key) def test_session(): """ 测试 a1282b2e1e2791b639f99417e7bbfe74 sdf54657dry0wetwete34.CoNfJA.LafZyL4wc77gmry9P7PPjgLGMlw sdf54657dry0wetwete34 True {"province": "\u4e0a\u6d77", "openid": "o9XD1weif6-0g_5MvZa7Bx6OkwxA", "headimgurl": "http://wx.qlogo.cn/mmopen/ALImIJLVKZtPiaaVkcKFR58xpgibiaxabiaStZYcwVNIfz4Tl8VkqzqpV5fKiaibbRGfkY2lDR9SlibQvVm2ClHD6AIhBYQeuy32qaj/0", "language": "zh_CN", "city": "\u95f8\u5317", "privilege": [], "country": "\u4e2d\u56fd", "nickname": "\u788eping\u5b50", "sex": 1} 碎ping子 """ session_app = Session('app') print session_app.generate_sign_key() session_id = 'sdf54657dry0wetwete34' session_id_sign = session_app.sign(session_id) print session_id_sign print session_app.un_sign(session_id_sign) user_info = { "province": "上海", "openid": "o9XD1weif6-0g_5MvZa7Bx6OkwxA", "headimgurl": "http://wx.qlogo.cn/mmopen/ALImIJLVKZtPiaaVkcKFR58xpgibiaxabiaStZYcwVNIfz4Tl8VkqzqpV5fKiaibbRGfkY2lDR9SlibQvVm2ClHD6AIhBYQeuy32qaj/0", "language": "zh_CN", "city": "闸北", "privilege": [], "country": "中国", "nickname": "碎ping子", "sex": 1 } user_info_str = json.dumps(user_info) print session_app.add_item(session_id, user_info_str) user_info_str = session_app.get_item(session_id) print user_info_str print json.loads(user_info_str).get('nickname') if __name__ == '__main__': test_session() ================================================ FILE: app_backend/lib/sms_chuanglan.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: cart.py @time: 16-1-27 上午10:27 """ import requests # 服务地址 host = "sms.253.com" # 端口号 port = 80 # 版本号 version = "v1.1" # 查账户信息的URI balance_get_uri = "/msg/balance" # 智能匹配模版短信接口的URI sms_send_uri = "/msg/send" # 创蓝账号 un = "xxxx" # 创蓝密码 pw = "xxxx" def get_user_balance(): """ 取账户余额 """ url = 'http://%s%s' % (host, balance_get_uri) params = { 'un': un, # 账号 'pw': pw, # 密码 } return requests.get(url, params).text def send_sms(msg, phone): """ 接口发短信 """ url = 'http://%s%s' % (host, sms_send_uri) params = { 'un': un, # 账号 'pw': pw, # 密码 'msg': msg, # 消息 'phone': phone, # 手机 'rd': 1, # 是否需要状态报告 } headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"} return requests.post(url, data=params, headers=headers, timeout=30).text if __name__ == '__main__': user_phone = "188xxxxxxxx" user_msg = "【您的签名】您的验证码是1234" # 查账户余额 print(get_user_balance()) # 调用智能匹配模版接口发短信 print(send_sms(user_msg, user_phone)) ================================================ FILE: app_backend/lib/sms_chuanglan_iso.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: sms_chuanglan_iso.py.py @time: 2017/3/3 下午10:03 """ import requests from config import current_config REQUESTS_TIME_OUT = current_config.REQUESTS_TIME_OUT class SmsChuangLanIsoApi(object): """ 创蓝国际短信接口 """ ISENDURL = 'http://222.73.117.140:8044/mt' # 单发接口 IQUERYURL = 'http://222.73.117.140:8044/bi' # 查询余额接口 BAT_SENDURL = 'http://222.73.117.140:8044/batchmt' # 群发接口 ERROR_DICT = { '0': u'提交成功', '101': u'无此用户', '102': u'密码错', '103': u'提交过快(提交速度超过流速限制)', '104': u'系统忙(因平台侧原因,暂时无法处理提交的短信)', '105': u'敏感短信(短信内容包含敏感词)', '106': u'消息长度错(>536或<=0)', '107': u'包含错误的手机号码', '108': u'手机号码个数错(群发>50000或<=0)', '109': u'无发送额度(该用户可用短信数已使用完)', '110': u'不在发送时间内', '113': u'extno格式错(非数字或者长度不对)', '116': u'签名不合法或未带签名(用户必须带签名的前提下)', '117': u'IP地址认证错,请求调用的IP地址不是系统登记的IP地址', '118': u'用户没有相应的发送权限(账号被禁止发送)', '119': u'用户已过期', '120': u'违反放盗用策略(日发限制) --自定义添加', '121': u'必填参数。是否需要状态报告,取值true或false', '122': u'5分钟内相同账号提交相同消息内容过多', '123': u'发送类型错误', '124': u'白模板匹配错误', '125': u'匹配驳回模板,提交失败', '126': u'审核通过模板匹配错误', } def __init__(self, account, password): self._sendUrl = '' # 发送短信接口url self._queryBalanceUrl = '' # 查询余额接口url self._un = account # 接口账号 self._pw = password # 接口密码 def send_international(self, phone, content, is_report=0): """ 发送国际短信 :param phone: :param content: :param is_report: :return: """ params = { 'un': self._un, 'pw': self._pw, 'sm': content, 'da': phone, 'rd': is_report, 'rf': 2, 'tf': 3, } result = requests.get(self.ISENDURL, params, timeout=REQUESTS_TIME_OUT or 30).json() return result def query_balance_international(self): """ 查询余额 :return: """ params = { 'un': self._un, 'pw': self._pw, 'rf': 2, } result = requests.get(self.IQUERYURL, params, timeout=REQUESTS_TIME_OUT or 30).json() return result if __name__ == '__main__': sms_client = SmsChuangLanIsoApi('接口账号', '接口账号') # print sms_client.send_international('8613800000000', '欢迎您注册九重天会员,此次注册验证码为1234,请在2分钟内进入验证。【九重天】', 1) print sms_client.query_balance_international() """ 账号错误: {u'r': u'101', u'success': False} 密码错误: {u'r': u'102', u'success': False} 国内号码不加86,收不到短信,但是返回True {u'id': u'17030322181000000859', u'success': True} 国内号码添加86,收到短信,也返回True {u'id': u'17030322211000000868', u'success': True} 国内错误号码 """ ================================================ FILE: app_backend/lib/token.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: token.py @time: 16-4-4 下午9:31 """ import redis import hashlib from base64 import urlsafe_b64encode, urlsafe_b64decode from datetime import timedelta redis_client = redis.StrictRedis(host='localhost', port=6379, db=0) class Token(object): """ token(数据结构:字符串) 应用场景: 1)用户id为key, 存储token为值 2)用户id为key, 存储验证码为值 """ # 定义支持的实体类型 entity_name_list = ['reg', 'login'] # 设置 key 生命周期 ttl = timedelta(seconds=60) # 设置签名密钥 _sign_key = '05bed70ae968e133a86e3ce388f4509838bd7cbf753f01c3' def __init__(self, entity_name, prefix='token'): if entity_name not in self.entity_name_list: raise TypeError(u'类型错误') self.entity_name = entity_name self.prefix = prefix @staticmethod def generate_sign_key(): """ 生成签名密钥 05bed70ae968e133a86e3ce388f4509838bd7cbf753f01c3 """ import os import binascii sk = os.urandom(24) # print sk # print binascii.b2a_hex(sk) return binascii.b2a_hex(sk) def create_token(self): """ 生成 token (基于 uuid) """ from uuid import uuid1 from itsdangerous import TimestampSigner s = TimestampSigner(self._sign_key) return s.sign(str(uuid1())) def check_token(self, token_sign): """ 校验 token, 返回解密后的 token """ from itsdangerous import TimestampSigner, SignatureExpired, BadTimeSignature s = TimestampSigner(self._sign_key) try: token = s.unsign(token_sign, max_age=60) # 60秒过期 return {'success': token} except SignatureExpired as e: # 处理签名超时 return {'error': e.message} except BadTimeSignature as e: # 处理签名错误 return {'error': e.message} def add_item(self, key_id, item): """ 添加 item :param key_id: :param item: :return:0/1 """ key = "%s:%s:%s" % (self.prefix, self.entity_name, key_id) return redis_client.set(key, item, ex=self.ttl) def get_item(self, key_id): """ 获取 item :param key_id: :return:None/String """ key = "%s:%s:%s" % (self.prefix, self.entity_name, key_id) return redis_client.get(key) def del_item(self, key_id): """ 删除 item :param key_id: :return:0/1 """ key = "%s:%s:%s" % (self.prefix, self.entity_name, key_id) return redis_client.delete(key) def test_token(): """ 测试 127.0.0.1:6379> get "token:reg:0020" "9B6E" """ token_reg = Token('reg') # print token_reg.add_item('0020', '9B6E') print token_reg.get_item('0020') print token_reg.del_item('0021') # print token_reg.del_item('0020') if __name__ == '__main__': test_token() # print urlsafe_b64encode('05bed70ae968e133a86e3ce388f4509838bd7cbf753f01c3') ================================================ FILE: app_backend/login.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: login.py @time: 17-4-20 下午1:46 """ from app_backend.models import Admin from flask_login import UserMixin class LoginUser(Admin, UserMixin): """ 用户登录类 """ ================================================ FILE: app_backend/models.py ================================================ # coding: utf-8 from sqlalchemy import Column, Date, DateTime, Index, Integer, Numeric, String, text from database import db Base = db.Model metadata = Base.metadata def to_dict(self): """ model 对象转 字典 model_obj.to_dict() """ return {c.name: getattr(self, c.name, None) for c in self.__table__.columns} Base.to_dict = to_dict class Active(Base): __tablename__ = 'active' user_id = Column(Integer, primary_key=True) amount = Column(Numeric(10, 0), nullable=False, server_default=text("'0'")) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class ActiveItem(Base): __tablename__ = 'active_item' id = Column(Integer, primary_key=True) user_id = Column(Integer, nullable=False, index=True) type = Column(Integer, nullable=False, server_default=text("'0'")) amount = Column(Numeric(8, 0), nullable=False, server_default=text("'0'")) sc_id = Column(Integer, nullable=False, index=True, server_default=text("'0'")) note = Column(String(256), nullable=False, server_default=text("''")) status_audit = Column(Integer, nullable=False, server_default=text("'0'")) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) audit_time = Column(DateTime) delete_time = Column(DateTime) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class Admin(Base): __tablename__ = 'admin' id = Column(Integer, primary_key=True) username = Column(String(20), nullable=False, unique=True, server_default=text("''")) password = Column(String(60), nullable=False, server_default=text("''")) area_id = Column(Integer, nullable=False, server_default=text("'0'")) area_code = Column(String(4), nullable=False, server_default=text("''")) phone = Column(String(20), nullable=False, server_default=text("''")) role_id = Column(Integer, nullable=False, server_default=text("'1'")) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) delete_time = Column(DateTime) login_time = Column(DateTime) login_ip = Column(String(20)) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class AdminRole(Base): __tablename__ = 'admin_role' id = Column(Integer, primary_key=True) name = Column(String(20), nullable=False, unique=True, server_default=text("''")) note = Column(String(256), nullable=False, server_default=text("''")) module = Column(String(256), nullable=False, server_default=text("''")) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class ApplyGet(Base): __tablename__ = 'apply_get' id = Column(Integer, primary_key=True) user_id = Column(Integer, nullable=False, index=True) type_apply = Column(Integer, nullable=False, server_default=text("'0'")) type_pay = Column(Integer, nullable=False, server_default=text("'0'")) type_withdraw = Column(Integer, nullable=False, server_default=text("'0'")) money_apply = Column(Numeric(8, 2), nullable=False, server_default=text("'0.00'")) money_order = Column(Numeric(8, 2), nullable=False, server_default=text("'0.00'")) status_apply = Column(Integer, nullable=False, server_default=text("'0'")) status_order = Column(Integer, nullable=False, server_default=text("'0'")) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) delete_time = Column(DateTime) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class ApplyPut(Base): __tablename__ = 'apply_put' id = Column(Integer, primary_key=True) user_id = Column(Integer, nullable=False, index=True) type_apply = Column(Integer, nullable=False, server_default=text("'0'")) type_pay = Column(Integer, nullable=False, server_default=text("'0'")) money_apply = Column(Numeric(8, 2), nullable=False, server_default=text("'0.00'")) money_order = Column(Numeric(8, 2), nullable=False, server_default=text("'0.00'")) status_apply = Column(Integer, nullable=False, server_default=text("'0'")) status_order = Column(Integer, nullable=False, server_default=text("'0'")) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) delete_time = Column(DateTime) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class AreaCode(Base): __tablename__ = 'area_code' id = Column(Integer, primary_key=True) short_code = Column(String(2), nullable=False, server_default=text("''")) area_code = Column(String(4), nullable=False, server_default=text("''")) phone_pre = Column(String(7), nullable=False, server_default=text("''")) name_c = Column(String(20), nullable=False, server_default=text("''")) name_e = Column(String(20), nullable=False, server_default=text("''")) country_area = Column(String(20), nullable=False, server_default=text("''")) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class BitCoin(Base): __tablename__ = 'bit_coin' user_id = Column(Integer, primary_key=True) amount = Column(Numeric(10, 2), nullable=False, server_default=text("'0.00'")) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class BitCoinItem(Base): __tablename__ = 'bit_coin_item' id = Column(Integer, primary_key=True) user_id = Column(Integer, nullable=False, index=True) type = Column(Integer, nullable=False, server_default=text("'0'")) amount = Column(Numeric(8, 2), nullable=False, server_default=text("'0.00'")) sc_id = Column(Integer, nullable=False, index=True, server_default=text("'0'")) note = Column(String(256), nullable=False, server_default=text("''")) status_audit = Column(Integer, nullable=False, server_default=text("'0'")) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) audit_time = Column(DateTime) delete_time = Column(DateTime) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class Bonus(Base): __tablename__ = 'bonus' user_id = Column(Integer, primary_key=True) amount = Column(Numeric(10, 2), nullable=False, server_default=text("'0.00'")) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class BonusItem(Base): __tablename__ = 'bonus_item' id = Column(Integer, primary_key=True) user_id = Column(Integer, nullable=False, index=True) type = Column(Integer, nullable=False, server_default=text("'0'")) amount = Column(Numeric(8, 2), nullable=False, server_default=text("'0.00'")) sc_id = Column(Integer, nullable=False, index=True, server_default=text("'0'")) note = Column(String(256), nullable=False, server_default=text("''")) status_audit = Column(Integer, nullable=False, server_default=text("'0'")) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) audit_time = Column(DateTime) delete_time = Column(DateTime) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class Complaint(Base): __tablename__ = 'complaint' id = Column(Integer, primary_key=True) send_user_id = Column(Integer, nullable=False, index=True) receive_user_id = Column(Integer, nullable=False, index=True) reply_admin_id = Column(Integer, nullable=False, index=True, server_default=text("'0'")) content = Column(String(512), nullable=False, server_default=text("''")) content_reply = Column(String(512), nullable=False, server_default=text("''")) status_reply = Column(Integer, nullable=False, server_default=text("'0'")) reply_time = Column(DateTime) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) delete_time = Column(DateTime) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class Credit(Base): __tablename__ = 'credit' user_id = Column(Integer, primary_key=True) behavior = Column(Numeric(3, 0), nullable=False, server_default=text("'0'")) characteristics = Column(Numeric(3, 0), nullable=False, server_default=text("'0'")) connections = Column(Numeric(3, 0), nullable=False, server_default=text("'0'")) history = Column(Numeric(3, 0), nullable=False, server_default=text("'0'")) performance = Column(Numeric(3, 0), nullable=False, server_default=text("'0'")) credit = Column(Numeric(3, 0), nullable=False, server_default=text("'0'")) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class Message(Base): __tablename__ = 'message' id = Column(Integer, primary_key=True) send_user_id = Column(Integer, nullable=False, index=True) receive_user_id = Column(Integer, nullable=False, index=True) content = Column(String(512), nullable=False, server_default=text("''")) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) delete_time = Column(DateTime) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class Order(Base): __tablename__ = 'order' id = Column(Integer, primary_key=True) apply_put_id = Column(Integer, nullable=False, index=True) apply_get_id = Column(Integer, nullable=False, index=True) apply_put_uid = Column(Integer, nullable=False, index=True) apply_get_uid = Column(Integer, nullable=False, index=True) type = Column(Integer, nullable=False, server_default=text("'0'")) money = Column(Numeric(8, 2), nullable=False, server_default=text("'0.00'")) status_audit = Column(Integer, nullable=False, server_default=text("'0'")) status_flow = Column(Integer, nullable=False, server_default=text("'0'")) status_pay = Column(Integer, nullable=False, server_default=text("'0'")) status_rec = Column(Integer, nullable=False, server_default=text("'0'")) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) audit_time = Column(DateTime) pay_time = Column(DateTime) rec_time = Column(DateTime) delete_time = Column(DateTime) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class OrderBill(Base): __tablename__ = 'order_bill' id = Column(Integer, primary_key=True) order_id = Column(Integer, nullable=False, index=True) bill_img = Column(String(255)) status_audit = Column(Integer, nullable=False, server_default=text("'0'")) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) audit_time = Column(DateTime) delete_time = Column(DateTime) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class OrderFlow(Base): __tablename__ = 'order_flow' order_id = Column(Integer, primary_key=True) flow_order_id = Column(Integer, nullable=False, index=True) apply_put_id = Column(Integer, nullable=False, index=True) apply_get_id = Column(Integer, nullable=False, index=True) apply_put_uid = Column(Integer, nullable=False, index=True) apply_get_uid = Column(Integer, nullable=False, index=True) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class Scheduling(Base): __tablename__ = 'scheduling' user_id = Column(Integer, primary_key=True) amount = Column(Numeric(10, 2), nullable=False, server_default=text("'0.00'")) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class SchedulingItem(Base): __tablename__ = 'scheduling_item' id = Column(Integer, primary_key=True) user_id = Column(Integer, nullable=False, index=True) type = Column(Integer, nullable=False, server_default=text("'0'")) amount = Column(Numeric(8, 2), nullable=False, server_default=text("'0.00'")) sc_id = Column(Integer, nullable=False, index=True, server_default=text("'0'")) note = Column(String(256), nullable=False, server_default=text("''")) status_audit = Column(Integer, nullable=False, server_default=text("'0'")) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) audit_time = Column(DateTime) delete_time = Column(DateTime) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class Score(Base): __tablename__ = 'score' user_id = Column(Integer, primary_key=True) amount = Column(Numeric(10, 2), nullable=False, server_default=text("'0.00'")) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class ScoreCharity(Base): __tablename__ = 'score_charity' user_id = Column(Integer, primary_key=True) amount = Column(Numeric(10, 2), nullable=False, server_default=text("'0.00'")) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class ScoreCharityItem(Base): __tablename__ = 'score_charity_item' id = Column(Integer, primary_key=True) user_id = Column(Integer, nullable=False, index=True) type = Column(Integer, nullable=False, server_default=text("'0'")) amount = Column(Numeric(8, 2), nullable=False, server_default=text("'0.00'")) sc_id = Column(Integer, nullable=False, index=True, server_default=text("'0'")) note = Column(String(256), nullable=False, server_default=text("''")) status_audit = Column(Integer, nullable=False, server_default=text("'0'")) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) audit_time = Column(DateTime) delete_time = Column(DateTime) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class ScoreDigital(Base): __tablename__ = 'score_digital' user_id = Column(Integer, primary_key=True) amount = Column(Numeric(10, 2), nullable=False, server_default=text("'0.00'")) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class ScoreDigitalItem(Base): __tablename__ = 'score_digital_item' id = Column(Integer, primary_key=True) user_id = Column(Integer, nullable=False, index=True) type = Column(Integer, nullable=False, server_default=text("'0'")) amount = Column(Numeric(8, 2), nullable=False, server_default=text("'0.00'")) sc_id = Column(Integer, nullable=False, index=True, server_default=text("'0'")) note = Column(String(256), nullable=False, server_default=text("''")) status_audit = Column(Integer, nullable=False, server_default=text("'0'")) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) audit_time = Column(DateTime) delete_time = Column(DateTime) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class ScoreExpense(Base): __tablename__ = 'score_expense' user_id = Column(Integer, primary_key=True) amount = Column(Numeric(10, 2), nullable=False, server_default=text("'0.00'")) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class ScoreExpenseItem(Base): __tablename__ = 'score_expense_item' id = Column(Integer, primary_key=True) user_id = Column(Integer, nullable=False, index=True) type = Column(Integer, nullable=False, server_default=text("'0'")) amount = Column(Numeric(8, 2), nullable=False, server_default=text("'0.00'")) sc_id = Column(Integer, nullable=False, index=True, server_default=text("'0'")) note = Column(String(256), nullable=False, server_default=text("''")) status_audit = Column(Integer, nullable=False, server_default=text("'0'")) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) audit_time = Column(DateTime) delete_time = Column(DateTime) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class ScoreItem(Base): __tablename__ = 'score_item' id = Column(Integer, primary_key=True) user_id = Column(Integer, nullable=False, index=True) type = Column(Integer, nullable=False, server_default=text("'0'")) amount = Column(Numeric(8, 2), nullable=False, server_default=text("'0.00'")) sc_id = Column(Integer, nullable=False, index=True, server_default=text("'0'")) note = Column(String(256), nullable=False, server_default=text("''")) status_audit = Column(Integer, nullable=False, server_default=text("'0'")) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) audit_time = Column(DateTime) delete_time = Column(DateTime) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True) status_active = Column(Integer, nullable=False, server_default=text("'0'")) status_lock = Column(Integer, nullable=False, server_default=text("'0'")) status_real_name = Column(Integer, nullable=False, server_default=text("'0'")) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) active_time = Column(DateTime) lock_time = Column(DateTime) delete_time = Column(DateTime) reg_ip = Column(String(20)) login_ip = Column(String(20)) login_time = Column(DateTime) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class UserAuth(Base): __tablename__ = 'user_auth' __table_args__ = ( Index('type_auth', 'type_auth', 'auth_key', unique=True), ) id = Column(Integer, primary_key=True) user_id = Column(Integer, nullable=False, index=True, server_default=text("'0'")) type_auth = Column(Integer, nullable=False, server_default=text("'0'")) auth_key = Column(String(60), nullable=False, server_default=text("''")) auth_secret = Column(String(60), nullable=False, server_default=text("''")) status_verified = Column(Integer, nullable=False, server_default=text("'0'")) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class UserBank(Base): __tablename__ = 'user_bank' user_id = Column(Integer, primary_key=True) account_name = Column(String(60), nullable=False, server_default=text("'0'")) bank_name = Column(String(60), nullable=False, server_default=text("''")) bank_address = Column(String(60), nullable=False, server_default=text("''")) bank_account = Column(String(32), nullable=False, server_default=text("''")) status_verified = Column(Integer, nullable=False, server_default=text("'0'")) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class UserConfig(Base): __tablename__ = 'user_config' user_id = Column(Integer, primary_key=True) team_bonus = Column(String(100), server_default=text("''")) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class UserProfile(Base): __tablename__ = 'user_profile' __table_args__ = ( Index('ind_phone', 'area_id', 'phone'), Index('ind_id_card', 'area_id', 'id_card') ) user_id = Column(Integer, primary_key=True) user_pid = Column(Integer, nullable=False, server_default=text("'0'")) nickname = Column(String(20), nullable=False, server_default=text("''")) type_level = Column(Integer, nullable=False, server_default=text("'0'")) avatar_url = Column(String(60)) email = Column(String(60), nullable=False, server_default=text("''")) area_id = Column(Integer, nullable=False, server_default=text("'0'")) area_code = Column(String(4), nullable=False, server_default=text("''")) phone = Column(String(20), nullable=False, server_default=text("''")) birthday = Column(Date, nullable=False, server_default=text("'1900-01-01'")) real_name = Column(String(20), nullable=False, server_default=text("''")) id_card = Column(String(32), nullable=False, server_default=text("''")) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class Wallet(Base): __tablename__ = 'wallet' user_id = Column(Integer, primary_key=True) amount_initial = Column(Numeric(10, 2), nullable=False, server_default=text("'0.00'")) amount_current = Column(Numeric(10, 2), nullable=False, server_default=text("'0.00'")) amount_lock = Column(Numeric(10, 2), nullable=False, server_default=text("'0.00'")) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) class WalletItem(Base): __tablename__ = 'wallet_item' id = Column(Integer, primary_key=True) user_id = Column(Integer, nullable=False, index=True) type = Column(Integer, nullable=False, server_default=text("'0'")) amount = Column(Numeric(8, 2), nullable=False, server_default=text("'0.00'")) sc_id = Column(Integer, nullable=False, index=True, server_default=text("'0'")) note = Column(String(256), nullable=False, server_default=text("''")) status_audit = Column(Integer, nullable=False, server_default=text("'0'")) status_delete = Column(Integer, nullable=False, server_default=text("'0'")) audit_time = Column(DateTime) delete_time = Column(DateTime) create_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP")) update_time = Column(DateTime, nullable=False, server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")) ================================================ FILE: app_backend/pages/blank.html ================================================ SB Admin 2 - Bootstrap Admin Theme ================================================ FILE: app_backend/pages/buttons.html ================================================ SB Admin 2 - Bootstrap Admin Theme

Buttons

Default Buttons

Normal Buttons


Disabled Buttons


Button Sizes



Circle Icon Buttons with Font Awesome Icons

Normal Circle Buttons



Large Circle Buttons



Extra Large Circle Buttons

Outline Buttons with Smooth Transition

Outline Buttons


Outline Button Sizes



================================================ FILE: app_backend/pages/flot.html ================================================ SB Admin 2 - Bootstrap Admin Theme

Flot

Line Chart Example
Pie Chart Example
Multiple Axes Line Chart Example
Moving Line Chart Example
Bar Chart Example
Flot Charts Usage

Flot is a pure JavaScript plotting library for jQuery, with a focus on simple usage, attractive looks, and interactive features. In SB Admin, we are using the most recent version of Flot along with a few plugins to enhance the user experience. The Flot plugins being used are the tooltip plugin for hoverable tooltips, and the resize plugin for fully responsive charts. The documentation for Flot Charts is available on their website, http://www.flotcharts.org/.

View Flot Charts Documentation
================================================ FILE: app_backend/pages/forms.html ================================================ SB Admin 2 - Bootstrap Admin Theme

Forms

Basic Form Elements

Example block-level help text here.

email@example.com

Disabled Form States

Form Validation States

Input Groups

@
.00
$ .00
================================================ FILE: app_backend/pages/grid.html ================================================ SB Admin 2 - Bootstrap Admin Theme

Grid

Grid options

See how aspects of the Bootstrap grid system work across multiple devices with a handy table.

Extra small devices Phones (<768px) Small devices Tablets (≥768px) Medium devices Desktops (≥992px) Large devices Desktops (≥1200px)
Grid behavior Horizontal at all times Collapsed to start, horizontal above breakpoints
Max container width None (auto) 750px 970px 1170px
Class prefix .col-xs- .col-sm- .col-md- .col-lg-
# of columns 12
Max column width Auto 60px 78px 95px
Gutter width 30px (15px on each side of a column)
Nestable Yes
Offsets Yes
Column ordering Yes

Grid classes apply to devices with screen widths greater than or equal to the breakpoint sizes, and override grid classes targeted at smaller devices. Therefore, applying any .col-md- class to an element will not only affect its styling on medium devices but also on large devices if a .col-lg- class is not present.

Example: Stacked-to-horizontal

Using a single set of .col-md-* grid classes, you can create a default grid system that starts out stacked on mobile devices and tablet devices (the extra small to small range) before becoming horizontal on desktop (medium) devices. Place grid columns in any .row.

.col-md-1
.col-md-1
.col-md-1
.col-md-1
.col-md-1
.col-md-1
.col-md-1
.col-md-1
.col-md-1
.col-md-1
.col-md-1
.col-md-1
.col-md-8
.col-md-4
.col-md-4
.col-md-4
.col-md-4
.col-md-6
.col-md-6

Example: Mobile and desktop

Don't want your columns to simply stack in smaller devices? Use the extra small and medium device grid classes by adding .col-xs-* .col-md-* to your columns. See the example below for a better idea of how it all works.

.col-xs-12 .col-md-8
.col-xs-6 .col-md-4
.col-xs-6 .col-md-4
.col-xs-6 .col-md-4
.col-xs-6 .col-md-4
.col-xs-6
.col-xs-6

Example: Mobile, tablet, desktops

Build on the previous example by creating even more dynamic and powerful layouts with tablet .col-sm-* classes.

.col-xs-12 .col-sm-6 .col-md-8
.col-xs-6 .col-md-4
.col-xs-6 .col-sm-4
.col-xs-6 .col-sm-4
.col-xs-6 .col-sm-4

Responsive column resets

With the four tiers of grids available you're bound to run into issues where, at certain breakpoints, your columns don't clear quite right as one is taller than the other. To fix that, use a combination of a .clearfix and our responsive utility classes.

.col-xs-6 .col-sm-3
Resize your viewport or check it out on your phone for an example.
.col-xs-6 .col-sm-3
.col-xs-6 .col-sm-3
.col-xs-6 .col-sm-3

Offsetting columns

Move columns to the right using .col-md-offset-* classes. These classes increase the left margin of a column by * columns. For example, .col-md-offset-4 moves .col-md-4 over four columns.

.col-md-4
.col-md-4 .col-md-offset-4
.col-md-3 .col-md-offset-3
.col-md-3 .col-md-offset-3
.col-md-6 .col-md-offset-3

Nesting columns

To nest your content with the default grid, add a new .row and set of .col-md-* columns within an existing .col-md-* column. Nested rows should include a set of columns that add up to 12.

Level 1: .col-md-9
Level 2: .col-md-6
Level 2: .col-md-6

Column ordering

Easily change the order of our built-in grid columns with .col-md-push-* and .col-md-pull-* modifier classes.

.col-md-9 .col-md-push-3
.col-md-3 .col-md-pull-9
================================================ FILE: app_backend/pages/icons.html ================================================ SB Admin 2 - Bootstrap Admin Theme

Icons

All available icons (font-awesome)

fa-glass


fa-music



fa-envelope-o


fa-heart


fa-star


fa-star-o


fa-user


fa-film


fa-th-large


fa-th


fa-th-list


fa-check


fa-times


fa-search-plus


fa-search-minus


fa-power-off


fa-signal


fa-gear


fa-cog


fa-trash-o


fa-home


fa-file-o


fa-clock-o


fa-road


fa-download


fa-arrow-circle-o-down


fa-arrow-circle-o-up


fa-inbox


fa-play-circle-o


fa-rotate-right


fa-repeat


fa-refresh


fa-list-alt


fa-lock


fa-flag


fa-headphones


fa-volume-off


fa-volume-down


fa-volume-up


fa-qrcode


fa-barcode


fa-tag


fa-tags


fa-book


fa-bookmark


fa-print


fa-camera


fa-font


fa-bold


fa-italic


fa-text-height


fa-text-width


fa-align-left


fa-align-center


fa-align-right


fa-align-justify


fa-list


fa-dedent


fa-outdent


fa-indent


fa-video-camera


fa-photo


fa-image


fa-picture-o


fa-pencil


fa-map-marker


fa-adjust


fa-tint


fa-edit


fa-pencil-square-o


fa-share-square-o


fa-check-square-o


fa-arrows


fa-step-backward


fa-fast-backward


fa-backward


fa-play


fa-pause


fa-stop


fa-forward


fa-fast-forward


fa-step-forward


fa-eject


fa-chevron-left


fa-chevron-right


fa-plus-circle


fa-minus-circle


fa-times-circle


fa-check-circle


fa-question-circle


fa-info-circle


fa-crosshairs


fa-times-circle-o


fa-check-circle-o


fa-ban


fa-arrow-left


fa-arrow-right


fa-arrow-up


fa-arrow-down


fa-mail-forward


fa-share


fa-expand


fa-compress


fa-plus


fa-minus


fa-asterisk


fa-exclamation-circle


fa-gift


fa-leaf


fa-fire


fa-eye


fa-eye-slash


fa-warning


fa-exclamation-triangle


fa-plane


fa-calendar


fa-random


fa-comment


fa-magnet


fa-chevron-up


fa-chevron-down


fa-retweet


fa-shopping-cart


fa-folder


fa-folder-open


fa-arrows-v


fa-arrows-h


fa-bar-chart-o


fa-twitter-square


fa-facebook-square


fa-camera-retro


fa-key


fa-gears


fa-cogs


fa-comments


fa-thumbs-o-up


fa-thumbs-o-down


fa-star-half


fa-heart-o


fa-sign-out


fa-linkedin-square


fa-thumb-tack




fa-trophy


fa-github-square


fa-upload


fa-lemon-o


fa-phone


fa-square-o


fa-bookmark-o


fa-phone-square


fa-twitter


fa-facebook


fa-github


fa-unlock


fa-credit-card


fa-rss


fa-hdd-o


fa-bullhorn


fa-bell


fa-certificate


fa-hand-o-right


fa-hand-o-left


fa-hand-o-up


fa-hand-o-down


fa-arrow-circle-left


fa-arrow-circle-right


fa-arrow-circle-up


fa-arrow-circle-down


fa-globe


fa-wrench


fa-tasks


fa-filter


fa-briefcase


fa-arrows-alt


fa-group


fa-users


fa-chain



fa-cloud


fa-flask


fa-cut


fa-scissors


fa-copy


fa-files-o


fa-paperclip


fa-save


fa-floppy-o


fa-square


fa-navicon


fa-reorder


fa-bars


fa-list-ul


fa-list-ol


fa-strikethrough


fa-underline


fa-table


fa-magic


fa-truck


fa-pinterest


fa-pinterest-square


fa-google-plus-square


fa-google-plus


fa-money


fa-caret-down


fa-caret-up


fa-caret-left


fa-caret-right


fa-columns


fa-unsorted


fa-sort


fa-sort-down


fa-sort-desc


fa-sort-up


fa-sort-asc


fa-envelope


fa-linkedin


fa-rotate-left


fa-undo



fa-gavel


fa-dashboard


fa-tachometer


fa-comment-o


fa-comments-o


fa-flash


fa-bolt


fa-sitemap


fa-umbrella


fa-paste


fa-clipboard


fa-lightbulb-o


fa-exchange


fa-cloud-download


fa-cloud-upload


fa-user-md


fa-stethoscope


fa-suitcase


fa-bell-o


fa-coffee


fa-cutlery


fa-file-text-o


fa-building-o


fa-hospital-o


fa-ambulance


fa-medkit


fa-fighter-jet


fa-beer


fa-h-square


fa-plus-square


fa-angle-double-left


fa-angle-double-right


fa-angle-double-up


fa-angle-double-down


fa-angle-left


fa-angle-right


fa-angle-up


fa-angle-down


fa-desktop


fa-laptop


fa-tablet


fa-mobile-phone


fa-mobile


fa-circle-o


fa-quote-left


fa-quote-right


fa-spinner


fa-circle


fa-mail-reply


fa-reply


fa-github-alt


fa-folder-o


fa-folder-open-o


fa-smile-o


fa-frown-o


fa-meh-o


fa-gamepad


fa-keyboard-o


fa-flag-o


fa-flag-checkered


fa-terminal


fa-code


fa-mail-reply-all


fa-reply-all


fa-star-half-empty


fa-star-half-full


fa-star-half-o


fa-location-arrow


fa-crop


fa-code-fork



fa-chain-broken


fa-question


fa-info


fa-exclamation


fa-superscript


fa-subscript


fa-eraser


fa-puzzle-piece


fa-microphone


fa-microphone-slash


fa-shield


fa-calendar-o


fa-fire-extinguisher


fa-rocket


fa-maxcdn


fa-chevron-circle-left


fa-chevron-circle-right


fa-chevron-circle-up


fa-chevron-circle-down


fa-html5


fa-css3


fa-anchor


fa-unlock-alt


fa-bullseye


fa-ellipsis-h


fa-ellipsis-v


fa-rss-square


fa-play-circle


fa-ticket


fa-minus-square


fa-minus-square-o


fa-level-up


fa-level-down


fa-check-square


fa-pencil-square



fa-share-square


fa-compass


fa-toggle-down


fa-caret-square-o-down


fa-toggle-up


fa-caret-square-o-up


fa-toggle-right


fa-caret-square-o-right


fa-euro


fa-eur


fa-gbp


fa-dollar


fa-usd


fa-rupee


fa-inr


fa-cny


fa-rmb


fa-yen


fa-jpy


fa-ruble


fa-rouble


fa-rub


fa-won


fa-krw


fa-bitcoin


fa-btc


fa-file


fa-file-text


fa-sort-alpha-asc


fa-sort-alpha-desc


fa-sort-amount-asc


fa-sort-amount-desc


fa-sort-numeric-asc


fa-sort-numeric-desc


fa-thumbs-up


fa-thumbs-down


fa-youtube-square


fa-youtube


fa-xing


fa-xing-square


fa-youtube-play


fa-dropbox


fa-stack-overflow


fa-instagram


fa-flickr


fa-adn


fa-bitbucket


fa-bitbucket-square


fa-tumblr


fa-tumblr-square


fa-long-arrow-down


fa-long-arrow-up


fa-long-arrow-left


fa-long-arrow-right


fa-apple


fa-windows


fa-android


fa-linux


fa-dribbble


fa-skype


fa-foursquare


fa-trello


fa-female


fa-male


fa-gittip


fa-sun-o


fa-moon-o


fa-archive


fa-bug


fa-vk


fa-weibo


fa-renren


fa-pagelines


fa-stack-exchange


fa-arrow-circle-o-right


fa-arrow-circle-o-left


fa-toggle-left


fa-caret-square-o-left


fa-dot-circle-o


fa-wheelchair


fa-vimeo-square


fa-turkish-lira


fa-try


fa-plus-square-o


fa-space-shuttle


fa-slack


fa-envelope-square


fa-wordpress


fa-openid


fa-institution


fa-bank


fa-university


fa-mortar-board


fa-graduation-cap


fa-yahoo


fa-google


fa-reddit


fa-reddit-square


fa-stumbleupon-circle


fa-stumbleupon


fa-delicious


fa-digg


fa-pied-piper-square


fa-pied-piper


fa-pied-piper-alt


fa-drupal


fa-joomla


fa-language


fa-fax


fa-building


fa-child


fa-paw


fa-spoon


fa-cube


fa-cubes


fa-behance


fa-behance-square


fa-steam


fa-steam-square


fa-recycle


fa-automobile


fa-car


fa-cab


fa-taxi


fa-tree


fa-spotify


fa-deviantart


fa-soundcloud


fa-database


fa-file-pdf-o


fa-file-word-o


fa-file-excel-o


fa-file-powerpoint-o


fa-file-photo-o


fa-file-picture-o


fa-file-image-o


fa-file-zip-o


fa-file-archive-o


fa-file-sound-o


fa-file-audio-o


fa-file-movie-o


fa-file-video-o


fa-file-code-o


fa-vine


fa-codepen


fa-jsfiddle


fa-life-bouy


fa-life-saver


fa-support


fa-life-ring


fa-circle-o-notch


fa-ra


fa-rebel


fa-ge


fa-empire


fa-git-square


fa-git


fa-hacker-news


fa-tencent-weibo


fa-qq


fa-wechat


fa-weixin


fa-send


fa-paper-plane


fa-send-o


fa-paper-plane-o


fa-history


fa-circle-thin


fa-header


fa-paragraph


fa-sliders


fa-share-alt


fa-share-alt-square


fa-bomb


All available icons (bootstrap)
glyphicon-asterisk
glyphicon-plus
glyphicon-euro
glyphicon-eur
glyphicon-minus
glyphicon-cloud
glyphicon-envelope
glyphicon-pencil
glyphicon-glass
glyphicon-music
glyphicon-search
glyphicon-heart
glyphicon-star
glyphicon-star-empty
glyphicon-user
glyphicon-film
glyphicon-th-large
glyphicon-th
glyphicon-th-list
glyphicon-ok
glyphicon-remove
glyphicon-zoom-in
glyphicon-zoom-out
glyphicon-off
glyphicon-signal
glyphicon-cog
glyphicon-trash
glyphicon-home
glyphicon-file
glyphicon-time
glyphicon-road
glyphicon-download-alt
glyphicon-download
glyphicon-upload
glyphicon-inbox
glyphicon-play-circle
glyphicon-repeat
glyphicon-refresh
glyphicon-list-alt
glyphicon-lock
glyphicon-flag
glyphicon-headphones
glyphicon-volume-off
glyphicon-volume-down
glyphicon-volume-up
glyphicon-qrcode
glyphicon-barcode
glyphicon-tag
glyphicon-tags
glyphicon-book
glyphicon-bookmark
glyphicon-print
glyphicon-camera
glyphicon-font
glyphicon-bold
glyphicon-italic
glyphicon-text-height
glyphicon-text-width
glyphicon-align-left
glyphicon-align-center
glyphicon-align-right
glyphicon-align-justify
glyphicon-list
glyphicon-indent-left
glyphicon-indent-right
glyphicon-facetime-video
glyphicon-picture
glyphicon-map-marker
glyphicon-adjust
glyphicon-tint
glyphicon-edit
glyphicon-share
glyphicon-check
glyphicon-move
glyphicon-step-backward
glyphicon-fast-backward
glyphicon-backward
glyphicon-play
glyphicon-pause
glyphicon-stop
glyphicon-forward
glyphicon-fast-forward
glyphicon-step-forward
glyphicon-eject
glyphicon-chevron-left
glyphicon-chevron-right
glyphicon-plus-sign
glyphicon-minus-sign
glyphicon-remove-sign
glyphicon-ok-sign
glyphicon-question-sign
glyphicon-info-sign
glyphicon-screenshot
glyphicon-remove-circle
glyphicon-ok-circle
glyphicon-ban-circle
glyphicon-arrow-left
glyphicon-arrow-right
glyphicon-arrow-up
glyphicon-arrow-down
glyphicon-share-alt
glyphicon-resize-full
glyphicon-resize-small
glyphicon-exclamation-sign
glyphicon-gift
glyphicon-leaf
glyphicon-fire
glyphicon-eye-open
glyphicon-eye-close
glyphicon-warning-sign
glyphicon-plane
glyphicon-calendar
glyphicon-random
glyphicon-comment
glyphicon-magnet
glyphicon-chevron-up
glyphicon-chevron-down
glyphicon-retweet
glyphicon-shopping-cart
glyphicon-folder-close
glyphicon-folder-open
glyphicon-resize-vertical
glyphicon-resize-horizontal
glyphicon-hdd
glyphicon-bullhorn
glyphicon-bell
glyphicon-certificate
glyphicon-thumbs-up
glyphicon-thumbs-down
glyphicon-hand-right
glyphicon-hand-left
glyphicon-hand-up
glyphicon-hand-down
glyphicon-circle-arrow-right
glyphicon-circle-arrow-left
glyphicon-circle-arrow-up
glyphicon-circle-arrow-down
glyphicon-globe
glyphicon-wrench
glyphicon-tasks
glyphicon-filter
glyphicon-briefcase
glyphicon-fullscreen
glyphicon-dashboard
glyphicon-paperclip
glyphicon-heart-empty
glyphicon-link
glyphicon-phone
glyphicon-pushpin
glyphicon-usd
glyphicon-gbp
glyphicon-sort
glyphicon-sort-by-alphabet
glyphicon-sort-by-alphabet-alt
glyphicon-sort-by-order
glyphicon-sort-by-order-alt
glyphicon-sort-by-attributes
glyphicon-sort-by-attributes-alt
glyphicon-unchecked
glyphicon-expand
glyphicon-collapse-down
glyphicon-collapse-up

glyphicon-flash
glyphicon-log-out
glyphicon-new-window
glyphicon-record
glyphicon-save
glyphicon-open
glyphicon-saved
glyphicon-import
glyphicon-export
glyphicon-send
glyphicon-floppy-disk
glyphicon-floppy-saved
glyphicon-floppy-remove
glyphicon-floppy-save
glyphicon-floppy-open
glyphicon-credit-card
glyphicon-transfer
glyphicon-cutlery
glyphicon-header
glyphicon-compressed
glyphicon-earphone
glyphicon-phone-alt
glyphicon-tower
glyphicon-stats
glyphicon-sd-video
glyphicon-hd-video
glyphicon-subtitles
glyphicon-sound-stereo
glyphicon-sound-dolby
glyphicon-sound-5-1
glyphicon-sound-6-1
glyphicon-sound-7-1
glyphicon-copyright-mark
glyphicon-registration-mark
glyphicon-cloud-download
glyphicon-cloud-upload
glyphicon-tree-conifer
glyphicon-tree-deciduous
glyphicon-cd
glyphicon-save-file
glyphicon-open-file
glyphicon-level-up
glyphicon-copy
glyphicon-paste
glyphicon-alert
glyphicon-equalizer
glyphicon-king
glyphicon-queen
glyphicon-pawn
glyphicon-bishop
glyphicon-knight
glyphicon-baby-formula
glyphicon-tent
glyphicon-blackboard
glyphicon-bed
glyphicon-apple
glyphicon-erase
glyphicon-hourglass
glyphicon-lamp
glyphicon-duplicate
glyphicon-piggy-bank
glyphicon-scissors
glyphicon-bitcoin
glyphicon-yen
glyphicon-ruble
glyphicon-scale
glyphicon-ice-lolly
glyphicon-ice-lolly-tasted
glyphicon-education
glyphicon-option-horizontal
glyphicon-option-vertical
glyphicon-menu-hamburger
glyphicon-modal-window
glyphicon-oil
glyphicon-grain
glyphicon-sunglasses
glyphicon-text-size
glyphicon-text-color
glyphicon-text-background
glyphicon-object-align-top
glyphicon-object-align-bottom
glyphicon-object-align-horizontal
glyphicon-object-align-left
glyphicon-object-align-vertical
glyphicon-object-align-right
glyphicon-triangle-right
glyphicon-triangle-left
glyphicon-triangle-bottom
glyphicon-triangle-top
glyphicon-console
glyphicon-superscript
glyphicon-subscript
glyphicon-menu-left
glyphicon-menu-right
glyphicon-menu-down
glyphicon-menu-up
================================================ FILE: app_backend/pages/index.html ================================================ SB Admin 2 - Bootstrap Admin Theme

Dashboard

26
New Comments!
12
New Tasks!
124
New Orders!
13
Support Tickets!
# Date Time Amount
3326 10/21/2013 3:29 PM $321.33
3325 10/21/2013 3:20 PM $234.34
3324 10/21/2013 3:03 PM $724.17
3323 10/21/2013 3:00 PM $23.71
3322 10/21/2013 2:49 PM $8345.23
3321 10/21/2013 2:23 PM $245.12
3320 10/21/2013 2:15 PM $5663.54
3319 10/21/2013 2:13 PM $943.45
Responsive Timeline
  • Lorem ipsum dolor

    11 hours ago via Twitter

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Libero laboriosam dolor perspiciatis omnis exercitationem. Beatae, officia pariatur? Est cum veniam excepturi. Maiores praesentium, porro voluptas suscipit facere rem dicta, debitis.

  • Lorem ipsum dolor

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Autem dolorem quibusdam, tenetur commodi provident cumque magni voluptatem libero, quis rerum. Fugiat esse debitis optio, tempore. Animi officiis alias, officia repellendus.

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Laudantium maiores odit qui est tempora eos, nostrum provident explicabo dignissimos debitis vel! Adipisci eius voluptates, ad aut recusandae minus eaque facere.

  • Lorem ipsum dolor

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellendus numquam facilis enim eaque, tenetur nam id qui vel velit similique nihil iure molestias aliquam, voluptatem totam quaerat, magni commodi quisquam.

  • Lorem ipsum dolor

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptates est quaerat asperiores sapiente, eligendi, nihil. Itaque quos, alias sapiente rerum quas odit! Aperiam officiis quidem delectus libero, omnis ut debitis!

  • Lorem ipsum dolor

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nobis minus modi quam ipsum alias at est molestiae excepturi delectus nesciunt, quibusdam debitis amet, beatae consequuntur impedit nulla qui! Laborum, atque.


  • Lorem ipsum dolor

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sequi fuga odio quibusdam. Iure expedita, incidunt unde quis nam! Quod, quisquam. Officia quam qui adipisci quas consequuntur nostrum sequi. Consequuntur, commodi.

  • Lorem ipsum dolor

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deserunt obcaecati, quaerat tempore officia voluptas debitis consectetur culpa amet, accusamus dolorum fugiat, animi dicta aperiam, enim incidunt quisquam maxime neque eaque.

Donut Chart Example
  • User Avatar
    Jack Sparrow 12 mins ago

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur bibendum ornare dolor, quis ullamcorper ligula sodales.

  • User Avatar
    13 mins ago Bhaumik Patel

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur bibendum ornare dolor, quis ullamcorper ligula sodales.

  • User Avatar
    Jack Sparrow 14 mins ago

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur bibendum ornare dolor, quis ullamcorper ligula sodales.

  • User Avatar
    15 mins ago Bhaumik Patel

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur bibendum ornare dolor, quis ullamcorper ligula sodales.

================================================ FILE: app_backend/pages/login.html ================================================ SB Admin 2 - Bootstrap Admin Theme
================================================ FILE: app_backend/pages/morris.html ================================================ SB Admin 2 - Bootstrap Admin Theme ================================================ FILE: app_backend/pages/notifications.html ================================================ SB Admin 2 - Bootstrap Admin Theme

Notifications

Alert Styles
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alert Link.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alert Link.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alert Link.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alert Link.
Dismissable Alerts
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alert Link.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alert Link.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alert Link.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Alert Link.
Modals
Tooltips and Popovers

Tooltip Demo


Clickable Popover Demo

================================================ FILE: app_backend/pages/panels-wells.html ================================================ SB Admin 2 - Bootstrap Admin Theme

Panels and Wells

Default Panel

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum tincidunt est vitae ultrices accumsan. Aliquam ornare lacus adipiscing, posuere lectus et, fringilla augue.

Primary Panel

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum tincidunt est vitae ultrices accumsan. Aliquam ornare lacus adipiscing, posuere lectus et, fringilla augue.

Success Panel

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum tincidunt est vitae ultrices accumsan. Aliquam ornare lacus adipiscing, posuere lectus et, fringilla augue.

Info Panel

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum tincidunt est vitae ultrices accumsan. Aliquam ornare lacus adipiscing, posuere lectus et, fringilla augue.

Warning Panel

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum tincidunt est vitae ultrices accumsan. Aliquam ornare lacus adipiscing, posuere lectus et, fringilla augue.

Danger Panel

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum tincidunt est vitae ultrices accumsan. Aliquam ornare lacus adipiscing, posuere lectus et, fringilla augue.

Green Panel

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum tincidunt est vitae ultrices accumsan. Aliquam ornare lacus adipiscing, posuere lectus et, fringilla augue.

Yellow Panel

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum tincidunt est vitae ultrices accumsan. Aliquam ornare lacus adipiscing, posuere lectus et, fringilla augue.

Red Panel

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum tincidunt est vitae ultrices accumsan. Aliquam ornare lacus adipiscing, posuere lectus et, fringilla augue.

Collapsible Accordion Panel Group
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Basic Tabs

Home Tab

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Profile Tab

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Messages Tab

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Settings Tab

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Pill Tabs

Home Tab

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Profile Tab

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Messages Tab

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Settings Tab

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Normal Well

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum tincidunt est vitae ultrices accumsan. Aliquam ornare lacus adipiscing, posuere lectus et, fringilla augue.

Large Well

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum tincidunt est vitae ultrices accumsan. Aliquam ornare lacus adipiscing, posuere lectus et, fringilla augue.

Small Well

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum tincidunt est vitae ultrices accumsan. Aliquam ornare lacus adipiscing, posuere lectus et, fringilla augue.

Jumbotron

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum tincidunt est vitae ultrices accumsan. Aliquam ornare lacus adipiscing, posuere lectus et, fringilla augue. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum tincidunt est vitae ultrices accumsan. Aliquam ornare lacus adipiscing.

Learn more

================================================ FILE: app_backend/pages/tables.html ================================================ SB Admin 2 - Bootstrap Admin Theme

Tables

DataTables Advanced Tables
Rendering engine Browser Platform(s) Engine version CSS grade
Trident Internet Explorer 4.0 Win 95+ 4 X
Trident Internet Explorer 5.0 Win 95+ 5 C
Trident Internet Explorer 5.5 Win 95+ 5.5 A
Trident Internet Explorer 6 Win 98+ 6 A
Trident Internet Explorer 7 Win XP SP2+ 7 A
Trident AOL browser (AOL desktop) Win XP 6 A
Gecko Firefox 1.0 Win 98+ / OSX.2+ 1.7 A
Gecko Firefox 1.5 Win 98+ / OSX.2+ 1.8 A
Gecko Firefox 2.0 Win 98+ / OSX.2+ 1.8 A
Gecko Firefox 3.0 Win 2k+ / OSX.3+ 1.9 A
Gecko Camino 1.0 OSX.2+ 1.8 A
Gecko Camino 1.5 OSX.3+ 1.8 A
Gecko Netscape 7.2 Win 95+ / Mac OS 8.6-9.2 1.7 A
Gecko Netscape Browser 8 Win 98SE+ 1.7 A
Gecko Netscape Navigator 9 Win 98+ / OSX.2+ 1.8 A
Gecko Mozilla 1.0 Win 95+ / OSX.1+ 1 A
Gecko Mozilla 1.1 Win 95+ / OSX.1+ 1.1 A
Gecko Mozilla 1.2 Win 95+ / OSX.1+ 1.2 A
Gecko Mozilla 1.3 Win 95+ / OSX.1+ 1.3 A
Gecko Mozilla 1.4 Win 95+ / OSX.1+ 1.4 A
Gecko Mozilla 1.5 Win 95+ / OSX.1+ 1.5 A
Gecko Mozilla 1.6 Win 95+ / OSX.1+ 1.6 A
Gecko Mozilla 1.7 Win 98+ / OSX.1+ 1.7 A
Gecko Mozilla 1.8 Win 98+ / OSX.1+ 1.8 A
Gecko Seamonkey 1.1 Win 98+ / OSX.2+ 1.8 A
Gecko Epiphany 2.20 Gnome 1.8 A
Webkit Safari 1.2 OSX.3 125.5 A
Webkit Safari 1.3 OSX.3 312.8 A
Webkit Safari 2.0 OSX.4+ 419.3 A
Webkit Safari 3.0 OSX.4+ 522.1 A
Webkit OmniWeb 5.5 OSX.4+ 420 A
Webkit iPod Touch / iPhone iPod 420.1 A
Webkit S60 S60 413 A
Presto Opera 7.0 Win 95+ / OSX.1+ - A
Presto Opera 7.5 Win 95+ / OSX.2+ - A
Presto Opera 8.0 Win 95+ / OSX.2+ - A
Presto Opera 8.5 Win 95+ / OSX.2+ - A
Presto Opera 9.0 Win 95+ / OSX.3+ - A
Presto Opera 9.2 Win 88+ / OSX.3+ - A
Presto Opera 9.5 Win 88+ / OSX.3+ - A
Presto Opera for Wii Wii - A
Presto Nokia N800 N800 - A
Presto Nintendo DS browser Nintendo DS 8.5 C/A1
KHTML Konqureror 3.1 KDE 3.1 3.1 C
KHTML Konqureror 3.3 KDE 3.3 3.3 A
KHTML Konqureror 3.5 KDE 3.5 3.5 A
Tasman Internet Explorer 4.5 Mac OS 8-9 - X
Tasman Internet Explorer 5.1 Mac OS 7.6-9 1 C
Tasman Internet Explorer 5.2 Mac OS 8-X 1 C
Misc NetFront 3.1 Embedded devices - C
Misc NetFront 3.4 Embedded devices - A
Misc Dillo 0.8 Embedded devices - X
Misc Links Text only - X
Misc Lynx Text only - X
Misc IE Mobile Windows Mobile 6 - C
Misc PSP browser PSP - C
Other browsers All others - - U

DataTables Usage Information

DataTables is a very flexible, advanced tables plugin for jQuery. In SB Admin, we are using a specialized version of DataTables built for Bootstrap 3. We have also customized the table headings to use Font Awesome icons in place of images. For complete documentation on DataTables, visit their website at https://datatables.net/.

View DataTables Documentation
Kitchen Sink
# First Name Last Name Username
1 Mark Otto @mdo
2 Jacob Thornton @fat
3 Larry the Bird @twitter
Basic Table
# First Name Last Name Username
1 Mark Otto @mdo
2 Jacob Thornton @fat
3 Larry the Bird @twitter
Striped Rows
# First Name Last Name Username
1 Mark Otto @mdo
2 Jacob Thornton @fat
3 Larry the Bird @twitter
Bordered Table
# First Name Last Name Username
1 Mark Otto @mdo
2 Jacob Thornton @fat
3 Larry the Bird @twitter
Hover Rows
# First Name Last Name Username
1 Mark Otto @mdo
2 Jacob Thornton @fat
3 Larry the Bird @twitter
Context Classes
# First Name Last Name Username
1 Mark Otto @mdo
2 Jacob Thornton @fat
3 Larry the Bird @twitter
4 John Smith @jsmith
================================================ FILE: app_backend/pages/typography.html ================================================ SB Admin 2 - Bootstrap Admin Theme

Typography

Headings

Heading 1 Sub-heading

Heading 2 Sub-heading

Heading 3 Sub-heading

Heading 4 Sub-heading

Heading 5 Sub-heading
Heading 6 Sub-heading
Paragraphs

This is an example of lead body copy.

This is an example of standard paragraph text. This is an example of link anchor text within body copy.

This is an example of small, fine print text.

This is an example of strong, bold text.

This is an example of emphasized, italic text.


Alignment Helpers

Left aligned text.

Center aligned text.

Right aligned text.

Emphasis Classes

This is an example of muted text.

This is an example of primary text.

This is an example of success text.

This is an example of info text.

This is an example of warning text.

This is an example of danger text.

Abbreviations

The abbreviation of the word attribute is attr.

HTMLis an abbreviation for a programming language.


Addresses

Twitter, Inc.
795 Folsom Ave, Suite 600
San Francisco, CA 94107
P:(123) 456-7890
Full Name
first.last@example.com
Blockquotes

Default Blockquote

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.

Blockquote with Citation

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.

Someone famous in Source Title

Right Aligned Blockquote

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.

Lists

Unordered List

  • List Item
  • List Item
    • List Item
    • List Item
    • List Item
  • List Item

Ordered List

  1. List Item
  2. List Item
  3. List Item

Unstyled List

  • List Item
  • List Item
  • List Item

Inline List

  • List Item
  • List Item
  • List Item
Description Lists
Standard Description List
Description Text
Description List Title
Description List Text
Horizontal Description List
Description Text
Description List Title
Description List Text
Code

This is an example of an inline code element within body copy. Wrap inline code within a <code>...</code>tag.

This is an example of preformatted text.
================================================ FILE: app_backend/permissions.py ================================================ #!/usr/bin/env python # encoding: utf-8 """ @author: zhanghe @software: PyCharm @file: permissions.py @time: 2017/6/8 下午11:16 """ from flask_principal import Permission, RoleNeed # 模块列表 modules = [ u'会员', u'订单', u'留言', u'系统', u'权限', u'统计', ] # 模块权限 permission_user = Permission(RoleNeed(u'会员')) permission_order = Permission(RoleNeed(u'订单')) permission_msg = Permission(RoleNeed(u'留言')) permission_sys = Permission(RoleNeed(u'系统')) permission_admin = Permission(RoleNeed(u'权限')) permission_stats = Permission(RoleNeed(u'统计')) permission_other = Permission(RoleNeed(u'其它')) ================================================ FILE: app_backend/static/css/bootstrap-select.css ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ select.bs-select-hidden, select.selectpicker { display: none !important; } .bootstrap-select { width: 220px \0; /*IE9 and below*/ } .bootstrap-select > .dropdown-toggle { width: 100%; padding-right: 25px; z-index: 1; } .bootstrap-select > .dropdown-toggle.bs-placeholder, .bootstrap-select > .dropdown-toggle.bs-placeholder:hover, .bootstrap-select > .dropdown-toggle.bs-placeholder:focus, .bootstrap-select > .dropdown-toggle.bs-placeholder:active { color: #999; } .bootstrap-select > select { position: absolute !important; bottom: 0; left: 50%; display: block !important; width: 0.5px !important; height: 100% !important; padding: 0 !important; opacity: 0 !important; border: none; } .bootstrap-select > select.mobile-device { top: 0; left: 0; display: block !important; width: 100% !important; z-index: 2; } .has-error .bootstrap-select .dropdown-toggle, .error .bootstrap-select .dropdown-toggle { border-color: #b94a48; } .bootstrap-select.fit-width { width: auto !important; } .bootstrap-select:not([class*="col-"]):not([class*="form-control"]):not(.input-group-btn) { width: 220px; } .bootstrap-select .dropdown-toggle:focus { outline: thin dotted #333333 !important; outline: 5px auto -webkit-focus-ring-color !important; outline-offset: -2px; } .bootstrap-select.form-control { margin-bottom: 0; padding: 0; border: none; } .bootstrap-select.form-control:not([class*="col-"]) { width: 100%; } .bootstrap-select.form-control.input-group-btn { z-index: auto; } .bootstrap-select.form-control.input-group-btn:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .bootstrap-select.btn-group:not(.input-group-btn), .bootstrap-select.btn-group[class*="col-"] { float: none; display: inline-block; margin-left: 0; } .bootstrap-select.btn-group.dropdown-menu-right, .bootstrap-select.btn-group[class*="col-"].dropdown-menu-right, .row .bootstrap-select.btn-group[class*="col-"].dropdown-menu-right { float: right; } .form-inline .bootstrap-select.btn-group, .form-horizontal .bootstrap-select.btn-group, .form-group .bootstrap-select.btn-group { margin-bottom: 0; } .form-group-lg .bootstrap-select.btn-group.form-control, .form-group-sm .bootstrap-select.btn-group.form-control { padding: 0; } .form-group-lg .bootstrap-select.btn-group.form-control .dropdown-toggle, .form-group-sm .bootstrap-select.btn-group.form-control .dropdown-toggle { height: 100%; font-size: inherit; line-height: inherit; border-radius: inherit; } .form-inline .bootstrap-select.btn-group .form-control { width: 100%; } .bootstrap-select.btn-group.disabled, .bootstrap-select.btn-group > .disabled { cursor: not-allowed; } .bootstrap-select.btn-group.disabled:focus, .bootstrap-select.btn-group > .disabled:focus { outline: none !important; } .bootstrap-select.btn-group.bs-container { position: absolute; height: 0 !important; padding: 0 !important; } .bootstrap-select.btn-group.bs-container .dropdown-menu { z-index: 1060; } .bootstrap-select.btn-group .dropdown-toggle .filter-option { display: inline-block; overflow: hidden; width: 100%; text-align: left; } .bootstrap-select.btn-group .dropdown-toggle .caret { position: absolute; top: 50%; right: 12px; margin-top: -2px; vertical-align: middle; } .bootstrap-select.btn-group[class*="col-"] .dropdown-toggle { width: 100%; } .bootstrap-select.btn-group .dropdown-menu { min-width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .bootstrap-select.btn-group .dropdown-menu.inner { position: static; float: none; border: 0; padding: 0; margin: 0; border-radius: 0; -webkit-box-shadow: none; box-shadow: none; } .bootstrap-select.btn-group .dropdown-menu li { position: relative; } .bootstrap-select.btn-group .dropdown-menu li.active small { color: #fff; } .bootstrap-select.btn-group .dropdown-menu li.disabled a { cursor: not-allowed; } .bootstrap-select.btn-group .dropdown-menu li a { cursor: pointer; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .bootstrap-select.btn-group .dropdown-menu li a.opt { position: relative; padding-left: 2.25em; } .bootstrap-select.btn-group .dropdown-menu li a span.check-mark { display: none; } .bootstrap-select.btn-group .dropdown-menu li a span.text { display: inline-block; } .bootstrap-select.btn-group .dropdown-menu li small { padding-left: 0.5em; } .bootstrap-select.btn-group .dropdown-menu .notify { position: absolute; bottom: 5px; width: 96%; margin: 0 2%; min-height: 26px; padding: 3px 5px; background: #f5f5f5; border: 1px solid #e3e3e3; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); pointer-events: none; opacity: 0.9; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .bootstrap-select.btn-group .no-results { padding: 3px; background: #f5f5f5; margin: 0 5px; white-space: nowrap; } .bootstrap-select.btn-group.fit-width .dropdown-toggle .filter-option { position: static; } .bootstrap-select.btn-group.fit-width .dropdown-toggle .caret { position: static; top: auto; margin-top: -1px; } .bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a span.check-mark { position: absolute; display: inline-block; right: 15px; margin-top: 5px; } .bootstrap-select.btn-group.show-tick .dropdown-menu li a span.text { margin-right: 34px; } .bootstrap-select.show-menu-arrow.open > .dropdown-toggle { z-index: 1061; } .bootstrap-select.show-menu-arrow .dropdown-toggle:before { content: ''; border-left: 7px solid transparent; border-right: 7px solid transparent; border-bottom: 7px solid rgba(204, 204, 204, 0.2); position: absolute; bottom: -4px; left: 9px; display: none; } .bootstrap-select.show-menu-arrow .dropdown-toggle:after { content: ''; border-left: 6px solid transparent; border-right: 6px solid transparent; border-bottom: 6px solid white; position: absolute; bottom: -4px; left: 10px; display: none; } .bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:before { bottom: auto; top: -3px; border-top: 7px solid rgba(204, 204, 204, 0.2); border-bottom: 0; } .bootstrap-select.show-menu-arrow.dropup .dropdown-toggle:after { bottom: auto; top: -3px; border-top: 6px solid white; border-bottom: 0; } .bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:before { right: 12px; left: auto; } .bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle:after { right: 13px; left: auto; } .bootstrap-select.show-menu-arrow.open > .dropdown-toggle:before, .bootstrap-select.show-menu-arrow.open > .dropdown-toggle:after { display: block; } .bs-searchbox, .bs-actionsbox, .bs-donebutton { padding: 4px 8px; } .bs-actionsbox { width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .bs-actionsbox .btn-group button { width: 50%; } .bs-donebutton { float: left; width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .bs-donebutton .btn-group button { width: 100%; } .bs-searchbox + .bs-actionsbox { padding: 0 8px 4px; } .bs-searchbox .form-control { margin-bottom: 0; width: 100%; float: none; } /*# sourceMappingURL=bootstrap-select.css.map */ ================================================ FILE: app_backend/static/css/bootstrap-theme.css ================================================ /*! * Bootstrap v3.3.5 (http://getbootstrap.com) * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ .btn-default, .btn-primary, .btn-success, .btn-info, .btn-warning, .btn-danger { text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); } .btn-default:active, .btn-primary:active, .btn-success:active, .btn-info:active, .btn-warning:active, .btn-danger:active, .btn-default.active, .btn-primary.active, .btn-success.active, .btn-info.active, .btn-warning.active, .btn-danger.active { -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); } .btn-default.disabled, .btn-primary.disabled, .btn-success.disabled, .btn-info.disabled, .btn-warning.disabled, .btn-danger.disabled, .btn-default[disabled], .btn-primary[disabled], .btn-success[disabled], .btn-info[disabled], .btn-warning[disabled], .btn-danger[disabled], fieldset[disabled] .btn-default, fieldset[disabled] .btn-primary, fieldset[disabled] .btn-success, fieldset[disabled] .btn-info, fieldset[disabled] .btn-warning, fieldset[disabled] .btn-danger { -webkit-box-shadow: none; box-shadow: none; } .btn-default .badge, .btn-primary .badge, .btn-success .badge, .btn-info .badge, .btn-warning .badge, .btn-danger .badge { text-shadow: none; } .btn:active, .btn.active { background-image: none; } .btn-default { text-shadow: 0 1px 0 #fff; background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #dbdbdb; border-color: #ccc; } .btn-default:hover, .btn-default:focus { background-color: #e0e0e0; background-position: 0 -15px; } .btn-default:active, .btn-default.active { background-color: #e0e0e0; border-color: #dbdbdb; } .btn-default.disabled, .btn-default[disabled], fieldset[disabled] .btn-default, .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, .btn-default.disabled:active, .btn-default[disabled]:active, fieldset[disabled] .btn-default:active, .btn-default.disabled.active, .btn-default[disabled].active, fieldset[disabled] .btn-default.active { background-color: #e0e0e0; background-image: none; } .btn-primary { background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #245580; } .btn-primary:hover, .btn-primary:focus { background-color: #265a88; background-position: 0 -15px; } .btn-primary:active, .btn-primary.active { background-color: #265a88; border-color: #245580; } .btn-primary.disabled, .btn-primary[disabled], fieldset[disabled] .btn-primary, .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, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active, .btn-primary.disabled.active, .btn-primary[disabled].active, fieldset[disabled] .btn-primary.active { background-color: #265a88; background-image: none; } .btn-success { background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #3e8f3e; } .btn-success:hover, .btn-success:focus { background-color: #419641; background-position: 0 -15px; } .btn-success:active, .btn-success.active { background-color: #419641; border-color: #3e8f3e; } .btn-success.disabled, .btn-success[disabled], fieldset[disabled] .btn-success, .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, .btn-success.disabled:active, .btn-success[disabled]:active, fieldset[disabled] .btn-success:active, .btn-success.disabled.active, .btn-success[disabled].active, fieldset[disabled] .btn-success.active { background-color: #419641; background-image: none; } .btn-info { background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #28a4c9; } .btn-info:hover, .btn-info:focus { background-color: #2aabd2; background-position: 0 -15px; } .btn-info:active, .btn-info.active { background-color: #2aabd2; border-color: #28a4c9; } .btn-info.disabled, .btn-info[disabled], fieldset[disabled] .btn-info, .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, .btn-info.disabled:active, .btn-info[disabled]:active, fieldset[disabled] .btn-info:active, .btn-info.disabled.active, .btn-info[disabled].active, fieldset[disabled] .btn-info.active { background-color: #2aabd2; background-image: none; } .btn-warning { background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #e38d13; } .btn-warning:hover, .btn-warning:focus { background-color: #eb9316; background-position: 0 -15px; } .btn-warning:active, .btn-warning.active { background-color: #eb9316; border-color: #e38d13; } .btn-warning.disabled, .btn-warning[disabled], fieldset[disabled] .btn-warning, .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, .btn-warning.disabled:active, .btn-warning[disabled]:active, fieldset[disabled] .btn-warning:active, .btn-warning.disabled.active, .btn-warning[disabled].active, fieldset[disabled] .btn-warning.active { background-color: #eb9316; background-image: none; } .btn-danger { background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-color: #b92c28; } .btn-danger:hover, .btn-danger:focus { background-color: #c12e2a; background-position: 0 -15px; } .btn-danger:active, .btn-danger.active { background-color: #c12e2a; border-color: #b92c28; } .btn-danger.disabled, .btn-danger[disabled], fieldset[disabled] .btn-danger, .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, .btn-danger.disabled:active, .btn-danger[disabled]:active, fieldset[disabled] .btn-danger:active, .btn-danger.disabled.active, .btn-danger[disabled].active, fieldset[disabled] .btn-danger.active { background-color: #c12e2a; background-image: none; } .thumbnail, .img-thumbnail { -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); box-shadow: 0 1px 2px rgba(0, 0, 0, .075); } .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { background-color: #e8e8e8; background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); background-repeat: repeat-x; } .dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus { background-color: #2e6da4; background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); background-repeat: repeat-x; } .navbar-default { background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-radius: 4px; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); } .navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .active > a { background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); background-repeat: repeat-x; -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); } .navbar-brand, .navbar-nav > li > a { text-shadow: 0 1px 0 rgba(255, 255, 255, .25); } .navbar-inverse { background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); background-repeat: repeat-x; border-radius: 4px; } .navbar-inverse .navbar-nav > .open > a, .navbar-inverse .navbar-nav > .active > a { background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); background-repeat: repeat-x; -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); } .navbar-inverse .navbar-brand, .navbar-inverse .navbar-nav > li > a { text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); } .navbar-static-top, .navbar-fixed-top, .navbar-fixed-bottom { border-radius: 0; } @media (max-width: 767px) { .navbar .navbar-nav .open .dropdown-menu > .active > a, .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { color: #fff; background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); background-repeat: repeat-x; } } .alert { text-shadow: 0 1px 0 rgba(255, 255, 255, .2); -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); } .alert-success { background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); background-repeat: repeat-x; border-color: #b2dba1; } .alert-info { background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); background-repeat: repeat-x; border-color: #9acfea; } .alert-warning { background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); background-repeat: repeat-x; border-color: #f5e79e; } .alert-danger { background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); background-repeat: repeat-x; border-color: #dca7a7; } .progress { background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); background-repeat: repeat-x; } .progress-bar { background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); background-repeat: repeat-x; } .progress-bar-success { background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); background-repeat: repeat-x; } .progress-bar-info { background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); background-repeat: repeat-x; } .progress-bar-warning { background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); background-repeat: repeat-x; } .progress-bar-danger { background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); background-repeat: repeat-x; } .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); } .list-group { border-radius: 4px; -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); box-shadow: 0 1px 2px rgba(0, 0, 0, .075); } .list-group-item.active, .list-group-item.active:hover, .list-group-item.active:focus { text-shadow: 0 -1px 0 #286090; background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); background-repeat: repeat-x; border-color: #2b669a; } .list-group-item.active .badge, .list-group-item.active:hover .badge, .list-group-item.active:focus .badge { text-shadow: none; } .panel { -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); box-shadow: 0 1px 2px rgba(0, 0, 0, .05); } .panel-default > .panel-heading { background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); background-repeat: repeat-x; } .panel-primary > .panel-heading { background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); background-repeat: repeat-x; } .panel-success > .panel-heading { background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); background-repeat: repeat-x; } .panel-info > .panel-heading { background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); background-repeat: repeat-x; } .panel-warning > .panel-heading { background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); background-repeat: repeat-x; } .panel-danger > .panel-heading { background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); background-repeat: repeat-x; } .well { background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); background-repeat: repeat-x; border-color: #dcdcdc; -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); } /*# sourceMappingURL=bootstrap-theme.css.map */ ================================================ FILE: app_backend/static/css/bootstrap.css ================================================ /*! * Bootstrap v3.3.5 (http://getbootstrap.com) * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ html { font-family: sans-serif; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } body { margin: 0; } article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; } audio, canvas, progress, video { display: inline-block; vertical-align: baseline; } audio:not([controls]) { display: none; height: 0; } [hidden], template { display: none; } a { background-color: transparent; } a:active, a:hover { outline: 0; } abbr[title] { border-bottom: 1px dotted; } b, strong { font-weight: 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: "\2a"; } .glyphicon-plus:before { content: "\2b"; } .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: thin dotted; 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: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } output { display: block; padding-top: 7px; font-size: 14px; line-height: 1.42857143; color: #555; } .form-control { display: block; width: 100%; height: 34px; padding: 6px 12px; font-size: 14px; line-height: 1.42857143; color: #555; background-color: #fff; background-image: none; border: 1px solid #ccc; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; } .form-control:focus { border-color: #66afe9; outline: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); } .form-control::-moz-placeholder { color: #999; opacity: 1; } .form-control:-ms-input-placeholder { color: #999; } .form-control::-webkit-input-placeholder { color: #999; } .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { background-color: #eee; opacity: 1; } .form-control[disabled], fieldset[disabled] .form-control { cursor: not-allowed; } textarea.form-control { height: auto; } input[type="search"] { -webkit-appearance: none; } @media screen and (-webkit-min-device-pixel-ratio: 0) { input[type="date"].form-control, input[type="time"].form-control, input[type="datetime-local"].form-control, input[type="month"].form-control { line-height: 34px; } input[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: 14.333333px; 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: thin dotted; 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, .btn-default[disabled], fieldset[disabled] .btn-default, .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, .btn-default.disabled:active, .btn-default[disabled]:active, fieldset[disabled] .btn-default:active, .btn-default.disabled.active, .btn-default[disabled].active, fieldset[disabled] .btn-default.active { 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, .btn-primary[disabled], fieldset[disabled] .btn-primary, .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, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active, .btn-primary.disabled.active, .btn-primary[disabled].active, fieldset[disabled] .btn-primary.active { 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, .btn-success[disabled], fieldset[disabled] .btn-success, .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, .btn-success.disabled:active, .btn-success[disabled]:active, fieldset[disabled] .btn-success:active, .btn-success.disabled.active, .btn-success[disabled].active, fieldset[disabled] .btn-success.active { 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, .btn-info[disabled], fieldset[disabled] .btn-info, .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, .btn-info.disabled:active, .btn-info[disabled]:active, fieldset[disabled] .btn-info:active, .btn-info.disabled.active, .btn-info[disabled].active, fieldset[disabled] .btn-info.active { 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, .btn-warning[disabled], fieldset[disabled] .btn-warning, .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, .btn-warning.disabled:active, .btn-warning[disabled]:active, fieldset[disabled] .btn-warning:active, .btn-warning.disabled.active, .btn-warning[disabled].active, fieldset[disabled] .btn-warning.active { 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, .btn-danger[disabled], fieldset[disabled] .btn-danger, .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, .btn-danger.disabled:active, .btn-danger[disabled]:active, fieldset[disabled] .btn-danger:active, .btn-danger.disabled.active, .btn-danger[disabled].active, fieldset[disabled] .btn-danger.active { 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-right-radius: 4px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn:last-child:not(:first-child) { border-top-left-radius: 0; border-top-right-radius: 0; border-bottom-left-radius: 4px; } .btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, .btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { border-top-left-radius: 0; border-top-right-radius: 0; } .btn-group-justified { display: table; width: 100%; table-layout: fixed; border-collapse: separate; } .btn-group-justified > .btn, .btn-group-justified > .btn-group { display: table-cell; float: none; width: 1%; } .btn-group-justified > .btn-group .btn { width: 100%; } .btn-group-justified > .btn-group .dropdown-menu { left: auto; } [data-toggle="buttons"] > .btn input[type="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-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: 3; 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: 2; 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 { 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 { min-height: 16.42857143px; 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); 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: -15px; font-size: 30px; } .carousel-control .glyphicon-chevron-left, .carousel-control .icon-prev { margin-left: -15px; } .carousel-control .glyphicon-chevron-right, .carousel-control .icon-next { margin-right: -15px; } .carousel-caption { right: 20%; left: 20%; padding-bottom: 30px; } .carousel-indicators { bottom: 20px; } } .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-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-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 */ ================================================ FILE: app_backend/static/css/custom.css ================================================ .fa { font-size: initial; } ================================================ FILE: app_backend/static/css/lightbox.css ================================================ /* Preload images */ body:after { content: url(../images/close.png) url(../images/loading.gif) url(../images/prev.png) url(../images/next.png); display: none; } body.lb-disable-scrolling { overflow: hidden; } .lightboxOverlay { position: absolute; top: 0; left: 0; z-index: 9999; background-color: black; filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80); opacity: 0.8; display: none; } .lightbox { position: absolute; left: 0; width: 100%; z-index: 10000; text-align: center; line-height: 0; font-weight: normal; } .lightbox .lb-image { display: block; height: auto; max-width: inherit; border-radius: 3px; } .lightbox a img { border: none; } .lb-outerContainer { position: relative; background-color: white; *zoom: 1; width: 250px; height: 250px; margin: 0 auto; border-radius: 4px; } .lb-outerContainer:after { content: ""; display: table; clear: both; } .lb-container { padding: 4px; } .lb-loader { position: absolute; top: 43%; left: 0; height: 25%; width: 100%; text-align: center; line-height: 0; } .lb-cancel { display: block; width: 32px; height: 32px; margin: 0 auto; background: url(../images/loading.gif) no-repeat; } .lb-nav { position: absolute; top: 0; left: 0; height: 100%; width: 100%; z-index: 10; } .lb-container > .nav { left: 0; } .lb-nav a { outline: none; background-image: url('data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='); } .lb-prev, .lb-next { height: 100%; cursor: pointer; display: block; } .lb-nav a.lb-prev { width: 34%; left: 0; float: left; background: url(../images/prev.png) left 48% no-repeat; filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); opacity: 0; -webkit-transition: opacity 0.6s; -moz-transition: opacity 0.6s; -o-transition: opacity 0.6s; transition: opacity 0.6s; } .lb-nav a.lb-prev:hover { filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); opacity: 1; } .lb-nav a.lb-next { width: 64%; right: 0; float: right; background: url(../images/next.png) right 48% no-repeat; filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0); opacity: 0; -webkit-transition: opacity 0.6s; -moz-transition: opacity 0.6s; -o-transition: opacity 0.6s; transition: opacity 0.6s; } .lb-nav a.lb-next:hover { filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); opacity: 1; } .lb-dataContainer { margin: 0 auto; padding-top: 5px; *zoom: 1; width: 100%; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } .lb-dataContainer:after { content: ""; display: table; clear: both; } .lb-data { padding: 0 4px; color: #ccc; } .lb-data .lb-details { width: 85%; float: left; text-align: left; line-height: 1.1em; } .lb-data .lb-caption { font-size: 13px; font-weight: bold; line-height: 1em; } .lb-data .lb-number { display: block; clear: left; padding-bottom: 1em; font-size: 12px; color: #999999; } .lb-data .lb-close { display: block; float: right; width: 30px; height: 30px; background: url(../images/close.png) top right no-repeat; text-align: right; outline: none; filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70); opacity: 0.7; -webkit-transition: opacity 0.2s; -moz-transition: opacity 0.2s; -o-transition: opacity 0.2s; transition: opacity 0.2s; } .lb-data .lb-close:hover { cursor: pointer; filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); opacity: 1; } ================================================ FILE: app_backend/static/css/sb-admin-2.css ================================================ /*! * Start Bootstrap - SB Admin 2 v3.3.7+1 (http://startbootstrap.com/template-overviews/sb-admin-2) * Copyright 2013-2016 Start Bootstrap * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap/blob/gh-pages/LICENSE) */ body { background-color: #f8f8f8; } #wrapper { width: 100%; } #page-wrapper { /*padding: 0 15px;*/ min-height: 568px; background-color: white; } @media (min-width: 768px) { #page-wrapper { position: inherit; margin: 0 0 0 250px; /*padding: 0 30px;*/ border-left: 1px solid #e7e7e7; } } .navbar-top-links { margin-right: 0; } .navbar-top-links li { display: inline-block; } .navbar-top-links li:last-child { margin-right: 15px; } .navbar-top-links li a { padding: 15px; min-height: 50px; } .navbar-top-links .dropdown-menu li { display: block; } .navbar-top-links .dropdown-menu li:last-child { margin-right: 0; } .navbar-top-links .dropdown-menu li a { padding: 3px 20px; min-height: 0; } .navbar-top-links .dropdown-menu li a div { white-space: normal; } .navbar-top-links .dropdown-messages, .navbar-top-links .dropdown-tasks, .navbar-top-links .dropdown-alerts { width: 310px; min-width: 0; } .navbar-top-links .dropdown-messages { margin-left: 5px; } .navbar-top-links .dropdown-tasks { margin-left: -59px; } .navbar-top-links .dropdown-alerts { margin-left: -123px; } .navbar-top-links .dropdown-user { right: 0; left: auto; } .sidebar .sidebar-nav.navbar-collapse { padding-left: 0; padding-right: 0; } .sidebar .sidebar-search { padding: 15px; } .sidebar ul li { border-bottom: 1px solid #e7e7e7; } .sidebar ul li a.active { background-color: #eeeeee; } .sidebar .arrow { float: right; } .sidebar .fa.arrow:before { content: "\f104"; } .sidebar .active > a > .fa.arrow:before { content: "\f107"; } .sidebar .nav-second-level li, .sidebar .nav-third-level li { border-bottom: none !important; } .sidebar .nav-second-level li a { padding-left: 37px; } .sidebar .nav-third-level li a { padding-left: 52px; } @media (min-width: 768px) { .sidebar { z-index: 1; position: absolute; width: 250px; margin-top: 51px; } .navbar-top-links .dropdown-messages, .navbar-top-links .dropdown-tasks, .navbar-top-links .dropdown-alerts { margin-left: auto; } } .btn-outline { color: inherit; background-color: transparent; transition: all .5s; } .btn-primary.btn-outline { color: #428bca; } .btn-success.btn-outline { color: #5cb85c; } .btn-info.btn-outline { color: #5bc0de; } .btn-warning.btn-outline { color: #f0ad4e; } .btn-danger.btn-outline { color: #d9534f; } .btn-primary.btn-outline:hover, .btn-success.btn-outline:hover, .btn-info.btn-outline:hover, .btn-warning.btn-outline:hover, .btn-danger.btn-outline:hover { color: white; } .chat { margin: 0; padding: 0; list-style: none; } .chat li { margin-bottom: 10px; padding-bottom: 5px; border-bottom: 1px dotted #999999; } .chat li.left .chat-body { margin-left: 60px; } .chat li.right .chat-body { margin-right: 60px; } .chat li .chat-body p { margin: 0; } .panel .slidedown .glyphicon, .chat .glyphicon { margin-right: 5px; } .chat-panel .panel-body { height: 350px; overflow-y: scroll; } .login-panel { margin-top: 25%; } .flot-chart { display: block; height: 400px; } .flot-chart-content { width: 100%; height: 100%; } table.dataTable thead .sorting, table.dataTable thead .sorting_asc, table.dataTable thead .sorting_desc, table.dataTable thead .sorting_asc_disabled, table.dataTable thead .sorting_desc_disabled { background: transparent; } table.dataTable thead .sorting_asc:after { content: "\f0de"; float: right; font-family: fontawesome; } table.dataTable thead .sorting_desc:after { content: "\f0dd"; float: right; font-family: fontawesome; } table.dataTable thead .sorting:after { content: "\f0dc"; float: right; font-family: fontawesome; color: rgba(50, 50, 50, 0.5); } .btn-circle { width: 30px; height: 30px; padding: 6px 0; border-radius: 15px; text-align: center; font-size: 12px; line-height: 1.428571429; } .btn-circle.btn-lg { width: 50px; height: 50px; padding: 10px 16px; border-radius: 25px; font-size: 18px; line-height: 1.33; } .btn-circle.btn-xl { width: 70px; height: 70px; padding: 10px 16px; border-radius: 35px; font-size: 24px; line-height: 1.33; } .show-grid [class^="col-"] { padding-top: 10px; padding-bottom: 10px; border: 1px solid #ddd; background-color: #eee !important; } .show-grid { margin: 15px 0; } .huge { font-size: 40px; } .panel-green { border-color: #5cb85c; } .panel-green > .panel-heading { border-color: #5cb85c; color: white; background-color: #5cb85c; } .panel-green > a { color: #5cb85c; } .panel-green > a:hover { color: #3d8b3d; } .panel-red { border-color: #d9534f; } .panel-red > .panel-heading { border-color: #d9534f; color: white; background-color: #d9534f; } .panel-red > a { color: #d9534f; } .panel-red > a:hover { color: #b52b27; } .panel-yellow { border-color: #f0ad4e; } .panel-yellow > .panel-heading { border-color: #f0ad4e; color: white; background-color: #f0ad4e; } .panel-yellow > a { color: #f0ad4e; } .panel-yellow > a:hover { color: #df8a13; } .timeline { position: relative; padding: 20px 0 20px; list-style: none; } .timeline:before { content: " "; position: absolute; top: 0; bottom: 0; left: 50%; width: 3px; margin-left: -1.5px; background-color: #eeeeee; } .timeline > li { position: relative; margin-bottom: 20px; } .timeline > li:before, .timeline > li:after { content: " "; display: table; } .timeline > li:after { clear: both; } .timeline > li:before, .timeline > li:after { content: " "; display: table; } .timeline > li:after { clear: both; } .timeline > li > .timeline-panel { float: left; position: relative; width: 46%; padding: 20px; border: 1px solid #d4d4d4; border-radius: 2px; -webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175); box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175); } .timeline > li > .timeline-panel:before { content: " "; display: inline-block; position: absolute; top: 26px; right: -15px; border-top: 15px solid transparent; border-right: 0 solid #ccc; border-bottom: 15px solid transparent; border-left: 15px solid #ccc; } .timeline > li > .timeline-panel:after { content: " "; display: inline-block; position: absolute; top: 27px; right: -14px; border-top: 14px solid transparent; border-right: 0 solid #fff; border-bottom: 14px solid transparent; border-left: 14px solid #fff; } .timeline > li > .timeline-badge { z-index: 100; position: absolute; top: 16px; left: 50%; width: 50px; height: 50px; margin-left: -25px; border-radius: 50% 50% 50% 50%; text-align: center; font-size: 1.4em; line-height: 50px; color: #fff; background-color: #999999; } .timeline > li.timeline-inverted > .timeline-panel { float: right; } .timeline > li.timeline-inverted > .timeline-panel:before { right: auto; left: -15px; border-right-width: 15px; border-left-width: 0; } .timeline > li.timeline-inverted > .timeline-panel:after { right: auto; left: -14px; border-right-width: 14px; border-left-width: 0; } .timeline-badge.primary { background-color: #2e6da4 !important; } .timeline-badge.success { background-color: #3f903f !important; } .timeline-badge.warning { background-color: #f0ad4e !important; } .timeline-badge.danger { background-color: #d9534f !important; } .timeline-badge.info { background-color: #5bc0de !important; } .timeline-title { margin-top: 0; color: inherit; } .timeline-body > p, .timeline-body > ul { margin-bottom: 0; } .timeline-body > p + p { margin-top: 5px; } @media (max-width: 767px) { ul.timeline:before { left: 40px; } ul.timeline > li > .timeline-panel { width: calc(10%); width: -moz-calc(10%); width: -webkit-calc(10%); } ul.timeline > li > .timeline-badge { top: 16px; left: 15px; margin-left: 0; } ul.timeline > li > .timeline-panel { float: right; } ul.timeline > li > .timeline-panel:before { right: auto; left: -15px; border-right-width: 15px; border-left-width: 0; } ul.timeline > li > .timeline-panel:after { right: auto; left: -14px; border-right-width: 14px; border-left-width: 0; } } ================================================ FILE: app_backend/static/css/slideout.css ================================================ .page-menu { background-image: linear-gradient(90deg, #eeeeee 90%, #bbbbbb 100%); } .page-menu .container { margin-right: 10%; } .page-panel { background-color: #fff; margin-bottom: 0; } .slideout-menu { position: fixed; left: 0; top: 0; bottom: 0; right: 0; z-index: 0; width: 256px; overflow-y: auto; -webkit-overflow-scrolling: touch; display: none; } .slideout-panel { position: relative; z-index: 1; /*will-change: transform;*/ } .slideout-open, .slideout-open body, .slideout-open .slideout-panel { overflow: hidden; } .slideout-open .slideout-menu { display: block; } ================================================ FILE: app_backend/static/csv/flare.csv ================================================ id,value flare, flare.analytics, flare.analytics.cluster, flare.analytics.cluster.AgglomerativeCluster,3938 flare.analytics.cluster.CommunityStructure,3812 flare.analytics.cluster.HierarchicalCluster,6714 flare.analytics.cluster.MergeEdge,743 flare.analytics.graph, flare.analytics.graph.BetweennessCentrality,3534 flare.analytics.graph.LinkDistance,5731 flare.analytics.graph.MaxFlowMinCut,7840 flare.analytics.graph.ShortestPaths,5914 flare.analytics.graph.SpanningTree,3416 flare.analytics.optimization, flare.analytics.optimization.AspectRatioBanker,7074 flare.animate, flare.animate.Easing,17010 flare.animate.FunctionSequence,5842 flare.animate.interpolate, flare.animate.interpolate.ArrayInterpolator,1983 flare.animate.interpolate.ColorInterpolator,2047 flare.animate.interpolate.DateInterpolator,1375 flare.animate.interpolate.Interpolator,8746 flare.animate.interpolate.MatrixInterpolator,2202 flare.animate.interpolate.NumberInterpolator,1382 flare.animate.interpolate.ObjectInterpolator,1629 flare.animate.interpolate.PointInterpolator,1675 flare.animate.interpolate.RectangleInterpolator,2042 flare.animate.ISchedulable,1041 flare.animate.Parallel,5176 flare.animate.Pause,449 flare.animate.Scheduler,5593 flare.animate.Sequence,5534 flare.animate.Transition,9201 flare.animate.Transitioner,19975 flare.animate.TransitionEvent,1116 flare.animate.Tween,6006 flare.data, flare.data.converters, flare.data.converters.Converters,721 flare.data.converters.DelimitedTextConverter,4294 flare.data.converters.GraphMLConverter,9800 flare.data.converters.IDataConverter,1314 flare.data.converters.JSONConverter,2220 flare.data.DataField,1759 flare.data.DataSchema,2165 flare.data.DataSet,586 flare.data.DataSource,3331 flare.data.DataTable,772 flare.data.DataUtil,3322 flare.display, flare.display.DirtySprite,8833 flare.display.LineSprite,1732 flare.display.RectSprite,3623 flare.display.TextSprite,10066 flare.flex, flare.flex.FlareVis,4116 flare.physics, flare.physics.DragForce,1082 flare.physics.GravityForce,1336 flare.physics.IForce,319 flare.physics.NBodyForce,10498 flare.physics.Particle,2822 flare.physics.Simulation,9983 flare.physics.Spring,2213 flare.physics.SpringForce,1681 flare.query, flare.query.AggregateExpression,1616 flare.query.And,1027 flare.query.Arithmetic,3891 flare.query.Average,891 flare.query.BinaryExpression,2893 flare.query.Comparison,5103 flare.query.CompositeExpression,3677 flare.query.Count,781 flare.query.DateUtil,4141 flare.query.Distinct,933 flare.query.Expression,5130 flare.query.ExpressionIterator,3617 flare.query.Fn,3240 flare.query.If,2732 flare.query.IsA,2039 flare.query.Literal,1214 flare.query.Match,3748 flare.query.Maximum,843 flare.query.methods, flare.query.methods.add,593 flare.query.methods.and,330 flare.query.methods.average,287 flare.query.methods.count,277 flare.query.methods.distinct,292 flare.query.methods.div,595 flare.query.methods.eq,594 flare.query.methods.fn,460 flare.query.methods.gt,603 flare.query.methods.gte,625 flare.query.methods.iff,748 flare.query.methods.isa,461 flare.query.methods.lt,597 flare.query.methods.lte,619 flare.query.methods.max,283 flare.query.methods.min,283 flare.query.methods.mod,591 flare.query.methods.mul,603 flare.query.methods.neq,599 flare.query.methods.not,386 flare.query.methods.or,323 flare.query.methods.orderby,307 flare.query.methods.range,772 flare.query.methods.select,296 flare.query.methods.stddev,363 flare.query.methods.sub,600 flare.query.methods.sum,280 flare.query.methods.update,307 flare.query.methods.variance,335 flare.query.methods.where,299 flare.query.methods.xor,354 flare.query.methods._,264 flare.query.Minimum,843 flare.query.Not,1554 flare.query.Or,970 flare.query.Query,13896 flare.query.Range,1594 flare.query.StringUtil,4130 flare.query.Sum,791 flare.query.Variable,1124 flare.query.Variance,1876 flare.query.Xor,1101 flare.scale, flare.scale.IScaleMap,2105 flare.scale.LinearScale,1316 flare.scale.LogScale,3151 flare.scale.OrdinalScale,3770 flare.scale.QuantileScale,2435 flare.scale.QuantitativeScale,4839 flare.scale.RootScale,1756 flare.scale.Scale,4268 flare.scale.ScaleType,1821 flare.scale.TimeScale,5833 flare.util, flare.util.Arrays,8258 flare.util.Colors,10001 flare.util.Dates,8217 flare.util.Displays,12555 flare.util.Filter,2324 flare.util.Geometry,10993 flare.util.heap, flare.util.heap.FibonacciHeap,9354 flare.util.heap.HeapNode,1233 flare.util.IEvaluable,335 flare.util.IPredicate,383 flare.util.IValueProxy,874 flare.util.math, flare.util.math.DenseMatrix,3165 flare.util.math.IMatrix,2815 flare.util.math.SparseMatrix,3366 flare.util.Maths,17705 flare.util.Orientation,1486 flare.util.palette, flare.util.palette.ColorPalette,6367 flare.util.palette.Palette,1229 flare.util.palette.ShapePalette,2059 flare.util.palette.SizePalette,2291 flare.util.Property,5559 flare.util.Shapes,19118 flare.util.Sort,6887 flare.util.Stats,6557 flare.util.Strings,22026 flare.vis, flare.vis.axis, flare.vis.axis.Axes,1302 flare.vis.axis.Axis,24593 flare.vis.axis.AxisGridLine,652 flare.vis.axis.AxisLabel,636 flare.vis.axis.CartesianAxes,6703 flare.vis.controls, flare.vis.controls.AnchorControl,2138 flare.vis.controls.ClickControl,3824 flare.vis.controls.Control,1353 flare.vis.controls.ControlList,4665 flare.vis.controls.DragControl,2649 flare.vis.controls.ExpandControl,2832 flare.vis.controls.HoverControl,4896 flare.vis.controls.IControl,763 flare.vis.controls.PanZoomControl,5222 flare.vis.controls.SelectionControl,7862 flare.vis.controls.TooltipControl,8435 flare.vis.data, flare.vis.data.Data,20544 flare.vis.data.DataList,19788 flare.vis.data.DataSprite,10349 flare.vis.data.EdgeSprite,3301 flare.vis.data.NodeSprite,19382 flare.vis.data.render, flare.vis.data.render.ArrowType,698 flare.vis.data.render.EdgeRenderer,5569 flare.vis.data.render.IRenderer,353 flare.vis.data.render.ShapeRenderer,2247 flare.vis.data.ScaleBinding,11275 flare.vis.data.Tree,7147 flare.vis.data.TreeBuilder,9930 flare.vis.events, flare.vis.events.DataEvent,2313 flare.vis.events.SelectionEvent,1880 flare.vis.events.TooltipEvent,1701 flare.vis.events.VisualizationEvent,1117 flare.vis.legend, flare.vis.legend.Legend,20859 flare.vis.legend.LegendItem,4614 flare.vis.legend.LegendRange,10530 flare.vis.operator, flare.vis.operator.distortion, flare.vis.operator.distortion.BifocalDistortion,4461 flare.vis.operator.distortion.Distortion,6314 flare.vis.operator.distortion.FisheyeDistortion,3444 flare.vis.operator.encoder, flare.vis.operator.encoder.ColorEncoder,3179 flare.vis.operator.encoder.Encoder,4060 flare.vis.operator.encoder.PropertyEncoder,4138 flare.vis.operator.encoder.ShapeEncoder,1690 flare.vis.operator.encoder.SizeEncoder,1830 flare.vis.operator.filter, flare.vis.operator.filter.FisheyeTreeFilter,5219 flare.vis.operator.filter.GraphDistanceFilter,3165 flare.vis.operator.filter.VisibilityFilter,3509 flare.vis.operator.IOperator,1286 flare.vis.operator.label, flare.vis.operator.label.Labeler,9956 flare.vis.operator.label.RadialLabeler,3899 flare.vis.operator.label.StackedAreaLabeler,3202 flare.vis.operator.layout, flare.vis.operator.layout.AxisLayout,6725 flare.vis.operator.layout.BundledEdgeRouter,3727 flare.vis.operator.layout.CircleLayout,9317 flare.vis.operator.layout.CirclePackingLayout,12003 flare.vis.operator.layout.DendrogramLayout,4853 flare.vis.operator.layout.ForceDirectedLayout,8411 flare.vis.operator.layout.IcicleTreeLayout,4864 flare.vis.operator.layout.IndentedTreeLayout,3174 flare.vis.operator.layout.Layout,7881 flare.vis.operator.layout.NodeLinkTreeLayout,12870 flare.vis.operator.layout.PieLayout,2728 flare.vis.operator.layout.RadialTreeLayout,12348 flare.vis.operator.layout.RandomLayout,870 flare.vis.operator.layout.StackedAreaLayout,9121 flare.vis.operator.layout.TreeMapLayout,9191 flare.vis.operator.Operator,2490 flare.vis.operator.OperatorList,5248 flare.vis.operator.OperatorSequence,4190 flare.vis.operator.OperatorSwitch,2581 flare.vis.operator.SortOperator,2023 flare.vis.Visualization,16540 ================================================ FILE: app_backend/static/js/bootstrap-select.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { 'use strict'; // if (!String.prototype.includes) { (function () { 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` var toString = {}.toString; var defineProperty = (function () { // IE 8 only supports `Object.defineProperty` on DOM elements try { var object = {}; var $defineProperty = Object.defineProperty; var result = $defineProperty(object, object, object) && $defineProperty; } catch (error) { } return result; }()); var indexOf = ''.indexOf; var includes = function (search) { if (this == null) { throw new TypeError(); } var string = String(this); if (search && toString.call(search) == '[object RegExp]') { throw new TypeError(); } var stringLength = string.length; var searchString = String(search); var searchLength = searchString.length; var position = arguments.length > 1 ? arguments[1] : undefined; // `ToInteger` var pos = position ? Number(position) : 0; if (pos != pos) { // better `isNaN` pos = 0; } var start = Math.min(Math.max(pos, 0), stringLength); // Avoid the `indexOf` call if no match is possible if (searchLength + start > stringLength) { return false; } return indexOf.call(string, searchString, pos) != -1; }; if (defineProperty) { defineProperty(String.prototype, 'includes', { 'value': includes, 'configurable': true, 'writable': true }); } else { String.prototype.includes = includes; } }()); } if (!String.prototype.startsWith) { (function () { 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` var defineProperty = (function () { // IE 8 only supports `Object.defineProperty` on DOM elements try { var object = {}; var $defineProperty = Object.defineProperty; var result = $defineProperty(object, object, object) && $defineProperty; } catch (error) { } return result; }()); var toString = {}.toString; var startsWith = function (search) { if (this == null) { throw new TypeError(); } var string = String(this); if (search && toString.call(search) == '[object RegExp]') { throw new TypeError(); } var stringLength = string.length; var searchString = String(search); var searchLength = searchString.length; var position = arguments.length > 1 ? arguments[1] : undefined; // `ToInteger` var pos = position ? Number(position) : 0; if (pos != pos) { // better `isNaN` pos = 0; } var start = Math.min(Math.max(pos, 0), stringLength); // Avoid the `indexOf` call if no match is possible if (searchLength + start > stringLength) { return false; } var index = -1; while (++index < searchLength) { if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) { return false; } } return true; }; if (defineProperty) { defineProperty(String.prototype, 'startsWith', { 'value': startsWith, 'configurable': true, 'writable': true }); } else { String.prototype.startsWith = startsWith; } }()); } if (!Object.keys) { Object.keys = function ( o, // object k, // key r // result array ){ // initialize object and result r=[]; // iterate over object keys for (k in o) // fill result array with non-prototypical keys r.hasOwnProperty.call(o, k) && r.push(k); // return result return r; }; } // set data-selected on select element if the value has been programmatically selected // prior to initialization of bootstrap-select // * consider removing or replacing an alternative method * var valHooks = { useDefault: false, _set: $.valHooks.select.set }; $.valHooks.select.set = function(elem, value) { if (value && !valHooks.useDefault) $(elem).data('selected', true); return valHooks._set.apply(this, arguments); }; var changed_arguments = null; $.fn.triggerNative = function (eventName) { var el = this[0], event; if (el.dispatchEvent) { // for modern browsers & IE9+ if (typeof Event === 'function') { // For modern browsers event = new Event(eventName, { bubbles: true }); } else { // For IE since it doesn't support Event constructor event = document.createEvent('Event'); event.initEvent(eventName, true, false); } el.dispatchEvent(event); } else if (el.fireEvent) { // for IE8 event = document.createEventObject(); event.eventType = eventName; el.fireEvent('on' + eventName, event); } else { // fall back to jQuery.trigger this.trigger(eventName); } }; // // Case insensitive contains search $.expr.pseudos.icontains = function (obj, index, meta) { var $obj = $(obj); var haystack = ($obj.data('tokens') || $obj.text()).toString().toUpperCase(); return haystack.includes(meta[3].toUpperCase()); }; // Case insensitive begins search $.expr.pseudos.ibegins = function (obj, index, meta) { var $obj = $(obj); var haystack = ($obj.data('tokens') || $obj.text()).toString().toUpperCase(); return haystack.startsWith(meta[3].toUpperCase()); }; // Case and accent insensitive contains search $.expr.pseudos.aicontains = function (obj, index, meta) { var $obj = $(obj); var haystack = ($obj.data('tokens') || $obj.data('normalizedText') || $obj.text()).toString().toUpperCase(); return haystack.includes(meta[3].toUpperCase()); }; // Case and accent insensitive begins search $.expr.pseudos.aibegins = function (obj, index, meta) { var $obj = $(obj); var haystack = ($obj.data('tokens') || $obj.data('normalizedText') || $obj.text()).toString().toUpperCase(); return haystack.startsWith(meta[3].toUpperCase()); }; /** * Remove all diatrics from the given text. * @access private * @param {String} text * @returns {String} */ function normalizeToBase(text) { var rExps = [ {re: /[\xC0-\xC6]/g, ch: "A"}, {re: /[\xE0-\xE6]/g, ch: "a"}, {re: /[\xC8-\xCB]/g, ch: "E"}, {re: /[\xE8-\xEB]/g, ch: "e"}, {re: /[\xCC-\xCF]/g, ch: "I"}, {re: /[\xEC-\xEF]/g, ch: "i"}, {re: /[\xD2-\xD6]/g, ch: "O"}, {re: /[\xF2-\xF6]/g, ch: "o"}, {re: /[\xD9-\xDC]/g, ch: "U"}, {re: /[\xF9-\xFC]/g, ch: "u"}, {re: /[\xC7-\xE7]/g, ch: "c"}, {re: /[\xD1]/g, ch: "N"}, {re: /[\xF1]/g, ch: "n"} ]; $.each(rExps, function () { text = text ? text.replace(this.re, this.ch) : ''; }); return text; } // List of HTML entities for escaping. var escapeMap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '`': '`' }; var unescapeMap = { '&': '&', '<': '<', '>': '>', '"': '"', ''': "'", '`': '`' }; // Functions for escaping and unescaping strings to/from HTML interpolation. var createEscaper = function(map) { var escaper = function(match) { return map[match]; }; // Regexes for identifying a key that needs to be escaped. var source = '(?:' + Object.keys(map).join('|') + ')'; var testRegexp = RegExp(source); var replaceRegexp = RegExp(source, 'g'); return function(string) { string = string == null ? '' : '' + string; return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; }; }; var htmlEscape = createEscaper(escapeMap); var htmlUnescape = createEscaper(unescapeMap); var Selectpicker = function (element, options) { // bootstrap-select has been initialized - revert valHooks.select.set back to its original function if (!valHooks.useDefault) { $.valHooks.select.set = valHooks._set; valHooks.useDefault = true; } this.$element = $(element); this.$newElement = null; this.$button = null; this.$menu = null; this.$lis = null; this.options = options; // If we have no title yet, try to pull it from the html title attribute (jQuery doesnt' pick it up as it's not a // data-attribute) if (this.options.title === null) { this.options.title = this.$element.attr('title'); } // Format window padding var winPad = this.options.windowPadding; if (typeof winPad === 'number') { this.options.windowPadding = [winPad, winPad, winPad, winPad]; } //Expose public methods this.val = Selectpicker.prototype.val; this.render = Selectpicker.prototype.render; this.refresh = Selectpicker.prototype.refresh; this.setStyle = Selectpicker.prototype.setStyle; this.selectAll = Selectpicker.prototype.selectAll; this.deselectAll = Selectpicker.prototype.deselectAll; this.destroy = Selectpicker.prototype.destroy; this.remove = Selectpicker.prototype.remove; this.show = Selectpicker.prototype.show; this.hide = Selectpicker.prototype.hide; this.init(); }; Selectpicker.VERSION = '1.12.2'; // part of this is duplicated in i18n/defaults-en_US.js. Make sure to update both. Selectpicker.DEFAULTS = { noneSelectedText: 'Nothing selected', noneResultsText: 'No results matched {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected == 1) ? "{0} item selected" : "{0} items selected"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)', (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)' ]; }, selectAllText: 'Select All', deselectAllText: 'Deselect All', doneButton: false, doneButtonText: 'Close', multipleSeparator: ', ', styleBase: 'btn', style: 'btn-default', size: 'auto', title: null, selectedTextFormat: 'values', width: false, container: false, hideDisabled: false, showSubtext: false, showIcon: true, showContent: true, dropupAuto: true, header: false, liveSearch: false, liveSearchPlaceholder: null, liveSearchNormalize: false, liveSearchStyle: 'contains', actionsBox: false, iconBase: 'glyphicon', tickIcon: 'glyphicon-ok', showTick: false, template: { caret: '' }, maxOptions: false, mobile: false, selectOnTab: false, dropdownAlignRight: false, windowPadding: 0 }; Selectpicker.prototype = { constructor: Selectpicker, init: function () { var that = this, id = this.$element.attr('id'); this.$element.addClass('bs-select-hidden'); // store originalIndex (key) and newIndex (value) in this.liObj for fast accessibility // allows us to do this.$lis.eq(that.liObj[index]) instead of this.$lis.filter('[data-original-index="' + index + '"]') this.liObj = {}; this.multiple = this.$element.prop('multiple'); this.autofocus = this.$element.prop('autofocus'); this.$newElement = this.createView(); this.$element .after(this.$newElement) .appendTo(this.$newElement); this.$button = this.$newElement.children('button'); this.$menu = this.$newElement.children('.dropdown-menu'); this.$menuInner = this.$menu.children('.inner'); this.$searchbox = this.$menu.find('input'); this.$element.removeClass('bs-select-hidden'); if (this.options.dropdownAlignRight === true) this.$menu.addClass('dropdown-menu-right'); if (typeof id !== 'undefined') { this.$button.attr('data-id', id); $('label[for="' + id + '"]').click(function (e) { e.preventDefault(); that.$button.focus(); }); } this.checkDisabled(); this.clickListener(); if (this.options.liveSearch) this.liveSearchListener(); this.render(); this.setStyle(); this.setWidth(); if (this.options.container) this.selectPosition(); this.$menu.data('this', this); this.$newElement.data('this', this); if (this.options.mobile) this.mobile(); this.$newElement.on({ 'hide.bs.dropdown': function (e) { that.$menuInner.attr('aria-expanded', false); that.$element.trigger('hide.bs.select', e); }, 'hidden.bs.dropdown': function (e) { that.$element.trigger('hidden.bs.select', e); }, 'show.bs.dropdown': function (e) { that.$menuInner.attr('aria-expanded', true); that.$element.trigger('show.bs.select', e); }, 'shown.bs.dropdown': function (e) { that.$element.trigger('shown.bs.select', e); } }); if (that.$element[0].hasAttribute('required')) { this.$element.on('invalid', function () { that.$button .addClass('bs-invalid') .focus(); that.$element.on({ 'focus.bs.select': function () { that.$button.focus(); that.$element.off('focus.bs.select'); }, 'shown.bs.select': function () { that.$element .val(that.$element.val()) // set the value to hide the validation message in Chrome when menu is opened .off('shown.bs.select'); }, 'rendered.bs.select': function () { // if select is no longer invalid, remove the bs-invalid class if (this.validity.valid) that.$button.removeClass('bs-invalid'); that.$element.off('rendered.bs.select'); } }); }); } setTimeout(function () { that.$element.trigger('loaded.bs.select'); }); }, createDropdown: function () { // Options // If we are multiple or showTick option is set, then add the show-tick class var showTick = (this.multiple || this.options.showTick) ? ' show-tick' : '', inputGroup = this.$element.parent().hasClass('input-group') ? ' input-group-btn' : '', autofocus = this.autofocus ? ' autofocus' : ''; // Elements var header = this.options.header ? '
' + this.options.header + '
' : ''; var searchbox = this.options.liveSearch ? '' : ''; var actionsbox = this.multiple && this.options.actionsBox ? '
' + '
' + '' + '' + '
' + '
' : ''; var donebutton = this.multiple && this.options.doneButton ? '
' + '
' + '' + '
' + '
' : ''; var drop = '
' + '' + '' + '
'; return $(drop); }, createView: function () { var $drop = this.createDropdown(), li = this.createLi(); $drop.find('ul')[0].innerHTML = li; return $drop; }, reloadLi: function () { // rebuild var li = this.createLi(); this.$menuInner[0].innerHTML = li; }, createLi: function () { var that = this, _li = [], optID = 0, titleOption = document.createElement('option'), liIndex = -1; // increment liIndex whenever a new
  • element is created to ensure liObj is correct // Helper functions /** * @param content * @param [index] * @param [classes] * @param [optgroup] * @returns {string} */ var generateLI = function (content, index, classes, optgroup) { return '' + content + '
  • '; }; /** * @param text * @param [classes] * @param [inline] * @param [tokens] * @returns {string} */ var generateA = function (text, classes, inline, tokens) { return '' + text + '' + ''; }; if (this.options.title && !this.multiple) { // this option doesn't create a new
  • element, but does add a new option, so liIndex is decreased // since liObj is recalculated on every refresh, liIndex needs to be decreased even if the titleOption is already appended liIndex--; if (!this.$element.find('.bs-title-option').length) { // Use native JS to prepend option (faster) var element = this.$element[0]; titleOption.className = 'bs-title-option'; titleOption.innerHTML = this.options.title; titleOption.value = ''; element.insertBefore(titleOption, element.firstChild); // Check if selected or data-selected attribute is already set on an option. If not, select the titleOption option. // the selected item may have been changed by user or programmatically before the bootstrap select plugin runs, // if so, the select will have the data-selected attribute var $opt = $(element.options[element.selectedIndex]); if ($opt.attr('selected') === undefined && this.$element.data('selected') === undefined) { titleOption.selected = true; } } } this.$element.find('option').each(function (index) { var $this = $(this); liIndex++; if ($this.hasClass('bs-title-option')) return; // Get the class and text for the option var optionClass = this.className || '', inline = this.style.cssText, text = $this.data('content') ? $this.data('content') : $this.html(), tokens = $this.data('tokens') ? $this.data('tokens') : null, subtext = typeof $this.data('subtext') !== 'undefined' ? '' + $this.data('subtext') + '' : '', icon = typeof $this.data('icon') !== 'undefined' ? ' ' : '', $parent = $this.parent(), isOptgroup = $parent[0].tagName === 'OPTGROUP', isOptgroupDisabled = isOptgroup && $parent[0].disabled, isDisabled = this.disabled || isOptgroupDisabled; if (icon !== '' && isDisabled) { icon = '' + icon + ''; } if (that.options.hideDisabled && (isDisabled && !isOptgroup || isOptgroupDisabled)) { liIndex--; return; } if (!$this.data('content')) { // Prepend any icon and append any subtext to the main text. text = icon + '' + text + subtext + ''; } if (isOptgroup && $this.data('divider') !== true) { if (that.options.hideDisabled && isDisabled) { if ($parent.data('allOptionsDisabled') === undefined) { var $options = $parent.children(); $parent.data('allOptionsDisabled', $options.filter(':disabled').length === $options.length); } if ($parent.data('allOptionsDisabled')) { liIndex--; return; } } var optGroupClass = ' ' + $parent[0].className || ''; if ($this.index() === 0) { // Is it the first option of the optgroup? optID += 1; // Get the opt group label var label = $parent[0].label, labelSubtext = typeof $parent.data('subtext') !== 'undefined' ? '' + $parent.data('subtext') + '' : '', labelIcon = $parent.data('icon') ? ' ' : ''; label = labelIcon + '' + htmlEscape(label) + labelSubtext + ''; if (index !== 0 && _li.length > 0) { // Is it NOT the first option of the select && are there elements in the dropdown? liIndex++; _li.push(generateLI('', null, 'divider', optID + 'div')); } liIndex++; _li.push(generateLI(label, null, 'dropdown-header' + optGroupClass, optID)); } if (that.options.hideDisabled && isDisabled) { liIndex--; return; } _li.push(generateLI(generateA(text, 'opt ' + optionClass + optGroupClass, inline, tokens), index, '', optID)); } else if ($this.data('divider') === true) { _li.push(generateLI('', index, 'divider')); } else if ($this.data('hidden') === true) { _li.push(generateLI(generateA(text, optionClass, inline, tokens), index, 'hidden is-hidden')); } else { var showDivider = this.previousElementSibling && this.previousElementSibling.tagName === 'OPTGROUP'; // if previous element is not an optgroup and hideDisabled is true if (!showDivider && that.options.hideDisabled) { // get previous elements var $prev = $(this).prevAll(); for (var i = 0; i < $prev.length; i++) { // find the first element in the previous elements that is an optgroup if ($prev[i].tagName === 'OPTGROUP') { var optGroupDistance = 0; // loop through the options in between the current option and the optgroup // and check if they are hidden or disabled for (var d = 0; d < i; d++) { var prevOption = $prev[d]; if (prevOption.disabled || $(prevOption).data('hidden') === true) optGroupDistance++; } // if all of the options between the current option and the optgroup are hidden or disabled, show the divider if (optGroupDistance === i) showDivider = true; break; } } } if (showDivider) { liIndex++; _li.push(generateLI('', null, 'divider', optID + 'div')); } _li.push(generateLI(generateA(text, optionClass, inline, tokens), index)); } that.liObj[index] = liIndex; }); //If we are not multiple, we don't have a selected item, and we don't have a title, select the first element so something is set in the button if (!this.multiple && this.$element.find('option:selected').length === 0 && !this.options.title) { this.$element.find('option').eq(0).prop('selected', true).attr('selected', 'selected'); } return _li.join(''); }, findLis: function () { if (this.$lis == null) this.$lis = this.$menu.find('li'); return this.$lis; }, /** * @param [updateLi] defaults to true */ render: function (updateLi) { var that = this, notDisabled; //Update the LI to match the SELECT if (updateLi !== false) { this.$element.find('option').each(function (index) { var $lis = that.findLis().eq(that.liObj[index]); that.setDisabled(index, this.disabled || this.parentNode.tagName === 'OPTGROUP' && this.parentNode.disabled, $lis); that.setSelected(index, this.selected, $lis); }); } this.togglePlaceholder(); this.tabIndex(); var selectedItems = this.$element.find('option').map(function () { if (this.selected) { if (that.options.hideDisabled && (this.disabled || this.parentNode.tagName === 'OPTGROUP' && this.parentNode.disabled)) return; var $this = $(this), icon = $this.data('icon') && that.options.showIcon ? ' ' : '', subtext; if (that.options.showSubtext && $this.data('subtext') && !that.multiple) { subtext = ' ' + $this.data('subtext') + ''; } else { subtext = ''; } if (typeof $this.attr('title') !== 'undefined') { return $this.attr('title'); } else if ($this.data('content') && that.options.showContent) { return $this.data('content').toString(); } else { return icon + $this.html() + subtext; } } }).toArray(); //Fixes issue in IE10 occurring when no default option is selected and at least one option is disabled //Convert all the values into a comma delimited string var title = !this.multiple ? selectedItems[0] : selectedItems.join(this.options.multipleSeparator); //If this is multi select, and the selectText type is count, the show 1 of 2 selected etc.. if (this.multiple && this.options.selectedTextFormat.indexOf('count') > -1) { var max = this.options.selectedTextFormat.split('>'); if ((max.length > 1 && selectedItems.length > max[1]) || (max.length == 1 && selectedItems.length >= 2)) { notDisabled = this.options.hideDisabled ? ', [disabled]' : ''; var totalCount = this.$element.find('option').not('[data-divider="true"], [data-hidden="true"]' + notDisabled).length, tr8nText = (typeof this.options.countSelectedText === 'function') ? this.options.countSelectedText(selectedItems.length, totalCount) : this.options.countSelectedText; title = tr8nText.replace('{0}', selectedItems.length.toString()).replace('{1}', totalCount.toString()); } } if (this.options.title == undefined) { this.options.title = this.$element.attr('title'); } if (this.options.selectedTextFormat == 'static') { title = this.options.title; } //If we dont have a title, then use the default, or if nothing is set at all, use the not selected text if (!title) { title = typeof this.options.title !== 'undefined' ? this.options.title : this.options.noneSelectedText; } //strip all HTML tags and trim the result, then unescape any escaped tags this.$button.attr('title', htmlUnescape($.trim(title.replace(/<[^>]*>?/g, '')))); this.$button.children('.filter-option').html(title); this.$element.trigger('rendered.bs.select'); }, /** * @param [style] * @param [status] */ setStyle: function (style, status) { if (this.$element.attr('class')) { this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi, '')); } var buttonClass = style ? style : this.options.style; if (status == 'add') { this.$button.addClass(buttonClass); } else if (status == 'remove') { this.$button.removeClass(buttonClass); } else { this.$button.removeClass(this.options.style); this.$button.addClass(buttonClass); } }, liHeight: function (refresh) { if (!refresh && (this.options.size === false || this.sizeInfo)) return; var newElement = document.createElement('div'), menu = document.createElement('div'), menuInner = document.createElement('ul'), divider = document.createElement('li'), li = document.createElement('li'), a = document.createElement('a'), text = document.createElement('span'), header = this.options.header && this.$menu.find('.popover-title').length > 0 ? this.$menu.find('.popover-title')[0].cloneNode(true) : null, search = this.options.liveSearch ? document.createElement('div') : null, actions = this.options.actionsBox && this.multiple && this.$menu.find('.bs-actionsbox').length > 0 ? this.$menu.find('.bs-actionsbox')[0].cloneNode(true) : null, doneButton = this.options.doneButton && this.multiple && this.$menu.find('.bs-donebutton').length > 0 ? this.$menu.find('.bs-donebutton')[0].cloneNode(true) : null; text.className = 'text'; newElement.className = this.$menu[0].parentNode.className + ' open'; menu.className = 'dropdown-menu open'; menuInner.className = 'dropdown-menu inner'; divider.className = 'divider'; text.appendChild(document.createTextNode('Inner text')); a.appendChild(text); li.appendChild(a); menuInner.appendChild(li); menuInner.appendChild(divider); if (header) menu.appendChild(header); if (search) { var input = document.createElement('input'); search.className = 'bs-searchbox'; input.className = 'form-control'; search.appendChild(input); menu.appendChild(search); } if (actions) menu.appendChild(actions); menu.appendChild(menuInner); if (doneButton) menu.appendChild(doneButton); newElement.appendChild(menu); document.body.appendChild(newElement); var liHeight = a.offsetHeight, headerHeight = header ? header.offsetHeight : 0, searchHeight = search ? search.offsetHeight : 0, actionsHeight = actions ? actions.offsetHeight : 0, doneButtonHeight = doneButton ? doneButton.offsetHeight : 0, dividerHeight = $(divider).outerHeight(true), // fall back to jQuery if getComputedStyle is not supported menuStyle = typeof getComputedStyle === 'function' ? getComputedStyle(menu) : false, $menu = menuStyle ? null : $(menu), menuPadding = { vert: parseInt(menuStyle ? menuStyle.paddingTop : $menu.css('paddingTop')) + parseInt(menuStyle ? menuStyle.paddingBottom : $menu.css('paddingBottom')) + parseInt(menuStyle ? menuStyle.borderTopWidth : $menu.css('borderTopWidth')) + parseInt(menuStyle ? menuStyle.borderBottomWidth : $menu.css('borderBottomWidth')), horiz: parseInt(menuStyle ? menuStyle.paddingLeft : $menu.css('paddingLeft')) + parseInt(menuStyle ? menuStyle.paddingRight : $menu.css('paddingRight')) + parseInt(menuStyle ? menuStyle.borderLeftWidth : $menu.css('borderLeftWidth')) + parseInt(menuStyle ? menuStyle.borderRightWidth : $menu.css('borderRightWidth')) }, menuExtras = { vert: menuPadding.vert + parseInt(menuStyle ? menuStyle.marginTop : $menu.css('marginTop')) + parseInt(menuStyle ? menuStyle.marginBottom : $menu.css('marginBottom')) + 2, horiz: menuPadding.horiz + parseInt(menuStyle ? menuStyle.marginLeft : $menu.css('marginLeft')) + parseInt(menuStyle ? menuStyle.marginRight : $menu.css('marginRight')) + 2 } document.body.removeChild(newElement); this.sizeInfo = { liHeight: liHeight, headerHeight: headerHeight, searchHeight: searchHeight, actionsHeight: actionsHeight, doneButtonHeight: doneButtonHeight, dividerHeight: dividerHeight, menuPadding: menuPadding, menuExtras: menuExtras }; }, setSize: function () { this.findLis(); this.liHeight(); if (this.options.header) this.$menu.css('padding-top', 0); if (this.options.size === false) return; var that = this, $menu = this.$menu, $menuInner = this.$menuInner, $window = $(window), selectHeight = this.$newElement[0].offsetHeight, selectWidth = this.$newElement[0].offsetWidth, liHeight = this.sizeInfo['liHeight'], headerHeight = this.sizeInfo['headerHeight'], searchHeight = this.sizeInfo['searchHeight'], actionsHeight = this.sizeInfo['actionsHeight'], doneButtonHeight = this.sizeInfo['doneButtonHeight'], divHeight = this.sizeInfo['dividerHeight'], menuPadding = this.sizeInfo['menuPadding'], menuExtras = this.sizeInfo['menuExtras'], notDisabled = this.options.hideDisabled ? '.disabled' : '', menuHeight, menuWidth, getHeight, getWidth, selectOffsetTop, selectOffsetBot, selectOffsetLeft, selectOffsetRight, getPos = function() { var pos = that.$newElement.offset(), $container = $(that.options.container), containerPos; if (that.options.container && !$container.is('body')) { containerPos = $container.offset(); containerPos.top += parseInt($container.css('borderTopWidth')); containerPos.left += parseInt($container.css('borderLeftWidth')); } else { containerPos = { top: 0, left: 0 }; } var winPad = that.options.windowPadding; selectOffsetTop = pos.top - containerPos.top - $window.scrollTop(); selectOffsetBot = $window.height() - selectOffsetTop - selectHeight - containerPos.top - winPad[2]; selectOffsetLeft = pos.left - containerPos.left - $window.scrollLeft(); selectOffsetRight = $window.width() - selectOffsetLeft - selectWidth - containerPos.left - winPad[1]; selectOffsetTop -= winPad[0]; selectOffsetLeft -= winPad[3]; }; getPos(); if (this.options.size === 'auto') { var getSize = function () { var minHeight, hasClass = function (className, include) { return function (element) { if (include) { return (element.classList ? element.classList.contains(className) : $(element).hasClass(className)); } else { return !(element.classList ? element.classList.contains(className) : $(element).hasClass(className)); } }; }, lis = that.$menuInner[0].getElementsByTagName('li'), lisVisible = Array.prototype.filter ? Array.prototype.filter.call(lis, hasClass('hidden', false)) : that.$lis.not('.hidden'), optGroup = Array.prototype.filter ? Array.prototype.filter.call(lisVisible, hasClass('dropdown-header', true)) : lisVisible.filter('.dropdown-header'); getPos(); menuHeight = selectOffsetBot - menuExtras.vert; menuWidth = selectOffsetRight - menuExtras.horiz; if (that.options.container) { if (!$menu.data('height')) $menu.data('height', $menu.height()); getHeight = $menu.data('height'); if (!$menu.data('width')) $menu.data('width', $menu.width()); getWidth = $menu.data('width'); } else { getHeight = $menu.height(); getWidth = $menu.width(); } if (that.options.dropupAuto) { that.$newElement.toggleClass('dropup', selectOffsetTop > selectOffsetBot && (menuHeight - menuExtras.vert) < getHeight); } if (that.$newElement.hasClass('dropup')) { menuHeight = selectOffsetTop - menuExtras.vert; } if (that.options.dropdownAlignRight === 'auto') { $menu.toggleClass('dropdown-menu-right', selectOffsetLeft > selectOffsetRight && (menuWidth - menuExtras.horiz) < (getWidth - selectWidth)); } if ((lisVisible.length + optGroup.length) > 3) { minHeight = liHeight * 3 + menuExtras.vert - 2; } else { minHeight = 0; } $menu.css({ 'max-height': menuHeight + 'px', 'overflow': 'hidden', 'min-height': minHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight + 'px' }); $menuInner.css({ 'max-height': menuHeight - headerHeight - searchHeight - actionsHeight - doneButtonHeight - menuPadding.vert + 'px', 'overflow-y': 'auto', 'min-height': Math.max(minHeight - menuPadding.vert, 0) + 'px' }); }; getSize(); this.$searchbox.off('input.getSize propertychange.getSize').on('input.getSize propertychange.getSize', getSize); $window.off('resize.getSize scroll.getSize').on('resize.getSize scroll.getSize', getSize); } else if (this.options.size && this.options.size != 'auto' && this.$lis.not(notDisabled).length > this.options.size) { var optIndex = this.$lis.not('.divider').not(notDisabled).children().slice(0, this.options.size).last().parent().index(), divLength = this.$lis.slice(0, optIndex + 1).filter('.divider').length; menuHeight = liHeight * this.options.size + divLength * divHeight + menuPadding.vert; if (that.options.container) { if (!$menu.data('height')) $menu.data('height', $menu.height()); getHeight = $menu.data('height'); } else { getHeight = $menu.height(); } if (that.options.dropupAuto) { //noinspection JSUnusedAssignment this.$newElement.toggleClass('dropup', selectOffsetTop > selectOffsetBot && (menuHeight - menuExtras.vert) < getHeight); } $menu.css({ 'max-height': menuHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight + 'px', 'overflow': 'hidden', 'min-height': '' }); $menuInner.css({ 'max-height': menuHeight - menuPadding.vert + 'px', 'overflow-y': 'auto', 'min-height': '' }); } }, setWidth: function () { if (this.options.width === 'auto') { this.$menu.css('min-width', '0'); // Get correct width if element is hidden var $selectClone = this.$menu.parent().clone().appendTo('body'), $selectClone2 = this.options.container ? this.$newElement.clone().appendTo('body') : $selectClone, ulWidth = $selectClone.children('.dropdown-menu').outerWidth(), btnWidth = $selectClone2.css('width', 'auto').children('button').outerWidth(); $selectClone.remove(); $selectClone2.remove(); // Set width to whatever's larger, button title or longest option this.$newElement.css('width', Math.max(ulWidth, btnWidth) + 'px'); } else if (this.options.width === 'fit') { // Remove inline min-width so width can be changed from 'auto' this.$menu.css('min-width', ''); this.$newElement.css('width', '').addClass('fit-width'); } else if (this.options.width) { // Remove inline min-width so width can be changed from 'auto' this.$menu.css('min-width', ''); this.$newElement.css('width', this.options.width); } else { // Remove inline min-width/width so width can be changed this.$menu.css('min-width', ''); this.$newElement.css('width', ''); } // Remove fit-width class if width is changed programmatically if (this.$newElement.hasClass('fit-width') && this.options.width !== 'fit') { this.$newElement.removeClass('fit-width'); } }, selectPosition: function () { this.$bsContainer = $('
    '); var that = this, $container = $(this.options.container), pos, containerPos, actualHeight, getPlacement = function ($element) { that.$bsContainer.addClass($element.attr('class').replace(/form-control|fit-width/gi, '')).toggleClass('dropup', $element.hasClass('dropup')); pos = $element.offset(); if (!$container.is('body')) { containerPos = $container.offset(); containerPos.top += parseInt($container.css('borderTopWidth')) - $container.scrollTop(); containerPos.left += parseInt($container.css('borderLeftWidth')) - $container.scrollLeft(); } else { containerPos = { top: 0, left: 0 }; } actualHeight = $element.hasClass('dropup') ? 0 : $element[0].offsetHeight; that.$bsContainer.css({ 'top': pos.top - containerPos.top + actualHeight, 'left': pos.left - containerPos.left, 'width': $element[0].offsetWidth }); }; this.$button.on('click', function () { var $this = $(this); if (that.isDisabled()) { return; } getPlacement(that.$newElement); that.$bsContainer .appendTo(that.options.container) .toggleClass('open', !$this.hasClass('open')) .append(that.$menu); }); $(window).on('resize scroll', function () { getPlacement(that.$newElement); }); this.$element.on('hide.bs.select', function () { that.$menu.data('height', that.$menu.height()); that.$bsContainer.detach(); }); }, /** * @param {number} index - the index of the option that is being changed * @param {boolean} selected - true if the option is being selected, false if being deselected * @param {JQuery} $lis - the 'li' element that is being modified */ setSelected: function (index, selected, $lis) { if (!$lis) { this.togglePlaceholder(); // check if setSelected is being called by changing the value of the select $lis = this.findLis().eq(this.liObj[index]); } $lis.toggleClass('selected', selected).find('a').attr('aria-selected', selected); }, /** * @param {number} index - the index of the option that is being disabled * @param {boolean} disabled - true if the option is being disabled, false if being enabled * @param {JQuery} $lis - the 'li' element that is being modified */ setDisabled: function (index, disabled, $lis) { if (!$lis) { $lis = this.findLis().eq(this.liObj[index]); } if (disabled) { $lis.addClass('disabled').children('a').attr('href', '#').attr('tabindex', -1).attr('aria-disabled', true); } else { $lis.removeClass('disabled').children('a').removeAttr('href').attr('tabindex', 0).attr('aria-disabled', false); } }, isDisabled: function () { return this.$element[0].disabled; }, checkDisabled: function () { var that = this; if (this.isDisabled()) { this.$newElement.addClass('disabled'); this.$button.addClass('disabled').attr('tabindex', -1).attr('aria-disabled', true); } else { if (this.$button.hasClass('disabled')) { this.$newElement.removeClass('disabled'); this.$button.removeClass('disabled').attr('aria-disabled', false); } if (this.$button.attr('tabindex') == -1 && !this.$element.data('tabindex')) { this.$button.removeAttr('tabindex'); } } this.$button.click(function () { return !that.isDisabled(); }); }, togglePlaceholder: function () { var value = this.$element.val(); this.$button.toggleClass('bs-placeholder', value === null || value === '' || (value.constructor === Array && value.length === 0)); }, tabIndex: function () { if (this.$element.data('tabindex') !== this.$element.attr('tabindex') && (this.$element.attr('tabindex') !== -98 && this.$element.attr('tabindex') !== '-98')) { this.$element.data('tabindex', this.$element.attr('tabindex')); this.$button.attr('tabindex', this.$element.data('tabindex')); } this.$element.attr('tabindex', -98); }, clickListener: function () { var that = this, $document = $(document); $document.data('spaceSelect', false); this.$button.on('keyup', function (e) { if (/(32)/.test(e.keyCode.toString(10)) && $document.data('spaceSelect')) { e.preventDefault(); $document.data('spaceSelect', false); } }); this.$button.on('click', function () { that.setSize(); }); this.$element.on('shown.bs.select', function () { if (!that.options.liveSearch && !that.multiple) { that.$menuInner.find('.selected a').focus(); } else if (!that.multiple) { var selectedIndex = that.liObj[that.$element[0].selectedIndex]; if (typeof selectedIndex !== 'number' || that.options.size === false) return; // scroll to selected option var offset = that.$lis.eq(selectedIndex)[0].offsetTop - that.$menuInner[0].offsetTop; offset = offset - that.$menuInner[0].offsetHeight/2 + that.sizeInfo.liHeight/2; that.$menuInner[0].scrollTop = offset; } }); this.$menuInner.on('click', 'li a', function (e) { var $this = $(this), clickedIndex = $this.parent().data('originalIndex'), prevValue = that.$element.val(), prevIndex = that.$element.prop('selectedIndex'), triggerChange = true; // Don't close on multi choice menu if (that.multiple && that.options.maxOptions !== 1) { e.stopPropagation(); } e.preventDefault(); //Don't run if we have been disabled if (!that.isDisabled() && !$this.parent().hasClass('disabled')) { var $options = that.$element.find('option'), $option = $options.eq(clickedIndex), state = $option.prop('selected'), $optgroup = $option.parent('optgroup'), maxOptions = that.options.maxOptions, maxOptionsGrp = $optgroup.data('maxOptions') || false; if (!that.multiple) { // Deselect all others if not multi select box $options.prop('selected', false); $option.prop('selected', true); that.$menuInner.find('.selected').removeClass('selected').find('a').attr('aria-selected', false); that.setSelected(clickedIndex, true); } else { // Toggle the one we have chosen if we are multi select. $option.prop('selected', !state); that.setSelected(clickedIndex, !state); $this.blur(); if (maxOptions !== false || maxOptionsGrp !== false) { var maxReached = maxOptions < $options.filter(':selected').length, maxReachedGrp = maxOptionsGrp < $optgroup.find('option:selected').length; if ((maxOptions && maxReached) || (maxOptionsGrp && maxReachedGrp)) { if (maxOptions && maxOptions == 1) { $options.prop('selected', false); $option.prop('selected', true); that.$menuInner.find('.selected').removeClass('selected'); that.setSelected(clickedIndex, true); } else if (maxOptionsGrp && maxOptionsGrp == 1) { $optgroup.find('option:selected').prop('selected', false); $option.prop('selected', true); var optgroupID = $this.parent().data('optgroup'); that.$menuInner.find('[data-optgroup="' + optgroupID + '"]').removeClass('selected'); that.setSelected(clickedIndex, true); } else { var maxOptionsText = typeof that.options.maxOptionsText === 'string' ? [that.options.maxOptionsText, that.options.maxOptionsText] : that.options.maxOptionsText, maxOptionsArr = typeof maxOptionsText === 'function' ? maxOptionsText(maxOptions, maxOptionsGrp) : maxOptionsText, maxTxt = maxOptionsArr[0].replace('{n}', maxOptions), maxTxtGrp = maxOptionsArr[1].replace('{n}', maxOptionsGrp), $notify = $('
    '); // If {var} is set in array, replace it /** @deprecated */ if (maxOptionsArr[2]) { maxTxt = maxTxt.replace('{var}', maxOptionsArr[2][maxOptions > 1 ? 0 : 1]); maxTxtGrp = maxTxtGrp.replace('{var}', maxOptionsArr[2][maxOptionsGrp > 1 ? 0 : 1]); } $option.prop('selected', false); that.$menu.append($notify); if (maxOptions && maxReached) { $notify.append($('
    ' + maxTxt + '
    ')); triggerChange = false; that.$element.trigger('maxReached.bs.select'); } if (maxOptionsGrp && maxReachedGrp) { $notify.append($('
    ' + maxTxtGrp + '
    ')); triggerChange = false; that.$element.trigger('maxReachedGrp.bs.select'); } setTimeout(function () { that.setSelected(clickedIndex, false); }, 10); $notify.delay(750).fadeOut(300, function () { $(this).remove(); }); } } } } if (!that.multiple || (that.multiple && that.options.maxOptions === 1)) { that.$button.focus(); } else if (that.options.liveSearch) { that.$searchbox.focus(); } // Trigger select 'change' if (triggerChange) { if ((prevValue != that.$element.val() && that.multiple) || (prevIndex != that.$element.prop('selectedIndex') && !that.multiple)) { // $option.prop('selected') is current option state (selected/unselected). state is previous option state. changed_arguments = [clickedIndex, $option.prop('selected'), state]; that.$element .triggerNative('change'); } } } }); this.$menu.on('click', 'li.disabled a, .popover-title, .popover-title :not(.close)', function (e) { if (e.currentTarget == this) { e.preventDefault(); e.stopPropagation(); if (that.options.liveSearch && !$(e.target).hasClass('close')) { that.$searchbox.focus(); } else { that.$button.focus(); } } }); this.$menuInner.on('click', '.divider, .dropdown-header', function (e) { e.preventDefault(); e.stopPropagation(); if (that.options.liveSearch) { that.$searchbox.focus(); } else { that.$button.focus(); } }); this.$menu.on('click', '.popover-title .close', function () { that.$button.click(); }); this.$searchbox.on('click', function (e) { e.stopPropagation(); }); this.$menu.on('click', '.actions-btn', function (e) { if (that.options.liveSearch) { that.$searchbox.focus(); } else { that.$button.focus(); } e.preventDefault(); e.stopPropagation(); if ($(this).hasClass('bs-select-all')) { that.selectAll(); } else { that.deselectAll(); } }); this.$element.change(function () { that.render(false); that.$element.trigger('changed.bs.select', changed_arguments); changed_arguments = null; }); }, liveSearchListener: function () { var that = this, $no_results = $('
  • '); this.$button.on('click.dropdown.data-api', function () { that.$menuInner.find('.active').removeClass('active'); if (!!that.$searchbox.val()) { that.$searchbox.val(''); that.$lis.not('.is-hidden').removeClass('hidden'); if (!!$no_results.parent().length) $no_results.remove(); } if (!that.multiple) that.$menuInner.find('.selected').addClass('active'); setTimeout(function () { that.$searchbox.focus(); }, 10); }); this.$searchbox.on('click.dropdown.data-api focus.dropdown.data-api touchend.dropdown.data-api', function (e) { e.stopPropagation(); }); this.$searchbox.on('input propertychange', function () { that.$lis.not('.is-hidden').removeClass('hidden'); that.$lis.filter('.active').removeClass('active'); $no_results.remove(); if (that.$searchbox.val()) { var $searchBase = that.$lis.not('.is-hidden, .divider, .dropdown-header'), $hideItems; if (that.options.liveSearchNormalize) { $hideItems = $searchBase.find('a').not(':a' + that._searchStyle() + '("' + normalizeToBase(that.$searchbox.val()) + '")'); } else { $hideItems = $searchBase.find('a').not(':' + that._searchStyle() + '("' + that.$searchbox.val() + '")'); } if ($hideItems.length === $searchBase.length) { $no_results.html(that.options.noneResultsText.replace('{0}', '"' + htmlEscape(that.$searchbox.val()) + '"')); that.$menuInner.append($no_results); that.$lis.addClass('hidden'); } else { $hideItems.parent().addClass('hidden'); var $lisVisible = that.$lis.not('.hidden'), $foundDiv; // hide divider if first or last visible, or if followed by another divider $lisVisible.each(function (index) { var $this = $(this); if ($this.hasClass('divider')) { if ($foundDiv === undefined) { $this.addClass('hidden'); } else { if ($foundDiv) $foundDiv.addClass('hidden'); $foundDiv = $this; } } else if ($this.hasClass('dropdown-header') && $lisVisible.eq(index + 1).data('optgroup') !== $this.data('optgroup')) { $this.addClass('hidden'); } else { $foundDiv = null; } }); if ($foundDiv) $foundDiv.addClass('hidden'); $searchBase.not('.hidden').first().addClass('active'); } } }); }, _searchStyle: function () { var styles = { begins: 'ibegins', startsWith: 'ibegins' }; return styles[this.options.liveSearchStyle] || 'icontains'; }, val: function (value) { if (typeof value !== 'undefined') { this.$element.val(value); this.render(); return this.$element; } else { return this.$element.val(); } }, changeAll: function (status) { if (!this.multiple) return; if (typeof status === 'undefined') status = true; this.findLis(); var $options = this.$element.find('option'), $lisVisible = this.$lis.not('.divider, .dropdown-header, .disabled, .hidden'), lisVisLen = $lisVisible.length, selectedOptions = []; if (status) { if ($lisVisible.filter('.selected').length === $lisVisible.length) return; } else { if ($lisVisible.filter('.selected').length === 0) return; } $lisVisible.toggleClass('selected', status); for (var i = 0; i < lisVisLen; i++) { var origIndex = $lisVisible[i].getAttribute('data-original-index'); selectedOptions[selectedOptions.length] = $options.eq(origIndex)[0]; } $(selectedOptions).prop('selected', status); this.render(false); this.togglePlaceholder(); this.$element .triggerNative('change'); }, selectAll: function () { return this.changeAll(true); }, deselectAll: function () { return this.changeAll(false); }, toggle: function (e) { e = e || window.event; if (e) e.stopPropagation(); this.$button.trigger('click'); }, keydown: function (e) { var $this = $(this), $parent = $this.is('input') ? $this.parent().parent() : $this.parent(), $items, that = $parent.data('this'), index, next, first, last, prev, nextPrev, prevIndex, isActive, selector = ':not(.disabled, .hidden, .dropdown-header, .divider)', keyCodeMap = { 32: ' ', 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', 54: '6', 55: '7', 56: '8', 57: '9', 59: ';', 65: 'a', 66: 'b', 67: 'c', 68: 'd', 69: 'e', 70: 'f', 71: 'g', 72: 'h', 73: 'i', 74: 'j', 75: 'k', 76: 'l', 77: 'm', 78: 'n', 79: 'o', 80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't', 85: 'u', 86: 'v', 87: 'w', 88: 'x', 89: 'y', 90: 'z', 96: '0', 97: '1', 98: '2', 99: '3', 100: '4', 101: '5', 102: '6', 103: '7', 104: '8', 105: '9' }; if (that.options.liveSearch) $parent = $this.parent().parent(); if (that.options.container) $parent = that.$menu; $items = $('[role="listbox"] li', $parent); isActive = that.$newElement.hasClass('open'); if (!isActive && (e.keyCode >= 48 && e.keyCode <= 57 || e.keyCode >= 96 && e.keyCode <= 105 || e.keyCode >= 65 && e.keyCode <= 90)) { if (!that.options.container) { that.setSize(); that.$menu.parent().addClass('open'); isActive = true; } else { that.$button.trigger('click'); } that.$searchbox.focus(); return; } if (that.options.liveSearch) { if (/(^9$|27)/.test(e.keyCode.toString(10)) && isActive) { e.preventDefault(); e.stopPropagation(); that.$menuInner.click(); that.$button.focus(); } // $items contains li elements when liveSearch is enabled $items = $('[role="listbox"] li' + selector, $parent); if (!$this.val() && !/(38|40)/.test(e.keyCode.toString(10))) { if ($items.filter('.active').length === 0) { $items = that.$menuInner.find('li'); if (that.options.liveSearchNormalize) { $items = $items.filter(':a' + that._searchStyle() + '(' + normalizeToBase(keyCodeMap[e.keyCode]) + ')'); } else { $items = $items.filter(':' + that._searchStyle() + '(' + keyCodeMap[e.keyCode] + ')'); } } } } if (!$items.length) return; if (/(38|40)/.test(e.keyCode.toString(10))) { index = $items.index($items.find('a').filter(':focus').parent()); first = $items.filter(selector).first().index(); last = $items.filter(selector).last().index(); next = $items.eq(index).nextAll(selector).eq(0).index(); prev = $items.eq(index).prevAll(selector).eq(0).index(); nextPrev = $items.eq(next).prevAll(selector).eq(0).index(); if (that.options.liveSearch) { $items.each(function (i) { if (!$(this).hasClass('disabled')) { $(this).data('index', i); } }); index = $items.index($items.filter('.active')); first = $items.first().data('index'); last = $items.last().data('index'); next = $items.eq(index).nextAll().eq(0).data('index'); prev = $items.eq(index).prevAll().eq(0).data('index'); nextPrev = $items.eq(next).prevAll().eq(0).data('index'); } prevIndex = $this.data('prevIndex'); if (e.keyCode == 38) { if (that.options.liveSearch) index--; if (index != nextPrev && index > prev) index = prev; if (index < first) index = first; if (index == prevIndex) index = last; } else if (e.keyCode == 40) { if (that.options.liveSearch) index++; if (index == -1) index = 0; if (index != nextPrev && index < next) index = next; if (index > last) index = last; if (index == prevIndex) index = first; } $this.data('prevIndex', index); if (!that.options.liveSearch) { $items.eq(index).children('a').focus(); } else { e.preventDefault(); if (!$this.hasClass('dropdown-toggle')) { $items.removeClass('active').eq(index).addClass('active').children('a').focus(); $this.focus(); } } } else if (!$this.is('input')) { var keyIndex = [], count, prevKey; $items.each(function () { if (!$(this).hasClass('disabled')) { if ($.trim($(this).children('a').text().toLowerCase()).substring(0, 1) == keyCodeMap[e.keyCode]) { keyIndex.push($(this).index()); } } }); count = $(document).data('keycount'); count++; $(document).data('keycount', count); prevKey = $.trim($(':focus').text().toLowerCase()).substring(0, 1); if (prevKey != keyCodeMap[e.keyCode]) { count = 1; $(document).data('keycount', count); } else if (count >= keyIndex.length) { $(document).data('keycount', 0); if (count > keyIndex.length) count = 1; } $items.eq(keyIndex[count - 1]).children('a').focus(); } // Select focused option if "Enter", "Spacebar" or "Tab" (when selectOnTab is true) are pressed inside the menu. if ((/(13|32)/.test(e.keyCode.toString(10)) || (/(^9$)/.test(e.keyCode.toString(10)) && that.options.selectOnTab)) && isActive) { if (!/(32)/.test(e.keyCode.toString(10))) e.preventDefault(); if (!that.options.liveSearch) { var elem = $(':focus'); elem.click(); // Bring back focus for multiselects elem.focus(); // Prevent screen from scrolling if the user hit the spacebar e.preventDefault(); // Fixes spacebar selection of dropdown items in FF & IE $(document).data('spaceSelect', true); } else if (!/(32)/.test(e.keyCode.toString(10))) { that.$menuInner.find('.active a').click(); $this.focus(); } $(document).data('keycount', 0); } if ((/(^9$|27)/.test(e.keyCode.toString(10)) && isActive && (that.multiple || that.options.liveSearch)) || (/(27)/.test(e.keyCode.toString(10)) && !isActive)) { that.$menu.parent().removeClass('open'); if (that.options.container) that.$newElement.removeClass('open'); that.$button.focus(); } }, mobile: function () { this.$element.addClass('mobile-device'); }, refresh: function () { this.$lis = null; this.liObj = {}; this.reloadLi(); this.render(); this.checkDisabled(); this.liHeight(true); this.setStyle(); this.setWidth(); if (this.$lis) this.$searchbox.trigger('propertychange'); this.$element.trigger('refreshed.bs.select'); }, hide: function () { this.$newElement.hide(); }, show: function () { this.$newElement.show(); }, remove: function () { this.$newElement.remove(); this.$element.remove(); }, destroy: function () { this.$newElement.before(this.$element).remove(); if (this.$bsContainer) { this.$bsContainer.remove(); } else { this.$menu.remove(); } this.$element .off('.bs.select') .removeData('selectpicker') .removeClass('bs-select-hidden selectpicker'); } }; // SELECTPICKER PLUGIN DEFINITION // ============================== function Plugin(option) { // get the args of the outer function.. var args = arguments; // The arguments of the function are explicitly re-defined from the argument list, because the shift causes them // to get lost/corrupted in android 2.3 and IE9 #715 #775 var _option = option; [].shift.apply(args); var value; var chain = this.each(function () { var $this = $(this); if ($this.is('select')) { var data = $this.data('selectpicker'), options = typeof _option == 'object' && _option; if (!data) { var config = $.extend({}, Selectpicker.DEFAULTS, $.fn.selectpicker.defaults || {}, $this.data(), options); config.template = $.extend({}, Selectpicker.DEFAULTS.template, ($.fn.selectpicker.defaults ? $.fn.selectpicker.defaults.template : {}), $this.data().template, options.template); $this.data('selectpicker', (data = new Selectpicker(this, config))); } else if (options) { for (var i in options) { if (options.hasOwnProperty(i)) { data.options[i] = options[i]; } } } if (typeof _option == 'string') { if (data[_option] instanceof Function) { value = data[_option].apply(data, args); } else { value = data.options[_option]; } } } }); if (typeof value !== 'undefined') { //noinspection JSUnusedAssignment return value; } else { return chain; } } var old = $.fn.selectpicker; $.fn.selectpicker = Plugin; $.fn.selectpicker.Constructor = Selectpicker; // SELECTPICKER NO CONFLICT // ======================== $.fn.selectpicker.noConflict = function () { $.fn.selectpicker = old; return this; }; $(document) .data('keycount', 0) .on('keydown.bs.select', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="listbox"], .bs-searchbox input', Selectpicker.prototype.keydown) .on('focusin.modal', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="listbox"], .bs-searchbox input', function (e) { e.stopPropagation(); }); // SELECTPICKER DATA-API // ===================== $(window).on('load.bs.select.data-api', function () { $('.selectpicker').each(function () { var $selectpicker = $(this); Plugin.call($selectpicker, $selectpicker.data()); }) }); })(jQuery); })); ================================================ FILE: app_backend/static/js/bootstrap.js ================================================ /*! * Bootstrap v3.3.5 (http://getbootstrap.com) * Copyright 2011-2015 Twitter, Inc. * Licensed under the MIT license */ if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript requires jQuery') } +function ($) { 'use strict'; var version = $.fn.jquery.split(' ')[0].split('.') if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1)) { throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher') } }(jQuery); /* ======================================================================== * Bootstrap: transition.js v3.3.5 * http://getbootstrap.com/javascript/#transitions * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) // ============================================================ function transitionEnd() { var el = document.createElement('bootstrap') var transEndEventNames = { WebkitTransition : 'webkitTransitionEnd', MozTransition : 'transitionend', OTransition : 'oTransitionEnd otransitionend', transition : 'transitionend' } for (var name in transEndEventNames) { if (el.style[name] !== undefined) { return { end: transEndEventNames[name] } } } return false // explicit for ie8 ( ._.) } // http://blog.alexmaccaw.com/css-transitions $.fn.emulateTransitionEnd = function (duration) { var called = false var $el = this $(this).one('bsTransitionEnd', function () { called = true }) var callback = function () { if (!called) $($el).trigger($.support.transition.end) } setTimeout(callback, duration) return this } $(function () { $.support.transition = transitionEnd() if (!$.support.transition) return $.event.special.bsTransitionEnd = { bindType: $.support.transition.end, delegateType: $.support.transition.end, handle: function (e) { if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) } } }) }(jQuery); /* ======================================================================== * Bootstrap: alert.js v3.3.5 * http://getbootstrap.com/javascript/#alerts * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // ALERT CLASS DEFINITION // ====================== var dismiss = '[data-dismiss="alert"]' var Alert = function (el) { $(el).on('click', dismiss, this.close) } Alert.VERSION = '3.3.5' Alert.TRANSITION_DURATION = 150 Alert.prototype.close = function (e) { var $this = $(this) var selector = $this.attr('data-target') if (!selector) { selector = $this.attr('href') selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } var $parent = $(selector) if (e) e.preventDefault() if (!$parent.length) { $parent = $this.closest('.alert') } $parent.trigger(e = $.Event('close.bs.alert')) if (e.isDefaultPrevented()) return $parent.removeClass('in') function removeElement() { // detach from parent, fire event then clean up data $parent.detach().trigger('closed.bs.alert').remove() } $.support.transition && $parent.hasClass('fade') ? $parent .one('bsTransitionEnd', removeElement) .emulateTransitionEnd(Alert.TRANSITION_DURATION) : removeElement() } // ALERT PLUGIN DEFINITION // ======================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.alert') if (!data) $this.data('bs.alert', (data = new Alert(this))) if (typeof option == 'string') data[option].call($this) }) } var old = $.fn.alert $.fn.alert = Plugin $.fn.alert.Constructor = Alert // ALERT NO CONFLICT // ================= $.fn.alert.noConflict = function () { $.fn.alert = old return this } // ALERT DATA-API // ============== $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) }(jQuery); /* ======================================================================== * Bootstrap: button.js v3.3.5 * http://getbootstrap.com/javascript/#buttons * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // BUTTON PUBLIC CLASS DEFINITION // ============================== var Button = function (element, options) { this.$element = $(element) this.options = $.extend({}, Button.DEFAULTS, options) this.isLoading = false } Button.VERSION = '3.3.5' Button.DEFAULTS = { loadingText: 'loading...' } Button.prototype.setState = function (state) { var d = 'disabled' var $el = this.$element var val = $el.is('input') ? 'val' : 'html' var data = $el.data() state += 'Text' if (data.resetText == null) $el.data('resetText', $el[val]()) // push to event loop to allow forms to submit setTimeout($.proxy(function () { $el[val](data[state] == null ? this.options[state] : data[state]) if (state == 'loadingText') { this.isLoading = true $el.addClass(d).attr(d, d) } else if (this.isLoading) { this.isLoading = false $el.removeClass(d).removeAttr(d) } }, this), 0) } Button.prototype.toggle = function () { var changed = true var $parent = this.$element.closest('[data-toggle="buttons"]') if ($parent.length) { var $input = this.$element.find('input') if ($input.prop('type') == 'radio') { if ($input.prop('checked')) changed = false $parent.find('.active').removeClass('active') this.$element.addClass('active') } else if ($input.prop('type') == 'checkbox') { if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false this.$element.toggleClass('active') } $input.prop('checked', this.$element.hasClass('active')) if (changed) $input.trigger('change') } else { this.$element.attr('aria-pressed', !this.$element.hasClass('active')) this.$element.toggleClass('active') } } // BUTTON PLUGIN DEFINITION // ======================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.button') var options = typeof option == 'object' && option if (!data) $this.data('bs.button', (data = new Button(this, options))) if (option == 'toggle') data.toggle() else if (option) data.setState(option) }) } var old = $.fn.button $.fn.button = Plugin $.fn.button.Constructor = Button // BUTTON NO CONFLICT // ================== $.fn.button.noConflict = function () { $.fn.button = old return this } // BUTTON DATA-API // =============== $(document) .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { var $btn = $(e.target) if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') Plugin.call($btn, 'toggle') if (!($(e.target).is('input[type="radio"]') || $(e.target).is('input[type="checkbox"]'))) e.preventDefault() }) .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) }) }(jQuery); /* ======================================================================== * Bootstrap: carousel.js v3.3.5 * http://getbootstrap.com/javascript/#carousel * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // CAROUSEL CLASS DEFINITION // ========================= var Carousel = function (element, options) { this.$element = $(element) this.$indicators = this.$element.find('.carousel-indicators') this.options = options this.paused = null this.sliding = null this.interval = null this.$active = null this.$items = null this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) } Carousel.VERSION = '3.3.5' Carousel.TRANSITION_DURATION = 600 Carousel.DEFAULTS = { interval: 5000, pause: 'hover', wrap: true, keyboard: true } Carousel.prototype.keydown = function (e) { if (/input|textarea/i.test(e.target.tagName)) return switch (e.which) { case 37: this.prev(); break case 39: this.next(); break default: return } e.preventDefault() } Carousel.prototype.cycle = function (e) { e || (this.paused = false) this.interval && clearInterval(this.interval) this.options.interval && !this.paused && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) return this } Carousel.prototype.getItemIndex = function (item) { this.$items = item.parent().children('.item') return this.$items.index(item || this.$active) } Carousel.prototype.getItemForDirection = function (direction, active) { var activeIndex = this.getItemIndex(active) var willWrap = (direction == 'prev' && activeIndex === 0) || (direction == 'next' && activeIndex == (this.$items.length - 1)) if (willWrap && !this.options.wrap) return active var delta = direction == 'prev' ? -1 : 1 var itemIndex = (activeIndex + delta) % this.$items.length return this.$items.eq(itemIndex) } Carousel.prototype.to = function (pos) { var that = this var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) if (pos > (this.$items.length - 1) || pos < 0) return if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" if (activeIndex == pos) return this.pause().cycle() return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) } Carousel.prototype.pause = function (e) { e || (this.paused = true) if (this.$element.find('.next, .prev').length && $.support.transition) { this.$element.trigger($.support.transition.end) this.cycle(true) } this.interval = clearInterval(this.interval) return this } Carousel.prototype.next = function () { if (this.sliding) return return this.slide('next') } Carousel.prototype.prev = function () { if (this.sliding) return return this.slide('prev') } Carousel.prototype.slide = function (type, next) { var $active = this.$element.find('.item.active') var $next = next || this.getItemForDirection(type, $active) var isCycling = this.interval var direction = type == 'next' ? 'left' : 'right' var that = this if ($next.hasClass('active')) return (this.sliding = false) var relatedTarget = $next[0] var slideEvent = $.Event('slide.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) this.$element.trigger(slideEvent) if (slideEvent.isDefaultPrevented()) return this.sliding = true isCycling && this.pause() if (this.$indicators.length) { this.$indicators.find('.active').removeClass('active') var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) $nextIndicator && $nextIndicator.addClass('active') } var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" if ($.support.transition && this.$element.hasClass('slide')) { $next.addClass(type) $next[0].offsetWidth // force reflow $active.addClass(direction) $next.addClass(direction) $active .one('bsTransitionEnd', function () { $next.removeClass([type, direction].join(' ')).addClass('active') $active.removeClass(['active', direction].join(' ')) that.sliding = false setTimeout(function () { that.$element.trigger(slidEvent) }, 0) }) .emulateTransitionEnd(Carousel.TRANSITION_DURATION) } else { $active.removeClass('active') $next.addClass('active') this.sliding = false this.$element.trigger(slidEvent) } isCycling && this.cycle() return this } // CAROUSEL PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.carousel') var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) var action = typeof option == 'string' ? option : options.slide if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) if (typeof option == 'number') data.to(option) else if (action) data[action]() else if (options.interval) data.pause().cycle() }) } var old = $.fn.carousel $.fn.carousel = Plugin $.fn.carousel.Constructor = Carousel // CAROUSEL NO CONFLICT // ==================== $.fn.carousel.noConflict = function () { $.fn.carousel = old return this } // CAROUSEL DATA-API // ================= var clickHandler = function (e) { var href var $this = $(this) var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 if (!$target.hasClass('carousel')) return var options = $.extend({}, $target.data(), $this.data()) var slideIndex = $this.attr('data-slide-to') if (slideIndex) options.interval = false Plugin.call($target, options) if (slideIndex) { $target.data('bs.carousel').to(slideIndex) } e.preventDefault() } $(document) .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) $(window).on('load', function () { $('[data-ride="carousel"]').each(function () { var $carousel = $(this) Plugin.call($carousel, $carousel.data()) }) }) }(jQuery); /* ======================================================================== * Bootstrap: collapse.js v3.3.5 * http://getbootstrap.com/javascript/#collapse * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // COLLAPSE PUBLIC CLASS DEFINITION // ================================ var Collapse = function (element, options) { this.$element = $(element) this.options = $.extend({}, Collapse.DEFAULTS, options) this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' + '[data-toggle="collapse"][data-target="#' + element.id + '"]') this.transitioning = null if (this.options.parent) { this.$parent = this.getParent() } else { this.addAriaAndCollapsedClass(this.$element, this.$trigger) } if (this.options.toggle) this.toggle() } Collapse.VERSION = '3.3.5' Collapse.TRANSITION_DURATION = 350 Collapse.DEFAULTS = { toggle: true } Collapse.prototype.dimension = function () { var hasWidth = this.$element.hasClass('width') return hasWidth ? 'width' : 'height' } Collapse.prototype.show = function () { if (this.transitioning || this.$element.hasClass('in')) return var activesData var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') if (actives && actives.length) { activesData = actives.data('bs.collapse') if (activesData && activesData.transitioning) return } var startEvent = $.Event('show.bs.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) return if (actives && actives.length) { Plugin.call(actives, 'hide') activesData || actives.data('bs.collapse', null) } var dimension = this.dimension() this.$element .removeClass('collapse') .addClass('collapsing')[dimension](0) .attr('aria-expanded', true) this.$trigger .removeClass('collapsed') .attr('aria-expanded', true) this.transitioning = 1 var complete = function () { this.$element .removeClass('collapsing') .addClass('collapse in')[dimension]('') this.transitioning = 0 this.$element .trigger('shown.bs.collapse') } if (!$.support.transition) return complete.call(this) var scrollSize = $.camelCase(['scroll', dimension].join('-')) this.$element .one('bsTransitionEnd', $.proxy(complete, this)) .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) } Collapse.prototype.hide = function () { if (this.transitioning || !this.$element.hasClass('in')) return var startEvent = $.Event('hide.bs.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) return var dimension = this.dimension() this.$element[dimension](this.$element[dimension]())[0].offsetHeight this.$element .addClass('collapsing') .removeClass('collapse in') .attr('aria-expanded', false) this.$trigger .addClass('collapsed') .attr('aria-expanded', false) this.transitioning = 1 var complete = function () { this.transitioning = 0 this.$element .removeClass('collapsing') .addClass('collapse') .trigger('hidden.bs.collapse') } if (!$.support.transition) return complete.call(this) this.$element [dimension](0) .one('bsTransitionEnd', $.proxy(complete, this)) .emulateTransitionEnd(Collapse.TRANSITION_DURATION) } Collapse.prototype.toggle = function () { this[this.$element.hasClass('in') ? 'hide' : 'show']() } Collapse.prototype.getParent = function () { return $(this.options.parent) .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') .each($.proxy(function (i, element) { var $element = $(element) this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) }, this)) .end() } Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { var isOpen = $element.hasClass('in') $element.attr('aria-expanded', isOpen) $trigger .toggleClass('collapsed', !isOpen) .attr('aria-expanded', isOpen) } function getTargetFromTrigger($trigger) { var href var target = $trigger.attr('data-target') || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 return $(target) } // COLLAPSE PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.collapse') var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.collapse $.fn.collapse = Plugin $.fn.collapse.Constructor = Collapse // COLLAPSE NO CONFLICT // ==================== $.fn.collapse.noConflict = function () { $.fn.collapse = old return this } // COLLAPSE DATA-API // ================= $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { var $this = $(this) if (!$this.attr('data-target')) e.preventDefault() var $target = getTargetFromTrigger($this) var data = $target.data('bs.collapse') var option = data ? 'toggle' : $this.data() Plugin.call($target, option) }) }(jQuery); /* ======================================================================== * Bootstrap: dropdown.js v3.3.5 * http://getbootstrap.com/javascript/#dropdowns * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // DROPDOWN CLASS DEFINITION // ========================= var backdrop = '.dropdown-backdrop' var toggle = '[data-toggle="dropdown"]' var Dropdown = function (element) { $(element).on('click.bs.dropdown', this.toggle) } Dropdown.VERSION = '3.3.5' function getParent($this) { var selector = $this.attr('data-target') if (!selector) { selector = $this.attr('href') selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } var $parent = selector && $(selector) return $parent && $parent.length ? $parent : $this.parent() } function clearMenus(e) { if (e && e.which === 3) return $(backdrop).remove() $(toggle).each(function () { var $this = $(this) var $parent = getParent($this) var relatedTarget = { relatedTarget: this } if (!$parent.hasClass('open')) return if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) if (e.isDefaultPrevented()) return $this.attr('aria-expanded', 'false') $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget) }) } Dropdown.prototype.toggle = function (e) { var $this = $(this) if ($this.is('.disabled, :disabled')) return var $parent = getParent($this) var isActive = $parent.hasClass('open') clearMenus() if (!isActive) { if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { // if mobile we use a backdrop because click events don't delegate $(document.createElement('div')) .addClass('dropdown-backdrop') .insertAfter($(this)) .on('click', clearMenus) } var relatedTarget = { relatedTarget: this } $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget)) if (e.isDefaultPrevented()) return $this .trigger('focus') .attr('aria-expanded', 'true') $parent .toggleClass('open') .trigger('shown.bs.dropdown', relatedTarget) } return false } Dropdown.prototype.keydown = function (e) { if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return var $this = $(this) e.preventDefault() e.stopPropagation() if ($this.is('.disabled, :disabled')) return var $parent = getParent($this) var isActive = $parent.hasClass('open') if (!isActive && e.which != 27 || isActive && e.which == 27) { if (e.which == 27) $parent.find(toggle).trigger('focus') return $this.trigger('click') } var desc = ' li:not(.disabled):visible a' var $items = $parent.find('.dropdown-menu' + desc) if (!$items.length) return var index = $items.index(e.target) if (e.which == 38 && index > 0) index-- // up if (e.which == 40 && index < $items.length - 1) index++ // down if (!~index) index = 0 $items.eq(index).trigger('focus') } // DROPDOWN PLUGIN DEFINITION // ========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.dropdown') if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))) if (typeof option == 'string') data[option].call($this) }) } var old = $.fn.dropdown $.fn.dropdown = Plugin $.fn.dropdown.Constructor = Dropdown // DROPDOWN NO CONFLICT // ==================== $.fn.dropdown.noConflict = function () { $.fn.dropdown = old return this } // APPLY TO STANDARD DROPDOWN ELEMENTS // =================================== $(document) .on('click.bs.dropdown.data-api', clearMenus) .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown) .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown) }(jQuery); /* ======================================================================== * Bootstrap: modal.js v3.3.5 * http://getbootstrap.com/javascript/#modals * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // MODAL CLASS DEFINITION // ====================== var Modal = function (element, options) { this.options = options this.$body = $(document.body) this.$element = $(element) this.$dialog = this.$element.find('.modal-dialog') this.$backdrop = null this.isShown = null this.originalBodyPad = null this.scrollbarWidth = 0 this.ignoreBackdropClick = false if (this.options.remote) { this.$element .find('.modal-content') .load(this.options.remote, $.proxy(function () { this.$element.trigger('loaded.bs.modal') }, this)) } } Modal.VERSION = '3.3.5' Modal.TRANSITION_DURATION = 300 Modal.BACKDROP_TRANSITION_DURATION = 150 Modal.DEFAULTS = { backdrop: true, keyboard: true, show: true } Modal.prototype.toggle = function (_relatedTarget) { return this.isShown ? this.hide() : this.show(_relatedTarget) } Modal.prototype.show = function (_relatedTarget) { var that = this var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }) this.$element.trigger(e) if (this.isShown || e.isDefaultPrevented()) return this.isShown = true this.checkScrollbar() this.setScrollbar() this.$body.addClass('modal-open') this.escape() this.resize() this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) this.$dialog.on('mousedown.dismiss.bs.modal', function () { that.$element.one('mouseup.dismiss.bs.modal', function (e) { if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true }) }) this.backdrop(function () { var transition = $.support.transition && that.$element.hasClass('fade') if (!that.$element.parent().length) { that.$element.appendTo(that.$body) // don't move modals dom position } that.$element .show() .scrollTop(0) that.adjustDialog() if (transition) { that.$element[0].offsetWidth // force reflow } that.$element.addClass('in') that.enforceFocus() var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) transition ? that.$dialog // wait for modal to slide in .one('bsTransitionEnd', function () { that.$element.trigger('focus').trigger(e) }) .emulateTransitionEnd(Modal.TRANSITION_DURATION) : that.$element.trigger('focus').trigger(e) }) } Modal.prototype.hide = function (e) { if (e) e.preventDefault() e = $.Event('hide.bs.modal') this.$element.trigger(e) if (!this.isShown || e.isDefaultPrevented()) return this.isShown = false this.escape() this.resize() $(document).off('focusin.bs.modal') this.$element .removeClass('in') .off('click.dismiss.bs.modal') .off('mouseup.dismiss.bs.modal') this.$dialog.off('mousedown.dismiss.bs.modal') $.support.transition && this.$element.hasClass('fade') ? this.$element .one('bsTransitionEnd', $.proxy(this.hideModal, this)) .emulateTransitionEnd(Modal.TRANSITION_DURATION) : this.hideModal() } Modal.prototype.enforceFocus = function () { $(document) .off('focusin.bs.modal') // guard against infinite focus loop .on('focusin.bs.modal', $.proxy(function (e) { if (this.$element[0] !== e.target && !this.$element.has(e.target).length) { this.$element.trigger('focus') } }, this)) } Modal.prototype.escape = function () { if (this.isShown && this.options.keyboard) { this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { e.which == 27 && this.hide() }, this)) } else if (!this.isShown) { this.$element.off('keydown.dismiss.bs.modal') } } Modal.prototype.resize = function () { if (this.isShown) { $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this)) } else { $(window).off('resize.bs.modal') } } Modal.prototype.hideModal = function () { var that = this this.$element.hide() this.backdrop(function () { that.$body.removeClass('modal-open') that.resetAdjustments() that.resetScrollbar() that.$element.trigger('hidden.bs.modal') }) } Modal.prototype.removeBackdrop = function () { this.$backdrop && this.$backdrop.remove() this.$backdrop = null } Modal.prototype.backdrop = function (callback) { var that = this var animate = this.$element.hasClass('fade') ? 'fade' : '' if (this.isShown && this.options.backdrop) { var doAnimate = $.support.transition && animate this.$backdrop = $(document.createElement('div')) .addClass('modal-backdrop ' + animate) .appendTo(this.$body) this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { if (this.ignoreBackdropClick) { this.ignoreBackdropClick = false return } if (e.target !== e.currentTarget) return this.options.backdrop == 'static' ? this.$element[0].focus() : this.hide() }, this)) if (doAnimate) this.$backdrop[0].offsetWidth // force reflow this.$backdrop.addClass('in') if (!callback) return doAnimate ? this.$backdrop .one('bsTransitionEnd', callback) .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : callback() } else if (!this.isShown && this.$backdrop) { this.$backdrop.removeClass('in') var callbackRemove = function () { that.removeBackdrop() callback && callback() } $.support.transition && this.$element.hasClass('fade') ? this.$backdrop .one('bsTransitionEnd', callbackRemove) .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : callbackRemove() } else if (callback) { callback() } } // these following methods are used to handle overflowing modals Modal.prototype.handleUpdate = function () { this.adjustDialog() } Modal.prototype.adjustDialog = function () { var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight this.$element.css({ paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' }) } Modal.prototype.resetAdjustments = function () { this.$element.css({ paddingLeft: '', paddingRight: '' }) } Modal.prototype.checkScrollbar = function () { var fullWindowWidth = window.innerWidth if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8 var documentElementRect = document.documentElement.getBoundingClientRect() fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left) } this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth this.scrollbarWidth = this.measureScrollbar() } Modal.prototype.setScrollbar = function () { var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) this.originalBodyPad = document.body.style.paddingRight || '' if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) } Modal.prototype.resetScrollbar = function () { this.$body.css('padding-right', this.originalBodyPad) } Modal.prototype.measureScrollbar = function () { // thx walsh var scrollDiv = document.createElement('div') scrollDiv.className = 'modal-scrollbar-measure' this.$body.append(scrollDiv) var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth this.$body[0].removeChild(scrollDiv) return scrollbarWidth } // MODAL PLUGIN DEFINITION // ======================= function Plugin(option, _relatedTarget) { return this.each(function () { var $this = $(this) var data = $this.data('bs.modal') var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) if (!data) $this.data('bs.modal', (data = new Modal(this, options))) if (typeof option == 'string') data[option](_relatedTarget) else if (options.show) data.show(_relatedTarget) }) } var old = $.fn.modal $.fn.modal = Plugin $.fn.modal.Constructor = Modal // MODAL NO CONFLICT // ================= $.fn.modal.noConflict = function () { $.fn.modal = old return this } // MODAL DATA-API // ============== $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { var $this = $(this) var href = $this.attr('href') var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7 var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) if ($this.is('a')) e.preventDefault() $target.one('show.bs.modal', function (showEvent) { if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown $target.one('hidden.bs.modal', function () { $this.is(':visible') && $this.trigger('focus') }) }) Plugin.call($target, option, this) }) }(jQuery); /* ======================================================================== * Bootstrap: tooltip.js v3.3.5 * http://getbootstrap.com/javascript/#tooltip * Inspired by the original jQuery.tipsy by Jason Frame * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // TOOLTIP PUBLIC CLASS DEFINITION // =============================== var Tooltip = function (element, options) { this.type = null this.options = null this.enabled = null this.timeout = null this.hoverState = null this.$element = null this.inState = null this.init('tooltip', element, options) } Tooltip.VERSION = '3.3.5' Tooltip.TRANSITION_DURATION = 150 Tooltip.DEFAULTS = { animation: true, placement: 'top', selector: false, template: '', trigger: 'hover focus', title: '', delay: 0, html: false, container: false, viewport: { selector: 'body', padding: 0 } } Tooltip.prototype.init = function (type, element, options) { this.enabled = true this.type = type this.$element = $(element) this.options = this.getOptions(options) this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport)) this.inState = { click: false, hover: false, focus: false } if (this.$element[0] instanceof document.constructor && !this.options.selector) { throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!') } var triggers = this.options.trigger.split(' ') for (var i = triggers.length; i--;) { var trigger = triggers[i] if (trigger == 'click') { this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) } else if (trigger != 'manual') { var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) } } this.options.selector ? (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : this.fixTitle() } Tooltip.prototype.getDefaults = function () { return Tooltip.DEFAULTS } Tooltip.prototype.getOptions = function (options) { options = $.extend({}, this.getDefaults(), this.$element.data(), options) if (options.delay && typeof options.delay == 'number') { options.delay = { show: options.delay, hide: options.delay } } return options } Tooltip.prototype.getDelegateOptions = function () { var options = {} var defaults = this.getDefaults() this._options && $.each(this._options, function (key, value) { if (defaults[key] != value) options[key] = value }) return options } Tooltip.prototype.enter = function (obj) { var self = obj instanceof this.constructor ? obj : $(obj.currentTarget).data('bs.' + this.type) if (!self) { self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) $(obj.currentTarget).data('bs.' + this.type, self) } if (obj instanceof $.Event) { self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true } if (self.tip().hasClass('in') || self.hoverState == 'in') { self.hoverState = 'in' return } clearTimeout(self.timeout) self.hoverState = 'in' if (!self.options.delay || !self.options.delay.show) return self.show() self.timeout = setTimeout(function () { if (self.hoverState == 'in') self.show() }, self.options.delay.show) } Tooltip.prototype.isInStateTrue = function () { for (var key in this.inState) { if (this.inState[key]) return true } return false } Tooltip.prototype.leave = function (obj) { var self = obj instanceof this.constructor ? obj : $(obj.currentTarget).data('bs.' + this.type) if (!self) { self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) $(obj.currentTarget).data('bs.' + this.type, self) } if (obj instanceof $.Event) { self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false } if (self.isInStateTrue()) return clearTimeout(self.timeout) self.hoverState = 'out' if (!self.options.delay || !self.options.delay.hide) return self.hide() self.timeout = setTimeout(function () { if (self.hoverState == 'out') self.hide() }, self.options.delay.hide) } Tooltip.prototype.show = function () { var e = $.Event('show.bs.' + this.type) if (this.hasContent() && this.enabled) { this.$element.trigger(e) var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]) if (e.isDefaultPrevented() || !inDom) return var that = this var $tip = this.tip() var tipId = this.getUID(this.type) this.setContent() $tip.attr('id', tipId) this.$element.attr('aria-describedby', tipId) if (this.options.animation) $tip.addClass('fade') var placement = typeof this.options.placement == 'function' ? this.options.placement.call(this, $tip[0], this.$element[0]) : this.options.placement var autoToken = /\s?auto?\s?/i var autoPlace = autoToken.test(placement) if (autoPlace) placement = placement.replace(autoToken, '') || 'top' $tip .detach() .css({ top: 0, left: 0, display: 'block' }) .addClass(placement) .data('bs.' + this.type, this) this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) this.$element.trigger('inserted.bs.' + this.type) var pos = this.getPosition() var actualWidth = $tip[0].offsetWidth var actualHeight = $tip[0].offsetHeight if (autoPlace) { var orgPlacement = placement var viewportDim = this.getPosition(this.$viewport) placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' : placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' : placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' : placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' : placement $tip .removeClass(orgPlacement) .addClass(placement) } var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) this.applyPlacement(calculatedOffset, placement) var complete = function () { var prevHoverState = that.hoverState that.$element.trigger('shown.bs.' + that.type) that.hoverState = null if (prevHoverState == 'out') that.leave(that) } $.support.transition && this.$tip.hasClass('fade') ? $tip .one('bsTransitionEnd', complete) .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : complete() } } Tooltip.prototype.applyPlacement = function (offset, placement) { var $tip = this.tip() var width = $tip[0].offsetWidth var height = $tip[0].offsetHeight // manually read margins because getBoundingClientRect includes difference var marginTop = parseInt($tip.css('margin-top'), 10) var marginLeft = parseInt($tip.css('margin-left'), 10) // we must check for NaN for ie 8/9 if (isNaN(marginTop)) marginTop = 0 if (isNaN(marginLeft)) marginLeft = 0 offset.top += marginTop offset.left += marginLeft // $.fn.offset doesn't round pixel values // so we use setOffset directly with our own function B-0 $.offset.setOffset($tip[0], $.extend({ using: function (props) { $tip.css({ top: Math.round(props.top), left: Math.round(props.left) }) } }, offset), 0) $tip.addClass('in') // check to see if placing tip in new offset caused the tip to resize itself var actualWidth = $tip[0].offsetWidth var actualHeight = $tip[0].offsetHeight if (placement == 'top' && actualHeight != height) { offset.top = offset.top + height - actualHeight } var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight) if (delta.left) offset.left += delta.left else offset.top += delta.top var isVertical = /top|bottom/.test(placement) var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' $tip.offset(offset) this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) } Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) { this.arrow() .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') .css(isVertical ? 'top' : 'left', '') } Tooltip.prototype.setContent = function () { var $tip = this.tip() var title = this.getTitle() $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) $tip.removeClass('fade in top bottom left right') } Tooltip.prototype.hide = function (callback) { var that = this var $tip = $(this.$tip) var e = $.Event('hide.bs.' + this.type) function complete() { if (that.hoverState != 'in') $tip.detach() that.$element .removeAttr('aria-describedby') .trigger('hidden.bs.' + that.type) callback && callback() } this.$element.trigger(e) if (e.isDefaultPrevented()) return $tip.removeClass('in') $.support.transition && $tip.hasClass('fade') ? $tip .one('bsTransitionEnd', complete) .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : complete() this.hoverState = null return this } Tooltip.prototype.fixTitle = function () { var $e = this.$element if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') { $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') } } Tooltip.prototype.hasContent = function () { return this.getTitle() } Tooltip.prototype.getPosition = function ($element) { $element = $element || this.$element var el = $element[0] var isBody = el.tagName == 'BODY' var elRect = el.getBoundingClientRect() if (elRect.width == null) { // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) } var elOffset = isBody ? { top: 0, left: 0 } : $element.offset() var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() } var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null return $.extend({}, elRect, scroll, outerDims, elOffset) } Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } } Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) { var delta = { top: 0, left: 0 } if (!this.$viewport) return delta var viewportPadding = this.options.viewport && this.options.viewport.padding || 0 var viewportDimensions = this.getPosition(this.$viewport) if (/right|left/.test(placement)) { var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight if (topEdgeOffset < viewportDimensions.top) { // top overflow delta.top = viewportDimensions.top - topEdgeOffset } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset } } else { var leftEdgeOffset = pos.left - viewportPadding var rightEdgeOffset = pos.left + viewportPadding + actualWidth if (leftEdgeOffset < viewportDimensions.left) { // left overflow delta.left = viewportDimensions.left - leftEdgeOffset } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset } } return delta } Tooltip.prototype.getTitle = function () { var title var $e = this.$element var o = this.options title = $e.attr('data-original-title') || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) return title } Tooltip.prototype.getUID = function (prefix) { do prefix += ~~(Math.random() * 1000000) while (document.getElementById(prefix)) return prefix } Tooltip.prototype.tip = function () { if (!this.$tip) { this.$tip = $(this.options.template) if (this.$tip.length != 1) { throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!') } } return this.$tip } Tooltip.prototype.arrow = function () { return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) } Tooltip.prototype.enable = function () { this.enabled = true } Tooltip.prototype.disable = function () { this.enabled = false } Tooltip.prototype.toggleEnabled = function () { this.enabled = !this.enabled } Tooltip.prototype.toggle = function (e) { var self = this if (e) { self = $(e.currentTarget).data('bs.' + this.type) if (!self) { self = new this.constructor(e.currentTarget, this.getDelegateOptions()) $(e.currentTarget).data('bs.' + this.type, self) } } if (e) { self.inState.click = !self.inState.click if (self.isInStateTrue()) self.enter(self) else self.leave(self) } else { self.tip().hasClass('in') ? self.leave(self) : self.enter(self) } } Tooltip.prototype.destroy = function () { var that = this clearTimeout(this.timeout) this.hide(function () { that.$element.off('.' + that.type).removeData('bs.' + that.type) if (that.$tip) { that.$tip.detach() } that.$tip = null that.$arrow = null that.$viewport = null }) } // TOOLTIP PLUGIN DEFINITION // ========================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.tooltip') var options = typeof option == 'object' && option if (!data && /destroy|hide/.test(option)) return if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.tooltip $.fn.tooltip = Plugin $.fn.tooltip.Constructor = Tooltip // TOOLTIP NO CONFLICT // =================== $.fn.tooltip.noConflict = function () { $.fn.tooltip = old return this } }(jQuery); /* ======================================================================== * Bootstrap: popover.js v3.3.5 * http://getbootstrap.com/javascript/#popovers * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // POPOVER PUBLIC CLASS DEFINITION // =============================== var Popover = function (element, options) { this.init('popover', element, options) } if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') Popover.VERSION = '3.3.5' Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { placement: 'right', trigger: 'click', content: '', template: '' }) // NOTE: POPOVER EXTENDS tooltip.js // ================================ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype) Popover.prototype.constructor = Popover Popover.prototype.getDefaults = function () { return Popover.DEFAULTS } Popover.prototype.setContent = function () { var $tip = this.tip() var title = this.getTitle() var content = this.getContent() $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text' ](content) $tip.removeClass('fade top bottom left right in') // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do // this manually by checking the contents. if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide() } Popover.prototype.hasContent = function () { return this.getTitle() || this.getContent() } Popover.prototype.getContent = function () { var $e = this.$element var o = this.options return $e.attr('data-content') || (typeof o.content == 'function' ? o.content.call($e[0]) : o.content) } Popover.prototype.arrow = function () { return (this.$arrow = this.$arrow || this.tip().find('.arrow')) } // POPOVER PLUGIN DEFINITION // ========================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.popover') var options = typeof option == 'object' && option if (!data && /destroy|hide/.test(option)) return if (!data) $this.data('bs.popover', (data = new Popover(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.popover $.fn.popover = Plugin $.fn.popover.Constructor = Popover // POPOVER NO CONFLICT // =================== $.fn.popover.noConflict = function () { $.fn.popover = old return this } }(jQuery); /* ======================================================================== * Bootstrap: scrollspy.js v3.3.5 * http://getbootstrap.com/javascript/#scrollspy * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // SCROLLSPY CLASS DEFINITION // ========================== function ScrollSpy(element, options) { this.$body = $(document.body) this.$scrollElement = $(element).is(document.body) ? $(window) : $(element) this.options = $.extend({}, ScrollSpy.DEFAULTS, options) this.selector = (this.options.target || '') + ' .nav li > a' this.offsets = [] this.targets = [] this.activeTarget = null this.scrollHeight = 0 this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this)) this.refresh() this.process() } ScrollSpy.VERSION = '3.3.5' ScrollSpy.DEFAULTS = { offset: 10 } ScrollSpy.prototype.getScrollHeight = function () { return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight) } ScrollSpy.prototype.refresh = function () { var that = this var offsetMethod = 'offset' var offsetBase = 0 this.offsets = [] this.targets = [] this.scrollHeight = this.getScrollHeight() if (!$.isWindow(this.$scrollElement[0])) { offsetMethod = 'position' offsetBase = this.$scrollElement.scrollTop() } this.$body .find(this.selector) .map(function () { var $el = $(this) var href = $el.data('target') || $el.attr('href') var $href = /^#./.test(href) && $(href) return ($href && $href.length && $href.is(':visible') && [[$href[offsetMethod]().top + offsetBase, href]]) || null }) .sort(function (a, b) { return a[0] - b[0] }) .each(function () { that.offsets.push(this[0]) that.targets.push(this[1]) }) } ScrollSpy.prototype.process = function () { var scrollTop = this.$scrollElement.scrollTop() + this.options.offset var scrollHeight = this.getScrollHeight() var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height() var offsets = this.offsets var targets = this.targets var activeTarget = this.activeTarget var i if (this.scrollHeight != scrollHeight) { this.refresh() } if (scrollTop >= maxScroll) { return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) } if (activeTarget && scrollTop < offsets[0]) { this.activeTarget = null return this.clear() } for (i = offsets.length; i--;) { activeTarget != targets[i] && scrollTop >= offsets[i] && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1]) && this.activate(targets[i]) } } ScrollSpy.prototype.activate = function (target) { this.activeTarget = target this.clear() var selector = this.selector + '[data-target="' + target + '"],' + this.selector + '[href="' + target + '"]' var active = $(selector) .parents('li') .addClass('active') if (active.parent('.dropdown-menu').length) { active = active .closest('li.dropdown') .addClass('active') } active.trigger('activate.bs.scrollspy') } ScrollSpy.prototype.clear = function () { $(this.selector) .parentsUntil(this.options.target, '.active') .removeClass('active') } // SCROLLSPY PLUGIN DEFINITION // =========================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.scrollspy') var options = typeof option == 'object' && option if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.scrollspy $.fn.scrollspy = Plugin $.fn.scrollspy.Constructor = ScrollSpy // SCROLLSPY NO CONFLICT // ===================== $.fn.scrollspy.noConflict = function () { $.fn.scrollspy = old return this } // SCROLLSPY DATA-API // ================== $(window).on('load.bs.scrollspy.data-api', function () { $('[data-spy="scroll"]').each(function () { var $spy = $(this) Plugin.call($spy, $spy.data()) }) }) }(jQuery); /* ======================================================================== * Bootstrap: tab.js v3.3.5 * http://getbootstrap.com/javascript/#tabs * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // TAB CLASS DEFINITION // ==================== var Tab = function (element) { // jscs:disable requireDollarBeforejQueryAssignment this.element = $(element) // jscs:enable requireDollarBeforejQueryAssignment } Tab.VERSION = '3.3.5' Tab.TRANSITION_DURATION = 150 Tab.prototype.show = function () { var $this = this.element var $ul = $this.closest('ul:not(.dropdown-menu)') var selector = $this.data('target') if (!selector) { selector = $this.attr('href') selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 } if ($this.parent('li').hasClass('active')) return var $previous = $ul.find('.active:last a') var hideEvent = $.Event('hide.bs.tab', { relatedTarget: $this[0] }) var showEvent = $.Event('show.bs.tab', { relatedTarget: $previous[0] }) $previous.trigger(hideEvent) $this.trigger(showEvent) if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return var $target = $(selector) this.activate($this.closest('li'), $ul) this.activate($target, $target.parent(), function () { $previous.trigger({ type: 'hidden.bs.tab', relatedTarget: $this[0] }) $this.trigger({ type: 'shown.bs.tab', relatedTarget: $previous[0] }) }) } Tab.prototype.activate = function (element, container, callback) { var $active = container.find('> .active') var transition = callback && $.support.transition && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length) function next() { $active .removeClass('active') .find('> .dropdown-menu > .active') .removeClass('active') .end() .find('[data-toggle="tab"]') .attr('aria-expanded', false) element .addClass('active') .find('[data-toggle="tab"]') .attr('aria-expanded', true) if (transition) { element[0].offsetWidth // reflow for transition element.addClass('in') } else { element.removeClass('fade') } if (element.parent('.dropdown-menu').length) { element .closest('li.dropdown') .addClass('active') .end() .find('[data-toggle="tab"]') .attr('aria-expanded', true) } callback && callback() } $active.length && transition ? $active .one('bsTransitionEnd', next) .emulateTransitionEnd(Tab.TRANSITION_DURATION) : next() $active.removeClass('in') } // TAB PLUGIN DEFINITION // ===================== function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.tab') if (!data) $this.data('bs.tab', (data = new Tab(this))) if (typeof option == 'string') data[option]() }) } var old = $.fn.tab $.fn.tab = Plugin $.fn.tab.Constructor = Tab // TAB NO CONFLICT // =============== $.fn.tab.noConflict = function () { $.fn.tab = old return this } // TAB DATA-API // ============ var clickHandler = function (e) { e.preventDefault() Plugin.call($(this), 'show') } $(document) .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler) .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler) }(jQuery); /* ======================================================================== * Bootstrap: affix.js v3.3.5 * http://getbootstrap.com/javascript/#affix * ======================================================================== * Copyright 2011-2015 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * ======================================================================== */ +function ($) { 'use strict'; // AFFIX CLASS DEFINITION // ====================== var Affix = function (element, options) { this.options = $.extend({}, Affix.DEFAULTS, options) this.$target = $(this.options.target) .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) this.$element = $(element) this.affixed = null this.unpin = null this.pinnedOffset = null this.checkPosition() } Affix.VERSION = '3.3.5' Affix.RESET = 'affix affix-top affix-bottom' Affix.DEFAULTS = { offset: 0, target: window } Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) { var scrollTop = this.$target.scrollTop() var position = this.$element.offset() var targetHeight = this.$target.height() if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false if (this.affixed == 'bottom') { if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom' return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom' } var initializing = this.affixed == null var colliderTop = initializing ? scrollTop : position.top var colliderHeight = initializing ? targetHeight : height if (offsetTop != null && scrollTop <= offsetTop) return 'top' if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom' return false } Affix.prototype.getPinnedOffset = function () { if (this.pinnedOffset) return this.pinnedOffset this.$element.removeClass(Affix.RESET).addClass('affix') var scrollTop = this.$target.scrollTop() var position = this.$element.offset() return (this.pinnedOffset = position.top - scrollTop) } Affix.prototype.checkPositionWithEventLoop = function () { setTimeout($.proxy(this.checkPosition, this), 1) } Affix.prototype.checkPosition = function () { if (!this.$element.is(':visible')) return var height = this.$element.height() var offset = this.options.offset var offsetTop = offset.top var offsetBottom = offset.bottom var scrollHeight = Math.max($(document).height(), $(document.body).height()) if (typeof offset != 'object') offsetBottom = offsetTop = offset if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom) if (this.affixed != affix) { if (this.unpin != null) this.$element.css('top', '') var affixType = 'affix' + (affix ? '-' + affix : '') var e = $.Event(affixType + '.bs.affix') this.$element.trigger(e) if (e.isDefaultPrevented()) return this.affixed = affix this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null this.$element .removeClass(Affix.RESET) .addClass(affixType) .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') } if (affix == 'bottom') { this.$element.offset({ top: scrollHeight - height - offsetBottom }) } } // AFFIX PLUGIN DEFINITION // ======================= function Plugin(option) { return this.each(function () { var $this = $(this) var data = $this.data('bs.affix') var options = typeof option == 'object' && option if (!data) $this.data('bs.affix', (data = new Affix(this, options))) if (typeof option == 'string') data[option]() }) } var old = $.fn.affix $.fn.affix = Plugin $.fn.affix.Constructor = Affix // AFFIX NO CONFLICT // ================= $.fn.affix.noConflict = function () { $.fn.affix = old return this } // AFFIX DATA-API // ============== $(window).on('load', function () { $('[data-spy="affix"]').each(function () { var $spy = $(this) var data = $spy.data() data.offset = data.offset || {} if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom if (data.offsetTop != null) data.offset.top = data.offsetTop Plugin.call($spy, data) }) }) }(jQuery); ================================================ FILE: app_backend/static/js/custom.js ================================================ /** * Created by zhanghe on 16-1-31. */ /* 轮播大图延时加载 */ function lazyContainer(searchNode) { $(searchNode).find('.active').find('img.lazy').each(function () { var imgSrc = $(this).attr('data-src'); if (imgSrc) { $(this).attr('src', imgSrc); $(this).attr('data-src', ''); } }); } $('#myCarousel').bind('slid.bs.carousel', function () { lazyContainer(this); }); lazyContainer('#myCarousel'); /* 生成工具提示 */ $('[rel="tooltip"]').tooltip(); /* 附加导航 */ $(function () { // 滚动页面侧边悬浮动态导航宽度控制 $(window).resize(function () { $('#affix_nav_ul').width($('#affix_nav_ul').parent().width()); //console.log($('.container').width()); // 如果屏幕宽度小于940px 隐藏侧边导航 if ($('.container').width() <= 940) { $('#affix_nav_ul').hide(); } else { $('#affix_nav_ul').show(); } }); $(window).resize(); }); /* 按钮加载状态 */ // html button 标签其中 autocomplete="off" 属性是针对FF浏览器在页面加载之后,禁用状态不会自动解除用的。 $(function () { $(".btn-load").click(function () { $(this).button('loading').delay(1000).queue(function () { $(this).button('reset').dequeue(); }); }); }); /* 返回顶部 */ $('#top-link').click(function () { $('html,body').animate({scrollTop: 0}, 'slow'); return false; }); if (($(window).height() + 100) < $(document).height()) { $('#top-link-block').removeClass('hidden').affix({ // how far to scroll down before link "slides" into view offset: {top: 100} }); } /* 侧滑插件 */ /* var slideout = new Slideout({ 'panel': document.getElementById('panel'), 'menu': document.getElementById('menu'), 'padding': 256, 'tolerance': 70 }); */ /** * Vertically center Bootstrap 3 modals so they aren't always stuck at the top */ $(function() { function reposition() { var modal = $(this), dialog = modal.find('.modal-dialog'); modal.css('display', 'block'); // Dividing by two centers the modal exactly, but dividing by three // or four works better for larger screens. dialog.css("margin-top", Math.max(0, ($(window).height() - dialog.height()) / 2)); } // Reposition when a modal is shown $('.modal').on('show.bs.modal', reposition); // Reposition when the window is resized $(window).on('resize', function() { $('.modal:visible').each(reposition); }); }); /** * 闪现消息自动关闭 */ $(".alert").fadeTo(2000, 500).slideUp(500, function(){ $(".alert").slideUp(500); }); ================================================ FILE: app_backend/static/js/i18n/defaults-ar_AR.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { /*! * Translated default messages for bootstrap-select. * Locale: AR (Arabic) * Author: Yasser Lotfy */ (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'لم يتم إختيار شئ', noneResultsText: 'لا توجد نتائج مطابقة لـ {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected == 1) ? "{0} خيار تم إختياره" : "{0} خيارات تمت إختيارها"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll == 1) ? 'تخطى الحد المسموح ({n} خيار بحد أقصى)' : 'تخطى الحد المسموح ({n} خيارات بحد أقصى)', (numGroup == 1) ? 'تخطى الحد المسموح للمجموعة ({n} خيار بحد أقصى)' : 'تخطى الحد المسموح للمجموعة ({n} خيارات بحد أقصى)' ]; }, selectAllText: 'إختيار الجميع', deselectAllText: 'إلغاء إختيار الجميع', multipleSeparator: '، ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-bg_BG.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Нищо избрано', noneResultsText: 'Няма резултат за {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected == 1) ? "{0} избран елемент" : "{0} избрани елемента"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll == 1) ? 'Лимита е достигнат ({n} елемент максимум)' : 'Лимита е достигнат ({n} елемента максимум)', (numGroup == 1) ? 'Груповия лимит е достигнат ({n} елемент максимум)' : 'Груповия лимит е достигнат ({n} елемента максимум)' ]; }, selectAllText: 'Избери всички', deselectAllText: 'Размаркирай всички', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-cro_CRO.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Odaberite stavku', noneResultsText: 'Nema rezultata pretrage {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected == 1) ? "{0} stavka selektirana" : "{0} stavke selektirane"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll == 1) ? 'Limit je postignut ({n} stvar maximalno)' : 'Limit je postignut ({n} stavke maksimalno)', (numGroup == 1) ? 'Grupni limit je postignut ({n} stvar maksimalno)' : 'Grupni limit je postignut ({n} stavke maksimalno)' ]; }, selectAllText: 'Selektiraj sve', deselectAllText: 'Deselektiraj sve', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-cs_CZ.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Nic není vybráno', noneResultsText: 'Žádné výsledky {0}', countSelectedText: 'Označeno {0} z {1}', maxOptionsText: ['Limit překročen ({n} {var} max)', 'Limit skupiny překročen ({n} {var} max)', ['položek', 'položka']], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-da_DK.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Intet valgt', noneResultsText: 'Ingen resultater fundet {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected == 1) ? "{0} valgt" : "{0} valgt"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll == 1) ? 'Begrænsning nået (max {n} valgt)' : 'Begrænsning nået (max {n} valgte)', (numGroup == 1) ? 'Gruppe-begrænsning nået (max {n} valgt)' : 'Gruppe-begrænsning nået (max {n} valgte)' ]; }, selectAllText: 'Markér alle', deselectAllText: 'Afmarkér alle', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-de_DE.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Bitte wählen...', noneResultsText: 'Keine Ergebnisse für {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected == 1) ? "{0} Element ausgewählt" : "{0} Elemente ausgewählt"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll == 1) ? 'Limit erreicht ({n} Element max.)' : 'Limit erreicht ({n} Elemente max.)', (numGroup == 1) ? 'Gruppen-Limit erreicht ({n} Element max.)' : 'Gruppen-Limit erreicht ({n} Elemente max.)' ]; }, selectAllText: 'Alles auswählen', deselectAllText: 'Nichts auswählen', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-en_US.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Nothing selected', noneResultsText: 'No results match {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected == 1) ? "{0} item selected" : "{0} items selected"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)', (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)' ]; }, selectAllText: 'Select All', deselectAllText: 'Deselect All', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-es_CL.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'No hay selección', noneResultsText: 'No hay resultados {0}', countSelectedText: 'Seleccionados {0} de {1}', maxOptionsText: ['Límite alcanzado ({n} {var} max)', 'Límite del grupo alcanzado({n} {var} max)', ['elementos', 'element']], multipleSeparator: ', ', selectAllText: 'Seleccionar Todos', deselectAllText: 'Desmarcar Todos' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-es_ES.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'No hay selección', noneResultsText: 'No hay resultados {0}', countSelectedText: 'Seleccionados {0} de {1}', maxOptionsText: ['Límite alcanzado ({n} {var} max)', 'Límite del grupo alcanzado({n} {var} max)', ['elementos', 'element']], multipleSeparator: ', ', selectAllText: 'Seleccionar Todos', deselectAllText: 'Desmarcar Todos' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-eu.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Hautapenik ez', noneResultsText: 'Emaitzarik ez {0}', countSelectedText: '{1}(e)tik {0} hautatuta', maxOptionsText: ['Mugara iritsita ({n} {var} gehienez)', 'Taldearen mugara iritsita ({n} {var} gehienez)', ['elementu', 'elementu']], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-fa_IR.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'چیزی انتخاب نشده است', noneResultsText: 'هیج مشابهی برای {0} پیدا نشد', countSelectedText: "{0} از {1} مورد انتخاب شده", maxOptionsText: ['بیشتر ممکن نیست {حداکثر {n} عدد}', 'بیشتر ممکن نیست {حداکثر {n} عدد}'], selectAllText: 'انتخاب همه', deselectAllText: 'انتخاب هیچ کدام', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-fi_FI.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Ei valintoja', noneResultsText: 'Ei hakutuloksia {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected == 1) ? "{0} valittu" : "{0} valitut"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll == 1) ? 'Valintojen maksimimäärä ({n} saavutettu)' : 'Valintojen maksimimäärä ({n} saavutettu)', (numGroup == 1) ? 'Ryhmän maksimimäärä ({n} saavutettu)' : 'Ryhmän maksimimäärä ({n} saavutettu)' ]; }, selectAllText: 'Valitse kaikki', deselectAllText: 'Poista kaikki', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-fr_FR.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Aucune sélection', noneResultsText: 'Aucun résultat pour {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected > 1) ? "{0} éléments sélectionnés" : "{0} élément sélectionné"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll > 1) ? 'Limite atteinte ({n} éléments max)' : 'Limite atteinte ({n} élément max)', (numGroup > 1) ? 'Limite du groupe atteinte ({n} éléments max)' : 'Limite du groupe atteinte ({n} élément max)' ]; }, multipleSeparator: ', ', selectAllText: 'Tout Sélectionner', deselectAllText: 'Tout Dé-selectionner', }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-hu_HU.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Válasszon!', noneResultsText: 'Nincs találat {0}', countSelectedText: function (numSelected, numTotal) { return '{0} elem kiválasztva'; }, maxOptionsText: function (numAll, numGroup) { return [ 'Legfeljebb {n} elem választható', 'A csoportban legfeljebb {n} elem választható' ]; }, selectAllText: 'Mind', deselectAllText: 'Egyik sem', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-id_ID.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Tidak ada yang dipilih', noneResultsText: 'Tidak ada yang cocok {0}', countSelectedText: '{0} terpilih', maxOptionsText: ['Mencapai batas (maksimum {n})', 'Mencapai batas grup (maksimum {n})'], selectAllText: 'Pilih Semua', deselectAllText: 'Hapus Semua', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-it_IT.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Nessuna selezione', noneResultsText: 'Nessun risultato per {0}', countSelectedText: function (numSelected, numTotal){ return (numSelected == 1) ? 'Selezionato {0} di {1}' : 'Selezionati {0} di {1}'; }, maxOptionsText: ['Limite raggiunto ({n} {var} max)', 'Limite del gruppo raggiunto ({n} {var} max)', ['elementi', 'elemento']], multipleSeparator: ', ', selectAllText: 'Seleziona Tutto', deselectAllText: 'Deseleziona Tutto' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-ko_KR.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: '항목을 선택해주세요', noneResultsText: '{0} 검색 결과가 없습니다', countSelectedText: function (numSelected, numTotal) { return "{0}개를 선택하였습니다"; }, maxOptionsText: function (numAll, numGroup) { return [ '{n}개까지 선택 가능합니다', '해당 그룹은 {n}개까지 선택 가능합니다' ]; }, selectAllText: '전체선택', deselectAllText: '전체해제', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-lt_LT.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Niekas nepasirinkta', noneResultsText: 'Niekas nesutapo su {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected == 1) ? "{0} elementas pasirinktas" : "{0} elementai(-ų) pasirinkta"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll == 1) ? 'Pasiekta riba ({n} elementas daugiausiai)' : 'Riba pasiekta ({n} elementai(-ų) daugiausiai)', (numGroup == 1) ? 'Grupės riba pasiekta ({n} elementas daugiausiai)' : 'Grupės riba pasiekta ({n} elementai(-ų) daugiausiai)' ]; }, selectAllText: 'Pasirinkti visus', deselectAllText: 'Atmesti visus', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-nb_NO.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Ingen valgt', noneResultsText: 'Søket gir ingen treff {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected == 1) ? "{0} alternativ valgt" : "{0} alternativer valgt"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll == 1) ? 'Grense nådd (maks {n} valg)' : 'Grense nådd (maks {n} valg)', (numGroup == 1) ? 'Grense for grupper nådd (maks {n} grupper)' : 'Grense for grupper nådd (maks {n} grupper)' ]; }, selectAllText: 'Merk alle', deselectAllText: 'Fjern alle', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-nl_NL.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Niets geselecteerd', noneResultsText: 'Geen resultaten gevonden voor {0}', countSelectedText: '{0} van {1} geselecteerd', maxOptionsText: ['Limiet bereikt ({n} {var} max)', 'Groep limiet bereikt ({n} {var} max)', ['items', 'item']], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-pl_PL.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Nic nie zaznaczono', noneResultsText: 'Brak wyników wyszukiwania {0}', countSelectedText: 'Zaznaczono {0} z {1}', maxOptionsText: ['Osiągnięto limit ({n} {var} max)', 'Limit grupy osiągnięty ({n} {var} max)', ['elementy', 'element']], selectAll: 'Zaznacz wszystkie', deselectAll: 'Odznacz wszystkie', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-pt_BR.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Nada selecionado', noneResultsText: 'Nada encontrado contendo {0}', countSelectedText: 'Selecionado {0} de {1}', maxOptionsText: ['Limite excedido (máx. {n} {var})', 'Limite do grupo excedido (máx. {n} {var})', ['itens', 'item']], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-pt_PT.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Nenhum seleccionado', noneResultsText: 'Sem resultados contendo {0}', countSelectedText: 'Selecionado {0} de {1}', maxOptionsText: ['Limite ultrapassado (máx. {n} {var})', 'Limite de seleções ultrapassado (máx. {n} {var})', ['itens', 'item']], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-ro_RO.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Nu a fost selectat nimic', noneResultsText: 'Nu exista niciun rezultat {0}', countSelectedText: '{0} din {1} selectat(e)', maxOptionsText: ['Limita a fost atinsa ({n} {var} max)', 'Limita de grup a fost atinsa ({n} {var} max)', ['iteme', 'item']], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-ru_RU.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Ничего не выбрано', noneResultsText: 'Совпадений не найдено {0}', countSelectedText: 'Выбрано {0} из {1}', maxOptionsText: ['Достигнут предел ({n} {var} максимум)', 'Достигнут предел в группе ({n} {var} максимум)', ['шт.', 'шт.']], doneButtonText: 'Закрыть', selectAllText: 'Выбрать все', deselectAllText: 'Отменить все', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-sk_SK.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Vyberte zo zoznamu', noneResultsText: 'Pre výraz {0} neboli nájdené žiadne výsledky', countSelectedText: 'Vybrané {0} z {1}', maxOptionsText: ['Limit prekročený ({n} {var} max)', 'Limit skupiny prekročený ({n} {var} max)', ['položiek', 'položka']], selectAllText: 'Vybrať všetky', deselectAllText: 'Zrušiť výber', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-sl_SI.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Nič izbranega', noneResultsText: 'Ni zadetkov za {0}', countSelectedText: function (numSelected, numTotal) { "Število izbranih: {0}"; }, maxOptionsText: function (numAll, numGroup) { return [ 'Omejitev dosežena (max. izbranih: {n})', 'Omejitev skupine dosežena (max. izbranih: {n})' ]; }, selectAllText: 'Izberi vse', deselectAllText: 'Počisti izbor', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-sv_SE.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Inget valt', noneResultsText: 'Inget sökresultat matchar {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected === 1) ? "{0} alternativ valt" : "{0} alternativ valda"; }, maxOptionsText: function (numAll, numGroup) { return [ 'Gräns uppnåd (max {n} alternativ)', 'Gräns uppnåd (max {n} gruppalternativ)' ]; }, selectAllText: 'Markera alla', deselectAllText: 'Avmarkera alla', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-tr_TR.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Hiçbiri seçilmedi', noneResultsText: 'Hiçbir sonuç bulunamadı {0}', countSelectedText: function (numSelected, numTotal) { return (numSelected == 1) ? "{0} öğe seçildi" : "{0} öğe seçildi"; }, maxOptionsText: function (numAll, numGroup) { return [ (numAll == 1) ? 'Limit aşıldı (maksimum {n} sayıda öğe )' : 'Limit aşıldı (maksimum {n} sayıda öğe)', (numGroup == 1) ? 'Grup limiti aşıldı (maksimum {n} sayıda öğe)' : 'Grup limiti aşıldı (maksimum {n} sayıda öğe)' ]; }, selectAllText: 'Tümünü Seç', deselectAllText: 'Seçiniz', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-ua_UA.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: 'Нічого не вибрано', noneResultsText: 'Збігів не знайдено {0}', countSelectedText: 'Вибрано {0} із {1}', maxOptionsText: ['Досягнута межа ({n} {var} максимум)', 'Досягнута межа в групі ({n} {var} максимум)', ['items', 'item']], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-zh_CN.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: '没有选中任何项', noneResultsText: '没有找到匹配项', countSelectedText: '选中{1}中的{0}项', maxOptionsText: ['超出限制 (最多选择{n}项)', '组选择超出限制(最多选择{n}组)'], multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/i18n/defaults-zh_TW.js ================================================ /*! * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) * * Copyright 2013-2017 bootstrap-select * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define(["jquery"], function (a0) { return (factory(a0)); }); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(require("jquery")); } else { factory(root["jQuery"]); } }(this, function (jQuery) { (function ($) { $.fn.selectpicker.defaults = { noneSelectedText: '沒有選取任何項目', noneResultsText: '沒有找到符合的結果', countSelectedText: '已經選取{0}個項目', maxOptionsText: ['超過限制 (最多選擇{n}項)', '超過限制(最多選擇{n}組)'], selectAllText: '選取全部', deselectAllText: '全部取消', multipleSeparator: ', ' }; })(jQuery); })); ================================================ FILE: app_backend/static/js/npm.js ================================================ // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. require('../../js/transition.js') require('../../js/alert.js') require('../../js/button.js') require('../../js/carousel.js') require('../../js/collapse.js') require('../../js/dropdown.js') require('../../js/modal.js') require('../../js/tooltip.js') require('../../js/popover.js') require('../../js/scrollspy.js') require('../../js/tab.js') require('../../js/affix.js') ================================================ FILE: app_backend/static/js/sb-admin-2.js ================================================ /*! * Start Bootstrap - SB Admin 2 v3.3.7+1 (http://startbootstrap.com/template-overviews/sb-admin-2) * Copyright 2013-2016 Start Bootstrap * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap/blob/gh-pages/LICENSE) */ $(function() { $('#side-menu').metisMenu(); }); //Loads the correct sidebar on window load, //collapses the sidebar on window resize. // Sets the min-height of #page-wrapper to window size $(function() { $(window).bind("load resize", function() { var topOffset = 50; var width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width; if (width < 768) { $('div.navbar-collapse').addClass('collapse'); topOffset = 100; // 2-row-menu } else { $('div.navbar-collapse').removeClass('collapse'); } var height = ((this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height) - 1; height = height - topOffset; if (height < 1) height = 1; if (height > topOffset) { $("#page-wrapper").css("min-height", (height) + "px"); } }); var url = window.location; // var element = $('ul.nav a').filter(function() { // return this.href == url; // }).addClass('active').parent().parent().addClass('in').parent(); var element = $('ul.nav a').filter(function() { return this.href == url; }).addClass('active').parent(); while (true) { if (element.is('li')) { element = element.parent().addClass('in').parent(); } else { break; } } }); ================================================ FILE: app_backend/static/plugin/Chart.js-2.6.0/.codeclimate.yml ================================================ engines: duplication: enabled: true config: languages: - javascript exclude_paths: - "samples/samples.js" eslint: enabled: true channel: "eslint-3" fixme: enabled: true ratings: paths: - "src/**/*.js" exclude_paths: - dist/**/* - node_modules/**/* - test/**/* - coverage/**/* ================================================ FILE: app_backend/static/plugin/Chart.js-2.6.0/.editorconfig ================================================ # http://editorconfig.org root = true [*] indent_style = tab indent_size = 4 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = false ================================================ FILE: app_backend/static/plugin/Chart.js-2.6.0/.eslintignore ================================================ **/*{.,-}min.js ================================================ FILE: app_backend/static/plugin/Chart.js-2.6.0/.eslintrc ================================================ ecmaFeatures: modules: true jsx: true env: amd: true browser: true es6: true jquery: true node: true # http://eslint.org/docs/rules/ rules: # Possible Errors no-cond-assign: 2 no-console: [2, {allow: [warn, error]}] no-constant-condition: 2 no-control-regex: 2 no-debugger: 2 no-dupe-args: 2 no-dupe-keys: 2 no-duplicate-case: 2 no-empty: 2 no-empty-character-class: 2 no-ex-assign: 2 no-extra-boolean-cast: 2 no-extra-parens: [2, functions] no-extra-semi: 2 no-func-assign: 2 no-inner-declarations: [2, functions] no-invalid-regexp: 2 no-irregular-whitespace: 2 no-negated-in-lhs: 2 no-obj-calls: 2 no-regex-spaces: 2 no-sparse-arrays: 2 no-unexpected-multiline: 2 no-unreachable: 2 use-isnan: 2 valid-jsdoc: 0 valid-typeof: 2 # Best Practices accessor-pairs: 2 array-callback-return: 0 block-scoped-var: 0 complexity: [2, 6] consistent-return: 0 curly: [2, all] default-case: 2 dot-location: 0 dot-notation: 2 eqeqeq: 2 guard-for-in: 2 no-alert: 2 no-caller: 2 no-case-declarations: 2 no-div-regex: 2 no-else-return: 2 no-empty-pattern: 2 no-eq-null: 2 no-eval: 2 no-extend-native: 2 no-extra-bind: 2 no-fallthrough: 2 no-floating-decimal: 2 no-implicit-coercion: 0 no-implied-eval: 2 no-invalid-this: 0 no-iterator: 2 no-labels: 2 no-lone-blocks: 2 no-loop-func: 2 no-magic-number: 0 no-multi-spaces: 2 no-multi-str: 2 no-native-reassign: 2 no-new-func: 2 no-new-wrappers: 2 no-new: 2 no-octal-escape: 2 no-octal: 2 no-proto: 2 no-redeclare: 2 no-return-assign: 2 no-script-url: 2 no-self-compare: 2 no-sequences: 2 no-throw-literal: 0 no-unused-expressions: 2 no-useless-call: 2 no-useless-concat: 2 no-void: 2 no-warning-comments: 0 no-with: 2 radix: 2 vars-on-top: 0 wrap-iife: 2 yoda: [1, never] # Strict strict: 0 # Variables init-declarations: 0 no-catch-shadow: 2 no-delete-var: 2 no-label-var: 2 no-shadow-restricted-names: 2 no-shadow: 2 no-undef-init: 2 no-undef: 2 no-undefined: 0 no-unused-vars: 2 no-use-before-define: 2 # Node.js and CommonJS callback-return: 2 global-require: 2 handle-callback-err: 2 no-mixed-requires: 0 no-new-require: 0 no-path-concat: 2 no-process-exit: 2 no-restricted-modules: 0 no-sync: 0 # Stylistic Issues array-bracket-spacing: [2, never] block-spacing: 0 brace-style: [2, 1tbs] camelcase: 2 comma-dangle: [2, only-multiline] comma-spacing: 2 comma-style: [2, last] computed-property-spacing: [2, never] consistent-this: [2, me] eol-last: 2 func-call-spacing: 0 func-names: [2, never] func-style: 0 id-length: 0 id-match: 0 indent: [2, tab] jsx-quotes: 0 key-spacing: 2 keyword-spacing: 2 linebreak-style: 0 lines-around-comment: 0 max-depth: 0 max-len: 0 max-lines: 0 max-nested-callbacks: 0 max-params: 0 max-statements-per-line: 0 max-statements: [2, 30] multiline-ternary: 0 new-cap: 0 new-parens: 2 newline-after-var: 0 newline-before-return: 0 newline-per-chained-call: 0 no-array-constructor: 0 no-bitwise: 0 no-continue: 0 no-inline-comments: 0 no-lonely-if: 2 no-mixed-operators: 0 no-mixed-spaces-and-tabs: 2 no-multiple-empty-lines: [2, {max: 2}] no-negated-condition: 0 no-nested-ternary: 0 no-new-object: 0 no-plusplus: 0 no-restricted-syntax: 0 no-spaced-func: 0 no-ternary: 0 no-trailing-spaces: 2 no-underscore-dangle: 0 no-unneeded-ternary: 0 no-whitespace-before-property: 2 object-curly-newline: 0 object-curly-spacing: [2, never] object-property-newline: 0 one-var-declaration-per-line: 2 one-var: 0 operator-assignment: 0 operator-linebreak: 0 padded-blocks: 0 quote-props: [2, as-needed] quotes: [2, single, {avoidEscape: true}] require-jsdoc: 0 semi-spacing: 2 semi: [2, always] sort-keys: 0 sort-vars: 0 space-before-blocks: [2, always] space-before-function-paren: [2, never] space-in-parens: [2, never] space-infix-ops: 0 space-unary-ops: 0 spaced-comment: [2, always] unicode-bom: 0 wrap-regex: 2 # ECMAScript 6 arrow-body-style: 0 arrow-parens: 0 arrow-spacing: 0 constructor-super: 0 generator-star-spacing: 0 no-arrow-condition: 0 no-class-assign: 0 no-const-assign: 0 no-dupe-class-members: 0 no-this-before-super: 0 no-var: 0 object-shorthand: 0 prefer-arrow-callback: 0 prefer-const: 0 prefer-reflect: 0 prefer-spread: 0 prefer-template: 0 require-yield: 0 ================================================ FILE: app_backend/static/plugin/Chart.js-2.6.0/.github/ISSUE_TEMPLATE.md ================================================ ## Expected Behavior ## Current Behavior ## Possible Solution ## Steps to Reproduce (for bugs) 1. 2. 3. 4. ## Context ## Environment * Chart.js version: * Browser name and version: * Link to your project: ================================================ FILE: app_backend/static/plugin/Chart.js-2.6.0/.github/PULL_REQUEST_TEMPLATE.md ================================================ Please consider the following before submitting a pull request: Guidelines for contributing: https://github.com/chartjs/Chart.js/blob/master/docs/developers/contributing.md Example of changes on an interactive website such as the following: - http://jsbin.com/ - http://jsfiddle.net/ - http://codepen.io/pen/ - Premade template: http://codepen.io/pen?template=JXVYzq ================================================ FILE: app_backend/static/plugin/Chart.js-2.6.0/.gitignore ================================================ /_book /coverage /custom #/dist /docs/index.md /gh-pages /node_modules .DS_Store .idea .vscode bower.json *.log *.swp ================================================ FILE: app_backend/static/plugin/Chart.js-2.6.0/.npmignore ================================================ /.git /.github /coverage /custom /dist/*.zip /docs/index.md /node_modules .codeclimate.yml .DS_Store .gitignore .idea .travis.yml ================================================ FILE: app_backend/static/plugin/Chart.js-2.6.0/.travis.yml ================================================ language: node_js node_js: - "5.10" before_install: - "export CHROME_BIN=/usr/bin/google-chrome" - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" before_script: - npm install script: - gulp build - gulp test --coverage - gulp docs - gulp package - gulp bower - cat ./coverage/lcov.info | ./node_modules/.bin/coveralls notifications: slack: chartjs:pcfCZR6ugg5TEcaLtmIfQYuA sudo: required dist: trusty addons: firefox: latest apt: sources: - google-chrome packages: - google-chrome-stable # IMPORTANT: scripts require GITHUB_AUTH_TOKEN and GITHUB_AUTH_EMAIL environment variables # IMPORTANT: scripts has to be set executables in the Git repository (error 127) # https://github.com/travis-ci/travis-ci/issues/5538#issuecomment-225025939 deploy: - provider: script script: ./scripts/deploy.sh skip_cleanup: true on: all_branches: true - provider: script script: ./scripts/release.sh skip_cleanup: true on: branch: release - provider: releases api_key: $GITHUB_AUTH_TOKEN file: - "./dist/Chart.bundle.js" - "./dist/Chart.bundle.min.js" - "./dist/Chart.js" - "./dist/Chart.min.js" - "./dist/Chart.js.zip" skip_cleanup: true on: tags: true - provider: npm email: $NPM_AUTH_EMAIL api_key: $NPM_AUTH_TOKEN skip_cleanup: true on: tags: true ================================================ FILE: app_backend/static/plugin/Chart.js-2.6.0/LICENSE.md ================================================ The MIT License (MIT) Copyright (c) 2013-2017 Nick Downie 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. ================================================ FILE: app_backend/static/plugin/Chart.js-2.6.0/MAINTAINING.md ================================================ # Maintaining ## Release Process Chart.js relies on [Travis CI](https://travis-ci.org/) to automate the library [releases](https://github.com/chartjs/Chart.js/releases). ### Releasing a New Version 1. draft release notes on [GitHub](https://github.com/chartjs/Chart.js/releases/new) for the upcoming tag 1. update `master` `package.json` version using [semver](http://semver.org/) semantic 1. merge `master` into the `release` branch 1. follow the build process on [Travis CI](https://travis-ci.org/chartjs/Chart.js) > **Note:** if `master` is merged in `release` with a `package.json` version that already exists, the tag creation fails and the release process is aborted. ### Automated Tasks Merging into the `release` branch kicks off the automated release process: * build of the `dist/*.js` files * `bower.json` is generated from `package.json` * `dist/*.js` and `bower.json` are added to a detached branch * a tag is created from the `package.json` version * tag (with dist files) is pushed to GitHub Creation of this tag triggers a new build: * `Chart.js.zip` package is generated, containing dist files and examples * `dist/*.js` and `Chart.js.zip` are attached to the GitHub release (downloads) * a new npm package is published on [npmjs](https://www.npmjs.com/package/chart.js) Finally, [cdnjs](https://cdnjs.com/libraries/Chart.js) is automatically updated from the npm release. ### Further Reading * [Travis GitHub releases](https://github.com/chartjs/Chart.js/pull/2555) * [Bower support and dist/* files](https://github.com/chartjs/Chart.js/issues/3033) * [cdnjs npm auto update](https://github.com/cdnjs/cdnjs/pull/8401) ================================================ FILE: app_backend/static/plugin/Chart.js-2.6.0/README.md ================================================ # Chart.js [![Build Status](https://travis-ci.org/chartjs/Chart.js.svg?branch=master)](https://travis-ci.org/chartjs/Chart.js) [![Code Climate](https://codeclimate.com/github/nnnick/Chart.js/badges/gpa.svg)](https://codeclimate.com/github/nnnick/Chart.js) [![Coverage Status](https://coveralls.io/repos/github/chartjs/Chart.js/badge.svg?branch=master)](https://coveralls.io/github/chartjs/Chart.js?branch=master) [![Chart.js on Slack](https://img.shields.io/badge/slack-Chart.js-blue.svg)](https://chart-js-automation.herokuapp.com/) *Simple HTML5 Charts using the canvas element* [chartjs.org](http://www.chartjs.org) ## Installation You can download the latest version of Chart.js from the [GitHub releases](https://github.com/chartjs/Chart.js/releases/latest) or use a [Chart.js CDN](https://cdnjs.com/libraries/Chart.js). To install via npm: ```bash npm install chart.js --save ``` To install via bower: ```bash bower install chart.js --save ``` #### Selecting the Correct Build Chart.js provides two different builds that are available for your use. The `Chart.js` and `Chart.min.js` files include Chart.js and the accompanying color parsing library. If this version is used and you require the use of the time axis, [Moment.js](http://momentjs.com/) will need to be included before Chart.js. The `Chart.bundle.js` and `Chart.bundle.min.js` builds include Moment.js in a single file. This version should be used if you require time axes and want a single file to include, select this version. Do not use this build if your application already includes Moment.js. If you do, Moment.js will be included twice, increasing the page load time and potentially introducing version issues. ## Documentation You can find documentation at [www.chartjs.org/docs](http://www.chartjs.org/docs). The markdown files that build the site are available under `/docs`. Previous version documentation is available at [www.chartjs.org/docs/#notes-previous-versions](http://www.chartjs.org/docs/#notes-previous-versions). ## Contributing Before submitting an issue or a pull request, please take a moment to look over the [contributing guidelines](https://github.com/chartjs/Chart.js/blob/master/docs/developers/contributing.md) first. For support using Chart.js, please post questions with the [`chartjs` tag on Stack Overflow](http://stackoverflow.com/questions/tagged/chartjs). ## Building Instructions on building and testing Chart.js can be found in [the documentation](https://github.com/chartjs/Chart.js/blob/master/docs/developers/contributing.md#building-and-testing). ## Thanks - [BrowserStack](https://browserstack.com) for allowing our team to test on thousands of browsers. - [@n8agrin](https://twitter.com/n8agrin) for the Twitter handle donation. ## License Chart.js is available under the [MIT license](http://opensource.org/licenses/MIT). ================================================ FILE: app_backend/static/plugin/Chart.js-2.6.0/book.json ================================================ { "root": "./docs", "author": "chartjs", "gitbook": "3.2.2", "plugins": ["-lunr", "-search", "search-plus", "anchorjs", "chartjs", "ga"], "pluginsConfig": { "anchorjs": { "icon": "#", "placement": "left", "visible": "always" }, "ga": { "token": "UA-28909194-3", "configuration": "auto" }, "theme-default": { "showLevel": false, "styles": { "website": "style.css" } } } } ================================================ FILE: app_backend/static/plugin/Chart.js-2.6.0/composer.json ================================================ { "name": "nnnick/chartjs", "type": "library", "description": "Simple HTML5 charts using the canvas element.", "keywords": [ "chart", "js" ], "homepage": "http://www.chartjs.org/", "license": "MIT", "authors": [ { "name": "NICK DOWNIE", "email": "hello@nickdownie.com" } ], "require": { "php": ">=5.3.3" }, "minimum-stability": "stable", "extra": { "branch-alias": { "release/2.0": "v2.0-dev" } } } ================================================ FILE: app_backend/static/plugin/Chart.js-2.6.0/dist/Chart.bundle.js ================================================ /*! * Chart.js * http://chartjs.org/ * Version: 2.6.0 * * Copyright 2017 Nick Downie * Released under the MIT license * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md */ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o lum2) { return (lum1 + 0.05) / (lum2 + 0.05); } return (lum2 + 0.05) / (lum1 + 0.05); }, level: function (color2) { var contrastRatio = this.contrast(color2); if (contrastRatio >= 7.1) { return 'AAA'; } return (contrastRatio >= 4.5) ? 'AA' : ''; }, dark: function () { // YIQ equation from http://24ways.org/2010/calculating-color-contrast var rgb = this.values.rgb; var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; return yiq < 128; }, light: function () { return !this.dark(); }, negate: function () { var rgb = []; for (var i = 0; i < 3; i++) { rgb[i] = 255 - this.values.rgb[i]; } this.setValues('rgb', rgb); return this; }, lighten: function (ratio) { var hsl = this.values.hsl; hsl[2] += hsl[2] * ratio; this.setValues('hsl', hsl); return this; }, darken: function (ratio) { var hsl = this.values.hsl; hsl[2] -= hsl[2] * ratio; this.setValues('hsl', hsl); return this; }, saturate: function (ratio) { var hsl = this.values.hsl; hsl[1] += hsl[1] * ratio; this.setValues('hsl', hsl); return this; }, desaturate: function (ratio) { var hsl = this.values.hsl; hsl[1] -= hsl[1] * ratio; this.setValues('hsl', hsl); return this; }, whiten: function (ratio) { var hwb = this.values.hwb; hwb[1] += hwb[1] * ratio; this.setValues('hwb', hwb); return this; }, blacken: function (ratio) { var hwb = this.values.hwb; hwb[2] += hwb[2] * ratio; this.setValues('hwb', hwb); return this; }, greyscale: function () { var rgb = this.values.rgb; // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; this.setValues('rgb', [val, val, val]); return this; }, clearer: function (ratio) { var alpha = this.values.alpha; this.setValues('alpha', alpha - (alpha * ratio)); return this; }, opaquer: function (ratio) { var alpha = this.values.alpha; this.setValues('alpha', alpha + (alpha * ratio)); return this; }, rotate: function (degrees) { var hsl = this.values.hsl; var hue = (hsl[0] + degrees) % 360; hsl[0] = hue < 0 ? 360 + hue : hue; this.setValues('hsl', hsl); return this; }, /** * Ported from sass implementation in C * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 */ mix: function (mixinColor, weight) { var color1 = this; var color2 = mixinColor; var p = weight === undefined ? 0.5 : weight; var w = 2 * p - 1; var a = color1.alpha() - color2.alpha(); var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; var w2 = 1 - w1; return this .rgb( w1 * color1.red() + w2 * color2.red(), w1 * color1.green() + w2 * color2.green(), w1 * color1.blue() + w2 * color2.blue() ) .alpha(color1.alpha() * p + color2.alpha() * (1 - p)); }, toJSON: function () { return this.rgb(); }, clone: function () { // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, // making the final build way to big to embed in Chart.js. So let's do it manually, // assuming that values to clone are 1 dimension arrays containing only numbers, // except 'alpha' which is a number. var result = new Color(); var source = this.values; var target = result.values; var value, type; for (var prop in source) { if (source.hasOwnProperty(prop)) { value = source[prop]; type = ({}).toString.call(value); if (type === '[object Array]') { target[prop] = value.slice(0); } else if (type === '[object Number]') { target[prop] = value; } else { console.error('unexpected color value:', value); } } } return result; } }; Color.prototype.spaces = { rgb: ['red', 'green', 'blue'], hsl: ['hue', 'saturation', 'lightness'], hsv: ['hue', 'saturation', 'value'], hwb: ['hue', 'whiteness', 'blackness'], cmyk: ['cyan', 'magenta', 'yellow', 'black'] }; Color.prototype.maxes = { rgb: [255, 255, 255], hsl: [360, 100, 100], hsv: [360, 100, 100], hwb: [360, 100, 100], cmyk: [100, 100, 100, 100] }; Color.prototype.getValues = function (space) { var values = this.values; var vals = {}; for (var i = 0; i < space.length; i++) { vals[space.charAt(i)] = values[space][i]; } if (values.alpha !== 1) { vals.a = values.alpha; } // {r: 255, g: 255, b: 255, a: 0.4} return vals; }; Color.prototype.setValues = function (space, vals) { var values = this.values; var spaces = this.spaces; var maxes = this.maxes; var alpha = 1; var i; this.valid = true; if (space === 'alpha') { alpha = vals; } else if (vals.length) { // [10, 10, 10] values[space] = vals.slice(0, space.length); alpha = vals[space.length]; } else if (vals[space.charAt(0)] !== undefined) { // {r: 10, g: 10, b: 10} for (i = 0; i < space.length; i++) { values[space][i] = vals[space.charAt(i)]; } alpha = vals.a; } else if (vals[spaces[space][0]] !== undefined) { // {red: 10, green: 10, blue: 10} var chans = spaces[space]; for (i = 0; i < space.length; i++) { values[space][i] = vals[chans[i]]; } alpha = vals.alpha; } values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); if (space === 'alpha') { return false; } var capped; // cap values of the space prior converting all values for (i = 0; i < space.length; i++) { capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); values[space][i] = Math.round(capped); } // convert to all the other color spaces for (var sname in spaces) { if (sname !== space) { values[sname] = convert[space][sname](values[space]); } } return true; }; Color.prototype.setSpace = function (space, args) { var vals = args[0]; if (vals === undefined) { // color.rgb() return this.getValues(space); } // color.rgb(10, 10, 10) if (typeof vals === 'number') { vals = Array.prototype.slice.call(args); } this.setValues(space, vals); return this; }; Color.prototype.setChannel = function (space, index, val) { var svalues = this.values[space]; if (val === undefined) { // color.red() return svalues[index]; } else if (val === svalues[index]) { // color.red(color.red()) return this; } // color.red(100) svalues[index] = val; this.setValues(space, svalues); return this; }; if (typeof window !== 'undefined') { window.Color = Color; } module.exports = Color; },{"1":1,"4":4}],3:[function(require,module,exports){ /* MIT license */ module.exports = { rgb2hsl: rgb2hsl, rgb2hsv: rgb2hsv, rgb2hwb: rgb2hwb, rgb2cmyk: rgb2cmyk, rgb2keyword: rgb2keyword, rgb2xyz: rgb2xyz, rgb2lab: rgb2lab, rgb2lch: rgb2lch, hsl2rgb: hsl2rgb, hsl2hsv: hsl2hsv, hsl2hwb: hsl2hwb, hsl2cmyk: hsl2cmyk, hsl2keyword: hsl2keyword, hsv2rgb: hsv2rgb, hsv2hsl: hsv2hsl, hsv2hwb: hsv2hwb, hsv2cmyk: hsv2cmyk, hsv2keyword: hsv2keyword, hwb2rgb: hwb2rgb, hwb2hsl: hwb2hsl, hwb2hsv: hwb2hsv, hwb2cmyk: hwb2cmyk, hwb2keyword: hwb2keyword, cmyk2rgb: cmyk2rgb, cmyk2hsl: cmyk2hsl, cmyk2hsv: cmyk2hsv, cmyk2hwb: cmyk2hwb, cmyk2keyword: cmyk2keyword, keyword2rgb: keyword2rgb, keyword2hsl: keyword2hsl, keyword2hsv: keyword2hsv, keyword2hwb: keyword2hwb, keyword2cmyk: keyword2cmyk, keyword2lab: keyword2lab, keyword2xyz: keyword2xyz, xyz2rgb: xyz2rgb, xyz2lab: xyz2lab, xyz2lch: xyz2lch, lab2xyz: lab2xyz, lab2rgb: lab2rgb, lab2lch: lab2lch, lch2lab: lch2lab, lch2xyz: lch2xyz, lch2rgb: lch2rgb } function rgb2hsl(rgb) { var r = rgb[0]/255, g = rgb[1]/255, b = rgb[2]/255, min = Math.min(r, g, b), max = Math.max(r, g, b), delta = max - min, h, s, l; if (max == min) h = 0; else if (r == max) h = (g - b) / delta; else if (g == max) h = 2 + (b - r) / delta; else if (b == max) h = 4 + (r - g)/ delta; h = Math.min(h * 60, 360); if (h < 0) h += 360; l = (min + max) / 2; if (max == min) s = 0; else if (l <= 0.5) s = delta / (max + min); else s = delta / (2 - max - min); return [h, s * 100, l * 100]; } function rgb2hsv(rgb) { var r = rgb[0], g = rgb[1], b = rgb[2], min = Math.min(r, g, b), max = Math.max(r, g, b), delta = max - min, h, s, v; if (max == 0) s = 0; else s = (delta/max * 1000)/10; if (max == min) h = 0; else if (r == max) h = (g - b) / delta; else if (g == max) h = 2 + (b - r) / delta; else if (b == max) h = 4 + (r - g) / delta; h = Math.min(h * 60, 360); if (h < 0) h += 360; v = ((max / 255) * 1000) / 10; return [h, s, v]; } function rgb2hwb(rgb) { var r = rgb[0], g = rgb[1], b = rgb[2], h = rgb2hsl(rgb)[0], w = 1/255 * Math.min(r, Math.min(g, b)), b = 1 - 1/255 * Math.max(r, Math.max(g, b)); return [h, w * 100, b * 100]; } function rgb2cmyk(rgb) { var r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255, c, m, y, k; k = Math.min(1 - r, 1 - g, 1 - b); c = (1 - r - k) / (1 - k) || 0; m = (1 - g - k) / (1 - k) || 0; y = (1 - b - k) / (1 - k) || 0; return [c * 100, m * 100, y * 100, k * 100]; } function rgb2keyword(rgb) { return reverseKeywords[JSON.stringify(rgb)]; } function rgb2xyz(rgb) { var r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255; // assume sRGB r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); return [x * 100, y *100, z * 100]; } function rgb2lab(rgb) { var xyz = rgb2xyz(rgb), x = xyz[0], y = xyz[1], z = xyz[2], l, a, b; x /= 95.047; y /= 100; z /= 108.883; x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); l = (116 * y) - 16; a = 500 * (x - y); b = 200 * (y - z); return [l, a, b]; } function rgb2lch(args) { return lab2lch(rgb2lab(args)); } function hsl2rgb(hsl) { var h = hsl[0] / 360, s = hsl[1] / 100, l = hsl[2] / 100, t1, t2, t3, rgb, val; if (s == 0) { val = l * 255; return [val, val, val]; } if (l < 0.5) t2 = l * (1 + s); else t2 = l + s - l * s; t1 = 2 * l - t2; rgb = [0, 0, 0]; for (var i = 0; i < 3; i++) { t3 = h + 1 / 3 * - (i - 1); t3 < 0 && t3++; t3 > 1 && t3--; if (6 * t3 < 1) val = t1 + (t2 - t1) * 6 * t3; else if (2 * t3 < 1) val = t2; else if (3 * t3 < 2) val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; else val = t1; rgb[i] = val * 255; } return rgb; } function hsl2hsv(hsl) { var h = hsl[0], s = hsl[1] / 100, l = hsl[2] / 100, sv, v; if(l === 0) { // no need to do calc on black // also avoids divide by 0 error return [0, 0, 0]; } l *= 2; s *= (l <= 1) ? l : 2 - l; v = (l + s) / 2; sv = (2 * s) / (l + s); return [h, sv * 100, v * 100]; } function hsl2hwb(args) { return rgb2hwb(hsl2rgb(args)); } function hsl2cmyk(args) { return rgb2cmyk(hsl2rgb(args)); } function hsl2keyword(args) { return rgb2keyword(hsl2rgb(args)); } function hsv2rgb(hsv) { var h = hsv[0] / 60, s = hsv[1] / 100, v = hsv[2] / 100, hi = Math.floor(h) % 6; var f = h - Math.floor(h), p = 255 * v * (1 - s), q = 255 * v * (1 - (s * f)), t = 255 * v * (1 - (s * (1 - f))), v = 255 * v; switch(hi) { case 0: return [v, t, p]; case 1: return [q, v, p]; case 2: return [p, v, t]; case 3: return [p, q, v]; case 4: return [t, p, v]; case 5: return [v, p, q]; } } function hsv2hsl(hsv) { var h = hsv[0], s = hsv[1] / 100, v = hsv[2] / 100, sl, l; l = (2 - s) * v; sl = s * v; sl /= (l <= 1) ? l : 2 - l; sl = sl || 0; l /= 2; return [h, sl * 100, l * 100]; } function hsv2hwb(args) { return rgb2hwb(hsv2rgb(args)) } function hsv2cmyk(args) { return rgb2cmyk(hsv2rgb(args)); } function hsv2keyword(args) { return rgb2keyword(hsv2rgb(args)); } // http://dev.w3.org/csswg/css-color/#hwb-to-rgb function hwb2rgb(hwb) { var h = hwb[0] / 360, wh = hwb[1] / 100, bl = hwb[2] / 100, ratio = wh + bl, i, v, f, n; // wh + bl cant be > 1 if (ratio > 1) { wh /= ratio; bl /= ratio; } i = Math.floor(6 * h); v = 1 - bl; f = 6 * h - i; if ((i & 0x01) != 0) { f = 1 - f; } n = wh + f * (v - wh); // linear interpolation switch (i) { default: case 6: case 0: r = v; g = n; b = wh; break; case 1: r = n; g = v; b = wh; break; case 2: r = wh; g = v; b = n; break; case 3: r = wh; g = n; b = v; break; case 4: r = n; g = wh; b = v; break; case 5: r = v; g = wh; b = n; break; } return [r * 255, g * 255, b * 255]; } function hwb2hsl(args) { return rgb2hsl(hwb2rgb(args)); } function hwb2hsv(args) { return rgb2hsv(hwb2rgb(args)); } function hwb2cmyk(args) { return rgb2cmyk(hwb2rgb(args)); } function hwb2keyword(args) { return rgb2keyword(hwb2rgb(args)); } function cmyk2rgb(cmyk) { var c = cmyk[0] / 100, m = cmyk[1] / 100, y = cmyk[2] / 100, k = cmyk[3] / 100, r, g, b; r = 1 - Math.min(1, c * (1 - k) + k); g = 1 - Math.min(1, m * (1 - k) + k); b = 1 - Math.min(1, y * (1 - k) + k); return [r * 255, g * 255, b * 255]; } function cmyk2hsl(args) { return rgb2hsl(cmyk2rgb(args)); } function cmyk2hsv(args) { return rgb2hsv(cmyk2rgb(args)); } function cmyk2hwb(args) { return rgb2hwb(cmyk2rgb(args)); } function cmyk2keyword(args) { return rgb2keyword(cmyk2rgb(args)); } function xyz2rgb(xyz) { var x = xyz[0] / 100, y = xyz[1] / 100, z = xyz[2] / 100, r, g, b; r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); // assume sRGB r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) : r = (r * 12.92); g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) : g = (g * 12.92); b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) : b = (b * 12.92); r = Math.min(Math.max(0, r), 1); g = Math.min(Math.max(0, g), 1); b = Math.min(Math.max(0, b), 1); return [r * 255, g * 255, b * 255]; } function xyz2lab(xyz) { var x = xyz[0], y = xyz[1], z = xyz[2], l, a, b; x /= 95.047; y /= 100; z /= 108.883; x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); l = (116 * y) - 16; a = 500 * (x - y); b = 200 * (y - z); return [l, a, b]; } function xyz2lch(args) { return lab2lch(xyz2lab(args)); } function lab2xyz(lab) { var l = lab[0], a = lab[1], b = lab[2], x, y, z, y2; if (l <= 8) { y = (l * 100) / 903.3; y2 = (7.787 * (y / 100)) + (16 / 116); } else { y = 100 * Math.pow((l + 16) / 116, 3); y2 = Math.pow(y / 100, 1/3); } x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3); z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3); return [x, y, z]; } function lab2lch(lab) { var l = lab[0], a = lab[1], b = lab[2], hr, h, c; hr = Math.atan2(b, a); h = hr * 360 / 2 / Math.PI; if (h < 0) { h += 360; } c = Math.sqrt(a * a + b * b); return [l, c, h]; } function lab2rgb(args) { return xyz2rgb(lab2xyz(args)); } function lch2lab(lch) { var l = lch[0], c = lch[1], h = lch[2], a, b, hr; hr = h / 360 * 2 * Math.PI; a = c * Math.cos(hr); b = c * Math.sin(hr); return [l, a, b]; } function lch2xyz(args) { return lab2xyz(lch2lab(args)); } function lch2rgb(args) { return lab2rgb(lch2lab(args)); } function keyword2rgb(keyword) { return cssKeywords[keyword]; } function keyword2hsl(args) { return rgb2hsl(keyword2rgb(args)); } function keyword2hsv(args) { return rgb2hsv(keyword2rgb(args)); } function keyword2hwb(args) { return rgb2hwb(keyword2rgb(args)); } function keyword2cmyk(args) { return rgb2cmyk(keyword2rgb(args)); } function keyword2lab(args) { return rgb2lab(keyword2rgb(args)); } function keyword2xyz(args) { return rgb2xyz(keyword2rgb(args)); } var cssKeywords = { aliceblue: [240,248,255], antiquewhite: [250,235,215], aqua: [0,255,255], aquamarine: [127,255,212], azure: [240,255,255], beige: [245,245,220], bisque: [255,228,196], black: [0,0,0], blanchedalmond: [255,235,205], blue: [0,0,255], blueviolet: [138,43,226], brown: [165,42,42], burlywood: [222,184,135], cadetblue: [95,158,160], chartreuse: [127,255,0], chocolate: [210,105,30], coral: [255,127,80], cornflowerblue: [100,149,237], cornsilk: [255,248,220], crimson: [220,20,60], cyan: [0,255,255], darkblue: [0,0,139], darkcyan: [0,139,139], darkgoldenrod: [184,134,11], darkgray: [169,169,169], darkgreen: [0,100,0], darkgrey: [169,169,169], darkkhaki: [189,183,107], darkmagenta: [139,0,139], darkolivegreen: [85,107,47], darkorange: [255,140,0], darkorchid: [153,50,204], darkred: [139,0,0], darksalmon: [233,150,122], darkseagreen: [143,188,143], darkslateblue: [72,61,139], darkslategray: [47,79,79], darkslategrey: [47,79,79], darkturquoise: [0,206,209], darkviolet: [148,0,211], deeppink: [255,20,147], deepskyblue: [0,191,255], dimgray: [105,105,105], dimgrey: [105,105,105], dodgerblue: [30,144,255], firebrick: [178,34,34], floralwhite: [255,250,240], forestgreen: [34,139,34], fuchsia: [255,0,255], gainsboro: [220,220,220], ghostwhite: [248,248,255], gold: [255,215,0], goldenrod: [218,165,32], gray: [128,128,128], green: [0,128,0], greenyellow: [173,255,47], grey: [128,128,128], honeydew: [240,255,240], hotpink: [255,105,180], indianred: [205,92,92], indigo: [75,0,130], ivory: [255,255,240], khaki: [240,230,140], lavender: [230,230,250], lavenderblush: [255,240,245], lawngreen: [124,252,0], lemonchiffon: [255,250,205], lightblue: [173,216,230], lightcoral: [240,128,128], lightcyan: [224,255,255], lightgoldenrodyellow: [250,250,210], lightgray: [211,211,211], lightgreen: [144,238,144], lightgrey: [211,211,211], lightpink: [255,182,193], lightsalmon: [255,160,122], lightseagreen: [32,178,170], lightskyblue: [135,206,250], lightslategray: [119,136,153], lightslategrey: [119,136,153], lightsteelblue: [176,196,222], lightyellow: [255,255,224], lime: [0,255,0], limegreen: [50,205,50], linen: [250,240,230], magenta: [255,0,255], maroon: [128,0,0], mediumaquamarine: [102,205,170], mediumblue: [0,0,205], mediumorchid: [186,85,211], mediumpurple: [147,112,219], mediumseagreen: [60,179,113], mediumslateblue: [123,104,238], mediumspringgreen: [0,250,154], mediumturquoise: [72,209,204], mediumvioletred: [199,21,133], midnightblue: [25,25,112], mintcream: [245,255,250], mistyrose: [255,228,225], moccasin: [255,228,181], navajowhite: [255,222,173], navy: [0,0,128], oldlace: [253,245,230], olive: [128,128,0], olivedrab: [107,142,35], orange: [255,165,0], orangered: [255,69,0], orchid: [218,112,214], palegoldenrod: [238,232,170], palegreen: [152,251,152], paleturquoise: [175,238,238], palevioletred: [219,112,147], papayawhip: [255,239,213], peachpuff: [255,218,185], peru: [205,133,63], pink: [255,192,203], plum: [221,160,221], powderblue: [176,224,230], purple: [128,0,128], rebeccapurple: [102, 51, 153], red: [255,0,0], rosybrown: [188,143,143], royalblue: [65,105,225], saddlebrown: [139,69,19], salmon: [250,128,114], sandybrown: [244,164,96], seagreen: [46,139,87], seashell: [255,245,238], sienna: [160,82,45], silver: [192,192,192], skyblue: [135,206,235], slateblue: [106,90,205], slategray: [112,128,144], slategrey: [112,128,144], snow: [255,250,250], springgreen: [0,255,127], steelblue: [70,130,180], tan: [210,180,140], teal: [0,128,128], thistle: [216,191,216], tomato: [255,99,71], turquoise: [64,224,208], violet: [238,130,238], wheat: [245,222,179], white: [255,255,255], whitesmoke: [245,245,245], yellow: [255,255,0], yellowgreen: [154,205,50] }; var reverseKeywords = {}; for (var key in cssKeywords) { reverseKeywords[JSON.stringify(cssKeywords[key])] = key; } },{}],4:[function(require,module,exports){ var conversions = require(3); var convert = function() { return new Converter(); } for (var func in conversions) { // export Raw versions convert[func + "Raw"] = (function(func) { // accept array or plain args return function(arg) { if (typeof arg == "number") arg = Array.prototype.slice.call(arguments); return conversions[func](arg); } })(func); var pair = /(\w+)2(\w+)/.exec(func), from = pair[1], to = pair[2]; // export rgb2hsl and ["rgb"]["hsl"] convert[from] = convert[from] || {}; convert[from][to] = convert[func] = (function(func) { return function(arg) { if (typeof arg == "number") arg = Array.prototype.slice.call(arguments); var val = conversions[func](arg); if (typeof val == "string" || val === undefined) return val; // keyword for (var i = 0; i < val.length; i++) val[i] = Math.round(val[i]); return val; } })(func); } /* Converter does lazy conversion and caching */ var Converter = function() { this.convs = {}; }; /* Either get the values for a space or set the values for a space, depending on args */ Converter.prototype.routeSpace = function(space, args) { var values = args[0]; if (values === undefined) { // color.rgb() return this.getValues(space); } // color.rgb(10, 10, 10) if (typeof values == "number") { values = Array.prototype.slice.call(args); } return this.setValues(space, values); }; /* Set the values for a space, invalidating cache */ Converter.prototype.setValues = function(space, values) { this.space = space; this.convs = {}; this.convs[space] = values; return this; }; /* Get the values for a space. If there's already a conversion for the space, fetch it, otherwise compute it */ Converter.prototype.getValues = function(space) { var vals = this.convs[space]; if (!vals) { var fspace = this.space, from = this.convs[fspace]; vals = convert[fspace][space](from); this.convs[space] = vals; } return vals; }; ["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) { Converter.prototype[space] = function(vals) { return this.routeSpace(space, arguments); } }); module.exports = convert; },{"3":3}],5:[function(require,module,exports){ module.exports = { "aliceblue": [240, 248, 255], "antiquewhite": [250, 235, 215], "aqua": [0, 255, 255], "aquamarine": [127, 255, 212], "azure": [240, 255, 255], "beige": [245, 245, 220], "bisque": [255, 228, 196], "black": [0, 0, 0], "blanchedalmond": [255, 235, 205], "blue": [0, 0, 255], "blueviolet": [138, 43, 226], "brown": [165, 42, 42], "burlywood": [222, 184, 135], "cadetblue": [95, 158, 160], "chartreuse": [127, 255, 0], "chocolate": [210, 105, 30], "coral": [255, 127, 80], "cornflowerblue": [100, 149, 237], "cornsilk": [255, 248, 220], "crimson": [220, 20, 60], "cyan": [0, 255, 255], "darkblue": [0, 0, 139], "darkcyan": [0, 139, 139], "darkgoldenrod": [184, 134, 11], "darkgray": [169, 169, 169], "darkgreen": [0, 100, 0], "darkgrey": [169, 169, 169], "darkkhaki": [189, 183, 107], "darkmagenta": [139, 0, 139], "darkolivegreen": [85, 107, 47], "darkorange": [255, 140, 0], "darkorchid": [153, 50, 204], "darkred": [139, 0, 0], "darksalmon": [233, 150, 122], "darkseagreen": [143, 188, 143], "darkslateblue": [72, 61, 139], "darkslategray": [47, 79, 79], "darkslategrey": [47, 79, 79], "darkturquoise": [0, 206, 209], "darkviolet": [148, 0, 211], "deeppink": [255, 20, 147], "deepskyblue": [0, 191, 255], "dimgray": [105, 105, 105], "dimgrey": [105, 105, 105], "dodgerblue": [30, 144, 255], "firebrick": [178, 34, 34], "floralwhite": [255, 250, 240], "forestgreen": [34, 139, 34], "fuchsia": [255, 0, 255], "gainsboro": [220, 220, 220], "ghostwhite": [248, 248, 255], "gold": [255, 215, 0], "goldenrod": [218, 165, 32], "gray": [128, 128, 128], "green": [0, 128, 0], "greenyellow": [173, 255, 47], "grey": [128, 128, 128], "honeydew": [240, 255, 240], "hotpink": [255, 105, 180], "indianred": [205, 92, 92], "indigo": [75, 0, 130], "ivory": [255, 255, 240], "khaki": [240, 230, 140], "lavender": [230, 230, 250], "lavenderblush": [255, 240, 245], "lawngreen": [124, 252, 0], "lemonchiffon": [255, 250, 205], "lightblue": [173, 216, 230], "lightcoral": [240, 128, 128], "lightcyan": [224, 255, 255], "lightgoldenrodyellow": [250, 250, 210], "lightgray": [211, 211, 211], "lightgreen": [144, 238, 144], "lightgrey": [211, 211, 211], "lightpink": [255, 182, 193], "lightsalmon": [255, 160, 122], "lightseagreen": [32, 178, 170], "lightskyblue": [135, 206, 250], "lightslategray": [119, 136, 153], "lightslategrey": [119, 136, 153], "lightsteelblue": [176, 196, 222], "lightyellow": [255, 255, 224], "lime": [0, 255, 0], "limegreen": [50, 205, 50], "linen": [250, 240, 230], "magenta": [255, 0, 255], "maroon": [128, 0, 0], "mediumaquamarine": [102, 205, 170], "mediumblue": [0, 0, 205], "mediumorchid": [186, 85, 211], "mediumpurple": [147, 112, 219], "mediumseagreen": [60, 179, 113], "mediumslateblue": [123, 104, 238], "mediumspringgreen": [0, 250, 154], "mediumturquoise": [72, 209, 204], "mediumvioletred": [199, 21, 133], "midnightblue": [25, 25, 112], "mintcream": [245, 255, 250], "mistyrose": [255, 228, 225], "moccasin": [255, 228, 181], "navajowhite": [255, 222, 173], "navy": [0, 0, 128], "oldlace": [253, 245, 230], "olive": [128, 128, 0], "olivedrab": [107, 142, 35], "orange": [255, 165, 0], "orangered": [255, 69, 0], "orchid": [218, 112, 214], "palegoldenrod": [238, 232, 170], "palegreen": [152, 251, 152], "paleturquoise": [175, 238, 238], "palevioletred": [219, 112, 147], "papayawhip": [255, 239, 213], "peachpuff": [255, 218, 185], "peru": [205, 133, 63], "pink": [255, 192, 203], "plum": [221, 160, 221], "powderblue": [176, 224, 230], "purple": [128, 0, 128], "rebeccapurple": [102, 51, 153], "red": [255, 0, 0], "rosybrown": [188, 143, 143], "royalblue": [65, 105, 225], "saddlebrown": [139, 69, 19], "salmon": [250, 128, 114], "sandybrown": [244, 164, 96], "seagreen": [46, 139, 87], "seashell": [255, 245, 238], "sienna": [160, 82, 45], "silver": [192, 192, 192], "skyblue": [135, 206, 235], "slateblue": [106, 90, 205], "slategray": [112, 128, 144], "slategrey": [112, 128, 144], "snow": [255, 250, 250], "springgreen": [0, 255, 127], "steelblue": [70, 130, 180], "tan": [210, 180, 140], "teal": [0, 128, 128], "thistle": [216, 191, 216], "tomato": [255, 99, 71], "turquoise": [64, 224, 208], "violet": [238, 130, 238], "wheat": [245, 222, 179], "white": [255, 255, 255], "whitesmoke": [245, 245, 245], "yellow": [255, 255, 0], "yellowgreen": [154, 205, 50] }; },{}],6:[function(require,module,exports){ //! moment.js //! version : 2.18.1 //! authors : Tim Wood, Iskren Chernev, Moment.js contributors //! license : MIT //! momentjs.com ;(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : global.moment = factory() }(this, (function () { 'use strict'; var hookCallback; function hooks () { return hookCallback.apply(null, arguments); } // This is done to register the method called with moment() // without creating circular dependencies. function setHookCallback (callback) { hookCallback = callback; } function isArray(input) { return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]'; } function isObject(input) { // IE8 will treat undefined and null as object if it wasn't for // input != null return input != null && Object.prototype.toString.call(input) === '[object Object]'; } function isObjectEmpty(obj) { var k; for (k in obj) { // even if its not own property I'd still call it non-empty return false; } return true; } function isUndefined(input) { return input === void 0; } function isNumber(input) { return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]'; } function isDate(input) { return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]'; } function map(arr, fn) { var res = [], i; for (i = 0; i < arr.length; ++i) { res.push(fn(arr[i], i)); } return res; } function hasOwnProp(a, b) { return Object.prototype.hasOwnProperty.call(a, b); } function extend(a, b) { for (var i in b) { if (hasOwnProp(b, i)) { a[i] = b[i]; } } if (hasOwnProp(b, 'toString')) { a.toString = b.toString; } if (hasOwnProp(b, 'valueOf')) { a.valueOf = b.valueOf; } return a; } function createUTC (input, format, locale, strict) { return createLocalOrUTC(input, format, locale, strict, true).utc(); } function defaultParsingFlags() { // We need to deep clone this object. return { empty : false, unusedTokens : [], unusedInput : [], overflow : -2, charsLeftOver : 0, nullInput : false, invalidMonth : null, invalidFormat : false, userInvalidated : false, iso : false, parsedDateParts : [], meridiem : null, rfc2822 : false, weekdayMismatch : false }; } function getParsingFlags(m) { if (m._pf == null) { m._pf = defaultParsingFlags(); } return m._pf; } var some; if (Array.prototype.some) { some = Array.prototype.some; } else { some = function (fun) { var t = Object(this); var len = t.length >>> 0; for (var i = 0; i < len; i++) { if (i in t && fun.call(this, t[i], i, t)) { return true; } } return false; }; } var some$1 = some; function isValid(m) { if (m._isValid == null) { var flags = getParsingFlags(m); var parsedParts = some$1.call(flags.parsedDateParts, function (i) { return i != null; }); var isNowValid = !isNaN(m._d.getTime()) && flags.overflow < 0 && !flags.empty && !flags.invalidMonth && !flags.invalidWeekday && !flags.nullInput && !flags.invalidFormat && !flags.userInvalidated && (!flags.meridiem || (flags.meridiem && parsedParts)); if (m._strict) { isNowValid = isNowValid && flags.charsLeftOver === 0 && flags.unusedTokens.length === 0 && flags.bigHour === undefined; } if (Object.isFrozen == null || !Object.isFrozen(m)) { m._isValid = isNowValid; } else { return isNowValid; } } return m._isValid; } function createInvalid (flags) { var m = createUTC(NaN); if (flags != null) { extend(getParsingFlags(m), flags); } else { getParsingFlags(m).userInvalidated = true; } return m; } // Plugins that add properties should also add the key here (null value), // so we can properly clone ourselves. var momentProperties = hooks.momentProperties = []; function copyConfig(to, from) { var i, prop, val; if (!isUndefined(from._isAMomentObject)) { to._isAMomentObject = from._isAMomentObject; } if (!isUndefined(from._i)) { to._i = from._i; } if (!isUndefined(from._f)) { to._f = from._f; } if (!isUndefined(from._l)) { to._l = from._l; } if (!isUndefined(from._strict)) { to._strict = from._strict; } if (!isUndefined(from._tzm)) { to._tzm = from._tzm; } if (!isUndefined(from._isUTC)) { to._isUTC = from._isUTC; } if (!isUndefined(from._offset)) { to._offset = from._offset; } if (!isUndefined(from._pf)) { to._pf = getParsingFlags(from); } if (!isUndefined(from._locale)) { to._locale = from._locale; } if (momentProperties.length > 0) { for (i = 0; i < momentProperties.length; i++) { prop = momentProperties[i]; val = from[prop]; if (!isUndefined(val)) { to[prop] = val; } } } return to; } var updateInProgress = false; // Moment prototype object function Moment(config) { copyConfig(this, config); this._d = new Date(config._d != null ? config._d.getTime() : NaN); if (!this.isValid()) { this._d = new Date(NaN); } // Prevent infinite loop in case updateOffset creates new moment // objects. if (updateInProgress === false) { updateInProgress = true; hooks.updateOffset(this); updateInProgress = false; } } function isMoment (obj) { return obj instanceof Moment || (obj != null && obj._isAMomentObject != null); } function absFloor (number) { if (number < 0) { // -0 -> 0 return Math.ceil(number) || 0; } else { return Math.floor(number); } } function toInt(argumentForCoercion) { var coercedNumber = +argumentForCoercion, value = 0; if (coercedNumber !== 0 && isFinite(coercedNumber)) { value = absFloor(coercedNumber); } return value; } // compare two arrays, return the number of differences function compareArrays(array1, array2, dontConvert) { var len = Math.min(array1.length, array2.length), lengthDiff = Math.abs(array1.length - array2.length), diffs = 0, i; for (i = 0; i < len; i++) { if ((dontConvert && array1[i] !== array2[i]) || (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { diffs++; } } return diffs + lengthDiff; } function warn(msg) { if (hooks.suppressDeprecationWarnings === false && (typeof console !== 'undefined') && console.warn) { console.warn('Deprecation warning: ' + msg); } } function deprecate(msg, fn) { var firstTime = true; return extend(function () { if (hooks.deprecationHandler != null) { hooks.deprecationHandler(null, msg); } if (firstTime) { var args = []; var arg; for (var i = 0; i < arguments.length; i++) { arg = ''; if (typeof arguments[i] === 'object') { arg += '\n[' + i + '] '; for (var key in arguments[0]) { arg += key + ': ' + arguments[0][key] + ', '; } arg = arg.slice(0, -2); // Remove trailing comma and space } else { arg = arguments[i]; } args.push(arg); } warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack); firstTime = false; } return fn.apply(this, arguments); }, fn); } var deprecations = {}; function deprecateSimple(name, msg) { if (hooks.deprecationHandler != null) { hooks.deprecationHandler(name, msg); } if (!deprecations[name]) { warn(msg); deprecations[name] = true; } } hooks.suppressDeprecationWarnings = false; hooks.deprecationHandler = null; function isFunction(input) { return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; } function set (config) { var prop, i; for (i in config) { prop = config[i]; if (isFunction(prop)) { this[i] = prop; } else { this['_' + i] = prop; } } this._config = config; // Lenient ordinal parsing accepts just a number in addition to // number + (possibly) stuff coming from _dayOfMonthOrdinalParse. // TODO: Remove "ordinalParse" fallback in next major release. this._dayOfMonthOrdinalParseLenient = new RegExp( (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + '|' + (/\d{1,2}/).source); } function mergeConfigs(parentConfig, childConfig) { var res = extend({}, parentConfig), prop; for (prop in childConfig) { if (hasOwnProp(childConfig, prop)) { if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { res[prop] = {}; extend(res[prop], parentConfig[prop]); extend(res[prop], childConfig[prop]); } else if (childConfig[prop] != null) { res[prop] = childConfig[prop]; } else { delete res[prop]; } } } for (prop in parentConfig) { if (hasOwnProp(parentConfig, prop) && !hasOwnProp(childConfig, prop) && isObject(parentConfig[prop])) { // make sure changes to properties don't modify parent config res[prop] = extend({}, res[prop]); } } return res; } function Locale(config) { if (config != null) { this.set(config); } } var keys; if (Object.keys) { keys = Object.keys; } else { keys = function (obj) { var i, res = []; for (i in obj) { if (hasOwnProp(obj, i)) { res.push(i); } } return res; }; } var keys$1 = keys; var defaultCalendar = { sameDay : '[Today at] LT', nextDay : '[Tomorrow at] LT', nextWeek : 'dddd [at] LT', lastDay : '[Yesterday at] LT', lastWeek : '[Last] dddd [at] LT', sameElse : 'L' }; function calendar (key, mom, now) { var output = this._calendar[key] || this._calendar['sameElse']; return isFunction(output) ? output.call(mom, now) : output; } var defaultLongDateFormat = { LTS : 'h:mm:ss A', LT : 'h:mm A', L : 'MM/DD/YYYY', LL : 'MMMM D, YYYY', LLL : 'MMMM D, YYYY h:mm A', LLLL : 'dddd, MMMM D, YYYY h:mm A' }; function longDateFormat (key) { var format = this._longDateFormat[key], formatUpper = this._longDateFormat[key.toUpperCase()]; if (format || !formatUpper) { return format; } this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { return val.slice(1); }); return this._longDateFormat[key]; } var defaultInvalidDate = 'Invalid date'; function invalidDate () { return this._invalidDate; } var defaultOrdinal = '%d'; var defaultDayOfMonthOrdinalParse = /\d{1,2}/; function ordinal (number) { return this._ordinal.replace('%d', number); } var defaultRelativeTime = { future : 'in %s', past : '%s ago', s : 'a few seconds', ss : '%d seconds', m : 'a minute', mm : '%d minutes', h : 'an hour', hh : '%d hours', d : 'a day', dd : '%d days', M : 'a month', MM : '%d months', y : 'a year', yy : '%d years' }; function relativeTime (number, withoutSuffix, string, isFuture) { var output = this._relativeTime[string]; return (isFunction(output)) ? output(number, withoutSuffix, string, isFuture) : output.replace(/%d/i, number); } function pastFuture (diff, output) { var format = this._relativeTime[diff > 0 ? 'future' : 'past']; return isFunction(format) ? format(output) : format.replace(/%s/i, output); } var aliases = {}; function addUnitAlias (unit, shorthand) { var lowerCase = unit.toLowerCase(); aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; } function normalizeUnits(units) { return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; } function normalizeObjectUnits(inputObject) { var normalizedInput = {}, normalizedProp, prop; for (prop in inputObject) { if (hasOwnProp(inputObject, prop)) { normalizedProp = normalizeUnits(prop); if (normalizedProp) { normalizedInput[normalizedProp] = inputObject[prop]; } } } return normalizedInput; } var priorities = {}; function addUnitPriority(unit, priority) { priorities[unit] = priority; } function getPrioritizedUnits(unitsObj) { var units = []; for (var u in unitsObj) { units.push({unit: u, priority: priorities[u]}); } units.sort(function (a, b) { return a.priority - b.priority; }); return units; } function makeGetSet (unit, keepTime) { return function (value) { if (value != null) { set$1(this, unit, value); hooks.updateOffset(this, keepTime); return this; } else { return get(this, unit); } }; } function get (mom, unit) { return mom.isValid() ? mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN; } function set$1 (mom, unit, value) { if (mom.isValid()) { mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); } } // MOMENTS function stringGet (units) { units = normalizeUnits(units); if (isFunction(this[units])) { return this[units](); } return this; } function stringSet (units, value) { if (typeof units === 'object') { units = normalizeObjectUnits(units); var prioritized = getPrioritizedUnits(units); for (var i = 0; i < prioritized.length; i++) { this[prioritized[i].unit](units[prioritized[i].unit]); } } else { units = normalizeUnits(units); if (isFunction(this[units])) { return this[units](value); } } return this; } function zeroFill(number, targetLength, forceSign) { var absNumber = '' + Math.abs(number), zerosToFill = targetLength - absNumber.length, sign = number >= 0; return (sign ? (forceSign ? '+' : '') : '-') + Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; } var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; var formatFunctions = {}; var formatTokenFunctions = {}; // token: 'M' // padded: ['MM', 2] // ordinal: 'Mo' // callback: function () { this.month() + 1 } function addFormatToken (token, padded, ordinal, callback) { var func = callback; if (typeof callback === 'string') { func = function () { return this[callback](); }; } if (token) { formatTokenFunctions[token] = func; } if (padded) { formatTokenFunctions[padded[0]] = function () { return zeroFill(func.apply(this, arguments), padded[1], padded[2]); }; } if (ordinal) { formatTokenFunctions[ordinal] = function () { return this.localeData().ordinal(func.apply(this, arguments), token); }; } } function removeFormattingTokens(input) { if (input.match(/\[[\s\S]/)) { return input.replace(/^\[|\]$/g, ''); } return input.replace(/\\/g, ''); } function makeFormatFunction(format) { var array = format.match(formattingTokens), i, length; for (i = 0, length = array.length; i < length; i++) { if (formatTokenFunctions[array[i]]) { array[i] = formatTokenFunctions[array[i]]; } else { array[i] = removeFormattingTokens(array[i]); } } return function (mom) { var output = '', i; for (i = 0; i < length; i++) { output += isFunction(array[i]) ? array[i].call(mom, format) : array[i]; } return output; }; } // format date using native date object function formatMoment(m, format) { if (!m.isValid()) { return m.localeData().invalidDate(); } format = expandFormat(format, m.localeData()); formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); return formatFunctions[format](m); } function expandFormat(format, locale) { var i = 5; function replaceLongDateFormatTokens(input) { return locale.longDateFormat(input) || input; } localFormattingTokens.lastIndex = 0; while (i >= 0 && localFormattingTokens.test(format)) { format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); localFormattingTokens.lastIndex = 0; i -= 1; } return format; } var match1 = /\d/; // 0 - 9 var match2 = /\d\d/; // 00 - 99 var match3 = /\d{3}/; // 000 - 999 var match4 = /\d{4}/; // 0000 - 9999 var match6 = /[+-]?\d{6}/; // -999999 - 999999 var match1to2 = /\d\d?/; // 0 - 99 var match3to4 = /\d\d\d\d?/; // 999 - 9999 var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999 var match1to3 = /\d{1,3}/; // 0 - 999 var match1to4 = /\d{1,4}/; // 0 - 9999 var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 var matchUnsigned = /\d+/; // 0 - inf var matchSigned = /[+-]?\d+/; // -inf - inf var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 // any word (or two) characters or numbers including two/three word month in arabic. // includes scottish gaelic two word and hyphenated months var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i; var regexes = {}; function addRegexToken (token, regex, strictRegex) { regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) { return (isStrict && strictRegex) ? strictRegex : regex; }; } function getParseRegexForToken (token, config) { if (!hasOwnProp(regexes, token)) { return new RegExp(unescapeFormat(token)); } return regexes[token](config._strict, config._locale); } // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript function unescapeFormat(s) { return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { return p1 || p2 || p3 || p4; })); } function regexEscape(s) { return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); } var tokens = {}; function addParseToken (token, callback) { var i, func = callback; if (typeof token === 'string') { token = [token]; } if (isNumber(callback)) { func = function (input, array) { array[callback] = toInt(input); }; } for (i = 0; i < token.length; i++) { tokens[token[i]] = func; } } function addWeekParseToken (token, callback) { addParseToken(token, function (input, array, config, token) { config._w = config._w || {}; callback(input, config._w, config, token); }); } function addTimeToArrayFromToken(token, input, config) { if (input != null && hasOwnProp(tokens, token)) { tokens[token](input, config._a, config, token); } } var YEAR = 0; var MONTH = 1; var DATE = 2; var HOUR = 3; var MINUTE = 4; var SECOND = 5; var MILLISECOND = 6; var WEEK = 7; var WEEKDAY = 8; var indexOf; if (Array.prototype.indexOf) { indexOf = Array.prototype.indexOf; } else { indexOf = function (o) { // I know var i; for (i = 0; i < this.length; ++i) { if (this[i] === o) { return i; } } return -1; }; } var indexOf$1 = indexOf; function daysInMonth(year, month) { return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); } // FORMATTING addFormatToken('M', ['MM', 2], 'Mo', function () { return this.month() + 1; }); addFormatToken('MMM', 0, 0, function (format) { return this.localeData().monthsShort(this, format); }); addFormatToken('MMMM', 0, 0, function (format) { return this.localeData().months(this, format); }); // ALIASES addUnitAlias('month', 'M'); // PRIORITY addUnitPriority('month', 8); // PARSING addRegexToken('M', match1to2); addRegexToken('MM', match1to2, match2); addRegexToken('MMM', function (isStrict, locale) { return locale.monthsShortRegex(isStrict); }); addRegexToken('MMMM', function (isStrict, locale) { return locale.monthsRegex(isStrict); }); addParseToken(['M', 'MM'], function (input, array) { array[MONTH] = toInt(input) - 1; }); addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { var month = config._locale.monthsParse(input, token, config._strict); // if we didn't find a month name, mark the date as invalid. if (month != null) { array[MONTH] = month; } else { getParsingFlags(config).invalidMonth = input; } }); // LOCALES var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/; var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); function localeMonths (m, format) { if (!m) { return isArray(this._months) ? this._months : this._months['standalone']; } return isArray(this._months) ? this._months[m.month()] : this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()]; } var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); function localeMonthsShort (m, format) { if (!m) { return isArray(this._monthsShort) ? this._monthsShort : this._monthsShort['standalone']; } return isArray(this._monthsShort) ? this._monthsShort[m.month()] : this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()]; } function handleStrictParse(monthName, format, strict) { var i, ii, mom, llc = monthName.toLocaleLowerCase(); if (!this._monthsParse) { // this is not used this._monthsParse = []; this._longMonthsParse = []; this._shortMonthsParse = []; for (i = 0; i < 12; ++i) { mom = createUTC([2000, i]); this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase(); this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); } } if (strict) { if (format === 'MMM') { ii = indexOf$1.call(this._shortMonthsParse, llc); return ii !== -1 ? ii : null; } else { ii = indexOf$1.call(this._longMonthsParse, llc); return ii !== -1 ? ii : null; } } else { if (format === 'MMM') { ii = indexOf$1.call(this._shortMonthsParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._longMonthsParse, llc); return ii !== -1 ? ii : null; } else { ii = indexOf$1.call(this._longMonthsParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._shortMonthsParse, llc); return ii !== -1 ? ii : null; } } } function localeMonthsParse (monthName, format, strict) { var i, mom, regex; if (this._monthsParseExact) { return handleStrictParse.call(this, monthName, format, strict); } if (!this._monthsParse) { this._monthsParse = []; this._longMonthsParse = []; this._shortMonthsParse = []; } // TODO: add sorting // Sorting makes sure if one month (or abbr) is a prefix of another // see sorting in computeMonthsParse for (i = 0; i < 12; i++) { // make the regex if we don't have it already mom = createUTC([2000, i]); if (strict && !this._longMonthsParse[i]) { this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); } if (!strict && !this._monthsParse[i]) { regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); } // test the regex if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { return i; } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { return i; } else if (!strict && this._monthsParse[i].test(monthName)) { return i; } } } // MOMENTS function setMonth (mom, value) { var dayOfMonth; if (!mom.isValid()) { // No op return mom; } if (typeof value === 'string') { if (/^\d+$/.test(value)) { value = toInt(value); } else { value = mom.localeData().monthsParse(value); // TODO: Another silent failure? if (!isNumber(value)) { return mom; } } } dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); return mom; } function getSetMonth (value) { if (value != null) { setMonth(this, value); hooks.updateOffset(this, true); return this; } else { return get(this, 'Month'); } } function getDaysInMonth () { return daysInMonth(this.year(), this.month()); } var defaultMonthsShortRegex = matchWord; function monthsShortRegex (isStrict) { if (this._monthsParseExact) { if (!hasOwnProp(this, '_monthsRegex')) { computeMonthsParse.call(this); } if (isStrict) { return this._monthsShortStrictRegex; } else { return this._monthsShortRegex; } } else { if (!hasOwnProp(this, '_monthsShortRegex')) { this._monthsShortRegex = defaultMonthsShortRegex; } return this._monthsShortStrictRegex && isStrict ? this._monthsShortStrictRegex : this._monthsShortRegex; } } var defaultMonthsRegex = matchWord; function monthsRegex (isStrict) { if (this._monthsParseExact) { if (!hasOwnProp(this, '_monthsRegex')) { computeMonthsParse.call(this); } if (isStrict) { return this._monthsStrictRegex; } else { return this._monthsRegex; } } else { if (!hasOwnProp(this, '_monthsRegex')) { this._monthsRegex = defaultMonthsRegex; } return this._monthsStrictRegex && isStrict ? this._monthsStrictRegex : this._monthsRegex; } } function computeMonthsParse () { function cmpLenRev(a, b) { return b.length - a.length; } var shortPieces = [], longPieces = [], mixedPieces = [], i, mom; for (i = 0; i < 12; i++) { // make the regex if we don't have it already mom = createUTC([2000, i]); shortPieces.push(this.monthsShort(mom, '')); longPieces.push(this.months(mom, '')); mixedPieces.push(this.months(mom, '')); mixedPieces.push(this.monthsShort(mom, '')); } // Sorting makes sure if one month (or abbr) is a prefix of another it // will match the longer piece. shortPieces.sort(cmpLenRev); longPieces.sort(cmpLenRev); mixedPieces.sort(cmpLenRev); for (i = 0; i < 12; i++) { shortPieces[i] = regexEscape(shortPieces[i]); longPieces[i] = regexEscape(longPieces[i]); } for (i = 0; i < 24; i++) { mixedPieces[i] = regexEscape(mixedPieces[i]); } this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); this._monthsShortRegex = this._monthsRegex; this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); } // FORMATTING addFormatToken('Y', 0, 0, function () { var y = this.year(); return y <= 9999 ? '' + y : '+' + y; }); addFormatToken(0, ['YY', 2], 0, function () { return this.year() % 100; }); addFormatToken(0, ['YYYY', 4], 0, 'year'); addFormatToken(0, ['YYYYY', 5], 0, 'year'); addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); // ALIASES addUnitAlias('year', 'y'); // PRIORITIES addUnitPriority('year', 1); // PARSING addRegexToken('Y', matchSigned); addRegexToken('YY', match1to2, match2); addRegexToken('YYYY', match1to4, match4); addRegexToken('YYYYY', match1to6, match6); addRegexToken('YYYYYY', match1to6, match6); addParseToken(['YYYYY', 'YYYYYY'], YEAR); addParseToken('YYYY', function (input, array) { array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input); }); addParseToken('YY', function (input, array) { array[YEAR] = hooks.parseTwoDigitYear(input); }); addParseToken('Y', function (input, array) { array[YEAR] = parseInt(input, 10); }); // HELPERS function daysInYear(year) { return isLeapYear(year) ? 366 : 365; } function isLeapYear(year) { return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; } // HOOKS hooks.parseTwoDigitYear = function (input) { return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); }; // MOMENTS var getSetYear = makeGetSet('FullYear', true); function getIsLeapYear () { return isLeapYear(this.year()); } function createDate (y, m, d, h, M, s, ms) { // can't just apply() to create a date: // https://stackoverflow.com/q/181348 var date = new Date(y, m, d, h, M, s, ms); // the date constructor remaps years 0-99 to 1900-1999 if (y < 100 && y >= 0 && isFinite(date.getFullYear())) { date.setFullYear(y); } return date; } function createUTCDate (y) { var date = new Date(Date.UTC.apply(null, arguments)); // the Date.UTC function remaps years 0-99 to 1900-1999 if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) { date.setUTCFullYear(y); } return date; } // start-of-first-week - start-of-year function firstWeekOffset(year, dow, doy) { var // first-week day -- which january is always in the first week (4 for iso, 1 for other) fwd = 7 + dow - doy, // first-week day local weekday -- which local weekday is fwd fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; return -fwdlw + fwd - 1; } // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday function dayOfYearFromWeeks(year, week, weekday, dow, doy) { var localWeekday = (7 + weekday - dow) % 7, weekOffset = firstWeekOffset(year, dow, doy), dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, resYear, resDayOfYear; if (dayOfYear <= 0) { resYear = year - 1; resDayOfYear = daysInYear(resYear) + dayOfYear; } else if (dayOfYear > daysInYear(year)) { resYear = year + 1; resDayOfYear = dayOfYear - daysInYear(year); } else { resYear = year; resDayOfYear = dayOfYear; } return { year: resYear, dayOfYear: resDayOfYear }; } function weekOfYear(mom, dow, doy) { var weekOffset = firstWeekOffset(mom.year(), dow, doy), week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, resWeek, resYear; if (week < 1) { resYear = mom.year() - 1; resWeek = week + weeksInYear(resYear, dow, doy); } else if (week > weeksInYear(mom.year(), dow, doy)) { resWeek = week - weeksInYear(mom.year(), dow, doy); resYear = mom.year() + 1; } else { resYear = mom.year(); resWeek = week; } return { week: resWeek, year: resYear }; } function weeksInYear(year, dow, doy) { var weekOffset = firstWeekOffset(year, dow, doy), weekOffsetNext = firstWeekOffset(year + 1, dow, doy); return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; } // FORMATTING addFormatToken('w', ['ww', 2], 'wo', 'week'); addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); // ALIASES addUnitAlias('week', 'w'); addUnitAlias('isoWeek', 'W'); // PRIORITIES addUnitPriority('week', 5); addUnitPriority('isoWeek', 5); // PARSING addRegexToken('w', match1to2); addRegexToken('ww', match1to2, match2); addRegexToken('W', match1to2); addRegexToken('WW', match1to2, match2); addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { week[token.substr(0, 1)] = toInt(input); }); // HELPERS // LOCALES function localeWeek (mom) { return weekOfYear(mom, this._week.dow, this._week.doy).week; } var defaultLocaleWeek = { dow : 0, // Sunday is the first day of the week. doy : 6 // The week that contains Jan 1st is the first week of the year. }; function localeFirstDayOfWeek () { return this._week.dow; } function localeFirstDayOfYear () { return this._week.doy; } // MOMENTS function getSetWeek (input) { var week = this.localeData().week(this); return input == null ? week : this.add((input - week) * 7, 'd'); } function getSetISOWeek (input) { var week = weekOfYear(this, 1, 4).week; return input == null ? week : this.add((input - week) * 7, 'd'); } // FORMATTING addFormatToken('d', 0, 'do', 'day'); addFormatToken('dd', 0, 0, function (format) { return this.localeData().weekdaysMin(this, format); }); addFormatToken('ddd', 0, 0, function (format) { return this.localeData().weekdaysShort(this, format); }); addFormatToken('dddd', 0, 0, function (format) { return this.localeData().weekdays(this, format); }); addFormatToken('e', 0, 0, 'weekday'); addFormatToken('E', 0, 0, 'isoWeekday'); // ALIASES addUnitAlias('day', 'd'); addUnitAlias('weekday', 'e'); addUnitAlias('isoWeekday', 'E'); // PRIORITY addUnitPriority('day', 11); addUnitPriority('weekday', 11); addUnitPriority('isoWeekday', 11); // PARSING addRegexToken('d', match1to2); addRegexToken('e', match1to2); addRegexToken('E', match1to2); addRegexToken('dd', function (isStrict, locale) { return locale.weekdaysMinRegex(isStrict); }); addRegexToken('ddd', function (isStrict, locale) { return locale.weekdaysShortRegex(isStrict); }); addRegexToken('dddd', function (isStrict, locale) { return locale.weekdaysRegex(isStrict); }); addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { var weekday = config._locale.weekdaysParse(input, token, config._strict); // if we didn't get a weekday name, mark the date as invalid if (weekday != null) { week.d = weekday; } else { getParsingFlags(config).invalidWeekday = input; } }); addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { week[token] = toInt(input); }); // HELPERS function parseWeekday(input, locale) { if (typeof input !== 'string') { return input; } if (!isNaN(input)) { return parseInt(input, 10); } input = locale.weekdaysParse(input); if (typeof input === 'number') { return input; } return null; } function parseIsoWeekday(input, locale) { if (typeof input === 'string') { return locale.weekdaysParse(input) % 7 || 7; } return isNaN(input) ? null : input; } // LOCALES var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); function localeWeekdays (m, format) { if (!m) { return isArray(this._weekdays) ? this._weekdays : this._weekdays['standalone']; } return isArray(this._weekdays) ? this._weekdays[m.day()] : this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()]; } var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); function localeWeekdaysShort (m) { return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort; } var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); function localeWeekdaysMin (m) { return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin; } function handleStrictParse$1(weekdayName, format, strict) { var i, ii, mom, llc = weekdayName.toLocaleLowerCase(); if (!this._weekdaysParse) { this._weekdaysParse = []; this._shortWeekdaysParse = []; this._minWeekdaysParse = []; for (i = 0; i < 7; ++i) { mom = createUTC([2000, 1]).day(i); this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase(); this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase(); this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); } } if (strict) { if (format === 'dddd') { ii = indexOf$1.call(this._weekdaysParse, llc); return ii !== -1 ? ii : null; } else if (format === 'ddd') { ii = indexOf$1.call(this._shortWeekdaysParse, llc); return ii !== -1 ? ii : null; } else { ii = indexOf$1.call(this._minWeekdaysParse, llc); return ii !== -1 ? ii : null; } } else { if (format === 'dddd') { ii = indexOf$1.call(this._weekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._shortWeekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._minWeekdaysParse, llc); return ii !== -1 ? ii : null; } else if (format === 'ddd') { ii = indexOf$1.call(this._shortWeekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._weekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._minWeekdaysParse, llc); return ii !== -1 ? ii : null; } else { ii = indexOf$1.call(this._minWeekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._weekdaysParse, llc); if (ii !== -1) { return ii; } ii = indexOf$1.call(this._shortWeekdaysParse, llc); return ii !== -1 ? ii : null; } } } function localeWeekdaysParse (weekdayName, format, strict) { var i, mom, regex; if (this._weekdaysParseExact) { return handleStrictParse$1.call(this, weekdayName, format, strict); } if (!this._weekdaysParse) { this._weekdaysParse = []; this._minWeekdaysParse = []; this._shortWeekdaysParse = []; this._fullWeekdaysParse = []; } for (i = 0; i < 7; i++) { // make the regex if we don't have it already mom = createUTC([2000, 1]).day(i); if (strict && !this._fullWeekdaysParse[i]) { this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i'); this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i'); this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i'); } if (!this._weekdaysParse[i]) { regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); } // test the regex if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) { return i; } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) { return i; } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) { return i; } else if (!strict && this._weekdaysParse[i].test(weekdayName)) { return i; } } } // MOMENTS function getSetDayOfWeek (input) { if (!this.isValid()) { return input != null ? this : NaN; } var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); if (input != null) { input = parseWeekday(input, this.localeData()); return this.add(input - day, 'd'); } else { return day; } } function getSetLocaleDayOfWeek (input) { if (!this.isValid()) { return input != null ? this : NaN; } var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; return input == null ? weekday : this.add(input - weekday, 'd'); } function getSetISODayOfWeek (input) { if (!this.isValid()) { return input != null ? this : NaN; } // behaves the same as moment#day except // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) // as a setter, sunday should belong to the previous week. if (input != null) { var weekday = parseIsoWeekday(input, this.localeData()); return this.day(this.day() % 7 ? weekday : weekday - 7); } else { return this.day() || 7; } } var defaultWeekdaysRegex = matchWord; function weekdaysRegex (isStrict) { if (this._weekdaysParseExact) { if (!hasOwnProp(this, '_weekdaysRegex')) { computeWeekdaysParse.call(this); } if (isStrict) { return this._weekdaysStrictRegex; } else { return this._weekdaysRegex; } } else { if (!hasOwnProp(this, '_weekdaysRegex')) { this._weekdaysRegex = defaultWeekdaysRegex; } return this._weekdaysStrictRegex && isStrict ? this._weekdaysStrictRegex : this._weekdaysRegex; } } var defaultWeekdaysShortRegex = matchWord; function weekdaysShortRegex (isStrict) { if (this._weekdaysParseExact) { if (!hasOwnProp(this, '_weekdaysRegex')) { computeWeekdaysParse.call(this); } if (isStrict) { return this._weekdaysShortStrictRegex; } else { return this._weekdaysShortRegex; } } else { if (!hasOwnProp(this, '_weekdaysShortRegex')) { this._weekdaysShortRegex = defaultWeekdaysShortRegex; } return this._weekdaysShortStrictRegex && isStrict ? this._weekdaysShortStrictRegex : this._weekdaysShortRegex; } } var defaultWeekdaysMinRegex = matchWord; function weekdaysMinRegex (isStrict) { if (this._weekdaysParseExact) { if (!hasOwnProp(this, '_weekdaysRegex')) { computeWeekdaysParse.call(this); } if (isStrict) { return this._weekdaysMinStrictRegex; } else { return this._weekdaysMinRegex; } } else { if (!hasOwnProp(this, '_weekdaysMinRegex')) { this._weekdaysMinRegex = defaultWeekdaysMinRegex; } return this._weekdaysMinStrictRegex && isStrict ? this._weekdaysMinStrictRegex : this._weekdaysMinRegex; } } function computeWeekdaysParse () { function cmpLenRev(a, b) { return b.length - a.length; } var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [], i, mom, minp, shortp, longp; for (i = 0; i < 7; i++) { // make the regex if we don't have it already mom = createUTC([2000, 1]).day(i); minp = this.weekdaysMin(mom, ''); shortp = this.weekdaysShort(mom, ''); longp = this.weekdays(mom, ''); minPieces.push(minp); shortPieces.push(shortp); longPieces.push(longp); mixedPieces.push(minp); mixedPieces.push(shortp); mixedPieces.push(longp); } // Sorting makes sure if one weekday (or abbr) is a prefix of another it // will match the longer piece. minPieces.sort(cmpLenRev); shortPieces.sort(cmpLenRev); longPieces.sort(cmpLenRev); mixedPieces.sort(cmpLenRev); for (i = 0; i < 7; i++) { shortPieces[i] = regexEscape(shortPieces[i]); longPieces[i] = regexEscape(longPieces[i]); mixedPieces[i] = regexEscape(mixedPieces[i]); } this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); this._weekdaysShortRegex = this._weekdaysRegex; this._weekdaysMinRegex = this._weekdaysRegex; this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i'); } // FORMATTING function hFormat() { return this.hours() % 12 || 12; } function kFormat() { return this.hours() || 24; } addFormatToken('H', ['HH', 2], 0, 'hour'); addFormatToken('h', ['hh', 2], 0, hFormat); addFormatToken('k', ['kk', 2], 0, kFormat); addFormatToken('hmm', 0, 0, function () { return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); }); addFormatToken('hmmss', 0, 0, function () { return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + zeroFill(this.seconds(), 2); }); addFormatToken('Hmm', 0, 0, function () { return '' + this.hours() + zeroFill(this.minutes(), 2); }); addFormatToken('Hmmss', 0, 0, function () { return '' + this.hours() + zeroFill(this.minutes(), 2) + zeroFill(this.seconds(), 2); }); function meridiem (token, lowercase) { addFormatToken(token, 0, 0, function () { return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); }); } meridiem('a', true); meridiem('A', false); // ALIASES addUnitAlias('hour', 'h'); // PRIORITY addUnitPriority('hour', 13); // PARSING function matchMeridiem (isStrict, locale) { return locale._meridiemParse; } addRegexToken('a', matchMeridiem); addRegexToken('A', matchMeridiem); addRegexToken('H', match1to2); addRegexToken('h', match1to2); addRegexToken('k', match1to2); addRegexToken('HH', match1to2, match2); addRegexToken('hh', match1to2, match2); addRegexToken('kk', match1to2, match2); addRegexToken('hmm', match3to4); addRegexToken('hmmss', match5to6); addRegexToken('Hmm', match3to4); addRegexToken('Hmmss', match5to6); addParseToken(['H', 'HH'], HOUR); addParseToken(['k', 'kk'], function (input, array, config) { var kInput = toInt(input); array[HOUR] = kInput === 24 ? 0 : kInput; }); addParseToken(['a', 'A'], function (input, array, config) { config._isPm = config._locale.isPM(input); config._meridiem = input; }); addParseToken(['h', 'hh'], function (input, array, config) { array[HOUR] = toInt(input); getParsingFlags(config).bigHour = true; }); addParseToken('hmm', function (input, array, config) { var pos = input.length - 2; array[HOUR] = toInt(input.substr(0, pos)); array[MINUTE] = toInt(input.substr(pos)); getParsingFlags(config).bigHour = true; }); addParseToken('hmmss', function (input, array, config) { var pos1 = input.length - 4; var pos2 = input.length - 2; array[HOUR] = toInt(input.substr(0, pos1)); array[MINUTE] = toInt(input.substr(pos1, 2)); array[SECOND] = toInt(input.substr(pos2)); getParsingFlags(config).bigHour = true; }); addParseToken('Hmm', function (input, array, config) { var pos = input.length - 2; array[HOUR] = toInt(input.substr(0, pos)); array[MINUTE] = toInt(input.substr(pos)); }); addParseToken('Hmmss', function (input, array, config) { var pos1 = input.length - 4; var pos2 = input.length - 2; array[HOUR] = toInt(input.substr(0, pos1)); array[MINUTE] = toInt(input.substr(pos1, 2)); array[SECOND] = toInt(input.substr(pos2)); }); // LOCALES function localeIsPM (input) { // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays // Using charAt should be more compatible. return ((input + '').toLowerCase().charAt(0) === 'p'); } var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; function localeMeridiem (hours, minutes, isLower) { if (hours > 11) { return isLower ? 'pm' : 'PM'; } else { return isLower ? 'am' : 'AM'; } } // MOMENTS // Setting the hour should keep the time, because the user explicitly // specified which hour he wants. So trying to maintain the same hour (in // a new timezone) makes sense. Adding/subtracting hours does not follow // this rule. var getSetHour = makeGetSet('Hours', true); // months // week // weekdays // meridiem var baseConfig = { calendar: defaultCalendar, longDateFormat: defaultLongDateFormat, invalidDate: defaultInvalidDate, ordinal: defaultOrdinal, dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse, relativeTime: defaultRelativeTime, months: defaultLocaleMonths, monthsShort: defaultLocaleMonthsShort, week: defaultLocaleWeek, weekdays: defaultLocaleWeekdays, weekdaysMin: defaultLocaleWeekdaysMin, weekdaysShort: defaultLocaleWeekdaysShort, meridiemParse: defaultLocaleMeridiemParse }; // internal storage for locale config files var locales = {}; var localeFamilies = {}; var globalLocale; function normalizeLocale(key) { return key ? key.toLowerCase().replace('_', '-') : key; } // pick the locale from the array // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root function chooseLocale(names) { var i = 0, j, next, locale, split; while (i < names.length) { split = normalizeLocale(names[i]).split('-'); j = split.length; next = normalizeLocale(names[i + 1]); next = next ? next.split('-') : null; while (j > 0) { locale = loadLocale(split.slice(0, j).join('-')); if (locale) { return locale; } if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { //the next array item is better than a shallower substring of this one break; } j--; } i++; } return null; } function loadLocale(name) { var oldLocale = null; // TODO: Find a better way to register and load all the locales in Node if (!locales[name] && (typeof module !== 'undefined') && module && module.exports) { try { oldLocale = globalLocale._abbr; require('./locale/' + name); // because defineLocale currently also sets the global locale, we // want to undo that for lazy loaded locales getSetGlobalLocale(oldLocale); } catch (e) { } } return locales[name]; } // This function will load locale and then set the global locale. If // no arguments are passed in, it will simply return the current global // locale key. function getSetGlobalLocale (key, values) { var data; if (key) { if (isUndefined(values)) { data = getLocale(key); } else { data = defineLocale(key, values); } if (data) { // moment.duration._locale = moment._locale = data; globalLocale = data; } } return globalLocale._abbr; } function defineLocale (name, config) { if (config !== null) { var parentConfig = baseConfig; config.abbr = name; if (locales[name] != null) { deprecateSimple('defineLocaleOverride', 'use moment.updateLocale(localeName, config) to change ' + 'an existing locale. moment.defineLocale(localeName, ' + 'config) should only be used for creating a new locale ' + 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'); parentConfig = locales[name]._config; } else if (config.parentLocale != null) { if (locales[config.parentLocale] != null) { parentConfig = locales[config.parentLocale]._config; } else { if (!localeFamilies[config.parentLocale]) { localeFamilies[config.parentLocale] = []; } localeFamilies[config.parentLocale].push({ name: name, config: config }); return null; } } locales[name] = new Locale(mergeConfigs(parentConfig, config)); if (localeFamilies[name]) { localeFamilies[name].forEach(function (x) { defineLocale(x.name, x.config); }); } // backwards compat for now: also set the locale // make sure we set the locale AFTER all child locales have been // created, so we won't end up with the child locale set. getSetGlobalLocale(name); return locales[name]; } else { // useful for testing delete locales[name]; return null; } } function updateLocale(name, config) { if (config != null) { var locale, parentConfig = baseConfig; // MERGE if (locales[name] != null) { parentConfig = locales[name]._config; } config = mergeConfigs(parentConfig, config); locale = new Locale(config); locale.parentLocale = locales[name]; locales[name] = locale; // backwards compat for now: also set the locale getSetGlobalLocale(name); } else { // pass null for config to unupdate, useful for tests if (locales[name] != null) { if (locales[name].parentLocale != null) { locales[name] = locales[name].parentLocale; } else if (locales[name] != null) { delete locales[name]; } } } return locales[name]; } // returns locale data function getLocale (key) { var locale; if (key && key._locale && key._locale._abbr) { key = key._locale._abbr; } if (!key) { return globalLocale; } if (!isArray(key)) { //short-circuit everything else locale = loadLocale(key); if (locale) { return locale; } key = [key]; } return chooseLocale(key); } function listLocales() { return keys$1(locales); } function checkOverflow (m) { var overflow; var a = m._a; if (a && getParsingFlags(m).overflow === -2) { overflow = a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : -1; if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { overflow = DATE; } if (getParsingFlags(m)._overflowWeeks && overflow === -1) { overflow = WEEK; } if (getParsingFlags(m)._overflowWeekday && overflow === -1) { overflow = WEEKDAY; } getParsingFlags(m).overflow = overflow; } return m; } // iso 8601 regex // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/; var isoDates = [ ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], ['GGGG-[W]WW', /\d{4}-W\d\d/, false], ['YYYY-DDD', /\d{4}-\d{3}/], ['YYYY-MM', /\d{4}-\d\d/, false], ['YYYYYYMMDD', /[+-]\d{10}/], ['YYYYMMDD', /\d{8}/], // YYYYMM is NOT allowed by the standard ['GGGG[W]WWE', /\d{4}W\d{3}/], ['GGGG[W]WW', /\d{4}W\d{2}/, false], ['YYYYDDD', /\d{7}/] ]; // iso time formats and regexes var isoTimes = [ ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], ['HH:mm:ss', /\d\d:\d\d:\d\d/], ['HH:mm', /\d\d:\d\d/], ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], ['HHmmss', /\d\d\d\d\d\d/], ['HHmm', /\d\d\d\d/], ['HH', /\d\d/] ]; var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; // date from iso format function configFromISO(config) { var i, l, string = config._i, match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), allowTime, dateFormat, timeFormat, tzFormat; if (match) { getParsingFlags(config).iso = true; for (i = 0, l = isoDates.length; i < l; i++) { if (isoDates[i][1].exec(match[1])) { dateFormat = isoDates[i][0]; allowTime = isoDates[i][2] !== false; break; } } if (dateFormat == null) { config._isValid = false; return; } if (match[3]) { for (i = 0, l = isoTimes.length; i < l; i++) { if (isoTimes[i][1].exec(match[3])) { // match[2] should be 'T' or space timeFormat = (match[2] || ' ') + isoTimes[i][0]; break; } } if (timeFormat == null) { config._isValid = false; return; } } if (!allowTime && timeFormat != null) { config._isValid = false; return; } if (match[4]) { if (tzRegex.exec(match[4])) { tzFormat = 'Z'; } else { config._isValid = false; return; } } config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); configFromStringAndFormat(config); } else { config._isValid = false; } } // RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3 var basicRfcRegex = /^((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d?\d\s(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(?:\d\d)?\d\d\s)(\d\d:\d\d)(\:\d\d)?(\s(?:UT|GMT|[ECMP][SD]T|[A-IK-Za-ik-z]|[+-]\d{4}))$/; // date and time from ref 2822 format function configFromRFC2822(config) { var string, match, dayFormat, dateFormat, timeFormat, tzFormat; var timezones = { ' GMT': ' +0000', ' EDT': ' -0400', ' EST': ' -0500', ' CDT': ' -0500', ' CST': ' -0600', ' MDT': ' -0600', ' MST': ' -0700', ' PDT': ' -0700', ' PST': ' -0800' }; var military = 'YXWVUTSRQPONZABCDEFGHIKLM'; var timezone, timezoneIndex; string = config._i .replace(/\([^\)]*\)|[\n\t]/g, ' ') // Remove comments and folding whitespace .replace(/(\s\s+)/g, ' ') // Replace multiple-spaces with a single space .replace(/^\s|\s$/g, ''); // Remove leading and trailing spaces match = basicRfcRegex.exec(string); if (match) { dayFormat = match[1] ? 'ddd' + ((match[1].length === 5) ? ', ' : ' ') : ''; dateFormat = 'D MMM ' + ((match[2].length > 10) ? 'YYYY ' : 'YY '); timeFormat = 'HH:mm' + (match[4] ? ':ss' : ''); // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check. if (match[1]) { // day of week given var momentDate = new Date(match[2]); var momentDay = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][momentDate.getDay()]; if (match[1].substr(0,3) !== momentDay) { getParsingFlags(config).weekdayMismatch = true; config._isValid = false; return; } } switch (match[5].length) { case 2: // military if (timezoneIndex === 0) { timezone = ' +0000'; } else { timezoneIndex = military.indexOf(match[5][1].toUpperCase()) - 12; timezone = ((timezoneIndex < 0) ? ' -' : ' +') + (('' + timezoneIndex).replace(/^-?/, '0')).match(/..$/)[0] + '00'; } break; case 4: // Zone timezone = timezones[match[5]]; break; default: // UT or +/-9999 timezone = timezones[' GMT']; } match[5] = timezone; config._i = match.splice(1).join(''); tzFormat = ' ZZ'; config._f = dayFormat + dateFormat + timeFormat + tzFormat; configFromStringAndFormat(config); getParsingFlags(config).rfc2822 = true; } else { config._isValid = false; } } // date from iso format or fallback function configFromString(config) { var matched = aspNetJsonRegex.exec(config._i); if (matched !== null) { config._d = new Date(+matched[1]); return; } configFromISO(config); if (config._isValid === false) { delete config._isValid; } else { return; } configFromRFC2822(config); if (config._isValid === false) { delete config._isValid; } else { return; } // Final attempt, use Input Fallback hooks.createFromInputFallback(config); } hooks.createFromInputFallback = deprecate( 'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + 'discouraged and will be removed in an upcoming major release. Please refer to ' + 'http://momentjs.com/guides/#/warnings/js-date/ for more info.', function (config) { config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); } ); // Pick the first defined of two or three arguments. function defaults(a, b, c) { if (a != null) { return a; } if (b != null) { return b; } return c; } function currentDateArray(config) { // hooks is actually the exported moment object var nowValue = new Date(hooks.now()); if (config._useUTC) { return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()]; } return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; } // convert an array to a date. // the array should mirror the parameters below // note: all values past the year are optional and will default to the lowest possible value. // [year, month, day , hour, minute, second, millisecond] function configFromArray (config) { var i, date, input = [], currentDate, yearToUse; if (config._d) { return; } currentDate = currentDateArray(config); //compute day of the year from weeks and weekdays if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { dayOfYearFromWeekInfo(config); } //if the day of the year is set, figure out what it is if (config._dayOfYear != null) { yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) { getParsingFlags(config)._overflowDayOfYear = true; } date = createUTCDate(yearToUse, 0, config._dayOfYear); config._a[MONTH] = date.getUTCMonth(); config._a[DATE] = date.getUTCDate(); } // Default to current date. // * if no year, month, day of month are given, default to today // * if day of month is given, default month and year // * if month is given, default only year // * if year is given, don't default anything for (i = 0; i < 3 && config._a[i] == null; ++i) { config._a[i] = input[i] = currentDate[i]; } // Zero out whatever was not defaulted, including time for (; i < 7; i++) { config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; } // Check for 24:00:00.000 if (config._a[HOUR] === 24 && config._a[MINUTE] === 0 && config._a[SECOND] === 0 && config._a[MILLISECOND] === 0) { config._nextDay = true; config._a[HOUR] = 0; } config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); // Apply timezone offset from input. The actual utcOffset can be changed // with parseZone. if (config._tzm != null) { config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); } if (config._nextDay) { config._a[HOUR] = 24; } } function dayOfYearFromWeekInfo(config) { var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow; w = config._w; if (w.GG != null || w.W != null || w.E != null) { dow = 1; doy = 4; // TODO: We need to take the current isoWeekYear, but that depends on // how we interpret now (local, utc, fixed offset). So create // a now version of current config (take local/utc/offset flags, and // create now). weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year); week = defaults(w.W, 1); weekday = defaults(w.E, 1); if (weekday < 1 || weekday > 7) { weekdayOverflow = true; } } else { dow = config._locale._week.dow; doy = config._locale._week.doy; var curWeek = weekOfYear(createLocal(), dow, doy); weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); // Default to current week. week = defaults(w.w, curWeek.week); if (w.d != null) { // weekday -- low day numbers are considered next week weekday = w.d; if (weekday < 0 || weekday > 6) { weekdayOverflow = true; } } else if (w.e != null) { // local weekday -- counting starts from begining of week weekday = w.e + dow; if (w.e < 0 || w.e > 6) { weekdayOverflow = true; } } else { // default to begining of week weekday = dow; } } if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { getParsingFlags(config)._overflowWeeks = true; } else if (weekdayOverflow != null) { getParsingFlags(config)._overflowWeekday = true; } else { temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); config._a[YEAR] = temp.year; config._dayOfYear = temp.dayOfYear; } } // constant that refers to the ISO standard hooks.ISO_8601 = function () {}; // constant that refers to the RFC 2822 form hooks.RFC_2822 = function () {}; // date from string and format string function configFromStringAndFormat(config) { // TODO: Move this to another part of the creation flow to prevent circular deps if (config._f === hooks.ISO_8601) { configFromISO(config); return; } if (config._f === hooks.RFC_2822) { configFromRFC2822(config); return; } config._a = []; getParsingFlags(config).empty = true; // This array is used to make a Date, either with `new Date` or `Date.UTC` var string = '' + config._i, i, parsedInput, tokens, token, skipped, stringLength = string.length, totalParsedInputLength = 0; tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; for (i = 0; i < tokens.length; i++) { token = tokens[i]; parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; // console.log('token', token, 'parsedInput', parsedInput, // 'regex', getParseRegexForToken(token, config)); if (parsedInput) { skipped = string.substr(0, string.indexOf(parsedInput)); if (skipped.length > 0) { getParsingFlags(config).unusedInput.push(skipped); } string = string.slice(string.indexOf(parsedInput) + parsedInput.length); totalParsedInputLength += parsedInput.length; } // don't parse if it's not a known token if (formatTokenFunctions[token]) { if (parsedInput) { getParsingFlags(config).empty = false; } else { getParsingFlags(config).unusedTokens.push(token); } addTimeToArrayFromToken(token, parsedInput, config); } else if (config._strict && !parsedInput) { getParsingFlags(config).unusedTokens.push(token); } } // add remaining unparsed input length to the string getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; if (string.length > 0) { getParsingFlags(config).unusedInput.push(string); } // clear _12h flag if hour is <= 12 if (config._a[HOUR] <= 12 && getParsingFlags(config).bigHour === true && config._a[HOUR] > 0) { getParsingFlags(config).bigHour = undefined; } getParsingFlags(config).parsedDateParts = config._a.slice(0); getParsingFlags(config).meridiem = config._meridiem; // handle meridiem config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); configFromArray(config); checkOverflow(config); } function meridiemFixWrap (locale, hour, meridiem) { var isPm; if (meridiem == null) { // nothing to do return hour; } if (locale.meridiemHour != null) { return locale.meridiemHour(hour, meridiem); } else if (locale.isPM != null) { // Fallback isPm = locale.isPM(meridiem); if (isPm && hour < 12) { hour += 12; } if (!isPm && hour === 12) { hour = 0; } return hour; } else { // this is not supposed to happen return hour; } } // date from string and array of format strings function configFromStringAndArray(config) { var tempConfig, bestMoment, scoreToBeat, i, currentScore; if (config._f.length === 0) { getParsingFlags(config).invalidFormat = true; config._d = new Date(NaN); return; } for (i = 0; i < config._f.length; i++) { currentScore = 0; tempConfig = copyConfig({}, config); if (config._useUTC != null) { tempConfig._useUTC = config._useUTC; } tempConfig._f = config._f[i]; configFromStringAndFormat(tempConfig); if (!isValid(tempConfig)) { continue; } // if there is any input that was not parsed add a penalty for that format currentScore += getParsingFlags(tempConfig).charsLeftOver; //or tokens currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; getParsingFlags(tempConfig).score = currentScore; if (scoreToBeat == null || currentScore < scoreToBeat) { scoreToBeat = currentScore; bestMoment = tempConfig; } } extend(config, bestMoment || tempConfig); } function configFromObject(config) { if (config._d) { return; } var i = normalizeObjectUnits(config._i); config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) { return obj && parseInt(obj, 10); }); configFromArray(config); } function createFromConfig (config) { var res = new Moment(checkOverflow(prepareConfig(config))); if (res._nextDay) { // Adding is smart enough around DST res.add(1, 'd'); res._nextDay = undefined; } return res; } function prepareConfig (config) { var input = config._i, format = config._f; config._locale = config._locale || getLocale(config._l); if (input === null || (format === undefined && input === '')) { return createInvalid({nullInput: true}); } if (typeof input === 'string') { config._i = input = config._locale.preparse(input); } if (isMoment(input)) { return new Moment(checkOverflow(input)); } else if (isDate(input)) { config._d = input; } else if (isArray(format)) { configFromStringAndArray(config); } else if (format) { configFromStringAndFormat(config); } else { configFromInput(config); } if (!isValid(config)) { config._d = null; } return config; } function configFromInput(config) { var input = config._i; if (isUndefined(input)) { config._d = new Date(hooks.now()); } else if (isDate(input)) { config._d = new Date(input.valueOf()); } else if (typeof input === 'string') { configFromString(config); } else if (isArray(input)) { config._a = map(input.slice(0), function (obj) { return parseInt(obj, 10); }); configFromArray(config); } else if (isObject(input)) { configFromObject(config); } else if (isNumber(input)) { // from milliseconds config._d = new Date(input); } else { hooks.createFromInputFallback(config); } } function createLocalOrUTC (input, format, locale, strict, isUTC) { var c = {}; if (locale === true || locale === false) { strict = locale; locale = undefined; } if ((isObject(input) && isObjectEmpty(input)) || (isArray(input) && input.length === 0)) { input = undefined; } // object construction must be done this way. // https://github.com/moment/moment/issues/1423 c._isAMomentObject = true; c._useUTC = c._isUTC = isUTC; c._l = locale; c._i = input; c._f = format; c._strict = strict; return createFromConfig(c); } function createLocal (input, format, locale, strict) { return createLocalOrUTC(input, format, locale, strict, false); } var prototypeMin = deprecate( 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', function () { var other = createLocal.apply(null, arguments); if (this.isValid() && other.isValid()) { return other < this ? this : other; } else { return createInvalid(); } } ); var prototypeMax = deprecate( 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', function () { var other = createLocal.apply(null, arguments); if (this.isValid() && other.isValid()) { return other > this ? this : other; } else { return createInvalid(); } } ); // Pick a moment m from moments so that m[fn](other) is true for all // other. This relies on the function fn to be transitive. // // moments should either be an array of moment objects or an array, whose // first element is an array of moment objects. function pickBy(fn, moments) { var res, i; if (moments.length === 1 && isArray(moments[0])) { moments = moments[0]; } if (!moments.length) { return createLocal(); } res = moments[0]; for (i = 1; i < moments.length; ++i) { if (!moments[i].isValid() || moments[i][fn](res)) { res = moments[i]; } } return res; } // TODO: Use [].sort instead? function min () { var args = [].slice.call(arguments, 0); return pickBy('isBefore', args); } function max () { var args = [].slice.call(arguments, 0); return pickBy('isAfter', args); } var now = function () { return Date.now ? Date.now() : +(new Date()); }; var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond']; function isDurationValid(m) { for (var key in m) { if (!(ordering.indexOf(key) !== -1 && (m[key] == null || !isNaN(m[key])))) { return false; } } var unitHasDecimal = false; for (var i = 0; i < ordering.length; ++i) { if (m[ordering[i]]) { if (unitHasDecimal) { return false; // only allow non-integers for smallest unit } if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) { unitHasDecimal = true; } } } return true; } function isValid$1() { return this._isValid; } function createInvalid$1() { return createDuration(NaN); } function Duration (duration) { var normalizedInput = normalizeObjectUnits(duration), years = normalizedInput.year || 0, quarters = normalizedInput.quarter || 0, months = normalizedInput.month || 0, weeks = normalizedInput.week || 0, days = normalizedInput.day || 0, hours = normalizedInput.hour || 0, minutes = normalizedInput.minute || 0, seconds = normalizedInput.second || 0, milliseconds = normalizedInput.millisecond || 0; this._isValid = isDurationValid(normalizedInput); // representation for dateAddRemove this._milliseconds = +milliseconds + seconds * 1e3 + // 1000 minutes * 6e4 + // 1000 * 60 hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 // Because of dateAddRemove treats 24 hours as different from a // day when working around DST, we need to store them separately this._days = +days + weeks * 7; // It is impossible translate months into days without knowing // which months you are are talking about, so we have to store // it separately. this._months = +months + quarters * 3 + years * 12; this._data = {}; this._locale = getLocale(); this._bubble(); } function isDuration (obj) { return obj instanceof Duration; } function absRound (number) { if (number < 0) { return Math.round(-1 * number) * -1; } else { return Math.round(number); } } // FORMATTING function offset (token, separator) { addFormatToken(token, 0, 0, function () { var offset = this.utcOffset(); var sign = '+'; if (offset < 0) { offset = -offset; sign = '-'; } return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); }); } offset('Z', ':'); offset('ZZ', ''); // PARSING addRegexToken('Z', matchShortOffset); addRegexToken('ZZ', matchShortOffset); addParseToken(['Z', 'ZZ'], function (input, array, config) { config._useUTC = true; config._tzm = offsetFromString(matchShortOffset, input); }); // HELPERS // timezone chunker // '+10:00' > ['10', '00'] // '-1530' > ['-15', '30'] var chunkOffset = /([\+\-]|\d\d)/gi; function offsetFromString(matcher, string) { var matches = (string || '').match(matcher); if (matches === null) { return null; } var chunk = matches[matches.length - 1] || []; var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; var minutes = +(parts[1] * 60) + toInt(parts[2]); return minutes === 0 ? 0 : parts[0] === '+' ? minutes : -minutes; } // Return a moment from input, that is local/utc/zone equivalent to model. function cloneWithOffset(input, model) { var res, diff; if (model._isUTC) { res = model.clone(); diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); // Use low-level api, because this fn is low-level api. res._d.setTime(res._d.valueOf() + diff); hooks.updateOffset(res, false); return res; } else { return createLocal(input).local(); } } function getDateOffset (m) { // On Firefox.24 Date#getTimezoneOffset returns a floating point. // https://github.com/moment/moment/pull/1871 return -Math.round(m._d.getTimezoneOffset() / 15) * 15; } // HOOKS // This function will be called whenever a moment is mutated. // It is intended to keep the offset in sync with the timezone. hooks.updateOffset = function () {}; // MOMENTS // keepLocalTime = true means only change the timezone, without // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset // +0200, so we adjust the time as needed, to be valid. // // Keeping the time actually adds/subtracts (one hour) // from the actual represented time. That is why we call updateOffset // a second time. In case it wants us to change the offset again // _changeInProgress == true case, then we have to adjust, because // there is no such time in the given timezone. function getSetOffset (input, keepLocalTime, keepMinutes) { var offset = this._offset || 0, localAdjust; if (!this.isValid()) { return input != null ? this : NaN; } if (input != null) { if (typeof input === 'string') { input = offsetFromString(matchShortOffset, input); if (input === null) { return this; } } else if (Math.abs(input) < 16 && !keepMinutes) { input = input * 60; } if (!this._isUTC && keepLocalTime) { localAdjust = getDateOffset(this); } this._offset = input; this._isUTC = true; if (localAdjust != null) { this.add(localAdjust, 'm'); } if (offset !== input) { if (!keepLocalTime || this._changeInProgress) { addSubtract(this, createDuration(input - offset, 'm'), 1, false); } else if (!this._changeInProgress) { this._changeInProgress = true; hooks.updateOffset(this, true); this._changeInProgress = null; } } return this; } else { return this._isUTC ? offset : getDateOffset(this); } } function getSetZone (input, keepLocalTime) { if (input != null) { if (typeof input !== 'string') { input = -input; } this.utcOffset(input, keepLocalTime); return this; } else { return -this.utcOffset(); } } function setOffsetToUTC (keepLocalTime) { return this.utcOffset(0, keepLocalTime); } function setOffsetToLocal (keepLocalTime) { if (this._isUTC) { this.utcOffset(0, keepLocalTime); this._isUTC = false; if (keepLocalTime) { this.subtract(getDateOffset(this), 'm'); } } return this; } function setOffsetToParsedOffset () { if (this._tzm != null) { this.utcOffset(this._tzm, false, true); } else if (typeof this._i === 'string') { var tZone = offsetFromString(matchOffset, this._i); if (tZone != null) { this.utcOffset(tZone); } else { this.utcOffset(0, true); } } return this; } function hasAlignedHourOffset (input) { if (!this.isValid()) { return false; } input = input ? createLocal(input).utcOffset() : 0; return (this.utcOffset() - input) % 60 === 0; } function isDaylightSavingTime () { return ( this.utcOffset() > this.clone().month(0).utcOffset() || this.utcOffset() > this.clone().month(5).utcOffset() ); } function isDaylightSavingTimeShifted () { if (!isUndefined(this._isDSTShifted)) { return this._isDSTShifted; } var c = {}; copyConfig(c, this); c = prepareConfig(c); if (c._a) { var other = c._isUTC ? createUTC(c._a) : createLocal(c._a); this._isDSTShifted = this.isValid() && compareArrays(c._a, other.toArray()) > 0; } else { this._isDSTShifted = false; } return this._isDSTShifted; } function isLocal () { return this.isValid() ? !this._isUTC : false; } function isUtcOffset () { return this.isValid() ? this._isUTC : false; } function isUtc () { return this.isValid() ? this._isUTC && this._offset === 0 : false; } // ASP.NET json date format regex var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/; // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere // and further modified to allow for strings containing both week and day var isoRegex = /^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/; function createDuration (input, key) { var duration = input, // matching against regexp is expensive, do it on demand match = null, sign, ret, diffRes; if (isDuration(input)) { duration = { ms : input._milliseconds, d : input._days, M : input._months }; } else if (isNumber(input)) { duration = {}; if (key) { duration[key] = input; } else { duration.milliseconds = input; } } else if (!!(match = aspNetRegex.exec(input))) { sign = (match[1] === '-') ? -1 : 1; duration = { y : 0, d : toInt(match[DATE]) * sign, h : toInt(match[HOUR]) * sign, m : toInt(match[MINUTE]) * sign, s : toInt(match[SECOND]) * sign, ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match }; } else if (!!(match = isoRegex.exec(input))) { sign = (match[1] === '-') ? -1 : 1; duration = { y : parseIso(match[2], sign), M : parseIso(match[3], sign), w : parseIso(match[4], sign), d : parseIso(match[5], sign), h : parseIso(match[6], sign), m : parseIso(match[7], sign), s : parseIso(match[8], sign) }; } else if (duration == null) {// checks for null or undefined duration = {}; } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to)); duration = {}; duration.ms = diffRes.milliseconds; duration.M = diffRes.months; } ret = new Duration(duration); if (isDuration(input) && hasOwnProp(input, '_locale')) { ret._locale = input._locale; } return ret; } createDuration.fn = Duration.prototype; createDuration.invalid = createInvalid$1; function parseIso (inp, sign) { // We'd normally use ~~inp for this, but unfortunately it also // converts floats to ints. // inp may be undefined, so careful calling replace on it. var res = inp && parseFloat(inp.replace(',', '.')); // apply sign while we're at it return (isNaN(res) ? 0 : res) * sign; } function positiveMomentsDifference(base, other) { var res = {milliseconds: 0, months: 0}; res.months = other.month() - base.month() + (other.year() - base.year()) * 12; if (base.clone().add(res.months, 'M').isAfter(other)) { --res.months; } res.milliseconds = +other - +(base.clone().add(res.months, 'M')); return res; } function momentsDifference(base, other) { var res; if (!(base.isValid() && other.isValid())) { return {milliseconds: 0, months: 0}; } other = cloneWithOffset(other, base); if (base.isBefore(other)) { res = positiveMomentsDifference(base, other); } else { res = positiveMomentsDifference(other, base); res.milliseconds = -res.milliseconds; res.months = -res.months; } return res; } // TODO: remove 'name' arg after deprecation is removed function createAdder(direction, name) { return function (val, period) { var dur, tmp; //invert the arguments, but complain about it if (period !== null && !isNaN(+period)) { deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.'); tmp = val; val = period; period = tmp; } val = typeof val === 'string' ? +val : val; dur = createDuration(val, period); addSubtract(this, dur, direction); return this; }; } function addSubtract (mom, duration, isAdding, updateOffset) { var milliseconds = duration._milliseconds, days = absRound(duration._days), months = absRound(duration._months); if (!mom.isValid()) { // No op return; } updateOffset = updateOffset == null ? true : updateOffset; if (milliseconds) { mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); } if (days) { set$1(mom, 'Date', get(mom, 'Date') + days * isAdding); } if (months) { setMonth(mom, get(mom, 'Month') + months * isAdding); } if (updateOffset) { hooks.updateOffset(mom, days || months); } } var add = createAdder(1, 'add'); var subtract = createAdder(-1, 'subtract'); function getCalendarFormat(myMoment, now) { var diff = myMoment.diff(now, 'days', true); return diff < -6 ? 'sameElse' : diff < -1 ? 'lastWeek' : diff < 0 ? 'lastDay' : diff < 1 ? 'sameDay' : diff < 2 ? 'nextDay' : diff < 7 ? 'nextWeek' : 'sameElse'; } function calendar$1 (time, formats) { // We want to compare the start of today, vs this. // Getting start-of-today depends on whether we're local/utc/offset or not. var now = time || createLocal(), sod = cloneWithOffset(now, this).startOf('day'), format = hooks.calendarFormat(this, sod) || 'sameElse'; var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]); return this.format(output || this.localeData().calendar(format, this, createLocal(now))); } function clone () { return new Moment(this); } function isAfter (input, units) { var localInput = isMoment(input) ? input : createLocal(input); if (!(this.isValid() && localInput.isValid())) { return false; } units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); if (units === 'millisecond') { return this.valueOf() > localInput.valueOf(); } else { return localInput.valueOf() < this.clone().startOf(units).valueOf(); } } function isBefore (input, units) { var localInput = isMoment(input) ? input : createLocal(input); if (!(this.isValid() && localInput.isValid())) { return false; } units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); if (units === 'millisecond') { return this.valueOf() < localInput.valueOf(); } else { return this.clone().endOf(units).valueOf() < localInput.valueOf(); } } function isBetween (from, to, units, inclusivity) { inclusivity = inclusivity || '()'; return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) && (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units)); } function isSame (input, units) { var localInput = isMoment(input) ? input : createLocal(input), inputMs; if (!(this.isValid() && localInput.isValid())) { return false; } units = normalizeUnits(units || 'millisecond'); if (units === 'millisecond') { return this.valueOf() === localInput.valueOf(); } else { inputMs = localInput.valueOf(); return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf(); } } function isSameOrAfter (input, units) { return this.isSame(input, units) || this.isAfter(input,units); } function isSameOrBefore (input, units) { return this.isSame(input, units) || this.isBefore(input,units); } function diff (input, units, asFloat) { var that, zoneDelta, delta, output; if (!this.isValid()) { return NaN; } that = cloneWithOffset(input, this); if (!that.isValid()) { return NaN; } zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; units = normalizeUnits(units); if (units === 'year' || units === 'month' || units === 'quarter') { output = monthDiff(this, that); if (units === 'quarter') { output = output / 3; } else if (units === 'year') { output = output / 12; } } else { delta = this - that; output = units === 'second' ? delta / 1e3 : // 1000 units === 'minute' ? delta / 6e4 : // 1000 * 60 units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60 units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst delta; } return asFloat ? output : absFloor(output); } function monthDiff (a, b) { // difference in months var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), // b is in (anchor - 1 month, anchor + 1 month) anchor = a.clone().add(wholeMonthDiff, 'months'), anchor2, adjust; if (b - anchor < 0) { anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); // linear across the month adjust = (b - anchor) / (anchor - anchor2); } else { anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); // linear across the month adjust = (b - anchor) / (anchor2 - anchor); } //check for negative zero, return zero if negative zero return -(wholeMonthDiff + adjust) || 0; } hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; function toString () { return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); } function toISOString() { if (!this.isValid()) { return null; } var m = this.clone().utc(); if (m.year() < 0 || m.year() > 9999) { return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); } if (isFunction(Date.prototype.toISOString)) { // native implementation is ~50x faster, use it when we can return this.toDate().toISOString(); } return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); } /** * Return a human readable representation of a moment that can * also be evaluated to get a new moment which is the same * * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects */ function inspect () { if (!this.isValid()) { return 'moment.invalid(/* ' + this._i + ' */)'; } var func = 'moment'; var zone = ''; if (!this.isLocal()) { func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone'; zone = 'Z'; } var prefix = '[' + func + '("]'; var year = (0 <= this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY'; var datetime = '-MM-DD[T]HH:mm:ss.SSS'; var suffix = zone + '[")]'; return this.format(prefix + year + datetime + suffix); } function format (inputString) { if (!inputString) { inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat; } var output = formatMoment(this, inputString); return this.localeData().postformat(output); } function from (time, withoutSuffix) { if (this.isValid() && ((isMoment(time) && time.isValid()) || createLocal(time).isValid())) { return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); } else { return this.localeData().invalidDate(); } } function fromNow (withoutSuffix) { return this.from(createLocal(), withoutSuffix); } function to (time, withoutSuffix) { if (this.isValid() && ((isMoment(time) && time.isValid()) || createLocal(time).isValid())) { return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix); } else { return this.localeData().invalidDate(); } } function toNow (withoutSuffix) { return this.to(createLocal(), withoutSuffix); } // If passed a locale key, it will set the locale for this // instance. Otherwise, it will return the locale configuration // variables for this instance. function locale (key) { var newLocaleData; if (key === undefined) { return this._locale._abbr; } else { newLocaleData = getLocale(key); if (newLocaleData != null) { this._locale = newLocaleData; } return this; } } var lang = deprecate( 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', function (key) { if (key === undefined) { return this.localeData(); } else { return this.locale(key); } } ); function localeData () { return this._locale; } function startOf (units) { units = normalizeUnits(units); // the following switch intentionally omits break keywords // to utilize falling through the cases. switch (units) { case 'year': this.month(0); /* falls through */ case 'quarter': case 'month': this.date(1); /* falls through */ case 'week': case 'isoWeek': case 'day': case 'date': this.hours(0); /* falls through */ case 'hour': this.minutes(0); /* falls through */ case 'minute': this.seconds(0); /* falls through */ case 'second': this.milliseconds(0); } // weeks are a special case if (units === 'week') { this.weekday(0); } if (units === 'isoWeek') { this.isoWeekday(1); } // quarters are also special if (units === 'quarter') { this.month(Math.floor(this.month() / 3) * 3); } return this; } function endOf (units) { units = normalizeUnits(units); if (units === undefined || units === 'millisecond') { return this; } // 'date' is an alias for 'day', so it should be considered as such. if (units === 'date') { units = 'day'; } return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); } function valueOf () { return this._d.valueOf() - ((this._offset || 0) * 60000); } function unix () { return Math.floor(this.valueOf() / 1000); } function toDate () { return new Date(this.valueOf()); } function toArray () { var m = this; return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; } function toObject () { var m = this; return { years: m.year(), months: m.month(), date: m.date(), hours: m.hours(), minutes: m.minutes(), seconds: m.seconds(), milliseconds: m.milliseconds() }; } function toJSON () { // new Date(NaN).toJSON() === null return this.isValid() ? this.toISOString() : null; } function isValid$2 () { return isValid(this); } function parsingFlags () { return extend({}, getParsingFlags(this)); } function invalidAt () { return getParsingFlags(this).overflow; } function creationData() { return { input: this._i, format: this._f, locale: this._locale, isUTC: this._isUTC, strict: this._strict }; } // FORMATTING addFormatToken(0, ['gg', 2], 0, function () { return this.weekYear() % 100; }); addFormatToken(0, ['GG', 2], 0, function () { return this.isoWeekYear() % 100; }); function addWeekYearFormatToken (token, getter) { addFormatToken(0, [token, token.length], 0, getter); } addWeekYearFormatToken('gggg', 'weekYear'); addWeekYearFormatToken('ggggg', 'weekYear'); addWeekYearFormatToken('GGGG', 'isoWeekYear'); addWeekYearFormatToken('GGGGG', 'isoWeekYear'); // ALIASES addUnitAlias('weekYear', 'gg'); addUnitAlias('isoWeekYear', 'GG'); // PRIORITY addUnitPriority('weekYear', 1); addUnitPriority('isoWeekYear', 1); // PARSING addRegexToken('G', matchSigned); addRegexToken('g', matchSigned); addRegexToken('GG', match1to2, match2); addRegexToken('gg', match1to2, match2); addRegexToken('GGGG', match1to4, match4); addRegexToken('gggg', match1to4, match4); addRegexToken('GGGGG', match1to6, match6); addRegexToken('ggggg', match1to6, match6); addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { week[token.substr(0, 2)] = toInt(input); }); addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { week[token] = hooks.parseTwoDigitYear(input); }); // MOMENTS function getSetWeekYear (input) { return getSetWeekYearHelper.call(this, input, this.week(), this.weekday(), this.localeData()._week.dow, this.localeData()._week.doy); } function getSetISOWeekYear (input) { return getSetWeekYearHelper.call(this, input, this.isoWeek(), this.isoWeekday(), 1, 4); } function getISOWeeksInYear () { return weeksInYear(this.year(), 1, 4); } function getWeeksInYear () { var weekInfo = this.localeData()._week; return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); } function getSetWeekYearHelper(input, week, weekday, dow, doy) { var weeksTarget; if (input == null) { return weekOfYear(this, dow, doy).year; } else { weeksTarget = weeksInYear(input, dow, doy); if (week > weeksTarget) { week = weeksTarget; } return setWeekAll.call(this, input, week, weekday, dow, doy); } } function setWeekAll(weekYear, week, weekday, dow, doy) { var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); this.year(date.getUTCFullYear()); this.month(date.getUTCMonth()); this.date(date.getUTCDate()); return this; } // FORMATTING addFormatToken('Q', 0, 'Qo', 'quarter'); // ALIASES addUnitAlias('quarter', 'Q'); // PRIORITY addUnitPriority('quarter', 7); // PARSING addRegexToken('Q', match1); addParseToken('Q', function (input, array) { array[MONTH] = (toInt(input) - 1) * 3; }); // MOMENTS function getSetQuarter (input) { return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); } // FORMATTING addFormatToken('D', ['DD', 2], 'Do', 'date'); // ALIASES addUnitAlias('date', 'D'); // PRIOROITY addUnitPriority('date', 9); // PARSING addRegexToken('D', match1to2); addRegexToken('DD', match1to2, match2); addRegexToken('Do', function (isStrict, locale) { // TODO: Remove "ordinalParse" fallback in next major release. return isStrict ? (locale._dayOfMonthOrdinalParse || locale._ordinalParse) : locale._dayOfMonthOrdinalParseLenient; }); addParseToken(['D', 'DD'], DATE); addParseToken('Do', function (input, array) { array[DATE] = toInt(input.match(match1to2)[0], 10); }); // MOMENTS var getSetDayOfMonth = makeGetSet('Date', true); // FORMATTING addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); // ALIASES addUnitAlias('dayOfYear', 'DDD'); // PRIORITY addUnitPriority('dayOfYear', 4); // PARSING addRegexToken('DDD', match1to3); addRegexToken('DDDD', match3); addParseToken(['DDD', 'DDDD'], function (input, array, config) { config._dayOfYear = toInt(input); }); // HELPERS // MOMENTS function getSetDayOfYear (input) { var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); } // FORMATTING addFormatToken('m', ['mm', 2], 0, 'minute'); // ALIASES addUnitAlias('minute', 'm'); // PRIORITY addUnitPriority('minute', 14); // PARSING addRegexToken('m', match1to2); addRegexToken('mm', match1to2, match2); addParseToken(['m', 'mm'], MINUTE); // MOMENTS var getSetMinute = makeGetSet('Minutes', false); // FORMATTING addFormatToken('s', ['ss', 2], 0, 'second'); // ALIASES addUnitAlias('second', 's'); // PRIORITY addUnitPriority('second', 15); // PARSING addRegexToken('s', match1to2); addRegexToken('ss', match1to2, match2); addParseToken(['s', 'ss'], SECOND); // MOMENTS var getSetSecond = makeGetSet('Seconds', false); // FORMATTING addFormatToken('S', 0, 0, function () { return ~~(this.millisecond() / 100); }); addFormatToken(0, ['SS', 2], 0, function () { return ~~(this.millisecond() / 10); }); addFormatToken(0, ['SSS', 3], 0, 'millisecond'); addFormatToken(0, ['SSSS', 4], 0, function () { return this.millisecond() * 10; }); addFormatToken(0, ['SSSSS', 5], 0, function () { return this.millisecond() * 100; }); addFormatToken(0, ['SSSSSS', 6], 0, function () { return this.millisecond() * 1000; }); addFormatToken(0, ['SSSSSSS', 7], 0, function () { return this.millisecond() * 10000; }); addFormatToken(0, ['SSSSSSSS', 8], 0, function () { return this.millisecond() * 100000; }); addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { return this.millisecond() * 1000000; }); // ALIASES addUnitAlias('millisecond', 'ms'); // PRIORITY addUnitPriority('millisecond', 16); // PARSING addRegexToken('S', match1to3, match1); addRegexToken('SS', match1to3, match2); addRegexToken('SSS', match1to3, match3); var token; for (token = 'SSSS'; token.length <= 9; token += 'S') { addRegexToken(token, matchUnsigned); } function parseMs(input, array) { array[MILLISECOND] = toInt(('0.' + input) * 1000); } for (token = 'S'; token.length <= 9; token += 'S') { addParseToken(token, parseMs); } // MOMENTS var getSetMillisecond = makeGetSet('Milliseconds', false); // FORMATTING addFormatToken('z', 0, 0, 'zoneAbbr'); addFormatToken('zz', 0, 0, 'zoneName'); // MOMENTS function getZoneAbbr () { return this._isUTC ? 'UTC' : ''; } function getZoneName () { return this._isUTC ? 'Coordinated Universal Time' : ''; } var proto = Moment.prototype; proto.add = add; proto.calendar = calendar$1; proto.clone = clone; proto.diff = diff; proto.endOf = endOf; proto.format = format; proto.from = from; proto.fromNow = fromNow; proto.to = to; proto.toNow = toNow; proto.get = stringGet; proto.invalidAt = invalidAt; proto.isAfter = isAfter; proto.isBefore = isBefore; proto.isBetween = isBetween; proto.isSame = isSame; proto.isSameOrAfter = isSameOrAfter; proto.isSameOrBefore = isSameOrBefore; proto.isValid = isValid$2; proto.lang = lang; proto.locale = locale; proto.localeData = localeData; proto.max = prototypeMax; proto.min = prototypeMin; proto.parsingFlags = parsingFlags; proto.set = stringSet; proto.startOf = startOf; proto.subtract = subtract; proto.toArray = toArray; proto.toObject = toObject; proto.toDate = toDate; proto.toISOString = toISOString; proto.inspect = inspect; proto.toJSON = toJSON; proto.toString = toString; proto.unix = unix; proto.valueOf = valueOf; proto.creationData = creationData; // Year proto.year = getSetYear; proto.isLeapYear = getIsLeapYear; // Week Year proto.weekYear = getSetWeekYear; proto.isoWeekYear = getSetISOWeekYear; // Quarter proto.quarter = proto.quarters = getSetQuarter; // Month proto.month = getSetMonth; proto.daysInMonth = getDaysInMonth; // Week proto.week = proto.weeks = getSetWeek; proto.isoWeek = proto.isoWeeks = getSetISOWeek; proto.weeksInYear = getWeeksInYear; proto.isoWeeksInYear = getISOWeeksInYear; // Day proto.date = getSetDayOfMonth; proto.day = proto.days = getSetDayOfWeek; proto.weekday = getSetLocaleDayOfWeek; proto.isoWeekday = getSetISODayOfWeek; proto.dayOfYear = getSetDayOfYear; // Hour proto.hour = proto.hours = getSetHour; // Minute proto.minute = proto.minutes = getSetMinute; // Second proto.second = proto.seconds = getSetSecond; // Millisecond proto.millisecond = proto.milliseconds = getSetMillisecond; // Offset proto.utcOffset = getSetOffset; proto.utc = setOffsetToUTC; proto.local = setOffsetToLocal; proto.parseZone = setOffsetToParsedOffset; proto.hasAlignedHourOffset = hasAlignedHourOffset; proto.isDST = isDaylightSavingTime; proto.isLocal = isLocal; proto.isUtcOffset = isUtcOffset; proto.isUtc = isUtc; proto.isUTC = isUtc; // Timezone proto.zoneAbbr = getZoneAbbr; proto.zoneName = getZoneName; // Deprecations proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone); proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted); function createUnix (input) { return createLocal(input * 1000); } function createInZone () { return createLocal.apply(null, arguments).parseZone(); } function preParsePostFormat (string) { return string; } var proto$1 = Locale.prototype; proto$1.calendar = calendar; proto$1.longDateFormat = longDateFormat; proto$1.invalidDate = invalidDate; proto$1.ordinal = ordinal; proto$1.preparse = preParsePostFormat; proto$1.postformat = preParsePostFormat; proto$1.relativeTime = relativeTime; proto$1.pastFuture = pastFuture; proto$1.set = set; // Month proto$1.months = localeMonths; proto$1.monthsShort = localeMonthsShort; proto$1.monthsParse = localeMonthsParse; proto$1.monthsRegex = monthsRegex; proto$1.monthsShortRegex = monthsShortRegex; // Week proto$1.week = localeWeek; proto$1.firstDayOfYear = localeFirstDayOfYear; proto$1.firstDayOfWeek = localeFirstDayOfWeek; // Day of Week proto$1.weekdays = localeWeekdays; proto$1.weekdaysMin = localeWeekdaysMin; proto$1.weekdaysShort = localeWeekdaysShort; proto$1.weekdaysParse = localeWeekdaysParse; proto$1.weekdaysRegex = weekdaysRegex; proto$1.weekdaysShortRegex = weekdaysShortRegex; proto$1.weekdaysMinRegex = weekdaysMinRegex; // Hours proto$1.isPM = localeIsPM; proto$1.meridiem = localeMeridiem; function get$1 (format, index, field, setter) { var locale = getLocale(); var utc = createUTC().set(setter, index); return locale[field](utc, format); } function listMonthsImpl (format, index, field) { if (isNumber(format)) { index = format; format = undefined; } format = format || ''; if (index != null) { return get$1(format, index, field, 'month'); } var i; var out = []; for (i = 0; i < 12; i++) { out[i] = get$1(format, i, field, 'month'); } return out; } // () // (5) // (fmt, 5) // (fmt) // (true) // (true, 5) // (true, fmt, 5) // (true, fmt) function listWeekdaysImpl (localeSorted, format, index, field) { if (typeof localeSorted === 'boolean') { if (isNumber(format)) { index = format; format = undefined; } format = format || ''; } else { format = localeSorted; index = format; localeSorted = false; if (isNumber(format)) { index = format; format = undefined; } format = format || ''; } var locale = getLocale(), shift = localeSorted ? locale._week.dow : 0; if (index != null) { return get$1(format, (index + shift) % 7, field, 'day'); } var i; var out = []; for (i = 0; i < 7; i++) { out[i] = get$1(format, (i + shift) % 7, field, 'day'); } return out; } function listMonths (format, index) { return listMonthsImpl(format, index, 'months'); } function listMonthsShort (format, index) { return listMonthsImpl(format, index, 'monthsShort'); } function listWeekdays (localeSorted, format, index) { return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); } function listWeekdaysShort (localeSorted, format, index) { return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); } function listWeekdaysMin (localeSorted, format, index) { return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); } getSetGlobalLocale('en', { dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, ordinal : function (number) { var b = number % 10, output = (toInt(number % 100 / 10) === 1) ? 'th' : (b === 1) ? 'st' : (b === 2) ? 'nd' : (b === 3) ? 'rd' : 'th'; return number + output; } }); // Side effect imports hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale); hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale); var mathAbs = Math.abs; function abs () { var data = this._data; this._milliseconds = mathAbs(this._milliseconds); this._days = mathAbs(this._days); this._months = mathAbs(this._months); data.milliseconds = mathAbs(data.milliseconds); data.seconds = mathAbs(data.seconds); data.minutes = mathAbs(data.minutes); data.hours = mathAbs(data.hours); data.months = mathAbs(data.months); data.years = mathAbs(data.years); return this; } function addSubtract$1 (duration, input, value, direction) { var other = createDuration(input, value); duration._milliseconds += direction * other._milliseconds; duration._days += direction * other._days; duration._months += direction * other._months; return duration._bubble(); } // supports only 2.0-style add(1, 's') or add(duration) function add$1 (input, value) { return addSubtract$1(this, input, value, 1); } // supports only 2.0-style subtract(1, 's') or subtract(duration) function subtract$1 (input, value) { return addSubtract$1(this, input, value, -1); } function absCeil (number) { if (number < 0) { return Math.floor(number); } else { return Math.ceil(number); } } function bubble () { var milliseconds = this._milliseconds; var days = this._days; var months = this._months; var data = this._data; var seconds, minutes, hours, years, monthsFromDays; // if we have a mix of positive and negative values, bubble down first // check: https://github.com/moment/moment/issues/2166 if (!((milliseconds >= 0 && days >= 0 && months >= 0) || (milliseconds <= 0 && days <= 0 && months <= 0))) { milliseconds += absCeil(monthsToDays(months) + days) * 864e5; days = 0; months = 0; } // The following code bubbles up values, see the tests for // examples of what that means. data.milliseconds = milliseconds % 1000; seconds = absFloor(milliseconds / 1000); data.seconds = seconds % 60; minutes = absFloor(seconds / 60); data.minutes = minutes % 60; hours = absFloor(minutes / 60); data.hours = hours % 24; days += absFloor(hours / 24); // convert days to months monthsFromDays = absFloor(daysToMonths(days)); months += monthsFromDays; days -= absCeil(monthsToDays(monthsFromDays)); // 12 months -> 1 year years = absFloor(months / 12); months %= 12; data.days = days; data.months = months; data.years = years; return this; } function daysToMonths (days) { // 400 years have 146097 days (taking into account leap year rules) // 400 years have 12 months === 4800 return days * 4800 / 146097; } function monthsToDays (months) { // the reverse of daysToMonths return months * 146097 / 4800; } function as (units) { if (!this.isValid()) { return NaN; } var days; var months; var milliseconds = this._milliseconds; units = normalizeUnits(units); if (units === 'month' || units === 'year') { days = this._days + milliseconds / 864e5; months = this._months + daysToMonths(days); return units === 'month' ? months : months / 12; } else { // handle milliseconds separately because of floating point math errors (issue #1867) days = this._days + Math.round(monthsToDays(this._months)); switch (units) { case 'week' : return days / 7 + milliseconds / 6048e5; case 'day' : return days + milliseconds / 864e5; case 'hour' : return days * 24 + milliseconds / 36e5; case 'minute' : return days * 1440 + milliseconds / 6e4; case 'second' : return days * 86400 + milliseconds / 1000; // Math.floor prevents floating point math errors here case 'millisecond': return Math.floor(days * 864e5) + milliseconds; default: throw new Error('Unknown unit ' + units); } } } // TODO: Use this.as('ms')? function valueOf$1 () { if (!this.isValid()) { return NaN; } return ( this._milliseconds + this._days * 864e5 + (this._months % 12) * 2592e6 + toInt(this._months / 12) * 31536e6 ); } function makeAs (alias) { return function () { return this.as(alias); }; } var asMilliseconds = makeAs('ms'); var asSeconds = makeAs('s'); var asMinutes = makeAs('m'); var asHours = makeAs('h'); var asDays = makeAs('d'); var asWeeks = makeAs('w'); var asMonths = makeAs('M'); var asYears = makeAs('y'); function get$2 (units) { units = normalizeUnits(units); return this.isValid() ? this[units + 's']() : NaN; } function makeGetter(name) { return function () { return this.isValid() ? this._data[name] : NaN; }; } var milliseconds = makeGetter('milliseconds'); var seconds = makeGetter('seconds'); var minutes = makeGetter('minutes'); var hours = makeGetter('hours'); var days = makeGetter('days'); var months = makeGetter('months'); var years = makeGetter('years'); function weeks () { return absFloor(this.days() / 7); } var round = Math.round; var thresholds = { ss: 44, // a few seconds to seconds s : 45, // seconds to minute m : 45, // minutes to hour h : 22, // hours to day d : 26, // days to month M : 11 // months to year }; // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); } function relativeTime$1 (posNegDuration, withoutSuffix, locale) { var duration = createDuration(posNegDuration).abs(); var seconds = round(duration.as('s')); var minutes = round(duration.as('m')); var hours = round(duration.as('h')); var days = round(duration.as('d')); var months = round(duration.as('M')); var years = round(duration.as('y')); var a = seconds <= thresholds.ss && ['s', seconds] || seconds < thresholds.s && ['ss', seconds] || minutes <= 1 && ['m'] || minutes < thresholds.m && ['mm', minutes] || hours <= 1 && ['h'] || hours < thresholds.h && ['hh', hours] || days <= 1 && ['d'] || days < thresholds.d && ['dd', days] || months <= 1 && ['M'] || months < thresholds.M && ['MM', months] || years <= 1 && ['y'] || ['yy', years]; a[2] = withoutSuffix; a[3] = +posNegDuration > 0; a[4] = locale; return substituteTimeAgo.apply(null, a); } // This function allows you to set the rounding function for relative time strings function getSetRelativeTimeRounding (roundingFunction) { if (roundingFunction === undefined) { return round; } if (typeof(roundingFunction) === 'function') { round = roundingFunction; return true; } return false; } // This function allows you to set a threshold for relative time strings function getSetRelativeTimeThreshold (threshold, limit) { if (thresholds[threshold] === undefined) { return false; } if (limit === undefined) { return thresholds[threshold]; } thresholds[threshold] = limit; if (threshold === 's') { thresholds.ss = limit - 1; } return true; } function humanize (withSuffix) { if (!this.isValid()) { return this.localeData().invalidDate(); } var locale = this.localeData(); var output = relativeTime$1(this, !withSuffix, locale); if (withSuffix) { output = locale.pastFuture(+this, output); } return locale.postformat(output); } var abs$1 = Math.abs; function toISOString$1() { // for ISO strings we do not use the normal bubbling rules: // * milliseconds bubble up until they become hours // * days do not bubble at all // * months bubble up until they become years // This is because there is no context-free conversion between hours and days // (think of clock changes) // and also not between days and months (28-31 days per month) if (!this.isValid()) { return this.localeData().invalidDate(); } var seconds = abs$1(this._milliseconds) / 1000; var days = abs$1(this._days); var months = abs$1(this._months); var minutes, hours, years; // 3600 seconds -> 60 minutes -> 1 hour minutes = absFloor(seconds / 60); hours = absFloor(minutes / 60); seconds %= 60; minutes %= 60; // 12 months -> 1 year years = absFloor(months / 12); months %= 12; // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js var Y = years; var M = months; var D = days; var h = hours; var m = minutes; var s = seconds; var total = this.asSeconds(); if (!total) { // this is the same as C#'s (Noda) and python (isodate)... // but not other JS (goog.date) return 'P0D'; } return (total < 0 ? '-' : '') + 'P' + (Y ? Y + 'Y' : '') + (M ? M + 'M' : '') + (D ? D + 'D' : '') + ((h || m || s) ? 'T' : '') + (h ? h + 'H' : '') + (m ? m + 'M' : '') + (s ? s + 'S' : ''); } var proto$2 = Duration.prototype; proto$2.isValid = isValid$1; proto$2.abs = abs; proto$2.add = add$1; proto$2.subtract = subtract$1; proto$2.as = as; proto$2.asMilliseconds = asMilliseconds; proto$2.asSeconds = asSeconds; proto$2.asMinutes = asMinutes; proto$2.asHours = asHours; proto$2.asDays = asDays; proto$2.asWeeks = asWeeks; proto$2.asMonths = asMonths; proto$2.asYears = asYears; proto$2.valueOf = valueOf$1; proto$2._bubble = bubble; proto$2.get = get$2; proto$2.milliseconds = milliseconds; proto$2.seconds = seconds; proto$2.minutes = minutes; proto$2.hours = hours; proto$2.days = days; proto$2.weeks = weeks; proto$2.months = months; proto$2.years = years; proto$2.humanize = humanize; proto$2.toISOString = toISOString$1; proto$2.toString = toISOString$1; proto$2.toJSON = toISOString$1; proto$2.locale = locale; proto$2.localeData = localeData; // Deprecations proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1); proto$2.lang = lang; // Side effect imports // FORMATTING addFormatToken('X', 0, 0, 'unix'); addFormatToken('x', 0, 0, 'valueOf'); // PARSING addRegexToken('x', matchSigned); addRegexToken('X', matchTimestamp); addParseToken('X', function (input, array, config) { config._d = new Date(parseFloat(input, 10) * 1000); }); addParseToken('x', function (input, array, config) { config._d = new Date(toInt(input)); }); // Side effect imports hooks.version = '2.18.1'; setHookCallback(createLocal); hooks.fn = proto; hooks.min = min; hooks.max = max; hooks.now = now; hooks.utc = createUTC; hooks.unix = createUnix; hooks.months = listMonths; hooks.isDate = isDate; hooks.locale = getSetGlobalLocale; hooks.invalid = createInvalid; hooks.duration = createDuration; hooks.isMoment = isMoment; hooks.weekdays = listWeekdays; hooks.parseZone = createInZone; hooks.localeData = getLocale; hooks.isDuration = isDuration; hooks.monthsShort = listMonthsShort; hooks.weekdaysMin = listWeekdaysMin; hooks.defineLocale = defineLocale; hooks.updateLocale = updateLocale; hooks.locales = listLocales; hooks.weekdaysShort = listWeekdaysShort; hooks.normalizeUnits = normalizeUnits; hooks.relativeTimeRounding = getSetRelativeTimeRounding; hooks.relativeTimeThreshold = getSetRelativeTimeThreshold; hooks.calendarFormat = getCalendarFormat; hooks.prototype = proto; return hooks; }))); },{}],7:[function(require,module,exports){ /** * @namespace Chart */ var Chart = require(28)(); require(26)(Chart); require(40)(Chart); require(22)(Chart); require(25)(Chart); require(30)(Chart); require(21)(Chart); require(23)(Chart); require(24)(Chart); require(29)(Chart); require(32)(Chart); require(33)(Chart); require(31)(Chart); require(27)(Chart); require(34)(Chart); require(35)(Chart); require(36)(Chart); require(37)(Chart); require(38)(Chart); require(46)(Chart); require(44)(Chart); require(45)(Chart); require(47)(Chart); require(48)(Chart); require(49)(Chart); // Controllers must be loaded after elements // See Chart.core.datasetController.dataElementType require(15)(Chart); require(16)(Chart); require(17)(Chart); require(18)(Chart); require(19)(Chart); require(20)(Chart); require(8)(Chart); require(9)(Chart); require(10)(Chart); require(11)(Chart); require(12)(Chart); require(13)(Chart); require(14)(Chart); // Loading built-it plugins var plugins = []; plugins.push( require(41)(Chart), require(42)(Chart), require(43)(Chart) ); Chart.plugins.register(plugins); module.exports = Chart; if (typeof window !== 'undefined') { window.Chart = Chart; } },{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"34":34,"35":35,"36":36,"37":37,"38":38,"40":40,"41":41,"42":42,"43":43,"44":44,"45":45,"46":46,"47":47,"48":48,"49":49,"8":8,"9":9}],8:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Bar = function(context, config) { config.type = 'bar'; return new Chart(context, config); }; }; },{}],9:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Bubble = function(context, config) { config.type = 'bubble'; return new Chart(context, config); }; }; },{}],10:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Doughnut = function(context, config) { config.type = 'doughnut'; return new Chart(context, config); }; }; },{}],11:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Line = function(context, config) { config.type = 'line'; return new Chart(context, config); }; }; },{}],12:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.PolarArea = function(context, config) { config.type = 'polarArea'; return new Chart(context, config); }; }; },{}],13:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { Chart.Radar = function(context, config) { config.type = 'radar'; return new Chart(context, config); }; }; },{}],14:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { var defaultConfig = { hover: { mode: 'single' }, scales: { xAxes: [{ type: 'linear', // scatter should not use a category axis position: 'bottom', id: 'x-axis-1' // need an ID so datasets can reference the scale }], yAxes: [{ type: 'linear', position: 'left', id: 'y-axis-1' }] }, tooltips: { callbacks: { title: function() { // Title doesn't make sense for scatter since we format the data as a point return ''; }, label: function(tooltipItem) { return '(' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ')'; } } } }; // Register the default config for this type Chart.defaults.scatter = defaultConfig; // Scatter charts use line controllers Chart.controllers.scatter = Chart.controllers.line; Chart.Scatter = function(context, config) { config.type = 'scatter'; return new Chart(context, config); }; }; },{}],15:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { var helpers = Chart.helpers; Chart.defaults.bar = { hover: { mode: 'label' }, scales: { xAxes: [{ type: 'category', // Specific to Bar Controller categoryPercentage: 0.8, barPercentage: 0.9, // grid line settings gridLines: { offsetGridLines: true } }], yAxes: [{ type: 'linear' }] } }; Chart.controllers.bar = Chart.DatasetController.extend({ dataElementType: Chart.elements.Rectangle, initialize: function() { var me = this; var meta; Chart.DatasetController.prototype.initialize.apply(me, arguments); meta = me.getMeta(); meta.stack = me.getDataset().stack; meta.bar = true; }, update: function(reset) { var me = this; var elements = me.getMeta().data; var i, ilen; me._ruler = me.getRuler(); for (i = 0, ilen = elements.length; i < ilen; ++i) { me.updateElement(elements[i], i, reset); } }, updateElement: function(rectangle, index, reset) { var me = this; var chart = me.chart; var meta = me.getMeta(); var dataset = me.getDataset(); var custom = rectangle.custom || {}; var rectangleOptions = chart.options.elements.rectangle; rectangle._xScale = me.getScaleForId(meta.xAxisID); rectangle._yScale = me.getScaleForId(meta.yAxisID); rectangle._datasetIndex = me.index; rectangle._index = index; rectangle._model = { datasetLabel: dataset.label, label: chart.data.labels[index], borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped, backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor), borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor), borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth) }; me.updateElementGeometry(rectangle, index, reset); rectangle.pivot(); }, /** * @private */ updateElementGeometry: function(rectangle, index, reset) { var me = this; var model = rectangle._model; var vscale = me.getValueScale(); var base = vscale.getBasePixel(); var horizontal = vscale.isHorizontal(); var ruler = me._ruler || me.getRuler(); var vpixels = me.calculateBarValuePixels(me.index, index); var ipixels = me.calculateBarIndexPixels(me.index, index, ruler); model.horizontal = horizontal; model.base = reset? base : vpixels.base; model.x = horizontal? reset? base : vpixels.head : ipixels.center; model.y = horizontal? ipixels.center : reset? base : vpixels.head; model.height = horizontal? ipixels.size : undefined; model.width = horizontal? undefined : ipixels.size; }, /** * @private */ getValueScaleId: function() { return this.getMeta().yAxisID; }, /** * @private */ getIndexScaleId: function() { return this.getMeta().xAxisID; }, /** * @private */ getValueScale: function() { return this.getScaleForId(this.getValueScaleId()); }, /** * @private */ getIndexScale: function() { return this.getScaleForId(this.getIndexScaleId()); }, /** * Returns the effective number of stacks based on groups and bar visibility. * @private */ getStackCount: function(last) { var me = this; var chart = me.chart; var scale = me.getIndexScale(); var stacked = scale.options.stacked; var ilen = last === undefined? chart.data.datasets.length : last + 1; var stacks = []; var i, meta; for (i = 0; i < ilen; ++i) { meta = chart.getDatasetMeta(i); if (meta.bar && chart.isDatasetVisible(i) && (stacked === false || (stacked === true && stacks.indexOf(meta.stack) === -1) || (stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) { stacks.push(meta.stack); } } return stacks.length; }, /** * Returns the stack index for the given dataset based on groups and bar visibility. * @private */ getStackIndex: function(datasetIndex) { return this.getStackCount(datasetIndex) - 1; }, /** * @private */ getRuler: function() { var me = this; var scale = me.getIndexScale(); var options = scale.options; var stackCount = me.getStackCount(); var fullSize = scale.isHorizontal()? scale.width : scale.height; var tickSize = fullSize / scale.ticks.length; var categorySize = tickSize * options.categoryPercentage; var fullBarSize = categorySize / stackCount; var barSize = fullBarSize * options.barPercentage; barSize = Math.min( helpers.getValueOrDefault(options.barThickness, barSize), helpers.getValueOrDefault(options.maxBarThickness, Infinity)); return { stackCount: stackCount, tickSize: tickSize, categorySize: categorySize, categorySpacing: tickSize - categorySize, fullBarSize: fullBarSize, barSize: barSize, barSpacing: fullBarSize - barSize, scale: scale }; }, /** * Note: pixel values are not clamped to the scale area. * @private */ calculateBarValuePixels: function(datasetIndex, index) { var me = this; var chart = me.chart; var meta = me.getMeta(); var scale = me.getValueScale(); var datasets = chart.data.datasets; var value = Number(datasets[datasetIndex].data[index]); var stacked = scale.options.stacked; var stack = meta.stack; var start = 0; var i, imeta, ivalue, base, head, size; if (stacked || (stacked === undefined && stack !== undefined)) { for (i = 0; i < datasetIndex; ++i) { imeta = chart.getDatasetMeta(i); if (imeta.bar && imeta.stack === stack && imeta.controller.getValueScaleId() === scale.id && chart.isDatasetVisible(i)) { ivalue = Number(datasets[i].data[index]); if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { start += ivalue; } } } } base = scale.getPixelForValue(start); head = scale.getPixelForValue(start + value); size = (head - base) / 2; return { size: size, base: base, head: head, center: head + size / 2 }; }, /** * @private */ calculateBarIndexPixels: function(datasetIndex, index, ruler) { var me = this; var scale = ruler.scale; var isCombo = me.chart.isCombo; var stackIndex = me.getStackIndex(datasetIndex); var base = scale.getPixelForValue(null, index, datasetIndex, isCombo); var size = ruler.barSize; base -= isCombo? ruler.tickSize / 2 : 0; base += ruler.fullBarSize * stackIndex; base += ruler.categorySpacing / 2; base += ruler.barSpacing / 2; return { size: size, base: base, head: base + size, center: base + size / 2 }; }, draw: function() { var me = this; var chart = me.chart; var elements = me.getMeta().data; var dataset = me.getDataset(); var ilen = elements.length; var i = 0; var d; helpers.canvas.clipArea(chart.ctx, chart.chartArea); for (; i 0) { if (tooltipItems[0].yLabel) { title = tooltipItems[0].yLabel; } else if (data.labels.length > 0 && tooltipItems[0].index < data.labels.length) { title = data.labels[tooltipItems[0].index]; } } return title; }, label: function(tooltipItem, data) { var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; return datasetLabel + ': ' + tooltipItem.xLabel; } } } }; Chart.controllers.horizontalBar = Chart.controllers.bar.extend({ /** * @private */ getValueScaleId: function() { return this.getMeta().xAxisID; }, /** * @private */ getIndexScaleId: function() { return this.getMeta().yAxisID; } }); }; },{}],16:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { var helpers = Chart.helpers; Chart.defaults.bubble = { hover: { mode: 'single' }, scales: { xAxes: [{ type: 'linear', // bubble should probably use a linear scale by default position: 'bottom', id: 'x-axis-0' // need an ID so datasets can reference the scale }], yAxes: [{ type: 'linear', position: 'left', id: 'y-axis-0' }] }, tooltips: { callbacks: { title: function() { // Title doesn't make sense for scatter since we format the data as a point return ''; }, label: function(tooltipItem, data) { var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || ''; var dataPoint = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; return datasetLabel + ': (' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ', ' + dataPoint.r + ')'; } } } }; Chart.controllers.bubble = Chart.DatasetController.extend({ dataElementType: Chart.elements.Point, update: function(reset) { var me = this; var meta = me.getMeta(); var points = meta.data; // Update Points helpers.each(points, function(point, index) { me.updateElement(point, index, reset); }); }, updateElement: function(point, index, reset) { var me = this; var meta = me.getMeta(); var xScale = me.getScaleForId(meta.xAxisID); var yScale = me.getScaleForId(meta.yAxisID); var custom = point.custom || {}; var dataset = me.getDataset(); var data = dataset.data[index]; var pointElementOptions = me.chart.options.elements.point; var dsIndex = me.index; helpers.extend(point, { // Utility _xScale: xScale, _yScale: yScale, _datasetIndex: dsIndex, _index: index, // Desired view properties _model: { x: reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex, me.chart.isCombo), y: reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex), // Appearance radius: reset ? 0 : custom.radius ? custom.radius : me.getRadius(data), // Tooltip hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius) } }); // Trick to reset the styles of the point Chart.DatasetController.prototype.removeHoverStyle.call(me, point, pointElementOptions); var model = point._model; model.skip = custom.skip ? custom.skip : (isNaN(model.x) || isNaN(model.y)); point.pivot(); }, getRadius: function(value) { return value.r || this.chart.options.elements.point.radius; }, setHoverStyle: function(point) { var me = this; Chart.DatasetController.prototype.setHoverStyle.call(me, point); // Radius var dataset = me.chart.data.datasets[point._datasetIndex]; var index = point._index; var custom = point.custom || {}; var model = point._model; model.radius = custom.hoverRadius ? custom.hoverRadius : (helpers.getValueAtIndexOrDefault(dataset.hoverRadius, index, me.chart.options.elements.point.hoverRadius)) + me.getRadius(dataset.data[index]); }, removeHoverStyle: function(point) { var me = this; Chart.DatasetController.prototype.removeHoverStyle.call(me, point, me.chart.options.elements.point); var dataVal = me.chart.data.datasets[point._datasetIndex].data[point._index]; var custom = point.custom || {}; var model = point._model; model.radius = custom.radius ? custom.radius : me.getRadius(dataVal); } }); }; },{}],17:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { var helpers = Chart.helpers, defaults = Chart.defaults; defaults.doughnut = { animation: { // Boolean - Whether we animate the rotation of the Doughnut animateRotate: true, // Boolean - Whether we animate scaling the Doughnut from the centre animateScale: false }, aspectRatio: 1, hover: { mode: 'single' }, legendCallback: function(chart) { var text = []; text.push('
      '); var data = chart.data; var datasets = data.datasets; var labels = data.labels; if (datasets.length) { for (var i = 0; i < datasets[0].data.length; ++i) { text.push('
    • '); if (labels[i]) { text.push(labels[i]); } text.push('
    • '); } } text.push('
    '); return text.join(''); }, legend: { labels: { generateLabels: function(chart) { var data = chart.data; if (data.labels.length && data.datasets.length) { return data.labels.map(function(label, i) { var meta = chart.getDatasetMeta(0); var ds = data.datasets[0]; var arc = meta.data[i]; var custom = arc && arc.custom || {}; var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; var arcOpts = chart.options.elements.arc; var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); return { text: label, fillStyle: fill, strokeStyle: stroke, lineWidth: bw, hidden: isNaN(ds.data[i]) || meta.data[i].hidden, // Extra data used for toggling the correct item index: i }; }); } return []; } }, onClick: function(e, legendItem) { var index = legendItem.index; var chart = this.chart; var i, ilen, meta; for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { meta = chart.getDatasetMeta(i); // toggle visibility of index if exists if (meta.data[index]) { meta.data[index].hidden = !meta.data[index].hidden; } } chart.update(); } }, // The percentage of the chart that we cut out of the middle. cutoutPercentage: 50, // The rotation of the chart, where the first data arc begins. rotation: Math.PI * -0.5, // The total circumference of the chart. circumference: Math.PI * 2.0, // Need to override these to give a nice default tooltips: { callbacks: { title: function() { return ''; }, label: function(tooltipItem, data) { var dataLabel = data.labels[tooltipItem.index]; var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; if (helpers.isArray(dataLabel)) { // show value on first line of multiline label // need to clone because we are changing the value dataLabel = dataLabel.slice(); dataLabel[0] += value; } else { dataLabel += value; } return dataLabel; } } } }; defaults.pie = helpers.clone(defaults.doughnut); helpers.extend(defaults.pie, { cutoutPercentage: 0 }); Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({ dataElementType: Chart.elements.Arc, linkScales: helpers.noop, // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly getRingIndex: function(datasetIndex) { var ringIndex = 0; for (var j = 0; j < datasetIndex; ++j) { if (this.chart.isDatasetVisible(j)) { ++ringIndex; } } return ringIndex; }, update: function(reset) { var me = this; var chart = me.chart, chartArea = chart.chartArea, opts = chart.options, arcOpts = opts.elements.arc, availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth, availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth, minSize = Math.min(availableWidth, availableHeight), offset = { x: 0, y: 0 }, meta = me.getMeta(), cutoutPercentage = opts.cutoutPercentage, circumference = opts.circumference; // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc if (circumference < Math.PI * 2.0) { var startAngle = opts.rotation % (Math.PI * 2.0); startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0); var endAngle = startAngle + circumference; var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)}; var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)}; var contains0 = (startAngle <= 0 && 0 <= endAngle) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); var cutout = cutoutPercentage / 100.0; var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))}; var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))}; var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5}; minSize = Math.min(availableWidth / size.width, availableHeight / size.height); offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; } chart.borderWidth = me.getMaxBorderWidth(meta.data); chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0); chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); chart.offsetX = offset.x * chart.outerRadius; chart.offsetY = offset.y * chart.outerRadius; meta.total = me.calculateTotal(); me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index)); me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0); helpers.each(meta.data, function(arc, index) { me.updateElement(arc, index, reset); }); }, updateElement: function(arc, index, reset) { var me = this; var chart = me.chart, chartArea = chart.chartArea, opts = chart.options, animationOpts = opts.animation, centerX = (chartArea.left + chartArea.right) / 2, centerY = (chartArea.top + chartArea.bottom) / 2, startAngle = opts.rotation, // non reset case handled later endAngle = opts.rotation, // non reset case handled later dataset = me.getDataset(), circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)), innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius, outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius, valueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; helpers.extend(arc, { // Utility _datasetIndex: me.index, _index: index, // Desired view properties _model: { x: centerX + chart.offsetX, y: centerY + chart.offsetY, startAngle: startAngle, endAngle: endAngle, circumference: circumference, outerRadius: outerRadius, innerRadius: innerRadius, label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) } }); var model = arc._model; // Resets the visual styles this.removeHoverStyle(arc); // Set correct angles if not resetting if (!reset || !animationOpts.animateRotate) { if (index === 0) { model.startAngle = opts.rotation; } else { model.startAngle = me.getMeta().data[index - 1]._model.endAngle; } model.endAngle = model.startAngle + model.circumference; } arc.pivot(); }, removeHoverStyle: function(arc) { Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); }, calculateTotal: function() { var dataset = this.getDataset(); var meta = this.getMeta(); var total = 0; var value; helpers.each(meta.data, function(element, index) { value = dataset.data[index]; if (!isNaN(value) && !element.hidden) { total += Math.abs(value); } }); /* if (total === 0) { total = NaN; }*/ return total; }, calculateCircumference: function(value) { var total = this.getMeta().total; if (total > 0 && !isNaN(value)) { return (Math.PI * 2.0) * (value / total); } return 0; }, // gets the max border or hover width to properly scale pie charts getMaxBorderWidth: function(elements) { var max = 0, index = this.index, length = elements.length, borderWidth, hoverWidth; for (var i = 0; i < length; i++) { borderWidth = elements[i]._model ? elements[i]._model.borderWidth : 0; hoverWidth = elements[i]._chart ? elements[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; max = borderWidth > max ? borderWidth : max; max = hoverWidth > max ? hoverWidth : max; } return max; } }); }; },{}],18:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { var helpers = Chart.helpers; Chart.defaults.line = { showLines: true, spanGaps: false, hover: { mode: 'label' }, scales: { xAxes: [{ type: 'category', id: 'x-axis-0' }], yAxes: [{ type: 'linear', id: 'y-axis-0' }] } }; function lineEnabled(dataset, options) { return helpers.getValueOrDefault(dataset.showLine, options.showLines); } Chart.controllers.line = Chart.DatasetController.extend({ datasetElementType: Chart.elements.Line, dataElementType: Chart.elements.Point, update: function(reset) { var me = this; var meta = me.getMeta(); var line = meta.dataset; var points = meta.data || []; var options = me.chart.options; var lineElementOptions = options.elements.line; var scale = me.getScaleForId(meta.yAxisID); var i, ilen, custom; var dataset = me.getDataset(); var showLine = lineEnabled(dataset, options); // Update Line if (showLine) { custom = line.custom || {}; // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { dataset.lineTension = dataset.tension; } // Utility line._scale = scale; line._datasetIndex = me.index; // Data line._children = points; // Model line._model = { // Appearance // The default behavior of lines is to break at null values, according // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 // This option gives lines the ability to span gaps spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps, tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension), backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), steppedLine: custom.steppedLine ? custom.steppedLine : helpers.getValueOrDefault(dataset.steppedLine, lineElementOptions.stepped), cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.getValueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode), }; line.pivot(); } // Update Points for (i=0, ilen=points.length; i'); var data = chart.data; var datasets = data.datasets; var labels = data.labels; if (datasets.length) { for (var i = 0; i < datasets[0].data.length; ++i) { text.push('
  • '); if (labels[i]) { text.push(labels[i]); } text.push('
  • '); } } text.push(''); return text.join(''); }, legend: { labels: { generateLabels: function(chart) { var data = chart.data; if (data.labels.length && data.datasets.length) { return data.labels.map(function(label, i) { var meta = chart.getDatasetMeta(0); var ds = data.datasets[0]; var arc = meta.data[i]; var custom = arc.custom || {}; var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; var arcOpts = chart.options.elements.arc; var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); return { text: label, fillStyle: fill, strokeStyle: stroke, lineWidth: bw, hidden: isNaN(ds.data[i]) || meta.data[i].hidden, // Extra data used for toggling the correct item index: i }; }); } return []; } }, onClick: function(e, legendItem) { var index = legendItem.index; var chart = this.chart; var i, ilen, meta; for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { meta = chart.getDatasetMeta(i); meta.data[index].hidden = !meta.data[index].hidden; } chart.update(); } }, // Need to override these to give a nice default tooltips: { callbacks: { title: function() { return ''; }, label: function(tooltipItem, data) { return data.labels[tooltipItem.index] + ': ' + tooltipItem.yLabel; } } } }; Chart.controllers.polarArea = Chart.DatasetController.extend({ dataElementType: Chart.elements.Arc, linkScales: helpers.noop, update: function(reset) { var me = this; var chart = me.chart; var chartArea = chart.chartArea; var meta = me.getMeta(); var opts = chart.options; var arcOpts = opts.elements.arc; var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0); chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); me.innerRadius = me.outerRadius - chart.radiusLength; meta.count = me.countVisibleElements(); helpers.each(meta.data, function(arc, index) { me.updateElement(arc, index, reset); }); }, updateElement: function(arc, index, reset) { var me = this; var chart = me.chart; var dataset = me.getDataset(); var opts = chart.options; var animationOpts = opts.animation; var scale = chart.scale; var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault; var labels = chart.data.labels; var circumference = me.calculateCircumference(dataset.data[index]); var centerX = scale.xCenter; var centerY = scale.yCenter; // If there is NaN data before us, we need to calculate the starting angle correctly. // We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data var visibleCount = 0; var meta = me.getMeta(); for (var i = 0; i < index; ++i) { if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) { ++visibleCount; } } // var negHalfPI = -0.5 * Math.PI; var datasetStartAngle = opts.startAngle; var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); var startAngle = datasetStartAngle + (circumference * visibleCount); var endAngle = startAngle + (arc.hidden ? 0 : circumference); var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); helpers.extend(arc, { // Utility _datasetIndex: me.index, _index: index, _scale: scale, // Desired view properties _model: { x: centerX, y: centerY, innerRadius: 0, outerRadius: reset ? resetRadius : distance, startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, label: getValueAtIndexOrDefault(labels, index, labels[index]) } }); // Apply border and fill style me.removeHoverStyle(arc); arc.pivot(); }, removeHoverStyle: function(arc) { Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); }, countVisibleElements: function() { var dataset = this.getDataset(); var meta = this.getMeta(); var count = 0; helpers.each(meta.data, function(element, index) { if (!isNaN(dataset.data[index]) && !element.hidden) { count++; } }); return count; }, calculateCircumference: function(value) { var count = this.getMeta().count; if (count > 0 && !isNaN(value)) { return (2 * Math.PI) / count; } return 0; } }); }; },{}],20:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { var helpers = Chart.helpers; Chart.defaults.radar = { aspectRatio: 1, scale: { type: 'radialLinear' }, elements: { line: { tension: 0 // no bezier in radar } } }; Chart.controllers.radar = Chart.DatasetController.extend({ datasetElementType: Chart.elements.Line, dataElementType: Chart.elements.Point, linkScales: helpers.noop, update: function(reset) { var me = this; var meta = me.getMeta(); var line = meta.dataset; var points = meta.data; var custom = line.custom || {}; var dataset = me.getDataset(); var lineElementOptions = me.chart.options.elements.line; var scale = me.chart.scale; // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { dataset.lineTension = dataset.tension; } helpers.extend(meta.dataset, { // Utility _datasetIndex: me.index, _scale: scale, // Data _children: points, _loop: true, // Model _model: { // Appearance tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension), backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), } }); meta.dataset.pivot(); // Update Points helpers.each(points, function(point, index) { me.updateElement(point, index, reset); }, me); // Update bezier control points me.updateBezierControlPoints(); }, updateElement: function(point, index, reset) { var me = this; var custom = point.custom || {}; var dataset = me.getDataset(); var scale = me.chart.scale; var pointElementOptions = me.chart.options.elements.point; var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); // Compatibility: If the properties are defined with only the old name, use those values if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { dataset.pointRadius = dataset.radius; } if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { dataset.pointHitRadius = dataset.hitRadius; } helpers.extend(point, { // Utility _datasetIndex: me.index, _index: index, _scale: scale, // Desired view properties _model: { x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales y: reset ? scale.yCenter : pointPosition.y, // Appearance tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension), radius: custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius), backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor), borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor), borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth), pointStyle: custom.pointStyle ? custom.pointStyle : helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle), // Tooltip hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius) } }); point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); }, updateBezierControlPoints: function() { var chartArea = this.chart.chartArea; var meta = this.getMeta(); helpers.each(meta.data, function(point, index) { var model = point._model; var controlPoints = helpers.splineCurve( helpers.previousItem(meta.data, index, true)._model, model, helpers.nextItem(meta.data, index, true)._model, model.tension ); // Prevent the bezier going outside of the bounds of the graph model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left); model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top); model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left); model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top); // Now pivot the point for animation point.pivot(); }); }, setHoverStyle: function(point) { // Point var dataset = this.chart.data.datasets[point._datasetIndex]; var custom = point.custom || {}; var index = point._index; var model = point._model; model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); }, removeHoverStyle: function(point) { var dataset = this.chart.data.datasets[point._datasetIndex]; var custom = point.custom || {}; var index = point._index; var model = point._model; var pointElementOptions = this.chart.options.elements.point; model.radius = custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius); model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor); model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor); model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth); } }); }; },{}],21:[function(require,module,exports){ /* global window: false */ 'use strict'; module.exports = function(Chart) { var helpers = Chart.helpers; Chart.defaults.global.animation = { duration: 1000, easing: 'easeOutQuart', onProgress: helpers.noop, onComplete: helpers.noop }; Chart.Animation = Chart.Element.extend({ chart: null, // the animation associated chart instance currentStep: 0, // the current animation step numSteps: 60, // default number of steps easing: '', // the easing to use for this animation render: null, // render function used by the animation service onAnimationProgress: null, // user specified callback to fire on each step of the animation onAnimationComplete: null, // user specified callback to fire when the animation finishes }); Chart.animationService = { frameDuration: 17, animations: [], dropFrames: 0, request: null, /** * @param {Chart} chart - The chart to animate. * @param {Chart.Animation} animation - The animation that we will animate. * @param {Number} duration - The animation duration in ms. * @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions */ addAnimation: function(chart, animation, duration, lazy) { var animations = this.animations; var i, ilen; animation.chart = chart; if (!lazy) { chart.animating = true; } for (i=0, ilen=animations.length; i < ilen; ++i) { if (animations[i].chart === chart) { animations[i] = animation; return; } } animations.push(animation); // If there are no animations queued, manually kickstart a digest, for lack of a better word if (animations.length === 1) { this.requestAnimationFrame(); } }, cancelAnimation: function(chart) { var index = helpers.findIndex(this.animations, function(animation) { return animation.chart === chart; }); if (index !== -1) { this.animations.splice(index, 1); chart.animating = false; } }, requestAnimationFrame: function() { var me = this; if (me.request === null) { // Skip animation frame requests until the active one is executed. // This can happen when processing mouse events, e.g. 'mousemove' // and 'mouseout' events will trigger multiple renders. me.request = helpers.requestAnimFrame.call(window, function() { me.request = null; me.startDigest(); }); } }, /** * @private */ startDigest: function() { var me = this; var startTime = Date.now(); var framesToDrop = 0; if (me.dropFrames > 1) { framesToDrop = Math.floor(me.dropFrames); me.dropFrames = me.dropFrames % 1; } me.advance(1 + framesToDrop); var endTime = Date.now(); me.dropFrames += (endTime - startTime) / me.frameDuration; // Do we have more stuff to animate? if (me.animations.length > 0) { me.requestAnimationFrame(); } }, /** * @private */ advance: function(count) { var animations = this.animations; var animation, chart; var i = 0; while (i < animations.length) { animation = animations[i]; chart = animation.chart; animation.currentStep = (animation.currentStep || 0) + count; animation.currentStep = Math.min(animation.currentStep, animation.numSteps); helpers.callback(animation.render, [chart, animation], chart); helpers.callback(animation.onAnimationProgress, [animation], chart); if (animation.currentStep >= animation.numSteps) { helpers.callback(animation.onAnimationComplete, [animation], chart); chart.animating = false; animations.splice(i, 1); } else { ++i; } } } }; /** * Provided for backward compatibility, use Chart.Animation instead * @prop Chart.Animation#animationObject * @deprecated since version 2.6.0 * @todo remove at version 3 */ Object.defineProperty(Chart.Animation.prototype, 'animationObject', { get: function() { return this; } }); /** * Provided for backward compatibility, use Chart.Animation#chart instead * @prop Chart.Animation#chartInstance * @deprecated since version 2.6.0 * @todo remove at version 3 */ Object.defineProperty(Chart.Animation.prototype, 'chartInstance', { get: function() { return this.chart; }, set: function(value) { this.chart = value; } }); }; },{}],22:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { // Global Chart canvas helpers object for drawing items to canvas var helpers = Chart.canvasHelpers = {}; helpers.drawPoint = function(ctx, pointStyle, radius, x, y) { var type, edgeLength, xOffset, yOffset, height, size; if (typeof pointStyle === 'object') { type = pointStyle.toString(); if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2, pointStyle.width, pointStyle.height); return; } } if (isNaN(radius) || radius <= 0) { return; } switch (pointStyle) { // Default includes circle default: ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI * 2); ctx.closePath(); ctx.fill(); break; case 'triangle': ctx.beginPath(); edgeLength = 3 * radius / Math.sqrt(3); height = edgeLength * Math.sqrt(3) / 2; ctx.moveTo(x - edgeLength / 2, y + height / 3); ctx.lineTo(x + edgeLength / 2, y + height / 3); ctx.lineTo(x, y - 2 * height / 3); ctx.closePath(); ctx.fill(); break; case 'rect': size = 1 / Math.SQRT2 * radius; ctx.beginPath(); ctx.fillRect(x - size, y - size, 2 * size, 2 * size); ctx.strokeRect(x - size, y - size, 2 * size, 2 * size); break; case 'rectRounded': var offset = radius / Math.SQRT2; var leftX = x - offset; var topY = y - offset; var sideSize = Math.SQRT2 * radius; Chart.helpers.drawRoundedRectangle(ctx, leftX, topY, sideSize, sideSize, radius / 2); ctx.fill(); break; case 'rectRot': size = 1 / Math.SQRT2 * radius; ctx.beginPath(); ctx.moveTo(x - size, y); ctx.lineTo(x, y + size); ctx.lineTo(x + size, y); ctx.lineTo(x, y - size); ctx.closePath(); ctx.fill(); break; case 'cross': ctx.beginPath(); ctx.moveTo(x, y + radius); ctx.lineTo(x, y - radius); ctx.moveTo(x - radius, y); ctx.lineTo(x + radius, y); ctx.closePath(); break; case 'crossRot': ctx.beginPath(); xOffset = Math.cos(Math.PI / 4) * radius; yOffset = Math.sin(Math.PI / 4) * radius; ctx.moveTo(x - xOffset, y - yOffset); ctx.lineTo(x + xOffset, y + yOffset); ctx.moveTo(x - xOffset, y + yOffset); ctx.lineTo(x + xOffset, y - yOffset); ctx.closePath(); break; case 'star': ctx.beginPath(); ctx.moveTo(x, y + radius); ctx.lineTo(x, y - radius); ctx.moveTo(x - radius, y); ctx.lineTo(x + radius, y); xOffset = Math.cos(Math.PI / 4) * radius; yOffset = Math.sin(Math.PI / 4) * radius; ctx.moveTo(x - xOffset, y - yOffset); ctx.lineTo(x + xOffset, y + yOffset); ctx.moveTo(x - xOffset, y + yOffset); ctx.lineTo(x + xOffset, y - yOffset); ctx.closePath(); break; case 'line': ctx.beginPath(); ctx.moveTo(x - radius, y); ctx.lineTo(x + radius, y); ctx.closePath(); break; case 'dash': ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x + radius, y); ctx.closePath(); break; } ctx.stroke(); }; helpers.clipArea = function(ctx, clipArea) { ctx.save(); ctx.beginPath(); ctx.rect(clipArea.left, clipArea.top, clipArea.right - clipArea.left, clipArea.bottom - clipArea.top); ctx.clip(); }; helpers.unclipArea = function(ctx) { ctx.restore(); }; helpers.lineTo = function(ctx, previous, target, flip) { if (target.steppedLine) { if (target.steppedLine === 'after') { ctx.lineTo(previous.x, target.y); } else { ctx.lineTo(target.x, previous.y); } ctx.lineTo(target.x, target.y); return; } if (!target.tension) { ctx.lineTo(target.x, target.y); return; } ctx.bezierCurveTo( flip? previous.controlPointPreviousX : previous.controlPointNextX, flip? previous.controlPointPreviousY : previous.controlPointNextY, flip? target.controlPointNextX : target.controlPointPreviousX, flip? target.controlPointNextY : target.controlPointPreviousY, target.x, target.y); }; Chart.helpers.canvas = helpers; }; },{}],23:[function(require,module,exports){ 'use strict'; module.exports = function(Chart) { var helpers = Chart.helpers; var plugins = Chart.plugins; var platform = Chart.platform; // Create a dictionary of chart types, to allow for extension of existing types Chart.types = {}; // Store a reference to each instance - allowing us to globally resize chart instances on window resize. // Destroy method on the chart will remove the instance of the chart from this reference. Chart.instances = {}; // Controllers available for dataset visualization eg. bar, line, slice, etc. Chart.controllers = {}; /** * Initializes the given config with global and chart default values. */ function initConfig(config) { config = config || {}; // Do NOT use configMerge() for the data object because this method merges arrays // and so would change references to labels and datasets, preventing data updates. var data = config.data = config.data || {}; data.datasets = data.datasets || []; data.labels = data.labels || []; config.options = helpers.configMerge( Chart.defaults.global, Chart.defaults[config.type], config.options || {}); return config; } /** * Updates the config of the chart * @param chart {Chart} chart to update the options for */ function updateConfig(chart) { var newOptions = chart.options; // Update Scale(s) with options if (newOptions.scale) { chart.scale.options = newOptions.scale; } else if (newOptions.scales) { newOptions.scales.xAxes.concat(newOptions.scales.yAxes).forEach(function(scaleOptions) { chart.scales[scaleOptions.id].options = scaleOptions; }); } // Tooltip chart.tooltip._options = newOptions.tooltips; } function positionIsHorizontal(position) { return position === 'top' || position === 'bottom'; } helpers.extend(Chart.prototype, /** @lends Chart */ { /** * @private */ construct: function(item, config) { var me = this; config = initConfig(config); var context = platform.acquireContext(item, config); var canvas = context && context.canvas; var height = canvas && canvas.height; var width = canvas && canvas.width; me.id = helpers.uid(); me.ctx = context; me.canvas = canvas; me.config = config; me.width = width; me.height = height; me.aspectRatio = height? width / height : null; me.options = config.options; me._bufferedRender = false; /** * Provided for backward compatibility, Chart and Chart.Controller have been merged, * the "instance" still need to be defined since it might be called from plugins. * @prop Chart#chart * @deprecated since version 2.6.0 * @todo remove at version 3 * @private */ me.chart = me; me.controller = me; // chart.chart.controller #inception // Add the chart instance to the global namespace Chart.instances[me.id] = me; // Define alias to the config data: `chart.data === chart.config.data` Object.defineProperty(me, 'data', { get: function() { return me.config.data; }, set: function(value) { me.config.data = value; } }); if (!context || !canvas) { // The given item is not a compatible context2d element, let's return before finalizing // the chart initialization but after setting basic chart / controller properties that // can help to figure out that the chart is not valid (e.g chart.canvas !== null); // https://github.com/chartjs/Chart.js/issues/2807 console.error("Failed to create chart: can't acquire context from the given item"); return; } me.initialize(); me.update(); }, /** * @private */ initialize: function() { var me = this; // Before init plugin notification plugins.notify(me, 'beforeInit'); helpers.retinaScale(me); me.bindEvents(); if (me.options.responsive) { // Initial resize before chart draws (must be silent to preserve initial animations). me.resize(true); } // Make sure scales have IDs and are built before we build any controllers. me.ensureScalesHaveIDs(); me.buildScales(); me.initToolTip(); // After init plugin notification plugins.notify(me, 'afterInit'); return me; }, clear: function() { helpers.clear(this); return this; }, stop: function() { // Stops any current animation loop occurring Chart.animationService.cancelAnimation(this); return this; }, resize: function(silent) { var me = this; var options = me.options; var canvas = me.canvas; var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null; // the canvas render width and height will be casted to integers so make sure that // the canvas display style uses the same integer values to avoid blurring effect. var newWidth = Math.floor(helpers.getMaximumWidth(canvas)); var newHeight = Math.floor(aspectRatio? newWidth / aspectRatio : helpers.getMaximumHeight(canvas)); if (me.width === newWidth && me.height === newHeight) { return; } canvas.width = me.width = newWidth; canvas.height = me.height = newHeight; canvas.style.width = newWidth + 'px'; canvas.style.height = newHeight + 'px'; helpers.retinaScale(me); if (!silent) { // Notify any plugins about the resize var newSize = {width: newWidth, height: newHeight}; plugins.notify(me, 'resize', [newSize]); // Notify of resize if (me.options.onResize) { me.options.onResize(me, newSize); } me.stop(); me.update(me.options.responsiveAnimationDuration); } }, ensureScalesHaveIDs: function() { var options = this.options; var scalesOptions = options.scales || {}; var scaleOptions = options.scale; helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) { xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index); }); helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) { yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index); }); if (scaleOptions) { scaleOptions.id = scaleOptions.id || 'scale'; } }, /** * Builds a map of scale ID to scale object for future lookup. */ buildScales: function() { var me = this; var options = me.options; var scales = me.scales = {}; var items = []; if (options.scales) { items = items.concat( (options.scales.xAxes || []).map(function(xAxisOptions) { return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'}; }), (options.scales.yAxes || []).map(function(yAxisOptions) { return {options: yAxisOptions, dtype: 'linear', dposition: 'left'}; }) ); } if (options.scale) { items.push({ options: options.scale, dtype: 'radialLinear', isDefault: true, dposition: 'chartArea' }); } helpers.each(items, function(item) { var scaleOptions = item.options; var scaleType = helpers.getValueOrDefault(scaleOptions.type, item.dtype); var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); if (!scaleClass) { return; } if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) { scaleOptions.position = item.dposition; } var scale = new scaleClass({ id: scaleOptions.id, options: scaleOptions, ctx: me.ctx, chart: me }); scales[scale.id] = scale; // TODO(SB): I think we should be able to remove this custom case (options.scale) // and consider it as a regular scale part of the "scales"" map only! This would // make the logic easier and remove some useless? custom code. if (item.isDefault) { me.scale = scale; } }); Chart.scaleService.addScalesToLayout(this); }, buildOrUpdateControllers: function() { var me = this; var types = []; var newControllers = []; helpers.each(me.data.datasets, function(dataset, datasetIndex) { var meta = me.getDatasetMeta(datasetIndex); if (!meta.type) { meta.type = dataset.type || me.config.type; } types.push(meta.type); if (meta.controller) { meta.controller.updateIndex(datasetIndex); } else { var ControllerClass = Chart.controllers[meta.type]; if (ControllerClass === undefined) { throw new Error('"' + meta.type + '" is not a chart type.'); } meta.controller = new ControllerClass(me, datasetIndex); newControllers.push(meta.controller); } }, me); if (types.length > 1) { for (var i = 1; i < types.length; i++) { if (types[i] !== types[i - 1]) { me.isCombo = true; break; } } } return newControllers; }, /** * Reset the elements of all datasets * @private */ resetElements: function() { var me = this; helpers.each(me.data.datasets, function(dataset, datasetIndex) { me.getDatasetMeta(datasetIndex).controller.reset(); }, me); }, /** * Resets the chart back to it's state before the initial animation */ reset: function() { this.resetElements(); this.tooltip.initialize(); }, update: function(animationDuration, lazy) { var me = this; updateConfig(me); if (plugins.notify(me, 'beforeUpdate') === false) { return; } // In case the entire data object changed me.tooltip._data = me.data; // Make sure dataset controllers are updated and new controllers are reset var newControllers = me.buildOrUpdateControllers(); // Make sure all dataset controllers have correct meta data counts helpers.each(me.data.datasets, function(dataset, datasetIndex) { me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements(); }, me); me.updateLayout(); // Can only reset the new controllers after the scales have been updated helpers.each(newControllers, function(controller) { controller.reset(); }); me.updateDatasets(); // Do this before render so that any plugins that need final scale updates can use it plugins.notify(me, 'afterUpdate'); if (me._bufferedRender) { me._bufferedRequest = { lazy: lazy, duration: animationDuration }; } else { me.render(animationDuration, lazy); } }, /** * Updates the chart layout unless a plugin returns `false` to the `beforeLayout` * hook, in which case, plugins will not be called on `afterLayout`. * @private */ updateLayout: function() { var me = this; if (plugins.notify(me, 'beforeLayout') === false) { return; } Chart.layoutService.update(this, this.width, this.height); /** * Provided for backward compatibility, use `afterLayout` instead. * @method IPlugin#afterScaleUpdate * @deprecated since version 2.5.0 * @todo remove at version 3 * @private */ plugins.notify(me, 'afterScaleUpdate'); plugins.notify(me, 'afterLayout'); }, /** * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate` * hook, in which case, plugins will not be called on `afterDatasetsUpdate`. * @private */ updateDatasets: function() { var me = this; if (plugins.notify(me, 'beforeDatasetsUpdate') === false) { return; } for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { me.updateDataset(i); } plugins.notify(me, 'afterDatasetsUpdate'); }, /** * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate` * hook, in which case, plugins will not be called on `afterDatasetUpdate`. * @private */ updateDataset: function(index) { var me = this; var meta = me.getDatasetMeta(index); var args = { meta: meta, index: index }; if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) { return; } meta.controller.update(); plugins.notify(me, 'afterDatasetUpdate', [args]); }, render: function(duration, lazy) { var me = this; if (plugins.notify(me, 'beforeRender') === false) { return; } var animationOptions = me.options.animation; var onComplete = function(animation) { plugins.notify(me, 'afterRender'); helpers.callback(animationOptions && animationOptions.onComplete, [animation], me); }; if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) { var animation = new Chart.Animation({ numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps easing: animationOptions.easing, render: function(chart, animationObject) { var easingFunction = helpers.easingEffects[animationObject.easing]; var currentStep = animationObject.currentStep; var stepDecimal = currentStep / animationObject.numSteps; chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep); }, onAnimationProgress: animationOptions.onProgress, onAnimationComplete: onComplete }); Chart.animationService.addAnimation(me, animation, duration, lazy); } else { me.draw(); // See https://github.com/chartjs/Chart.js/issues/3781 onComplete(new Chart.Animation({numSteps: 0, chart: me})); } return me; }, draw: function(easingValue) { var me = this; me.clear(); if (easingValue === undefined || easingValue === null) { easingValue = 1; } me.transition(easingValue); if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) { return; } // Draw all the scales helpers.each(me.boxes, function(box) { box.draw(me.chartArea); }, me); if (me.scale) { me.scale.draw(); } me.drawDatasets(easingValue); // Finally draw the tooltip me.tooltip.draw(); plugins.notify(me, 'afterDraw', [easingValue]); }, /** * @private */ transition: function(easingValue) { var me = this; for (var i=0, ilen=(me.data.datasets || []).length; i= 0; --i) { if (me.isDatasetVisible(i)) { me.drawDataset(i, easingValue); } } plugins.notify(me, 'afterDatasetsDraw', [easingValue]); }, /** * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw` * hook, in which case, plugins will not be called on `afterDatasetDraw`. * @private */ drawDataset: function(index, easingValue) { var me = this; var meta = me.getDatasetMeta(index); var args = { meta: meta, index: index, easingValue: easingValue }; if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) { return; } meta.controller.draw(easingValue); plugins.notify(me, 'afterDatasetDraw', [args]); }, // Get the single element that was clicked on // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw getElementAtEvent: function(e) { return Chart.Interaction.modes.single(this, e); }, getElementsAtEvent: function(e) { return Chart.Interaction.modes.label(this, e, {intersect: true}); }, getElementsAtXAxis: function(e) { return Chart.Interaction.modes['x-axis'](this, e, {intersect: true}); }, getElementsAtEventForMode: function(e, mode, options) { var method = Chart.Interaction.modes[mode]; if (typeof method === 'function') { return method(this, e, options); } return []; }, getDatasetAtEvent: function(e) { return Chart.Interaction.modes.dataset(this, e, {intersect: true}); }, getDatasetMeta: function(datasetIndex) { var me = this; var dataset = me.data.datasets[datasetIndex]; if (!dataset._meta) { dataset._meta = {}; } var meta = dataset._meta[me.id]; if (!meta) { meta = dataset._meta[me.id] = { type: null, data: [], dataset: null, controller: null, hidden: null, // See isDatasetVisible() comment xAxisID: null, yAxisID: null }; } return meta; }, getVisibleDatasetCount: function() { var count = 0; for (var i = 0, ilen = this.data.datasets.length; i