Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Examples up through Ajax/Comet

  • Loading branch information...
commit 48b0dea3557004e37f2abb3b3d6466fd499b8797 1 parent 8f3cd74
@dpp dpp authored
View
1  src/main/resources/src/webapp
View
14 src/main/scala/bootstrap/liftweb/Boot.scala
@@ -22,12 +22,14 @@ class Boot {
// Build SiteMap
def sitemap = SiteMap(
- Menu.i("Home") / "index", // the simple way to declare a menu
-
- // more complex because this menu allows anything in the
- // /static path to be visible
- Menu(Loc("Static", Link(List("static"), true, "/static/index"),
- "Static Content")))
+ Menu.i("Home") / "index",
+ Menu("lazy", "Lazy Loading") / "lazy",
+ Menu("parallel", "Parallel Rendering") / "parallel",
+ Menu("comet", "Comet & Ajax") / "comet",
+ Menu("wiring", "Wiring") / "wiring",
+ Menu("tempales", "Designer Friendly Templates") / "templates",
+ Menu("wizard", "Wizard") / "wizard",
+ Menu("security", "Security") / "security")
// set the sitemap. Note if you don't want access control for
// each page, just comment this line out.
View
43 src/main/scala/net/liftweb/seventhings/comet/Chat.scala
@@ -0,0 +1,43 @@
+package net.liftweb.seventhings
+package comet
+
+import net.liftweb._
+import http._
+import actor._
+import util._
+import Helpers._
+
+
+/**
+ * The comet chat component
+ */
+class Chat extends CometActor with CometListener {
+ private var msgs: Vector[String] = Vector() // private state
+
+ // register this component
+ def registerWith = ChatServer
+
+ // listen for messages
+ override def lowPriority = {
+ case v: Vector[String] => msgs = v; reRender()
+ }
+
+ // render the component
+ def render = ClearClearable & "li *" #> msgs
+}
+
+
+/**
+ * The chat server
+ */
+object ChatServer extends LiftActor with ListenerManager {
+ private var msgs = Vector("Welcome") // the private data
+
+ // what we send to listeners on update
+ def createUpdate = msgs
+
+ // handle incoming messages
+ override def lowPriority = {
+ case s: String => msgs :+= s; updateListeners()
+ }
+}
View
57 src/main/scala/net/liftweb/seventhings/lib/DependencyFactory.scala
@@ -1,57 +0,0 @@
-package code {
-package lib {
-
-import net.liftweb._
-import http._
-import util._
-import common._
-import _root_.java.util.Date
-
-/**
- * A factory for generating new instances of Date. You can create
- * factories for each kind of thing you want to vend in your application.
- * An example is a payment gateway. You can change the default implementation,
- * or override the default implementation on a session, request or current call
- * stack basis.
- */
-object DependencyFactory extends Factory {
- implicit object time extends FactoryMaker(Helpers.now _)
-
- /**
- * objects in Scala are lazily created. The init()
- * method creates a List of all the objects. This
- * results in all the objects getting initialized and
- * registering their types with the dependency injector
- */
- private def init() {
- List(time)
- }
- init()
-}
-
-/*
-/**
- * Examples of changing the implementation
- */
-sealed abstract class Changer {
- def changeDefaultImplementation() {
- DependencyFactory.time.default.set(() => new Date())
- }
-
- def changeSessionImplementation() {
- DependencyFactory.time.session.set(() => new Date())
- }
-
- def changeRequestImplementation() {
- DependencyFactory.time.request.set(() => new Date())
- }
-
- def changeJustForCall(d: Date) {
- DependencyFactory.time.doWith(d) {
- // perform some calculations here
- }
- }
-}
-*/
-}
-}
View
23 src/main/scala/net/liftweb/seventhings/snippet/ChatIn.scala
@@ -0,0 +1,23 @@
+package net.liftweb.seventhings
+package snippet
+
+import comet._
+
+import net.liftweb._
+import util._
+import Helpers._
+import http._
+import js.JsCmds._
+import js.JE._
+
+/**
+ * Handle input by sending the input line
+ * to the ChatServer
+ */
+object ChatIn {
+ def render =
+ "*" #> SHtml.onSubmit(s => {
+ ChatServer ! s // send the message
+ SetValById("chat_in", "") // clear the input box
+ })
+}
View
19 src/main/scala/net/liftweb/seventhings/snippet/FetchAd.scala
@@ -0,0 +1,19 @@
+package net.liftweb.seventhings
+package snippet
+
+import net.liftweb._
+import util._
+import Helpers._
+
+/**
+ * Fetch an ad from an ad server (takes about 1/2 second
+ */
+object FetchAd {
+ def render = {
+ // sleep for 1/2 second
+ Thread.sleep(500 millis)
+
+ // send the result back
+ ".ad" #> Thread.currentThread.getName
+ }
+}
View
16 src/main/scala/net/liftweb/seventhings/snippet/HelloWorld.scala
@@ -1,16 +0,0 @@
-package net.liftweb.seventhings
-package snippet
-
-import scala.xml.{NodeSeq, Text}
-import net.liftweb.util._
-import net.liftweb.common._
-import java.util.Date
-import code.lib._
-import Helpers._
-
-class HelloWorld {
- lazy val date: Box[Date] = DependencyFactory.inject[Date] // inject the date
-
- // replace the contents of the element with id "time" with the date
- def howdy = "#time *" #> date.map(_.toString)
-}
View
23 src/main/scala/net/liftweb/seventhings/snippet/LongTime.scala
@@ -0,0 +1,23 @@
+package net.liftweb.seventhings
+package snippet
+
+import net.liftweb._
+import util._
+import Helpers._
+
+/**
+ * Do something that takes a long time to do
+ */
+object LongTime {
+ def render = {
+ // capture the start time
+ val start = now
+
+ // sleep for up to 15 seconds
+ Thread.sleep(randomLong(15 seconds))
+
+ // send the result back
+ "#start" #> start.toString &
+ "#end" #> now.toString
+ }
+}
View
54 src/main/scala/net/liftweb/seventhings/snippet/ShowCode.scala
@@ -3,27 +3,65 @@ package snippet
import net.liftweb._
import http._
+import util._
+import Helpers._
import common._
import scala.xml.NodeSeq
+/**
+ * A snippet that will show part or all of a source file
+ * that's part of this project
+ */
object ShowCode {
def render: NodeSeq =
for {
- fileName <- S.attr("name") ?~
- "Name missing"
+ fileName <- S.attr("name") ?~ "Name missing"
- code <- LiftRules.loadResource("/src/scala"+fileName) ?~
+ code <- (LiftRules.loadResource("/src/scala"+fileName) or
+ LiftRules.loadResource("/src/webapp"+fileName)) ?~
("Cannot find "+fileName)
- } yield <div style="text-align: center"><tt>Listing: <a href={"https://github.com/lift/seventhings/tree/master/src/main/scala"+fileName}>{fileName}</a></tt>
- <pre class="listing brush: scala">{new String(code, "UTF-8")}</pre>
- </div>
+ } yield {
+ def calcBrush = if (fileName.endsWith(".html") ||
+ fileName.endsWith(".xml")) "html" else "scala"
+
+ def gitDir = if (fileName.endsWith(".html") ||
+ fileName.endsWith(".xml")) "webapp" else "scala"
+
+ val start = S.attr("start")
+ val end = S.attr("end")
+
+ val rawCode = (new String(code, "UTF-8").
+ split("""\n""").
+ toList.map(_.replace("\t", " "))).
+ dropWhile(s => start.map(st => s.indexOf(st) < 0) openOr false) match {
+ case Nil => Nil
+ case x :: xs => x :: xs.
+ takeWhile(s => end.map(en => s.indexOf(en) < 0) openOr true)
+ }
+
+
+
+
+ <div style="text-align: center"><tt>Listing:
+ <a href={"https://github.com/lift/seventhings/tree/master/src/main/"+
+ gitDir+fileName}>{fileName}</a></tt>
+ <pre class={"listing brush: "+calcBrush}>{rawCode.mkString("\n")}</pre>
+ </div>
+ }
private implicit def boxNStoNS(in: Box[NodeSeq]): NodeSeq =
in match {
case Full(ns) => ns
- case Failure(msg, _, _) => throw new SnippetExecutionException(msg)
- case _ => throw new SnippetExecutionException("Couldn't calculate value")
+ case Failure(msg, _, _) =>
+ <div style="display: block; margin: 8px; border: 2px solid red">
+ Failed to look up source: {msg}
+ </div>
+ case _ => <div style="display: block; margin: 8px; border: 2px solid red">
+ Failed to look up source
+ </div>
+
+
}
}
View
133 src/main/webapp/comet.html
@@ -0,0 +1,133 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type">
+ <title>Comet</title>
+ </head>
+ <body class="lift:content_id=main">
+ <div id="main" class="lift:surround?with=default;at=content">
+ <p>
+ <big>
+ <big>
+ One of Lift's key features in Comet (or server push)
+ support and associated Ajax support.
+ This page contains a multi-user chat application
+ with Comet (server-push) that updates the browser
+ when anyone sends a chat message. There's an
+ input box that allows the user to send a line into
+ the chat and that's done via an asynchronous call to
+ the server (the page is not reloaded as part of the call).
+ </big>
+ </big>
+ </p>
+
+ <div style="margin: 12px; border: 2px solid purple">
+ <ul class="lift:comet?type=Chat">
+ <li>Line 1</li>
+ <li class="clearable">Line 2</li>
+ <li class="clearable">Line 3</li>
+ </ul>
+
+ <form class="lift:Form.ajax">
+ <input class="lift:ChatIn" id="chat_in">
+ <input type="submit" value="Chat">
+ </form>
+ </div>
+
+ <big>
+ <big>
+ Let's take a look at the markup for the
+ Comet (server pushed list of chat items)
+ part of the application:
+ </big>
+ </big>
+ <pre class="lift:ShowCode?name=/comet.html;start=ul+;end=form">view code</pre>
+
+ <div>
+ <big>
+ <big>
+ We simply define the markup and mark the &lt;ul> tag
+ with a snippet invocation that loads the <tt>Chat</tt>
+ Comet component. Let's look at the <tt>Chat</tt>
+ component.
+ </big>
+ </big>
+ </div>
+
+ <pre class="lift:ShowCode?name=/net/liftweb/seventhings/comet/Chat.scala;start=**;end=**">view code</pre>
+
+
+ <div>
+ <big>
+ <big>
+ Once again, something that's hard or impossible
+ in other web frameworks is is trivial in Lift.
+ The developer doesn't worry about the plumbing of how
+ the browser polls for changes. By default Lift uses
+ long polling and multiplexes multiple comet components
+ into a single long poll, but when web sockets are
+ standardized, Lift will automatically support them
+ without requiring any code changes. The developer
+ focuses on the semantics of "when this changes on the
+ server, update the component" and Lift takes care of the
+ rest.
+ </big>
+ </big>
+ </div>
+
+ <br>
+
+ <div>
+ <big>
+ <big>
+ Note that you can attempt to attack the site with
+ a <a href="http://en.wikipedia.org/wiki/Cross-site_scripting">cross site scripting</a> attack (e.g., type <tt>&lt;script>alert('I ownz your machine')&lt/script></tt> into the chat box). You'll see that Lift
+ properly escapes the input without any developer intervention.
+ </big>
+ </big>
+ </div>
+
+ <br>
+
+ <div>
+ <big>
+ <big>
+ Let's take a look at the markup for the Ajax input form:
+ </big>
+ </big>
+ </div>
+
+ <pre class="lift:ShowCode?name=/comet.html;start=form;end=div">view code</pre>
+
+ <div>
+ <big>
+ <big>
+ We designate the <tt>form</tt> as Ajax with the <tt>Form.ajax</tt>
+ snippet. The text input invokes the <tt>ChatIn</tt>
+ snippet. Let's look at that snippet:
+ </big>
+ </big>
+ </div>
+
+ <pre class="lift:ShowCode?name=/net/liftweb/seventhings/snippet/ChatIn.scala;start=**">view code</pre>
+
+ <div>
+ <big>
+ <big>
+ Once again, we didn't need to do any plumbing. Lift
+ takes care of associating the behavior (sending the input
+ <tt>String</tt> to the <tt>ChatServer</tt>.) We didn't
+ set up routes or anything else. Further, this code is
+ resistant to replay attacks because the automatically
+ generated routes are dynamic and random and cannot
+ be predicted. And in case you're worried about doing
+ things via standard REST calls, Lift has
+ <a href="http://www.assembla.com/wiki/show/liftweb/REST_Web_Services">excellent REST support</a>.
+ </big>
+ </big>
+ </div>
+
+
+ </div>
+ </body>
+</html>
View
51 src/main/webapp/index.html
@@ -1,23 +1,34 @@
<!DOCTYPE html>
-<html>
- <head>
- <meta content="text/html; charset=UTF-8" http-equiv="content-type" />
- <title>Home</title>
- </head>
- <body class="lift:content_id=main">
- <div id="main" class="lift:surround?with=default;at=content">
- <h2>Welcome to your project!</h2>
- <p>
- <span class="lift:helloWorld.howdy">
- Welcome to your Lift app at <span id="time">Time goes here</span>
- </span>
+<html><head>
+
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type">
+ <title>Home</title>
- <div>
- Your code looks like:
- <pre lift="ShowCode?name=/net/liftweb/seventhings/snippet/ShowCode.scala">some code</pre>
- </div>
- </p>
- </div>
- </body>
-</html>
+
+</head><body class="lift:content_id=main">
+<div id="main" class="lift:surround?with=default;at=content">
+<p><big><big>Lift is the most powerful, most secure web framework
+available today.&nbsp; Yeah, it's a bold claim, but this site is going
+to show you 7 things that are hard or impossible in other web
+frameworks.&nbsp; These seven things are crucial to building and
+maintaining interactive, secure web sites.&nbsp; Each of the seven
+things has the source code right on the page.&nbsp; This will let you
+see why Lift is different and better.</big></big></p>
+<ol>
+ <li><big><span class="lift:Menu.item?name=lazy">Lazy Loading</span></big></li>
+ <li><big class="lift:Menu.item?name=parallel">Parallel page rendering</big></li>
+ <li><big>Comet and Ajax</big></li>
+ <li><big>Wiring -- declare interdepencies between page elements</big></li>
+ <li><big>Designer friendly templates</big></li>
+ <li><big>Wizard -- multipage input screens with full back-button
+support</big></li>
+ <li><big>Security</big><br>
+ </li>
+</ol>
+<big><big>Lift gives you the above Seven Things, plus your code is
+concise and maintainable, Lift's <a href="http://lift.la/lift-state-and-scaling">performance and
+scalability</a>, and all the benefits of deploying your application on
+battle-tested J/EE infrastructure.</big></big>
+</div>
+</body></html>
View
63 src/main/webapp/lazy.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type">
+ <title>Lazy</title>
+ </head>
+ <body class="lift:content_id=main">
+ <div id="main" class="lift:surround?with=default;at=content">
+ <p><big><big>Lift has built-in Lazy Loading.</big></big></p>
+ <p>
+ </p>
+ <div class="lift:LazyLoad">
+ <span class="lift:LongTime"> I started this computation at
+ <span id="start">start</span> and it
+ completed at <span id="end">end</span>.
+ </span>
+ </div>
+ <big><big>Notice the spinning icon above this paragraph?&nbsp; That
+ icon indicates that a computation is going on.&nbsp; The page was
+ rendered back to the browser and when the computation for that part of
+ screen real-estate is complete, the HTML will be delivered to the
+ browser.&nbsp; And, you can have more than one lazy loaded components
+ on the same page: </big></big><br>
+ <div class="lift:LazyLoad">
+ <div class="lift:LongTime"> I started this computation at <span id="start">start</span> and it
+ completed at <span id="end">end</span>. </div>
+ </div>
+ <div>
+ <big><big>How hard is the code? Here's the display part of the code:</big></big><br>
+ </div>
+ <pre class="lift:ShowCode?name=/lazy.html;start=LazyLoad;end=big">view code</pre>
+
+ <div>
+ <big>
+ <big>
+ That's not a lot of markup code. Just mark the
+ block of markup that's going to take a long time to
+ calculate. Lift takes care of the rest.
+ Let's look at the <a href="http://stable.simply.liftweb.net/#sec:Snippets">snippet</a> code:
+ </big>
+ </big>
+ </div>
+
+ <pre class="lift:ShowCode?name=/net/liftweb/seventhings/snippet/LongTime.scala;start=**">view code</pre>
+
+
+ <div>
+ <big>
+ <big>
+ Most other frameworks don't have lazy loading as part of
+ the core framework. It's generally very hard to do, but
+ because Lift has excellent
+ <span class="lift:Menu.item?name=comet">comet</span>
+ support, it's easy to "push" content from the
+ server to the browser.
+ </big>
+ </big>
+ </div>
+
+
+ </div>
+ </body>
+</html>
View
103 src/main/webapp/parallel.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta content="text/html; charset=UTF-8" http-equiv="content-type">
+ <title>Parallel</title>
+ </head>
+ <body class="lift:content_id=main">
+ <div id="main" class="lift:surround?with=default;at=content">
+ <p>
+ <big>
+ <big>
+ Lift can parallelize page rendering.
+ This is a huge win if you need to consult external
+ servers while rendering a page. If you need to
+ go out to 10 different ad servers and each one takes
+ half a second to respond, you want to run those in parallel:
+ </big>
+ </big>
+ </p>
+
+ <div class="lift:FetchAd?parallel=true"
+ style="margin: 12px; border: 1px solid blue">
+ Ad server #1: <span class="ad">The Ad</span>
+ </div>
+
+ <div class="lift:FetchAd?parallel=true" style="margin: 12px; border: 1px solid blue">
+ Ad server #2: <span class="ad">The Ad</span>
+ </div>
+
+ <div class="lift:FetchAd?parallel=true" style="margin: 12px; border: 1px solid blue">
+ Ad server #3: <span class="ad">The Ad</span>
+ </div>
+
+ <div class="lift:FetchAd?parallel=true" style="margin: 12px; border: 1px solid blue">
+ Ad server #4: <span class="ad">The Ad</span>
+ </div>
+
+ <div class="lift:FetchAd?parallel=true" style="margin: 12px; border: 1px solid blue">
+ Ad server #5: <span class="ad">The Ad</span>
+ </div>
+
+ <div class="lift:FetchAd?parallel=true" style="margin: 12px; border: 1px solid blue">
+ Ad server #6: <span class="ad">The Ad</span>
+ </div>
+
+ <div class="lift:FetchAd?parallel=true" style="margin: 12px; border: 1px solid blue">
+ Ad server #7: <span class="ad">The Ad</span>
+ </div>
+
+ <div class="lift:FetchAd?parallel=true" style="margin: 12px; border: 1px solid blue">
+ Ad server #8: <span class="ad">The Ad</span>
+ </div>
+
+ <div class="lift:FetchAd?parallel=true" style="margin: 12px; border: 1px solid blue">
+ Ad server #9: <span class="ad">The Ad</span>
+ </div>
+
+ <div class="lift:FetchAd?parallel=true" style="margin: 12px; border: 1px solid blue">
+ Ad server #10: <span class="ad">The Ad</span>
+ </div>
+
+
+ <big>
+ <big>
+ The markup is very simple:
+ </big>
+ </big>
+ <pre class="lift:ShowCode?name=/parallel.html;start=FetchAd;end=FetchAd">view code</pre>
+
+ <div>
+ <big>
+ <big>
+ That's not a lot of markup code. When you mark the
+ snippet as <tt>parallel</tt>, Lift forwards the
+ snippet calculation to another thread in a pool
+ of threads. Lift continues to evaluate the markup
+ on the page in parallel and recombines the results
+ before the page is sent to the browser.
+ Let's look at the <a href="http://stable.simply.liftweb.net/#sec:Snippets">snippet</a> code:
+ </big>
+ </big>
+ </div>
+
+ <pre class="lift:ShowCode?name=/net/liftweb/seventhings/snippet/FetchAd.scala;start=**">view code</pre>
+
+
+ <div>
+ <big>
+ <big>
+ Once again, something that's hard or impossible
+ in other web frameworks is is trivial in Lift.
+ How much does this matter? Is your page load time
+ slow because you're going out to lots and lots of
+ different back end servers while composing a page?
+ Lift makes that kind of work parallelizable.
+ </big>
+ </big>
+ </div>
+
+
+ </div>
+ </body>
+</html>
View
8 src/main/webapp/templates-hidden/default.html
@@ -1,9 +1,9 @@
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:lift="http://liftweb.net/">
+<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<meta name="description" content="" />
<meta name="keywords" content="" />
- <title class="lift:Menu.title">App: </title>
+ <title class="lift:Menu.title">Seven Things: </title>
<style class="lift:CSS.blueprint"></style>
<style class="lift:CSS.fancyType"></style>
@@ -64,7 +64,7 @@
<body>
<div class="container">
<div class="column span-12 last" style="text-align: right">
- <h1 class="alt">app<img alt="" id="ajax-loader" style="display:none; margin-bottom: 0px; margin-left: 5px" src="/images/ajax-loader.gif"></h1>
+ <h1 class="alt">Seven Things<img alt="" id="ajax-loader" style="display:none; margin-bottom: 0px; margin-left: 5px" src="/images/ajax-loader.gif"></h1>
</div>
<hr>
@@ -86,7 +86,7 @@ <h1 class="alt">app<img alt="" id="ajax-loader" style="display:none; margin-bott
<div class="column span-23 last" style="text-align: center">
<h4 class="alt">
<a href="http://www.liftweb.net"><i>Lift</i></a>
- is Copyright 2007-2010 WorldWide Conferencing, LLC.
+ is Copyright 2007-2011 WorldWide Conferencing, LLC.
Distributed under an Apache 2.0 License.</h4>
</div>
</div>
Please sign in to comment.
Something went wrong with that request. Please try again.