├── tmp
└── projects
│ └── .gitkeep
├── public
├── images
│ └── favicon.png
└── bootstrap
│ ├── fonts
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.ttf
│ ├── glyphicons-halflings-regular.woff
│ └── glyphicons-halflings-regular.svg
│ ├── css
│ ├── bootstrap-theme.min.css
│ ├── bootstrap-theme.css.map
│ └── bootstrap-theme.css
│ └── js
│ └── bootstrap.min.js
├── activator-launch-1.1.2.jar
├── project
├── build.properties
└── plugins.sbt
├── app
├── views
│ ├── index.scala.html
│ ├── project.scala.html
│ ├── sidebar
│ │ ├── projects.scala.html
│ │ └── lock.scala.html
│ ├── welcome.scala.html
│ ├── layout.scala.html
│ ├── commits.scala.html
│ └── main.scala.html
├── assets
│ ├── stylesheets
│ │ ├── main.less
│ │ └── commits.less
│ └── javascripts
│ │ └── main.coffee
├── models
│ ├── ProcessEnumerator.scala
│ ├── Lock.scala
│ ├── UnbufferedCommand.scala
│ ├── User.scala
│ ├── LdapSearcher.scala
│ ├── WorkingDir.scala
│ ├── Repo.scala
│ ├── Hook.scala
│ ├── Commit.scala
│ └── Project.scala
├── Global.scala
└── controllers
│ └── Application.scala
├── .gitignore
├── .travis.yml
├── test
├── IntegrationSpec.scala
└── ApplicationSpec.scala
├── Dockerfile
├── LICENSE
├── conf
├── messages.ja
├── messages
├── routes
└── application.conf
├── README.md
└── activator
/tmp/projects/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edvakf/pploy/HEAD/public/images/favicon.png
--------------------------------------------------------------------------------
/activator-launch-1.1.2.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edvakf/pploy/HEAD/activator-launch-1.1.2.jar
--------------------------------------------------------------------------------
/public/bootstrap/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edvakf/pploy/HEAD/public/bootstrap/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/public/bootstrap/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edvakf/pploy/HEAD/public/bootstrap/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/public/bootstrap/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/edvakf/pploy/HEAD/public/bootstrap/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | #Activator-generated Properties
2 | #Sun Aug 03 21:56:29 JST 2014
3 | template.uuid=688014f0-0585-43e3-a9fa-d62f7d12f1ab
4 | sbt.version=0.13.15
5 |
--------------------------------------------------------------------------------
/app/views/index.scala.html:
--------------------------------------------------------------------------------
1 | @()(implicit request: Request[AnyContent], messages: Messages, flash: Flash)
2 |
3 | @layout{
4 | @welcome()
5 | }{
6 | @sidebar.projects(None)
7 | }
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | logs
2 | project/project
3 | project/target
4 | target
5 | tmp
6 | .history
7 | dist
8 | /.idea
9 | /*.iml
10 | /out
11 | /.idea_modules
12 | /.classpath
13 | /.project
14 | /RUNNING_PID
15 | /.settings
16 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/main.less:
--------------------------------------------------------------------------------
1 | .sidebar-section {
2 | margin-top: 1em;
3 | margin-bottom: 1em;
4 | padding: 1em;
5 | }
6 | .box {
7 | background-color: #eee;
8 | }
9 | .log-frame {
10 | border: solid black 2px !important; //override twitter bootstrap's embed-responsive iframe
11 | }
12 | .log-frame.loading {
13 | transition-duration: 0.4s;
14 | transition-property: border;
15 | border: solid red 2px !important;
16 | }
17 |
--------------------------------------------------------------------------------
/app/views/project.scala.html:
--------------------------------------------------------------------------------
1 | @import models.Project
2 | @import models.User
3 |
4 | @(currentProject: Project, currentUserOption: Option[User], useWebSocket: Boolean)(implicit request: Request[AnyContent], messages: Messages, flash: Flash)
5 |
6 | @layout{
7 | @main(currentProject, currentUserOption, useWebSocket)
8 | }{
9 | @sidebar.lock(currentProject, currentUserOption)
10 | @sidebar.projects(Some(currentProject))
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: scala
2 | scala: 2.11.7
3 | sudo: false
4 | jdk: oraclejdk8
5 | script: sbt dist
6 | deploy:
7 | provider: releases
8 | api_key:
9 | secure: HqzrciYL691qxmq8/1gv7j+Ogm9MHVC/tsfW+7rO0LvJn8NbWH6/0gXSkt4CtP0PimmQt+vh98x+tPALwPJCSoV38KEfdtTTD9yBdbaDK17YBEXsbwwr5TRPX7K/Kv5+nhtAtWxXhln+12S7sa7i0q4Va4Ssh2MN5I0A8vFJSL4=
10 | file: target/universal/pploy.zip
11 | on:
12 | repo: edvakf/pploy
13 | all_branches: true
14 | tags: true
15 |
--------------------------------------------------------------------------------
/test/IntegrationSpec.scala:
--------------------------------------------------------------------------------
1 | import org.scalatest.{ FunSuite, Matchers }
2 | import play.api.test.TestServer
3 | import play.api.test.Helpers._
4 |
5 | class IntegrationSpec extends FunSuite with Matchers {
6 |
7 | test("work from within a browser") {
8 | val port = 3333
9 | running(TestServer(port), HTMLUNIT) { browser =>
10 | browser.goTo("http://localhost:" + port)
11 | browser.$("h2").getTexts.get(0) should be("Welcome to pploy")
12 | }
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/test/ApplicationSpec.scala:
--------------------------------------------------------------------------------
1 | import org.scalatestplus.play.PlaySpec
2 | import play.api.test.Helpers._
3 | import play.api.test._
4 |
5 | class ApplicationSpec extends PlaySpec {
6 |
7 | "Application" must {
8 | "should render the index page" in {
9 | running(FakeApplication()) {
10 | val home = route(FakeRequest(GET, "/")).get
11 | status(home) must be(OK)
12 | contentType(home) must be(Some("text/html"))
13 | contentAsString(home) must include("Welcome to pploy")
14 | }
15 | }
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/app/models/ProcessEnumerator.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import play.api.libs.iteratee.Enumerator
4 | import scala.sys.process._
5 | import scala.concurrent.ExecutionContext.Implicits.global
6 |
7 | object ProcessEnumerator {
8 | // wraps a ProcessBuilder with Play's Enumerator
9 | // and executes the process in Future
10 | // so that the process' output can be streamed
11 | def apply(process: ProcessBuilder): Enumerator[String] = {
12 | Enumerator.enumerate[String](
13 | process.lineStream_!(ProcessLogger(line => ())).map { line => line + "\n" }
14 | )
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | ### how to build in docker
2 | #
3 | # TAG=$(git log --oneline -n 1 | cut -f 1 -d ' ')
4 | # NAME=pploy-$TAG
5 | #
6 | # docker build -t pploy-build .
7 | # mkdir -p $PWD/.ivy2 $PWD/.sbt
8 | # docker rm -f $NAME || true
9 | # docker run --name=$NAME -v $PWD/.ivy2:/root/.ivy2 -v $PWD/.sbt:/root/.sbt \
10 | # pploy-build \
11 | # bash -c './activator dist'
12 | #
13 | # docker cp $NAME:/pploy/target/universal/pploy-1.0-SNAPSHOT.zip ./
14 | # docker rm -f $NAME || true
15 |
16 | FROM java:8-jdk
17 |
18 | COPY . /pploy
19 | WORKDIR /pploy
20 |
21 | VOLUME ["/root/.ivy2", "/root/.sbt"]
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This software is licensed under the Apache 2 license, quoted below.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this project except in compliance with
4 | the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
5 |
6 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
7 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
8 | language governing permissions and limitations under the License.
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
2 |
3 | // The Play plugin
4 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.11")
5 |
6 | // web plugins
7 |
8 | addSbtPlugin("com.typesafe.sbt" % "sbt-coffeescript" % "1.0.0")
9 |
10 | addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.0.0")
11 |
12 | addSbtPlugin("com.typesafe.sbt" % "sbt-jshint" % "1.0.0")
13 |
14 | addSbtPlugin("com.typesafe.sbt" % "sbt-rjs" % "1.0.1")
15 |
16 | addSbtPlugin("com.typesafe.sbt" % "sbt-digest" % "1.0.0")
17 |
18 | addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.6.0")
19 |
--------------------------------------------------------------------------------
/app/views/sidebar/projects.scala.html:
--------------------------------------------------------------------------------
1 | @import models.Project
2 |
3 | @(currentProjectOption: Option[Project])(implicit messages: Messages)
4 |
5 |
21 |
--------------------------------------------------------------------------------
/app/models/Lock.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import play.api.Play.current
4 | import play.api.cache.Cache
5 |
6 | import org.joda.time._
7 |
8 | case class Lock(project: Project, user: User, endTime: DateTime) {
9 | def save() = Cache.set("lock_" + project.name, this)
10 | def delete() = Cache.remove("lock_" + project.name)
11 |
12 | def secondsLeft = Seconds.secondsBetween(new DateTime(), endTime).getSeconds
13 | def minutesAndSecondsLeft = f"${secondsLeft / 60}%02d:${secondsLeft % 60}%02d"
14 | }
15 |
16 | object Lock {
17 | def fetch(project: Project) = {
18 | Cache.getAs[Lock]("lock_" + project.name)
19 | }
20 |
21 | def save(project: Project, user: User, endTime: DateTime) = {
22 | new Lock(project, user, endTime).save()
23 | }
24 | }
--------------------------------------------------------------------------------
/app/models/UnbufferedCommand.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import scala.sys.process._
4 |
5 | object UnbufferedCommand {
6 | // libc does not line buffer if output is not a terminal, instead use full buffering.
7 | // `script` runs a given command in a pseudo terminal.
8 | // it also redirects stderr to stdout which fits our usage
9 |
10 | // see: http://unix.stackexchange.com/questions/25372/turn-off-buffering-in-pipe
11 |
12 | private lazy val hasStdbufCommand = {
13 | 0 == Process("which stdbuf").!
14 | }
15 |
16 | def apply(command: String): Seq[String] = {
17 | if (hasStdbufCommand) {
18 | Seq("bash", "-c", "stdbuf -oL -eL " + command + " 2>&1") // Linux
19 | } else {
20 | Seq("script", "-q", "/dev/null", "bash", "-c", command) // Mac OS X
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/conf/messages.ja:
--------------------------------------------------------------------------------
1 | welcome=pployへようこそ
2 | welcome.click_sidebar=サイドバーのプロジェクト名をクリックしてください
3 | welcome.add_project=プロジェクトを追加する
4 | welcome.remove_project=プロジェクトを削除する
5 | welcome.remove_project_confirmation=プロジェクト"{0}"を本当に削除しますか?
6 | main.recent_commits=最近のコミット
7 | main.checkout=チェックアウト
8 | main.deploy={0}環境にデプロイ
9 | main.previous_log=前回実行ログ
10 | sidebar.header.projects=プロジェクト
11 | sidebar.user.noselect=選択してください
12 | sidebar.lock.user=デプロイ中
13 | sidebar.lock.extend={0}分延長する
14 | sidebar.lock.release=デプロイを終了する
15 | sidebar.lock.gain=デプロイを開始する
16 | sidebar.lock.time_left=残り時間
17 | lock.operation.expired=デプロイ時間切れです
18 | lock.operation.taken=他の人がデプロイ中です
19 | hook.lock.gained=[{0}] {1}さんがデプロイ中になりました
20 | hook.lock.released=[{0}] {1}さんがデプロイを終了しました
21 | hook.lock.extended=[{0}] {1}さんがデプロイを延長しました
22 | hook.deployed=[{0}] {1}さんが{2}環境にデプロイしました
23 |
--------------------------------------------------------------------------------
/conf/messages:
--------------------------------------------------------------------------------
1 | welcome=Welcome to pploy
2 | welcome.click_sidebar=Click on a project in the sidebar.
3 | welcome.add_project=Add a project
4 | welcome.remove_project=Remove a project
5 | welcome.remove_project_confirmation=Are you sure to remove {0}?
6 | main.recent_commits=Recent Commits
7 | main.checkout=Checkout
8 | main.deploy=Deploy to {0} environment
9 | main.deploy.staging=Deploy to staging
10 | main.previous_log=Previous log
11 | sidebar.header.projects=Projects
12 | sidebar.user.noselect=Please Select
13 | sidebar.lock.user=Working
14 | sidebar.lock.extend=Extend {0} minutes
15 | sidebar.lock.release=Finish deploying
16 | sidebar.lock.gain=Start deploying
17 | sidebar.lock.time_left=Time left
18 | lock.operation.expired=Your lock is expired
19 | lock.operation.taken=Lock to this project is held by someone else
20 | hook.lock.gained=[{0}] {1} has started a deploy session
21 | hook.lock.released=[{0}] {1} has finished a deploy session
22 | hook.lock.extended=[{0}] {1} has extended the deploy session
23 | hook.deployed=[{0}] {1} has deployed to {2} environment
24 |
--------------------------------------------------------------------------------
/conf/routes:
--------------------------------------------------------------------------------
1 | # Routes
2 | # This file defines all application routes (Higher priority routes first)
3 | # ~~~~
4 |
5 | # Home page
6 | GET / controllers.Application.index()
7 | POST /_/create controllers.Application.create()
8 | GET /:project controllers.Application.project(project)
9 | GET /:project/lock controllers.Application.lockUser(project)
10 | POST /:project/lock controllers.Application.lock(project)
11 | POST /:project/checkout controllers.Application.checkout(project)
12 | GET /:project/checkout controllers.Application.checkoutWS(project, ref)
13 | GET /:project/commits controllers.Application.commits(project)
14 | POST /:project/deploy controllers.Application.deploy(project)
15 | GET /:project/deploy controllers.Application.deployWS(project, target)
16 | GET /:project/logs controllers.Application.logs(project, full: Long ?= 0)
17 | POST /:project/remove controllers.Application.remove(project)
18 |
19 | # Map static resources from the /public folder to the /assets URL path
20 | GET /assets/*file controllers.Assets.at(path="/public", file)
21 |
--------------------------------------------------------------------------------
/app/views/welcome.scala.html:
--------------------------------------------------------------------------------
1 | @import helper._
2 |
3 | @()(implicit request: Request[AnyContent], messages: Messages)
4 |
5 | @Messages("welcome")
6 |
7 | @Messages("welcome.click_sidebar")
8 |
9 | @Messages("welcome.add_project")
10 |
11 |
20 |
21 | @if(!Project.allNames.isEmpty) {
22 | @Messages("welcome.remove_project")
23 |
24 |
25 | @Project.allNames.map { name =>
26 |
27 | |
28 |
33 | |
34 |
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/app/models/User.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import play.api.Logger
4 | import play.api.Play.current
5 | import play.api.cache.Cache
6 |
7 | case class User(name: String) {
8 | if (!User.allNames.contains(name)) {
9 | throw new IllegalArgumentException("bad user name")
10 | }
11 | }
12 |
13 | object User {
14 | def allNames: Seq[String] = {
15 | current.configuration.getString("pploy.ldap.url").map { url =>
16 | // if url is present, assume others are present too
17 |
18 | val login = current.configuration.getString("pploy.ldap.login").get
19 | val password = current.configuration.getString("pploy.ldap.password").get
20 | val search = current.configuration.getString("pploy.ldap.search").get
21 |
22 | def fetchLdap = {
23 | Logger.info("fetching users from LDAP: " + search)
24 | new LdapSearcher(url, login, password).search(search)
25 | }
26 |
27 | current.configuration.getInt("pploy.ldap.cachettl").fold(fetchLdap) { ttl =>
28 | Cache.getAs[Seq[String]]("ldap_users").getOrElse {
29 | val names = fetchLdap
30 | Cache.set("ldap_users", names, ttl)
31 | names
32 | }
33 | }
34 |
35 | }.getOrElse(
36 | // otherwise get users from pploy.users
37 | current.configuration.getStringSeq("pploy.users").get
38 | )
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/models/LdapSearcher.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import java.util
4 | import javax.naming.Context
5 | import javax.naming.directory.SearchControls
6 | import javax.naming.ldap.InitialLdapContext
7 |
8 | import scala.collection.mutable.ListBuffer
9 |
10 | class LdapSearcher(url: String, user: String, pass: String) {
11 | val env = new util.Hashtable[String, String]()
12 | env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory")
13 | env.put(Context.PROVIDER_URL, url)
14 | env.put(Context.SECURITY_PRINCIPAL, user)
15 | env.put(Context.SECURITY_CREDENTIALS, pass)
16 |
17 | // http://stackoverflow.com/questions/2172831/how-do-a-ldap-search-authenticate-against-this-ldap-in-java
18 | def search(where: String) = {
19 | val ctx = new InitialLdapContext(env, null)
20 | ctx.setRequestControls(null)
21 | val namingEnum = ctx.search(where, "cn=*", getSimpleSearchControls)
22 | val users = new ListBuffer[String]
23 | while (namingEnum.hasMore) {
24 | val attr = namingEnum.next().getAttributes.get("cn")
25 | if (attr.size > 0) users += attr.get(0).toString
26 | }
27 | namingEnum.close()
28 | users.toSeq
29 | }
30 |
31 | private def getSimpleSearchControls = {
32 | val searchControls = new SearchControls()
33 | searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE)
34 | searchControls.setTimeLimit(30000)
35 | searchControls
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/models/WorkingDir.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import java.io.File
4 |
5 | import play.api.Logger
6 | import play.api.Play.current
7 | import scala.collection.JavaConversions._
8 |
9 | object WorkingDir {
10 |
11 | val dirname = current.configuration.getString("pploy.dir").getOrElse(sys.error("pploy.dir is not set"))
12 |
13 | val projectsDir = new File(dirname, "projects")
14 |
15 | val logsDir = new File(dirname, "logs")
16 |
17 | def setup(): Unit = {
18 | mkdir(new File(dirname))
19 | mkdir(projectsDir)
20 | mkdir(logsDir)
21 | }
22 |
23 | def projectDir(project: String) = new File(projectsDir, project)
24 |
25 | def logFile(project: String) = new File(logsDir, project + ".log")
26 |
27 | private def mkdir(dir: File): Unit = {
28 | if (!dir.exists()) {
29 | Logger.info("making directory: " + dir.toString)
30 | dir.mkdirs()
31 | } else if (!dir.isDirectory) {
32 | throw new RuntimeException("working directory is taken!")
33 | }
34 | }
35 |
36 | def projects = {
37 | // FIXME: handle irregular files in projects dir
38 | projectsDir.listFiles().map { _.getName }.sorted
39 | }
40 |
41 | def removeProjectFiles(project: String) = {
42 | delete(projectDir(project))
43 | delete(logFile(project))
44 | }
45 |
46 | private def delete(f: File): Unit = {
47 | if (f.exists) {
48 | if (f.isDirectory) {
49 | f.listFiles.map(delete)
50 | }
51 | if (!f.delete())
52 | throw new RuntimeException("Failed to delete file: " + f)
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/views/layout.scala.html:
--------------------------------------------------------------------------------
1 | @(mainContent: Html)(sidebarContent: Html)(implicit lang: Lang, flash: Flash)
2 |
3 |
4 |
5 |
6 |
7 | pploy
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
22 |
23 |
24 | @flash.get("message").map{ message =>
25 |
28 | }
29 |
30 |
31 |
32 |
33 | @mainContent
34 |
35 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/Global.scala:
--------------------------------------------------------------------------------
1 | import java.net.URL
2 |
3 | import play.api.Play.current
4 | import play.api._
5 | import play.api.mvc._
6 | import play.filters.csrf._
7 | import play.api.mvc.Results._
8 | import scala.concurrent.Future
9 | import org.apache.commons.lang3.exception.ExceptionUtils
10 | import models._
11 |
12 | object Global extends WithFilters(CSRFFilter()) with GlobalSettings {
13 |
14 | lazy val isDev = Play.isDev(current)
15 |
16 | override def onStart(app: Application) {
17 | WorkingDir.setup()
18 | }
19 |
20 | private def backUrl(request: RequestHeader) = {
21 | request.headers.get("referer") match {
22 | case Some(url) => new URL(url).getFile
23 | case None => controllers.routes.Application.index().toString()
24 | }
25 | }
26 |
27 | override def onError(request: RequestHeader, ex: Throwable) = {
28 | if (isDev) super.onError(request, ex)
29 | else {
30 | Logger.info(ex.getMessage)
31 | Logger.info(ExceptionUtils.getStackTrace(ex))
32 | Future.successful(
33 | Redirect(backUrl(request))
34 | .flashing("message" -> ex.getMessage)
35 | )
36 | }
37 | }
38 |
39 | override def onHandlerNotFound(request: RequestHeader) = {
40 | if (isDev) super.onHandlerNotFound(request)
41 | else {
42 | Future.successful(
43 | Redirect(backUrl(request))
44 | )
45 | }
46 | }
47 |
48 | override def onBadRequest(request: RequestHeader, error: String) = {
49 | if (isDev) super.onBadRequest(request, error)
50 | else {
51 | Future.successful(
52 | Redirect(backUrl(request))
53 | .flashing("message" -> error)
54 | )
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/commits.less:
--------------------------------------------------------------------------------
1 | body, html {
2 | margin: 0;
3 | padding: 0;
4 | }
5 | .commits {
6 | table {
7 | width: 100%;
8 | border-collapse: collapse;
9 | box-sizing: border-box;
10 | }
11 |
12 | th, td {
13 | padding: 3px;
14 | border: solid gray 1px;
15 | box-sizing: border-box;
16 |
17 | // http://stackoverflow.com/questions/19068909/why-is-box-sizing-acting-different-on-table-vs-div?answertab=active#tab-top
18 | &:first-child {
19 | border-left: none;
20 | }
21 | &:last-child {
22 | border-right: none;
23 | }
24 | }
25 |
26 | th {
27 | border-bottom: solid black 2px;
28 | background: #eee;
29 | font-size: 14px;
30 | }
31 |
32 | td {
33 | font-size: 14px;
34 | font-family: "Consolas", "Courier New", "Courier", "monospace";
35 | }
36 |
37 | .header {
38 | background: #eee;
39 | }
40 |
41 | .subject {
42 | background: #eee;
43 | font-weight: bold;
44 | }
45 |
46 | .code {
47 | padding: 10px 6px 20px;
48 |
49 | pre {
50 | margin: 0;
51 | font-family: "Consolas", "Courier New", "Courier", "monospace";
52 | }
53 | }
54 | }
55 |
56 | .hash, .hash:link, .hash:visited {
57 | color: #2fa0bb;
58 | font-weight: bold;
59 | text-decoration: none;
60 | }
61 |
62 | .ref {
63 | display: inline-block;
64 | margin: 2px 0;
65 | padding: 2px 4px;
66 | border-radius: 4px;
67 | background: #999;
68 | color: #fff;
69 | font-size: 12px;
70 |
71 | &.tag {
72 | background: #418be5;
73 | color: #fff;
74 | }
75 |
76 | &.master {
77 | background: #00c53d;
78 | color: #fff;
79 | }
80 |
81 | &.head {
82 | background: #e53d5f;
83 | color: #fff;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/app/views/sidebar/lock.scala.html:
--------------------------------------------------------------------------------
1 | @import helper._
2 | @import models.Project
3 | @import models.User
4 |
5 | @(currentProject: Project, currentUserOption: Option[User])(implicit request: Request[AnyContent], messages: Messages)
6 |
7 |
38 |
39 |
--------------------------------------------------------------------------------
/app/models/Repo.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import java.io._
4 | import org.eclipse.jgit.api._
5 | import org.eclipse.jgit.transport.URIish
6 | import play.api.Play._
7 | import scala.collection.JavaConversions._
8 | import scala.sys.process.Process
9 |
10 | case class Repo(name: String) {
11 | lazy val dir = WorkingDir.projectDir(name)
12 | lazy val git = Git.open(dir)
13 |
14 | def readmeFile: Option[File] = {
15 | val file = new File(dir, ".deploy/config/readme.html")
16 | if (file.isFile) { Some(file) } else { None }
17 | }
18 |
19 | def deployEnvsFile: Option[File] = {
20 | val file = new File(dir, ".deploy/config/deploy_envs")
21 | if (file.isFile) { Some(file) } else { None }
22 | }
23 |
24 | val deployCommand = ".deploy/bin/deploy"
25 |
26 | def checkoutCommand = {
27 | val checkoutScript = ".deploy/bin/checkout_overwrite"
28 | // if checkout_overwrite script exists
29 | if (new File(dir, checkoutScript).isFile) {
30 | checkoutScript
31 | } else {
32 | "bash -x -c '" + Seq(
33 | "git fetch --prune",
34 | "git checkout -f $DEPLOY_COMMIT",
35 | "git reset --hard $DEPLOY_COMMIT",
36 | "git clean -fdx",
37 | "git submodule sync",
38 | "git submodule init",
39 | "git submodule update --recursive"
40 | ).mkString(" && ") + "'"
41 | }
42 | }
43 |
44 | def commits = {
45 | val logs = Process(Seq("git", "log", "-n", Repo.commitLength.toString) ++ Commit.gitLogOption, dir).!!
46 | Commit.parse(logs)
47 | }
48 | }
49 |
50 | object Repo {
51 | val commitLength = current.configuration.getInt("pploy.commits.length").getOrElse(20)
52 |
53 | def clone(url: String): Repo = {
54 | val uriish = new URIish(url)
55 | Process(Seq("git", "clone", url, "--depth", Repo.commitLength.toString), WorkingDir.projectsDir).!
56 | Repo(uriish.getHumanishName)
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/views/commits.scala.html:
--------------------------------------------------------------------------------
1 | @import models.Commit
2 |
3 | @(commits: Iterable[Commit])
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | @commits.map { commit =>
16 |
45 |
46 | | @commit.subject |
47 |
48 |
49 |
50 | @commit.nameStatus
51 | |
52 |
53 | }
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/app/models/Hook.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import play.api.Play._
4 | import play.api.i18n.Messages.Implicits._
5 | import play.api.i18n.{ Lang, Messages }
6 | import play.api.libs.json._
7 | import play.api.libs.ws.WS
8 |
9 | object Hook {
10 | val idobata = current.configuration.getString("pploy.idobata.endpoint").map {
11 | url => new IdobataGenericHook(url)
12 | }
13 | val slack = current.configuration.getString("pploy.slack.endpoint").map {
14 | url => new SlackIncomingWebHook(url)
15 | }
16 | implicit val lang = Lang.availables.head
17 |
18 | def lockGained(projectName: String, userName: String) = {
19 | idobata.foreach { _.post(Messages("hook.lock.gained", projectName, userName)) }
20 | slack.foreach { _.post(Messages("hook.lock.gained", projectName, userName)) }
21 | }
22 |
23 | def lockReleased(projectName: String, userName: String) = {
24 | idobata.foreach { _.post(Messages("hook.lock.released", projectName, userName)) }
25 | slack.foreach { _.post(Messages("hook.lock.released", projectName, userName)) }
26 | }
27 |
28 | def lockExtended(projectName: String, userName: String) = {
29 | idobata.foreach { _.post(Messages("hook.lock.extended", projectName, userName)) }
30 | slack.foreach { _.post(Messages("hook.lock.extended", projectName, userName)) }
31 | }
32 |
33 | def deployed(projectName: String, userName: String, target: String) = {
34 | idobata.foreach { _.post(Messages("hook.deployed", projectName, userName, target)) }
35 | slack.foreach { _.post(Messages("hook.deployed", projectName, userName, target)) }
36 | }
37 |
38 | }
39 |
40 | class IdobataGenericHook(endpoint: String) {
41 | def post(html: String): Unit = {
42 | WS.url(endpoint).post(Map("format" -> Seq("html"), "source" -> Seq(html)))
43 | }
44 | }
45 |
46 | class SlackIncomingWebHook(endpoint: String) {
47 | def post(text: String): Unit = {
48 | val payload = Json.obj(
49 | "text" -> text
50 | )
51 | WS.url(endpoint).post(payload)
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/models/Commit.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import org.joda.time.DateTime
4 | import org.joda.time.format.{ ISODateTimeFormat, DateTimeFormat }
5 |
6 | case class Commit(hash: String, dateTime: DateTime, author: String, otherRefs: Seq[String], subject: String, body: String, nameStatus: String) {
7 | def dateTimeString = dateTime.toString(DateTimeFormat.longDateTime())
8 | }
9 |
10 | object Commit {
11 | private val delim1 = "1PPLOY1YOLPP1"
12 | private val delim2 = "2PPLOY2YOLPP2"
13 |
14 | val gitLogOption = Seq(
15 | "--decorate=full", // prefix refs with refs/heads/, refs/remotes/origin/ and so on
16 | "--name-status", // show list of file diffs
17 | "-m", // show file diffs for merge commit
18 | "--first-parent", // -m shows file diffs from each parent. --first-parent make it from the first parent
19 | s"--pretty=format:${delim1}%H${delim2}%ci${delim2}%an${delim2}%d${delim2}%s${delim2}%b${delim2}"
20 | )
21 |
22 | def parse(logs: String): Seq[Commit] = {
23 | logs.split(delim1).drop(1).map { log =>
24 | log.split(delim2).toSeq match {
25 | case Seq(hash, isoLikeDate, author, refs, subject, body, nameStatus) =>
26 | val re = """^ \((.*?)\)$""".r // git 1.7 doesn't have `%D` format, which is the same as `%d` without the parens
27 | val otherRefs: Seq[String] = refs match {
28 | case re(foo) =>
29 | foo.split(", ")
30 | .flatMap(_.split(" -> ")) // master becomes "HEAD -> refs/head/master"
31 | .filterNot(_ == "") // filter out the case when refs is an empty string
32 | case _ =>
33 | Seq()
34 | }
35 |
36 | Commit(
37 | hash,
38 | // newer version of git has `%cI` format which can be parsed with `ISODateTimeFormat.dateTimeNoMillis`, but git 1.7 doesn't...
39 | DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss Z").parseDateTime(isoLikeDate),
40 | author,
41 | otherRefs,
42 | subject,
43 | body.trim,
44 | nameStatus.trim
45 | )
46 | case _ =>
47 | throw new RuntimeException(s"failed to parse commit log: ${log}")
48 | }
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/app/views/main.scala.html:
--------------------------------------------------------------------------------
1 | @import helper._
2 | @import models.Project
3 | @import models.User
4 |
5 | @(currentProject: Project, currentUserOption: Option[User], useWebSocket: Boolean)(implicit request: Request[AnyContent], messages: Messages)
6 |
7 | @if(useWebSocket) {
8 |
9 | }
10 |
11 |
12 |
13 |
@currentProject.name
14 |
15 |
16 | @if(currentProject.isLockedBy(currentUserOption)) {
17 |
18 |
@Messages("main.checkout")
19 |
24 |
31 |
32 | }
33 |
34 |
35 | @currentProject.readme.map { readme =>
36 |
@Html(readme)
37 | }
38 |
39 |
40 |
41 |
42 |
43 |
@Messages("main.recent_commits")
44 |
45 |
46 |
47 |
48 |
@Messages("main.previous_log")
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/conf/application.conf:
--------------------------------------------------------------------------------
1 | # This is the main configuration file for the application.
2 | # ~~~~~
3 |
4 | # Secret key
5 | # ~~~~~
6 | # The secret key is used to secure cryptographics functions.
7 | #
8 | # This must be changed for production, but we recommend not changing it in this file.
9 | #
10 | # See http://www.playframework.com/documentation/latest/ApplicationSecret for more details.
11 | application.secret="3U=sXah8dWOyDVf_er/DInCyBG]3aF1oyTjY_vKX[`mm/sr86BZ]uWB4o>Z]9e11"
12 |
13 | # The application languages
14 | # ~~~~~
15 | application.langs="en,ja"
16 |
17 | # Global object class
18 | # ~~~~~
19 | # Define the Global object class for this application.
20 | # Default to Global in the root package.
21 | # application.global=Global
22 |
23 | # Router
24 | # ~~~~~
25 | # Define the Router object to use for this application.
26 | # This router will be looked up first when the application is starting up,
27 | # so make sure this is the entry point.
28 | # Furthermore, it's assumed your route file is named properly.
29 | # So for an application router like `my.application.Router`,
30 | # you may need to define a router file `conf/my.application.routes`.
31 | # Default to Routes in the root package (and conf/routes)
32 | # application.router=my.application.Routes
33 |
34 | # Database configuration
35 | # ~~~~~
36 | # You can declare as many datasources as you want.
37 | # By convention, the default datasource is named `default`
38 | #
39 | # db.default.driver=org.h2.Driver
40 | # db.default.url="jdbc:h2:mem:play"
41 | # db.default.user=sa
42 | # db.default.password=""
43 |
44 | # Evolutions
45 | # ~~~~~
46 | # You can disable evolutions if needed
47 | # evolutionplugin=disabled
48 |
49 | # Logger
50 | # ~~~~~
51 | # You can also configure logback (http://logback.qos.ch/),
52 | # by providing an application-logger.xml file in the conf directory.
53 |
54 | # Root logger:
55 | logger.root=ERROR
56 |
57 | # Logger used by the framework:
58 | logger.play=INFO
59 |
60 | # Logger provided to your application:
61 | logger.application=DEBUG
62 |
63 | # pploy specific
64 | pploy.dir="/tmp/pploy"
65 |
66 | pploy.users=["foo", "bar"]
67 |
68 | #pploy.ldap.url="ldap://ldap.example.com:389"
69 | #pploy.ldap.login="cn=someone,dc=example,dc=com"
70 | #pploy.ldap.password="SomeonesPassword"
71 | #pploy.ldap.search="dc=deployers,dc=example,dc=com"
72 | #pploy.ldap.cachettl=3600
73 |
74 | pploy.lock.gainMinutes=20
75 | pploy.lock.extendMinutes=10
76 |
77 | # number of commit logs to show
78 | pploy.commits.length=20
79 |
80 | pploy.commitlogs.lines=1000
81 |
82 | # prefer websocket for streaming logs
83 | # useful when HTTP 1.1 `Content-Encoding: chunked` doesn't go through the proxy
84 | #pploy.preference.websocket=true
85 |
86 | #pploy.idobata.endpoint="https://idobata.io/hook/generic/11112222-3333-4444-5555-666677778888"
87 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **THIS PROJECT IS NO LONGER MAINTAINED IN FAVOR OF https://github.com/edvakf/go-pploy**
2 |
3 | # pploy
4 |
5 | The deploy lock manager for [pixiv](http://www.pixiv.net/).
6 |
7 | With pploy you can;
8 |
9 | * deploy any git repository with a deploy script placed at .deploy/bin/deploy
10 | * add a git repository for deploy list easily
11 | * let your project members lock a project between staging check and production deploy
12 | * configure LDAP info to fetch staffs' names
13 | * notify deployments to [Slack](https://slack.com/) and [idobata](https://idobata.io/)
14 |
15 | ## Requirements to a repository to be deployed
16 |
17 | It must be a git repo.
18 |
19 | It must have an executable file `.deploy/bin/deploy` at the top level of the repo.
20 |
21 | It can optionally have `.deploy/config/readme.html` which will be displayed at it's deploy page.
22 |
23 | The deploy script is run as;
24 |
25 | ```
26 | DEPLOY_USER=foo DEPLOY_ENV=staging .deploy/bin/deploy
27 | ```
28 |
29 | So you can do pretty much anything there, eg. call a Capistrano command.
30 |
31 | ### Changing the `DEPLOY_ENV`s
32 |
33 | By default, `staging` and `production` are the only deploy envs supported. If you want to change them, add a file named `.deploy/config/deploy_envs` in the repo with one line per env.
34 |
35 | ```
36 | preview
37 | staging
38 | production
39 | ```
40 |
41 | ## Config values
42 |
43 | ### Working directory
44 |
45 | ```
46 | pploy.dir="/tmp/pploy"
47 | ```
48 |
49 | The working directory in which repos are cloned and logs are written.
50 |
51 | ### Deploy user names
52 |
53 | ```
54 | pploy.users=["foo", "bar"]
55 | ```
56 |
57 | Names of users who can gain lock of projects.
58 |
59 | ### LDAP
60 |
61 | ```
62 | pploy.ldap.url="ldap://ldap.example.com:389"
63 | pploy.ldap.login="cn=someone,dc=example,dc=com"
64 | pploy.ldap.password="SomeonesPassword"
65 | pploy.ldap.search="dc=deployers,dc=example,dc=com"
66 | pploy.ldap.cachettl=3600
67 | ```
68 |
69 | You can optionally set an LDAP configuration from which to fetch member names.
70 |
71 | LDAP's `cn` (Common Name) values are used.
72 |
73 | If `pploy.ldap.cachettl` is omitted, it caches the names for an hour.
74 |
75 | ### Lock minutes
76 |
77 | ```
78 | pploy.lock.gainMinutes=20
79 | pploy.lock.extendMinutes=10
80 | ```
81 |
82 | These are for how long a project is locked once someone gained and extended a lock.
83 |
84 | ### Slack
85 |
86 | ```
87 | pploy.slack.endpoint="https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/1234567890abcdefghijklmn"
88 | ```
89 |
90 | If enabled, deploy logs are posted to Slack through an Incoming WebHook integration.
91 |
92 | ### Idobata
93 |
94 | ```
95 | pploy.idobata.endpoint="https://idobata.io/hook/generic/11112222-3333-4444-5555-666677778888"
96 | ```
97 |
98 | If enabled, deploy logs are posted to idobata.
99 |
--------------------------------------------------------------------------------
/app/models/Project.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import java.nio.charset.CodingErrorAction
4 |
5 | import org.joda.time.DateTime
6 | import play.api.Logger
7 | import play.api.Play.current
8 | import play.api.i18n.{ Messages, Lang }
9 | import scala.io.{ Source, Codec }
10 | import scala.sys.process._
11 |
12 | case class Project(name: String) {
13 | if (!Project.allNames.contains(name)) {
14 | throw new IllegalArgumentException("bad project name")
15 | }
16 |
17 | lazy val repo = Repo(name)
18 |
19 | private lazy val lock_ = Lock.fetch(this) match {
20 | case Some(l) if l.secondsLeft <= 0 =>
21 | l.delete()
22 | None
23 | case other => other
24 | }
25 | def isLocked = lock_.isDefined
26 |
27 | // This method must be called after `isLocked`.
28 | // Plus, it's not updated during gainLock, extendLock or releaseLock
29 | def lock = lock_.get
30 |
31 | def isLockedBy(user: User): Boolean = {
32 | isLocked && lock.user == user
33 | }
34 | def isLockedBy(user: Option[User]): Boolean = {
35 | user.isDefined && isLockedBy(user.get)
36 | }
37 | def assertLockedByUser(user: User) = {
38 | if (!isLockedBy(user)) new LockStatusException(this, user)
39 | }
40 |
41 | def gainLock(user: User) = {
42 | lock_ match {
43 | case None =>
44 | Lock.save(this, user, new DateTime().plusMinutes(Project.gainMinutes))
45 | case _ =>
46 | throw new LockStatusException(this, user)
47 | }
48 | Hook.lockGained(name, user.name)
49 | }
50 |
51 | def extendLock(user: User) = {
52 | lock_ match {
53 | case Some(lock) if lock.user == user =>
54 | Lock.save(this, user, lock.endTime.plusMinutes(Project.extendMinutes))
55 | case _ =>
56 | throw new LockStatusException(this, user)
57 | }
58 | Hook.lockExtended(name, user.name)
59 | }
60 |
61 | def releaseLock(user: User) = {
62 | lock_ match {
63 | case Some(lock) if lock.user == user =>
64 | lock.delete()
65 | case _ =>
66 | throw new LockStatusException(this, user)
67 | }
68 | Hook.lockReleased(name, user.name)
69 | }
70 |
71 | def checkoutProcess(ref: String) = {
72 | Logger.info(repo.checkoutCommand)
73 |
74 | Process(
75 | UnbufferedCommand(repo.checkoutCommand),
76 | repo.dir,
77 | "DEPLOY_COMMIT" -> ref
78 | )
79 | }
80 |
81 | def deployProcess(user: User, target: String) = {
82 | Logger.info(repo.deployCommand)
83 | Hook.deployed(name, user.name, target)
84 |
85 | Process(
86 | UnbufferedCommand(repo.deployCommand),
87 | repo.dir,
88 | "DEPLOY_ENV" -> target,
89 | "DEPLOY_USER" -> user.name
90 | ) #| Process(Seq("tee", WorkingDir.logFile(name).toString))
91 | }
92 |
93 | def readme: Option[String] = {
94 | repo.readmeFile map { file =>
95 | val codec = Codec.UTF8
96 | codec.onMalformedInput(CodingErrorAction.IGNORE)
97 |
98 | val source = Source.fromFile(file)(codec)
99 | try source.mkString
100 | finally source.close()
101 | }
102 | }
103 |
104 | def deployEnvs: Seq[String] = {
105 | repo.deployEnvsFile match {
106 | case Some(file) =>
107 | val codec = Codec.UTF8
108 | codec.onMalformedInput(CodingErrorAction.IGNORE)
109 |
110 | val source = Source.fromFile(file)(codec)
111 | try {
112 | source.mkString.split("\n").toSeq
113 | } finally {
114 | source.close()
115 | }
116 | case None =>
117 | Seq("staging", "production")
118 | }
119 | }
120 |
121 | def remove() = {
122 | WorkingDir.removeProjectFiles(this.name)
123 | }
124 | }
125 |
126 | object Project {
127 | def allNames = WorkingDir.projects.toSeq
128 |
129 | lazy val gainMinutes = current.configuration.getInt("pploy.lock.gainMinutes").get
130 | lazy val extendMinutes = current.configuration.getInt("pploy.lock.extendMinutes").get
131 |
132 | def apply(repo: Repo): Project = Project(repo.name)
133 | }
134 |
135 | class LockStatusException(message: String = null, cause: Throwable = null)
136 | extends RuntimeException(message, cause) {
137 |
138 | def this(project: Project, user: User) = this(
139 | if (project.isLocked) "lock.operation.taken" else "lock.operation.expired"
140 | )
141 |
142 | }
143 |
--------------------------------------------------------------------------------
/app/assets/javascripts/main.coffee:
--------------------------------------------------------------------------------
1 | $ ->
2 | jsSetting()
3 | countDown()
4 | checkLocked()
5 | commandForm()
6 | submitButtonHack()
7 | checkoutBeforeDeploy()
8 |
9 | $ '.confirm'
10 | .on 'click', ->
11 | return confirm $(this).attr('data-confirm-text')
12 |
13 | iframeFollowScroll = (frame) ->
14 | frame.addClass('loading')
15 |
16 | timer = setInterval ->
17 | contents = frame.contents()
18 | contents.scrollTop contents.height()
19 | clearTimeout(timer) if not frame.hasClass('loading')
20 | return
21 | , 100
22 | return
23 |
24 | iframeStopFollowingScroll = (frame) ->
25 | frame.removeClass('loading')
26 | return
27 |
28 | countDown = ->
29 | elem = $ '.time-left'
30 | return if not elem.length
31 | maxSeconds = + elem.attr 'data-seconds'
32 | startTime = Date.now()
33 | setInterval ->
34 | leftSeconds = maxSeconds - (Date.now() - startTime) / 1000
35 | location.reload() if leftSeconds <= 0
36 | elem.html secondsToString(leftSeconds)
37 | return
38 | , 1000
39 | return
40 |
41 | pad02 = (num) ->
42 | ('0' + num).substr(-2)
43 |
44 | secondsToString = (seconds) ->
45 | pad02(Math.floor(seconds/60)) + ':' + pad02(Math.floor(seconds%60))
46 |
47 | checkLocked = ->
48 | elem = $('#lock-form')
49 | return if not elem.length
50 | checkUrl = elem.attr('action')
51 | lockUser = elem.attr('data-lock-user') or ""
52 | setInterval ->
53 | $.ajax checkUrl
54 | .success (res) ->
55 | if lockUser isnt res
56 | location.reload()
57 | return
58 | return
59 | , 10000
60 | return
61 |
62 | commandForm = ->
63 | $ '.command-form'
64 | .on 'submit', (ev) ->
65 | $ '#command-log'
66 | .removeClass 'hidden'
67 |
68 | commandLog = $ '#command-log iframe'
69 | iframeFollowScroll(commandLog)
70 | setTimeout disableAllButtons, 10
71 |
72 | if window.setting['use_web_socket']
73 | postWebSocket this, commandLog, ->
74 | reloadCommitLog()
75 | iframeStopFollowingScroll(commandLog)
76 | enableAllButtons()
77 | return
78 | ev.preventDefault()
79 | else
80 | commandLog.on 'load', onload = ->
81 | commandLog.off 'load', onload
82 | reloadCommitLog()
83 | iframeStopFollowingScroll(commandLog)
84 | enableAllButtons()
85 | return
86 |
87 | return
88 | return
89 |
90 | reloadCommitLog = ->
91 | commitLog = $ '#commit-log iframe'
92 | commitLog.attr 'src', commitLog.attr 'src'
93 | return
94 |
95 | disableAllButtons = ->
96 | $ 'button'
97 | .each (i, e) ->
98 | $(e).prop 'disabled', true
99 | return
100 | return
101 |
102 | enableAllButtons = ->
103 | $ 'button'
104 | .each (i, e) ->
105 | $(e).prop 'disabled', false
106 | return
107 | return
108 |
109 | postWebSocket = (form, frame, oncomplete) ->
110 | $(frame).contents().find('body').html('')
111 | pre = $(frame).contents().find('pre')
112 |
113 | action = $(form).prop 'action'
114 | query = $(form).serialize()
115 | ws = new WebSocket action.replace(/^http(s)?:\/\//, "ws$1:") + '?' + query
116 | ws.onopen = (event) ->
117 | return
118 | ws.onmessage = (event) ->
119 | pre.append($('').text(event.data))
120 | return
121 | ws.onerror = (event) ->
122 | oncomplete()
123 | return
124 | ws.onclose = (event) ->
125 | oncomplete()
126 | return
127 | return
128 |
129 | # for form.serialize()
130 | # http://stackoverflow.com/a/11271850
131 | submitButtonHack = ->
132 | $('form :submit').on 'click', ->
133 | if $(this).attr 'name'
134 | $form = $(this).closest('form')
135 | $hidden = $('').attr({
136 | name: $(this).attr('name'),
137 | value: $(this).attr('value')
138 | })
139 | $form.append($hidden)
140 | setTimeout ->
141 | $hidden.remove()
142 | return
143 | , 10
144 | return
145 | return
146 |
147 | # pass Application settings to JavaScript via DOM elements with classname "js-setting"
148 | jsSetting = ->
149 | window.setting = {}
150 | $('.js-setting').each ->
151 | window.setting[$(this).attr('data-name')] = $(this).attr('data-value')
152 | return
153 | return
154 |
155 | checkoutBeforeDeploy = ->
156 | $('.checkout-button').on 'click', ->
157 | $('.deploy-button').prop('disabled', false)
--------------------------------------------------------------------------------
/app/controllers/Application.scala:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import java.io.File
4 |
5 | import models._
6 | import play.api.Play.current
7 | import play.api.data.Form
8 | import play.api.data.Forms._
9 | import play.api.i18n.Messages.Implicits._
10 | import play.api.libs.iteratee.Iteratee
11 | import play.api.mvc._
12 |
13 | import scala.io.Source
14 |
15 | object Application extends Controller {
16 | def getCurrentUser(request: RequestHeader) = {
17 | request.cookies.get("user").map {
18 | cookie => User(cookie.value)
19 | }
20 | }
21 |
22 | val createProjectForm = Form("url" -> text)
23 |
24 | def index() = Action { implicit request =>
25 | Ok(views.html.index())
26 | }
27 |
28 | def create() = Action { implicit request =>
29 | val url = createProjectForm.bindFromRequest.get
30 | val proj = Project(Repo.clone(url))
31 | Redirect(routes.Application.project(proj.name))
32 | }
33 |
34 | def project(project: String) = Action { implicit request =>
35 | val proj = Project(project)
36 | val user = getCurrentUser(request)
37 | val useWebSocket = current.configuration.getBoolean("pploy.preference.websocket").getOrElse(false)
38 |
39 | Ok(views.html.project(proj, user, useWebSocket))
40 | }
41 |
42 | def lockUser(project: String) = Action { implicit request =>
43 | val proj = Project(project)
44 | if (proj.isLocked) Ok(proj.lock.user.name)
45 | else Ok("")
46 | }
47 |
48 | val lockOperationForm = Form(tuple("user" -> nonEmptyText, "operation" -> text))
49 |
50 | def lock(project: String) = Action { implicit request =>
51 | val proj = Project(project)
52 |
53 | lockOperationForm.bindFromRequest.fold(
54 | errors => Redirect(routes.Application.project(proj.name)),
55 | f => {
56 | val (userName, operation) = f
57 | val user = User(userName)
58 | operation match {
59 | case "gain" => proj.gainLock(user)
60 | case "release" => proj.releaseLock(user)
61 | case "extend" => proj.extendLock(user)
62 | case _ =>
63 | throw new RuntimeException("operation not specified")
64 | }
65 | Redirect(routes.Application.project(proj.name))
66 | .withCookies(Cookie("user", userName, Some(3600 * 24 * 7)))
67 | }
68 | )
69 | }
70 |
71 | val checkoutForm = Form("ref" -> nonEmptyText)
72 |
73 | def checkout(project: String) = Action { implicit request =>
74 | val proj = Project(project)
75 | val user = getCurrentUser(request)
76 | .getOrElse(throw new RuntimeException("user not selected"))
77 | proj.assertLockedByUser(user)
78 |
79 | checkoutForm.bindFromRequest.fold(
80 | errors => BadRequest,
81 | ref => {
82 | Ok.chunked(ProcessEnumerator(proj.checkoutProcess(ref)))
83 | .withHeaders("Content-Type" -> "text/plain; charset=utf-8", "X-Content-Type-Options" -> "nosniff")
84 | }
85 | )
86 | }
87 |
88 | def checkoutWS(project: String, ref: String) = WebSocket.using[String] { request =>
89 | val proj = Project(project)
90 | val user = getCurrentUser(request)
91 | .getOrElse(throw new RuntimeException("user not selected"))
92 |
93 | val in = Iteratee.ignore[String]
94 | val out = ProcessEnumerator(proj.checkoutProcess(ref))
95 | (in, out)
96 | }
97 |
98 | val deployForm = Form("target" -> text)
99 |
100 | def deploy(project: String) = Action { implicit request =>
101 | val proj = Project(project)
102 | val user = getCurrentUser(request)
103 | .getOrElse(throw new RuntimeException("user not selected"))
104 | proj.assertLockedByUser(user)
105 |
106 | deployForm.bindFromRequest.fold(
107 | errors => BadRequest,
108 | target => {
109 | Ok.chunked(ProcessEnumerator(proj.deployProcess(user, target)))
110 | .withHeaders("Content-Type" -> "text/plain; charset=utf-8", "X-Content-Type-Options" -> "nosniff")
111 | }
112 | )
113 | }
114 |
115 | def deployWS(project: String, target: String) = WebSocket.using[String] { request =>
116 | val proj = Project(project)
117 | val user = getCurrentUser(request)
118 | .getOrElse(throw new RuntimeException("user not selected"))
119 |
120 | val in = Iteratee.ignore[String]
121 | val out = ProcessEnumerator(proj.deployProcess(user, target))
122 | (in, out)
123 | }
124 |
125 | def commits(project: String) = Action { implicit request =>
126 | val proj = Project(project)
127 | Ok(views.html.commits(proj.repo.commits))
128 | }
129 |
130 | def logs(project: String, full: Long) = Action { implicit request =>
131 | val proj = Project(project)
132 | val file = new File(WorkingDir.logFile(proj.name).toString)
133 | if (file.isFile) {
134 | if (full != 1) {
135 | val limit = current.configuration.getInt("pploy.commitlogs.lines").getOrElse(1000)
136 |
137 | val lines = Source.fromFile(file.getCanonicalPath).getLines().take(limit).toList
138 | if (lines.length >= limit) {
139 | Ok(lines.mkString("\n") + "\n*** LOG FILE TOO LONG. PLEASE SEE THE FULL LOG. ***")
140 | .withHeaders("Content-Type" -> "text/plain; charset=utf-8", "X-Content-Type-Options" -> "nosniff")
141 | } else {
142 | Ok(lines.mkString("\n"))
143 | .withHeaders("Content-Type" -> "text/plain; charset=utf-8", "X-Content-Type-Options" -> "nosniff")
144 | }
145 | } else {
146 | Ok.sendFile(content = file, inline = true)
147 | .withHeaders("Content-Type" -> "text/plain; charset=utf-8", "X-Content-Type-Options" -> "nosniff")
148 | }
149 | } else {
150 | NotFound
151 | }
152 | }
153 |
154 | def remove(project: String) = Action { implicit request =>
155 | Project(project).remove()
156 | Redirect(routes.Application.index())
157 | }
158 | }
--------------------------------------------------------------------------------
/activator:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ### ------------------------------- ###
4 | ### Helper methods for BASH scripts ###
5 | ### ------------------------------- ###
6 |
7 | realpath () {
8 | (
9 | TARGET_FILE="$1"
10 |
11 | cd $(dirname "$TARGET_FILE")
12 | TARGET_FILE=$(basename "$TARGET_FILE")
13 |
14 | COUNT=0
15 | while [ -L "$TARGET_FILE" -a $COUNT -lt 100 ]
16 | do
17 | TARGET_FILE=$(readlink "$TARGET_FILE")
18 | cd $(dirname "$TARGET_FILE")
19 | TARGET_FILE=$(basename "$TARGET_FILE")
20 | COUNT=$(($COUNT + 1))
21 | done
22 |
23 | if [ "$TARGET_FILE" == "." -o "$TARGET_FILE" == ".." ]; then
24 | cd "$TARGET_FILE"
25 | TARGET_FILEPATH=
26 | else
27 | TARGET_FILEPATH=/$TARGET_FILE
28 | fi
29 |
30 | # make sure we grab the actual windows path, instead of cygwin's path.
31 | if ! is_cygwin; then
32 | echo "$(pwd -P)/$TARGET_FILE"
33 | else
34 | echo $(cygwinpath "$(pwd -P)/$TARGET_FILE")
35 | fi
36 | )
37 | }
38 |
39 | # TODO - Do we need to detect msys?
40 |
41 | # Uses uname to detect if we're in the odd cygwin environment.
42 | is_cygwin() {
43 | local os=$(uname -s)
44 | case "$os" in
45 | CYGWIN*) return 0 ;;
46 | *) return 1 ;;
47 | esac
48 | }
49 |
50 | # This can fix cygwin style /cygdrive paths so we get the
51 | # windows style paths.
52 | cygwinpath() {
53 | local file="$1"
54 | if is_cygwin; then
55 | echo $(cygpath -w $file)
56 | else
57 | echo $file
58 | fi
59 | }
60 |
61 | # Make something URI friendly
62 | make_url() {
63 | url="$1"
64 | local nospaces=${url// /%20}
65 | if is_cygwin; then
66 | echo "/${nospaces//\\//}"
67 | else
68 | echo "$nospaces"
69 | fi
70 | }
71 |
72 | # Detect if we should use JAVA_HOME or just try PATH.
73 | get_java_cmd() {
74 | if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then
75 | echo "$JAVA_HOME/bin/java"
76 | else
77 | echo "java"
78 | fi
79 | }
80 |
81 | echoerr () {
82 | echo 1>&2 "$@"
83 | }
84 | vlog () {
85 | [[ $verbose || $debug ]] && echoerr "$@"
86 | }
87 | dlog () {
88 | [[ $debug ]] && echoerr "$@"
89 | }
90 | execRunner () {
91 | # print the arguments one to a line, quoting any containing spaces
92 | [[ $verbose || $debug ]] && echo "# Executing command line:" && {
93 | for arg; do
94 | if printf "%s\n" "$arg" | grep -q ' '; then
95 | printf "\"%s\"\n" "$arg"
96 | else
97 | printf "%s\n" "$arg"
98 | fi
99 | done
100 | echo ""
101 | }
102 |
103 | exec "$@"
104 | }
105 | addJava () {
106 | dlog "[addJava] arg = '$1'"
107 | java_args=( "${java_args[@]}" "$1" )
108 | }
109 | addApp () {
110 | dlog "[addApp] arg = '$1'"
111 | sbt_commands=( "${app_commands[@]}" "$1" )
112 | }
113 | addResidual () {
114 | dlog "[residual] arg = '$1'"
115 | residual_args=( "${residual_args[@]}" "$1" )
116 | }
117 | addDebugger () {
118 | addJava "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1"
119 | }
120 | # a ham-fisted attempt to move some memory settings in concert
121 | # so they need not be messed around with individually.
122 | get_mem_opts () {
123 | local mem=${1:-1024}
124 | local perm=$(( $mem / 4 ))
125 | (( $perm > 256 )) || perm=256
126 | (( $perm < 1024 )) || perm=1024
127 |
128 | # default is to set memory options but this can be overridden by code section below
129 | memopts="-Xms${mem}m -Xmx${mem}m"
130 | extmemopts="-XX:PermSize=64m -XX:MaxPermSize=${perm}m"
131 |
132 | if [[ "${java_opts}" == *-Xmx* ]] || [[ "${java_opts}" == *-Xms* ]] || [[ "${java_opts}" == *-XX:MaxPermSize* ]] || [[ "${java_opts}" == *-XX:ReservedCodeCacheSize* ]]; then
133 | # if we detect any of these settings in ${java_opts} we need to NOT output our settings.
134 | # The reason is the Xms/Xmx, if they don't line up, cause errors.
135 | memopts=""
136 | extmemopts=""
137 | elif [[ "${java_version}" > "1.8" ]]; then
138 | extmemopts=""
139 | fi
140 |
141 | echo "${memopts} ${extmemopts}"
142 | }
143 | require_arg () {
144 | local type="$1"
145 | local opt="$2"
146 | local arg="$3"
147 | if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then
148 | die "$opt requires <$type> argument"
149 | fi
150 | }
151 | require_arg () {
152 | local type="$1"
153 | local opt="$2"
154 | local arg="$3"
155 | if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then
156 | die "$opt requires <$type> argument"
157 | fi
158 | }
159 | is_function_defined() {
160 | declare -f "$1" > /dev/null
161 | }
162 |
163 | # If we're *not* running in a terminal, and we don't have any arguments, then we need to add the 'ui' parameter
164 | detect_terminal_for_ui() {
165 | [[ ! -t 0 ]] && [[ "${#residual_args}" == "0" ]] && {
166 | addResidual "ui"
167 | }
168 | # SPECIAL TEST FOR MAC
169 | [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]] && [[ "${#residual_args}" == "0" ]] && {
170 | echo "Detected MAC OSX launched script...."
171 | echo "Swapping to UI"
172 | addResidual "ui"
173 | }
174 | }
175 |
176 | # Processes incoming arguments and places them in appropriate global variables. called by the run method.
177 | process_args () {
178 | while [[ $# -gt 0 ]]; do
179 | case "$1" in
180 | -h|-help) usage; exit 1 ;;
181 | -v|-verbose) verbose=1 && shift ;;
182 | -d|-debug) debug=1 && shift ;;
183 | -mem) require_arg integer "$1" "$2" && app_mem="$2" && shift 2 ;;
184 | -jvm-debug) [[ "$2" =~ ^[0-9]+$ ]] && addDebugger "$2" && shift || addDebugger 9999 && shift ;;
185 | -java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;;
186 | -D*) addJava "$1" && shift ;;
187 | -J*) addJava "${1:2}" && shift ;;
188 | *) addResidual "$1" && shift ;;
189 | esac
190 | done
191 |
192 | is_function_defined process_my_args && {
193 | myargs=("${residual_args[@]}")
194 | residual_args=()
195 | process_my_args "${myargs[@]}"
196 | }
197 | }
198 |
199 | # Actually runs the script.
200 | run() {
201 | # TODO - check for sane environment
202 |
203 | # process the combined args, then reset "$@" to the residuals
204 | process_args "$@"
205 | detect_terminal_for_ui
206 | set -- "${residual_args[@]}"
207 | argumentCount=$#
208 |
209 | #check for jline terminal fixes on cygwin
210 | if is_cygwin; then
211 | stty -icanon min 1 -echo > /dev/null 2>&1
212 | addJava "-Djline.terminal=jline.UnixTerminal"
213 | addJava "-Dsbt.cygwin=true"
214 | fi
215 |
216 | # run sbt
217 | execRunner "$java_cmd" \
218 | "-Dactivator.home=$(make_url "$activator_home")" \
219 | $(get_mem_opts $app_mem) \
220 | ${java_opts[@]} \
221 | ${java_args[@]} \
222 | -jar "$app_launcher" \
223 | "${app_commands[@]}" \
224 | "${residual_args[@]}"
225 |
226 | local exit_code=$?
227 | if is_cygwin; then
228 | stty icanon echo > /dev/null 2>&1
229 | fi
230 | exit $exit_code
231 | }
232 |
233 | # Loads a configuration file full of default command line options for this script.
234 | loadConfigFile() {
235 | cat "$1" | sed '/^\#/d'
236 | }
237 |
238 | ### ------------------------------- ###
239 | ### Start of customized settings ###
240 | ### ------------------------------- ###
241 | usage() {
242 | cat < [options]
244 |
245 | Command:
246 | ui Start the Activator UI
247 | new [name] [template-id] Create a new project with [name] using template [template-id]
248 | list-templates Print all available template names
249 | -h | -help Print this message
250 |
251 | Options:
252 | -v | -verbose Make this runner chattier
253 | -d | -debug Set sbt log level to debug
254 | -mem Set memory options (default: $sbt_mem, which is $(get_mem_opts $sbt_mem))
255 | -jvm-debug Turn on JVM debugging, open at the given port.
256 |
257 | # java version (default: java from PATH, currently $(java -version 2>&1 | grep version))
258 | -java-home Alternate JAVA_HOME
259 |
260 | # jvm options and output control
261 | -Dkey=val Pass -Dkey=val directly to the java runtime
262 | -J-X Pass option -X directly to the java runtime
263 | (-J is stripped)
264 |
265 | # environment variables (read from context)
266 | JAVA_OPTS Environment variable, if unset uses ""
267 | SBT_OPTS Environment variable, if unset uses ""
268 | ACTIVATOR_OPTS Environment variable, if unset uses ""
269 |
270 | In the case of duplicated or conflicting options, the order above
271 | shows precedence: environment variables lowest, command line options highest.
272 | EOM
273 | }
274 |
275 | ### ------------------------------- ###
276 | ### Main script ###
277 | ### ------------------------------- ###
278 |
279 | declare -a residual_args
280 | declare -a java_args
281 | declare -a app_commands
282 | declare -r real_script_path="$(realpath "$0")"
283 | declare -r activator_home="$(realpath "$(dirname "$real_script_path")")"
284 | declare -r app_version="1.1.2"
285 |
286 | declare -r app_launcher="${activator_home}/activator-launch-${app_version}.jar"
287 | declare -r script_name=activator
288 | declare -r java_cmd=$(get_java_cmd)
289 | declare -r java_opts=( "${ACTIVATOR_OPTS[@]}" "${SBT_OPTS[@]}" "${JAVA_OPTS[@]}" "${java_opts[@]}" )
290 | userhome="$HOME"
291 | if is_cygwin; then
292 | # cygwin sets home to something f-d up, set to real windows homedir
293 | userhome="$USERPROFILE"
294 | fi
295 | declare -r activator_user_home_dir="${userhome}/.activator"
296 | declare -r java_opts_config="${activator_user_home_dir}/activatorconfig.txt"
297 |
298 | # Now check to see if it's a good enough version
299 | declare -r java_version=$("$java_cmd" -version 2>&1 | awk -F '"' '/version/ {print $2}')
300 | if [[ "$java_version" == "" ]]; then
301 | echo
302 | echo No java installations was detected.
303 | echo Please go to http://www.java.com/getjava/ and download
304 | echo
305 | exit 1
306 | elif [[ ! "$java_version" > "1.6" ]]; then
307 | echo
308 | echo The java installation you have is not up to date
309 | echo Activator requires at least version 1.6+, you have
310 | echo version $java_version
311 | echo
312 | echo Please go to http://www.java.com/getjava/ and download
313 | echo a valid Java Runtime and install before running Activator.
314 | echo
315 | exit 1
316 | fi
317 |
318 | # if configuration files exist, prepend their contents to the java args so it can be processed by this runner
319 | if [[ -f "$java_opts_config" ]]; then
320 | config_opts=$(loadConfigFile "$java_opts_config")
321 | for item in $config_opts
322 | do
323 | addJava "$item"
324 | done
325 | fi
326 |
327 | run "$@"
328 |
--------------------------------------------------------------------------------
/public/bootstrap/css/bootstrap-theme.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.2.0 (http://getbootstrap.com)
3 | * Copyright 2011-2014 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */.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: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]{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:-o-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#2d6ca2));background-image:linear-gradient(to bottom,#428bca 0,#2d6ca2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#2b669a}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-primary:disabled,.btn-primary[disabled]{background-color:#2d6ca2;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]{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]{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]{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]{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:#357ebd;background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-o-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#357ebd));background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', 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>.active>a{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f3f3f3));background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', 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}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:-o-linear-gradient(top,#222 0,#282828 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#222),to(#282828));background-image:linear-gradient(to bottom,#222 0,#282828 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', 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}.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,#428bca 0,#3071a9 100%);background-image:-o-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#3071a9));background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', 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 #3071a9;background-image:-webkit-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:-o-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#3278b3));background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);background-repeat:repeat-x;border-color:#3278b3}.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,#428bca 0,#357ebd 100%);background-image:-o-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#357ebd));background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', 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)}
--------------------------------------------------------------------------------
/public/bootstrap/css/bootstrap-theme.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"bootstrap-theme.css","sources":["less/theme.less","less/mixins/vendor-prefixes.less","bootstrap-theme.css","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"AAeA;;;;;;EAME,0CAAA;EC+CA,6FAAA;EACQ,qFAAA;EC5DT;AFiBC;;;;;;;;;;;;EC0CA,0DAAA;EACQ,kDAAA;EC7CT;AFqCC;;EAEE,wBAAA;EEnCH;AFwCD;EG/CI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EA+B2C,2BAAA;EAA2B,oBAAA;EE7BvE;AFAC;;EAEE,2BAAA;EACA,8BAAA;EEEH;AFCC;;EAEE,2BAAA;EACA,uBAAA;EECH;AFEC;;EAEE,2BAAA;EACA,wBAAA;EEAH;AFeD;EGhDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EE0BD;AFxBC;;EAEE,2BAAA;EACA,8BAAA;EE0BH;AFvBC;;EAEE,2BAAA;EACA,uBAAA;EEyBH;AFtBC;;EAEE,2BAAA;EACA,wBAAA;EEwBH;AFRD;EGjDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EEkDD;AFhDC;;EAEE,2BAAA;EACA,8BAAA;EEkDH;AF/CC;;EAEE,2BAAA;EACA,uBAAA;EEiDH;AF9CC;;EAEE,2BAAA;EACA,wBAAA;EEgDH;AF/BD;EGlDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EE0ED;AFxEC;;EAEE,2BAAA;EACA,8BAAA;EE0EH;AFvEC;;EAEE,2BAAA;EACA,uBAAA;EEyEH;AFtEC;;EAEE,2BAAA;EACA,wBAAA;EEwEH;AFtDD;EGnDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EEkGD;AFhGC;;EAEE,2BAAA;EACA,8BAAA;EEkGH;AF/FC;;EAEE,2BAAA;EACA,uBAAA;EEiGH;AF9FC;;EAEE,2BAAA;EACA,wBAAA;EEgGH;AF7ED;EGpDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJ8BA,6BAAA;EACA,uBAAA;EE0HD;AFxHC;;EAEE,2BAAA;EACA,8BAAA;EE0HH;AFvHC;;EAEE,2BAAA;EACA,uBAAA;EEyHH;AFtHC;;EAEE,2BAAA;EACA,wBAAA;EEwHH;AF7FD;;ECbE,oDAAA;EACQ,4CAAA;EC8GT;AFvFD;;EGvEI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EHsEF,2BAAA;EE6FD;AF3FD;;;EG5EI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH4EF,2BAAA;EEiGD;AFvFD;EG1FI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ECnBF,qEAAA;EJ4GA,oBAAA;EC9CA,6FAAA;EACQ,qFAAA;EC4IT;AFlGD;EG1FI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EF2CF,0DAAA;EACQ,kDAAA;ECqJT;AF/FD;;EAEE,gDAAA;EEiGD;AF7FD;EG5GI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ECnBF,qEAAA;EFgOD;AFrGD;EG5GI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EF2CF,yDAAA;EACQ,iDAAA;EC0KT;AF9GD;;EAWI,2CAAA;EEuGH;AFlGD;;;EAGE,kBAAA;EEoGD;AF1FD;EACE,+CAAA;EC3FA,4FAAA;EACQ,oFAAA;ECwLT;AFlFD;EGtJI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH8IF,uBAAA;EE8FD;AFzFD;EGvJI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH8IF,uBAAA;EEsGD;AFhGD;EGxJI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH8IF,uBAAA;EE8GD;AFvGD;EGzJI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH8IF,uBAAA;EEsHD;AFtGD;EGlKI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED2QH;AFnGD;EG5KI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDkRH;AFzGD;EG7KI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDyRH;AF/GD;EG9KI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDgSH;AFrHD;EG/KI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDuSH;AF3HD;EGhLI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED8SH;AF9HD;EGnJI,+MAAA;EACA,0MAAA;EACA,uMAAA;EDoRH;AF1HD;EACE,oBAAA;EC/IA,oDAAA;EACQ,4CAAA;EC4QT;AF3HD;;;EAGE,+BAAA;EGpME,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EHkMF,uBAAA;EEiID;AFvHD;ECjKE,mDAAA;EACQ,2CAAA;EC2RT;AFjHD;EG1NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED8UH;AFvHD;EG3NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDqVH;AF7HD;EG5NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED4VH;AFnID;EG7NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDmWH;AFzID;EG9NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED0WH;AF/ID;EG/NI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDiXH;AF9ID;EGvOI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EHqOF,uBAAA;EC1LA,2FAAA;EACQ,mFAAA;EC+UT","sourcesContent":["\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &:disabled,\n &[disabled] {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-bg, 5%); @end-color: darken(@navbar-default-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-bg; @end-color: lighten(@navbar-inverse-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n}\n\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n &::-moz-placeholder { color: @color; // Firefox\n opacity: 1; } // See https://github.com/twbs/bootstrap/pull/11526\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n",null,"// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]}
--------------------------------------------------------------------------------
/public/bootstrap/css/bootstrap-theme.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.2.0 (http://getbootstrap.com)
3 | * Copyright 2011-2014 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 |
7 | .btn-default,
8 | .btn-primary,
9 | .btn-success,
10 | .btn-info,
11 | .btn-warning,
12 | .btn-danger {
13 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
14 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
15 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
16 | }
17 | .btn-default:active,
18 | .btn-primary:active,
19 | .btn-success:active,
20 | .btn-info:active,
21 | .btn-warning:active,
22 | .btn-danger:active,
23 | .btn-default.active,
24 | .btn-primary.active,
25 | .btn-success.active,
26 | .btn-info.active,
27 | .btn-warning.active,
28 | .btn-danger.active {
29 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
30 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
31 | }
32 | .btn:active,
33 | .btn.active {
34 | background-image: none;
35 | }
36 | .btn-default {
37 | text-shadow: 0 1px 0 #fff;
38 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
39 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
40 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
41 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
42 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
43 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
44 | background-repeat: repeat-x;
45 | border-color: #dbdbdb;
46 | border-color: #ccc;
47 | }
48 | .btn-default:hover,
49 | .btn-default:focus {
50 | background-color: #e0e0e0;
51 | background-position: 0 -15px;
52 | }
53 | .btn-default:active,
54 | .btn-default.active {
55 | background-color: #e0e0e0;
56 | border-color: #dbdbdb;
57 | }
58 | .btn-default:disabled,
59 | .btn-default[disabled] {
60 | background-color: #e0e0e0;
61 | background-image: none;
62 | }
63 | .btn-primary {
64 | background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%);
65 | background-image: -o-linear-gradient(top, #428bca 0%, #2d6ca2 100%);
66 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#2d6ca2));
67 | background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%);
68 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);
69 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
70 | background-repeat: repeat-x;
71 | border-color: #2b669a;
72 | }
73 | .btn-primary:hover,
74 | .btn-primary:focus {
75 | background-color: #2d6ca2;
76 | background-position: 0 -15px;
77 | }
78 | .btn-primary:active,
79 | .btn-primary.active {
80 | background-color: #2d6ca2;
81 | border-color: #2b669a;
82 | }
83 | .btn-primary:disabled,
84 | .btn-primary[disabled] {
85 | background-color: #2d6ca2;
86 | background-image: none;
87 | }
88 | .btn-success {
89 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
90 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
91 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
92 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
93 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
94 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
95 | background-repeat: repeat-x;
96 | border-color: #3e8f3e;
97 | }
98 | .btn-success:hover,
99 | .btn-success:focus {
100 | background-color: #419641;
101 | background-position: 0 -15px;
102 | }
103 | .btn-success:active,
104 | .btn-success.active {
105 | background-color: #419641;
106 | border-color: #3e8f3e;
107 | }
108 | .btn-success:disabled,
109 | .btn-success[disabled] {
110 | background-color: #419641;
111 | background-image: none;
112 | }
113 | .btn-info {
114 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
115 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
116 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
117 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
118 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
119 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
120 | background-repeat: repeat-x;
121 | border-color: #28a4c9;
122 | }
123 | .btn-info:hover,
124 | .btn-info:focus {
125 | background-color: #2aabd2;
126 | background-position: 0 -15px;
127 | }
128 | .btn-info:active,
129 | .btn-info.active {
130 | background-color: #2aabd2;
131 | border-color: #28a4c9;
132 | }
133 | .btn-info:disabled,
134 | .btn-info[disabled] {
135 | background-color: #2aabd2;
136 | background-image: none;
137 | }
138 | .btn-warning {
139 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
140 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
141 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
142 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
143 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
144 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
145 | background-repeat: repeat-x;
146 | border-color: #e38d13;
147 | }
148 | .btn-warning:hover,
149 | .btn-warning:focus {
150 | background-color: #eb9316;
151 | background-position: 0 -15px;
152 | }
153 | .btn-warning:active,
154 | .btn-warning.active {
155 | background-color: #eb9316;
156 | border-color: #e38d13;
157 | }
158 | .btn-warning:disabled,
159 | .btn-warning[disabled] {
160 | background-color: #eb9316;
161 | background-image: none;
162 | }
163 | .btn-danger {
164 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
165 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
166 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
167 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
168 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
169 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
170 | background-repeat: repeat-x;
171 | border-color: #b92c28;
172 | }
173 | .btn-danger:hover,
174 | .btn-danger:focus {
175 | background-color: #c12e2a;
176 | background-position: 0 -15px;
177 | }
178 | .btn-danger:active,
179 | .btn-danger.active {
180 | background-color: #c12e2a;
181 | border-color: #b92c28;
182 | }
183 | .btn-danger:disabled,
184 | .btn-danger[disabled] {
185 | background-color: #c12e2a;
186 | background-image: none;
187 | }
188 | .thumbnail,
189 | .img-thumbnail {
190 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
191 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
192 | }
193 | .dropdown-menu > li > a:hover,
194 | .dropdown-menu > li > a:focus {
195 | background-color: #e8e8e8;
196 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
197 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
198 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
199 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
200 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
201 | background-repeat: repeat-x;
202 | }
203 | .dropdown-menu > .active > a,
204 | .dropdown-menu > .active > a:hover,
205 | .dropdown-menu > .active > a:focus {
206 | background-color: #357ebd;
207 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
208 | background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%);
209 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd));
210 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
211 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
212 | background-repeat: repeat-x;
213 | }
214 | .navbar-default {
215 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
216 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
217 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
218 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
219 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
220 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
221 | background-repeat: repeat-x;
222 | border-radius: 4px;
223 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
224 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
225 | }
226 | .navbar-default .navbar-nav > .active > a {
227 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%);
228 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%);
229 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f3f3f3));
230 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%);
231 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);
232 | background-repeat: repeat-x;
233 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
234 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
235 | }
236 | .navbar-brand,
237 | .navbar-nav > li > a {
238 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
239 | }
240 | .navbar-inverse {
241 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
242 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
243 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
244 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
245 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
246 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
247 | background-repeat: repeat-x;
248 | }
249 | .navbar-inverse .navbar-nav > .active > a {
250 | background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%);
251 | background-image: -o-linear-gradient(top, #222 0%, #282828 100%);
252 | background-image: -webkit-gradient(linear, left top, left bottom, from(#222), to(#282828));
253 | background-image: linear-gradient(to bottom, #222 0%, #282828 100%);
254 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);
255 | background-repeat: repeat-x;
256 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
257 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
258 | }
259 | .navbar-inverse .navbar-brand,
260 | .navbar-inverse .navbar-nav > li > a {
261 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
262 | }
263 | .navbar-static-top,
264 | .navbar-fixed-top,
265 | .navbar-fixed-bottom {
266 | border-radius: 0;
267 | }
268 | .alert {
269 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
270 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
271 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
272 | }
273 | .alert-success {
274 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
275 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
276 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
277 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
278 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
279 | background-repeat: repeat-x;
280 | border-color: #b2dba1;
281 | }
282 | .alert-info {
283 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
284 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
285 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
286 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
287 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
288 | background-repeat: repeat-x;
289 | border-color: #9acfea;
290 | }
291 | .alert-warning {
292 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
293 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
294 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
295 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
296 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
297 | background-repeat: repeat-x;
298 | border-color: #f5e79e;
299 | }
300 | .alert-danger {
301 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
302 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
303 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
304 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
305 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
306 | background-repeat: repeat-x;
307 | border-color: #dca7a7;
308 | }
309 | .progress {
310 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
311 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
312 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
313 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
314 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
315 | background-repeat: repeat-x;
316 | }
317 | .progress-bar {
318 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%);
319 | background-image: -o-linear-gradient(top, #428bca 0%, #3071a9 100%);
320 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3071a9));
321 | background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);
322 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);
323 | background-repeat: repeat-x;
324 | }
325 | .progress-bar-success {
326 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
327 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
328 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
329 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
330 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
331 | background-repeat: repeat-x;
332 | }
333 | .progress-bar-info {
334 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
335 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
336 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
337 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
338 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
339 | background-repeat: repeat-x;
340 | }
341 | .progress-bar-warning {
342 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
343 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
344 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
345 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
346 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
347 | background-repeat: repeat-x;
348 | }
349 | .progress-bar-danger {
350 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
351 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
352 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
353 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
354 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
355 | background-repeat: repeat-x;
356 | }
357 | .progress-bar-striped {
358 | 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);
359 | 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);
360 | 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);
361 | }
362 | .list-group {
363 | border-radius: 4px;
364 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
365 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
366 | }
367 | .list-group-item.active,
368 | .list-group-item.active:hover,
369 | .list-group-item.active:focus {
370 | text-shadow: 0 -1px 0 #3071a9;
371 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%);
372 | background-image: -o-linear-gradient(top, #428bca 0%, #3278b3 100%);
373 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3278b3));
374 | background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%);
375 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);
376 | background-repeat: repeat-x;
377 | border-color: #3278b3;
378 | }
379 | .panel {
380 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
381 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
382 | }
383 | .panel-default > .panel-heading {
384 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
385 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
386 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
387 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
388 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
389 | background-repeat: repeat-x;
390 | }
391 | .panel-primary > .panel-heading {
392 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
393 | background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%);
394 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd));
395 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
396 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
397 | background-repeat: repeat-x;
398 | }
399 | .panel-success > .panel-heading {
400 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
401 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
403 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
405 | background-repeat: repeat-x;
406 | }
407 | .panel-info > .panel-heading {
408 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
409 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
410 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
411 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
412 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
413 | background-repeat: repeat-x;
414 | }
415 | .panel-warning > .panel-heading {
416 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
417 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
418 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
419 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
420 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
421 | background-repeat: repeat-x;
422 | }
423 | .panel-danger > .panel-heading {
424 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
425 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
426 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
427 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
428 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
429 | background-repeat: repeat-x;
430 | }
431 | .well {
432 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
433 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
434 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
435 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
436 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
437 | background-repeat: repeat-x;
438 | border-color: #dcdcdc;
439 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
440 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
441 | }
442 | /*# sourceMappingURL=bootstrap-theme.css.map */
443 |
--------------------------------------------------------------------------------
/public/bootstrap/js/bootstrap.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.2.0 (http://getbootstrap.com)
3 | * Copyright 2011-2014 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.2.0",d.prototype.close=function(b){function c(){f.detach().trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one("bsTransitionEnd",c).emulateTransitionEnd(150):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.2.0",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),d[e](null==f[b]?this.options[b]:f[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b).on("keydown.bs.carousel",a.proxy(this.keydown,this)),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.2.0",c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},c.prototype.keydown=function(a){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.to=function(b){var c=this,d=this.getItemIndex(this.$active=this.$element.find(".item.active"));return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=e[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:g});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,f&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(e)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:g});return a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one("bsTransitionEnd",function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger(m)),f&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(b=!b),e||d.data("bs.collapse",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};c.VERSION="3.2.0",c.DEFAULTS={toggle:!0},c.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},c.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var c=a.Event("show.bs.collapse");if(this.$element.trigger(c),!c.isDefaultPrevented()){var d=this.$parent&&this.$parent.find("> .panel > .in");if(d&&d.length){var e=d.data("bs.collapse");if(e&&e.transitioning)return;b.call(d,"hide"),e||d.data("bs.collapse",null)}var f=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[f](0),this.transitioning=1;var g=function(){this.$element.removeClass("collapsing").addClass("collapse in")[f](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return g.call(this);var h=a.camelCase(["scroll",f].join("-"));this.$element.one("bsTransitionEnd",a.proxy(g,this)).emulateTransitionEnd(350)[f](this.$element[0][h])}}},c.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},c.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var d=a.fn.collapse;a.fn.collapse=b,a.fn.collapse.Constructor=c,a.fn.collapse.noConflict=function(){return a.fn.collapse=d,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(c){var d,e=a(this),f=e.attr("data-target")||c.preventDefault()||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),g=a(f),h=g.data("bs.collapse"),i=h?"toggle":e.data(),j=e.attr("data-parent"),k=j&&a(j);h&&h.transitioning||(k&&k.find('[data-toggle="collapse"][data-parent="'+j+'"]').not(e).addClass("collapsed"),e[g.hasClass("in")?"addClass":"removeClass"]("collapsed")),b.call(g,i)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.2.0",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('').insertAfter(a(this)).on("click",b);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(b){if(/(38|40|27)/.test(b.keyCode)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var e=c(d),g=e.hasClass("open");if(!g||g&&27==b.keyCode)return 27==b.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.divider):visible a",i=e.find('[role="menu"]'+h+', [role="listbox"]'+h);if(i.length){var j=i.index(i.filter(":focus"));38==b.keyCode&&j>0&&j--,40==b.keyCode&&j').appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus.call(this.$element[0]):this.hide.call(this))},this)),e&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;e?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(150):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var f=function(){c.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",f).emulateTransitionEnd(150):f()}else b&&b()},c.prototype.checkScrollbar=function(){document.body.clientWidth>=window.innerWidth||(this.scrollbarWidth=this.scrollbarWidth||this.measureScrollbar())},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.scrollbarWidth&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right","")},c.prototype.measureScrollbar=function(){var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b;(e||"destroy"!=b)&&(e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=this.options=this.enabled=this.timeout=this.hoverState=this.$element=null,this.init("tooltip",a,b)};c.VERSION="3.2.0",c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(this.options.viewport.selector||this.options.viewport);for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show()},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var c=a.contains(document.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!c)return;var d=this,e=this.tip(),f=this.getUID(this.type);this.setContent(),e.attr("id",f),this.$element.attr("aria-describedby",f),this.options.animation&&e.addClass("fade");var g="function"==typeof this.options.placement?this.options.placement.call(this,e[0],this.$element[0]):this.options.placement,h=/\s?auto?\s?/i,i=h.test(g);i&&(g=g.replace(h,"")||"top"),e.detach().css({top:0,left:0,display:"block"}).addClass(g).data("bs."+this.type,this),this.options.container?e.appendTo(this.options.container):e.insertAfter(this.$element);var j=this.getPosition(),k=e[0].offsetWidth,l=e[0].offsetHeight;if(i){var m=g,n=this.$element.parent(),o=this.getPosition(n);g="bottom"==g&&j.top+j.height+l-o.scroll>o.height?"top":"top"==g&&j.top-o.scroll-l<0?"bottom":"right"==g&&j.right+k>o.width?"left":"left"==g&&j.left-kg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.width&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){return this.$tip=this.$tip||a(this.options.template)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.validate=function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){clearTimeout(this.timeout),this.hide().$element.off("."+this.type).removeData("bs."+this.type)};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||"destroy"!=b)&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.2.0",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").empty()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},c.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){var e=a.proxy(this.process,this);this.$body=a("body"),this.$scrollElement=a(a(c).is("body")?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",e),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.2.0",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b="offset",c=0;a.isWindow(this.$scrollElement[0])||(b="position",c=this.$scrollElement.scrollTop()),this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight();var d=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+c,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){d.offsets.push(this[0]),d.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b<=e[0])return g!=(a=f[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parentsUntil(this.options.target,".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.2.0",c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.closest("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},c.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one("bsTransitionEnd",e).emulateTransitionEnd(150):e(),f.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(c){c.preventDefault(),b.call(a(this),"show")})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.2.0",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=a(document).height(),d=this.$target.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top(this.$element)),"function"==typeof h&&(h=f.bottom(this.$element));var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=b-h?"bottom":null!=g&&g>=d?"top":!1;if(this.affixed!==i){null!=this.unpin&&this.$element.css("top","");var j="affix"+(i?"-"+i:""),k=a.Event(j+".bs.affix");this.$element.trigger(k),k.isDefaultPrevented()||(this.affixed=i,this.unpin="bottom"==i?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(j).trigger(a.Event(j.replace("affix","affixed"))),"bottom"==i&&this.$element.offset({top:b-this.$element.height()-h}))}}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},d.offsetBottom&&(d.offset.bottom=d.offsetBottom),d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
--------------------------------------------------------------------------------
/public/bootstrap/fonts/glyphicons-halflings-regular.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------