From dab933c342d755086253b1465a04caeeaa2af823 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Tue, 11 Nov 2014 21:32:25 -0800 Subject: Pure Scala! --- book/src/main/resources/js/ui.js | 97 --------------------------- book/src/main/scala/book/Book.scala | 47 +++---------- build.sbt | 4 +- examples/demos/Controller.scala | 130 ++++++++++++++++++++++++++++++++++++ examples/demos/build.sbt | 4 ++ 5 files changed, 147 insertions(+), 135 deletions(-) delete mode 100755 book/src/main/resources/js/ui.js create mode 100644 examples/demos/Controller.scala diff --git a/book/src/main/resources/js/ui.js b/book/src/main/resources/js/ui.js deleted file mode 100755 index ba79579..0000000 --- a/book/src/main/resources/js/ui.js +++ /dev/null @@ -1,97 +0,0 @@ -document.addEventListener("DOMContentLoaded", function(){ - console.log("HIGHLIGHTING") - - var layout = document.getElementById('layout'), - menu = document.getElementById('menu'), - menuLink = document.getElementById('menuLink'); - - function toggleClass(element, className) { - var classes = element.className.split(/\s+/), - length = classes.length, - i = 0; - - for(; i < length; i++) { - if (classes[i] === className) { - classes.splice(i, 1); - break; - } - } - // The className is not found - if (length === classes.length) { - classes.push(className); - } - - element.className = classes.join(' '); - } - - menuLink.onclick = function (e) { - var active = 'active'; - - e.preventDefault(); - toggleClass(layout, active); - toggleClass(menu, active); - toggleClass(menuLink, active); - }; - - - var snippets = document.getElementsByClassName("highlight-me"); - for(var i = 0; i < snippets.length; i++){ - hljs.highlightBlock(snippets[i]) - } - - - - - // Cache selectors - var lastId = -1; - var main = document.getElementById("main"); - - scrollItems = document.getElementsByClassName("menu-item"); - - scrollHeaders = [] - - for(var i = 0; i < scrollItems.length; i++){ - scrollHeaders.push( - document.getElementById( - scrollItems[i].getAttribute("href").substring(1) - ) - ) - } - - - function isElementInViewport (el) { - var rect = el.getBoundingClientRect(); - - return ( - rect.top >= 0 && - rect.left >= 0 && - rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */ - rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */ - ); - } - main.addEventListener("scroll", function(){ - // Get container scroll position - var fromTop = main.scrollTop; - - // Get id of current scroll item - for(var i = scrollItems.length - 1; i >= 0; i--){ - if (scrollHeaders[i].offsetTop < fromTop + 100 /*fudge factor*/){ - if (lastId != i) { - if (lastId != -1) { - scrollItems[lastId].parentElement.className = scrollItems[lastId].parentElement.className.replace( - " pure-menu-selected", - "" - ); - } - scrollItems[i].parentElement.className = scrollItems[i].parentElement.className + " pure-menu-selected" - if (!isElementInViewport(scrollItems[i].parentElement)){ - scrollItems[i].parentElement.scrollIntoView(lastId > i) - } - - lastId = i - } - break; - } - } - }); -}); diff --git a/book/src/main/scala/book/Book.scala b/book/src/main/scala/book/Book.scala index c6c9c2e..ea57945 100644 --- a/book/src/main/scala/book/Book.scala +++ b/book/src/main/scala/book/Book.scala @@ -25,7 +25,6 @@ object Book { "META-INF/resources/webjars/font-awesome/4.2.0/fonts/fontawesome-webfont.ttf", "META-INF/resources/webjars/font-awesome/4.2.0/fonts/fontawesome-webfont.woff", "css/side-menu.css", - "js/ui.js", "example-fastopt.js", "webpage/weather.js", "favicon.svg", @@ -52,24 +51,8 @@ object Book { } val txt = Index() - val contentBar = { - def rec(current: Node, depth: Int): Seq[Frag] = { - println("\t"*depth + current.name) - Seq( - li( - a( - current.name, - href:="#"+sect.munge(current.name), - paddingLeft := s"${depth * 10 + 10}px", - cls := "menu-item" + (if (depth == 1) " menu-item-divided " else "") - ) - ) - ) ++ current.children.flatMap(rec(_, depth + 1)) - } + val data = upickle.write(sect.structure) - println("TABLE OF CONTENTS") - rec(sect.structure, 0) - } val site = Seq( raw(""), html( @@ -80,27 +63,19 @@ object Book { tags2.title("Hands-on Scala.js"), includes ), - - div(id:="layout")( - a(href:="#menu", id:="menuLink", cls:="menu-link")( - span + body( + onload:=s"Controller().main($data)", + div(id:="layout")( + a(href:="#menu", id:="menuLink", cls:="menu-link")( + span + ), + div(id:="menu") ), - - div(id:="menu")( - div(cls:="pure-menu pure-menu-open")( - a(cls:="pure-menu-heading", href:="#")( - "Contents" - ), - ul(cls:="menu-item-list")( - contentBar - ) + div(id:="main", + div(id:="main-box")( + txt ) ) - ), - div(id:="main", - div(id:="main-box")( - txt - ) ) ) ).render diff --git a/build.sbt b/build.sbt index c566cab..a2012d5 100644 --- a/build.sbt +++ b/build.sbt @@ -39,7 +39,8 @@ lazy val book = Project( "org.webjars" % "font-awesome" % "4.2.0", "org.scala-lang" % "scala-reflect" % scalaVersion.value, "org.scala-lang" % "scala-compiler" % scalaVersion.value, - "org.eclipse.jgit" % "org.eclipse.jgit" % "3.5.1.201410131835-r" + "org.eclipse.jgit" % "org.eclipse.jgit" % "3.5.1.201410131835-r", + "com.lihaoyi" %%% "upickle" % "0.2.5" ), (resources in Compile) += { (fastOptJS in (demos, Compile)).value @@ -67,7 +68,6 @@ lazy val book = Project( addCompilerPlugin("com.lihaoyi" %% "acyclic" % "0.1.2") ) - lazy val demos = project.in(file("examples/demos")) lazy val simple = project.in(file("examples/crossBuilds/simple")) diff --git a/examples/demos/Controller.scala b/examples/demos/Controller.scala new file mode 100644 index 0000000..d27c68e --- /dev/null +++ b/examples/demos/Controller.scala @@ -0,0 +1,130 @@ + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExport +import org.scalajs.dom +import org.scalajs.dom.extensions._ +import scala.collection.mutable +import scalatags.JsDom.all._ +import rx._ +import util.{Success, Failure} +case class Node(name: String, children: mutable.Buffer[Node]) +@JSExport +object Controller{ + + def munge(name: String) = { + name.replace(" ", "") + } + def toggleClass(el: dom.HTMLElement, cls: String) = { + val frags = el.className.split(' ') + if (!frags.contains(cls)) el.className = el.className + " " + cls + else el.className = el.className.split(' ').filter(_ != cls).mkString(" ") + } + @JSExport + def main(data: scala.scalajs.js.Any) = { + + val structure = upickle.readJs[Node](upickle.json.readJs(data)) + + val main = dom.document.getElementById("main") + val menu = dom.document.getElementById("menu") + + val layout = dom.document.getElementById("layout") + val menuLink = dom.document.getElementById("menuLink") + val snippets = dom.document.getElementsByClassName("highlight-me") + + snippets.foreach(js.Dynamic.global.hljs.highlightBlock(_)) + + val contentBar = { + def rec(current: Node, depth: Int): Seq[dom.HTMLLIElement] = { + println("\t"*depth + current.name) + val myCls = + "menu-item" + + (if (depth == 1) " menu-item-divided" else "") + + val frag = + li( + a( + current.name, + href:="#"+munge(current.name), + paddingLeft := s"${depth * 10 + 10}px", + cls:=myCls + ) + ).render + + frag +: current.children.flatMap(rec(_, depth + 1)) + } + structure.children.flatMap(rec(_, 0)) + } + def menuItems = { + def rec(current: Node): Seq[String] = { + current.name +: current.children.flatMap(rec) + } + rec(structure).tail + } + val frag = div(cls:="pure-menu pure-menu-open")( + a(cls:="pure-menu-heading", href:="#")( + "Contents" + ), + ul(cls:="menu-item-list")( + contentBar + ) + ) + menu.appendChild(frag.render) + + def offset(el: dom.HTMLElement, parent: dom.HTMLElement): Double = { + if (el == parent) 0 + else el.offsetTop + offset(el.offsetParent.asInstanceOf[dom.HTMLElement], parent) + } + val headers = menuItems.map(munge) + .map(dom.document.getElementById) + .map(offset(_, main)) + .toArray + + scrollSpy(main, headers, contentBar) + + menuLink.onclick = (e: dom.MouseEvent) => { + toggleClass(layout, "active") + toggleClass(menu, "active") + toggleClass(menuLink, "active") + }; + } + + + + def scrollSpy(main: dom.HTMLElement, + headers: Seq[Double], + contentBar: Seq[dom.HTMLElement]) = { + def isElementInViewport(el: dom.HTMLElement) = { + val rect = el.getBoundingClientRect() + rect.top >= 0 && rect.bottom <= dom.innerHeight + } + + var scrolling = false + var lastIndex = -1 + def run() = { + scrolling = false + val threshold = main.scrollTop + main.clientHeight + var index = 0 + while(index < headers.length && index >= 0){ + index += 1 + if (headers(index) > threshold) index *= -1 + } + index = -index - 1 + if (index != lastIndex){ + if (!isElementInViewport(contentBar(index))) { + contentBar(index).scrollIntoView(lastIndex > index) + } + if (lastIndex != -1) + toggleClass(contentBar(lastIndex), "pure-menu-selected") + toggleClass(contentBar(index), "pure-menu-selected") + lastIndex = index + } + } + run() + main.onscroll = (e: dom.UIEvent) => { + if (!scrolling){ + scrolling = true + dom.requestAnimationFrame((d: Double) => run()) + } + } + } +} diff --git a/examples/demos/build.sbt b/examples/demos/build.sbt index 20b9fca..5374c30 100644 --- a/examples/demos/build.sbt +++ b/examples/demos/build.sbt @@ -6,6 +6,10 @@ version := "0.1-SNAPSHOT" scalaVersion := "2.11.4" +libraryDependencies += "com.scalarx" %%% "scalarx" % "0.2.6" + +libraryDependencies += "com.lihaoyi" %%% "upickle" % "0.2.5" + libraryDependencies += "org.scala-lang.modules.scalajs" %%% "scalajs-dom" % "0.6" libraryDependencies += "com.scalatags" %%% "scalatags" % "0.4.2" \ No newline at end of file -- cgit v1.2.3