├── .cache
├── .classpath
├── .gitignore
├── .project
├── .settings
├── org.eclipse.core.resources.prefs
└── org.scala-ide.sdt.core.prefs
├── Procfile
├── bin
└── application.conf
├── build.sbt
├── policy
├── project
├── build.properties
├── cdn.scala
├── plugins.sbt
└── translate-zh.scala
├── readme.md
├── sbt
├── sbt
├── sbt-launch-0.11.3-2.jar
└── sbt.cmd
├── scala-tour-zh.md
├── scala-tour.md
├── src
├── main
│ ├── resources
│ │ └── application.conf
│ └── scala
│ │ └── com
│ │ └── yankay
│ │ └── scalaTour
│ │ ├── ScalaScriptRunner.scala
│ │ ├── Web.scala
│ │ └── script
│ │ └── TaskMem.scala
└── test
│ └── scala
│ ├── com
│ └── yankay
│ │ └── scalaTour
│ │ ├── ScalaScriptCompilerTest.scala
│ │ └── script
│ │ └── TaskMem.scala
│ └── test.scala
└── webapp
├── apple-touch-icon.png
├── css
├── bootstrap-responsive.css
├── bootstrap-responsive.min.css
├── bootstrap.css
├── bootstrap.min.css
├── codemirror.css
├── elegant.css
├── impress-demo.css
├── scala-tour.css
└── solarized.css
├── favicon.png
├── img
├── Lambda_lc.svg.png
├── glyphicons-halflings-white.png
├── glyphicons-halflings.png
├── glyphicons_065_tag.png
├── glyphicons_221_unshare.png
└── logo.png
├── index-zh.html
├── index.html
└── js
├── bootstrap.js
├── bootstrap.min.js
├── clike.js
├── codemirror.js
├── codemirror.min.js
├── go.js
├── impress.js
├── jquery.js
├── jquery.min.js
├── matchbrackets.js
└── scala-tour.js
/.cache:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yankay/scala-tour/7a482eea52e1ee429cb5961cb7ee5272227b44b1/.cache
--------------------------------------------------------------------------------
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .idea_modules
3 | target
4 | project/boot
5 | project/target
6 | project/plugins/target
7 | sftp-config.json
8 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 | scala-tour
3 |
4 |
5 | org.scala-ide.sdt.core.scalabuilder
6 |
7 |
8 |
9 | org.scala-ide.sdt.core.scalanature
10 | org.eclipse.jdt.core.javanature
11 |
12 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding/=UTF-8
3 |
--------------------------------------------------------------------------------
/.settings/org.scala-ide.sdt.core.prefs:
--------------------------------------------------------------------------------
1 | Xcheck-null=false
2 | Xcheckinit=false
3 | Xdisable-assertions=false
4 | Xelide-below=-2147483648
5 | Xexperimental=false
6 | Xfatal-warnings=false
7 | Xfuture=false
8 | Xlog-implicits=false
9 | Xno-uescape=false
10 | Xpluginsdir=C\:\\Program Files\\eclipse\\configuration\\org.eclipse.osgi\\bundles\\245\\1\\.cp\\lib
11 | Ybuild-manager-debug=false
12 | Yno-generic-signatures=false
13 | Yno-imports=false
14 | Ypresentation-debug=false
15 | Ypresentation-delay=0
16 | Ypresentation-verbose=false
17 | Yrecursion=0
18 | Yself-in-annots=false
19 | Ystruct-dispatch=poly-cache
20 | Ywarn-dead-code=false
21 | buildmanager=sbt
22 | compileorder=Mixed
23 | debugIncremental=true
24 | deprecation=false
25 | eclipse.preferences.version=1
26 | explaintypes=false
27 | formatter.alignParameters=false
28 | formatter.alignSingleLineCaseStatements=false
29 | formatter.alignSingleLineCaseStatements.maxArrowIndent=40
30 | formatter.compactControlReadability=false
31 | formatter.compactStringConcatenation=false
32 | formatter.doubleIndentClassDeclaration=false
33 | formatter.formatXml=true
34 | formatter.indentLocalDefs=false
35 | formatter.indentPackageBlocks=true
36 | formatter.indentSpaces=2
37 | formatter.indentWithTabs=false
38 | formatter.multilineScaladocCommentsStartOnFirstLine=false
39 | formatter.placeScaladocAsterisksBeneathSecondAsterisk=false
40 | formatter.preserveDanglingCloseParenthesis=false
41 | formatter.preserveSpaceBeforeArguments=false
42 | formatter.rewriteArrowSymbols=false
43 | formatter.spaceBeforeColon=false
44 | formatter.spaceInsideBrackets=false
45 | formatter.spaceInsideParentheses=false
46 | formatter.spacesWithinPatternBinders=true
47 | g=vars
48 | no-specialization=false
49 | nowarn=false
50 | optimise=false
51 | organizeimports.expandcollapse=expand
52 | organizeimports.groups=java$scala$org$com
53 | organizeimports.scalapackage=false
54 | organizeimports.wildcards=scalaz$scalaz.Scalaz
55 | scala.compiler.useProjectSettings=true
56 | stopBuildOnError=true
57 | syntaxColouring.bracket.backgroundColourEnabled=false
58 | syntaxColouring.bracket.bold=false
59 | syntaxColouring.bracket.colour=0,0,0
60 | syntaxColouring.bracket.enabled=true
61 | syntaxColouring.bracket.italic=false
62 | syntaxColouring.bracket.underline=false
63 | syntaxColouring.default.backgroundColourEnabled=false
64 | syntaxColouring.default.bold=false
65 | syntaxColouring.default.colour=0,0,0
66 | syntaxColouring.default.enabled=true
67 | syntaxColouring.default.italic=false
68 | syntaxColouring.default.underline=false
69 | syntaxColouring.keyword.backgroundColourEnabled=false
70 | syntaxColouring.keyword.bold=true
71 | syntaxColouring.keyword.colour=127,0,85
72 | syntaxColouring.keyword.enabled=true
73 | syntaxColouring.keyword.italic=false
74 | syntaxColouring.keyword.underline=false
75 | syntaxColouring.operator.backgroundColourEnabled=false
76 | syntaxColouring.operator.bold=false
77 | syntaxColouring.operator.colour=0,0,0
78 | syntaxColouring.operator.enabled=true
79 | syntaxColouring.operator.italic=false
80 | syntaxColouring.operator.underline=false
81 | syntaxColouring.scaladoc.backgroundColourEnabled=false
82 | syntaxColouring.scaladoc.bold=false
83 | syntaxColouring.scaladoc.colour=63,95,191
84 | syntaxColouring.scaladoc.enabled=true
85 | syntaxColouring.scaladoc.italic=false
86 | syntaxColouring.scaladoc.underline=false
87 | syntaxColouring.singleLineComment.backgroundColourEnabled=false
88 | syntaxColouring.singleLineComment.bold=false
89 | syntaxColouring.singleLineComment.colour=63,127,95
90 | syntaxColouring.singleLineComment.enabled=true
91 | syntaxColouring.singleLineComment.italic=false
92 | syntaxColouring.singleLineComment.underline=false
93 | syntaxColouring.string.backgroundColourEnabled=false
94 | syntaxColouring.string.bold=false
95 | syntaxColouring.string.colour=42,0,255
96 | syntaxColouring.string.enabled=true
97 | syntaxColouring.string.italic=false
98 | syntaxColouring.string.underline=false
99 | syntaxColouring.xml.attributeName.backgroundColourEnabled=false
100 | syntaxColouring.xml.attributeName.bold=false
101 | syntaxColouring.xml.attributeName.colour=127,0,127
102 | syntaxColouring.xml.attributeName.enabled=true
103 | syntaxColouring.xml.attributeName.italic=false
104 | syntaxColouring.xml.attributeName.underline=false
105 | syntaxColouring.xml.attributeValue.backgroundColourEnabled=false
106 | syntaxColouring.xml.attributeValue.bold=false
107 | syntaxColouring.xml.attributeValue.colour=42,0,255
108 | syntaxColouring.xml.attributeValue.enabled=true
109 | syntaxColouring.xml.attributeValue.italic=true
110 | syntaxColouring.xml.attributeValue.underline=false
111 | syntaxColouring.xml.comment.backgroundColourEnabled=false
112 | syntaxColouring.xml.comment.bold=false
113 | syntaxColouring.xml.comment.colour=63,85,191
114 | syntaxColouring.xml.comment.enabled=true
115 | syntaxColouring.xml.comment.italic=false
116 | syntaxColouring.xml.comment.underline=false
117 | syntaxColouring.xml.equals.backgroundColourEnabled=false
118 | syntaxColouring.xml.equals.bold=false
119 | syntaxColouring.xml.equals.colour=0,0,0
120 | syntaxColouring.xml.equals.enabled=true
121 | syntaxColouring.xml.equals.italic=false
122 | syntaxColouring.xml.equals.underline=false
123 | syntaxColouring.xml.processingInstruction.backgroundColourEnabled=false
124 | syntaxColouring.xml.processingInstruction.bold=false
125 | syntaxColouring.xml.processingInstruction.colour=0,128,128
126 | syntaxColouring.xml.processingInstruction.enabled=true
127 | syntaxColouring.xml.processingInstruction.italic=false
128 | syntaxColouring.xml.processingInstruction.underline=false
129 | syntaxColouring.xml.tagDelimiter.backgroundColourEnabled=false
130 | syntaxColouring.xml.tagDelimiter.bold=false
131 | syntaxColouring.xml.tagDelimiter.colour=0,128,128
132 | syntaxColouring.xml.tagDelimiter.enabled=true
133 | syntaxColouring.xml.tagDelimiter.italic=false
134 | syntaxColouring.xml.tagDelimiter.underline=false
135 | syntaxColouring.xml.tagName.backgroundColourEnabled=false
136 | syntaxColouring.xml.tagName.bold=false
137 | syntaxColouring.xml.tagName.colour=63,127,127
138 | syntaxColouring.xml.tagName.enabled=true
139 | syntaxColouring.xml.tagName.italic=false
140 | syntaxColouring.xml.tagName.underline=false
141 | target=jvm-1.6
142 | unchecked=false
143 | verbose=false
144 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: target/start com.yankay.scalaTour.Web
2 |
--------------------------------------------------------------------------------
/bin/application.conf:
--------------------------------------------------------------------------------
1 | remote {
2 | akka {
3 |
4 | actor {
5 | provider = "akka.remote.RemoteActorRefProvider"
6 | }
7 | remote {
8 | transport = "akka.remote.netty.NettyRemoteTransport"
9 | netty {
10 | hostname = "127.0.0.1"
11 | port = 2552
12 | }
13 | }
14 |
15 | }
16 | }
--------------------------------------------------------------------------------
/build.sbt:
--------------------------------------------------------------------------------
1 | import com.typesafe.sbt.SbtStartScript
2 |
3 | seq(SbtStartScript.startScriptForClassesSettings: _*)
4 |
5 | net.virtualvoid.sbt.graph.Plugin.graphSettings
6 |
7 | name := "scala-tour"
8 |
9 | version := "1.0"
10 |
11 | scalaVersion := "2.10.1"
12 |
13 | libraryDependencies ++= Seq(
14 | "org.scala-lang" % "scala-actors" % "2.10.1",
15 | "org.scala-lang" % "scala-compiler" % "2.10.1",
16 | "com.typesafe.akka" %% "akka-testkit" % "2.1.2",
17 | "com.typesafe.akka" %% "akka-actor" % "2.1.2",
18 | "com.typesafe.akka" %% "akka-remote" % "2.1.2",
19 | "org.eclipse.jetty" % "jetty-server" % "8.1.10.v20130312",
20 | "org.eclipse.jetty" % "jetty-servlet" % "8.1.10.v20130312",
21 | "org.eclipse.jetty" % "jetty-servlets" % "8.1.10.v20130312",
22 | "org.json4s" %% "json4s-jackson" % "3.2.2",
23 | "commons-beanutils" % "commons-beanutils" % "1.8.3",
24 | "org.specs2" % "specs2_2.10" % "1.14"
25 | )
--------------------------------------------------------------------------------
/policy:
--------------------------------------------------------------------------------
1 | /* AUTOMATICALLY GENERATED ON Sat Aug 10 20:31:30 PDT 2013*/
2 | /* DO NOT EDIT */
3 |
4 | grant {
5 | permission java.io.FilePermission "${user.home}/.ivy2/cache/com.typesafe", "read";
6 | permission java.io.FilePermission "${user.home}/.ivy2/cache/com.typesafe/-", "read";
7 | permission java.io.FilePermission "${user.home}/.ivy2/cache/com.typesafe.akka", "read";
8 | permission java.io.FilePermission "${user.home}/.ivy2/cache/com.typesafe.akka/-", "read";
9 | permission java.io.FilePermission "./target/specs2-reports", "read, write";
10 | permission java.io.FilePermission "./target/specs2-reports/-", "read, write";
11 |
12 | permission java.io.FilePermission "/tmp", "read";
13 |
14 | permission java.io.FilePermission "./bin/application.conf", "read";
15 | permission java.io.FilePermission "/usr/lib/jvm/java-*/-", "read";
16 | permission java.io.FilePermission "/usr/lib/jvm/java-*/jre/-", "read";
17 | permission java.io.FilePermission "/usr/java/packages/lib/ext/-", "read";
18 | permission java.io.FilePermission "/usr/java/packages/lib/ext", "read";
19 | permission java.io.FilePermission "/usr/share/java/-", "read";
20 |
21 | permission java.util.PropertyPermission "java.version", "read";
22 | permission java.util.PropertyPermission "java.vendor", "read";
23 | permission java.util.PropertyPermission "os.name", "read";
24 | permission java.util.PropertyPermission "os.version", "read";
25 | permission java.util.PropertyPermission "os.arch", "read";
26 |
27 | permission java.io.FilePermission "/proc/self/stat", "read";
28 |
29 | permission java.net.SocketPermission "github.com:443", "connect, resolve";
30 | permission java.net.SocketPermission "raw.github.com:443", "connect, resolve";
31 | permission java.net.SocketPermission "scala-lang.org:80", "connect, resolve";
32 | permission java.net.SocketPermission "127.0.0.1:2552", "connect, resolve, listen, accept";
33 |
34 | permission java.lang.RuntimePermission "getenv.AKKA_HOME", "read";
35 | permission java.util.PropertyPermission "akka.home", "read";
36 | permission java.util.PropertyPermission "*", "read, write";
37 | permission java.lang.RuntimePermission "setContextClassLoader";
38 | permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
39 | permission java.lang.RuntimePermission "accessClassInPackage.sun.misc";
40 | permission java.lang.RuntimePermission "accessDeclaredMembers";
41 | permission java.lang.RuntimePermission "modifyThread";
42 | };
43 |
44 |
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.12.2
2 |
--------------------------------------------------------------------------------
/project/cdn.scala:
--------------------------------------------------------------------------------
1 | object CDN {
2 |
3 | def main(args: Array[String]) {
4 | val url = args(0)
5 | val cdn = scala.collection.mutable.Map[String, String]()
6 | cdn += ("""href="css/""" -> ("href=\"" + url + "css/").toString())
7 | cdn += ("""src="js/""" -> ("src=\"" + url + "js/").toString())
8 | cdn += ("""src="img/""" -> ("src=\"" + url + "img/").toString())
9 |
10 | scala.io.Source.stdin.getLines.foreach(
11 | x => {
12 | var line = x
13 | cdn.foreach(p => line = line.replace(p._1, p._2))
14 | println(line)
15 | })
16 | }
17 | }
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | addSbtPlugin("com.typesafe.sbt" % "sbt-start-script" % "0.7.0")
2 |
3 | addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.1.1")
4 |
5 | addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.7.1")
6 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Scala Tour
2 |
3 | A cooler tour the same as scala language
4 |
5 | [http://www.scala-tour.com/](http://www.scala-tour.com/)
6 |
7 |
8 | ## Compile And Run
9 | ```
10 | Linux/Mac:
11 | ./sbt/sbt stage
12 | ./target/start
13 |
14 | Windows can only compile
15 | sbt\sbt stage
16 | ```
17 | view
18 | http://localhost:8080/#/welcome
19 |
20 | ## Pure Text
21 | [Scala tour](https://github.com/yankay/scala-tour/blob/master/scala-tour.md)
22 |
23 | ## Translate
24 | The tour is can be translate to Chinese by these scripts:
25 | ```
26 | cat webapp/index.html | scala project/translate-zh.scala > webapp/index-cn.html
27 | cat scala-tour.md | scala project/translate-zh.scala > scala-tour-cn.md
28 |
29 | ```
30 |
--------------------------------------------------------------------------------
/sbt/sbt:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | EXTRA_ARGS=""
3 | if [ "$MESOS_HOME" != "" ]; then
4 | EXTRA_ARGS="-Djava.library.path=$MESOS_HOME/lib/java"
5 | fi
6 | export SPARK_HOME=$(cd "$(dirname $0)/.."; pwd)
7 | export SPARK_TESTING=1 # To put test classes on classpath
8 | java -Xmx1200M -XX:MaxPermSize=200m $EXTRA_ARGS -jar $SPARK_HOME/sbt/sbt-launch-*.jar "$@"
9 |
--------------------------------------------------------------------------------
/sbt/sbt-launch-0.11.3-2.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yankay/scala-tour/7a482eea52e1ee429cb5961cb7ee5272227b44b1/sbt/sbt-launch-0.11.3-2.jar
--------------------------------------------------------------------------------
/sbt/sbt.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 | set EXTRA_ARGS=
3 | if not "%MESOS_HOME%x"=="x" set EXTRA_ARGS=-Djava.library.path=%MESOS_HOME%\lib\java
4 | set SPARK_HOME=%~dp0..
5 | java -Xmx1200M -XX:MaxPermSize=200m %EXTRA_ARGS% -jar %SPARK_HOME%\sbt\sbt-launch-0.11.3-2.jar "%*"
6 |
--------------------------------------------------------------------------------
/scala-tour-zh.md:
--------------------------------------------------------------------------------
1 | # Scala 指南
2 |
3 | [http://zh.scala-修改为ur.com/](http://zh.scala-修改为ur.com/)
4 |
5 | ## 基本
6 |
7 | ### 表达式和值
8 |
9 | 在Scala中,几乎所有的语言元素都是表达式。println("hello wolrd")是一个表达式, "hello"+" world"
10 | 也是一个表达式。
11 |
12 | 可以通过val定义一个常量,亦可以通过var定义一个变量。推荐多使用常量。
13 |
14 | ```
15 | var helloWorld = "hello" + " world"
16 | println(helloWorld)
17 |
18 | val again = " again"
19 | helloWorld = helloWorld + again
20 | println(helloWorld)
21 | ```
22 |
23 | ### 函数是一等公民
24 |
25 | 可以使用def来定义一个函数。函数体是一个表达式。
26 |
27 | 使用Block表达式的时候,默认最后一行的返回是返回值,无需显式指定。
28 |
29 | 函数还可以像值一样,赋值给var或val。因此他也可以作为参数传给另一个函数。
30 |
31 | ```
32 | def square(a: Int) = a * a
33 |
34 | def squareWithBlock(a: Int) = {
35 | a * a
36 | }
37 |
38 | val squareVal = (a: Int) => a * a
39 |
40 | def addOne(f: Int => Int, arg: Int) = f(arg) + 1
41 |
42 | println("square(2):" + square(2))
43 | println("squareWithBlock(2):" + squareWithBlock(2))
44 | println("squareVal(2):" + squareVal(2))
45 | println("addOne(squareVal,2):" + addOne(squareVal, 2))
46 | ```
47 |
48 |
49 |
50 | ### 借贷模式
51 |
52 | 由于函数可以像值一样作为参数传递,所以可以方便的实现借贷模式。
53 |
54 | 这个例子是从/proc/self/stat文件中读取当前进程的pid。
55 |
56 | withScanner封装了try-finally块,所以调用者不用再close。
57 |
58 | 注:当表达式没有返回值时,默认返回Unit。
59 |
60 | ```
61 | import scala.reflect.io.File
62 | import java.util.Scanner
63 |
64 | def withScanner(f: File, op: Scanner => Unit) = {
65 | val scanner = new Scanner(f.bufferedReader)
66 | try {
67 | op(scanner)
68 | } finally {
69 | scanner.close()
70 | }
71 | }
72 |
73 | withScanner(File("/proc/self/stat"),
74 | scanner => println("pid is " + scanner.next()))
75 | ```
76 |
77 |
78 | ### 按名称传递参数
79 |
80 | 这个例子演示了按名称传递参数,由于有除以0,所以运行该程序会产生异常。
81 |
82 | 试着将def log(msg: String)修改为def log(msg: => String)。由按值传递修改为按名称传递后将不会产生异常。
83 |
84 |
85 | 因为log函数的参数是按名称传递,参数会等到真正访问的时候才会计算,由于logEnable = false,所以被跳过。
86 |
87 |
88 | 按名称传递参数可以减少不必要的计算和异常。
89 |
90 |
91 | ```
92 | val logEnable = false
93 |
94 | def log(msg: String) =
95 | if (logEnable) println(msg)
96 |
97 | val MSG = "programing is running"
98 |
99 | log(MSG + 1 / 0)
100 | ```
101 |
102 | ### 类定义
103 |
104 | 可以用class关键字来定义类。并通过new来创建类。
105 | 在定义类时可以定义字段,如firstName,lastName。这样做还可以自动生成构造函数。
106 | 可以在类中通过def定义函数。var和val定义字段。
107 | 函数名是任何字符如+,-,*,/。
108 | 例子中obama.age_=(51)的函数调用,可以简化为obama.age = 51 。
109 | obama.age()的函数调用,可以省略小括号,简化为obama.age。
110 |
111 | ```
112 | class Person(val firstName: String, val lastName: String) {
113 |
114 | private var _age = 0
115 | def age = _age
116 | def age_=(newAge: Int) = _age = newAge
117 |
118 | def fullName() = firstName + " " + lastName
119 |
120 | override def 修改为String() = fullName()
121 | }
122 |
123 | val obama: Person = new Person("Barack", "Obama")
124 |
125 | println("Person: " + obama)
126 | println("firstName: " + obama.firstName)
127 | println("lastName: " + obama.lastName)
128 | obama.age_=(51)
129 | println("age: " + obama.age())
130 | ```
131 |
132 |
133 | ### 鸭子类型
134 |
135 | 走起来像鸭子,叫起来像鸭子,就是鸭子。
136 | 这个例子中使用{ def close(): Unit }作为参数类型。因此任何含有close()的函数的类都可以作为参数。
137 | 不必使用继承这种不够灵活的特性。
138 |
139 | ```
140 | def withClose(closeAble: { def close(): Unit }, op: { def close(): Unit } => Unit) {
141 | try {
142 | op(closeAble)
143 | } finally {
144 | closeAble.close()
145 | }
146 | }
147 |
148 | class Connection {
149 | def close() = println("close Connection")
150 | }
151 |
152 | val conn: Connection = new Connection()
153 | withClose(conn, conn =>
154 | println("do something with Connection"))
155 | ```
156 |
157 |
158 | ### 柯里化
159 |
160 | 这个例子和上面的功能相同。不同的是使用了柯里化(Currying)的技术。
161 | def add(x:Int, y:Int) = x + y 是普通的函数
162 | def add(x:Int) = (y:Int) => x + y 是柯里化后的函数,相当于返回一个匿名函数表达式。
163 | def add(x:Int)(y:Int) = x + y 是上面的简化写法
164 | 柯里化可以让我们构造出更像原生语言提供的功能的代码
165 | 例子中的withclose(...)(...)换成withclose(...){...}
166 | 是否和java中的synchronized关键字用法很像?
167 |
168 | ```
169 | def withClose(closeAble: { def close(): Unit })(op: { def close(): Unit } => Unit) {
170 | try {
171 | op(closeAble)
172 | } finally {
173 | closeAble.close()
174 | }
175 | }
176 |
177 | class Connection {
178 | def close() = println("close Connection")
179 | }
180 |
181 | val conn: Connection = new Connection()
182 | withClose(conn)(conn =>
183 | println("do something with Connection"))
184 | ```
185 |
186 |
187 | ### 范型
188 |
189 | 之前的例子可以使用泛型变得更简洁更灵活。
190 | 试着将val msg = "123456"修改为val msg = 123456。
191 | 虽然msg由String类型变为Int类型,但是由于使用了泛型,代码依旧可以正常运行。
192 |
193 | ```
194 | def withClose[A <: { def close(): Unit }, B](closeAble: A)(op: A => B) {
195 | try {
196 | op(closeAble)
197 | } finally {
198 | closeAble.close()
199 | }
200 | }
201 |
202 | class Connection {
203 | val msg = "123456"
204 | def close() = println("close Connection")
205 | }
206 |
207 | val conn: Connection = new Connection()
208 | val msg = withClose(conn) { conn =>
209 | {
210 | println("do something with Connection")
211 | conn.msg
212 | }
213 | }
214 |
215 | println(msg)
216 | ```
217 |
218 | ### Traits
219 |
220 | Traits就像是有函数体的Interface。使用with关键字来混入。
221 | 这个例子是给java.util.ArrayList添加了foreach的功能。
222 | 试着再在后面加上with JsonAble,给list添加修改为Json的能力
223 |
224 | ```
225 | trait ForEachAble[A] {
226 | def itera修改为r: java.util.Itera修改为r[A]
227 | def foreach(f: A => Unit) = {
228 | val iter = itera修改为r
229 | while (iter.hasNext)
230 | f(iter.next)
231 | }
232 | }
233 |
234 | trait JsonAble {
235 | override def 修改为String() =
236 | scala.util.parsing.json.JSONFormat.defaultFormatter(this)
237 | }
238 |
239 | val list = new java.util.ArrayList[Int]() with ForEachAble[Int]
240 | list.add(1); list.add(2)
241 |
242 | list.foreach(x => println(x))
243 | println("Json: " + list.修改为String())
244 | ```
245 |
246 |
247 | ## 函数式编程
248 |
249 | ### 模式匹配
250 |
251 | 模式匹配是类似switch-case特性,但更加灵活;也类似if-else,但更加简约。
252 | 这个例子展示的使用用模式匹配实现斐波那契。
253 | 使用case来匹配参数,如果case _,则可以匹配任何参数。
254 | 这个例子有所不足,当输入负数时,会无限循环。
255 | 可以在case后添加if语句判断,将case n: Int 修改为 case n: Int if (n > 1)即可。
256 | Try 修改为 add case n: String => fibonacci(n.修改为Int) before case _ 这样就可以匹配String类型
257 | 在最后添加 println(fibonacci(-3));println(fibonacci("3"));来检查刚刚修改的效果。
258 |
259 |
260 | ```
261 | def fibonacci(in: Any): Int = in match {
262 | case 0 => 0
263 | case 1 => 1
264 | case n: Int => fibonacci(n - 1) + fibonacci(n - 2)
265 | case _ => 0
266 | }
267 |
268 | println(fibonacci(3))
269 | ```
270 |
271 | ### Case Class
272 |
273 | case class 顾名思义就是为case语句专门设计的类。
274 | 在普通类的基础上添加了和类名一致的工厂方法,
275 | 还添加了hashcode,equals和修改为String等方法。
276 | 试试最后添加 println(Sum(1,2)) 。
277 | 由于使用了require(n >= 0)来检验参数,尝试使用负数,会抛出异常。
278 |
279 |
280 | ```
281 | abstract class Expr
282 |
283 | case class FibonacciExpr(n: Int) extends Expr {
284 | require(n >= 0)
285 | }
286 |
287 | case class SumExpr(a: Expr, b: Expr) extends Expr
288 |
289 | def value(in: Expr): Int = in match {
290 | case FibonacciExpr(0) => 0
291 | case FibonacciExpr(1) => 1
292 | case FibonacciExpr(n) => value(SumExpr(FibonacciExpr(n - 1), FibonacciExpr(n - 2)))
293 | case SumExpr(a, b) => value(a) + value(b)
294 | case _ => 0
295 | }
296 | println(value(FibonacciExpr(3)))
297 | ```
298 |
299 | ### 函数式的威力
300 |
301 | 这个例子是判断一个List中是否含有奇数。
302 | 第一行到倒数第二行是使用for循环的指令式编程解决。最后一行是通过函数式编程解决。
303 | 通过将函数作为参数,可以使程序极为简洁。其中 _ % 2 == 1 是 (x: Int) => x % 2 == 1 的简化写法。
304 | 相比于Ruby等动态语言,这威力来自于科学而不是魔法
305 |
306 | ```
307 | val list = List(1, 2, 3, 4)
308 |
309 | def containsOdd(list: List[Int]): Boolean = {
310 | for (i <- list) {
311 | if (i % 2 == 1)
312 | return true;
313 | }
314 | return false;
315 | }
316 |
317 | println("list containsOdd by for loop:" + containsOdd(list))
318 |
319 | println("list containsOdd by funtional:" + list.exists(_ % 2 == 1))
320 | ```
321 |
322 | ### 函数式真正的威力
323 |
324 | 函数式除了能简化代码外,更重要的是他关注的是Input和Output,函数本身没有副作用。
325 | 就是Unix pipeline一样,简单的命令组合在一起威力无穷。
326 | List的filter方法接受一个过滤函数,返回一个新的List。
327 | 如果你喜欢Unix pipeline的方式,你一定也会喜欢函数式编程。
328 | 这个例子是用函数式的代码模拟“cat file | grep 'warn' | grep '2013' | wc”的行为。
329 |
330 | ```
331 | val file = List("warn 2013 msg", "warn 2012 msg", "error 2013 msg", "warn 2013 msg")
332 |
333 | println("cat file | grep 'warn' | grep '2013' | wc : "
334 | + file.filter(_.contains("warn")).filter(_.contains("2013")).size)
335 | ```
336 | ### Word Count
337 | Word Count是一个MapReduce的一个经典示例。巧合的是,使用函数式的编程法,用类似MapReduce的方法实现word count也是最直观的。
338 | 这个例子介绍了List的两个重要的高阶方法map和reduceLeft。
339 | List的map方法接受一个转换函数,返回一个经过转换的List。
340 | List的reduceLeft方法接受一个合并函数,依次遍历合并。第一个参数是合并后的值,第二个参数是下一个需要合并的值。
341 | 将reduceLeft(_ + _)修改为foldLeft(0)(_ + _)。foldLeft比将reduceLeft更常用,因为他可以提供一个初始参数。
342 | Map和foldLeft可以代替大部分需要for循环的操作,并且使代码更清晰
343 |
344 | ```
345 | val file = List("warn 2013 msg", "warn 2012 msg", "error 2013 msg", "warn 2013 msg")
346 |
347 | def wordcount(str: String): Int = str.split(" ").count("msg" == _)
348 |
349 | val num = file.map(wordcount).reduceLeft(_ + _)
350 |
351 | println("wordcount:" + num)
352 | ```
353 | ### 尾递归
354 |
355 | 这个例子是用尾递归实现foldLeft。尾递归是递归的一种,特点在于会在函数的最末调用自身。
356 | 当用List做match case时,可以使用 :: 来解构。返回第一个元素head和剩余元素tail。
357 | 尾递归会在编译期优化,因此不用担心一般递归造成的栈溢出问题。
358 |
359 | ```
360 | val file = List("warn 2013 msg", "warn 2012 msg", "error 2013 msg", "warn 2013 msg")
361 |
362 | def wordcount(str: String): Int = str.split(" ").count("msg" == _)
363 |
364 | def foldLeft(list: List[Int])(init: Int)(f: (Int, Int) => Int): Int = {
365 | list match {
366 | case List() => init
367 | case head :: tail => foldLeft(tail)(f(init, head))(f)
368 | }
369 | }
370 |
371 | val num = foldLeft(file.map(wordcount))(0)(_ + _)
372 |
373 | println("wordcount:" + num)
374 | ```
375 |
376 | ### 更强大的For
377 |
378 | 循环语句是指令式编程的特产,Scala对其加以改进,成为适应函数式风格的利器。
379 | For循环也是有返回值的,其返回是一个List。在每一轮迭代中加入yield,yield后的值可以加入到List中。
380 | 这个例子是使用for循环代替map函数。
381 |
382 | ```
383 | val file = List("warn 2013 msg", "warn 2012 msg", "error 2013 msg", "warn 2013 msg")
384 |
385 | def wordcount(str: String): Int = str.split(" ").count("msg" == _)
386 |
387 | val counts =
388 | for (line <- file)
389 | yield wordcount(line)
390 |
391 | val num = counts.reduceLeft(_ + _)
392 |
393 | println("wordcount:" + num)
394 | ```
395 | ### Option
396 |
397 | NullException是Java中最常见的异常,要想避免他只有不断检查null。Scala提供了Option机制来解决。
398 | 这个例子包装了可能返回null的getProperty方法,使其返回一个Option。
399 | 这样就可以不再漫无目的地null检查。只要Option类型的值即可。
400 | 使用pattern match来检查是常见做法。也可以使用getOrElse来提供当为None时的默认值。
401 | 给力的是Option还可以看作是最大长度为1的List,其的强大功能都可以使用。
402 | 尝试在最后添加 osName.foreach(print _) 。
403 |
404 |
405 | ```
406 |
407 | def getProperty(name: String): Option[String] = {
408 | val value = System.getProperty(name)
409 | if (value != null) Some(value) else None
410 | }
411 |
412 | val osName = getProperty("os.name")
413 |
414 | osName match {
415 | case Some(value) => println(value)
416 | case _ => println("none")
417 | }
418 |
419 | println(osName.getOrElse("none"))
420 |
421 |
422 |
423 | ```
424 |
425 |
426 | ### Lazy
427 |
428 | Lazy可以延迟初始化。加上lazy的字段会在第一次访问的时候初始化。
429 | 这个例子是从github获得Scala的版本号,由于访问网络需要较多时间。
430 | 如果费尽力气获取到,而调用它的代码却不去访问就会很浪费。
431 | 可以使用lazy来延迟获取。
432 |
433 | ```
434 | class ScalaCurrentVersion(val url: String) {
435 | lazy val source= {
436 | println("fetching from url...")
437 | scala.io.Source.fromURL(url).getLines().修改为List
438 | }
439 | lazy val majorVersion = source.find(_.contains("version.major"))
440 | lazy val minorVersion = source.find(_.contains("version.minor"))
441 | }
442 | val version = new ScalaCurrentVersion("https://raw.github.com/scala/scala/master/build.number")
443 | println("get scala version from " + version.url)
444 | version.majorVersion.foreach(println _)
445 | version.minorVersion.foreach(println _)
446 | ```
447 |
448 |
449 | ## 并发
450 |
451 | ### 使用Ac修改为r
452 |
453 | Ac修改为r是Scala的并发模型。在2.10之后的版本中,使用[http://akka.io/](Akka)作为其推荐Ac修改为r实现。
454 | Ac修改为r是类似线程的实体,有一个邮箱。
455 | 可以通过system.ac修改为rOf来创建,receive获取邮箱消息,!向邮箱发送消息。
456 | 这个例子是一个EchoServer,接受信息并打印。
457 |
458 | ```
459 | import akka.ac修改为r.{ Ac修改为r, Ac修改为rSystem, Props }
460 |
461 | val system = Ac修改为rSystem()
462 |
463 | class EchoServer extends Ac修改为r {
464 | def receive = {
465 | case msg: String => println("echo " + msg)
466 | }
467 | }
468 |
469 | val echoServer = system.ac修改为rOf(Props[EchoServer])
470 | echoServer ! "hi"
471 |
472 | system.shutdown
473 | ```
474 |
475 | ### Ac修改为r更简化的用法
476 |
477 | 可以通过更简化的办法声明Ac修改为r。
478 | 导入akka.ac修改为r.Ac修改为rDSL中的ac修改为r函数。
479 | 这个函数可以接受一个Ac修改为r的构造器Act,启动并返回Ac修改为r。
480 |
481 |
482 | ```
483 | import akka.ac修改为r.Ac修改为rDSL._
484 | import akka.ac修改为r.Ac修改为rSystem
485 |
486 | implicit val system = Ac修改为rSystem()
487 |
488 | val echoServer = ac修改为r(new Act {
489 | become {
490 | case msg => println("echo " + msg)
491 | }
492 | })
493 | echoServer ! "hi"
494 | system.shutdown
495 | ```
496 | ### Ac修改为r原理
497 | Ac修改为r比线程轻量。在Scala中可以创建数以百万级的Ac修改为r。奥秘在于Ac修改为r可以复用线程。
498 | Ac修改为r和线程是不同的抽象,他们的对应关系是由Dispatcher决定的。
499 | 这个例子创建4个Ac修改为r,每次调用的时候打印自身线程。
500 | 可以发现Ac修改为r和线程之间没有一对一的对应关系。
501 | 一个Ac修改为r可以使用多个线程,一个线程也会被多个Ac修改为r复用。
502 |
503 | ```
504 | import akka.ac修改为r.{ Ac修改为r, Props, Ac修改为rSystem }
505 | import akka.testkit.CallingThreadDispatcher
506 |
507 | implicit val system = Ac修改为rSystem()
508 |
509 | class EchoServer(name: String) extends Ac修改为r {
510 | def receive = {
511 | case msg => println("server" + name + " echo " + msg +
512 | " by " + Thread.currentThread().getName())
513 | }
514 | }
515 |
516 | val echoServers = (1 修改为 10).map(x =>
517 | system.ac修改为rOf(Props(new EchoServer(x.修改为String))
518 | .withDispatcher(CallingThreadDispatcher.Id)))
519 | (1 修改为 10).foreach(msg =>
520 | echoServers(scala.util.Random.nextInt(10)) ! msg.修改为String)
521 |
522 | system.shutdown
523 | ```
524 |
525 |
526 |
527 | ### 同步返回
528 |
529 | Ac修改为r非常适合于较耗时的操作。比如获取网络资源。
530 | 这个例子通过调用ask函数来获取一个Future。
531 | 在Ac修改为r内部通过 sender ! 传递结果。
532 | Future像Option一样有很多高阶方法,可以使用foreach查看结果。
533 |
534 |
535 | ```
536 | import akka.ac修改为r.Ac修改为rDSL._
537 | import akka.pattern.ask
538 |
539 | implicit val ec = scala.concurrent.ExecutionContext.Implicits.global
540 | implicit val system = akka.ac修改为r.Ac修改为rSystem()
541 |
542 | val versionUrl = "https://raw.github.com/scala/scala/master/starr.number"
543 |
544 | val fromURL = ac修改为r(new Act {
545 | become {
546 | case url: String => sender ! scala.io.Source.fromURL(url)
547 | .getLines().mkString("\n")
548 | }
549 | })
550 |
551 | val version = fromURL.ask(versionUrl)(akka.util.Timeout(5 * 1000))
552 | version.foreach(println _)
553 |
554 | system.shutdown
555 | ```
556 |
557 | ### 异步返回
558 |
559 | 异步操作可以最大发挥效能。Scala的Futrue很强大,可以异步返回。
560 | 可以实现Futrue的onComplete方法。当Futrue结束的时候就会回调。
561 | 在调用ask的时候,可以设定超时。
562 |
563 | ```
564 | import akka.ac修改为r.Ac修改为rDSL._
565 | import akka.pattern.ask
566 |
567 | implicit val ec = scala.concurrent.ExecutionContext.Implicits.global
568 | implicit val system = akka.ac修改为r.Ac修改为rSystem()
569 |
570 | val versionUrl = "https://raw.github.com/scala/scala/master/starr.number"
571 |
572 | val fromURL = ac修改为r(new Act {
573 | become {
574 | case url: String => sender ! scala.io.Source.fromURL(url)
575 | .getLines().mkString("\n")
576 | }
577 | })
578 |
579 | val version = fromURL.ask(versionUrl)(akka.util.Timeout(5 * 1000))
580 | version onComplete {
581 | case msg => println(msg); system.shutdown
582 | }
583 | ```
584 | ### 并发集合
585 |
586 | 这个例子是访问若干URL,并记录时间。
587 | 如果能并发访问,就可以大幅提高性能。
588 | 尝试将urls.map修改为urls.par.map。这样每个map中的函数都可以并发执行。
589 | 当函数式和并发结合,就会这样让人兴奋。
590 |
591 | ```
592 | import scala.io.Codec
593 | import java.nio.charset.CodingErrorAction
594 |
595 | implicit val codec = Codec("UTF-8")
596 | codec.onMalformedInput(CodingErrorAction.REPLACE)
597 | codec.onUnmappableCharacter(CodingErrorAction.REPLACE)
598 |
599 | val urls = "http://scala-lang.org" :: "https://github.com/yankay/scala-修改为ur" :: Nil
600 |
601 | def fromURL(url: String) = scala.io.Source.fromURL(url).getLines().mkString("\n")
602 |
603 | val s = System.currentTimeMillis()
604 | time(urls.map(fromURL(_)))
605 | println("time: " + (System.currentTimeMillis - s) + "ms")
606 | ```
607 |
608 | ### 并发wordcount
609 | 并发集合支持大部分集合的功能。
610 | 在前面有一个word count例子,也可以用并行集合加以实现。
611 | 不增加程序复杂性,却能大幅提高程序利用多核的能力。
612 |
613 | ```
614 | val file = List("warn 2013 msg", "warn 2012 msg", "error 2013 msg", "warn 2013 msg")
615 |
616 | def wordcount(str: String): Int = str.split(" ").count("msg" == _)
617 |
618 | val num = file.par.map(wordcount).par.reduceLeft(_ + _)
619 |
620 | println("wordcount:" + num)
621 | ```
622 |
623 | ### 远程Ac修改为r
624 | Ac修改为r是并发模型,也使用于分布式。
625 | 这个例子创建一个Echo服务器,通过ac修改为rOf来注册自己。
626 | 然后再创建一个client,通过Akka url来寻址。
627 | 除了是通过url创建的,其他使用的方法和普通Ac修改为r一样。
628 |
629 | ```
630 | import akka.ac修改为r.{ Ac修改为r, Ac修改为rSystem, Props }
631 | import com.typesafe.config.ConfigFac修改为ry
632 |
633 | implicit val system = akka.ac修改为r.Ac修改为rSystem("RemoteSystem",
634 | ConfigFac修改为ry.load.getConfig("remote"))
635 | class EchoServer extends Ac修改为r {
636 | def receive = {
637 | case msg: String => println("echo " + msg)
638 | }
639 | }
640 |
641 | val server = system.ac修改为rOf(Props[EchoServer], name = "echoServer")
642 |
643 | val echoClient = system
644 | .ac修改为rFor("akka://RemoteSystem@127.0.0.1:2552/user/echoServer")
645 | echoClient ! "Hi Remote"
646 |
647 | system.shutdown
648 |
649 | ```
650 |
651 | ## 实践
652 |
653 | ### 使用Java
654 |
655 | Scala和Java可以非常方便的互操作,前面已经有大量Scala直接使用Java的例子。
656 | 同样Java也可以使用Scala。这个例子演示使用@BeanProperty注解来生成Java Style的Bean。
657 | 尝试将在var name前加上@BeanProperty。这样就给bean添加了getter/setter
658 | Apache BeanUtils就可以正常工作。
659 |
660 | ```
661 | import org.apache.commons.beanutils.BeanUtils
662 | import scala.beans.BeanProperty
663 |
664 | class SimpleBean( var name: String) {
665 | }
666 |
667 | val bean = new SimpleBean("foo")
668 |
669 | println(BeanUtils.describe(bean))
670 | ```
671 |
672 | ### 相等性
673 | 在Scala中==操作等效于equals,这一点和Java不同。更自然一些。
674 | 这个例子定义了一个equals函数,并验证。
675 | 写一个完全正确的equal函数并不容易,这个例子也有子类会不对称的Bug。
676 | 尝试将class修改为case class并删除equals函数。
677 | case类会自动生成正确的equals函数。
678 |
679 | ```
680 | class Person(val name: String) {
681 | override def equals(other: Any) = other match {
682 | case that: Person => name.equals(that.name)
683 | case _ => false
684 | }
685 | }
686 |
687 | println(new Person("Black") == new Person("Black"))
688 | ```
689 |
690 |
691 | ### 抽取器
692 | 抽取器可以帮助pattern match进行解构。
693 | 这个例子是构建一个Email抽取器,只要实现unapply函数就可以了。
694 | Scala的正则表达式会自带抽取器,可以抽取出一个List。List的元素是匹配()里的表达式。
695 | 抽取器很有用,短短的例子里就有两处使用抽取器:
696 | case user :: domain :: Nil解构List;case Email(user, domain) 解构Email。
697 |
698 | ```
699 | object Email {
700 | def apply(user: String, domain: String) = user + "@" + domain
701 |
702 | def unapply(str: String) = new Regex("""(.*)@(.*)""").unapplySeq(str).get match {
703 | case user :: domain :: Nil => Some(user, domain)
704 | case _ => None
705 | }
706 | }
707 |
708 | "user@domain.com" match {
709 | case Email(user, domain) => println(user + "@" + domain)
710 | }
711 | ```
712 |
713 | ### 记忆模式
714 | 记忆模式可以解决手动编写存取cache代码的麻烦。
715 | 这个例子中,memo可以将一个不含cache函数,包装成一个含有cache功能的。
716 | 还是斐波那契的例子,通过cache可以使性能提高。
717 | 尝试将fibonacci_(n - 1) + fibonacci_(n - 2)修改memo(fibonacci_)(n - 1) + memo(fibonacci_)(n - 2),可以提高更多。
718 |
719 | ```
720 | import scala.collection.mutable.WeakHashMap
721 |
722 | val cache = new WeakHashMap[Int, Int]
723 | def memo(f: Int => Int) = (x: Int) => cache.getOrElseUpdate(x, f(x))
724 |
725 | def fibonacci_(in: Int): Int = in match {
726 | case 0 => 0;
727 | case 1 => 1;
728 | case n: Int => fibonacci_(n - 1) + fibonacci_(n - 2)
729 | }
730 |
731 | val fibonacci: Int => Int = memo(fibonacci_)
732 |
733 | val t1 = System.currentTimeMillis()
734 | println(fibonacci(40))
735 | println("it takes " + (System.currentTimeMillis() - t1) + "ms")
736 |
737 | val t2 = System.currentTimeMillis()
738 | println(fibonacci(40))
739 | println("it takes " + (System.currentTimeMillis() - t2) + "ms")
740 | ```
741 | ### 隐式转换
742 | implicit可以定义一个转换函数,可以在下面的使用到的时候自动转换。
743 | 这个例子可以将String自动转换为Date类型。隐式转换时实现DSL的重要工具。
744 |
745 | ```
746 | implicit def strToDate(str: String) = new SimpleDateFormat("yyyy-MM-dd").parse(str)
747 |
748 | println("2013-01-01 unix time: " + "2013-01-01".getTime()/1000l)
749 | ```
750 |
751 | ### DSL
752 | DSL是Scala最强大武器,Scala可以使一些描述性代码变得极为简单。
753 | 这个例子是使用DSL生成JSON。Scala很多看似是语言级的特性也是用DSL做到的。
754 | 自己编写DSL有点复杂,但使用方便灵活的。
755 |
756 | ```
757 | import org.json4s._
758 | import org.json4s.JsonDSL._
759 |
760 | import org.json4s.jackson.JsonMethods._
761 |
762 | case class Twitter(id: Long, text: String, publishedAt: Option[java.util.Date])
763 |
764 | var twitters = Twitter(1, "hello scala", Some(new Date())) :: Twitter(2, "I like scala 修改为ur", None) :: Nil
765 |
766 | var json = ("twitters"
767 | -> twitters.map(
768 | t => ("id" -> t.id)
769 | ~ ("text" -> t.text)
770 | ~ ("published_at" -> t.publishedAt.修改为String())))
771 |
772 | println(pretty(render(json)))
773 | ```
774 |
775 | ### 测试
776 | Scala DSL可以使测试更方便。
777 | 这个例子是测试一个阶乘函数。使用should/in来建立测试用例。
778 | 测试是默认并发执行的。
779 |
780 | ```
781 | import org.specs2.mutable._
782 |
783 | class Fac修改为rialSpec extends Specification {
784 | args.report(color = false)
785 |
786 | def fac修改为rial(n: Int) = (1 修改为 n).reduce(_ * _)
787 |
788 | "The 'Hello world' string" should {
789 | "fac修改为rial 3 must be 6" in {
790 | fac修改为rial(3) mustEqual 6
791 | }
792 | "fac修改为rial 4 must be 6" in {
793 | fac修改为rial(4) must greaterThan(6)
794 | }
795 | }
796 | }
797 | specs2.run(new Fac修改为rialSpec)
798 | ```
799 |
800 |
801 | ### Simple Build Tool
802 | SBT是Scala的最佳编译工具。
803 | 在他的帮助下,you can develop Scala even without installing anything except JRE.
804 | This example is 修改为 run this Scala 指南 in your computer.
805 |
806 | ```
807 | #Linux/Mac(compile & run):
808 | git clone https://github.com/yankay/scala-修改为ur-zh.git
809 | cd scala-修改为ur-zh
810 | ./sbt/sbt stage
811 | ./target/start
812 |
813 | #Windows(can only compile):
814 | git clone https://github.com/yankay/scala-修改为ur-zh.git
815 | cd scala-修改为ur-zh
816 | sbt\sbt stage
817 | ```
818 |
--------------------------------------------------------------------------------
/scala-tour.md:
--------------------------------------------------------------------------------
1 | # Scala Tour
2 |
3 | [http://www.scala-tour.com/](http://www.scala-tour.com/)
4 |
5 | ## Basic
6 |
7 | ### Expressions and Values
8 |
9 | In Scala, almost everything is an expression.println("hello wolrd")is an expression, "hello"+" world"
10 | is also an expression.
11 |
12 | Constants can be created with val, and variables can be created with var. More constants are better.
13 |
14 | ```
15 | var helloWorld = "hello" + " world"
16 | println(helloWorld)
17 |
18 | val again = " again"
19 | helloWorld = helloWorld + again
20 | println(helloWorld)
21 | ```
22 |
23 | ### First class Functions
24 |
25 | You can create functions with def. And the function body is an expression.
26 |
27 | When the body is a block expression, it returns the value of the last line. So there's no need to use the return keyword
28 |
29 | And like values, functions can also be assigned using var or val So it can be passed as an argument to another function.
30 |
31 | ```
32 | def square(a: Int) = a * a
33 |
34 | def squareWithBlock(a: Int) = {
35 | a * a
36 | }
37 |
38 | val squareVal = (a: Int) => a * a
39 |
40 | def addOne(f: Int => Int, arg: Int) = f(arg) + 1
41 |
42 | println("square(2):" + square(2))
43 | println("squareWithBlock(2):" + squareWithBlock(2))
44 | println("squareVal(2):" + squareVal(2))
45 | println("addOne(squareVal,2):" + addOne(squareVal, 2))
46 | ```
47 |
48 |
49 |
50 | ### Loan Pattern
51 |
52 | For functions which can be passed as arguments, the 'Loan' pattern is easy to implement.
53 |
54 | This example reads the self pid from /proc/self/stat.
55 |
56 | Because the 'withScanner' function encapsulates the 'try-finally' block,there's no need to call 'close()' any more.
57 |
58 | PS: the expression's return type is 'Unit' when it doesn't return a value.
59 |
60 | ```
61 | import scala.reflect.io.File
62 | import java.util.Scanner
63 |
64 | def withScanner(f: File, op: Scanner => Unit) = {
65 | val scanner = new Scanner(f.bufferedReader)
66 | try {
67 | op(scanner)
68 | } finally {
69 | scanner.close()
70 | }
71 | }
72 |
73 | withScanner(File("/proc/self/stat"),
74 | scanner => println("pid is " + scanner.next()))
75 | ```
76 |
77 |
78 | ### Call-by-Name
79 |
80 | This example shows the call by name, When the last line tries to calculate '1 / 0', the program will throw an exception.
81 |
82 | Try to change 'def log(msg: String)' to 'def log(msg: => String)'.The program will not throw an exception because it has been changed to call-by-name
83 |
84 |
85 | Call-by-name means that the argument will be calculated when it be actually called. Because 'logEnable = false', the '1 / 0' would be skipped.
86 |
87 |
88 | Call-by-name can reduce the useless calculation and exception.
89 |
90 |
91 | ```
92 | val logEnable = false
93 |
94 | def log(msg: String) =
95 | if (logEnable) println(msg)
96 |
97 | val MSG = "programing is running"
98 |
99 | log(MSG + 1 / 0)
100 | ```
101 |
102 | ### Define Class
103 |
104 | The 'class' keyword defines a class, and the 'new' keyword creates an instance.
105 | The fields can be also defined in class, like the 'firstName' and 'lastName'.These are automatically generated from the constructor's arguments.
106 | Methods can be defined with def, and fields can be defined with val or var
107 | The function name can be any characters like +,-,*,/.
108 | The 'obama.age_=(51)' can be simplified as 'obama.age = 51'.
109 | And the 'obama.age()' can be simplified as 'obama.age'.
110 |
111 | ```
112 | class Person(val firstName: String, val lastName: String) {
113 |
114 | private var _age = 0
115 | def age = _age
116 | def age_=(newAge: Int) = _age = newAge
117 |
118 | def fullName() = firstName + " " + lastName
119 |
120 | override def toString() = fullName()
121 | }
122 |
123 | val obama: Person = new Person("Barack", "Obama")
124 |
125 | println("Person: " + obama)
126 | println("firstName: " + obama.firstName)
127 | println("lastName: " + obama.lastName)
128 | obama.age_=(51)
129 | println("age: " + obama.age())
130 | ```
131 |
132 |
133 | ### Duck Typing
134 |
135 | When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.
136 | This example uses '{ def close(): Unit }' as the type of argument. So any class contains methods 'close()' can be passed.
137 | And there's no need to use 'inherit'。
138 |
139 | ```
140 | def withClose(closeAble: { def close(): Unit }, op: { def close(): Unit } => Unit) {
141 | try {
142 | op(closeAble)
143 | } finally {
144 | closeAble.close()
145 | }
146 | }
147 |
148 | class Connection {
149 | def close() = println("close Connection")
150 | }
151 |
152 | val conn: Connection = new Connection()
153 | withClose(conn, conn =>
154 | println("do something with Connection"))
155 | ```
156 |
157 |
158 | ### Currying
159 |
160 | This example is similar to the previous one.The difference between them is this one leverage currying technology。
161 | def add(x:Int, y:Int) = x + y is a normal function
162 | def add(x:Int) = (y:Int) => x + y is a curried function.The return value is a function expression.
163 | def add(x:Int)(y:Int) = x + y is syntactic sugar
164 | Currying can let our code look like it is part of the language.
165 | Change the withclose(...)(...) to withclose(...){...}
166 | Is it similar with Java's synchronized block?
167 |
168 | ```
169 | def withClose(closeAble: { def close(): Unit })(op: { def close(): Unit } => Unit) {
170 | try {
171 | op(closeAble)
172 | } finally {
173 | closeAble.close()
174 | }
175 | }
176 |
177 | class Connection {
178 | def close() = println("close Connection")
179 | }
180 |
181 | val conn: Connection = new Connection()
182 | withClose(conn)(conn =>
183 | println("do something with Connection"))
184 | ```
185 |
186 |
187 | ### Generic
188 |
189 | The sample before can be more simplified with generics.
190 | Try to change val msg = "123456" to val msg = 123456.
191 | Although the type of msg changed from String to Int, the program still compiles.
192 |
193 | ```
194 | def withClose[A <: { def close(): Unit }, B](closeAble: A)(op: A => B) {
195 | try {
196 | op(closeAble)
197 | } finally {
198 | closeAble.close()
199 | }
200 | }
201 |
202 | class Connection {
203 | val msg = "123456"
204 | def close() = println("close Connection")
205 | }
206 |
207 | val conn: Connection = new Connection()
208 | val msg = withClose(conn) { conn =>
209 | {
210 | println("do something with Connection")
211 | conn.msg
212 | }
213 | }
214 |
215 | println(msg)
216 | ```
217 |
218 | ### Traits
219 |
220 | Traits look like Java's interfaces, but with function blocks. One class can extend several traits using the with keyword.
221 | This example extends java.util.ArrayList with a foreach loop.
222 | Try to append with JsonAble to extend the ability of toJson.
223 |
224 | ```
225 | trait ForEachAble[A] {
226 | def iterator: java.util.Iterator[A]
227 | def foreach(f: A => Unit) = {
228 | val iter = iterator
229 | while (iter.hasNext)
230 | f(iter.next)
231 | }
232 | }
233 |
234 | trait JsonAble {
235 | override def toString() =
236 | scala.util.parsing.json.JSONFormat.defaultFormatter(this)
237 | }
238 |
239 | val list = new java.util.ArrayList[Int]() with ForEachAble[Int]
240 | list.add(1); list.add(2)
241 |
242 | list.foreach(x => println(x))
243 | println("Json: " + list.toString())
244 | ```
245 |
246 |
247 | ## Functional Programing
248 |
249 | ### Pattern Matching
250 |
251 | Pattern Matching is more flexible than switch-case and simpler than if-else.
252 | This example shows a Fibonacci function implemented with pattern matching.
253 | The case keyword matches on a value. The case _ means it can match anything.
254 | But the example has a bug. If the input is a negative number, it will loop endlessly.
255 | Try to add if after case. Change case n: Int to case n: Int if (n > 1).
256 | Try to add case n: String => fibonacci(n.toInt) before case _ to match String type.
257 | Try to add println(fibonacci(-3));println(fibonacci("3")) to test the program.
258 |
259 |
260 | ```
261 | def fibonacci(in: Any): Int = in match {
262 | case 0 => 0
263 | case 1 => 1
264 | case n: Int => fibonacci(n - 1) + fibonacci(n - 2)
265 | case _ => 0
266 | }
267 |
268 | println(fibonacci(3))
269 | ```
270 |
271 | ### Case Class
272 |
273 | Case classes are used to conveniently store and match on the contents of a class.
274 | You can construct them without using new.
275 | It also has hashcode, equality and nice toString methods.
276 | Try to append println(Sum(1,2)) last.
277 | Because of the require(n >= 0), it will throw an exception when the input is negative.
278 |
279 |
280 | ```
281 | abstract class Expr
282 |
283 | case class FibonacciExpr(n: Int) extends Expr {
284 | require(n >= 0)
285 | }
286 |
287 | case class SumExpr(a: Expr, b: Expr) extends Expr
288 |
289 | def value(in: Expr): Int = in match {
290 | case FibonacciExpr(0) => 0
291 | case FibonacciExpr(1) => 1
292 | case FibonacciExpr(n) => value(SumExpr(FibonacciExpr(n - 1), FibonacciExpr(n - 2)))
293 | case SumExpr(a, b) => value(a) + value(b)
294 | case _ => 0
295 | }
296 | println(value(FibonacciExpr(3)))
297 | ```
298 |
299 | ### The power of functional programing
300 |
301 | This example determines whether there is an odd number in the list.
302 | Every line of the code excluding the last line is created using imperative programming.The last line is created using functional programming.
303 | Treating function expression as function arguments can simplify code effectively.The _ % 2 == 1 is the syntactic sugar of (x: Int) => x % 2 == 1.
304 | Ruby's power is from magic, but Scala's power is from science.
305 |
306 | ```
307 | val list = List(1, 2, 3, 4)
308 |
309 | def containsOdd(list: List[Int]): Boolean = {
310 | for (i <- list) {
311 | if (i % 2 == 1)
312 | return true;
313 | }
314 | return false;
315 | }
316 |
317 | println("list containsOdd by for loop:" + containsOdd(list))
318 |
319 | println("list containsOdd by funtional:" + list.exists(_ % 2 == 1))
320 | ```
321 |
322 | ### The true power of functional programming
323 |
324 | Besides simplifying code, functional programming is more concerned with Input & Output without side-effects.
325 | Like the Unix pipeline, simple commands can be combined together.
326 | The filter method in List can accept a filter function to return a new List.
327 | If you like the way Unix pipelines commands, you may also like functional programming
328 | This example uses Scala code to simulate the Unix command line "cat file | grep 'warn' | grep '2013' | wc."
329 |
330 | ```
331 | val file = List("warn 2013 msg", "warn 2012 msg", "error 2013 msg", "warn 2013 msg")
332 |
333 | println("cat file | grep 'warn' | grep '2013' | wc : "
334 | + file.filter(_.contains("warn")).filter(_.contains("2013")).size)
335 | ```
336 | ### Word Count
337 | Word Count is a classic use case for Map Reduce. Map Reduce with functional programming is also a wonderful way to implement word count.
338 | The example shows two important functions 'map' and 'reduceLeft' in List.
339 | The map function accepts a translation expression and returns the translated list.
340 | The reduceLeft function accepts a combining expression and returns the combined result.The first argument is the reduced value, and the second argument is the value next.
341 | Try to change reduceLeft(_ + _) into foldLeft(0)(_ + _).foldLeft is more popular than reduceLeft for it can provide a initial value.
342 | Map and ReduceLeft can replace a for-loop expression, making code cleaner.
343 |
344 | ```
345 | val file = List("warn 2013 msg", "warn 2012 msg", "error 2013 msg", "warn 2013 msg")
346 |
347 | def wordcount(str: String): Int = str.split(" ").count("msg" == _)
348 |
349 | val num = file.map(wordcount).reduceLeft(_ + _)
350 |
351 | println("wordcount:" + num)
352 | ```
353 | ### Tail Recursion
354 |
355 | This example shows how to implement foldLeft with Tail RecursionTail Recursion is one type of Recursion, in which a function calls itself as its last expression.
356 | Lists can be pattern matched by '::', the first element returned is head, and the others are the tail.
357 | Tail Recursion can be optimized at compile time. So there's no need to worry about stack overflow.
358 |
359 | ```
360 | val file = List("warn 2013 msg", "warn 2012 msg", "error 2013 msg", "warn 2013 msg")
361 |
362 | def wordcount(str: String): Int = str.split(" ").count("msg" == _)
363 |
364 | def foldLeft(list: List[Int])(init: Int)(f: (Int, Int) => Int): Int = {
365 | list match {
366 | case List() => init
367 | case head :: tail => foldLeft(tail)(f(init, head))(f)
368 | }
369 | }
370 |
371 | val num = foldLeft(file.map(wordcount))(0)(_ + _)
372 |
373 | println("wordcount:" + num)
374 | ```
375 |
376 | ### Powerful For Expression
377 |
378 | Loop expression is feature of imperative programming.So Scala improved it to suit functional programming.
379 | For expressions in Scala can also return a List. With 'yield' in the loop, the value after yield will be appended to the List.
380 | This example replaces the map function with for loop.
381 |
382 | ```
383 | val file = List("warn 2013 msg", "warn 2012 msg", "error 2013 msg", "warn 2013 msg")
384 |
385 | def wordcount(str: String): Int = str.split(" ").count("msg" == _)
386 |
387 | val counts =
388 | for (line <- file)
389 | yield wordcount(line)
390 |
391 | val num = counts.reduceLeft(_ + _)
392 |
393 | println("wordcount:" + num)
394 | ```
395 | ### Option
396 |
397 | NullException is the most common exception in Java. The only way to avoid it is to check null everywhere.Scala provides an Option feature to solve it.
398 | This example creates a getProperty function, which returns Option instead of null.
399 | We don't need to check null, getting a value from Option is enough.
400 | Using pattern matching is a common way to get the value of Option. Use getOrElse() to set a default value when Option is None.
401 | Another important think is that Option has lots of functions in List, so it can work like a list in most of time.
402 | Try to add 'osName.foreach(print _)' at last.
403 |
404 |
405 | ```
406 |
407 | def getProperty(name: String): Option[String] = {
408 | val value = System.getProperty(name)
409 | if (value != null) Some(value) else None
410 | }
411 |
412 | val osName = getProperty("os.name")
413 |
414 | osName match {
415 | case Some(value) => println(value)
416 | case _ => println("none")
417 | }
418 |
419 | println(osName.getOrElse("none"))
420 |
421 |
422 |
423 | ```
424 |
425 |
426 | ### Lazy
427 |
428 | Lazy is lazy initial value. A field with the lazy keyword is only initialized when it is first accessed.
429 | This example is to get the Scala Version Code from Github. It takes a little time because of network latency.
430 | It waste of time that if we get it with latency but we do not use it later.
431 | So we can get it with lazy.
432 |
433 | ```
434 | class ScalaCurrentVersion(val url: String) {
435 | lazy val source= {
436 | println("fetching from url...")
437 | scala.io.Source.fromURL(url).getLines().toList
438 | }
439 | lazy val majorVersion = source.find(_.contains("version.major"))
440 | lazy val minorVersion = source.find(_.contains("version.minor"))
441 | }
442 | val version = new ScalaCurrentVersion("https://raw.github.com/scala/scala/master/build.number")
443 | println("get scala version from " + version.url)
444 | version.majorVersion.foreach(println _)
445 | version.minorVersion.foreach(println _)
446 | ```
447 |
448 |
449 | ## Concurrent
450 |
451 | ### Using Actor
452 |
453 | Actors are one of Scala's concurrent models.Users of Scala earlier than version 2.10 must install [http://akka.io/](Akka).
454 | An Actor is a like a thread instance with a mailbox.
455 | It can be created with system.actorOf: use receive to get a message and ! to send a message.
456 | This example is an EchoServer which can receive messages then print them.
457 |
458 | ```
459 | import akka.actor.{ Actor, ActorSystem, Props }
460 |
461 | val system = ActorSystem()
462 |
463 | class EchoServer extends Actor {
464 | def receive = {
465 | case msg: String => println("echo " + msg)
466 | }
467 | }
468 |
469 | val echoServer = system.actorOf(Props[EchoServer])
470 | echoServer ! "hi"
471 |
472 | system.shutdown
473 | ```
474 |
475 | ### Simplify Actor
476 |
477 | There is a simpler way to define an Actor.
478 | Import the actor function from akka.actor.ActorDSL.
479 | This function accepts an Actor instance, and returns a started Actor.
480 |
481 |
482 | ```
483 | import akka.actor.ActorDSL._
484 | import akka.actor.ActorSystem
485 |
486 | implicit val system = ActorSystem()
487 |
488 | val echoServer = actor(new Act {
489 | become {
490 | case msg => println("echo " + msg)
491 | }
492 | })
493 | echoServer ! "hi"
494 | system.shutdown
495 | ```
496 | ### Actor Implementation
497 | An Actor is more lightweight than a thread. Millions of actors can be generated in Scala. The secret is that an Actor can reuse a thread.
498 | The mapping relationship between an Actor and a Thread is decided by a Dispatcher.
499 | This example creates 4 Actors, and prints its thread name when invoked.
500 | You will find there is no fixed mapping relationship between Actors and Threads.
501 | An Actor can use many threads. And a thread can be used by many Actors.
502 |
503 | ```
504 | import akka.actor.{ Actor, Props, ActorSystem }
505 | import akka.testkit.CallingThreadDispatcher
506 |
507 | implicit val system = ActorSystem()
508 |
509 | class EchoServer(name: String) extends Actor {
510 | def receive = {
511 | case msg => println("server" + name + " echo " + msg +
512 | " by " + Thread.currentThread().getName())
513 | }
514 | }
515 |
516 | val echoServers = (1 to 10).map(x =>
517 | system.actorOf(Props(new EchoServer(x.toString))
518 | .withDispatcher(CallingThreadDispatcher.Id)))
519 | (1 to 10).foreach(msg =>
520 | echoServers(scala.util.Random.nextInt(10)) ! msg.toString)
521 |
522 | system.shutdown
523 | ```
524 |
525 |
526 |
527 | ### Synchronized Return
528 |
529 | Actors are very suitable for long-running operations, like getting resources over a network.
530 | This example creates a Future with the ask function.
531 | In the actor we use 'sender !' to return the value.
532 | Like Option, Future has lots of functions. The result can be printed with a foreach.
533 |
534 |
535 | ```
536 | import akka.actor.ActorDSL._
537 | import akka.pattern.ask
538 |
539 | implicit val ec = scala.concurrent.ExecutionContext.Implicits.global
540 | implicit val system = akka.actor.ActorSystem()
541 |
542 | val versionUrl = "https://raw.github.com/scala/scala/master/starr.number"
543 |
544 | val fromURL = actor(new Act {
545 | become {
546 | case url: String => sender ! scala.io.Source.fromURL(url)
547 | .getLines().mkString("\n")
548 | }
549 | })
550 |
551 | val version = fromURL.ask(versionUrl)(akka.util.Timeout(5 * 1000))
552 | version.foreach(println _)
553 |
554 | system.shutdown
555 | ```
556 |
557 | ### Asynchronous Return
558 |
559 | Asynchronous operations can provide better performance. A Future in Scala is very powerful, it can execute asynchronously.
560 | The Future will call the 'onComplete' function when it is finished.
561 | It can also set a TIMEOUT when specified.
562 |
563 | ```
564 | import akka.actor.ActorDSL._
565 | import akka.pattern.ask
566 |
567 | implicit val ec = scala.concurrent.ExecutionContext.Implicits.global
568 | implicit val system = akka.actor.ActorSystem()
569 |
570 | val versionUrl = "https://raw.github.com/scala/scala/master/starr.number"
571 |
572 | val fromURL = actor(new Act {
573 | become {
574 | case url: String => sender ! scala.io.Source.fromURL(url)
575 | .getLines().mkString("\n")
576 | }
577 | })
578 |
579 | val version = fromURL.ask(versionUrl)(akka.util.Timeout(5 * 1000))
580 | version onComplete {
581 | case msg => println(msg); system.shutdown
582 | }
583 | ```
584 | ### Concurrent Collection
585 |
586 | This example prints the time needed to access several URLs.
587 | If we access them concurrently, the performance can be better.
588 | Try to change the 'urls.map' to 'urls.par.map'.Now, the functions in map will run concurrently.
589 | It's exciting to combine functional and concurrent programming!
590 |
591 | ```
592 | import scala.io.Codec
593 | import java.nio.charset.CodingErrorAction
594 |
595 | implicit val codec = Codec("UTF-8")
596 | codec.onMalformedInput(CodingErrorAction.REPLACE)
597 | codec.onUnmappableCharacter(CodingErrorAction.REPLACE)
598 |
599 | val urls = "http://scala-lang.org" :: "https://github.com/yankay/scala-tour" :: Nil
600 |
601 | def fromURL(url: String) = scala.io.Source.fromURL(url).getLines().mkString("\n")
602 |
603 | val s = System.currentTimeMillis()
604 | time(urls.map(fromURL(_)))
605 | println("time: " + (System.currentTimeMillis - s) + "ms")
606 | ```
607 |
608 | ### Concurrentwordcount
609 | Concurrent Collection support most functions in normal ones.
610 | Here is the word count example from earlier, improved using a parallel collection.
611 | It can use the power of multiple cores without increasing the complexity.
612 |
613 | ```
614 | val file = List("warn 2013 msg", "warn 2012 msg", "error 2013 msg", "warn 2013 msg")
615 |
616 | def wordcount(str: String): Int = str.split(" ").count("msg" == _)
617 |
618 | val num = file.par.map(wordcount).par.reduceLeft(_ + _)
619 |
620 | println("wordcount:" + num)
621 | ```
622 |
623 | ### Remote Actor
624 | Actor is not only a concurrency model, it can also be used for distributed computing.
625 | This example builds an EchoServer using an Actor.
626 | Then it creates a client to access the Akka URL.
627 | The usage is the same as with a normal Actor.
628 |
629 | ```
630 | import akka.actor.{ Actor, ActorSystem, Props }
631 | import com.typesafe.config.ConfigFactory
632 |
633 | implicit val system = akka.actor.ActorSystem("RemoteSystem",
634 | ConfigFactory.load.getConfig("remote"))
635 | class EchoServer extends Actor {
636 | def receive = {
637 | case msg: String => println("echo " + msg)
638 | }
639 | }
640 |
641 | val server = system.actorOf(Props[EchoServer], name = "echoServer")
642 |
643 | val echoClient = system
644 | .actorFor("akka://RemoteSystem@127.0.0.1:2552/user/echoServer")
645 | echoClient ! "Hi Remote"
646 |
647 | system.shutdown
648 |
649 | ```
650 |
651 | ## Practice
652 |
653 | ### Using Java
654 |
655 | Scala can execute Java code very easily. There have already been many examples of this.
656 | Java can also use Scala. This example shows how to use the @BeanProperty Annotation to create a Java Style Bean.
657 | Try to add @BeanProperty before var name. Now the bean contains getter/setter functions.
658 | And the Apache BeanUtils can work correctly.
659 |
660 | ```
661 | import org.apache.commons.beanutils.BeanUtils
662 | import scala.beans.BeanProperty
663 |
664 | class SimpleBean( var name: String) {
665 | }
666 |
667 | val bean = new SimpleBean("foo")
668 |
669 | println(BeanUtils.describe(bean))
670 | ```
671 |
672 | ### Equality
673 | In Scala == is the same as equals function. It's not the same as Java, but it's more reasonable.
674 | This example defines a equals function, and prints the result.
675 | Correctly writing an equals function is difficult. This example has an issue with subclasses.
676 | Try to change 'class' to 'case class', and delete the equals function.
677 | Case Class correctly generates the equals function for us.
678 |
679 | ```
680 | class Person(val name: String) {
681 | override def equals(other: Any) = other match {
682 | case that: Person => name.equals(that.name)
683 | case _ => false
684 | }
685 | }
686 |
687 | println(new Person("Black") == new Person("Black"))
688 | ```
689 |
690 |
691 | ### Extractor
692 | Extractor objects can deconstruct pattern matches.
693 | This example builds an Email Extractor, only the 'unapply function' is needed.
694 | Scala's Regex contains an extractor, which extracts a List. The List elements sequentially match expressions captured in ().
695 | Extractor is very useful. There are 2 cases in this example.
696 | case user :: domain :: Nil extracts a List. case Email(user, domain) extracts an Email.
697 |
698 | ```
699 | object Email {
700 | def apply(user: String, domain: String) = user + "@" + domain
701 |
702 | def unapply(str: String) = new Regex("""(.*)@(.*)""").unapplySeq(str).get match {
703 | case user :: domain :: Nil => Some(user, domain)
704 | case _ => None
705 | }
706 | }
707 |
708 | "user@domain.com" match {
709 | case Email(user, domain) => println(user + "@" + domain)
710 | }
711 | ```
712 |
713 | ### Memory Pattern
714 | Memory Pattern can be used to simplify caching.
715 | In this example, the 'memo function' wraps a function without caching to add the simple cache capability.
716 | In this Fibonacci example, a cache improves performance after the first call.
717 | Try to change fibonacci_(n - 1) + fibonacci_(n - 2) to memo(fibonacci_)(n - 1) + memo(fibonacci_)(n - 2), it can improve more.
718 |
719 | ```
720 | import scala.collection.mutable.WeakHashMap
721 |
722 | val cache = new WeakHashMap[Int, Int]
723 | def memo(f: Int => Int) = (x: Int) => cache.getOrElseUpdate(x, f(x))
724 |
725 | def fibonacci_(in: Int): Int = in match {
726 | case 0 => 0;
727 | case 1 => 1;
728 | case n: Int => fibonacci_(n - 1) + fibonacci_(n - 2)
729 | }
730 |
731 | val fibonacci: Int => Int = memo(fibonacci_)
732 |
733 | val t1 = System.currentTimeMillis()
734 | println(fibonacci(40))
735 | println("it takes " + (System.currentTimeMillis() - t1) + "ms")
736 |
737 | val t2 = System.currentTimeMillis()
738 | println(fibonacci(40))
739 | println("it takes " + (System.currentTimeMillis() - t2) + "ms")
740 | ```
741 | Implicit Conversion
742 | Implicit can be used to define a Conversion function. Types are automatically implicitly converted when needed.
743 | This example converts String to Date automatically. Implicit is the most important feature when implementing a DSL.
744 |
745 | ```
746 | implicit def strToDate(str: String) = new SimpleDateFormat("yyyy-MM-dd").parse(str)
747 |
748 | println("2013-01-01 unix time: " + "2013-01-01".getTime()/1000l)
749 | ```
750 |
751 | ### DSL
752 | DSL is the most powerful tool in Scala. With it Scala code can become more descriptive.
753 | This example generates Json with a DSL. Some of the features look like native features but are created by a DSL.
754 | It's complex to write your own DSL. But it's very easy to use.
755 |
756 | ```
757 | import org.json4s._
758 | import org.json4s.JsonDSL._
759 |
760 | import org.json4s.jackson.JsonMethods._
761 |
762 | case class Twitter(id: Long, text: String, publishedAt: Option[java.util.Date])
763 |
764 | var twitters = Twitter(1, "hello scala", Some(new Date())) :: Twitter(2, "I like scala tour", None) :: Nil
765 |
766 | var json = ("twitters"
767 | -> twitters.map(
768 | t => ("id" -> t.id)
769 | ~ ("text" -> t.text)
770 | ~ ("published_at" -> t.publishedAt.toString())))
771 |
772 | println(pretty(render(json)))
773 | ```
774 |
775 | ### Testing
776 | Scala DSL can make testing even easier.
777 | This example tests a Factorial function. It creates a test case with should/in.
778 | Test cases run concurrently by default.
779 |
780 | ```
781 | import org.specs2.mutable._
782 |
783 | class FactorialSpec extends Specification {
784 | args.report(color = false)
785 |
786 | def factorial(n: Int) = (1 to n).reduce(_ * _)
787 |
788 | "The 'Hello world' string" should {
789 | "factorial 3 must be 6" in {
790 | factorial(3) mustEqual 6
791 | }
792 | "factorial 4 must be 6" in {
793 | factorial(4) must greaterThan(6)
794 | }
795 | }
796 | }
797 | specs2.run(new FactorialSpec)
798 | ```
799 |
800 |
801 | ### Simple Build Tool
802 | SBT is a very popular build tool for Scala.
803 | With its help, you can develop Scala even without installing anything except JRE.
804 | This example is to run this Scala Tour in your computer.
805 |
806 | ```
807 | #Linux/Mac(compile & run):
808 | git clone https://github.com/yankay/scala-tour-zh.git
809 | cd scala-tour-zh
810 | ./sbt/sbt stage
811 | ./target/start
812 |
813 | #Windows(can only compile):
814 | git clone https://github.com/yankay/scala-tour-zh.git
815 | cd scala-tour-zh
816 | sbt\sbt stage
817 | ```
818 |
--------------------------------------------------------------------------------
/src/main/resources/application.conf:
--------------------------------------------------------------------------------
1 | remote {
2 | akka {
3 |
4 | actor {
5 | provider = "akka.remote.RemoteActorRefProvider"
6 | }
7 | remote {
8 | transport = "akka.remote.netty.NettyRemoteTransport"
9 | netty {
10 | hostname = "127.0.0.1"
11 | port = 2552
12 | }
13 | }
14 |
15 | }
16 | }
--------------------------------------------------------------------------------
/src/main/scala/com/yankay/scalaTour/ScalaScriptRunner.scala:
--------------------------------------------------------------------------------
1 | package com.yankay.scalaTour
2 |
3 | import java.io.OutputStream
4 | import java.io.PrintStream
5 | import java.io.PrintWriter
6 | import scala.collection.mutable.StringBuilder
7 | import scala.reflect.io.Directory
8 | import scala.reflect.io.File
9 | import scala.reflect.io.Path
10 | import scala.reflect.io.Path.string2path
11 | import scala.sys.process.Process
12 | import scala.sys.process.ProcessBuilder
13 | import scala.sys.process.ProcessCreation
14 | import scala.sys.process.ProcessIO
15 | import scala.sys.process.ProcessLogger
16 | import scala.tools.nsc.GenericRunnerCommand
17 | import scala.tools.nsc.GenericRunnerSettings
18 | import scala.tools.nsc.Global
19 | import scala.tools.nsc.Settings
20 | import scala.tools.nsc.reporters.ConsoleReporter
21 | import scala.tools.nsc.reporters.Reporter
22 | import scala.tools.nsc.util.HasClassPath
23 | import scala.util.Properties
24 | import scala.tools.nsc.io.Jar
25 |
26 | object ScalaScriptCompiler {
27 | // /** Default name to use for the wrapped script */
28 | val defaultScriptMain = "Main"
29 | //
30 | // /** Pick a main object name from the specified settings */
31 | // def scriptMain(settings: Settings) = settings.script.value match {
32 | // case "" => defaultScriptMain
33 | // case x => x
34 | // }
35 |
36 | val settings: GenericRunnerSettings = {
37 | val command = new GenericRunnerCommand(List(), Console.err println _)
38 | val CP = Properties.propOrEmpty("java.class.path")
39 | val PS = Properties.propOrEmpty("path.separator")
40 | CP.split(PS).foreach((x: String) => {
41 | command.settings.classpath.append(x)
42 | command.settings.bootclasspath.append(x)
43 | })
44 | command.settings.nc.value = true
45 | command.settings.feature.value = true
46 | command.settings.language.appendToValue("reflectiveCalls")
47 | command.settings.language.appendToValue("implicitConversions")
48 | command.settings.nospecialization.value = true
49 | command.settings.nouescape.value = true
50 | command.settings;
51 | }
52 |
53 | def compile(script: String, err: OutputStream): Option[File] = {
54 | val scriptFile = File.makeTemp("scala-script", ".scala")
55 | // save the command to the file
56 |
57 | val illegalCodePattern = """.*(\.sys\.).*""".r
58 |
59 | val strippedScript = {
60 | if (illegalCodePattern.findAllIn(script).toList.isEmpty) script
61 | else """println("No need to import anything")"""
62 | }
63 |
64 | val scriptAll = """
65 | import scala.io.Codec
66 | import java.nio.charset.CodingErrorAction
67 | object Main {
68 | def main(args: Array[String]) {
69 | implicit val codec = Codec("UTF-8")
70 | codec.onMalformedInput(CodingErrorAction.REPLACE)
71 | codec.onUnmappableCharacter(CodingErrorAction.REPLACE)
72 | """ + strippedScript + """
73 | }
74 | }
75 | """
76 | scriptFile.writeAll(scriptAll)
77 | try compile(scriptFile, err)
78 | finally scriptFile.delete() // in case there was a compilation error
79 | }
80 |
81 | def compile(
82 | scriptFile: File, err: OutputStream): Option[File] = {
83 | // def mainClass = scriptMain(settings)
84 | val compiledPath = Directory makeTemp "scalascript"
85 |
86 | // delete the directory after the user code has finished
87 | // sys.addShutdownHook(compiledPath.deleteRecursively())
88 |
89 | settings.outdir.value = compiledPath.path
90 |
91 | /**
92 | * Setting settings.script.value informs the compiler this is not a
93 | * self contained compilation unit.
94 | */
95 | // settings.script.value = mainClass
96 | val reporter = new ConsoleReporter(settings, Console.in, new PrintWriter(err, true))
97 | val compiler = Global(settings, reporter)
98 |
99 | new compiler.Run compile List(scriptFile.path)
100 | if (reporter.hasErrors) None else
101 | try createjar(compiledPath)
102 | finally compiledPath.deleteRecursively;
103 | }
104 |
105 | def createjar(compiledPath: Directory): Option[File] = {
106 | val jarpath = File.makeTemp("scalascript", ".jar")
107 | jarpath.deleteIfExists()
108 | try {
109 | Jar.create(jarpath, compiledPath, defaultScriptMain)
110 | Some(jarpath)
111 | } catch { case _: Exception => jarpath.delete(); None }
112 |
113 | }
114 | }
115 |
116 | object ScalaScriptProcess {
117 | def javaf(): String = {
118 | val head = Properties.propOrEmpty("java.home") + File.separator + "bin" + File.separator + "java"
119 | if (File.separator == "/")
120 | head + ""
121 | else
122 | head + ".exe"
123 | }
124 |
125 | def create(compiledLocation: File, out: OutputStream, err: OutputStream): Option[ScalaScriptProcess] = {
126 | val policy = new java.io.File("policy")
127 | val sep = Properties.propOrEmpty("path.separator")
128 | val CP = Properties.propOrEmpty("java.class.path") + sep + compiledLocation + sep + "./bin" + sep + "."
129 | var args = List("-Djava.security.manager", "-Djava.security.policy=" + policy.getAbsolutePath ,"-cp", CP, "Main")
130 | if (Path(javaf()).exists) {
131 | val outp = new PrintStream(out, true);
132 | val errp = new PrintStream(err, true);
133 | val pl = ProcessLogger(
134 | outp println _,
135 | errp println _)
136 | Some(new ScalaScriptProcess(Process(javaf(), args), pl))
137 | } else None
138 |
139 | }
140 | }
141 |
142 | class ScalaScriptProcess(val builder: ProcessBuilder, val logger: ProcessLogger) {
143 | def run(): Process = {
144 | // println(builder.toString)
145 | builder.run(logger);
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/main/scala/com/yankay/scalaTour/Web.scala:
--------------------------------------------------------------------------------
1 | package com.yankay.scalaTour
2 |
3 | import java.io.ByteArrayOutputStream
4 | import scala.actors.Actor
5 | import scala.actors.TIMEOUT
6 | import scala.reflect.io.File
7 | import scala.util.Properties
8 | import org.eclipse.jetty.server.Handler
9 | import org.eclipse.jetty.server.Server
10 | import org.eclipse.jetty.servlet.ServletContextHandler
11 | import org.eclipse.jetty.servlet.ServletHolder
12 | import org.json4s.jackson.JsonMethods
13 | import javax.servlet.http.HttpServlet
14 | import javax.servlet.http.HttpServletRequest
15 | import javax.servlet.http.HttpServletResponse
16 | import org.eclipse.jetty.server.handler.ResourceHandler
17 | import org.eclipse.jetty.servlet.DefaultServlet
18 | import org.eclipse.jetty.servlet.FilterHolder
19 | import org.eclipse.jetty.server.handler.GzipHandler
20 | import org.eclipse.jetty.servlets.GzipFilter
21 | import java.util.EnumSet
22 | import javax.servlet.DispatcherType
23 | import scala.collection.mutable.WeakHashMap
24 | import scala.util.matching.Regex
25 |
26 | object Web {
27 | val cache = new WeakHashMap[String, RunResponse]
28 |
29 | def handler(): Handler = {
30 | val context = new ServletContextHandler
31 | context.setContextPath("/");
32 | context.setResourceBase("webapp")
33 | context.addServlet(new ServletHolder(new RunServlet()), "/run");
34 | context.addServlet(new ServletHolder(new DefaultServlet()), "/");
35 | val fh = new FilterHolder(new GzipFilter())
36 | fh.setInitParameter("mimeTypes", "text/html,text/css,application/x-javascript,imgage/png")
37 | context.addFilter(fh, "/*", EnumSet.of(DispatcherType.FORWARD,
38 | DispatcherType.INCLUDE,
39 | DispatcherType.REQUEST,
40 | DispatcherType.ASYNC,
41 | DispatcherType.ERROR))
42 | context
43 | }
44 |
45 | def main(args: Array[String]) {
46 |
47 | val server = new Server(Properties.envOrElse("PORT", "8080").toInt);
48 | server.setHandler(handler())
49 | println("Start")
50 | server.start()
51 | }
52 | }
53 |
54 | class RunServlet extends HttpServlet {
55 |
56 | def compileAndRun(code: String): RunResponse = {
57 | // println("code:" + code)
58 | val buffer = new ByteArrayOutputStream();
59 | val file = ScalaScriptCompiler.compile(code, buffer);
60 | val error = new String(buffer.toByteArray()).lines.toList
61 | // println(new String(buffer.toByteArray()))
62 | // println(error);
63 | def replaceErrorCodeNum(src: String): String = {
64 | val reg = new Regex("""main\.scala:([0-9]*): error:.*""")
65 | reg.unapplySeq(src).getOrElse(src) match {
66 | case l :: Nil => "main.scala:" + (l.toString.toInt - 8).toString + src.substring("main.scala:".length() + l.toString.length())
67 | case _ => src
68 | }
69 | }
70 | val errorMsg = error.map(x => x.replaceAll("/tmp/scala-script.*scala", "main.scala")).map(replaceErrorCodeNum)
71 |
72 | file match {
73 | case Some(f) => {
74 | try {
75 | val (outEvents, errEvents, exitValue) = run(f);
76 | new RunResponse(errorMsg.toList, outEvents, errEvents, exitValue)
77 | } finally {
78 | f.deleteIfExists();
79 | }
80 | }
81 | case _ => new RunResponse(errorMsg, List(), List(), -1)
82 | }
83 | }
84 |
85 | def run(file: File): (List[String], List[String], Int) = {
86 | val out = new ByteArrayOutputStream();
87 | val err = new ByteArrayOutputStream();
88 | val proc = ScalaScriptProcess.create(file, out, err);
89 | proc match {
90 | case Some(p) => {
91 | val pid = p.run();
92 | val timeout = new TimeoutActor(pid, 10 * 1000)
93 | timeout.start
94 | val existValue = pid.exitValue()
95 | timeout ! existValue
96 | val outEvents = new String(out.toByteArray()).lines.toList
97 | val errEvents = new String(err.toByteArray()).lines.toList.filterNot(_.startsWith("Picked up JAVA_TOOL_OPTIONS"))
98 | existValue match {
99 | case 0 => (outEvents, errEvents, 0)
100 | case x => (outEvents, errEvents ::: List("exit value is " + x), x)
101 | }
102 |
103 | }
104 | case _ => (List(), List(), -1)
105 | }
106 | }
107 |
108 | class TimeoutActor(proc: scala.sys.process.Process, timeout: Long) extends Actor {
109 | def act() {
110 | receiveWithin(timeout) {
111 | case Int =>
112 | case TIMEOUT => proc.destroy()
113 | }
114 | }
115 | }
116 |
117 | def json(mode: RunResponse): String = {
118 | import org.json4s._
119 | import org.json4s.JsonDSL._
120 | val json = ("Errors" -> mode.errors) ~ ("Events" -> mode.events) ~ ("ErrEvents" -> mode.errEvents)
121 | JsonMethods.pretty(JsonMethods.render(json))
122 | }
123 |
124 | def memo(f: String => RunResponse) = {
125 | (x: String) =>
126 | {
127 | val response = Web.cache.get(x)
128 | response match {
129 | case Some(resp) => resp
130 | case None => {
131 | val resp = f(x)
132 | resp.exitValue match {
133 | case 0 => Web.cache.getOrElseUpdate(x, resp)
134 | case _ => resp
135 | }
136 | }
137 |
138 | }
139 | }
140 | }
141 | override def doGet(req: HttpServletRequest, resp: HttpServletResponse) = {
142 | val code = req.getParameter("code")
143 | if (code == null)
144 | resp.setStatus(404)
145 | else {
146 | var f = memo(compileAndRun)
147 | val model = f(code)
148 | resp.getWriter().print(json(model))
149 | resp.getWriter().flush()
150 | resp.setStatus(200)
151 | }
152 | }
153 |
154 | override def doPost(req: HttpServletRequest, resp: HttpServletResponse) = {
155 | doGet(req, resp)
156 | }
157 | }
158 |
159 | case class RunResponse(errors: List[String], events: List[String], errEvents: List[String], exitValue: Int) {
160 |
161 | }
--------------------------------------------------------------------------------
/src/main/scala/com/yankay/scalaTour/script/TaskMem.scala:
--------------------------------------------------------------------------------
1 | package com.yankay.scalaTour.script
2 |
3 | import java.util.Date
4 | import scala.actors.Actor._
5 | import scala.actors.TIMEOUT
6 | import org.apache.commons.logging.LogFactory
7 |
8 | object CompileStatus extends Enumeration {
9 | type T = Value
10 | val Perpared, Compileing, Compiled = Value
11 | }
12 |
13 | object RunStatus extends Enumeration {
14 | type T = Value
15 | val Perpared, Running, Runned = Value
16 | }
17 |
18 | case class Task(val taskId: Int, val script: String,
19 | val compiledPath: Option[String], val compileLog: List[String], val compileStatus: CompileStatus.T,
20 | val stdOut: List[String], val stdErr: List[String],
21 | val runStart: Option[Date], val runEnd: Option[Date],
22 | val runStatus: RunStatus.T, val runCode: Option[Int],
23 | val expireAt: Date)
24 |
25 | object TaskMem {
26 |
27 | val logger = LogFactory.getLog("TaskMem")
28 |
29 | var tasks = List[Task]()
30 |
31 | def getById(id: Int): Option[Task] = {
32 | TaskMem.synchronized(
33 | tasks.find(_.taskId == id))
34 | }
35 |
36 | def getByScript(script: String): Option[Task] = {
37 | TaskMem.synchronized(
38 | tasks.find(_.script == script))
39 | }
40 |
41 | def putAndReplace(task: Task) = {
42 | logger.debug("putAndReplace " + task)
43 | TaskMem.synchronized(
44 | tasks = task :: tasks.filterNot(_.taskId == task.taskId))
45 | }
46 |
47 | actor {
48 | loop {
49 | reactWithin(60 * 1000) {
50 | case TIMEOUT => removeExpire
51 | }
52 | }
53 | }
54 |
55 | def removeExpire() = {
56 | logger.debug("removeExpire")
57 | TaskMem.synchronized(
58 | tasks = tasks.filter(_.expireAt.after(new Date())))
59 | }
60 |
61 | var id = 0
62 |
63 | def generateId(): Int = {
64 | TaskMem.synchronized {
65 | id = id + 1;
66 | return id
67 | }
68 | }
69 |
70 | }
--------------------------------------------------------------------------------
/src/test/scala/com/yankay/scalaTour/ScalaScriptCompilerTest.scala:
--------------------------------------------------------------------------------
1 | package com.yankay.scalaTour
2 |
3 | import java.util.Date
4 | import java.text.SimpleDateFormat
5 | import scala.util.matching.Regex
6 |
7 | object ScalaScriptCompilerTest {
8 | // this only handles functions with a single argument.
9 |
10 | }
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/test/scala/com/yankay/scalaTour/script/TaskMem.scala:
--------------------------------------------------------------------------------
1 | package com.yankay.scalaTour.script
2 |
3 | import java.util.Date
4 |
5 | object TaskMemTest {
6 | def main(args: Array[String]) {
7 |
8 | }
9 |
10 |
11 |
12 |
13 | }
--------------------------------------------------------------------------------
/src/test/scala/test.scala:
--------------------------------------------------------------------------------
1 |
2 |
3 | import scala.util.matching.Regex
4 |
5 | object test {
6 | def main(args: Array[String]) {
7 | import scala.collection.mutable.WeakHashMap
8 |
9 | val cache = new WeakHashMap[Int, Int]
10 | def memo(f: Int => Int) = (x: Int) => cache.getOrElseUpdate(x, f(x))
11 |
12 | def fibonacci_(in: Int): Int = in match {
13 | case 0 => 0;
14 | case 1 => 1;
15 | case n: Int => memo(fibonacci_)(n - 1) + memo(fibonacci_)(n - 2)
16 | }
17 |
18 | val fibonacci: Int => Int = memo(fibonacci_)
19 |
20 | val t1 = System.currentTimeMillis()
21 | println(fibonacci(40))
22 | println("it takes " + (System.currentTimeMillis() - t1) + "ms")
23 |
24 | val t2 = System.currentTimeMillis()
25 | println(fibonacci(40))
26 | println("it takes " + (System.currentTimeMillis() - t2) + "ms")
27 |
28 | }
29 |
30 | }
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/webapp/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yankay/scala-tour/7a482eea52e1ee429cb5961cb7ee5272227b44b1/webapp/apple-touch-icon.png
--------------------------------------------------------------------------------
/webapp/css/bootstrap-responsive.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Responsive v2.3.1
3 | *
4 | * Copyright 2012 Twitter, Inc
5 | * Licensed under the Apache License v2.0
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Designed and built with all the love in the world @twitter by @mdo and @fat.
9 | */
10 |
11 | .clearfix {
12 | *zoom: 1;
13 | }
14 |
15 | .clearfix:before,
16 | .clearfix:after {
17 | display: table;
18 | line-height: 0;
19 | content: "";
20 | }
21 |
22 | .clearfix:after {
23 | clear: both;
24 | }
25 |
26 | .hide-text {
27 | font: 0/0 a;
28 | color: transparent;
29 | text-shadow: none;
30 | background-color: transparent;
31 | border: 0;
32 | }
33 |
34 | .input-block-level {
35 | display: block;
36 | width: 100%;
37 | min-height: 30px;
38 | -webkit-box-sizing: border-box;
39 | -moz-box-sizing: border-box;
40 | box-sizing: border-box;
41 | }
42 |
43 | @-ms-viewport {
44 | width: device-width;
45 | }
46 |
47 | .hidden {
48 | display: none;
49 | visibility: hidden;
50 | }
51 |
52 | .visible-phone {
53 | display: none !important;
54 | }
55 |
56 | .visible-tablet {
57 | display: none !important;
58 | }
59 |
60 | .hidden-desktop {
61 | display: none !important;
62 | }
63 |
64 | .visible-desktop {
65 | display: inherit !important;
66 | }
67 |
68 | @media (min-width: 768px) and (max-width: 979px) {
69 | .hidden-desktop {
70 | display: inherit !important;
71 | }
72 | .visible-desktop {
73 | display: none !important ;
74 | }
75 | .visible-tablet {
76 | display: inherit !important;
77 | }
78 | .hidden-tablet {
79 | display: none !important;
80 | }
81 | }
82 |
83 | @media (max-width: 767px) {
84 | .hidden-desktop {
85 | display: inherit !important;
86 | }
87 | .visible-desktop {
88 | display: none !important;
89 | }
90 | .visible-phone {
91 | display: inherit !important;
92 | }
93 | .hidden-phone {
94 | display: none !important;
95 | }
96 | }
97 |
98 | .visible-print {
99 | display: none !important;
100 | }
101 |
102 | @media print {
103 | .visible-print {
104 | display: inherit !important;
105 | }
106 | .hidden-print {
107 | display: none !important;
108 | }
109 | }
110 |
111 | @media (min-width: 1200px) {
112 | .row {
113 | margin-left: -30px;
114 | *zoom: 1;
115 | }
116 | .row:before,
117 | .row:after {
118 | display: table;
119 | line-height: 0;
120 | content: "";
121 | }
122 | .row:after {
123 | clear: both;
124 | }
125 | [class*="span"] {
126 | float: left;
127 | min-height: 1px;
128 | margin-left: 30px;
129 | }
130 | .container,
131 | .navbar-static-top .container,
132 | .navbar-fixed-top .container,
133 | .navbar-fixed-bottom .container {
134 | width: 1170px;
135 | }
136 | .span12 {
137 | width: 1170px;
138 | }
139 | .span11 {
140 | width: 1070px;
141 | }
142 | .span10 {
143 | width: 970px;
144 | }
145 | .span9 {
146 | width: 870px;
147 | }
148 | .span8 {
149 | width: 770px;
150 | }
151 | .span7 {
152 | width: 670px;
153 | }
154 | .span6 {
155 | width: 570px;
156 | }
157 | .span5 {
158 | width: 470px;
159 | }
160 | .span4 {
161 | width: 370px;
162 | }
163 | .span3 {
164 | width: 270px;
165 | }
166 | .span2 {
167 | width: 170px;
168 | }
169 | .span1 {
170 | width: 70px;
171 | }
172 | .offset12 {
173 | margin-left: 1230px;
174 | }
175 | .offset11 {
176 | margin-left: 1130px;
177 | }
178 | .offset10 {
179 | margin-left: 1030px;
180 | }
181 | .offset9 {
182 | margin-left: 930px;
183 | }
184 | .offset8 {
185 | margin-left: 830px;
186 | }
187 | .offset7 {
188 | margin-left: 730px;
189 | }
190 | .offset6 {
191 | margin-left: 630px;
192 | }
193 | .offset5 {
194 | margin-left: 530px;
195 | }
196 | .offset4 {
197 | margin-left: 430px;
198 | }
199 | .offset3 {
200 | margin-left: 330px;
201 | }
202 | .offset2 {
203 | margin-left: 230px;
204 | }
205 | .offset1 {
206 | margin-left: 130px;
207 | }
208 | .row-fluid {
209 | width: 100%;
210 | *zoom: 1;
211 | }
212 | .row-fluid:before,
213 | .row-fluid:after {
214 | display: table;
215 | line-height: 0;
216 | content: "";
217 | }
218 | .row-fluid:after {
219 | clear: both;
220 | }
221 | .row-fluid [class*="span"] {
222 | display: block;
223 | float: left;
224 | width: 100%;
225 | min-height: 30px;
226 | margin-left: 2.564102564102564%;
227 | *margin-left: 2.5109110747408616%;
228 | -webkit-box-sizing: border-box;
229 | -moz-box-sizing: border-box;
230 | box-sizing: border-box;
231 | }
232 | .row-fluid [class*="span"]:first-child {
233 | margin-left: 0;
234 | }
235 | .row-fluid .controls-row [class*="span"] + [class*="span"] {
236 | margin-left: 2.564102564102564%;
237 | }
238 | .row-fluid .span12 {
239 | width: 100%;
240 | *width: 99.94680851063829%;
241 | }
242 | .row-fluid .span11 {
243 | width: 91.45299145299145%;
244 | *width: 91.39979996362975%;
245 | }
246 | .row-fluid .span10 {
247 | width: 82.90598290598291%;
248 | *width: 82.8527914166212%;
249 | }
250 | .row-fluid .span9 {
251 | width: 74.35897435897436%;
252 | *width: 74.30578286961266%;
253 | }
254 | .row-fluid .span8 {
255 | width: 65.81196581196582%;
256 | *width: 65.75877432260411%;
257 | }
258 | .row-fluid .span7 {
259 | width: 57.26495726495726%;
260 | *width: 57.21176577559556%;
261 | }
262 | .row-fluid .span6 {
263 | width: 48.717948717948715%;
264 | *width: 48.664757228587014%;
265 | }
266 | .row-fluid .span5 {
267 | width: 40.17094017094017%;
268 | *width: 40.11774868157847%;
269 | }
270 | .row-fluid .span4 {
271 | width: 31.623931623931625%;
272 | *width: 31.570740134569924%;
273 | }
274 | .row-fluid .span3 {
275 | width: 23.076923076923077%;
276 | *width: 23.023731587561375%;
277 | }
278 | .row-fluid .span2 {
279 | width: 14.52991452991453%;
280 | *width: 14.476723040552828%;
281 | }
282 | .row-fluid .span1 {
283 | width: 5.982905982905983%;
284 | *width: 5.929714493544281%;
285 | }
286 | .row-fluid .offset12 {
287 | margin-left: 105.12820512820512%;
288 | *margin-left: 105.02182214948171%;
289 | }
290 | .row-fluid .offset12:first-child {
291 | margin-left: 102.56410256410257%;
292 | *margin-left: 102.45771958537915%;
293 | }
294 | .row-fluid .offset11 {
295 | margin-left: 96.58119658119658%;
296 | *margin-left: 96.47481360247316%;
297 | }
298 | .row-fluid .offset11:first-child {
299 | margin-left: 94.01709401709402%;
300 | *margin-left: 93.91071103837061%;
301 | }
302 | .row-fluid .offset10 {
303 | margin-left: 88.03418803418803%;
304 | *margin-left: 87.92780505546462%;
305 | }
306 | .row-fluid .offset10:first-child {
307 | margin-left: 85.47008547008548%;
308 | *margin-left: 85.36370249136206%;
309 | }
310 | .row-fluid .offset9 {
311 | margin-left: 79.48717948717949%;
312 | *margin-left: 79.38079650845607%;
313 | }
314 | .row-fluid .offset9:first-child {
315 | margin-left: 76.92307692307693%;
316 | *margin-left: 76.81669394435352%;
317 | }
318 | .row-fluid .offset8 {
319 | margin-left: 70.94017094017094%;
320 | *margin-left: 70.83378796144753%;
321 | }
322 | .row-fluid .offset8:first-child {
323 | margin-left: 68.37606837606839%;
324 | *margin-left: 68.26968539734497%;
325 | }
326 | .row-fluid .offset7 {
327 | margin-left: 62.393162393162385%;
328 | *margin-left: 62.28677941443899%;
329 | }
330 | .row-fluid .offset7:first-child {
331 | margin-left: 59.82905982905982%;
332 | *margin-left: 59.72267685033642%;
333 | }
334 | .row-fluid .offset6 {
335 | margin-left: 53.84615384615384%;
336 | *margin-left: 53.739770867430444%;
337 | }
338 | .row-fluid .offset6:first-child {
339 | margin-left: 51.28205128205128%;
340 | *margin-left: 51.175668303327875%;
341 | }
342 | .row-fluid .offset5 {
343 | margin-left: 45.299145299145295%;
344 | *margin-left: 45.1927623204219%;
345 | }
346 | .row-fluid .offset5:first-child {
347 | margin-left: 42.73504273504273%;
348 | *margin-left: 42.62865975631933%;
349 | }
350 | .row-fluid .offset4 {
351 | margin-left: 36.75213675213675%;
352 | *margin-left: 36.645753773413354%;
353 | }
354 | .row-fluid .offset4:first-child {
355 | margin-left: 34.18803418803419%;
356 | *margin-left: 34.081651209310785%;
357 | }
358 | .row-fluid .offset3 {
359 | margin-left: 28.205128205128204%;
360 | *margin-left: 28.0987452264048%;
361 | }
362 | .row-fluid .offset3:first-child {
363 | margin-left: 25.641025641025642%;
364 | *margin-left: 25.53464266230224%;
365 | }
366 | .row-fluid .offset2 {
367 | margin-left: 19.65811965811966%;
368 | *margin-left: 19.551736679396257%;
369 | }
370 | .row-fluid .offset2:first-child {
371 | margin-left: 17.094017094017094%;
372 | *margin-left: 16.98763411529369%;
373 | }
374 | .row-fluid .offset1 {
375 | margin-left: 11.11111111111111%;
376 | *margin-left: 11.004728132387708%;
377 | }
378 | .row-fluid .offset1:first-child {
379 | margin-left: 8.547008547008547%;
380 | *margin-left: 8.440625568285142%;
381 | }
382 | input,
383 | textarea,
384 | .uneditable-input {
385 | margin-left: 0;
386 | }
387 | .controls-row [class*="span"] + [class*="span"] {
388 | margin-left: 30px;
389 | }
390 | input.span12,
391 | textarea.span12,
392 | .uneditable-input.span12 {
393 | width: 1156px;
394 | }
395 | input.span11,
396 | textarea.span11,
397 | .uneditable-input.span11 {
398 | width: 1056px;
399 | }
400 | input.span10,
401 | textarea.span10,
402 | .uneditable-input.span10 {
403 | width: 956px;
404 | }
405 | input.span9,
406 | textarea.span9,
407 | .uneditable-input.span9 {
408 | width: 856px;
409 | }
410 | input.span8,
411 | textarea.span8,
412 | .uneditable-input.span8 {
413 | width: 756px;
414 | }
415 | input.span7,
416 | textarea.span7,
417 | .uneditable-input.span7 {
418 | width: 656px;
419 | }
420 | input.span6,
421 | textarea.span6,
422 | .uneditable-input.span6 {
423 | width: 556px;
424 | }
425 | input.span5,
426 | textarea.span5,
427 | .uneditable-input.span5 {
428 | width: 456px;
429 | }
430 | input.span4,
431 | textarea.span4,
432 | .uneditable-input.span4 {
433 | width: 356px;
434 | }
435 | input.span3,
436 | textarea.span3,
437 | .uneditable-input.span3 {
438 | width: 256px;
439 | }
440 | input.span2,
441 | textarea.span2,
442 | .uneditable-input.span2 {
443 | width: 156px;
444 | }
445 | input.span1,
446 | textarea.span1,
447 | .uneditable-input.span1 {
448 | width: 56px;
449 | }
450 | .thumbnails {
451 | margin-left: -30px;
452 | }
453 | .thumbnails > li {
454 | margin-left: 30px;
455 | }
456 | .row-fluid .thumbnails {
457 | margin-left: 0;
458 | }
459 | }
460 |
461 | @media (min-width: 768px) and (max-width: 979px) {
462 | .row {
463 | margin-left: -20px;
464 | *zoom: 1;
465 | }
466 | .row:before,
467 | .row:after {
468 | display: table;
469 | line-height: 0;
470 | content: "";
471 | }
472 | .row:after {
473 | clear: both;
474 | }
475 | [class*="span"] {
476 | float: left;
477 | min-height: 1px;
478 | margin-left: 20px;
479 | }
480 | .container,
481 | .navbar-static-top .container,
482 | .navbar-fixed-top .container,
483 | .navbar-fixed-bottom .container {
484 | width: 724px;
485 | }
486 | .span12 {
487 | width: 724px;
488 | }
489 | .span11 {
490 | width: 662px;
491 | }
492 | .span10 {
493 | width: 600px;
494 | }
495 | .span9 {
496 | width: 538px;
497 | }
498 | .span8 {
499 | width: 476px;
500 | }
501 | .span7 {
502 | width: 414px;
503 | }
504 | .span6 {
505 | width: 352px;
506 | }
507 | .span5 {
508 | width: 290px;
509 | }
510 | .span4 {
511 | width: 228px;
512 | }
513 | .span3 {
514 | width: 166px;
515 | }
516 | .span2 {
517 | width: 104px;
518 | }
519 | .span1 {
520 | width: 42px;
521 | }
522 | .offset12 {
523 | margin-left: 764px;
524 | }
525 | .offset11 {
526 | margin-left: 702px;
527 | }
528 | .offset10 {
529 | margin-left: 640px;
530 | }
531 | .offset9 {
532 | margin-left: 578px;
533 | }
534 | .offset8 {
535 | margin-left: 516px;
536 | }
537 | .offset7 {
538 | margin-left: 454px;
539 | }
540 | .offset6 {
541 | margin-left: 392px;
542 | }
543 | .offset5 {
544 | margin-left: 330px;
545 | }
546 | .offset4 {
547 | margin-left: 268px;
548 | }
549 | .offset3 {
550 | margin-left: 206px;
551 | }
552 | .offset2 {
553 | margin-left: 144px;
554 | }
555 | .offset1 {
556 | margin-left: 82px;
557 | }
558 | .row-fluid {
559 | width: 100%;
560 | *zoom: 1;
561 | }
562 | .row-fluid:before,
563 | .row-fluid:after {
564 | display: table;
565 | line-height: 0;
566 | content: "";
567 | }
568 | .row-fluid:after {
569 | clear: both;
570 | }
571 | .row-fluid [class*="span"] {
572 | display: block;
573 | float: left;
574 | width: 100%;
575 | min-height: 30px;
576 | margin-left: 2.7624309392265194%;
577 | *margin-left: 2.709239449864817%;
578 | -webkit-box-sizing: border-box;
579 | -moz-box-sizing: border-box;
580 | box-sizing: border-box;
581 | }
582 | .row-fluid [class*="span"]:first-child {
583 | margin-left: 0;
584 | }
585 | .row-fluid .controls-row [class*="span"] + [class*="span"] {
586 | margin-left: 2.7624309392265194%;
587 | }
588 | .row-fluid .span12 {
589 | width: 100%;
590 | *width: 99.94680851063829%;
591 | }
592 | .row-fluid .span11 {
593 | width: 91.43646408839778%;
594 | *width: 91.38327259903608%;
595 | }
596 | .row-fluid .span10 {
597 | width: 82.87292817679558%;
598 | *width: 82.81973668743387%;
599 | }
600 | .row-fluid .span9 {
601 | width: 74.30939226519337%;
602 | *width: 74.25620077583166%;
603 | }
604 | .row-fluid .span8 {
605 | width: 65.74585635359117%;
606 | *width: 65.69266486422946%;
607 | }
608 | .row-fluid .span7 {
609 | width: 57.18232044198895%;
610 | *width: 57.12912895262725%;
611 | }
612 | .row-fluid .span6 {
613 | width: 48.61878453038674%;
614 | *width: 48.56559304102504%;
615 | }
616 | .row-fluid .span5 {
617 | width: 40.05524861878453%;
618 | *width: 40.00205712942283%;
619 | }
620 | .row-fluid .span4 {
621 | width: 31.491712707182323%;
622 | *width: 31.43852121782062%;
623 | }
624 | .row-fluid .span3 {
625 | width: 22.92817679558011%;
626 | *width: 22.87498530621841%;
627 | }
628 | .row-fluid .span2 {
629 | width: 14.3646408839779%;
630 | *width: 14.311449394616199%;
631 | }
632 | .row-fluid .span1 {
633 | width: 5.801104972375691%;
634 | *width: 5.747913483013988%;
635 | }
636 | .row-fluid .offset12 {
637 | margin-left: 105.52486187845304%;
638 | *margin-left: 105.41847889972962%;
639 | }
640 | .row-fluid .offset12:first-child {
641 | margin-left: 102.76243093922652%;
642 | *margin-left: 102.6560479605031%;
643 | }
644 | .row-fluid .offset11 {
645 | margin-left: 96.96132596685082%;
646 | *margin-left: 96.8549429881274%;
647 | }
648 | .row-fluid .offset11:first-child {
649 | margin-left: 94.1988950276243%;
650 | *margin-left: 94.09251204890089%;
651 | }
652 | .row-fluid .offset10 {
653 | margin-left: 88.39779005524862%;
654 | *margin-left: 88.2914070765252%;
655 | }
656 | .row-fluid .offset10:first-child {
657 | margin-left: 85.6353591160221%;
658 | *margin-left: 85.52897613729868%;
659 | }
660 | .row-fluid .offset9 {
661 | margin-left: 79.8342541436464%;
662 | *margin-left: 79.72787116492299%;
663 | }
664 | .row-fluid .offset9:first-child {
665 | margin-left: 77.07182320441989%;
666 | *margin-left: 76.96544022569647%;
667 | }
668 | .row-fluid .offset8 {
669 | margin-left: 71.2707182320442%;
670 | *margin-left: 71.16433525332079%;
671 | }
672 | .row-fluid .offset8:first-child {
673 | margin-left: 68.50828729281768%;
674 | *margin-left: 68.40190431409427%;
675 | }
676 | .row-fluid .offset7 {
677 | margin-left: 62.70718232044199%;
678 | *margin-left: 62.600799341718584%;
679 | }
680 | .row-fluid .offset7:first-child {
681 | margin-left: 59.94475138121547%;
682 | *margin-left: 59.838368402492065%;
683 | }
684 | .row-fluid .offset6 {
685 | margin-left: 54.14364640883978%;
686 | *margin-left: 54.037263430116376%;
687 | }
688 | .row-fluid .offset6:first-child {
689 | margin-left: 51.38121546961326%;
690 | *margin-left: 51.27483249088986%;
691 | }
692 | .row-fluid .offset5 {
693 | margin-left: 45.58011049723757%;
694 | *margin-left: 45.47372751851417%;
695 | }
696 | .row-fluid .offset5:first-child {
697 | margin-left: 42.81767955801105%;
698 | *margin-left: 42.71129657928765%;
699 | }
700 | .row-fluid .offset4 {
701 | margin-left: 37.01657458563536%;
702 | *margin-left: 36.91019160691196%;
703 | }
704 | .row-fluid .offset4:first-child {
705 | margin-left: 34.25414364640884%;
706 | *margin-left: 34.14776066768544%;
707 | }
708 | .row-fluid .offset3 {
709 | margin-left: 28.45303867403315%;
710 | *margin-left: 28.346655695309746%;
711 | }
712 | .row-fluid .offset3:first-child {
713 | margin-left: 25.69060773480663%;
714 | *margin-left: 25.584224756083227%;
715 | }
716 | .row-fluid .offset2 {
717 | margin-left: 19.88950276243094%;
718 | *margin-left: 19.783119783707537%;
719 | }
720 | .row-fluid .offset2:first-child {
721 | margin-left: 17.12707182320442%;
722 | *margin-left: 17.02068884448102%;
723 | }
724 | .row-fluid .offset1 {
725 | margin-left: 11.32596685082873%;
726 | *margin-left: 11.219583872105325%;
727 | }
728 | .row-fluid .offset1:first-child {
729 | margin-left: 8.56353591160221%;
730 | *margin-left: 8.457152932878806%;
731 | }
732 | input,
733 | textarea,
734 | .uneditable-input {
735 | margin-left: 0;
736 | }
737 | .controls-row [class*="span"] + [class*="span"] {
738 | margin-left: 20px;
739 | }
740 | input.span12,
741 | textarea.span12,
742 | .uneditable-input.span12 {
743 | width: 710px;
744 | }
745 | input.span11,
746 | textarea.span11,
747 | .uneditable-input.span11 {
748 | width: 648px;
749 | }
750 | input.span10,
751 | textarea.span10,
752 | .uneditable-input.span10 {
753 | width: 586px;
754 | }
755 | input.span9,
756 | textarea.span9,
757 | .uneditable-input.span9 {
758 | width: 524px;
759 | }
760 | input.span8,
761 | textarea.span8,
762 | .uneditable-input.span8 {
763 | width: 462px;
764 | }
765 | input.span7,
766 | textarea.span7,
767 | .uneditable-input.span7 {
768 | width: 400px;
769 | }
770 | input.span6,
771 | textarea.span6,
772 | .uneditable-input.span6 {
773 | width: 338px;
774 | }
775 | input.span5,
776 | textarea.span5,
777 | .uneditable-input.span5 {
778 | width: 276px;
779 | }
780 | input.span4,
781 | textarea.span4,
782 | .uneditable-input.span4 {
783 | width: 214px;
784 | }
785 | input.span3,
786 | textarea.span3,
787 | .uneditable-input.span3 {
788 | width: 152px;
789 | }
790 | input.span2,
791 | textarea.span2,
792 | .uneditable-input.span2 {
793 | width: 90px;
794 | }
795 | input.span1,
796 | textarea.span1,
797 | .uneditable-input.span1 {
798 | width: 28px;
799 | }
800 | }
801 |
802 | @media (max-width: 767px) {
803 | body {
804 | padding-right: 20px;
805 | padding-left: 20px;
806 | }
807 | .navbar-fixed-top,
808 | .navbar-fixed-bottom,
809 | .navbar-static-top {
810 | margin-right: -20px;
811 | margin-left: -20px;
812 | }
813 | .container-fluid {
814 | padding: 0;
815 | }
816 | .dl-horizontal dt {
817 | float: none;
818 | width: auto;
819 | clear: none;
820 | text-align: left;
821 | }
822 | .dl-horizontal dd {
823 | margin-left: 0;
824 | }
825 | .container {
826 | width: auto;
827 | }
828 | .row-fluid {
829 | width: 100%;
830 | }
831 | .row,
832 | .thumbnails {
833 | margin-left: 0;
834 | }
835 | .thumbnails > li {
836 | float: none;
837 | margin-left: 0;
838 | }
839 | [class*="span"],
840 | .uneditable-input[class*="span"],
841 | .row-fluid [class*="span"] {
842 | display: block;
843 | float: none;
844 | width: 100%;
845 | margin-left: 0;
846 | -webkit-box-sizing: border-box;
847 | -moz-box-sizing: border-box;
848 | box-sizing: border-box;
849 | }
850 | .span12,
851 | .row-fluid .span12 {
852 | width: 100%;
853 | -webkit-box-sizing: border-box;
854 | -moz-box-sizing: border-box;
855 | box-sizing: border-box;
856 | }
857 | .row-fluid [class*="offset"]:first-child {
858 | margin-left: 0;
859 | }
860 | .input-large,
861 | .input-xlarge,
862 | .input-xxlarge,
863 | input[class*="span"],
864 | select[class*="span"],
865 | textarea[class*="span"],
866 | .uneditable-input {
867 | display: block;
868 | width: 100%;
869 | min-height: 30px;
870 | -webkit-box-sizing: border-box;
871 | -moz-box-sizing: border-box;
872 | box-sizing: border-box;
873 | }
874 | .input-prepend input,
875 | .input-append input,
876 | .input-prepend input[class*="span"],
877 | .input-append input[class*="span"] {
878 | display: inline-block;
879 | width: auto;
880 | }
881 | .controls-row [class*="span"] + [class*="span"] {
882 | margin-left: 0;
883 | }
884 | .modal {
885 | position: fixed;
886 | top: 20px;
887 | right: 20px;
888 | left: 20px;
889 | width: auto;
890 | margin: 0;
891 | }
892 | .modal.fade {
893 | top: -100px;
894 | }
895 | .modal.fade.in {
896 | top: 20px;
897 | }
898 | }
899 |
900 | @media (max-width: 480px) {
901 | .nav-collapse {
902 | -webkit-transform: translate3d(0, 0, 0);
903 | }
904 | .page-header h1 small {
905 | display: block;
906 | line-height: 20px;
907 | }
908 | input[type="checkbox"],
909 | input[type="radio"] {
910 | border: 1px solid #ccc;
911 | }
912 | .form-horizontal .control-label {
913 | float: none;
914 | width: auto;
915 | padding-top: 0;
916 | text-align: left;
917 | }
918 | .form-horizontal .controls {
919 | margin-left: 0;
920 | }
921 | .form-horizontal .control-list {
922 | padding-top: 0;
923 | }
924 | .form-horizontal .form-actions {
925 | padding-right: 10px;
926 | padding-left: 10px;
927 | }
928 | .media .pull-left,
929 | .media .pull-right {
930 | display: block;
931 | float: none;
932 | margin-bottom: 10px;
933 | }
934 | .media-object {
935 | margin-right: 0;
936 | margin-left: 0;
937 | }
938 | .modal {
939 | top: 10px;
940 | right: 10px;
941 | left: 10px;
942 | }
943 | .modal-header .close {
944 | padding: 10px;
945 | margin: -10px;
946 | }
947 | .carousel-caption {
948 | position: static;
949 | }
950 | }
951 |
952 | @media (max-width: 979px) {
953 | body {
954 | padding-top: 0;
955 | }
956 | .navbar-fixed-top,
957 | .navbar-fixed-bottom {
958 | position: static;
959 | }
960 | .navbar-fixed-top {
961 | margin-bottom: 20px;
962 | }
963 | .navbar-fixed-bottom {
964 | margin-top: 20px;
965 | }
966 | .navbar-fixed-top .navbar-inner,
967 | .navbar-fixed-bottom .navbar-inner {
968 | padding: 5px;
969 | }
970 | .navbar .container {
971 | width: auto;
972 | padding: 0;
973 | }
974 | .navbar .brand {
975 | padding-right: 10px;
976 | padding-left: 10px;
977 | margin: 0 0 0 -5px;
978 | }
979 | .nav-collapse {
980 | clear: both;
981 | }
982 | .nav-collapse .nav {
983 | float: none;
984 | margin: 0 0 10px;
985 | }
986 | .nav-collapse .nav > li {
987 | float: none;
988 | }
989 | .nav-collapse .nav > li > a {
990 | margin-bottom: 2px;
991 | }
992 | .nav-collapse .nav > .divider-vertical {
993 | display: none;
994 | }
995 | .nav-collapse .nav .nav-header {
996 | color: #777777;
997 | text-shadow: none;
998 | }
999 | .nav-collapse .nav > li > a,
1000 | .nav-collapse .dropdown-menu a {
1001 | padding: 9px 15px;
1002 | font-weight: bold;
1003 | color: #777777;
1004 | -webkit-border-radius: 3px;
1005 | -moz-border-radius: 3px;
1006 | border-radius: 3px;
1007 | }
1008 | .nav-collapse .btn {
1009 | padding: 4px 10px 4px;
1010 | font-weight: normal;
1011 | -webkit-border-radius: 4px;
1012 | -moz-border-radius: 4px;
1013 | border-radius: 4px;
1014 | }
1015 | .nav-collapse .dropdown-menu li + li a {
1016 | margin-bottom: 2px;
1017 | }
1018 | .nav-collapse .nav > li > a:hover,
1019 | .nav-collapse .nav > li > a:focus,
1020 | .nav-collapse .dropdown-menu a:hover,
1021 | .nav-collapse .dropdown-menu a:focus {
1022 | background-color: #f2f2f2;
1023 | }
1024 | .navbar-inverse .nav-collapse .nav > li > a,
1025 | .navbar-inverse .nav-collapse .dropdown-menu a {
1026 | color: #999999;
1027 | }
1028 | .navbar-inverse .nav-collapse .nav > li > a:hover,
1029 | .navbar-inverse .nav-collapse .nav > li > a:focus,
1030 | .navbar-inverse .nav-collapse .dropdown-menu a:hover,
1031 | .navbar-inverse .nav-collapse .dropdown-menu a:focus {
1032 | background-color: #111111;
1033 | }
1034 | .nav-collapse.in .btn-group {
1035 | padding: 0;
1036 | margin-top: 5px;
1037 | }
1038 | .nav-collapse .dropdown-menu {
1039 | position: static;
1040 | top: auto;
1041 | left: auto;
1042 | display: none;
1043 | float: none;
1044 | max-width: none;
1045 | padding: 0;
1046 | margin: 0 15px;
1047 | background-color: transparent;
1048 | border: none;
1049 | -webkit-border-radius: 0;
1050 | -moz-border-radius: 0;
1051 | border-radius: 0;
1052 | -webkit-box-shadow: none;
1053 | -moz-box-shadow: none;
1054 | box-shadow: none;
1055 | }
1056 | .nav-collapse .open > .dropdown-menu {
1057 | display: block;
1058 | }
1059 | .nav-collapse .dropdown-menu:before,
1060 | .nav-collapse .dropdown-menu:after {
1061 | display: none;
1062 | }
1063 | .nav-collapse .dropdown-menu .divider {
1064 | display: none;
1065 | }
1066 | .nav-collapse .nav > li > .dropdown-menu:before,
1067 | .nav-collapse .nav > li > .dropdown-menu:after {
1068 | display: none;
1069 | }
1070 | .nav-collapse .navbar-form,
1071 | .nav-collapse .navbar-search {
1072 | float: none;
1073 | padding: 10px 15px;
1074 | margin: 10px 0;
1075 | border-top: 1px solid #f2f2f2;
1076 | border-bottom: 1px solid #f2f2f2;
1077 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1078 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1079 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1080 | }
1081 | .navbar-inverse .nav-collapse .navbar-form,
1082 | .navbar-inverse .nav-collapse .navbar-search {
1083 | border-top-color: #111111;
1084 | border-bottom-color: #111111;
1085 | }
1086 | .navbar .nav-collapse .nav.pull-right {
1087 | float: none;
1088 | margin-left: 0;
1089 | }
1090 | .nav-collapse,
1091 | .nav-collapse.collapse {
1092 | height: 0;
1093 | overflow: hidden;
1094 | }
1095 | .navbar .btn-navbar {
1096 | display: block;
1097 | }
1098 | .navbar-static .navbar-inner {
1099 | padding-right: 10px;
1100 | padding-left: 10px;
1101 | }
1102 | }
1103 |
1104 | @media (min-width: 980px) {
1105 | .nav-collapse.collapse {
1106 | height: auto !important;
1107 | overflow: visible !important;
1108 | }
1109 | }
1110 |
--------------------------------------------------------------------------------
/webapp/css/bootstrap-responsive.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Responsive v2.3.1
3 | *
4 | * Copyright 2012 Twitter, Inc
5 | * Licensed under the Apache License v2.0
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Designed and built with all the love in the world @twitter by @mdo and @fat.
9 | */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:inherit!important}.hidden-print{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .nav>li>a:focus,.nav-collapse .dropdown-menu a:hover,.nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .nav>li>a:focus,.navbar-inverse .nav-collapse .dropdown-menu a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}}
10 |
--------------------------------------------------------------------------------
/webapp/css/codemirror.css:
--------------------------------------------------------------------------------
1 | /* BASICS */
2 |
3 | .CodeMirror {
4 | /* Set height, width, borders, and global font properties here */
5 | font-family: monospace;
6 | height: 300px;
7 | }
8 | .CodeMirror-scroll {
9 | /* Set scrolling behaviour here */
10 | overflow: auto;
11 | }
12 |
13 | /* PADDING */
14 |
15 | .CodeMirror-lines {
16 | padding: 4px 0; /* Vertical padding around content */
17 | }
18 | .CodeMirror pre {
19 | padding: 0 4px; /* Horizontal padding of content */
20 | }
21 |
22 | .CodeMirror-scrollbar-filler {
23 | background-color: white; /* The little square between H and V scrollbars */
24 | }
25 |
26 | /* GUTTER */
27 |
28 | .CodeMirror-gutters {
29 | border-right: 1px solid #ddd;
30 | background-color: #f7f7f7;
31 | }
32 | .CodeMirror-linenumbers {}
33 | .CodeMirror-linenumber {
34 | padding: 0 3px 0 0px;
35 | min-width: 20px;
36 | text-align: right;
37 | color: #999;
38 | }
39 |
40 | /* CURSOR */
41 |
42 | .CodeMirror div.CodeMirror-cursor {
43 | border-left: 1px solid black;
44 | z-index: 3;
45 | }
46 | /* Shown when moving in bi-directional text */
47 | .CodeMirror div.CodeMirror-secondarycursor {
48 | border-left: 1px solid silver;
49 | }
50 | .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
51 | width: auto;
52 | border: 0;
53 | background: #7e7;
54 | z-index: 1;
55 | }
56 | /* Can style cursor different in overwrite (non-insert) mode */
57 | .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
58 |
59 | .cm-tab { display: inline-block; }
60 |
61 | /* DEFAULT THEME */
62 |
63 | .cm-s-default .cm-keyword {color: #708;}
64 | .cm-s-default .cm-atom {color: #219;}
65 | .cm-s-default .cm-number {color: #164;}
66 | .cm-s-default .cm-def {color: #00f;}
67 | .cm-s-default .cm-variable {color: black;}
68 | .cm-s-default .cm-variable-2 {color: #05a;}
69 | .cm-s-default .cm-variable-3 {color: #085;}
70 | .cm-s-default .cm-property {color: black;}
71 | .cm-s-default .cm-operator {color: black;}
72 | .cm-s-default .cm-comment {color: #a50;}
73 | .cm-s-default .cm-string {color: #a11;}
74 | .cm-s-default .cm-string-2 {color: #f50;}
75 | .cm-s-default .cm-meta {color: #555;}
76 | .cm-s-default .cm-error {color: #f00;}
77 | .cm-s-default .cm-qualifier {color: #555;}
78 | .cm-s-default .cm-builtin {color: #30a;}
79 | .cm-s-default .cm-bracket {color: #997;}
80 | .cm-s-default .cm-tag {color: #170;}
81 | .cm-s-default .cm-attribute {color: #00c;}
82 | .cm-s-default .cm-header {color: blue;}
83 | .cm-s-default .cm-quote {color: #090;}
84 | .cm-s-default .cm-hr {color: #999;}
85 | .cm-s-default .cm-link {color: #00c;}
86 |
87 | .cm-negative {color: #d44;}
88 | .cm-positive {color: #292;}
89 | .cm-header, .cm-strong {font-weight: bold;}
90 | .cm-em {font-style: italic;}
91 | .cm-link {text-decoration: underline;}
92 |
93 | .cm-invalidchar {color: #f00;}
94 |
95 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
96 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
97 |
98 | /* STOP */
99 |
100 | /* The rest of this file contains styles related to the mechanics of
101 | the editor. You probably shouldn't touch them. */
102 |
103 | .CodeMirror {
104 | line-height: 1;
105 | position: relative;
106 | overflow: hidden;
107 | background: white;
108 | color: black;
109 | }
110 |
111 | .CodeMirror-scroll {
112 | /* 30px is the magic margin used to hide the element's real scrollbars */
113 | /* See overflow: hidden in .CodeMirror, and the paddings in .CodeMirror-sizer */
114 | margin-bottom: -30px; margin-right: -30px;
115 | padding-bottom: 30px; padding-right: 30px;
116 | height: 100%;
117 | outline: none; /* Prevent dragging from highlighting the element */
118 | position: relative;
119 | }
120 | .CodeMirror-sizer {
121 | position: relative;
122 | }
123 |
124 | /* The fake, visible scrollbars. Used to force redraw during scrolling
125 | before actuall scrolling happens, thus preventing shaking and
126 | flickering artifacts. */
127 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
128 | position: absolute;
129 | z-index: 6;
130 | display: none;
131 | }
132 | .CodeMirror-vscrollbar {
133 | right: 0; top: 0;
134 | overflow-x: hidden;
135 | overflow-y: scroll;
136 | }
137 | .CodeMirror-hscrollbar {
138 | bottom: 0; left: 0;
139 | overflow-y: hidden;
140 | overflow-x: scroll;
141 | }
142 | .CodeMirror-scrollbar-filler {
143 | right: 0; bottom: 0;
144 | z-index: 6;
145 | }
146 |
147 | .CodeMirror-gutters {
148 | position: absolute; left: 0; top: 0;
149 | height: 100%;
150 | padding-bottom: 30px;
151 | z-index: 3;
152 | }
153 | .CodeMirror-gutter {
154 | height: 100%;
155 | padding-bottom: 30px;
156 | margin-bottom: -32px;
157 | display: inline-block;
158 | /* Hack to make IE7 behave */
159 | *zoom:1;
160 | *display:inline;
161 | }
162 | .CodeMirror-gutter-elt {
163 | position: absolute;
164 | cursor: default;
165 | z-index: 4;
166 | }
167 |
168 | .CodeMirror-lines {
169 | cursor: text;
170 | }
171 | .CodeMirror pre {
172 | /* Reset some styles that the rest of the page might have set */
173 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
174 | border-width: 0;
175 | background: transparent;
176 | font-family: inherit;
177 | font-size: inherit;
178 | margin: 0;
179 | white-space: pre;
180 | word-wrap: normal;
181 | line-height: inherit;
182 | color: inherit;
183 | z-index: 2;
184 | position: relative;
185 | overflow: visible;
186 | }
187 | .CodeMirror-wrap pre {
188 | word-wrap: break-word;
189 | white-space: pre-wrap;
190 | word-break: normal;
191 | }
192 | .CodeMirror-linebackground {
193 | position: absolute;
194 | left: 0; right: 0; top: 0; bottom: 0;
195 | z-index: 0;
196 | }
197 |
198 | .CodeMirror-linewidget {
199 | position: relative;
200 | z-index: 2;
201 | overflow: auto;
202 | }
203 |
204 | .CodeMirror-widget {
205 | display: inline-block;
206 | }
207 |
208 | .CodeMirror-wrap .CodeMirror-scroll {
209 | overflow-x: hidden;
210 | }
211 |
212 | .CodeMirror-measure {
213 | position: absolute;
214 | width: 100%; height: 0px;
215 | overflow: hidden;
216 | visibility: hidden;
217 | }
218 | .CodeMirror-measure pre { position: static; }
219 |
220 | .CodeMirror div.CodeMirror-cursor {
221 | position: absolute;
222 | visibility: hidden;
223 | border-right: none;
224 | width: 0;
225 | }
226 | .CodeMirror-focused div.CodeMirror-cursor {
227 | visibility: visible;
228 | }
229 |
230 | .CodeMirror-selected { background: #d9d9d9; }
231 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
232 |
233 | .cm-searching {
234 | background: #ffa;
235 | background: rgba(255, 255, 0, .4);
236 | }
237 |
238 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */
239 | .CodeMirror span { *vertical-align: text-bottom; }
240 |
241 | @media print {
242 | /* Hide the cursor when printing */
243 | .CodeMirror div.CodeMirror-cursor {
244 | visibility: hidden;
245 | }
246 | }
247 |
--------------------------------------------------------------------------------
/webapp/css/elegant.css:
--------------------------------------------------------------------------------
1 | .cm-s-elegant span.cm-number, .cm-s-elegant span.cm-string, .cm-s-elegant span.cm-atom {color: #762;}
2 | .cm-s-elegant span.cm-comment {color: #262; font-style: italic; line-height: 1em;}
3 | .cm-s-elegant span.cm-meta {color: #555; font-style: italic; line-height: 1em;}
4 | .cm-s-elegant span.cm-variable {color: black;}
5 | .cm-s-elegant span.cm-variable-2 {color: #b11;}
6 | .cm-s-elegant span.cm-qualifier {color: #555;}
7 | .cm-s-elegant span.cm-keyword {color: #730;}
8 | .cm-s-elegant span.cm-builtin {color: #30a;}
9 | .cm-s-elegant span.cm-error {background-color: #fdd;}
10 | .cm-s-elegant span.cm-link {color: #762;}
11 |
--------------------------------------------------------------------------------
/webapp/css/impress-demo.css:
--------------------------------------------------------------------------------
1 | /*
2 | So you like the style of impress.js demo?
3 | Or maybe you are just curious how it was done?
4 |
5 | You couldn't find a better place to find out!
6 |
7 | Welcome to the stylesheet impress.js demo presentation.
8 |
9 | Please remember that it is not meant to be a part of impress.js and is
10 | not required by impress.js.
11 | I expect that anyone creating a presentation for impress.js would create
12 | their own set of styles.
13 |
14 | But feel free to read through it and learn how to get the most of what
15 | impress.js provides.
16 |
17 | And let me be your guide.
18 |
19 | Shall we begin?
20 | */
21 |
22 |
23 | /*
24 | We start with a good ol' reset.
25 | That's the one by Eric Meyer http://meyerweb.com/eric/tools/css/reset/
26 |
27 | You can probably argue if it is needed here, or not, but for sure it
28 | doesn't do any harm and gives us a fresh start.
29 | */
30 |
31 | html, body, div, span, applet, object, iframe,
32 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
33 | a, abbr, acronym, address, big, cite, code,
34 | del, dfn, em, img, ins, kbd, q, s, samp,
35 | small, strike, strong, sub, sup, tt, var,
36 | b, u, i, center,
37 | dl, dt, dd, ol, ul, li,
38 | fieldset, form, label, legend,
39 | table, caption, tbody, tfoot, thead, tr, th, td,
40 | article, aside, canvas, details, embed,
41 | figure, figcaption, footer, header, hgroup,
42 | menu, nav, output, ruby, section, summary,
43 | time, mark, audio, video {
44 | margin: 0;
45 | padding: 0;
46 | border: 0;
47 | font-size: 100%;
48 | font: inherit;
49 | vertical-align: baseline;
50 | }
51 |
52 | /* HTML5 display-role reset for older browsers */
53 | article, aside, details, figcaption, figure,
54 | footer, header, hgroup, menu, nav, section {
55 | display: block;
56 | }
57 | body {
58 | line-height: 1;
59 | }
60 | ol, ul {
61 | list-style: none;
62 | }
63 | blockquote, q {
64 | quotes: none;
65 | }
66 | blockquote:before, blockquote:after,
67 | q:before, q:after {
68 | content: '';
69 | content: none;
70 | }
71 |
72 | table {
73 | border-collapse: collapse;
74 | border-spacing: 0;
75 | }
76 |
77 | /*
78 | Now here is when interesting things start to appear.
79 |
80 | We set up styles with default font and nice gradient in the background.
81 | And yes, there is a lot of repetition there because of -prefixes but we don't
82 | want to leave anybody behind.
83 | */
84 | body {
85 | font-family: 'PT Sans', sans-serif;
86 | min-height: 640px;
87 |
88 | background: rgb(215, 215, 215);
89 | background: -webkit-gradient(radial, 50% 50%, 0, 50% 50%, 500, from(rgb(240, 240, 240)), to(rgb(190, 190, 190)));
90 | background: -webkit-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
91 | background: -moz-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
92 | background: -ms-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
93 | background: -o-radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
94 | background: radial-gradient(rgb(240, 240, 240), rgb(190, 190, 190));
95 | }
96 |
97 | /*
98 | Now let's bring some text styles back ...
99 | */
100 | b, strong { font-weight: bold }
101 | i, em { font-style: italic }
102 |
103 | /*
104 | ... and give links a nice look.
105 | */
106 | a {
107 | color: inherit;
108 | text-decoration: none;
109 | padding: 0 0.1em;
110 | background: rgba(255,255,255,0.5);
111 | text-shadow: -1px -1px 2px rgba(100,100,100,0.9);
112 | border-radius: 0.2em;
113 |
114 | -webkit-transition: 0.5s;
115 | -moz-transition: 0.5s;
116 | -ms-transition: 0.5s;
117 | -o-transition: 0.5s;
118 | transition: 0.5s;
119 | }
120 |
121 | a:hover,
122 | a:focus {
123 | background: rgba(255,255,255,1);
124 | text-shadow: -1px -1px 2px rgba(100,100,100,0.5);
125 | }
126 |
127 | /*
128 | Because the main point behind the impress.js demo is to demo impress.js
129 | we display a fallback message for users with browsers that don't support
130 | all the features required by it.
131 |
132 | All of the content will be still fully accessible for them, but I want
133 | them to know that they are missing something - that's what the demo is
134 | about, isn't it?
135 |
136 | And then we hide the message, when support is detected in the browser.
137 | */
138 |
139 | .fallback-message {
140 | font-family: sans-serif;
141 | line-height: 1.3;
142 |
143 | width: 780px;
144 | padding: 10px 10px 0;
145 | margin: 20px auto;
146 |
147 | border: 1px solid #E4C652;
148 | border-radius: 10px;
149 | background: #EEDC94;
150 | }
151 |
152 | .fallback-message p {
153 | margin-bottom: 10px;
154 | }
155 |
156 | .impress-supported .fallback-message {
157 | display: none;
158 | }
159 |
160 | .impress-not-supported #impress {
161 | display: none;
162 | }
163 | .impress-not-supported .hint {
164 | display: none;
165 | }
166 |
167 |
168 | /*
169 | Now let's style the presentation steps.
170 |
171 | We start with basics to make sure it displays correctly in everywhere ...
172 | */
173 |
174 | .step {
175 | position: relative;
176 | width: 1024px;
177 | margin: 20px auto;
178 |
179 | -webkit-box-sizing: border-box;
180 | -moz-box-sizing: border-box;
181 | -ms-box-sizing: border-box;
182 | -o-box-sizing: border-box;
183 | box-sizing: border-box;
184 |
185 | font-family: 'PT Serif', georgia, serif;
186 | /*
187 | padding: 40px;
188 | font-size: 48px;
189 | line-height: 1.5;
190 | */
191 | }
192 |
193 | /*
194 | ... and we enhance the styles for impress.js.
195 |
196 | Basically we remove the margin and make inactive steps a little bit transparent.
197 | */
198 | .impress-enabled .step {
199 | margin: 20px 0 0 0;
200 | opacity: 0;
201 |
202 | -webkit-transition: opacity 1s;
203 | -moz-transition: opacity 1s;
204 | -ms-transition: opacity 1s;
205 | -o-transition: opacity 1s;
206 | transition: opacity 1s;
207 | }
208 |
209 | .impress-enabled .step.active { opacity: 1 }
210 |
211 | /*
212 | These 'slide' step styles were heavily inspired by HTML5 Slides:
213 | http://html5slides.googlecode.com/svn/trunk/styles.css
214 |
215 | ;)
216 |
217 | They cover everything what you see on first three steps of the demo.
218 | */
219 | .slide {
220 | display: block;
221 |
222 | width: 1024px;
223 | height: 600px;
224 | color: rgb(102, 102, 102);
225 | text-shadow: 0 2px 2px rgba(0, 0, 0, .1);
226 |
227 | font-family: 'Open Sans', Arial, sans-serif;
228 |
229 | /*
230 | padding: 40px 60px;
231 |
232 | font-size: 30px;
233 | line-height: 36px;
234 | letter-spacing: -1px;*/
235 | }
236 |
237 | .slide_background{
238 | background-color: white;
239 | border: 1px solid rgba(0, 0, 0, .3);
240 | border-radius: 10px;
241 | box-shadow: 0 2px 6px rgba(0, 0, 0, .1);
242 | }
243 |
244 | .slide q {
245 | display: block;
246 | line-height: 72px;
247 |
248 | margin-top: 100px;
249 | /*
250 | font-size: 50px;
251 | */
252 | }
253 |
254 | .slide q strong {
255 | white-space: nowrap;
256 | }
257 |
258 | /*
259 | And now we start to style each step separately.
260 |
261 | I agree that this may be not the most efficient, object-oriented and
262 | scalable way of styling, but most of steps have quite a custom look
263 | and typography tricks here and there, so they had to be styled separately.
264 |
265 | First is the title step with a big (no room for padding) and some
266 | 3D positioning along Z axis.
267 | */
268 |
269 | #title {
270 | padding: 0;
271 | }
272 |
273 | #title .try {
274 | font-size: 64px;
275 | position: absolute;
276 | top: -0.5em;
277 | left: 1.5em;
278 |
279 | -webkit-transform: translateZ(20px);
280 | -moz-transform: translateZ(20px);
281 | -ms-transform: translateZ(20px);
282 | -o-transform: translateZ(20px);
283 | transform: translateZ(20px);
284 | }
285 |
286 | #title h1 {
287 | font-size: 190px;
288 |
289 | -webkit-transform: translateZ(50px);
290 | -moz-transform: translateZ(50px);
291 | -ms-transform: translateZ(50px);
292 | -o-transform: translateZ(50px);
293 | transform: translateZ(50px);
294 | }
295 |
296 | #title .footnote {
297 | font-size: 32px;
298 | }
299 |
300 | /*
301 | Second step is nothing special, just a text with a link, so it doesn't need
302 | any special styling.
303 |
304 | Let's move to 'big thoughts' with centered text and custom font sizes.
305 | */
306 | #big {
307 | width: 600px;
308 | text-align: center;
309 | font-size: 60px;
310 | line-height: 1;
311 | }
312 |
313 | #big b {
314 | display: block;
315 | font-size: 250px;
316 | line-height: 250px;
317 | }
318 |
319 | #big .thoughts {
320 | font-size: 90px;
321 | line-height: 150px;
322 | }
323 |
324 | /*
325 | 'Tiny ideas' just need some tiny styling.
326 | */
327 | #tiny {
328 | width: 500px;
329 | text-align: center;
330 | }
331 |
332 | /*
333 | This step has some animated text ...
334 | */
335 | #ing { width: 500px }
336 |
337 | /*
338 | ... so we define display to `inline-block` to enable transforms and
339 | transition duration to 0.5s ...
340 | */
341 | #ing b {
342 | display: inline-block;
343 | -webkit-transition: 0.5s;
344 | -moz-transition: 0.5s;
345 | -ms-transition: 0.5s;
346 | -o-transition: 0.5s;
347 | transition: 0.5s;
348 | }
349 |
350 | /*
351 | ... and we want 'positioning` word to move up a bit when the step gets
352 | `present` class ...
353 | */
354 | #ing.present .positioning {
355 | -webkit-transform: translateY(-10px);
356 | -moz-transform: translateY(-10px);
357 | -ms-transform: translateY(-10px);
358 | -o-transform: translateY(-10px);
359 | transform: translateY(-10px);
360 | }
361 |
362 | /*
363 | ... 'rotating' to rotate a quarter of a second later ...
364 | */
365 | #ing.present .rotating {
366 | -webkit-transform: rotate(-10deg);
367 | -moz-transform: rotate(-10deg);
368 | -ms-transform: rotate(-10deg);
369 | -o-transform: rotate(-10deg);
370 | transform: rotate(-10deg);
371 |
372 | -webkit-transition-delay: 0.25s;
373 | -moz-transition-delay: 0.25s;
374 | -ms-transition-delay: 0.25s;
375 | -o-transition-delay: 0.25s;
376 | transition-delay: 0.25s;
377 | }
378 |
379 | /*
380 | ... and 'scaling' to scale down after another quarter of a second.
381 | */
382 | #ing.present .scaling {
383 | -webkit-transform: scale(0.7);
384 | -moz-transform: scale(0.7);
385 | -ms-transform: scale(0.7);
386 | -o-transform: scale(0.7);
387 | transform: scale(0.7);
388 |
389 | -webkit-transition-delay: 0.5s;
390 | -moz-transition-delay: 0.5s;
391 | -ms-transition-delay: 0.5s;
392 | -o-transition-delay: 0.5s;
393 | transition-delay: 0.5s;
394 | }
395 |
396 | /*
397 | The 'imagination' step is again some boring font-sizing.
398 | */
399 |
400 | #imagination {
401 | width: 600px;
402 | }
403 |
404 | #imagination .imagination {
405 | font-size: 78px;
406 | }
407 |
408 | /*
409 | There is nothing really special about 'use the source, Luke' step, too,
410 | except maybe of the Yoda background.
411 |
412 | As you can see below I've 'hard-coded' it in data URL.
413 | That's not the best way to serve images, but because that's just this one
414 | I decided it will be OK to have it this way.
415 |
416 | Just make sure you don't blindly copy this approach.
417 | */
418 | #source {
419 | width: 700px;
420 | padding-bottom: 300px;
421 |
422 | /* Yoda Icon :: Pixel Art from Star Wars http://www.pixeljoint.com/pixelart/1423.htm */
423 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAEYCAMAAACwUBm+AAAAAXNSR0IArs4c6QAAAKtQTFRFsAAAvbWSLUUrLEQqY1s8UYJMqJ1vNTEgOiIdIzYhjIFVLhsXZ6lgSEIsP2U8JhcCVzMsSXZEgXdOO145XJdWOl03LzAYMk4vSXNExr+hwcuxRTs1Qmk+RW9Am49eFRANQz4pUoNMQWc+OSMDTz0wLBsCNVMxa2NBOyUDUoNNSnlEWo9VRGxAVzYFl6tXCggHbLNmMUIcHhwTXkk5f3VNRT8wUT8xAAAACQocRBWFFwAAAAF0Uk5TAEDm2GYAAAPCSURBVHja7d3JctNAFIZRMwRCCGEmzPM8z/D+T8bu/ptbXXJFdij5fMt2Wuo+2UgqxVmtttq5WVotLzBgwIABAwYMGDCn0qVqbo69psPqVpWx+1XG5iaavF8wYMCAAQMGDBgwi4DJ6Y6qkxB1HNlcN3a92gbR5P2CAQMGDBgwYMCAWSxMlrU+UY5yu2l9okfV4bAxUVbf7TJnAwMGDBgwYMCAAbMLMHeqbGR82Zy+VR1Ht81nVca6R+UdTLaU24Ruzd3qM/e4yjnAgAEDBgwYMGDA7AJMd1l/3NRdVGcj3eX/2WEhCmDGxnM7yqygu8XIPjJj8iN/MGDAgAEDBgwYMAuDGb8q0RGlLCHLv1t9qDKWn3vdNHVuEI6HPaxO9Jo3GDBgwIABAwYMmIXBdC9ShGgMk+XnkXUeuGcsP/e1+lhNnZsL/G5Vs3OAAQMGDBgwYMCAWSxMR3SzOmraG5atdy9wZKzb+vg16qyqe2FltbnAgAEDBgwYMGDALAxmTJSuN3WA76rnVca6GTnemGN1WoEBAwYMGDBgwIBZGMxUomy4+xO899V4LAg5Xnc2MGDAgAEDBgwYMGA218Wq+2K1LDqvY9xZu8zN8fICdM6btYABAwYMGDBgwIABMzfH0+pGU5afze2tXebmeAfVz+p8BQYMGDBgwIABAwbMPBzZ+oWmfJrln1273FhkbHzee9WWbw7AgAEDBgwYMGDALAKm43hcdctKgblcPamOhuXnXlY5Xs6bsW4FGyQCAwYMGDBgwIABswiYMceZKgvMo+h8mrHLTdn676rj+FEFoTtHd8MwOxEYMGDAgAEDBgyYRcBM5UhXqiymW3R3c9ARhWO/OmjqfjVZy+xEYMCAAQMGDBgwYBYG073OnCV0RFNhMhaOa9WfKmOB6XjHMN1tQmaAAQMGDBgwYMCA2VWY7vXjz1U4croAzgPztwIDBgwYMGDAgAEDZhswh035NBw59Dww3RgYMGDAgAEDBgwYMJuD6f4tXT7NUqfCdBvZLkxXdgQGDBgwYMCAAQNmt2DGj8WzwAfV/w7T/aq7mxwwYMCAAQMGDBgwuwqTOo7uTwTngflSzQ3TdaJvAwEDBgwYMGDAgAED5gSvgbyo5oHZ4Pc+gwEDBgwYMGDAgAEzhOm+5G0qTGaAAQMGDBgwYMCAAXNaMOcnls3tNwWm+zRzp54NDBgwYMCAAQMGDJh5YNL36k1TLuGvVq+qnKMbS5n7tulT9asCAwYMGDBgwIABA2ZumKuztLnjgQEDBgwYMGDAgNl5mH/4/ltKA6vBNAAAAABJRU5ErkJggg==);
424 | background-position: bottom right;
425 | background-repeat: no-repeat;
426 | }
427 |
428 | #source q {
429 | font-size: 60px;
430 | }
431 |
432 | /*
433 | And the "it's in 3D" step again brings some 3D typography - just for fun.
434 |
435 | Because we want to position elements in 3D we set transform-style to
436 | `preserve-3d` on the paragraph.
437 | It is not needed by webkit browsers, but it is in Firefox. It's hard to say
438 | which behaviour is correct as 3D transforms spec is not very clear about it.
439 | */
440 | #its-in-3d p {
441 | -webkit-transform-style: preserve-3d;
442 | -moz-transform-style: preserve-3d; /* Y U need this Firefox?! */
443 | -ms-transform-style: preserve-3d;
444 | -o-transform-style: preserve-3d;
445 | transform-style: preserve-3d;
446 | }
447 |
448 | /*
449 | Below we position each word separately along Z axis and we want it to transition
450 | to default position in 0.5s when the step gets `present` class.
451 |
452 | Quite a simple idea, but lot's of styles and prefixes.
453 | */
454 | #its-in-3d span,
455 | #its-in-3d b {
456 | display: inline-block;
457 | -webkit-transform: translateZ(40px);
458 | -moz-transform: translateZ(40px);
459 | -ms-transform: translateZ(40px);
460 | -o-transform: translateZ(40px);
461 | transform: translateZ(40px);
462 |
463 | -webkit-transition: 0.5s;
464 | -moz-transition: 0.5s;
465 | -ms-transition: 0.5s;
466 | -o-transition: 0.5s;
467 | transition: 0.5s;
468 | }
469 |
470 | #its-in-3d .have {
471 | -webkit-transform: translateZ(-40px);
472 | -moz-transform: translateZ(-40px);
473 | -ms-transform: translateZ(-40px);
474 | -o-transform: translateZ(-40px);
475 | transform: translateZ(-40px);
476 | }
477 |
478 | #its-in-3d .you {
479 | -webkit-transform: translateZ(20px);
480 | -moz-transform: translateZ(20px);
481 | -ms-transform: translateZ(20px);
482 | -o-transform: translateZ(20px);
483 | transform: translateZ(20px);
484 | }
485 |
486 | #its-in-3d .noticed {
487 | -webkit-transform: translateZ(-40px);
488 | -moz-transform: translateZ(-40px);
489 | -ms-transform: translateZ(-40px);
490 | -o-transform: translateZ(-40px);
491 | transform: translateZ(-40px);
492 | }
493 |
494 | #its-in-3d .its {
495 | -webkit-transform: translateZ(60px);
496 | -moz-transform: translateZ(60px);
497 | -ms-transform: translateZ(60px);
498 | -o-transform: translateZ(60px);
499 | transform: translateZ(60px);
500 | }
501 |
502 | #its-in-3d .in {
503 | -webkit-transform: translateZ(-10px);
504 | -moz-transform: translateZ(-10px);
505 | -ms-transform: translateZ(-10px);
506 | -o-transform: translateZ(-10px);
507 | transform: translateZ(-10px);
508 | }
509 |
510 | #its-in-3d .footnote {
511 | font-size: 32px;
512 |
513 | -webkit-transform: translateZ(-10px);
514 | -moz-transform: translateZ(-10px);
515 | -ms-transform: translateZ(-10px);
516 | -o-transform: translateZ(-10px);
517 | transform: translateZ(-10px);
518 | }
519 |
520 | #its-in-3d.present span,
521 | #its-in-3d.present b {
522 | -webkit-transform: translateZ(0px);
523 | -moz-transform: translateZ(0px);
524 | -ms-transform: translateZ(0px);
525 | -o-transform: translateZ(0px);
526 | transform: translateZ(0px);
527 | }
528 |
529 | /*
530 | The last step is an overview.
531 | There is no content in it, so we make sure it's not visible because we want
532 | to be able to click on other steps.
533 |
534 | */
535 | #overview { display: none }
536 |
537 | /*
538 | We also make other steps visible and give them a pointer cursor using the
539 | `impress-on-` class.
540 | */
541 | .impress-on-overview .step {
542 | opacity: 1;
543 | cursor: pointer;
544 | }
545 |
546 |
547 | /*
548 | Now, when we have all the steps styled let's give users a hint how to navigate
549 | around the presentation.
550 |
551 | The best way to do this would be to use JavaScript, show a delayed hint for a
552 | first time users, then hide it and store a status in cookie or localStorage...
553 |
554 | But I wanted to have some CSS fun and avoid additional scripting...
555 |
556 | Let me explain it first, so maybe the transition magic will be more readable
557 | when you read the code.
558 |
559 | First of all I wanted the hint to appear only when user is idle for a while.
560 | You can't detect the 'idle' state in CSS, but I delayed a appearing of the
561 | hint by 5s using transition-delay.
562 |
563 | You also can't detect in CSS if the user is a first-time visitor, so I had to
564 | make an assumption that I'll only show the hint on the first step. And when
565 | the step is changed hide the hint, because I can assume that user already
566 | knows how to navigate.
567 |
568 | To summarize it - hint is shown when the user is on the first step for longer
569 | than 5 seconds.
570 |
571 | The other problem I had was caused by the fact that I wanted the hint to fade
572 | in and out. It can be easily achieved by transitioning the opacity property.
573 | But that also meant that the hint was always on the screen, even if totally
574 | transparent. It covered part of the screen and you couldn't correctly clicked
575 | through it.
576 | Unfortunately you cannot transition between display `block` and `none` in pure
577 | CSS, so I needed a way to not only fade out the hint but also move it out of
578 | the screen.
579 |
580 | I solved this problem by positioning the hint below the bottom of the screen
581 | with CSS transform and moving it up to show it. But I also didn't want this move
582 | to be visible. I wanted the hint only to fade in and out visually, so I delayed
583 | the fade in transition, so it starts when the hint is already in its correct
584 | position on the screen.
585 |
586 | I know, it sounds complicated ... maybe it would be easier with the code?
587 | */
588 |
589 | .hint {
590 | /*
591 | We hide the hint until presentation is started and from browsers not supporting
592 | impress.js, as they will have a linear scrollable view ...
593 | */
594 | display: none;
595 |
596 | /*
597 | ... and give it some fixed position and nice styles.
598 | */
599 | position: fixed;
600 | left: 0;
601 | right: 0;
602 | bottom: 200px;
603 |
604 | background: rgba(0,0,0,0.5);
605 | color: #EEE;
606 | text-align: center;
607 |
608 | font-size: 50px;
609 | padding: 20px;
610 |
611 | z-index: 100;
612 |
613 | /*
614 | By default we don't want the hint to be visible, so we make it transparent ...
615 | */
616 | opacity: 0;
617 |
618 | /*
619 | ... and position it below the bottom of the screen (relative to it's fixed position)
620 | */
621 | -webkit-transform: translateY(400px);
622 | -moz-transform: translateY(400px);
623 | -ms-transform: translateY(400px);
624 | -o-transform: translateY(400px);
625 | transform: translateY(400px);
626 |
627 | /*
628 | Now let's imagine that the hint is visible and we want to fade it out and move out
629 | of the screen.
630 |
631 | So we define the transition on the opacity property with 1s duration and another
632 | transition on transform property delayed by 1s so it will happen after the fade out
633 | on opacity finished.
634 |
635 | This way user will not see the hint moving down.
636 | */
637 | -webkit-transition: opacity 1s, -webkit-transform 0.5s 1s;
638 | -moz-transition: opacity 1s, -moz-transform 0.5s 1s;
639 | -ms-transition: opacity 1s, -ms-transform 0.5s 1s;
640 | -o-transition: opacity 1s, -o-transform 0.5s 1s;
641 | transition: opacity 1s, transform 0.5s 1s;
642 | }
643 |
644 | /*
645 | Now we 'enable' the hint when presentation is initialized ...
646 | */
647 | .impress-enabled .hint { display: block }
648 |
649 | /*
650 | ... and we will show it when the first step (with id 'bored') is active.
651 | */
652 | .impress-on-welcome .hint {
653 | /*
654 | We remove the transparency and position the hint in its default fixed
655 | position.
656 | */
657 | opacity: 1;
658 |
659 | -webkit-transform: translateY(0px);
660 | -moz-transform: translateY(0px);
661 | -ms-transform: translateY(0px);
662 | -o-transform: translateY(0px);
663 | transform: translateY(0px);
664 |
665 | /*
666 | Now for fade in transition we have the oposite situation from the one
667 | above.
668 |
669 | First after 4.5s delay we animate the transform property to move the hint
670 | into its correct position and after that we fade it in with opacity
671 | transition.
672 | */
673 | -webkit-transition: opacity 1s 5s, -webkit-transform 0.5s 4.5s;
674 | -moz-transition: opacity 1s 5s, -moz-transform 0.5s 4.5s;
675 | -ms-transition: opacity 1s 5s, -ms-transform 0.5s 4.5s;
676 | -o-transition: opacity 1s 5s, -o-transform 0.5s 4.5s;
677 | transition: opacity 1s 5s, transform 0.5s 4.5s;
678 |
679 | }
680 |
681 | /*
682 | And as the last thing there is a workaround for quite strange bug.
683 | It happens a lot in Chrome. I don't remember if I've seen it in Firefox.
684 |
685 | Sometimes the element positioned in 3D (especially when it's moved back
686 | along Z axis) is not clickable, because it falls 'behind' the
687 | element.
688 |
689 | To prevent this, I decided to make non clickable by setting
690 | pointer-events property to `none` value.
691 | Value if this property is inherited, so to make everything else clickable
692 | I bring it back on the #impress element.
693 |
694 | If you want to know more about `pointer-events` here are some docs:
695 | https://developer.mozilla.org/en/CSS/pointer-events
696 |
697 | There is one very important thing to notice about this workaround - it makes
698 | everything 'unclickable' except what's in #impress element.
699 |
700 | So use it wisely ... or don't use at all.
701 | */
702 | .impress-enabled { pointer-events: none }
703 | .impress-enabled #impress { pointer-events: auto }
704 |
705 | /*
706 | There is one funny thing I just realized.
707 |
708 | Thanks to this workaround above everything except #impress element is invisible
709 | for click events. That means that the hint element is also not clickable.
710 | So basically all of this transforms and delayed transitions trickery was probably
711 | not needed at all...
712 |
713 | But it was fun to learn about it, wasn't it?
714 | */
715 |
716 | /*
717 | That's all I have for you in this file.
718 | Thanks for reading. I hope you enjoyed it at least as much as I enjoyed writing it
719 | for you.
720 | */
721 |
722 |
--------------------------------------------------------------------------------
/webapp/css/scala-tour.css:
--------------------------------------------------------------------------------
1 |
2 | .workspace {
3 | position: fixed;
4 | top: 0;
5 | bottom: 0;
6 | left: 0;
7 | right: 350px;
8 | min-height: 200px;
9 | text-shadow: 0 0 0 black
10 |
11 | }
12 |
13 | .workspace-top {
14 | position: absolute;
15 | top: 0;
16 | bottom: 30%;
17 | right: 0;
18 | left: 0;
19 | }
20 |
21 | .workspace-bottom {
22 | position: absolute;
23 | top: 70%;
24 | bottom: 0;
25 | right: 0;
26 | left: 0;
27 | }
28 |
29 |
30 | .workspace-editor {
31 | position: absolute;
32 | top: .6em;
33 | bottom: .3em;
34 | right: .6em;
35 | left: .6em;
36 | border: 1px solid #375EAB;
37 | background: #FFFFD8;
38 | -webkit-border-top-left-radius: 2px;
39 | -webkit-border-top-right-radius: 2px;
40 | -moz-border-radius-topleft: 2px;
41 | -moz-border-radius-topright: 2px;
42 | border-top-left-radius: 2px;
43 | border-top-right-radius: 2px;
44 | }
45 |
46 | .editor {
47 | position: absolute;
48 | top: 5px;
49 | bottom: 5px;
50 | left: 5px;
51 | right: 5px;
52 | border: none;
53 | outline: none;
54 | resize: none;
55 | background: #FFFFD8;
56 | font-size: 16px;
57 | }
58 |
59 | .output {
60 | position: absolute;
61 | top: .3em;
62 | bottom: .6em;
63 | right: .6em;
64 | left: .6em;
65 | border: 1px solid #375EAB;
66 | background: #FFFFFF;
67 | -webkit-border-bottom-left-radius: 5px;
68 | -webkit-border-bottom-right-radius: 5px;
69 | -moz-border-radius-bottomleft: 5px;
70 | -moz-border-radius-tottomright: 5px;
71 | border-bottom-left-radius: 5px;
72 | border-bottom-right-radius: 5px;
73 | overflow: auto;
74 | }
75 | .output pre {
76 | margin: 5px;
77 | padding: 0;
78 | background: none;
79 | font-size: 16px;
80 | }
81 | .output .loading {
82 | color: #999;
83 | }
84 | .output .stdout, .output pre {
85 | color: black;
86 | }
87 | .output .stderr, .output .error {
88 | color: #a11;
89 | }
90 | .output .system, .output .exit {
91 | color: #375EAB;
92 | }
93 | .output .stderr, .output .err {
94 | color: #a11;
95 | }
96 | .output img {
97 | margin: 5px;
98 | padding: 0;
99 | border: none;
100 | }
101 |
102 | pre, code {
103 | font-family: Menlo, monospace;
104 | font-size: 14px;
105 | border: 0px solid black;
106 | }
107 |
108 | div.source {
109 | display: none;
110 | }
111 |
112 | .CodeMirror {
113 | position: absolute;
114 | top: 0;
115 | bottom: 0;
116 | left: 0;
117 | right: 0;
118 | background: none;
119 | font-family: Menlo, monospace;
120 | }
121 | .CodeMirror {
122 | height: auto;
123 | }
124 |
125 | .CodeMirror-scroll {
126 | height: 100%;
127 | }
128 |
129 | .CodeMirror-gutter {
130 | background: inherit;
131 | border-right: 1px solid #999;
132 | }
133 |
134 | .CodeMirror-gutter-text {
135 | color: #999;
136 | }
137 |
138 | .CodeMirror-matchingbracket {
139 | color: #000 !important;
140 | text-decoration: underline;
141 | }
142 |
143 | .CodeMirror-focused div.CodeMirror-selected {
144 | background: #d0d0d0;
145 | }
146 |
147 | .CodeMirror .errLine {
148 | background: #FDD !important;
149 | }
150 |
151 |
152 |
153 | pre, code {
154 | font-family: Menlo, monospace;
155 | font-size: 14px;
156 | }
157 |
158 |
159 | .controls {
160 | position: absolute;
161 | z-index: 10;
162 | top: 1.2em;
163 | right: 2em;
164 | text-align: right;
165 | }
166 |
167 | .output , .error {
168 | color: #a11;
169 | }
170 |
171 | #brower-not-support{
172 | margin-top: 100px;
173 | font-size: 200%;
174 | }
175 |
176 | #welcome h1 {
177 | font-size: 96px;
178 | }
179 |
180 |
181 | #welcome p {
182 | font-size: 48px;
183 | line-height:3;
184 | }
185 |
186 | .scala-red {
187 | color:rgb(192, 32, 0);
188 | }
189 |
190 | #logo img{
191 | margin-right: 4px;
192 | }
193 |
194 | .navbar a {
195 | background: rgba(255,255,255,0);
196 | }
197 |
198 | .navbar .lang {
199 | display: block;
200 | float: right;
201 | padding: 10px 10px 10px;
202 | color: #777777;
203 | text-shadow: 0 1px 0 #ffffff;
204 | }
205 |
206 | .navbar .doc {
207 | display: block;
208 | float: right;
209 | padding: 10px 10px 10px;
210 |
211 | }
212 |
213 | .navbar .doc a{
214 | color: #777777;
215 | text-shadow: 0 1px 0 #ffffff;
216 | }
217 |
218 |
219 | .slide p{
220 | font-family: Helvetica, Arial, sans-serif;
221 | font-size: 16px;
222 | line-height: 1.6em;
223 | color: #222;
224 | }
225 |
226 |
227 | .impress-enabled .deeply{
228 | -webkit-transition: opacity 0s;
229 | -moz-transition: opacity 0s;
230 | -ms-transition: opacity 0s;
231 | -o-transition: opacity 0s;
232 | transition: opacity 0s;
233 | }
234 | .onload{
235 | opacity:0;
236 | }
237 |
238 | .chapter h2{
239 | font-size: 120px;
240 | }
241 |
242 | .chapter .btn{
243 | margin-left: 20px;
244 | margin-top: 150px;
245 | }
246 |
247 | .active .chapter .btn{
248 | }
249 |
250 |
251 | .step-nav{
252 | position: absolute;
253 | top: 90%;
254 | bottom: 0;
255 | right: 0;
256 | }
257 |
258 | #designed-by {
259 | position: absolute;
260 | top: 0;
261 | }
262 |
263 | #designed-by a {
264 | background-color: rgba(0, 0, 0, 0);
265 | }
266 |
267 | #compass {
268 | position: absolute;
269 | width: 300px;
270 | top: 0;
271 | bottom: 0;
272 | left: 0;
273 | right: 0;
274 | }
275 |
276 | #about {
277 | font-size: 24px;
278 | line-height: 3;
279 | }
280 | #about li{
281 | line-height: 2;
282 | }
283 | #about a{
284 | background: rgba(255,255,255,0);
285 | }
286 |
287 | #using-actor p a{
288 | background: rgba(255,255,255,0);
289 | }
290 |
291 | .contents h2{
292 | font-size: 36px;
293 | line-height: 2;
294 | }
295 |
296 | .contents li{
297 | list-style-image:url('../img/glyphicons_065_tag.png');
298 | }
299 | .contents .overview-li{
300 | list-style-image:url('../img/glyphicons_221_unshare.png');
301 | }
302 |
303 | .contents li{
304 | line-height: 2;
305 | }
306 |
307 | .contents li a {
308 | text-shadow: 0 2px 2px rgba(0, 0, 0, .1);
309 | font-size: 24px;
310 | color: inherit;
311 | background: rgba(255,255,255,0);
312 | }
313 |
314 | .important{
315 | font-weight:bold;
316 | }
317 |
318 | #disqus_thread {
319 | height:600px;
320 | overflow: auto;
321 | }
322 |
323 | .deeply h2{
324 | font-size: 30px;
325 | }
--------------------------------------------------------------------------------
/webapp/css/solarized.css:
--------------------------------------------------------------------------------
1 | /*
2 | Solarized theme for code-mirror
3 | http://ethanschoonover.com/solarized
4 | */
5 |
6 | /*
7 | Solarized color pallet
8 | http://ethanschoonover.com/solarized/img/solarized-palette.png
9 | */
10 |
11 | .solarized.base03 { color: #002b36; }
12 | .solarized.base02 { color: #073642; }
13 | .solarized.base01 { color: #586e75; }
14 | .solarized.base00 { color: #657b83; }
15 | .solarized.base0 { color: #839496; }
16 | .solarized.base1 { color: #93a1a1; }
17 | .solarized.base2 { color: #eee8d5; }
18 | .solarized.base3 { color: #fdf6e3; }
19 | .solarized.solar-yellow { color: #b58900; }
20 | .solarized.solar-orange { color: #cb4b16; }
21 | .solarized.solar-red { color: #dc322f; }
22 | .solarized.solar-magenta { color: #d33682; }
23 | .solarized.solar-violet { color: #6c71c4; }
24 | .solarized.solar-blue { color: #268bd2; }
25 | .solarized.solar-cyan { color: #2aa198; }
26 | .solarized.solar-green { color: #859900; }
27 |
28 | /* Color scheme for code-mirror */
29 |
30 | .cm-s-solarized {
31 | line-height: 1.45em;
32 | font-family: Menlo,Monaco,"Andale Mono","lucida console","Courier New",monospace !important;
33 | color-profile: sRGB;
34 | rendering-intent: auto;
35 | }
36 | .cm-s-solarized.cm-s-dark {
37 | color: #839496;
38 | background-color: #002b36;
39 | text-shadow: #002b36 0 1px;
40 | }
41 | .cm-s-solarized.cm-s-light {
42 | background-color: #FFFFD8;
43 | color: #657b83;
44 | text-shadow: #eee8d5 0 1px;
45 | }
46 |
47 | .cm-s-solarized .CodeMirror-widget {
48 | text-shadow: none;
49 | }
50 |
51 |
52 | .cm-s-solarized .cm-keyword { color: #cb4b16 }
53 | .cm-s-solarized .cm-atom { color: #d33682; }
54 | .cm-s-solarized .cm-number { color: #d33682; }
55 | .cm-s-solarized .cm-def { color: #2aa198; }
56 |
57 | .cm-s-solarized .cm-variable { color: #268bd2; }
58 | .cm-s-solarized .cm-variable-2 { color: #b58900; }
59 | .cm-s-solarized .cm-variable-3 { color: #6c71c4; }
60 |
61 | .cm-s-solarized .cm-property { color: #2aa198; }
62 | .cm-s-solarized .cm-operator {color: #6c71c4;}
63 |
64 | .cm-s-solarized .cm-comment { color: #586e75; font-style:italic; }
65 |
66 | .cm-s-solarized .cm-string { color: #859900; }
67 | .cm-s-solarized .cm-string-2 { color: #b58900; }
68 |
69 | .cm-s-solarized .cm-meta { color: #859900; }
70 | .cm-s-solarized .cm-error,
71 | .cm-s-solarized .cm-invalidchar {
72 | color: #586e75;
73 | border-bottom: 1px dotted #dc322f;
74 | }
75 | .cm-s-solarized .cm-qualifier { color: #b58900; }
76 | .cm-s-solarized .cm-builtin { color: #d33682; }
77 | .cm-s-solarized .cm-bracket { color: #cb4b16; }
78 | .cm-s-solarized .CodeMirror-matchingbracket { color: #859900; }
79 | .cm-s-solarized .CodeMirror-nonmatchingbracket { color: #dc322f; }
80 | .cm-s-solarized .cm-tag { color: #93a1a1 }
81 | .cm-s-solarized .cm-attribute { color: #2aa198; }
82 | .cm-s-solarized .cm-header { color: #586e75; }
83 | .cm-s-solarized .cm-quote { color: #93a1a1; }
84 | .cm-s-solarized .cm-hr {
85 | color: transparent;
86 | border-top: 1px solid #586e75;
87 | display: block;
88 | }
89 | .cm-s-solarized .cm-link { color: #93a1a1; cursor: pointer; }
90 | .cm-s-solarized .cm-special { color: #6c71c4; }
91 | .cm-s-solarized .cm-em {
92 | color: #999;
93 | text-decoration: underline;
94 | text-decoration-style: dotted;
95 | }
96 | .cm-s-solarized .cm-strong { color: #eee; }
97 | .cm-s-solarized .cm-tab:before {
98 | content: "➤"; /*visualize tab character*/
99 | color: #586e75;
100 | }
101 |
102 | .cm-s-solarized.cm-s-dark .CodeMirror-focused .CodeMirror-selected {
103 | background: #386774;
104 | color: inherit;
105 | }
106 |
107 | .cm-s-solarized.cm-s-dark ::selection {
108 | background: #386774;
109 | color: inherit;
110 | }
111 |
112 | .cm-s-solarized.cm-s-dark .CodeMirror-selected {
113 | background: #586e75;
114 | }
115 |
116 | .cm-s-solarized.cm-s-light .CodeMirror-focused .CodeMirror-selected {
117 | background: #eee8d5;
118 | color: inherit;
119 | }
120 |
121 | .cm-s-solarized.cm-s-light ::selection {
122 | background: #eee8d5;
123 | color: inherit;
124 | }
125 |
126 | .cm-s-solarized.cm-s-light .CodeMirror-selected {
127 | background: #93a1a1;
128 | }
129 |
130 |
131 |
132 | /* Editor styling */
133 |
134 |
135 |
136 | /* Little shadow on the view-port of the buffer view */
137 | .cm-s-solarized.CodeMirror {
138 | -moz-box-shadow: inset 7px 0 12px -6px #000;
139 | -webkit-box-shadow: inset 7px 0 12px -6px #000;
140 | box-shadow: inset 7px 0 12px -6px #000;
141 | }
142 |
143 | /* Gutter border and some shadow from it */
144 | /*.cm-s-solarized .CodeMirror-gutters {
145 | padding: 0 15px 0 10px;
146 | box-shadow: 0 10px 20px black;
147 | border-right: 1px solid;*/
148 | }
149 | */
150 | /* Gutter colors and line number styling based of color scheme (dark / light) */
151 |
152 | /* Dark */
153 | .cm-s-solarized.cm-s-dark .CodeMirror-gutters {
154 | background-color: #073642;
155 | border-color: #00232c;
156 | }
157 |
158 | .cm-s-solarized.cm-s-dark .CodeMirror-linenumber {
159 | text-shadow: #021014 0 -1px;
160 | }
161 |
162 | /* Light */
163 | /*.cm-s-solarized.cm-s-light .CodeMirror-gutters {
164 | background-color: #eee8d5;
165 | border-color: #eee8d5;
166 | }*/
167 |
168 | /* Common */
169 | .cm-s-solarized .CodeMirror-linenumber {
170 | color: #586e75;
171 | }
172 |
173 | .cm-s-solarized .CodeMirror-gutter .CodeMirror-gutter-text {
174 | color: #586e75;
175 | }
176 |
177 | .cm-s-solarized .CodeMirror-lines {
178 | padding-left: 5px;
179 | }
180 |
181 | .cm-s-solarized .CodeMirror-lines .CodeMirror-cursor {
182 | border-left: 1px solid #819090;
183 | }
184 |
185 | /*
186 | Active line. Negative margin compensates left padding of the text in the
187 | view-port
188 | */
189 | .cm-s-solarized .activeline {
190 | margin-left: -20px;
191 | }
192 |
193 | .cm-s-solarized.cm-s-dark .activeline {
194 | background: rgba(255, 255, 255, 0.05);
195 |
196 | }
197 | .cm-s-solarized.cm-s-light .activeline {
198 | background: rgba(0, 0, 0, 0.05);
199 | }
200 |
201 |
--------------------------------------------------------------------------------
/webapp/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yankay/scala-tour/7a482eea52e1ee429cb5961cb7ee5272227b44b1/webapp/favicon.png
--------------------------------------------------------------------------------
/webapp/img/Lambda_lc.svg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yankay/scala-tour/7a482eea52e1ee429cb5961cb7ee5272227b44b1/webapp/img/Lambda_lc.svg.png
--------------------------------------------------------------------------------
/webapp/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yankay/scala-tour/7a482eea52e1ee429cb5961cb7ee5272227b44b1/webapp/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/webapp/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yankay/scala-tour/7a482eea52e1ee429cb5961cb7ee5272227b44b1/webapp/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/webapp/img/glyphicons_065_tag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yankay/scala-tour/7a482eea52e1ee429cb5961cb7ee5272227b44b1/webapp/img/glyphicons_065_tag.png
--------------------------------------------------------------------------------
/webapp/img/glyphicons_221_unshare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yankay/scala-tour/7a482eea52e1ee429cb5961cb7ee5272227b44b1/webapp/img/glyphicons_221_unshare.png
--------------------------------------------------------------------------------
/webapp/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yankay/scala-tour/7a482eea52e1ee429cb5961cb7ee5272227b44b1/webapp/img/logo.png
--------------------------------------------------------------------------------
/webapp/js/bootstrap.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap.js by @fat & @mdo
3 | * Copyright 2012 Twitter, Inc.
4 | * http://www.apache.org/licenses/LICENSE-2.0.txt
5 | */
6 | !function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()};var r=e.fn.alert;e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e.fn.alert.noConflict=function(){return e.fn.alert=r,this},e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")};var n=e.fn.button;e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e.fn.button.noConflict=function(){return e.fn.button=n,this},e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=n,this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(t){var n=this.getActiveIndex(),r=this;if(t>this.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||s.toggleClass("open"),n.focus(),!1},keydown:function(n){var r,s,o,u,a,f;if(!/(38|40|27)/.test(n.keyCode))return;r=e(this),n.preventDefault(),n.stopPropagation();if(r.is(".disabled, :disabled"))return;u=i(r),a=u.hasClass("open");if(!a||a&&n.keyCode==27)return n.which==27&&u.find(t).focus(),r.click();s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus")),n.keyCode==38&&f>0&&f--,n.keyCode==40&&f').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?e.proxy(this.$element[0].focus,this.$element[0]):e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!t)return;i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,t):t()):t&&t()}};var n=e.fn.modal;e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e.fn.modal.noConflict=function(){return e.fn.modal=n,this},e(document).on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s,o,u,a;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,o=this.options.trigger.split(" ");for(a=o.length;a--;)u=o[a],u=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):u!="manual"&&(i=u=="hover"?"mouseenter":"focus",s=u=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this)));this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,this.$element.data(),t),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e.fn[this.type].defaults,r={},i;this._options&&e.each(this._options,function(e,t){n[e]!=t&&(r[e]=t)},this),i=e(t.currentTarget)[this.type](r).data(this.type);if(!i.options.delay||!i.options.delay.show)return i.show();clearTimeout(this.timeout),i.hoverState="in",this.timeout=setTimeout(function(){i.hoverState=="in"&&i.show()},i.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var t,n,r,i,s,o,u=e.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(u);if(u.isDefaultPrevented())return;t=this.tip(),this.setContent(),this.options.animation&&t.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,t[0],this.$element[0]):this.options.placement,t.detach().css({top:0,left:0,display:"block"}),this.options.container?t.appendTo(this.options.container):t.insertAfter(this.$element),n=this.getPosition(),r=t[0].offsetWidth,i=t[0].offsetHeight;switch(s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}this.applyPlacement(o,s),this.$element.trigger("shown")}},applyPlacement:function(e,t){var n=this.tip(),r=n[0].offsetWidth,i=n[0].offsetHeight,s,o,u,a;n.offset(e).addClass(t).addClass("in"),s=n[0].offsetWidth,o=n[0].offsetHeight,t=="top"&&o!=i&&(e.top=e.top+i-o,a=!0),t=="bottom"||t=="top"?(u=0,e.left<0&&(u=e.left*-2,e.left=0,n.offset(e),s=n[0].offsetWidth,o=n[0].offsetHeight),this.replaceArrow(u-r+s,s,"left")):this.replaceArrow(o-i,o,"top"),a&&n.offset(e)},replaceArrow:function(e,t,n){this.arrow().css(n,e?50*(1-e/t)+"%":"")},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function i(){var t=setTimeout(function(){n.off(e.support.transition.end).detach()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.detach()})}var t=this,n=this.tip(),r=e.Event("hide");this.$element.trigger(r);if(r.isDefaultPrevented())return;return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?i():n.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var t=this.$element[0];return e.extend({},typeof t.getBoundingClientRect=="function"?t.getBoundingClientRect():{width:t.offsetWidth,height:t.offsetHeight},this.$element.offset())},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(t){var n=t?e(t.currentTarget)[this.type](this._options).data(this.type):this;n.tip().hasClass("in")?n.hide():n.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var n=e.fn.tooltip;e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},e.fn.tooltip.noConflict=function(){return e.fn.tooltip=n,this}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=(typeof n.content=="function"?n.content.call(t[0]):n.content)||t.attr("data-content"),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var n=e.fn.popover;e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:''}),e.fn.popover.noConflict=function(){return e.fn.popover=n,this}}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var n=e(this),r=n.data("target")||n.attr("href"),i=/^#\w/.test(r)&&e(r);return i&&i.length&&[[i.position().top+(!e.isWindow(t.$scrollElement.get(0))&&t.$scrollElement.scrollTop()),r]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}};var n=e.fn.scrollspy;e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e.fn.scrollspy.noConflict=function(){return e.fn.scrollspy=n,this},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active:last a")[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}};var n=e.fn.tab;e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e.fn.tab.noConflict=function(){return e.fn.tab=n,this},e(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=e(this.options.menu),this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:t.top+t.height,left:t.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length"+t+""})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("focus",e.proxy(this.focus,this)).on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this)).on("mouseleave","li",e.proxy(this.mouseleave,this))},eventSupported:function(e){var t=e in this.$element;return t||(this.$element.setAttribute(e,"return;"),t=typeof this.$element[e]=="function"),t},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},focus:function(e){this.focused=!0},blur:function(e){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(e){e.stopPropagation(),e.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(t){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var n=e.fn.typeahead;e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'',item:'',minLength:1},e.fn.typeahead.Constructor=t,e.fn.typeahead.noConflict=function(){return e.fn.typeahead=n,this},e(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;n.typeahead(n.data())})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)).on("click.affix.data-api",e.proxy(function(){setTimeout(e.proxy(this.checkPosition,this),1)},this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))};var n=e.fn.affix;e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e.fn.affix.noConflict=function(){return e.fn.affix=n,this},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery);
--------------------------------------------------------------------------------
/webapp/js/clike.js:
--------------------------------------------------------------------------------
1 | CodeMirror.defineMode("clike", function(config, parserConfig) {
2 | var indentUnit = config.indentUnit,
3 | statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
4 | dontAlignCalls = parserConfig.dontAlignCalls,
5 | keywords = parserConfig.keywords || {},
6 | builtin = parserConfig.builtin || {},
7 | blockKeywords = parserConfig.blockKeywords || {},
8 | atoms = parserConfig.atoms || {},
9 | hooks = parserConfig.hooks || {},
10 | multiLineStrings = parserConfig.multiLineStrings;
11 | var isOperatorChar = /[+\-*&%=<>!?|\/]/;
12 |
13 | var curPunc;
14 |
15 | function tokenBase(stream, state) {
16 | var ch = stream.next();
17 | if (hooks[ch]) {
18 | var result = hooks[ch](stream, state);
19 | if (result !== false) return result;
20 | }
21 | if (ch == '"' || ch == "'") {
22 | state.tokenize = tokenString(ch);
23 | return state.tokenize(stream, state);
24 | }
25 | if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
26 | curPunc = ch;
27 | return null;
28 | }
29 | if (/\d/.test(ch)) {
30 | stream.eatWhile(/[\w\.]/);
31 | return "number";
32 | }
33 | if (ch == "/") {
34 | if (stream.eat("*")) {
35 | state.tokenize = tokenComment;
36 | return tokenComment(stream, state);
37 | }
38 | if (stream.eat("/")) {
39 | stream.skipToEnd();
40 | return "comment";
41 | }
42 | }
43 | if (isOperatorChar.test(ch)) {
44 | stream.eatWhile(isOperatorChar);
45 | return "operator";
46 | }
47 | stream.eatWhile(/[\w\$_]/);
48 | var cur = stream.current();
49 | if (keywords.propertyIsEnumerable(cur)) {
50 | if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
51 | return "keyword";
52 | }
53 | if (builtin.propertyIsEnumerable(cur)) {
54 | if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement";
55 | return "builtin";
56 | }
57 | if (atoms.propertyIsEnumerable(cur)) return "atom";
58 | return "variable";
59 | }
60 |
61 | function tokenString(quote) {
62 | return function(stream, state) {
63 | var escaped = false, next, end = false;
64 | while ((next = stream.next()) != null) {
65 | if (next == quote && !escaped) {end = true; break;}
66 | escaped = !escaped && next == "\\";
67 | }
68 | if (end || !(escaped || multiLineStrings))
69 | state.tokenize = null;
70 | return "string";
71 | };
72 | }
73 |
74 | function tokenComment(stream, state) {
75 | var maybeEnd = false, ch;
76 | while (ch = stream.next()) {
77 | if (ch == "/" && maybeEnd) {
78 | state.tokenize = null;
79 | break;
80 | }
81 | maybeEnd = (ch == "*");
82 | }
83 | return "comment";
84 | }
85 |
86 | function Context(indented, column, type, align, prev) {
87 | this.indented = indented;
88 | this.column = column;
89 | this.type = type;
90 | this.align = align;
91 | this.prev = prev;
92 | }
93 | function pushContext(state, col, type) {
94 | var indent = state.indented;
95 | if (state.context && state.context.type == "statement")
96 | indent = state.context.indented;
97 | return state.context = new Context(indent, col, type, null, state.context);
98 | }
99 | function popContext(state) {
100 | var t = state.context.type;
101 | if (t == ")" || t == "]" || t == "}")
102 | state.indented = state.context.indented;
103 | return state.context = state.context.prev;
104 | }
105 |
106 | // Interface
107 |
108 | return {
109 | startState: function(basecolumn) {
110 | return {
111 | tokenize: null,
112 | context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
113 | indented: 0,
114 | startOfLine: true
115 | };
116 | },
117 |
118 | token: function(stream, state) {
119 | var ctx = state.context;
120 | if (stream.sol()) {
121 | if (ctx.align == null) ctx.align = false;
122 | state.indented = stream.indentation();
123 | state.startOfLine = true;
124 | }
125 | if (stream.eatSpace()) return null;
126 | curPunc = null;
127 | var style = (state.tokenize || tokenBase)(stream, state);
128 | if (style == "comment" || style == "meta") return style;
129 | if (ctx.align == null) ctx.align = true;
130 |
131 | if ((curPunc == ";" || curPunc == ":" || curPunc == ",") && ctx.type == "statement") popContext(state);
132 | else if (curPunc == "{") pushContext(state, stream.column(), "}");
133 | else if (curPunc == "[") pushContext(state, stream.column(), "]");
134 | else if (curPunc == "(") pushContext(state, stream.column(), ")");
135 | else if (curPunc == "}") {
136 | while (ctx.type == "statement") ctx = popContext(state);
137 | if (ctx.type == "}") ctx = popContext(state);
138 | while (ctx.type == "statement") ctx = popContext(state);
139 | }
140 | else if (curPunc == ctx.type) popContext(state);
141 | else if (((ctx.type == "}" || ctx.type == "top") && curPunc != ';') || (ctx.type == "statement" && curPunc == "newstatement"))
142 | pushContext(state, stream.column(), "statement");
143 | state.startOfLine = false;
144 | return style;
145 | },
146 |
147 | indent: function(state, textAfter) {
148 | if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
149 | var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
150 | if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
151 | var closing = firstChar == ctx.type;
152 | if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
153 | else if (dontAlignCalls && ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit;
154 | else if (ctx.align) return ctx.column + (closing ? 0 : 1);
155 | else return ctx.indented + (closing ? 0 : indentUnit);
156 | },
157 |
158 | electricChars: "{}"
159 | };
160 | });
161 |
162 | (function() {
163 | function words(str) {
164 | var obj = {}, words = str.split(" ");
165 | for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
166 | return obj;
167 | }
168 | var cKeywords = "auto if break int case long char register continue return default short do sizeof " +
169 | "double static else struct entry switch extern typedef float union for unsigned " +
170 | "goto while enum void const signed volatile";
171 |
172 | function cppHook(stream, state) {
173 | if (!state.startOfLine) return false;
174 | for (;;) {
175 | if (stream.skipTo("\\")) {
176 | stream.next();
177 | if (stream.eol()) {
178 | state.tokenize = cppHook;
179 | break;
180 | }
181 | } else {
182 | stream.skipToEnd();
183 | state.tokenize = null;
184 | break;
185 | }
186 | }
187 | return "meta";
188 | }
189 |
190 | // C#-style strings where "" escapes a quote.
191 | function tokenAtString(stream, state) {
192 | var next;
193 | while ((next = stream.next()) != null) {
194 | if (next == '"' && !stream.eat('"')) {
195 | state.tokenize = null;
196 | break;
197 | }
198 | }
199 | return "string";
200 | }
201 |
202 | function mimes(ms, mode) {
203 | for (var i = 0; i < ms.length; ++i) CodeMirror.defineMIME(ms[i], mode);
204 | }
205 |
206 | mimes(["text/x-csrc", "text/x-c", "text/x-chdr"], {
207 | name: "clike",
208 | keywords: words(cKeywords),
209 | blockKeywords: words("case do else for if switch while struct"),
210 | atoms: words("null"),
211 | hooks: {"#": cppHook}
212 | });
213 | mimes(["text/x-c++src", "text/x-c++hdr"], {
214 | name: "clike",
215 | keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " +
216 | "static_cast typeid catch operator template typename class friend private " +
217 | "this using const_cast inline public throw virtual delete mutable protected " +
218 | "wchar_t"),
219 | blockKeywords: words("catch class do else finally for if struct switch try while"),
220 | atoms: words("true false null"),
221 | hooks: {"#": cppHook}
222 | });
223 | CodeMirror.defineMIME("text/x-java", {
224 | name: "clike",
225 | keywords: words("abstract assert boolean break byte case catch char class const continue default " +
226 | "do double else enum extends final finally float for goto if implements import " +
227 | "instanceof int interface long native new package private protected public " +
228 | "return short static strictfp super switch synchronized this throw throws transient " +
229 | "try void volatile while"),
230 | blockKeywords: words("catch class do else finally for if switch try while"),
231 | atoms: words("true false null"),
232 | hooks: {
233 | "@": function(stream) {
234 | stream.eatWhile(/[\w\$_]/);
235 | return "meta";
236 | }
237 | }
238 | });
239 | CodeMirror.defineMIME("text/x-csharp", {
240 | name: "clike",
241 | keywords: words("abstract as base break case catch checked class const continue" +
242 | " default delegate do else enum event explicit extern finally fixed for" +
243 | " foreach goto if implicit in interface internal is lock namespace new" +
244 | " operator out override params private protected public readonly ref return sealed" +
245 | " sizeof stackalloc static struct switch this throw try typeof unchecked" +
246 | " unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
247 | " global group into join let orderby partial remove select set value var yield"),
248 | blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
249 | builtin: words("Boolean Byte Char DateTime DateTimeOffset Decimal Double" +
250 | " Guid Int16 Int32 Int64 Object SByte Single String TimeSpan UInt16 UInt32" +
251 | " UInt64 bool byte char decimal double short int long object" +
252 | " sbyte float string ushort uint ulong"),
253 | atoms: words("true false null"),
254 | hooks: {
255 | "@": function(stream, state) {
256 | if (stream.eat('"')) {
257 | state.tokenize = tokenAtString;
258 | return tokenAtString(stream, state);
259 | }
260 | stream.eatWhile(/[\w\$_]/);
261 | return "meta";
262 | }
263 | }
264 | });
265 | CodeMirror.defineMIME("text/x-scala", {
266 | name: "clike",
267 | keywords: words(
268 |
269 | /* scala */
270 | "abstract case catch class def do else extends false final finally for forSome if " +
271 | "implicit import lazy match new null object override package private protected return " +
272 | "sealed super this throw trait try trye type val var while with yield _ : = => <- <: " +
273 | "<% >: # @ " +
274 |
275 | /* package scala */
276 | "assert assume require print println printf readLine readBoolean readByte readShort " +
277 | "readChar readInt readLong readFloat readDouble " +
278 |
279 | "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
280 | "Enumeration Equiv Error Exception Fractional Function IndexedSeq Integral Iterable " +
281 | "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " +
282 | "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " +
283 | "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector :: #:: " +
284 |
285 | /* package java.lang */
286 | "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
287 | "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
288 | "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
289 | "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
290 |
291 |
292 | ),
293 | blockKeywords: words("catch class do else finally for forSome if match switch try while"),
294 | atoms: words("true false null"),
295 | hooks: {
296 | "@": function(stream) {
297 | stream.eatWhile(/[\w\$_]/);
298 | return "meta";
299 | }
300 | }
301 | });
302 | }());
303 |
--------------------------------------------------------------------------------
/webapp/js/go.js:
--------------------------------------------------------------------------------
1 | CodeMirror.defineMode("go", function(config) {
2 | var indentUnit = config.indentUnit;
3 |
4 | var keywords = {
5 | "break":true, "case":true, "chan":true, "const":true, "continue":true,
6 | "default":true, "defer":true, "else":true, "fallthrough":true, "for":true,
7 | "func":true, "go":true, "goto":true, "if":true, "import":true,
8 | "interface":true, "map":true, "package":true, "range":true, "return":true,
9 | "select":true, "struct":true, "switch":true, "type":true, "var":true,
10 | "bool":true, "byte":true, "complex64":true, "complex128":true,
11 | "float32":true, "float64":true, "int8":true, "int16":true, "int32":true,
12 | "int64":true, "string":true, "uint8":true, "uint16":true, "uint32":true,
13 | "uint64":true, "int":true, "uint":true, "uintptr":true
14 | };
15 |
16 | var atoms = {
17 | "true":true, "false":true, "iota":true, "nil":true, "append":true,
18 | "cap":true, "close":true, "complex":true, "copy":true, "imag":true,
19 | "len":true, "make":true, "new":true, "panic":true, "print":true,
20 | "println":true, "real":true, "recover":true
21 | };
22 |
23 | var isOperatorChar = /[+\-*&^%:=<>!|\/]/;
24 |
25 | var curPunc;
26 |
27 | function tokenBase(stream, state) {
28 | var ch = stream.next();
29 | if (ch == '"' || ch == "'" || ch == "`") {
30 | state.tokenize = tokenString(ch);
31 | return state.tokenize(stream, state);
32 | }
33 | if (/[\d\.]/.test(ch)) {
34 | if (ch == ".") {
35 | stream.match(/^[0-9]+([eE][\-+]?[0-9]+)?/);
36 | } else if (ch == "0") {
37 | stream.match(/^[xX][0-9a-fA-F]+/) || stream.match(/^0[0-7]+/);
38 | } else {
39 | stream.match(/^[0-9]*\.?[0-9]*([eE][\-+]?[0-9]+)?/);
40 | }
41 | return "number";
42 | }
43 | if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
44 | curPunc = ch;
45 | return null;
46 | }
47 | if (ch == "/") {
48 | if (stream.eat("*")) {
49 | state.tokenize = tokenComment;
50 | return tokenComment(stream, state);
51 | }
52 | if (stream.eat("/")) {
53 | stream.skipToEnd();
54 | return "comment";
55 | }
56 | }
57 | if (isOperatorChar.test(ch)) {
58 | stream.eatWhile(isOperatorChar);
59 | return "operator";
60 | }
61 | stream.eatWhile(/[\w\$_]/);
62 | var cur = stream.current();
63 | if (keywords.propertyIsEnumerable(cur)) {
64 | if (cur == "case" || cur == "default") curPunc = "case";
65 | return "keyword";
66 | }
67 | if (atoms.propertyIsEnumerable(cur)) return "atom";
68 | return "variable";
69 | }
70 |
71 | function tokenString(quote) {
72 | return function(stream, state) {
73 | var escaped = false, next, end = false;
74 | while ((next = stream.next()) != null) {
75 | if (next == quote && !escaped) {end = true; break;}
76 | escaped = !escaped && next == "\\";
77 | }
78 | if (end || !(escaped || quote == "`"))
79 | state.tokenize = tokenBase;
80 | return "string";
81 | };
82 | }
83 |
84 | function tokenComment(stream, state) {
85 | var maybeEnd = false, ch;
86 | while (ch = stream.next()) {
87 | if (ch == "/" && maybeEnd) {
88 | state.tokenize = tokenBase;
89 | break;
90 | }
91 | maybeEnd = (ch == "*");
92 | }
93 | return "comment";
94 | }
95 |
96 | function Context(indented, column, type, align, prev) {
97 | this.indented = indented;
98 | this.column = column;
99 | this.type = type;
100 | this.align = align;
101 | this.prev = prev;
102 | }
103 | function pushContext(state, col, type) {
104 | return state.context = new Context(state.indented, col, type, null, state.context);
105 | }
106 | function popContext(state) {
107 | var t = state.context.type;
108 | if (t == ")" || t == "]" || t == "}")
109 | state.indented = state.context.indented;
110 | return state.context = state.context.prev;
111 | }
112 |
113 | // Interface
114 |
115 | return {
116 | startState: function(basecolumn) {
117 | return {
118 | tokenize: null,
119 | context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
120 | indented: 0,
121 | startOfLine: true
122 | };
123 | },
124 |
125 | token: function(stream, state) {
126 | var ctx = state.context;
127 | if (stream.sol()) {
128 | if (ctx.align == null) ctx.align = false;
129 | state.indented = stream.indentation();
130 | state.startOfLine = true;
131 | if (ctx.type == "case") ctx.type = "}";
132 | }
133 | if (stream.eatSpace()) return null;
134 | curPunc = null;
135 | var style = (state.tokenize || tokenBase)(stream, state);
136 | if (style == "comment") return style;
137 | if (ctx.align == null) ctx.align = true;
138 |
139 | if (curPunc == "{") pushContext(state, stream.column(), "}");
140 | else if (curPunc == "[") pushContext(state, stream.column(), "]");
141 | else if (curPunc == "(") pushContext(state, stream.column(), ")");
142 | else if (curPunc == "case") ctx.type = "case";
143 | else if (curPunc == "}" && ctx.type == "}") ctx = popContext(state);
144 | else if (curPunc == ctx.type) popContext(state);
145 | state.startOfLine = false;
146 | return style;
147 | },
148 |
149 | indent: function(state, textAfter) {
150 | if (state.tokenize != tokenBase && state.tokenize != null) return 0;
151 | var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
152 | if (ctx.type == "case" && /^(?:case|default)\b/.test(textAfter)) {
153 | state.context.type = "}";
154 | return ctx.indented;
155 | }
156 | var closing = firstChar == ctx.type;
157 | if (ctx.align) return ctx.column + (closing ? 0 : 1);
158 | else return ctx.indented + (closing ? 0 : indentUnit);
159 | },
160 |
161 | electricChars: "{}:"
162 | };
163 | });
164 |
165 | CodeMirror.defineMIME("text/x-go", "go");
166 |
--------------------------------------------------------------------------------
/webapp/js/matchbrackets.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
3 | (document.documentMode == null || document.documentMode < 8);
4 |
5 | var Pos = CodeMirror.Pos;
6 | // Disable brace matching in long lines, since it'll cause hugely slow updates
7 | var maxLineLen = 1000;
8 |
9 | var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
10 | function findMatchingBracket(cm) {
11 | var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
12 | var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
13 | if (!match) return null;
14 | var forward = match.charAt(1) == ">", d = forward ? 1 : -1;
15 | var style = cm.getTokenAt(Pos(cur.line, pos + 1)).type;
16 |
17 | var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
18 | function scan(line, lineNo, start) {
19 | if (!line.text) return;
20 | var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1;
21 | if (start != null) pos = start + d;
22 | for (; pos != end; pos += d) {
23 | var ch = line.text.charAt(pos);
24 | if (re.test(ch) && cm.getTokenAt(Pos(lineNo, pos + 1)).type == style) {
25 | var match = matching[ch];
26 | if (match.charAt(1) == ">" == forward) stack.push(ch);
27 | else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
28 | else if (!stack.length) return {pos: pos, match: true};
29 | }
30 | }
31 | }
32 | for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) {
33 | if (i == cur.line) found = scan(line, i, pos);
34 | else found = scan(cm.getLineHandle(i), i);
35 | if (found) break;
36 | }
37 | return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos), match: found && found.match};
38 | }
39 |
40 | function matchBrackets(cm, autoclear) {
41 | var found = findMatchingBracket(cm);
42 | if (!found || cm.getLine(found.from.line).length > maxLineLen ||
43 | found.to && cm.getLine(found.to.line).length > maxLineLen)
44 | return;
45 |
46 | var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
47 | var one = cm.markText(found.from, Pos(found.from.line, found.from.ch + 1), {className: style});
48 | var two = found.to && cm.markText(found.to, Pos(found.to.line, found.to.ch + 1), {className: style});
49 | // Kludge to work around the IE bug from issue #1193, where text
50 | // input stops going to the textare whever this fires.
51 | if (ie_lt8 && cm.state.focused) cm.display.input.focus();
52 | var clear = function() {
53 | cm.operation(function() { one.clear(); two && two.clear(); });
54 | };
55 | if (autoclear) setTimeout(clear, 800);
56 | else return clear;
57 | }
58 |
59 | var currentlyHighlighted = null;
60 | function doMatchBrackets(cm) {
61 | cm.operation(function() {
62 | if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
63 | if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false);
64 | });
65 | }
66 |
67 | CodeMirror.defineOption("matchBrackets", false, function(cm, val) {
68 | if (val) cm.on("cursorActivity", doMatchBrackets);
69 | else cm.off("cursorActivity", doMatchBrackets);
70 | });
71 |
72 | CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
73 | CodeMirror.defineExtension("findMatchingBracket", function(){return findMatchingBracket(this);});
74 | })();
75 |
--------------------------------------------------------------------------------
/webapp/js/scala-tour.js:
--------------------------------------------------------------------------------
1 | function loading(outputDiv) {
2 | // $(".output").html(''+('waiting')+'
');
3 | outputDiv.html('' + ('waiting') + '
');
4 | }
5 |
6 | function runFunc(codeStr, outputDiv) {
7 | $.ajax("/run?code=" + encodeURIComponent(codeStr), {
8 | type: "GET",
9 | dataType: "json",
10 | success: function(data) {
11 | if (!data) {
12 | return;
13 | }
14 | if (data.Errors && data.Errors.length > 0) {
15 | setOutput(outputDiv, null, null, data.Errors);
16 | return;
17 | }
18 | setOutput(outputDiv, data.Events, data.ErrEvents, false);
19 | },
20 | error: function() {
21 | outputDiv.addClass("error").text(
22 | "Error communicating with remote server.");
23 | }
24 | });
25 | }
26 |
27 | function setOutput(output, events, errevents, error) {
28 | output.empty();
29 | if (events) {
30 |
31 | var msg = ""
32 | for (var i = 0; i < events.length; i++) {
33 | msg += events[i] + "\n"
34 | }
35 | output.text(msg);
36 |
37 | msg = ""
38 | for (var i = 0; i < errevents.length; i++) {
39 | msg += errevents[i] + "\n"
40 | }
41 |
42 | if (msg != "") {
43 | var err = $('');
44 | err.text(msg);
45 | err.appendTo(output);
46 | }
47 |
48 | var exit = $('');
49 | exit.text("\nProgram exited.");
50 | exit.appendTo(output);
51 | }
52 | // Display errors.
53 | if (error) {
54 | var errorText = ""
55 | for (var i = 0; i < error.length; i++) {
56 | errorText += error[i] + "\n"
57 | }
58 | output.addClass("error").text(errorText);
59 | }
60 | }
61 |
62 | var editors = []
63 | var editorsMap = {}
64 |
65 | $(".run").click(function() {
66 | var editorText = $(this).parent().parent().parent().find(".editor");
67 | var outputDiv = $(this).parent().parent().parent().find(".output");
68 | loading(outputDiv);
69 | var editor = editorsMap[getElementPath(editorText)]
70 | runFunc(editor.getValue(), outputDiv.find("pre"));
71 | });
72 |
73 | function getElementPath(element) {
74 | return "//" + $(element).parents().andSelf().map(function() {
75 | var $this = $(this);
76 | var tagName = this.nodeName;
77 | if ($this.siblings(tagName).length > 0) {
78 | tagName += "[" + $this.prevAll(tagName).length + "]";
79 | }
80 | return tagName;
81 | }).get().join("/").toUpperCase();
82 | }
83 |
84 |
85 |
86 | document.addEventListener("impress:add-active", function(event) {
87 | //restore
88 | var editorsRemove=[]
89 |
90 | for(var i = 0; i < editors.length; i++){
91 | var editor=editors[i]
92 | editorsRemove.push(editor)
93 | }
94 | editors = []
95 | editorsMap = {}
96 | //add
97 | var textareas = $(".active .editor").get()
98 | for (var i = 0; i < textareas.length; i++) {
99 | var textarea = textareas[i]
100 | var editor = CodeMirror.fromTextArea(textarea, {
101 | theme: "solarized light",
102 | matchBrackets: true,
103 | indentUnit: 2,
104 | tabSize: 2,
105 | indentWithTabs: false,
106 | mode: "text/x-scala",
107 | smartIndent :false,
108 | lineNumbers: false
109 | });
110 | editors.push(editor)
111 | editorsMap[getElementPath(textarea)]=editor
112 | }
113 |
114 | for(var i = 0; i < editorsRemove.length; i++){
115 | var editor=editorsRemove[i]
116 | window.setTimeout(function() {
117 | editor.save()
118 | editor.toTextArea()
119 | }, 1000 /* but after 2000 ms */);
120 | }
121 |
122 |
123 | }, false);
124 |
125 |
126 | document.getElementById("body").className = document.getElementById("body").className.replace('onload', ' ');
127 | impress().init();
128 |
129 |
130 |
131 | // var textareas = document.getElementsByClassName("editor")
132 |
133 |
134 | // $(window).load(function() {
135 |
136 |
137 |
138 |
139 |
140 | // });
--------------------------------------------------------------------------------