From 4703b54bd5e9e77bc840abbad5288e6b67c0efbc Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Wed, 8 Nov 2017 19:22:24 +0100 Subject: [PATCH 01/33] Mordernizing demo WIP This is a first commit on the way from 2.5 to Lift 3.1. At this point we have moved to 3.1 but some scala files is remed and awaits porting to 3.1. The new layout is now responsive and uses Bootstrap v4 but the look is stil simular to the original. ToDo's is plenty, see Notes.md for more info. --- combo/example/Notes.md | 8 + combo/example/build.sbt | 36 +++ combo/example/project/build.properties | 1 + combo/example/project/plugins.sbt | 3 + .../main/scala/bootstrap/liftweb/Boot.scala | 44 ++- .../net/liftweb/example/comet/Chat.scala | 196 ++++++------ .../liftweb/example/comet/ChatServer.scala | 2 +- .../net/liftweb/example/comet/Clock.scala | 18 +- .../liftweb/example/lib/StatelessHtml.scala | 2 +- .../net/liftweb/example/lib/WebServices.scala | 2 +- .../net/liftweb/example/lib/WikiStuff.scala | 36 +-- .../net/liftweb/example/lib/XmlServer.scala | 2 +- .../net/liftweb/example/snippet/Ajax.scala | 137 +++++---- .../{AjaxForm.scala => AjaxForm.scalaREM} | 0 .../{AllJson.scala => AllJson.scalaREM} | 0 .../snippet/{Count.scala => Count.scalaREM} | 0 .../{CountGame.scala => CountGame.scalaREM} | 2 +- .../{Database.scala => Database.scalaREM} | 0 ...DivSelector.scala => DivSelector.scalaREM} | 2 +- ...rmWithAjax.scala => FormWithAjax.scalaREM} | 2 +- .../{JSDialog.scala => JSDialog.scalaREM} | 0 .../snippet/{Json.scala => Json.scalaREM} | 0 .../net/liftweb/example/snippet/Misc.scala | 13 +- .../example/snippet/RuntimeStats.scala | 4 +- ...mpleWizard.scala => SimpleWizard.scalaREM} | 0 .../{Template.scala => Template.scalaREM} | 0 .../snippet/{Wizard.scala => Wizard.scalaREM} | 0 combo/example/src/main/webapp/ajax.html | 278 +++++++++--------- .../src/main/webapp/assets/css/app.css | 96 ++++++ combo/example/src/main/webapp/chat.html | 167 +++++------ combo/example/src/main/webapp/form_ajax.html | 2 +- combo/example/src/main/webapp/index.html | 107 ++++--- .../example/src/main/webapp/interactive.html | 128 ++++---- combo/example/src/main/webapp/lazy.html | 101 ++----- combo/example/src/main/webapp/menu/four.html | 2 +- combo/example/src/main/webapp/menu/index.html | 2 +- combo/example/src/main/webapp/menu/one.html | 2 +- combo/example/src/main/webapp/menu/three.html | 2 +- combo/example/src/main/webapp/menu/two.html | 2 +- .../example/src/main/webapp/menu/two_one.html | 2 +- .../example/src/main/webapp/menu/two_two.html | 2 +- .../main/webapp/templates-hidden/default.html | 41 ++- .../webapp/templates-hidden/default2.html | 94 ++++++ .../templates-hidden/stateless-default.html | 36 ++- .../main/webapp/templating/selectomatic.html | 4 +- 45 files changed, 918 insertions(+), 660 deletions(-) create mode 100644 combo/example/Notes.md create mode 100644 combo/example/build.sbt create mode 100644 combo/example/project/build.properties create mode 100644 combo/example/project/plugins.sbt rename combo/example/src/main/scala/net/liftweb/example/snippet/{AjaxForm.scala => AjaxForm.scalaREM} (100%) rename combo/example/src/main/scala/net/liftweb/example/snippet/{AllJson.scala => AllJson.scalaREM} (100%) rename combo/example/src/main/scala/net/liftweb/example/snippet/{Count.scala => Count.scalaREM} (100%) rename combo/example/src/main/scala/net/liftweb/example/snippet/{CountGame.scala => CountGame.scalaREM} (97%) rename combo/example/src/main/scala/net/liftweb/example/snippet/{Database.scala => Database.scalaREM} (100%) rename combo/example/src/main/scala/net/liftweb/example/snippet/{DivSelector.scala => DivSelector.scalaREM} (97%) rename combo/example/src/main/scala/net/liftweb/example/snippet/{FormWithAjax.scala => FormWithAjax.scalaREM} (97%) rename combo/example/src/main/scala/net/liftweb/example/snippet/{JSDialog.scala => JSDialog.scalaREM} (100%) rename combo/example/src/main/scala/net/liftweb/example/snippet/{Json.scala => Json.scalaREM} (100%) rename combo/example/src/main/scala/net/liftweb/example/snippet/{SimpleWizard.scala => SimpleWizard.scalaREM} (100%) rename combo/example/src/main/scala/net/liftweb/example/snippet/{Template.scala => Template.scalaREM} (100%) rename combo/example/src/main/scala/net/liftweb/example/snippet/{Wizard.scala => Wizard.scalaREM} (100%) create mode 100644 combo/example/src/main/webapp/assets/css/app.css create mode 100644 combo/example/src/main/webapp/templates-hidden/default2.html diff --git a/combo/example/Notes.md b/combo/example/Notes.md new file mode 100644 index 0000000..b9b3991 --- /dev/null +++ b/combo/example/Notes.md @@ -0,0 +1,8 @@ +**Notes of things needed to be addressed in this update** + +- currently a lot of scala files is renamed with .rem as they are not yet converted from Lif 2.5 to 3.1 +- comet.Chat uses net.liftweb.http.js.jquery.JqJsCmds.{AppendHtml} +which are no longer available. ==> chat history is lost? +- net.liftweb.example.snippet.Ajax uses net.liftmodules.widgets.autocomplete +AutoComplete function that needs Query-Migrate v1.4.1 as it used old jquery +stuff (jqurey.browser). diff --git a/combo/example/build.sbt b/combo/example/build.sbt new file mode 100644 index 0000000..ac8854d --- /dev/null +++ b/combo/example/build.sbt @@ -0,0 +1,36 @@ +enablePlugins(JettyPlugin) + +organization := "Lift" + +name := "demo" + +version := "0.5" + +scalaVersion := "2.12.2" + +scalacOptions ++= Seq("-deprecation", "-unchecked") + +resolvers ++= Seq("snapshots" at "https://oss.sonatype.org/content/repositories/snapshots", + "staging" at "https://oss.sonatype.org/content/repositories/staging", + "releases" at "https://oss.sonatype.org/content/repositories/releases" +) + +libraryDependencies ++= { + val liftVersion = "3.1.1" + Seq( + "net.liftweb" %% "lift-webkit" % liftVersion, + "net.liftweb" %% "lift-db" % liftVersion, + "net.liftweb" %% "lift-mapper" % liftVersion, + "net.liftmodules" %% "fobo_3.1" % "2.0-SNAPSHOT", + "net.liftmodules" %% "lift-jquery-module_3.1" % "2.10", + "net.liftmodules" %% "widgets_3.1" % "1.6.0-SNAPSHOT", + "net.liftmodules" %% "textile_3.1" % "1.4-SNAPSHOT", + "org.eclipse.jetty" % "jetty-webapp" % "8.1.7.v20120910" % "test", + "junit" % "junit" % "4.10" % "test", + "ch.qos.logback" % "logback-classic" % "1.2.3", + "org.specs2" %% "specs2-core" % "3.8.6" % "test", + "org.specs2" %% "specs2-matcher-extra" % "3.8.6" % "test", + "org.specs2" %% "specs2-junit" % "3.8.6" % "test", + "com.h2database" % "h2" % "1.3.167" + ) +} \ No newline at end of file diff --git a/combo/example/project/build.properties b/combo/example/project/build.properties new file mode 100644 index 0000000..369929b --- /dev/null +++ b/combo/example/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.0.2 \ No newline at end of file diff --git a/combo/example/project/plugins.sbt b/combo/example/project/plugins.sbt new file mode 100644 index 0000000..d83a9f7 --- /dev/null +++ b/combo/example/project/plugins.sbt @@ -0,0 +1,3 @@ +addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.0.1") + +addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.2") \ No newline at end of file diff --git a/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala b/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala index 1637964..2d8bfa6 100644 --- a/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala @@ -26,10 +26,18 @@ import Helpers._ import example._ import net.liftmodules.widgets.autocomplete._ +// import net.liftmodules.JQueryModule +import net.liftmodules.fobo import comet._ import model._ import lib._ -import net.liftweb.mapper.{DB, ConnectionManager, Schemifier, DefaultConnectionIdentifier, ConnectionIdentifier} +import net.liftweb.mapper.{ +DB, +ConnectionManager, +Schemifier, +DefaultConnectionIdentifier, +ConnectionIdentifier +} import _root_.java.sql.{Connection, DriverManager} import snippet._ @@ -74,10 +82,16 @@ class Boot { () => Full(RedirectResponse("/login/validate")) }) + /* TODO PPE REM + * to make stuff compile + */ + /* LiftRules.snippetDispatch.append(NamedPF("Template") (Map("Template" -> Template, "AllJson" -> AllJson))) + */ + //note old rem /* LiftRules.snippetDispatch.append { case "MyWizard" => MyWizard @@ -130,6 +144,28 @@ class Boot { LiftSession.onEndServicing LiftRules.setSiteMapFunc(MenuInfo.sitemap) + LiftRules.securityRules = () => { + SecurityRules( + content = Some( + ContentSecurityPolicy( + scriptSources = List(ContentSourceRestriction.UnsafeEval, + ContentSourceRestriction.UnsafeInline, + ContentSourceRestriction.Self), + styleSources = List(ContentSourceRestriction.UnsafeInline, + ContentSourceRestriction.Self) + ))) + } + + // FoBo init + fobo.Toolkit.init = fobo.Toolkit.JQuery224 + fobo.Toolkit.init = fobo.Toolkit.HighlightJS930 + fobo.Toolkit.init = fobo.Toolkit.FontAwesome470 //update to latest + fobo.Toolkit.init = fobo.Toolkit.Bootstrap400 + fobo.Toolkit.init = fobo.Toolkit.Popper1125 + fobo.Toolkit.init = fobo.Toolkit.JQueryMigrate141 + + //JQueryModule.InitParam.JQuery = JQueryModule.JQuery224 + //JQueryModule.init() ThingBuilder.boot() @@ -157,7 +193,7 @@ object RequestLogger extends Loggable { def endServicing(session: LiftSession, req: Req, response: Box[LiftResponse]) { val delta = millis - startTime.is - logger.info("At " + (timeNow) + " Serviced " + req.uri + " in " + (delta) + "ms " + ( + logger.info("At " + (now) + " Serviced " + req.uri + " in " + (delta) + "ms " + ( response.map(r => " Headers: " + r.toResponse.headers) openOr "" )) } @@ -323,7 +359,7 @@ object SessionInfoDumper extends LiftActor with Loggable { val rt = Runtime.getRuntime rt.gc - RuntimeStats.lastUpdate = timeNow + RuntimeStats.lastUpdate = now RuntimeStats.totalMem = rt.totalMemory RuntimeStats.freeMem = rt.freeMemory RuntimeStats.sessions = sessions.size @@ -346,7 +382,7 @@ object SessionInfoDumper extends LiftActor with Loggable { if (newKillCnt > 0) SessionChecker.killCnt = newKillCnt } - val dateStr: String = timeNow.toString + val dateStr: String = now.toString logger.info("[MEMDEBUG] At " + dateStr + " Number of open sessions: " + sessions.size) logger.info("[MEMDEBUG] Free Memory: " + pretty(RuntimeStats.freeMem)) logger.info("[MEMDEBUG] Total Memory: " + pretty(RuntimeStats.totalMem)) diff --git a/combo/example/src/main/scala/net/liftweb/example/comet/Chat.scala b/combo/example/src/main/scala/net/liftweb/example/comet/Chat.scala index 723f884..ba445a0 100644 --- a/combo/example/src/main/scala/net/liftweb/example/comet/Chat.scala +++ b/combo/example/src/main/scala/net/liftweb/example/comet/Chat.scala @@ -15,106 +15,106 @@ */ package net.liftweb { -package example { -package comet { - -import _root_.net.liftweb._ -import http._ -import common._ -import actor._ -import util._ -import Helpers._ -import _root_.scala.xml._ -import S._ -import SHtml._ -import js._ -import JsCmds._ -import JE._ -import net.liftweb.http.js.jquery.JqJsCmds.{AppendHtml} - -class Chat extends CometActor with CometListener { - private var userName = "" - private var chats: List[ChatLine] = Nil - - /* need these vals to be set eagerly, within the scope - * of Comet component constructor - */ - private val ulId = S.attr("ul_id") openOr "some_ul_id" - - private val liId = S.attr("li_id") - - private lazy val li = liId. - flatMap{ Helpers.findId(defaultHtml, _) } openOr NodeSeq.Empty - - private val inputId = Helpers.nextFuncName - - // handle an update to the chat lists - // by diffing the lists and then sending a partial update - // to the browser - override def lowPriority = { - case ChatServerUpdate(value) => { - val update = (value filterNot (chats contains)).reverse. - map(b => AppendHtml(ulId, line(b))) - - partialUpdate(update) - chats = value - } - } - - // render the input area by binding the - // appropriate dynamically generated code to the - // view supplied by the template - override lazy val fixedRender: Box[NodeSeq] = - S.runTemplate("_chat_fixed" :: Nil, - "postit" -> Helpers.evalElemWithId { - (id, elem) => - SHtml.onSubmit((s: String) => { - ChatServer ! ChatServerMsg(userName, s.trim) - SetValById(id, "") - })(elem) - } _) - - // display a line - private def line(c: ChatLine) = { - ("name=when" #> hourFormat(c.when) & - "name=who" #> c.user & - "name=body" #> c.msg)(li) - } - - // display a list of chats - private def displayList: NodeSeq = chats.reverse.flatMap(line) - - // render the whole list of chats - override def render = { - "name=chat_name" #> userName & - ("#"+ulId+" *") #> displayList - } - - // setup the component - override def localSetup { - askForName - super.localSetup - } - - // register as a listener - def registerWith = ChatServer - - // ask for the user's name - private def askForName { - if (userName.length == 0) { - ask(new AskName, "what's your username") { - case s: String if (s.trim.length > 2) => - userName = s.trim - reRender(true) - - case _ => + package example { + package comet { + + import _root_.net.liftweb._ + import http._ + import common._ + import actor._ + import util._ + import Helpers._ + import _root_.scala.xml._ + import S._ + import SHtml._ + import js._ + import JsCmds._ + import JE._ + import net.liftweb.http.js.jquery.JqJsCmds.{AppendHtml} + + class Chat extends CometActor with CometListener { + private var userName = "" + private var chats: List[ChatLine] = Nil + + /* need these vals to be set eagerly, within the scope + * of Comet component constructor + */ + private val ulId = S.attr("ul_id") openOr "some_ul_id" + + private val liId = S.attr("li_id") + + private lazy val li = liId.flatMap { Helpers.findId(defaultHtml, _) } openOr NodeSeq.Empty + + private val inputId = Helpers.nextFuncName + + // handle an update to the chat lists + // by diffing the lists and then sending a partial update + // to the browser + override def lowPriority = { + case ChatServerUpdate(value) => { + val update = (value filterNot (chats contains)).reverse.map(b => + AppendHtml(ulId, line(b))) + + partialUpdate(update) + chats = value + } + } + + // render the input area by binding the + // appropriate dynamically generated code to the + // view supplied by the template + override lazy val fixedRender: Box[NodeSeq] = + S.runTemplate( + "_chat_fixed" :: Nil, + "postit" -> Helpers.evalElemWithId { (id, elem) => + SHtml.onSubmit((s: String) => { + ChatServer ! ChatServerMsg(userName, s.trim) + SetValById(id, "") + })(elem) + } _ + ) + + // display a line + private def line(c: ChatLine) = { + ("name=when" #> hourFormat(c.when) & + "name=who" #> c.user & + "name=body" #> c.msg)(li) + } + + // display a list of chats + private def displayList: NodeSeq = chats.reverse.flatMap(line) + + // render the whole list of chats + override def render = { + "name=chat_name" #> userName & + ("#" + ulId + " *") #> displayList + } + + // setup the component + override def localSetup { askForName - reRender(false) + super.localSetup + } + + // register as a listener + def registerWith = ChatServer + + // ask for the user's name + private def askForName { + if (userName.length == 0) { + ask(new AskName, "what's your username") { + case s: String if (s.trim.length > 2) => + userName = s.trim + reRender(true) + + case _ => + askForName + reRender(false) + } + } + } + } } } - -} -} -} } diff --git a/combo/example/src/main/scala/net/liftweb/example/comet/ChatServer.scala b/combo/example/src/main/scala/net/liftweb/example/comet/ChatServer.scala index af68a88..13c05ba 100644 --- a/combo/example/src/main/scala/net/liftweb/example/comet/ChatServer.scala +++ b/combo/example/src/main/scala/net/liftweb/example/comet/ChatServer.scala @@ -37,7 +37,7 @@ object ChatServer extends LiftActor with ListenerManager { override def lowPriority = { case ChatServerMsg(user, msg) if msg.length > 0 => - chats ::= ChatLine(user, toHtml(msg), timeNow) + chats ::= ChatLine(user, toHtml(msg), now) chats = chats.take(50) updateListeners() diff --git a/combo/example/src/main/scala/net/liftweb/example/comet/Clock.scala b/combo/example/src/main/scala/net/liftweb/example/comet/Clock.scala index ab798f1..8ff53f5 100644 --- a/combo/example/src/main/scala/net/liftweb/example/comet/Clock.scala +++ b/combo/example/src/main/scala/net/liftweb/example/comet/Clock.scala @@ -27,22 +27,30 @@ import js._ import JsCmds._ import _root_.scala.xml.{Text, NodeSeq} -class ExampleClock (initSession: LiftSession, +class ExampleClock(initSession: LiftSession, initType: Box[String], initName: Box[String], initDefaultXml: NodeSeq, - initAttributes: Map[String, String]) extends CometActor { + initAttributes: Map[String, String]) + extends CometActor { // schedule a ping every 10 seconds so we redraw Schedule.schedule(this, Tick, 10 seconds) - def render = "#clock_time *" replaceWith timeNow.toString + def render = "#clock_time *" #> Text(now.toString) override def lowPriority = { case Tick => - partialUpdate(SetHtml("clock_time", Text(timeNow.toString))) + partialUpdate(SetHtml("clock_time", Text(now.toString))) Schedule.schedule(this, Tick, 10 seconds) } - initCometActor(initSession, initType, initName, initDefaultXml, initAttributes) + + val creationInfo = new CometCreationInfo(initType.orNull, + initName, + initDefaultXml, + initAttributes, + initSession) + + initCometActor(creationInfo) } case object Tick diff --git a/combo/example/src/main/scala/net/liftweb/example/lib/StatelessHtml.scala b/combo/example/src/main/scala/net/liftweb/example/lib/StatelessHtml.scala index 5f4aa0b..ae2ace5 100644 --- a/combo/example/src/main/scala/net/liftweb/example/lib/StatelessHtml.scala +++ b/combo/example/src/main/scala/net/liftweb/example/lib/StatelessHtml.scala @@ -31,7 +31,7 @@ object StatelessHtml { private val fakeSession = new LiftSession("/", "fakeSession", Empty) def render(req: Req)(): Box[LiftResponse] = { - val xml: Box[NodeSeq] = S.init(req, fakeSession) { + val xml: Box[NodeSeq] = S.init(Full(req), fakeSession) { S.runTemplate(List("stateless")) } xml.map(ns => XhtmlResponse(ns(0), Empty, Nil, Nil, 200, false)) diff --git a/combo/example/src/main/scala/net/liftweb/example/lib/WebServices.scala b/combo/example/src/main/scala/net/liftweb/example/lib/WebServices.scala index c8798e0..7bb5d84 100644 --- a/combo/example/src/main/scala/net/liftweb/example/lib/WebServices.scala +++ b/combo/example/src/main/scala/net/liftweb/example/lib/WebServices.scala @@ -79,7 +79,7 @@ object WebServices extends RestHelper { // a couple of helpful conversion rules implicit def userToInfo(u: User): UserInfo = - UserInfo(u.firstName, u.lastName, u.email) + UserInfo(u.firstName.get, u.lastName.get, u.email.get) implicit def uLstToInfo(ul: List[User]): List[UserInfo] = ul.map(userToInfo) diff --git a/combo/example/src/main/scala/net/liftweb/example/lib/WikiStuff.scala b/combo/example/src/main/scala/net/liftweb/example/lib/WikiStuff.scala index 60fc94c..d314c48 100644 --- a/combo/example/src/main/scala/net/liftweb/example/lib/WikiStuff.scala +++ b/combo/example/src/main/scala/net/liftweb/example/lib/WikiStuff.scala @@ -18,20 +18,20 @@ package net.liftweb { package example { package lib { -import _root_.net.liftweb._ -import net.liftmodules.textile._ -import common._ -import util._ -import Helpers._ -import http._ -import mapper._ -import sitemap._ -import Loc._ + import _root_.net.liftweb._ + import net.liftmodules.textile._ + import common._ + import util._ + import Helpers._ + import http._ + import mapper._ + import sitemap._ + import Loc._ -import example._ -import model._ + import example._ + import model._ -import scala.xml.{Text, NodeSeq} + import scala.xml.{Text, NodeSeq} /** * A wiki location @@ -114,17 +114,17 @@ object WikiStuff extends Loc[WikiLoc] { Full(NamedPF("Wiki Rewrite") { case RewriteRequest(ParsePath("wiki" :: "edit" :: page :: Nil, _, _,_), _, _) => - (RewriteResponse("wiki" :: Nil), WikiLoc(page, true)) + (RewriteResponse("wiki" :: Nil), Full(WikiLoc(page, true))) case RewriteRequest(ParsePath("wiki" :: page :: Nil, _, _,_), _, _) => - (RewriteResponse("wiki" :: Nil), WikiLoc(page, false)) + (RewriteResponse("wiki" :: Nil), Full(WikiLoc(page, false))) }) def showAll(in: NodeSeq): NodeSeq = WikiEntry.findAll(OrderBy(WikiEntry.name, Ascending)).flatMap(entry => -
{entry.name}
) +
{entry.name}
) def url(page: String) = createLink(WikiLoc(page, false)) @@ -134,7 +134,7 @@ object WikiStuff extends Loc[WikiLoc] { Show All Pages
{ val isNew = !r.saved_? - val pageName = r.name.is + val pageName = r.name.get val action = url(pageName) val message = if (isNew) @@ -165,9 +165,9 @@ object WikiStuff extends Loc[WikiLoc] { def displayRecord(entry: WikiEntry)(in: NodeSeq): NodeSeq = Show All Pages
- {TextileParser.toHtml(entry.entry, textileWriter)} + {TextileParser.toHtml(entry.entry.get, textileWriter)} -
Edit +
Edit
import TextileParser._ diff --git a/combo/example/src/main/scala/net/liftweb/example/lib/XmlServer.scala b/combo/example/src/main/scala/net/liftweb/example/lib/XmlServer.scala index 9727079..1d5e3e5 100644 --- a/combo/example/src/main/scala/net/liftweb/example/lib/XmlServer.scala +++ b/combo/example/src/main/scala/net/liftweb/example/lib/XmlServer.scala @@ -38,7 +38,7 @@ object XmlServer { def showStates(which: String)(): Box[XmlResponse] = Full(XmlResponse( - { + { which match { case "red" => diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Ajax.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Ajax.scala index 3148bca..f5c1739 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/Ajax.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Ajax.scala @@ -15,76 +15,95 @@ */ package net.liftweb { -package example { -package snippet { + package example { + package snippet { -import _root_.net.liftweb.http._ -import _root_.net.liftmodules.widgets.autocomplete._ -import S._ -import SHtml._ -import js._ -import js.jquery._ -import http.jquery._ -import JqJsCmds._ -import JsCmds._ -import common._ -import util._ -import Helpers._ -import _root_.scala.xml.{Text, NodeSeq} + import net.liftweb.http._ + import net.liftmodules.widgets.autocomplete._ + import S._ + import SHtml._ + import js._ + import js.jquery._ +//import http.jquery._ + import JqJsCmds._ + import JsCmds._ + import common._ + import util._ + import Helpers._ + import _root_.scala.xml.{Text, NodeSeq} -class Ajax extends Loggable { + class Ajax extends Loggable { - def sample(xhtml: NodeSeq): NodeSeq = { - // local state for the counter - var cnt = 0 + def sample(xhtml: NodeSeq): NodeSeq = { + // local state for the counter + var cnt = 0 - // get the id of some elements to update - val spanName: String = S.attr("id_name") openOr "cnt_id" - val msgName: String = S.attr("id_msgs") openOr "messages" + // get the id of some elements to update + val spanName: String = S.attr("id_name") openOr "cnt_id" + val msgName: String = S.attr("id_msgs") openOr "messages" - // build up an ajax tag to increment the counter - def doClicker(text: NodeSeq) = - a(() => {cnt = cnt + 1; SetHtml(spanName, Text( cnt.toString))}, text) + // build up an ajax tag to increment the counter + def doClicker(text: NodeSeq) = + a(() => { cnt = cnt + 1; SetHtml(spanName, Text(cnt.toString)) }, + text) - // create an ajax select box - def doSelect(msg: NodeSeq) = - ajaxSelect((1 to 50).toList.map(i => (i.toString, i.toString)), - Full(1.toString), - v => DisplayMessage(msgName, - bind("sel", msg, "number" -> Text(v)), - 5 seconds, 1 second)) + // create an ajax select box + def doSelect(msg: NodeSeq) = + ajaxSelect( + (1 to 50).toList.map(i => (i.toString, i.toString)), + Full(1.toString), + v => { + val selectBind = "#selNumber" #> Text(v) + DisplayMessage( + msgName, + {selectBind(msg)}, + 5 seconds, + 1 second + ) + } + ) - // build up an ajax text box - def doText(msg: NodeSeq) = - ajaxText("", v => DisplayMessage(msgName, - bind("text", msg, "value" -> Text(v)), - 4 seconds, 1 second)) + // build up an ajax text box + def doText(msg: NodeSeq) = + ajaxText( + "", + v => { + val textBind = "#textValue" #> Text(v) + DisplayMessage(msgName, + {textBind(msg)}, + 4 seconds, + 1 second) + } + ) + // bind the view to the functionality + val viewBind = { + "#ajaxClicker" #> doClicker _ & + "#ajaxSelect" #> doSelect _ & + "#ajaxText" #> doText _ & + "#ajaxAuto" #> AutoComplete("", buildQuery _, _ => ()) + } + viewBind(xhtml) + } + private def buildQuery(current: String, limit: Int): Seq[String] = { + logger.info( + "Checking on server side with " + current + " limit " + limit) + (1 to limit).map(n => current + "" + n) + } - // bind the view to the functionality - bind("ajax", xhtml, - "clicker" -> doClicker _, - "select" -> doSelect _, - "text" -> doText _, - "auto" -> AutoComplete("", buildQuery _, _ => ())) - } - - private def buildQuery(current: String, limit: Int): Seq[String] = { - logger.info("Checking on server side with "+current+" limit "+limit) - (1 to limit).map(n => current+""+n) - } - - def time = Text(timeNow.toString) + def time = Text(now.toString) - def buttonClick = { - import js.JE._ + def buttonClick = { + import js.JE._ - "* [onclick]" #> SHtml.ajaxCall(ValById("the_input"), - s => SetHtml("messages", - Text box is {s})) + "* [onclick]" #> SHtml.ajaxCall( + ValById("the_input"), + s => + SetHtml("bcmessages", + Latest Button click was with text box value {s})) + } + } + } } } -} -} -} diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/AjaxForm.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/AjaxForm.scalaREM similarity index 100% rename from combo/example/src/main/scala/net/liftweb/example/snippet/AjaxForm.scala rename to combo/example/src/main/scala/net/liftweb/example/snippet/AjaxForm.scalaREM diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/AllJson.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/AllJson.scalaREM similarity index 100% rename from combo/example/src/main/scala/net/liftweb/example/snippet/AllJson.scala rename to combo/example/src/main/scala/net/liftweb/example/snippet/AllJson.scalaREM diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Count.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Count.scalaREM similarity index 100% rename from combo/example/src/main/scala/net/liftweb/example/snippet/Count.scala rename to combo/example/src/main/scala/net/liftweb/example/snippet/Count.scalaREM diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/CountGame.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/CountGame.scalaREM similarity index 97% rename from combo/example/src/main/scala/net/liftweb/example/snippet/CountGame.scala rename to combo/example/src/main/scala/net/liftweb/example/snippet/CountGame.scalaREM index af8653c..e6c9c91 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/CountGame.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/CountGame.scalaREM @@ -30,7 +30,7 @@ import _root_.net.liftweb.common._ import _root_.net.liftweb.util._ -class CountGame extends StatefulSnippet { +class CountGameREM extends StatefulSnippet { val dispatch: DispatchIt = { case "run" if lastGuess == number => xhtml => win(chooseTemplate("choose", "win", xhtml)) diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Database.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Database.scalaREM similarity index 100% rename from combo/example/src/main/scala/net/liftweb/example/snippet/Database.scala rename to combo/example/src/main/scala/net/liftweb/example/snippet/Database.scalaREM diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/DivSelector.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/DivSelector.scalaREM similarity index 97% rename from combo/example/src/main/scala/net/liftweb/example/snippet/DivSelector.scala rename to combo/example/src/main/scala/net/liftweb/example/snippet/DivSelector.scalaREM index 127ec70..7ddceb0 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/DivSelector.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/DivSelector.scalaREM @@ -30,7 +30,7 @@ import _root_.net.liftweb.util._ import _root_.scala.xml.{NodeSeq, Text, Group} -class DivSelector extends StatefulSnippet { +class DivSelectorREM extends StatefulSnippet { private var whichDivs: Array[Boolean] = Array(true, true, true, true, true, true) def dispatch: DispatchIt = diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/FormWithAjax.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/FormWithAjax.scalaREM similarity index 97% rename from combo/example/src/main/scala/net/liftweb/example/snippet/FormWithAjax.scala rename to combo/example/src/main/scala/net/liftweb/example/snippet/FormWithAjax.scalaREM index a4b7e3d..c69d0bf 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/FormWithAjax.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/FormWithAjax.scalaREM @@ -29,7 +29,7 @@ import Helpers._ import scala.xml.NodeSeq -class FormWithAjax extends StatefulSnippet { +class FormWithAjaxREM extends StatefulSnippet { private var firstName = "" private var lastName = "" private val from = S.referer openOr "/" diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/JSDialog.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/JSDialog.scalaREM similarity index 100% rename from combo/example/src/main/scala/net/liftweb/example/snippet/JSDialog.scala rename to combo/example/src/main/scala/net/liftweb/example/snippet/JSDialog.scalaREM diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Json.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Json.scalaREM similarity index 100% rename from combo/example/src/main/scala/net/liftweb/example/snippet/Json.scala rename to combo/example/src/main/scala/net/liftweb/example/snippet/Json.scalaREM diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Misc.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Misc.scala index e3a0da8..77c879a 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/Misc.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Misc.scala @@ -69,8 +69,11 @@ class Misc { // when the delete button is pressed, call the "deleteUser" // function (which is a closure and bound the "user" object // in the current content) - bind("xmp", xhtml, "username" -> (user.firstName.is+" "+user.lastName.is), - "delete" -> submit("Delete", deleteUser _)) + val bindDelete = { + "#username" #> (user.firstName.get + " " + user.lastName.get) & + "#delete" #> submit("Delete", deleteUser _) + } + bindDelete(xhtml) // if the was no ID or the user couldn't be found, // display an error and redirect @@ -123,6 +126,10 @@ class Misc { /** * Bind the appropriate XHTML to the form */ + /*TODO PPE REM + * + * */ + /* def upload(xhtml: Group): NodeSeq = if (S.get_?) bind("ul", chooseTemplate("choose", "get", xhtml), "file_upload" -> fileUpload(ul => theUpload(Full(ul)))) @@ -132,7 +139,7 @@ class Misc { "length" -> theUpload.is.map(v => Text(v.file.length.toString)), "md5" -> theUpload.is.map(v => Text(hexEncode(md5(v.file)))) ); - + */ def lang = { "#lang" #> locale.getDisplayLanguage(locale) & diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/RuntimeStats.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/RuntimeStats.scala index 7912363..3dade09 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/RuntimeStats.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/RuntimeStats.scala @@ -37,9 +37,9 @@ object RuntimeStats extends DispatchSnippet { var sessions = 1 @volatile - var lastUpdate = timeNow + var lastUpdate = now - val startedAt = timeNow + val startedAt = now private def nf(in: Long): String = NumberFormat.getInstance.format(in) diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWizard.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWizard.scalaREM similarity index 100% rename from combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWizard.scala rename to combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWizard.scalaREM diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Template.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Template.scalaREM similarity index 100% rename from combo/example/src/main/scala/net/liftweb/example/snippet/Template.scala rename to combo/example/src/main/scala/net/liftweb/example/snippet/Template.scalaREM diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scalaREM similarity index 100% rename from combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scala rename to combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scalaREM diff --git a/combo/example/src/main/webapp/ajax.html b/combo/example/src/main/webapp/ajax.html index 1c738af..6b20bab 100644 --- a/combo/example/src/main/webapp/ajax.html +++ b/combo/example/src/main/webapp/ajax.html @@ -1,134 +1,150 @@ - - -
AJAX Samples
-
-
- - Click me to increase the count (currently 0) - -
- -
- - - You selected From the select box. - - -
- - You entered in the text box. - - -
- - An example of autocomplete with a server round trip to - calculate the autocomplete list - -
-
- -
-
- And each time an Ajax or Comet update is made to the page, - we update this span: - The time is -
+ + + + + Template + + +
+ + + + + + +
AJAX Samples
+
+
+
+ Click me to increase the count (currently 0) +
+ +
+ +
+
+ +
+ You selected '' from the select box. +
+ + + +
+
+ You entered '' in the text box. +
+ + +
+ + An example of autocomplete with a server round trip to + calculate the autocomplete list +
+ +
+
+ +
+
+ And each time an Ajax or Comet update is made to the page, + we update this span: + The time is +
-
- -

- Enter some text: - and -

- -
-
-
- - The Lift Scala code to render the controls: -
- - - - -
-class Ajax {
-
-  def sample(xhtml: NodeSeq): NodeSeq = {
-    // local state for the counter
-    var cnt = 0
-
-    // get the id of some elements to update
-    val spanName: String = S.attr("id_name") openOr "cnt_id"
-    val msgName: String = S.attr("id_msgs") openOr "messages"
-
-    // build up an ajax <a> tag to increment the counter
-    def doClicker(text: NodeSeq) =
-    a(() => {cnt = cnt + 1; SetHtml(spanName, Text( cnt.toString))}, text)
-
-    // create an ajax select box
-    def doSelect(msg: NodeSeq) =
-    ajaxSelect((1 to 50).toList.map(i => (i.toString, i.toString)),
-               Full(1.toString),
-               v => DisplayMessage(msgName,
-                                   bind("sel", msg, "number" -> Text(v)),
-                                   5 seconds, 1 second))
-
-    // build up an ajax text box
-    def doText(msg: NodeSeq) =
-    ajaxText("", v => DisplayMessage(msgName,
-                                     bind("text", msg, "value" -> Text(v)),
-                                     4 seconds, 1 second))
-
-
-
-    // bind the view to the functionality
-    bind("ajax", xhtml,
-         "clicker" -> doClicker _,
-         "select" -> doSelect _,
-         "text" -> doText _,
-         "auto" -> JqSHtml.autocomplete("", buildQuery _, _ => ()))
-  }
-
-  private def buildQuery(current: String, limit: Int): Seq[String] = {
-    Log.info("Checking on server side with "+current+" limit "+limit)
-    (1 to limit).map(n => current+""+n)
-  }
-
-  def time = Text(timeNow.toString)
-}
-
- - + viewBind(xhtml) + } + + private def buildQuery(current: String, limit: Int): Seq[String] = { + logger.info( + "Checking on server side with " + current + " limit " + limit) + (1 to limit).map(n => current + "" + n) + } + + def time = Text(now.toString) + + def buttonClick = { + import js.JE._ + + "* [onclick]" #> SHtml.ajaxCall( + ValById("the_input"), + s => SetHtml("bcmessages", + <i>Latest Button click was with text box value {s}</i>)) + } +} + + +
+ diff --git a/combo/example/src/main/webapp/assets/css/app.css b/combo/example/src/main/webapp/assets/css/app.css new file mode 100644 index 0000000..d8359ec --- /dev/null +++ b/combo/example/src/main/webapp/assets/css/app.css @@ -0,0 +1,96 @@ +.widget { + border:1px solid #ccc; + background:#f5f5f5; + padding:5px; + margin:10px 0 0 0; + font-size:8pt; +} + +.sidebar ul { + margin: 10px 0 0 0; + padding: 5px; +} + + +.sidebar ul li { + margin:0; + padding:0; + list-style:none; + border:1px solid #ccc; + border-bottom:none; +} +.sidebar > ul > li:last-child { + border-bottom:1px solid #ccc; +} + +.sidebar ul li ul li { + margin:0; + padding:0; + border-style: none; + list-style-position: inside; + padding-left: 10px; + +} + +.sidebar ul li ul li a { + display: inline; +} + +.sidebar ul li ul { + margin: 0; + border-bottom: none; +} + +.sidebar ul li ul li span { + display:inline; + padding:0px; +} + + +.sidebar ul li a { + display:block; + padding:3px; + text-indent:10px; + text-decoration:none; +} + +.sidebar ul li span { + display:block; + padding:3px; + text-indent:10px; + text-decoration:none; +} + + +.sidebar ul li a:hover { + background-color: #eee; +} + + +#poweredBy .alt { + color: #666; + font-family: "Warnock Pro", "Goudy Old Style","Palatino","Book Antiqua", Georgia, serif; + font-style: italic; + font-weight: normal; +} + +.lift_error { + color: red; +} + +.lift_warning { + color: yellow; +} + +.lift_notice { +} + +.main-border { + border-left: none; +} + +@media (min-width: 768px) { + .main-border { + border-left: 1px solid rgba(0, 0, 0, 0.1); + } +} \ No newline at end of file diff --git a/combo/example/src/main/webapp/chat.html b/combo/example/src/main/webapp/chat.html index 3171029..66b1692 100644 --- a/combo/example/src/main/webapp/chat.html +++ b/combo/example/src/main/webapp/chat.html @@ -1,21 +1,29 @@ - + + + + + Template + + +
+

The total chat app, including the ask/answer component for soliciting a name - comments, etc. is listed on this page. + comments, etc. is listed on this page. There is no special code to support AJAX/Comet (all the wrapping is done automatically by Lift).

@@ -26,134 +34,97 @@ Until the AskName comet widget provides a name, all rendering messages are forwarded to AskName. Here's the code for the "AskName":

- - -
-class Chat extends CometActor with CometListener {
-  private var userName = ""
-  private var chats: List[ChatLine] = Nil
+  

+class Chat extends CometActor with CometListener {
+  private var userName = ""
+  private var chats: List[ChatLine] = Nil
 
-  /* need these vals to be set eagerly, within the scope
+  /* need these vals to be set eagerly, within the scope
    * of Comet component constructor
-   */
-  private val ulId = S.attr("ul_id") openOr "some_ul_id"
+   */
+  private val ulId = S.attr("ul_id") openOr "some_ul_id"
 
-  private val liId = S.attr("li_id")
+  private val liId = S.attr("li_id")
 
-  private lazy val li = liId.
-  flatMap{ Helpers.findId(defaultXml, _) } openOr NodeSeq.Empty
+  private lazy val li = liId.
+  flatMap{ Helpers.findId(defaultHtml, _) } openOr NodeSeq.Empty
 
-  private val inputId = Helpers.nextFuncName
+  private val inputId = Helpers.nextFuncName
 
-  // handle an update to the chat lists
-  // by diffing the lists and then sending a partial update
-  // to the browser
-  override def lowPriority = {
-    case ChatServerUpdate(value) => {
-      val update = (value -- chats).reverse.
-      map(b => AppendHtml(ulId, line(b)))
+  // handle an update to the chat lists
+  // by diffing the lists and then sending a partial update
+  // to the browser
+  override def lowPriority = {
+    case ChatServerUpdate(value) => {
+      val update = (value filterNot (chats contains)).reverse.
+      map(b => AppendHtml(ulId, line(b)))
 
       partialUpdate(update)
       chats = value
     }
   }
 
-  // render the input area by binding the
-  // appropriate dynamically generated code to the
-  // view supplied by the template
-  override lazy val fixedRender: Box[NodeSeq] =
-    S.runTemplate("_chat_fixed" :: Nil,
-                  "postit" -> Helpers.evalElemWithId {
-                    (id, elem) => 
-                      SHtml.onSubmit((s: String) => {
+  // render the input area by binding the
+  // appropriate dynamically generated code to the
+  // view supplied by the template
+  override lazy val fixedRender: Box[NodeSeq] =
+    S.runTemplate("_chat_fixed" :: Nil,
+                  "postit" -> Helpers.evalElemWithId {
+                    (id, elem) =>
+                      SHtml.onSubmit((s: String) => {
                         ChatServer ! ChatServerMsg(userName, s.trim)
-                        SetValById(id, "")
+                        SetValById(id, "")
                       })(elem)
                   } _)
 
-  // display a line
-  private def line(c: ChatLine) = {
-    ("name=when" #> hourFormat(c.when) &
-     "name=who" #> c.user &
-     "name=body" #> c.msg)(li)
+  // display a line
+  private def line(c: ChatLine) = {
+    ("name=when" #> hourFormat(c.when) &
+     "name=who" #> c.user &
+     "name=body" #> c.msg)(li)
   }
 
-  // display a list of chats
-  private def displayList: NodeSeq = chats.reverse.flatMap(line)
+  // display a list of chats
+  private def displayList: NodeSeq = chats.reverse.flatMap(line)
 
-  // render the whole list of chats
-  override def render = {
-    "name=chat_name" #> userName &
-    ("#"+ulId+" *") #> displayList
+  // render the whole list of chats
+  override def render = {
+    "name=chat_name" #> userName &
+    ("#"+ulId+" *") #> displayList
   }
 
-  // setup the component
-  override def localSetup {
+  // setup the component
+  override def localSetup {
     askForName
-    super.localSetup
+    super.localSetup
   }
 
-  // register as a listener
-  def registerWith = ChatServer
+  // register as a listener
+  def registerWith = ChatServer
 
-  // ask for the user's name
-  private def askForName {
-    if (userName.length == 0) {
-      ask(new AskName, "what's your username") {
-        case s: String if (s.trim.length > 2) =>
+  // ask for the user's name
+  private def askForName {
+    if (userName.length == 0) {
+      ask(new AskName, "what's your username") {
+        case s: String if (s.trim.length > 2) =>
           userName = s.trim
-          reRender(true)
+          reRender(true)
 
-        case _ =>
+        case _ =>
           askForName
-          reRender(false)
+          reRender(false)
       }
     }
   }
 
 }
-
- +

This example demonstrates the power of Scala's Actors and Lift. With very few lines of code, we've got a complete AJAX/Comet app that has Seaside style Ask/Answer for building modal dialogs.

- +
+ \ No newline at end of file diff --git a/combo/example/src/main/webapp/form_ajax.html b/combo/example/src/main/webapp/form_ajax.html index 40085ab..6d64680 100644 --- a/combo/example/src/main/webapp/form_ajax.html +++ b/combo/example/src/main/webapp/form_ajax.html @@ -2,7 +2,7 @@
Forms with Ajax callback on form element blur

Enter your first and last name:
-
+ First Name:
Last Name:
diff --git a/combo/example/src/main/webapp/index.html b/combo/example/src/main/webapp/index.html index 9eb8f2c..f1d16d7 100644 --- a/combo/example/src/main/webapp/index.html +++ b/combo/example/src/main/webapp/index.html @@ -1,54 +1,63 @@ - -

Welcome to the Lift Web Framework

-
- Lift provides the best features for building interactive - web applications: -
    -
  • - Super simple and wicked powerful Ajax and - Comet coding. This lets you build more interactive, user-friendly - sites. -
  • -
  • - Amazingly concise code with the powerful type-safety - of Scala. This means - more time spent coding features - and less time writing tests or chasing - parameter mis-matches. -
  • -
  • - Runs on all standard JEE (Java) application servers - including Jetty, Tomcat, WebLogic, etc. - This means you get the performance, scalability and - compatibility of the best web infrastructure around. -
  • -
  • - Built-in security means more time focusing on your - application and less time being defensive about - parameter tampering, SQL injection, Cross Site Scripting and - other nasty attacks. -
  • -
-
-
-

Lift is built on Scala, a hybrid Functional and - O-O language that compiles code down to the Java Virtual Machine. - Scala code can call any Java code and make use of all Java classes. - Java code can call some Scala code. Lift applications are packaged as WAR files and - can be deployed on any - Servlet 2.4 engine (e.g., Tomcat 5.5.xx, Jetty 6.0, etc.) -

+ + + + + Template + + +
-
Lift code is as clean and brief as Rails, yet performs at least 6 - times faster and - is multithreaded. Additionally, because Scala is strongly typed, - the compiler - catches type errors. For example:
+

Welcome to the Lift Web Framework

+
+ Lift provides the best features for building interactive + web applications: +
    +
  • + Super simple and wicked powerful Ajax and + Comet coding. This lets you build more interactive, user-friendly + sites. +
  • +
  • + Amazingly concise code with the powerful type-safety + of Scala. This means + more time spent coding features + and less time writing tests or chasing + parameter mis-matches. +
  • +
  • + Runs on all standard JEE (Java) application servers + including Jetty, Tomcat, WebLogic, etc. + This means you get the performance, scalability and + compatibility of the best web infrastructure around. +
  • +
  • + Built-in security means more time focusing on your + application and less time being defensive about + parameter tampering, SQL injection, Cross Site Scripting and + other nasty attacks. +
  • +
+
+
+

Lift is built on Scala, a hybrid Functional and + O-O language that compiles code down to the Java Virtual Machine. + Scala code can call any Java code and make use of all Java classes. + Java code can call some Scala code. Lift applications are packaged as WAR files and + can be deployed on any + Servlet 2.4 engine (e.g., Tomcat 5.5.xx, Jetty 6.0, etc.) +

-
 User.find(By(User.email, "foo@bar.com")) // legal
+    
Lift code is as clean and brief as Rails, yet performs at least 6 + times faster and + is multithreaded. Additionally, because Scala is strongly typed, + the compiler + catches type errors. For example:
+ +
 User.find(By(User.email, "foo@bar.com")) // legal
  User.find(By(User.birthday, new Date("Jan 4, 1975"))) // legal
  User.find(By(User.birthday, "foo@bar.com")) // compiler error
-
-

Lift is an open source project distributed under an Apache License V2.0

- +
+

Lift is an open source project distributed under an Apache License V2.0

+
+ diff --git a/combo/example/src/main/webapp/interactive.html b/combo/example/src/main/webapp/interactive.html index b656645..fd0e0a1 100644 --- a/combo/example/src/main/webapp/interactive.html +++ b/combo/example/src/main/webapp/interactive.html @@ -1,81 +1,57 @@ - -

- Lift provides powerful facilities to build highly - interactive web applications. -

-

Ajax

-
- Lift has powerful set of Ajax features that all you to - create Ajax controls with as little as 1 line of code: - -
- 
-  ajaxButton("", s => {println("you said: "+s); SetHtml("place", <b>{s}</b>)})
-  
-  
- -
- -

Comet

-
- Lift supports Comet-style long polling with very little work on the - part of the developer. Here's the code the implements the clock - that you see in this demo: - - - - - -
-class Clock extends CometActor {
-  override def defaultPrefix = Full("clk")
-  // schedule a ping every 10 seconds so we redraw
-  ActorPing.schedule(this, Tick, 10 seconds) 
+
+
+
+    
+    Template
+
+
+
+

+ Lift provides powerful facilities to build highly + interactive web applications. +

+

Ajax

+
+ Lift has powerful set of Ajax features that all you to + create Ajax controls with as little as 1 line of code: + +
ajaxButton("", s => {println("you said: "+s); SetHtml("place", <b>{s}</b>)})
+ +
+ +

Comet

+
+ Lift supports Comet-style long polling with very little work on the + part of the developer. Here's the code the implements the clock + that you see in this demo: +
class ExampleClock(initSession: LiftSession,
+                   initType: Box[String],
+                   initName: Box[String],
+                   initDefaultXml: NodeSeq,
+                   initAttributes: Map[String, String])
+    extends CometActor {
+  // schedule a ping every 10 seconds so we redraw
+  Schedule.schedule(this, Tick, 10 seconds)
+
+  def render = "#clock_time *" #> Text(now.toString)
+
+  override def lowPriority = {
+    case Tick =>
+      partialUpdate(SetHtml("clock_time", Text(now.toString)))
+      Schedule.schedule(this, Tick, 10 seconds)
+  }
 
-  private lazy val spanId = uniqueId+"_timespan"
+  val creationInfo = new CometCreationInfo(initType.orNull,
+                                           initName,
+                                           initDefaultXml,
+                                           initAttributes,
+                                           initSession)
 
-  def render = bind("time" -> timeSpan)
+  initCometActor(creationInfo)
+}
 
-  def timeSpan = (<span id={spanId}>{timeNow}</span>)
+case object Tick
- override def lowPriority = { - case Tick => - partialUpdate(SetHtml(spanId, Text(timeNow.toString))) - ActorPing.schedule(this, Tick, 10 seconds) - } -} -
+
- + \ No newline at end of file diff --git a/combo/example/src/main/webapp/lazy.html b/combo/example/src/main/webapp/lazy.html index bc3e0e1..ed2585a 100644 --- a/combo/example/src/main/webapp/lazy.html +++ b/combo/example/src/main/webapp/lazy.html @@ -1,95 +1,52 @@ - + + + + + Template + + +

Lift supports lazy loading of a snippet. This is useful when a snippet may take a long time to render, but you want to return the page to the browser quickly.

- -
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
+
+

The Markup:

+
<div data-lift="lazy-load">
+    <div data-lift="LongTime"></div>
+  </div>
- - -
- -

The Markup:

-
-  <div>
-    <lift:lazy-load><div class="lift:LongTime"></div></lift:lazy-load>
-  </div>
-
- -

The Scala Code:

-
-object LongTime {
-  def render = {
-    val delay = 1000L + randomLong(10000)
+  

The Scala Code:

+
object LongTime {
+  def render = {
+    val delay = 1000L + randomLong(10000)
 
     Thread.sleep(delay)
-    
+
     <div>
     This thread delayed {delay / 1000L} seconds
     </div>
   }
-}
-
+}
- +
+ diff --git a/combo/example/src/main/webapp/menu/four.html b/combo/example/src/main/webapp/menu/four.html index 4e65fbc..c4e12dd 100644 --- a/combo/example/src/main/webapp/menu/four.html +++ b/combo/example/src/main/webapp/menu/four.html @@ -1,3 +1,3 @@ - + Submenu Page 4 diff --git a/combo/example/src/main/webapp/menu/index.html b/combo/example/src/main/webapp/menu/index.html index 3e7b0e1..d923127 100644 --- a/combo/example/src/main/webapp/menu/index.html +++ b/combo/example/src/main/webapp/menu/index.html @@ -1,3 +1,3 @@ - + Main Menu page... note the submenus on the menu bar diff --git a/combo/example/src/main/webapp/menu/one.html b/combo/example/src/main/webapp/menu/one.html index 39b3634..3b8b1d7 100644 --- a/combo/example/src/main/webapp/menu/one.html +++ b/combo/example/src/main/webapp/menu/one.html @@ -1,3 +1,3 @@ - + Submenu Page 1 diff --git a/combo/example/src/main/webapp/menu/three.html b/combo/example/src/main/webapp/menu/three.html index 832bf09..f4d46dd 100644 --- a/combo/example/src/main/webapp/menu/three.html +++ b/combo/example/src/main/webapp/menu/three.html @@ -1,3 +1,3 @@ - + Submenu Page 3 diff --git a/combo/example/src/main/webapp/menu/two.html b/combo/example/src/main/webapp/menu/two.html index 560db3e..9bab16b 100644 --- a/combo/example/src/main/webapp/menu/two.html +++ b/combo/example/src/main/webapp/menu/two.html @@ -1,3 +1,3 @@ - + Submenu Page 2 diff --git a/combo/example/src/main/webapp/menu/two_one.html b/combo/example/src/main/webapp/menu/two_one.html index 6d2f9e9..0d7c836 100644 --- a/combo/example/src/main/webapp/menu/two_one.html +++ b/combo/example/src/main/webapp/menu/two_one.html @@ -1,3 +1,3 @@ - + Submenu Page 2-1 diff --git a/combo/example/src/main/webapp/menu/two_two.html b/combo/example/src/main/webapp/menu/two_two.html index 03cb015..0b8a243 100644 --- a/combo/example/src/main/webapp/menu/two_two.html +++ b/combo/example/src/main/webapp/menu/two_two.html @@ -1,3 +1,3 @@ - + Submenu Page 2-2 diff --git a/combo/example/src/main/webapp/templates-hidden/default.html b/combo/example/src/main/webapp/templates-hidden/default.html index 9efdede..b62c7c2 100644 --- a/combo/example/src/main/webapp/templates-hidden/default.html +++ b/combo/example/src/main/webapp/templates-hidden/default.html @@ -6,10 +6,18 @@ Lift Web Framework: %*% - - - - + + + + + + + + + + + + - - - + + + + + + + + + + + + + + Template + -
-class AjaxForm {
-  var state = AjaxForm.state
-  var city = ""
+
+  
+
+ AJAX Form Update +
+
Choose city and state: +
+
+ State: + + City: + + +
+
+ Here's the code: +
+

+class AjaxForm {
+  var state = AjaxForm.state
+  var city = ""
 
-  private def cityChoice(state: String): Elem = {
-    val cities = AjaxForm.citiesFor(state)
-    val first = cities.head
-    // make the select "untrusted" because we might put new values
-    // in the select
-    untrustedSelect(cities.map(s => (s,s)), Full(first), city = _)
+  private def cityChoice(state: String): Elem = {
+    val cities = AjaxForm.citiesFor(state)
+    val first = cities.head
+    // make the select "untrusted" because we might put new values
+    // in the select
+    untrustedSelect(cities.map(s => (s, s)), Full(first), s => city = s)
   }
 
-  private def replace(state: String): JsCmd = {
-    val cities = AjaxForm.citiesFor(state)
-    val first = cities.head
-    ReplaceOptions("city_select", cities.map(s => (s,s)), Full(first))
+  private def replace(state: String): JsCmd = {
+    val cities = AjaxForm.citiesFor(state)
+    val first = cities.head
+    ReplaceOptions("city", cities.map(s => (s, s)), Full(first))
   }
 
-
-  // bind the view to the dynamic HTML
-  def show(xhtml: Group): NodeSeq = {
-    val (name, js) = ajaxCall(JE.JsRaw("this.value"),
-                              s => After(200, replace(s)))
-    bind("select", xhtml,
-         "state" -> select(AjaxForm.states.map(s => (s,s)),
-                           Full(state), state = _, "onchange" -> js.toJsCmd) %
-         (new PrefixedAttribute("lift", "gc", name, Null)),
-         "city" -> cityChoice(state) % ("id" -> "city_select"),
-         "submit" -> submit(?("Save"),
-                            () =>
-                            {S.notice("City: "+city+" State: "+state);
-                             redirectTo("/")}))
+  def render = {
+    "#state" #> ajaxSelect(AjaxForm.states.map(s => (s, s)), Full(state), { s =>
+      state = s; After(200, replace(state))
+    }) &
+      "#city" #> cityChoice(state) &
+      "type=submit" #> submit(?("Save"), () => {
+        S.notice("City: " + city + ", State: " + state); redirectTo("/")
+      })
   }
 }
-
+object AjaxForm { + val citiesAndStates = List( + "Alabama" -> "Birmingham", + "Alabama" -> "Huntsville", + : + : + "Wisconsin" -> "Milwaukee" + ) + + val states = citiesAndStates.map(_._1).distinct + + val state: String = states.head + + def citiesFor(state: String): List[String] = + citiesAndStates.filter(_._1 == state).map(_._2) +} + +
+ +
+ - - + \ No newline at end of file diff --git a/combo/example/src/main/webapp/ajax.html b/combo/example/src/main/webapp/ajax.html index 1c738af..2a44b4d 100644 --- a/combo/example/src/main/webapp/ajax.html +++ b/combo/example/src/main/webapp/ajax.html @@ -1,134 +1,158 @@ - - -
AJAX Samples
-
-
- - Click me to increase the count (currently 0) - -
- -
- - - You selected From the select box. - - -
- - You entered in the text box. - - -
- - An example of autocomplete with a server round trip to - calculate the autocomplete list - -
-
- -
-
- And each time an Ajax or Comet update is made to the page, - we update this span: - The time is + + + + + Template + + +
+ + + + + + +
AJAX Samples
+
+
+
+ Click me to increase the count (currently 0) +
+ +
+ +
+
+ +
+
+
+ You selected '' from the select box. +
+
+
+
+
+
+ You entered '' in the text box. +
+
+
+
+ +
+
+
+
+
+
+

+ And each time an Ajax or Comet update is made to the page, + we update this span: + The time is +

+ + + +
+ +
+ +
+
+ and +
- -
- -

- Enter some text: - and -

- -
-
-
- - The Lift Scala code to render the controls: -
- - - - -
-class Ajax {
-
-  def sample(xhtml: NodeSeq): NodeSeq = {
-    // local state for the counter
-    var cnt = 0
-
-    // get the id of some elements to update
-    val spanName: String = S.attr("id_name") openOr "cnt_id"
-    val msgName: String = S.attr("id_msgs") openOr "messages"
-
-    // build up an ajax <a> tag to increment the counter
-    def doClicker(text: NodeSeq) =
-    a(() => {cnt = cnt + 1; SetHtml(spanName, Text( cnt.toString))}, text)
-
-    // create an ajax select box
-    def doSelect(msg: NodeSeq) =
-    ajaxSelect((1 to 50).toList.map(i => (i.toString, i.toString)),
-               Full(1.toString),
-               v => DisplayMessage(msgName,
-                                   bind("sel", msg, "number" -> Text(v)),
-                                   5 seconds, 1 second))
-
-    // build up an ajax text box
-    def doText(msg: NodeSeq) =
-    ajaxText("", v => DisplayMessage(msgName,
-                                     bind("text", msg, "value" -> Text(v)),
-                                     4 seconds, 1 second))
-
-
-
-    // bind the view to the functionality
-    bind("ajax", xhtml,
-         "clicker" -> doClicker _,
-         "select" -> doSelect _,
-         "text" -> doText _,
-         "auto" -> JqSHtml.autocomplete("", buildQuery _, _ => ()))
+      viewBind(xhtml)
+    }
+
+    private def buildQuery(current: String, limit: Int): Seq[String] = {
+      logger.info(
+        "Checking on server side with " + current + " limit " + limit)
+      (1 to limit).map(n => current + "" + n)
+    }
+
+    def time = Text(now.toString)
+
+    def buttonClick = {
+      import js.JE._
+
+      "* [onclick]" #> SHtml.ajaxCall(
+        ValById("the_input"),
+        s =>
+          SetHtml("bcmessages",
+                  <i>Latest Button click was with text box value '{s}'</i>))
+    }
   }
+    
- private def buildQuery(current: String, limit: Int): Seq[String] = { - Log.info("Checking on server side with "+current+" limit "+limit) - (1 to limit).map(n => current+""+n) - } - def time = Text(timeNow.toString) -} - - - +
+ + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/assets/css/app.css b/combo/example/src/main/webapp/assets/css/app.css new file mode 100644 index 0000000..a97fb21 --- /dev/null +++ b/combo/example/src/main/webapp/assets/css/app.css @@ -0,0 +1,127 @@ +.widget { + border:1px solid #ccc; + background:#f5f5f5; + padding:5px; + margin:10px 0 0 0; + font-size:8pt; +} + +.sidebar ul { + /*margin: 10px 0 0 0;*/ + padding: 5px; +} + + +.sidebar ul li { + margin:0; + padding:0; + list-style:none; + border:1px solid #ccc; + border-bottom:none; +} +.sidebar > ul > li:last-child { + border-bottom:1px solid #ccc; +} + +.sidebar ul li ul li { + margin:0; + padding:0; + border-style: none; + list-style-position: inside; + padding-left: 10px; + +} + +.sidebar ul li ul li a { + display: inline; +} + +.sidebar ul li ul { + margin: 0; + border-bottom: none; +} + +.sidebar ul li ul li span { + display:inline; + padding:0px; +} + + +.sidebar ul li a { + display:block; + padding:3px; + text-indent:10px; + text-decoration:none; +} + +.sidebar ul li span { + display:block; + padding:3px; + text-indent:10px; + text-decoration:none; +} + + +.sidebar ul li a:hover { + background-color: #eee; +} + + +#poweredBy .alt { + color: #666; + font-family: "Warnock Pro", "Goudy Old Style","Palatino","Book Antiqua", Georgia, serif; + font-style: italic; + font-weight: normal; +} + +.lift_error { + color: red; +} + +.lift_warning { + color: yellow; +} + +.lift_notice { +} + +.main-border { + border-left: none; +} + +/*Lift stuff*/ +/*Lift notice*/ +#wrappedNotice #lift__noticesContainer___notice ul, +#wrappedNotice #lift__noticesContainer___warning ul, +#wrappedNotice #lift__noticesContainer___error ul +{ + padding:5px; + list-style-type: none; +} + +#wrappedNotice #lift__noticesContainer___notice { + border-radius: 5px; + background-color: #D9EDF7; + border-color: #BCE8F1; + color: #3A87AD; +} + +#wrappedNotice #lift__noticesContainer___warning { + border-radius: 5px; + background-color: #FCF8E3; + border-color: #FBEED5; + color: #C09853; +} + +#wrappedNotice #lift__noticesContainer___error { + border-radius: 5px; + background-color: #F2DEDE; + border-color: #EED3D7; + color: #B94A48; +} + +@media (min-width: 768px) { + .main-border { + border-left: 1px solid rgba(0, 0, 0, 0.1); + } +} \ No newline at end of file diff --git a/combo/example/src/main/webapp/chat.html b/combo/example/src/main/webapp/chat.html index 3171029..f654cf3 100644 --- a/combo/example/src/main/webapp/chat.html +++ b/combo/example/src/main/webapp/chat.html @@ -1,21 +1,29 @@ - + + + + + Template + + +
+

The total chat app, including the ask/answer component for soliciting a name - comments, etc. is listed on this page. + comments, etc. is listed on this page. There is no special code to support AJAX/Comet (all the wrapping is done automatically by Lift).

@@ -26,134 +34,99 @@ Until the AskName comet widget provides a name, all rendering messages are forwarded to AskName. Here's the code for the "AskName":

- - -
-class Chat extends CometActor with CometListener {
-  private var userName = ""
-  private var chats: List[ChatLine] = Nil
+  

+class Chat extends CometActor with CometListener {
+  private var userName = ""
+  private var chats: List[ChatLine] = Nil
 
-  /* need these vals to be set eagerly, within the scope
+  /* need these vals to be set eagerly, within the scope
    * of Comet component constructor
-   */
-  private val ulId = S.attr("ul_id") openOr "some_ul_id"
+   */
+  private val ulId = S.attr("ul_id") openOr "some_ul_id"
 
-  private val liId = S.attr("li_id")
+  private val liId = S.attr("li_id")
 
-  private lazy val li = liId.
-  flatMap{ Helpers.findId(defaultXml, _) } openOr NodeSeq.Empty
+  private lazy val li = liId.
+  flatMap{ Helpers.findId(defaultHtml, _) } openOr NodeSeq.Empty
 
-  private val inputId = Helpers.nextFuncName
+  private val inputId = Helpers.nextFuncName
 
-  // handle an update to the chat lists
-  // by diffing the lists and then sending a partial update
-  // to the browser
-  override def lowPriority = {
-    case ChatServerUpdate(value) => {
-      val update = (value -- chats).reverse.
-      map(b => AppendHtml(ulId, line(b)))
+  // handle an update to the chat lists
+  // by diffing the lists and then sending a partial update
+  // to the browser
+  override def lowPriority = {
+    case ChatServerUpdate(value) => {
+      val update = (value filterNot (chats contains)).reverse.
+      map(b => AppendHtml(ulId, line(b)))
 
       partialUpdate(update)
       chats = value
     }
   }
 
-  // render the input area by binding the
-  // appropriate dynamically generated code to the
-  // view supplied by the template
-  override lazy val fixedRender: Box[NodeSeq] =
-    S.runTemplate("_chat_fixed" :: Nil,
-                  "postit" -> Helpers.evalElemWithId {
-                    (id, elem) => 
-                      SHtml.onSubmit((s: String) => {
+  // render the input area by binding the
+  // appropriate dynamically generated code to the
+  // view supplied by the template
+  override lazy val fixedRender: Box[NodeSeq] =
+    S.runTemplate("_chat_fixed" :: Nil,
+                  "postit" -> Helpers.evalElemWithId {
+                    (id, elem) =>
+                      SHtml.onSubmit((s: String) => {
                         ChatServer ! ChatServerMsg(userName, s.trim)
-                        SetValById(id, "")
+                        SetValById(id, "")
                       })(elem)
                   } _)
 
-  // display a line
-  private def line(c: ChatLine) = {
-    ("name=when" #> hourFormat(c.when) &
-     "name=who" #> c.user &
-     "name=body" #> c.msg)(li)
+  // display a line
+  private def line(c: ChatLine) = {
+    ("name=when" #> hourFormat(c.when) &
+     "name=who" #> c.user &
+     "name=body" #> c.msg)(li)
   }
 
-  // display a list of chats
-  private def displayList: NodeSeq = chats.reverse.flatMap(line)
+  // display a list of chats
+  private def displayList: NodeSeq = chats.reverse.flatMap(line)
 
-  // render the whole list of chats
-  override def render = {
-    "name=chat_name" #> userName &
-    ("#"+ulId+" *") #> displayList
+  // render the whole list of chats
+  override def render = {
+    "name=chat_name" #> userName &
+    ("#"+ulId+" *") #> displayList
   }
 
-  // setup the component
-  override def localSetup {
+  // setup the component
+  override def localSetup {
     askForName
-    super.localSetup
+    super.localSetup
   }
 
-  // register as a listener
-  def registerWith = ChatServer
+  // register as a listener
+  def registerWith = ChatServer
 
-  // ask for the user's name
-  private def askForName {
-    if (userName.length == 0) {
-      ask(new AskName, "what's your username") {
-        case s: String if (s.trim.length > 2) =>
+  // ask for the user's name
+  private def askForName {
+    if (userName.length == 0) {
+      ask(new AskName, "what's your username") {
+        case s: String if (s.trim.length > 2) =>
           userName = s.trim
-          reRender(true)
+          reRender(true)
 
-        case _ =>
+        case _ =>
           askForName
-          reRender(false)
+          reRender(false)
       }
     }
   }
 
 }
-
- +

This example demonstrates the power of Scala's Actors and Lift. With very few lines of code, we've got a complete AJAX/Comet app that has Seaside style Ask/Answer for building modal dialogs.

- +
+ + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/form_ajax.html b/combo/example/src/main/webapp/form_ajax.html index 40085ab..79d44c1 100644 --- a/combo/example/src/main/webapp/form_ajax.html +++ b/combo/example/src/main/webapp/form_ajax.html @@ -1,10 +1,89 @@ - -
Forms with Ajax callback on form element blur
-
- Enter your first and last name:
- - First Name:
- Last Name:
- - -
+ + + + + + Template + + + +
+
+ Forms with Ajax callback on form element blur +
+
Enter your first and last name: +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ + + +
+
+

+ Here's the code: +

+

+class FormWithAjax extends StatefulSnippet {
+  private var firstName = ""
+  private var lastName = ""
+  private val from = S.referer openOr "/"
+
+  def dispatch = {
+    case _ => render
+  }
+
+  def render = {
+
+    def validate() {
+      (firstName.length, lastName.length) match {
+        case (f, n) if f < 2 && n < 2 =>
+          S.error("First and last names too short")
+        case (f, _) if f < 2 => S.error("First name too short")
+        case (_, n) if n < 2 => S.error("Last name too short")
+        case _ => {
+          S.notice("Ajax form says Thanks!")
+          S.redirectTo(from)
+        }
+      }
+    }
+
+    "#first" #> textAjaxTest(firstName,
+                             s => firstName = s,
+                             s => {
+                               S.notice("First name " + s); Noop
+                             },
+                             "class" -> "form-control",
+                             "type" -> "text",
+                             "placeholder" -> "First name") &
+      "#last" #> textAjaxTest(lastName,
+                              s => lastName = s,
+                              s => {
+                                S.notice("Last name " + s); Noop
+                              },
+                              "class" -> "form-control",
+                              "type" -> "text",
+                              "placeholder" -> "Last name") &
+      "type=submit" #> submit("Send", validate _)
+  }
+}
+      
+
+ + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/index.html b/combo/example/src/main/webapp/index.html index 9eb8f2c..630ba38 100644 --- a/combo/example/src/main/webapp/index.html +++ b/combo/example/src/main/webapp/index.html @@ -1,54 +1,65 @@ - -

Welcome to the Lift Web Framework

-
- Lift provides the best features for building interactive - web applications: -
    -
  • - Super simple and wicked powerful Ajax and - Comet coding. This lets you build more interactive, user-friendly - sites. -
  • -
  • - Amazingly concise code with the powerful type-safety - of Scala. This means - more time spent coding features - and less time writing tests or chasing - parameter mis-matches. -
  • -
  • - Runs on all standard JEE (Java) application servers - including Jetty, Tomcat, WebLogic, etc. - This means you get the performance, scalability and - compatibility of the best web infrastructure around. -
  • -
  • - Built-in security means more time focusing on your - application and less time being defensive about - parameter tampering, SQL injection, Cross Site Scripting and - other nasty attacks. -
  • -
-
-
-

Lift is built on Scala, a hybrid Functional and - O-O language that compiles code down to the Java Virtual Machine. - Scala code can call any Java code and make use of all Java classes. - Java code can call some Scala code. Lift applications are packaged as WAR files and - can be deployed on any - Servlet 2.4 engine (e.g., Tomcat 5.5.xx, Jetty 6.0, etc.) -

+ + + + + Template + + +
-
Lift code is as clean and brief as Rails, yet performs at least 6 - times faster and - is multithreaded. Additionally, because Scala is strongly typed, - the compiler - catches type errors. For example:
+

Welcome to the Lift Web Framework

+
+ Lift provides the best features for building interactive + web applications: +
    +
  • + Super simple and wicked powerful Ajax and + Comet coding. This lets you build more interactive, user-friendly + sites. +
  • +
  • + Amazingly concise code with the powerful type-safety + of Scala. This means + more time spent coding features + and less time writing tests or chasing + parameter mis-matches. +
  • +
  • + Runs on all standard JEE (Java) application servers + including Jetty, Tomcat, WebLogic, etc. + This means you get the performance, scalability and + compatibility of the best web infrastructure around. +
  • +
  • + Built-in security means more time focusing on your + application and less time being defensive about + parameter tampering, SQL injection, Cross Site Scripting and + other nasty attacks. +
  • +
+
+
+

Lift is built on Scala, a hybrid Functional and + O-O language that compiles code down to the Java Virtual Machine. + Scala code can call any Java code and make use of all Java classes. + Java code can call some Scala code. Lift applications are packaged as WAR files and + can be deployed on any + Servlet 2.4 engine (e.g., Tomcat 5.5.xx, Jetty 6.0, etc.) +

-
 User.find(By(User.email, "foo@bar.com")) // legal
+    
Lift code is as clean and brief as Rails, yet performs at least 6 + times faster and + is multithreaded. Additionally, because Scala is strongly typed, + the compiler + catches type errors. For example:
+ +
 User.find(By(User.email, "foo@bar.com")) // legal
  User.find(By(User.birthday, new Date("Jan 4, 1975"))) // legal
  User.find(By(User.birthday, "foo@bar.com")) // compiler error
-
-

Lift is an open source project distributed under an Apache License V2.0

- +
+

Lift is an open source project distributed under an Apache License V2.0

+
+ + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/interactive.html b/combo/example/src/main/webapp/interactive.html index b656645..3f7c817 100644 --- a/combo/example/src/main/webapp/interactive.html +++ b/combo/example/src/main/webapp/interactive.html @@ -1,81 +1,56 @@ - -

- Lift provides powerful facilities to build highly - interactive web applications. -

-

Ajax

-
- Lift has powerful set of Ajax features that all you to - create Ajax controls with as little as 1 line of code: - -
- 
-  ajaxButton("", s => {println("you said: "+s); SetHtml("place", <b>{s}</b>)})
-  
-  
- -
- -

Comet

-
- Lift supports Comet-style long polling with very little work on the - part of the developer. Here's the code the implements the clock - that you see in this demo: - - - - - -
-class Clock extends CometActor {
-  override def defaultPrefix = Full("clk")
-  // schedule a ping every 10 seconds so we redraw
-  ActorPing.schedule(this, Tick, 10 seconds) 
+
+
+
+
+    
+    Template
+
+
+
+    
+

+ Lift provides powerful facilities to build highly interactive web applications. +

+
+

Ajax

+ Lift has powerful set of Ajax features that all you to create Ajax controls with as little as 1 line of code: +
ajaxButton("", s => {println("you said: "+s); SetHtml("place", <b>{s}</b>)})
+
+
+

Comet

+ Lift supports Comet-style long polling with very little work on the part of the developer. Here's the code the implements + the clock that you see in this demo: +

+class ExampleClock(initSession: LiftSession,
+                   initType: Box[String],
+                   initName: Box[String],
+                   initDefaultXml: NodeSeq,
+                   initAttributes: Map[String, String])
+    extends CometActor {
+  // schedule a ping every 10 seconds so we redraw
+  Schedule.schedule(this, Tick, 10 seconds)
+
+  def render = "#clock_time *" #> Text(now.toString)
+
+  override def lowPriority = {
+    case Tick =>
+      partialUpdate(SetHtml("clock_time", Text(now.toString)))
+      Schedule.schedule(this, Tick, 10 seconds)
+  }
 
-  private lazy val spanId = uniqueId+"_timespan"
+  val creationInfo = new CometCreationInfo(initType.orNull,
+                                           initName,
+                                           initDefaultXml,
+                                           initAttributes,
+                                           initSession)
 
-  def render = bind("time" -> timeSpan)
+  initCometActor(creationInfo)
+}
 
-  def timeSpan = (<span id={spanId}>{timeNow}</span>)
+case object Tick
+            
+
+
+ - override def lowPriority = { - case Tick => - partialUpdate(SetHtml(spanId, Text(timeNow.toString))) - ActorPing.schedule(this, Tick, 10 seconds) - } -} -
-
-
+ \ No newline at end of file diff --git a/combo/example/src/main/webapp/json.html b/combo/example/src/main/webapp/json.html index 07540e3..ad3cfbc 100644 --- a/combo/example/src/main/webapp/json.html +++ b/combo/example/src/main/webapp/json.html @@ -1,8 +1,17 @@ - + + + + + + Template + + + +
JSON Samples

- +


- +
@@ -68,4 +77,7 @@ - +
+ + + diff --git a/combo/example/src/main/webapp/lazy.html b/combo/example/src/main/webapp/lazy.html index bc3e0e1..ed2585a 100644 --- a/combo/example/src/main/webapp/lazy.html +++ b/combo/example/src/main/webapp/lazy.html @@ -1,95 +1,52 @@ - + + + + + Template + + +

Lift supports lazy loading of a snippet. This is useful when a snippet may take a long time to render, but you want to return the page to the browser quickly.

- -
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
+
+

The Markup:

+
<div data-lift="lazy-load">
+    <div data-lift="LongTime"></div>
+  </div>
- - -
- -

The Markup:

-
-  <div>
-    <lift:lazy-load><div class="lift:LongTime"></div></lift:lazy-load>
-  </div>
-
- -

The Scala Code:

-
-object LongTime {
-  def render = {
-    val delay = 1000L + randomLong(10000)
+  

The Scala Code:

+
object LongTime {
+  def render = {
+    val delay = 1000L + randomLong(10000)
 
     Thread.sleep(delay)
-    
+
     <div>
     This thread delayed {delay / 1000L} seconds
     </div>
   }
-}
-
+}
- +
+ diff --git a/combo/example/src/main/webapp/menu/four.html b/combo/example/src/main/webapp/menu/four.html index 4e65fbc..c4e12dd 100644 --- a/combo/example/src/main/webapp/menu/four.html +++ b/combo/example/src/main/webapp/menu/four.html @@ -1,3 +1,3 @@ - + Submenu Page 4 diff --git a/combo/example/src/main/webapp/menu/index.html b/combo/example/src/main/webapp/menu/index.html index 3e7b0e1..d923127 100644 --- a/combo/example/src/main/webapp/menu/index.html +++ b/combo/example/src/main/webapp/menu/index.html @@ -1,3 +1,3 @@ - + Main Menu page... note the submenus on the menu bar diff --git a/combo/example/src/main/webapp/menu/one.html b/combo/example/src/main/webapp/menu/one.html index 39b3634..3b8b1d7 100644 --- a/combo/example/src/main/webapp/menu/one.html +++ b/combo/example/src/main/webapp/menu/one.html @@ -1,3 +1,3 @@ - + Submenu Page 1 diff --git a/combo/example/src/main/webapp/menu/three.html b/combo/example/src/main/webapp/menu/three.html index 832bf09..f4d46dd 100644 --- a/combo/example/src/main/webapp/menu/three.html +++ b/combo/example/src/main/webapp/menu/three.html @@ -1,3 +1,3 @@ - + Submenu Page 3 diff --git a/combo/example/src/main/webapp/menu/two.html b/combo/example/src/main/webapp/menu/two.html index 560db3e..9bab16b 100644 --- a/combo/example/src/main/webapp/menu/two.html +++ b/combo/example/src/main/webapp/menu/two.html @@ -1,3 +1,3 @@ - + Submenu Page 2 diff --git a/combo/example/src/main/webapp/menu/two_one.html b/combo/example/src/main/webapp/menu/two_one.html index 6d2f9e9..0d7c836 100644 --- a/combo/example/src/main/webapp/menu/two_one.html +++ b/combo/example/src/main/webapp/menu/two_one.html @@ -1,3 +1,3 @@ - + Submenu Page 2-1 diff --git a/combo/example/src/main/webapp/menu/two_two.html b/combo/example/src/main/webapp/menu/two_two.html index 03cb015..0b8a243 100644 --- a/combo/example/src/main/webapp/menu/two_two.html +++ b/combo/example/src/main/webapp/menu/two_two.html @@ -1,3 +1,3 @@ - + Submenu Page 2-2 diff --git a/combo/example/src/main/webapp/rhodeisland.html b/combo/example/src/main/webapp/rhodeisland.html index dc249f3..67b6b39 100644 --- a/combo/example/src/main/webapp/rhodeisland.html +++ b/combo/example/src/main/webapp/rhodeisland.html @@ -1,68 +1,48 @@ - - - - - - Destroy Rhode Island - + + -

The code:

- - - -
-class JSDialog {
-  // build the button... when pressed, present
-  // a dialog based on running the _jsdialog_confirm
-  // template
-  def button(in: NodeSeq) =
-  ajaxButton(in,
-             () => S.runTemplate(List("_jsdialog_confirm")).
-             map(ns => ModalDialog(ns)) openOr
-             Alert("Couldn't find _jsdialog_confirm template"))
+
+  
+  Template
+
 
-  // the template needs to bind to either server-side behavior
-  // and unblock the UI
-  def confirm(in: NodeSeq) =
-  bind("confirm", in,
-       "yes" -> ((b: NodeSeq) => ajaxButton(b, () =>
-        {println("Rhode Island Destroyed")
-         Unblock & Alert("Rhode Island Destroyed")})),
-       "no" -> ((b: NodeSeq) => <button onclick={Unblock.toJsCmd}>{b}</button>))
-}
-
+ +
+ + + + + Destroy Rhode Island + +
+
The code: +

+      class JSDialog {
+        // build the button... when pressed, present
+        // a dialog based on running the _jsdialog_confirm
+        // template
+        def button(in: NodeSeq) =
+          ajaxButton(in,
+                     () =>
+                       S.runTemplate(List("_jsdialog_confirm"))
+                         .map(ns => ModalDialog(ns)) openOr
+                         Alert("Couldn't find _jsdialog_confirm template"))
 
+        // the template needs to bind to either server-side behavior
+        // and unblock the UI
+        def confirm = {
+          "#yes" #> ((b: NodeSeq) =>
+            ajaxButton(b, () => {
+              println("Rhode Island Destroyed")
+              Unblock & Alert("Rhode Island Destroyed")
+            })) &
+            "#no" #> ((b: NodeSeq) => <button onclick={Unblock.toJsCmd}>
+                {b}
+              </button>)
+        }
+      }
+    
+
+ -
- + \ No newline at end of file diff --git a/combo/example/src/main/webapp/scripts/jquery.blockUI.js b/combo/example/src/main/webapp/scripts/jquery.blockUI.js index 342db1a..e708add 100644 --- a/combo/example/src/main/webapp/scripts/jquery.blockUI.js +++ b/combo/example/src/main/webapp/scripts/jquery.blockUI.js @@ -1,10 +1,10 @@ -/* +/*! * jQuery blockUI plugin - * Version 2.18 (16-APR-2009) - * @requires jQuery v1.2.3 or later + * Version 2.70.0-2014.11.23 + * Requires jQuery v1.7 or later * * Examples at: http://malsup.com/jquery/block/ - * Copyright (c) 2007-2008 M. Alsup + * Copyright (c) 2007-2013 M. Alsup * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html @@ -12,392 +12,609 @@ * Thanks to Amir-Hossein Sobhi for some excellent contributions! */ -;(function($) { - -if (/1\.(0|1|2)\.(0|1|2)/.test($.fn.jquery) || /^1.1/.test($.fn.jquery)) { - alert('blockUI requires jQuery v1.2.3 or later! You are using v' + $.fn.jquery); - return; -} - -$.fn._fadeIn = $.fn.fadeIn; - -// global $ methods for blocking/unblocking the entire page -$.blockUI = function(opts) { install(window, opts); }; -$.unblockUI = function(opts) { remove(window, opts); }; - -// convenience method for quick growl-like notifications (http://www.google.com/search?q=growl) -$.growlUI = function(title, message, timeout) { - var $m = $('
'); - if (title) $m.append('

'+title+'

'); - if (message) $m.append('

'+message+'

'); - if (timeout == undefined) timeout = 3000; - $.blockUI({ - message: $m, fadeIn: 700, fadeOut: 1000, centerY: false, - timeout: timeout, showOverlay: false, - css: $.blockUI.defaults.growlCSS - }); -}; - -// plugin method for blocking element content -$.fn.block = function(opts) { - return this.unblock({ fadeOut: 0 }).each(function() { - if ($.css(this,'position') == 'static') - this.style.position = 'relative'; - if ($.browser.msie) - this.style.zoom = 1; // force 'hasLayout' - install(this, opts); - }); -}; - -// plugin method for unblocking element content -$.fn.unblock = function(opts) { - return this.each(function() { - remove(this, opts); - }); -}; - -$.blockUI.version = 2.18; // 2nd generation blocking at no extra cost! - -// override these in your code to change the default behavior and style -$.blockUI.defaults = { - // message displayed when blocking (use null for no message) - message: '

Please wait...

', - - // styles for the message when blocking; if you wish to disable - // these and use an external stylesheet then do this in your code: - // $.blockUI.defaults.css = {}; - css: { - padding: 0, - margin: 0, - width: '30%', - top: '40%', - left: '35%', - textAlign: 'center', - color: '#000', - border: '3px solid #aaa', - backgroundColor:'#fff', - cursor: 'wait' - }, - - // styles for the overlay - overlayCSS: { - backgroundColor: '#000', - opacity: '0.6' - }, - - // styles applied when using $.growlUI - growlCSS: { - width: '350px', - top: '10px', - left: '', - right: '10px', - border: 'none', - padding: '5px', - opacity: '0.6', - cursor: null, - color: '#fff', - backgroundColor: '#000', - '-webkit-border-radius': '10px', - '-moz-border-radius': '10px' - }, - - iframeSrc: 'javascript:false', // 'about:blank' fails on HTTPS - - // force usage of iframe in non-IE browsers (handy for blocking over objects and applets) - forceIframe: false, - - // z-index for the blocking overlay - baseZ: 1000, - - // set these to true to have the message automatically centered - centerX: true, // <-- only effects element blocking (page block controlled via css above) - centerY: true, - - // allow body element to be stetched in ie6; this makes blocking look better - // on "short" pages. disable if you wish to prevent changes to the body height - allowBodyStretch: true, - - // be default blockUI will supress tab navigation from leaving blocking content; - constrainTabKey: true, - - // fadeIn time in millis; set to 0 to disable fadeIn on block - fadeIn: 200, - - // fadeOut time in millis; set to 0 to disable fadeOut on unblock - fadeOut: 400, - - // time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock - timeout: 0, - - // disable if you don't want to show the overlay - showOverlay: true, - - // if true, focus will be placed in the first available input field when - // page blocking - focusInput: true, - - // suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity) - applyPlatformOpacityRules: true, - - // callback method invoked when unblocking has completed; the callback is - // passed the element that has been unblocked (which is the window object for page - // blocks) and the options that were passed to the unblock call: - // onUnblock(element, options) - onUnblock: null, - - // don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493 - quirksmodeOffsetHack: 4 -}; - -// private data and functions follow... - -var ie6 = $.browser.msie && /MSIE 6.0/.test(navigator.userAgent); -var pageBlock = null; -var pageBlockEls = []; - -function install(el, opts) { - var full = (el == window); - var msg = opts && opts.message !== undefined ? opts.message : undefined; - opts = $.extend({}, $.blockUI.defaults, opts || {}); - opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {}); - var css = $.extend({}, $.blockUI.defaults.css, opts.css || {}); - msg = msg === undefined ? opts.message : msg; - - // remove the current block (if there is one) - if (full && pageBlock) - remove(window, {fadeOut:0}); - - // if an existing element is being used as the blocking content then we capture - // its current place in the DOM (and current display style) so we can restore - // it when we unblock - if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) { - var node = msg.jquery ? msg[0] : msg; - var data = {}; - $(el).data('blockUI.history', data); - data.el = node; - data.parent = node.parentNode; - data.display = node.style.display; - data.position = node.style.position; - if (data.parent) - data.parent.removeChild(node); - } - - var z = opts.baseZ; - - // blockUI uses 3 layers for blocking, for simplicity they are all used on every platform; - // layer1 is the iframe layer which is used to supress bleed through of underlying content - // layer2 is the overlay layer which has opacity and a wait cursor - // layer3 is the message content that is displayed while blocking - - var lyr1 = ($.browser.msie) ? $('') - : $(''); - var lyr2 = $(''); - var lyr3 = full ? $('') - : $(''); - - // if we have a message, style it - if (msg) - lyr3.css(css); - - // style the overlay - if (!opts.applyPlatformOpacityRules || !($.browser.mozilla && /Linux/.test(navigator.platform))) - lyr2.css(opts.overlayCSS); - lyr2.css('position', full ? 'fixed' : 'absolute'); - - // make iframe layer transparent in IE - if ($.browser.msie) - lyr1.css('opacity','0.0'); - - $([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el); - - // ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling) - var expr = $.browser.msie && ($.browser.version < 8 || !$.boxModel) && (!$.boxModel || $('object,embed', full ? null : el).length > 0); - if (ie6 || (expr && lyr3[0].style.setExpression)) { - // give body 100% height - if (full && opts.allowBodyStretch && $.boxModel) - $('html,body').css('height','100%'); - - // fix ie6 issue when blocked element has a border width - if ((ie6 || !$.boxModel) && !full) { - var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth'); - var fixT = t ? '(0 - '+t+')' : 0; - var fixL = l ? '(0 - '+l+')' : 0; - } - - // simulate fixed position - $.each([lyr1,lyr2,lyr3], function(i,o) { - var s = o[0].style; - s.position = 'absolute'; - if (i < 2) { - full ? s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.boxModel?0:'+opts.quirksmodeOffsetHack+') + "px"') - : s.setExpression('height','this.parentNode.offsetHeight + "px"'); - full ? s.setExpression('width','jQuery.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"') - : s.setExpression('width','this.parentNode.offsetWidth + "px"'); - if (fixL) s.setExpression('left', fixL); - if (fixT) s.setExpression('top', fixT); - } - else if (opts.centerY) { - if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"'); - s.marginTop = 0; - } - else if (!opts.centerY && full) { - var top = (opts.css && opts.css.top) ? parseInt(opts.css.top) : 0; - var expression = '((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"'; - s.setExpression('top',expression); +;(function() { +/*jshint eqeqeq:false curly:false latedef:false */ +"use strict"; + + function setup($) { + $.fn._fadeIn = $.fn.fadeIn; + + var noOp = $.noop || function() {}; + + // this bit is to ensure we don't call setExpression when we shouldn't (with extra muscle to handle + // confusing userAgent strings on Vista) + var msie = /MSIE/.test(navigator.userAgent); + var ie6 = /MSIE 6.0/.test(navigator.userAgent) && ! /MSIE 8.0/.test(navigator.userAgent); + var mode = document.documentMode || 0; + var setExpr = $.isFunction( document.createElement('div').style.setExpression ); + + // global $ methods for blocking/unblocking the entire page + $.blockUI = function(opts) { install(window, opts); }; + $.unblockUI = function(opts) { remove(window, opts); }; + + // convenience method for quick growl-like notifications (http://www.google.com/search?q=growl) + $.growlUI = function(title, message, timeout, onClose) { + var $m = $('
'); + if (title) $m.append('

'+title+'

'); + if (message) $m.append('

'+message+'

'); + if (timeout === undefined) timeout = 3000; + + // Added by konapun: Set timeout to 30 seconds if this growl is moused over, like normal toast notifications + var callBlock = function(opts) { + opts = opts || {}; + + $.blockUI({ + message: $m, + fadeIn : typeof opts.fadeIn !== 'undefined' ? opts.fadeIn : 700, + fadeOut: typeof opts.fadeOut !== 'undefined' ? opts.fadeOut : 1000, + timeout: typeof opts.timeout !== 'undefined' ? opts.timeout : timeout, + centerY: false, + showOverlay: false, + onUnblock: onClose, + css: $.blockUI.defaults.growlCSS + }); + }; + + callBlock(); + var nonmousedOpacity = $m.css('opacity'); + $m.mouseover(function() { + callBlock({ + fadeIn: 0, + timeout: 30000 + }); + + var displayBlock = $('.blockMsg'); + displayBlock.stop(); // cancel fadeout if it has started + displayBlock.fadeTo(300, 1); // make it easier to read the message by removing transparency + }).mouseout(function() { + $('.blockMsg').fadeOut(1000); + }); + // End konapun additions + }; + + // plugin method for blocking element content + $.fn.block = function(opts) { + if ( this[0] === window ) { + $.blockUI( opts ); + return this; } - }); - } - - // show the message - if (msg) { - lyr3.append(msg); - if (msg.jquery || msg.nodeType) - $(msg).show(); - } + var fullOpts = $.extend({}, $.blockUI.defaults, opts || {}); + this.each(function() { + var $el = $(this); + if (fullOpts.ignoreIfBlocked && $el.data('blockUI.isBlocked')) + return; + $el.unblock({ fadeOut: 0 }); + }); + + return this.each(function() { + if ($.css(this,'position') == 'static') { + this.style.position = 'relative'; + $(this).data('blockUI.static', true); + } + this.style.zoom = 1; // force 'hasLayout' in ie + install(this, opts); + }); + }; + + // plugin method for unblocking element content + $.fn.unblock = function(opts) { + if ( this[0] === window ) { + $.unblockUI( opts ); + return this; + } + return this.each(function() { + remove(this, opts); + }); + }; + + $.blockUI.version = 2.70; // 2nd generation blocking at no extra cost! + + // override these in your code to change the default behavior and style + $.blockUI.defaults = { + // message displayed when blocking (use null for no message) + message: '

Please wait...

', + + title: null, // title string; only used when theme == true + draggable: true, // only used when theme == true (requires jquery-ui.js to be loaded) + + theme: false, // set to true to use with jQuery UI themes + + // styles for the message when blocking; if you wish to disable + // these and use an external stylesheet then do this in your code: + // $.blockUI.defaults.css = {}; + css: { + padding: 0, + margin: 0, + width: '30%', + top: '40%', + left: '35%', + textAlign: 'center', + color: '#000', + border: '3px solid #aaa', + backgroundColor:'#fff', + cursor: 'wait' + }, + + // minimal style set used when themes are used + themedCSS: { + width: '30%', + top: '40%', + left: '35%' + }, + + // styles for the overlay + overlayCSS: { + backgroundColor: '#000', + opacity: 0.6, + cursor: 'wait' + }, + + // style to replace wait cursor before unblocking to correct issue + // of lingering wait cursor + cursorReset: 'default', + + // styles applied when using $.growlUI + growlCSS: { + width: '350px', + top: '10px', + left: '', + right: '10px', + border: 'none', + padding: '5px', + opacity: 0.6, + cursor: 'default', + color: '#fff', + backgroundColor: '#000', + '-webkit-border-radius':'10px', + '-moz-border-radius': '10px', + 'border-radius': '10px' + }, + + // IE issues: 'about:blank' fails on HTTPS and javascript:false is s-l-o-w + // (hat tip to Jorge H. N. de Vasconcelos) + /*jshint scripturl:true */ + iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank', + + // force usage of iframe in non-IE browsers (handy for blocking applets) + forceIframe: false, + + // z-index for the blocking overlay + baseZ: 1000, + + // set these to true to have the message automatically centered + centerX: true, // <-- only effects element blocking (page block controlled via css above) + centerY: true, + + // allow body element to be stetched in ie6; this makes blocking look better + // on "short" pages. disable if you wish to prevent changes to the body height + allowBodyStretch: true, + + // enable if you want key and mouse events to be disabled for content that is blocked + bindEvents: true, + + // be default blockUI will supress tab navigation from leaving blocking content + // (if bindEvents is true) + constrainTabKey: true, + + // fadeIn time in millis; set to 0 to disable fadeIn on block + fadeIn: 200, + + // fadeOut time in millis; set to 0 to disable fadeOut on unblock + fadeOut: 400, + + // time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock + timeout: 0, + + // disable if you don't want to show the overlay + showOverlay: true, + + // if true, focus will be placed in the first available input field when + // page blocking + focusInput: true, + + // elements that can receive focus + focusableElements: ':input:enabled:visible', + + // suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity) + // no longer needed in 2012 + // applyPlatformOpacityRules: true, + + // callback method invoked when fadeIn has completed and blocking message is visible + onBlock: null, + + // callback method invoked when unblocking has completed; the callback is + // passed the element that has been unblocked (which is the window object for page + // blocks) and the options that were passed to the unblock call: + // onUnblock(element, options) + onUnblock: null, + + // callback method invoked when the overlay area is clicked. + // setting this will turn the cursor to a pointer, otherwise cursor defined in overlayCss will be used. + onOverlayClick: null, + + // don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493 + quirksmodeOffsetHack: 4, + + // class name of the message block + blockMsgClass: 'blockMsg', + + // if it is already blocked, then ignore it (don't unblock and reblock) + ignoreIfBlocked: false + }; + + // private data and functions follow... + + var pageBlock = null; + var pageBlockEls = []; + + function install(el, opts) { + var css, themedCSS; + var full = (el == window); + var msg = (opts && opts.message !== undefined ? opts.message : undefined); + opts = $.extend({}, $.blockUI.defaults, opts || {}); + + if (opts.ignoreIfBlocked && $(el).data('blockUI.isBlocked')) + return; + + opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {}); + css = $.extend({}, $.blockUI.defaults.css, opts.css || {}); + if (opts.onOverlayClick) + opts.overlayCSS.cursor = 'pointer'; + + themedCSS = $.extend({}, $.blockUI.defaults.themedCSS, opts.themedCSS || {}); + msg = msg === undefined ? opts.message : msg; + + // remove the current block (if there is one) + if (full && pageBlock) + remove(window, {fadeOut:0}); - if ($.browser.msie && opts.showOverlay) - lyr1.show(); // opacity is zero - if (opts.fadeIn) { - if (opts.showOverlay) - lyr2._fadeIn(opts.fadeIn); - if (msg) - lyr3.fadeIn(opts.fadeIn); - } - else { - if (opts.showOverlay) - lyr2.show(); - if (msg) - lyr3.show(); - } + // if an existing element is being used as the blocking content then we capture + // its current place in the DOM (and current display style) so we can restore + // it when we unblock + if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) { + var node = msg.jquery ? msg[0] : msg; + var data = {}; + $(el).data('blockUI.history', data); + data.el = node; + data.parent = node.parentNode; + data.display = node.style.display; + data.position = node.style.position; + if (data.parent) + data.parent.removeChild(node); + } + + $(el).data('blockUI.onUnblock', opts.onUnblock); + var z = opts.baseZ; + + // blockUI uses 3 layers for blocking, for simplicity they are all used on every platform; + // layer1 is the iframe layer which is used to supress bleed through of underlying content + // layer2 is the overlay layer which has opacity and a wait cursor (by default) + // layer3 is the message content that is displayed while blocking + var lyr1, lyr2, lyr3, s; + if (msie || opts.forceIframe) + lyr1 = $(''); + else + lyr1 = $(''); + + if (opts.theme) + lyr2 = $(''); + else + lyr2 = $(''); + + if (opts.theme && full) { + s = ''; + } + else if (opts.theme) { + s = ''; + } + else if (full) { + s = ''; + } + else { + s = ''; + } + lyr3 = $(s); + + // if we have a message, style it + if (msg) { + if (opts.theme) { + lyr3.css(themedCSS); + lyr3.addClass('ui-widget-content'); + } + else + lyr3.css(css); + } + + // style the overlay + if (!opts.theme /*&& (!opts.applyPlatformOpacityRules)*/) + lyr2.css(opts.overlayCSS); + lyr2.css('position', full ? 'fixed' : 'absolute'); + + // make iframe layer transparent in IE + if (msie || opts.forceIframe) + lyr1.css('opacity',0.0); + + //$([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el); + var layers = [lyr1,lyr2,lyr3], $par = full ? $('body') : $(el); + $.each(layers, function() { + this.appendTo($par); + }); + + if (opts.theme && opts.draggable && $.fn.draggable) { + lyr3.draggable({ + handle: '.ui-dialog-titlebar', + cancel: 'li' + }); + } + + // ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling) + var expr = setExpr && (!$.support.boxModel || $('object,embed', full ? null : el).length > 0); + if (ie6 || expr) { + // give body 100% height + if (full && opts.allowBodyStretch && $.support.boxModel) + $('html,body').css('height','100%'); + + // fix ie6 issue when blocked element has a border width + if ((ie6 || !$.support.boxModel) && !full) { + var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth'); + var fixT = t ? '(0 - '+t+')' : 0; + var fixL = l ? '(0 - '+l+')' : 0; + } + + // simulate fixed position + $.each(layers, function(i,o) { + var s = o[0].style; + s.position = 'absolute'; + if (i < 2) { + if (full) + s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.support.boxModel?0:'+opts.quirksmodeOffsetHack+') + "px"'); + else + s.setExpression('height','this.parentNode.offsetHeight + "px"'); + if (full) + s.setExpression('width','jQuery.support.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"'); + else + s.setExpression('width','this.parentNode.offsetWidth + "px"'); + if (fixL) s.setExpression('left', fixL); + if (fixT) s.setExpression('top', fixT); + } + else if (opts.centerY) { + if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"'); + s.marginTop = 0; + } + else if (!opts.centerY && full) { + var top = (opts.css && opts.css.top) ? parseInt(opts.css.top, 10) : 0; + var expression = '((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"'; + s.setExpression('top',expression); + } + }); + } + + // show the message + if (msg) { + if (opts.theme) + lyr3.find('.ui-widget-content').append(msg); + else + lyr3.append(msg); + if (msg.jquery || msg.nodeType) + $(msg).show(); + } + + if ((msie || opts.forceIframe) && opts.showOverlay) + lyr1.show(); // opacity is zero + if (opts.fadeIn) { + var cb = opts.onBlock ? opts.onBlock : noOp; + var cb1 = (opts.showOverlay && !msg) ? cb : noOp; + var cb2 = msg ? cb : noOp; + if (opts.showOverlay) + lyr2._fadeIn(opts.fadeIn, cb1); + if (msg) + lyr3._fadeIn(opts.fadeIn, cb2); + } + else { + if (opts.showOverlay) + lyr2.show(); + if (msg) + lyr3.show(); + if (opts.onBlock) + opts.onBlock.bind(lyr3)(); + } + + // bind key and mouse events + bind(1, el, opts); + + if (full) { + pageBlock = lyr3[0]; + pageBlockEls = $(opts.focusableElements,pageBlock); + if (opts.focusInput) + setTimeout(focus, 20); + } + else + center(lyr3[0], opts.centerX, opts.centerY); + + if (opts.timeout) { + // auto-unblock + var to = setTimeout(function() { + if (full) + $.unblockUI(opts); + else + $(el).unblock(opts); + }, opts.timeout); + $(el).data('blockUI.timeout', to); + } + } + + // remove the block + function remove(el, opts) { + var count; + var full = (el == window); + var $el = $(el); + var data = $el.data('blockUI.history'); + var to = $el.data('blockUI.timeout'); + if (to) { + clearTimeout(to); + $el.removeData('blockUI.timeout'); + } + opts = $.extend({}, $.blockUI.defaults, opts || {}); + bind(0, el, opts); // unbind events + + if (opts.onUnblock === null) { + opts.onUnblock = $el.data('blockUI.onUnblock'); + $el.removeData('blockUI.onUnblock'); + } + + var els; + if (full) // crazy selector to handle odd field errors in ie6/7 + els = $('body').children().filter('.blockUI').add('body > .blockUI'); + else + els = $el.find('>.blockUI'); + + // fix cursor issue + if ( opts.cursorReset ) { + if ( els.length > 1 ) + els[1].style.cursor = opts.cursorReset; + if ( els.length > 2 ) + els[2].style.cursor = opts.cursorReset; + } + + if (full) + pageBlock = pageBlockEls = null; + + if (opts.fadeOut) { + count = els.length; + els.stop().fadeOut(opts.fadeOut, function() { + if ( --count === 0) + reset(els,data,opts,el); + }); + } + else + reset(els, data, opts, el); + } + + // move blocking element back into the DOM where it started + function reset(els,data,opts,el) { + var $el = $(el); + if ( $el.data('blockUI.isBlocked') ) + return; + + els.each(function(i,o) { + // remove via DOM calls so we don't lose event handlers + if (this.parentNode) + this.parentNode.removeChild(this); + }); + + if (data && data.el) { + data.el.style.display = data.display; + data.el.style.position = data.position; + data.el.style.cursor = 'default'; // #59 + if (data.parent) + data.parent.appendChild(data.el); + $el.removeData('blockUI.history'); + } + + if ($el.data('blockUI.static')) { + $el.css('position', 'static'); // #22 + } + + if (typeof opts.onUnblock == 'function') + opts.onUnblock(el,opts); + + // fix issue in Safari 6 where block artifacts remain until reflow + var body = $(document.body), w = body.width(), cssW = body[0].style.width; + body.width(w-1).width(w); + body[0].style.width = cssW; + } + + // bind/unbind the handler + function bind(b, el, opts) { + var full = el == window, $el = $(el); + + // don't bother unbinding if there is nothing to unbind + if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked'))) + return; + + $el.data('blockUI.isBlocked', b); + + // don't bind events when overlay is not in use or if bindEvents is false + if (!full || !opts.bindEvents || (b && !opts.showOverlay)) + return; + + // bind anchors and inputs for mouse and key events + var events = 'mousedown mouseup keydown keypress keyup touchstart touchend touchmove'; + if (b) + $(document).bind(events, opts, handler); + else + $(document).unbind(events, handler); + + // former impl... + // var $e = $('a,:input'); + // b ? $e.bind(events, opts, handler) : $e.unbind(events, handler); + } + + // event handler to suppress keyboard/mouse events when blocking + function handler(e) { + // allow tab navigation (conditionally) + if (e.type === 'keydown' && e.keyCode && e.keyCode == 9) { + if (pageBlock && e.data.constrainTabKey) { + var els = pageBlockEls; + var fwd = !e.shiftKey && e.target === els[els.length-1]; + var back = e.shiftKey && e.target === els[0]; + if (fwd || back) { + setTimeout(function(){focus(back);},10); + return false; + } + } + } + var opts = e.data; + var target = $(e.target); + if (target.hasClass('blockOverlay') && opts.onOverlayClick) + opts.onOverlayClick(e); + + // allow events within the message content + if (target.parents('div.' + opts.blockMsgClass).length > 0) + return true; + + // allow events for content that is not being blocked + return target.parents().children().filter('div.blockUI').length === 0; + } + + function focus(back) { + if (!pageBlockEls) + return; + var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0]; + if (e) + e.focus(); + } + + function center(el, x, y) { + var p = el.parentNode, s = el.style; + var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth'); + var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth'); + if (x) s.left = l > 0 ? (l+'px') : '0'; + if (y) s.top = t > 0 ? (t+'px') : '0'; + } + + function sz(el, p) { + return parseInt($.css(el,p),10)||0; + } - // bind key and mouse events - bind(1, el, opts); - - if (full) { - pageBlock = lyr3[0]; - pageBlockEls = $(':input:enabled:visible',pageBlock); - if (opts.focusInput) - setTimeout(focus, 20); - } - else - center(lyr3[0], opts.centerX, opts.centerY); - - if (opts.timeout) { - // auto-unblock - var to = setTimeout(function() { - full ? $.unblockUI(opts) : $(el).unblock(opts); - }, opts.timeout); - $(el).data('blockUI.timeout', to); } -}; - -// remove the block -function remove(el, opts) { - var full = el == window; - var $el = $(el); - var data = $el.data('blockUI.history'); - var to = $el.data('blockUI.timeout'); - if (to) { - clearTimeout(to); - $el.removeData('blockUI.timeout'); + + + /*global define:true */ + if (typeof define === 'function' && define.amd && define.amd.jQuery) { + define(['jquery'], setup); + } else { + setup(jQuery); } - opts = $.extend({}, $.blockUI.defaults, opts || {}); - bind(0, el, opts); // unbind events - var els = full ? $('body').children().filter('.blockUI') : $('.blockUI', el); - - if (full) - pageBlock = pageBlockEls = null; - - if (opts.fadeOut) { - els.fadeOut(opts.fadeOut); - setTimeout(function() { reset(els,data,opts,el); }, opts.fadeOut); - } - else - reset(els, data, opts, el); -}; - -// move blocking element back into the DOM where it started -function reset(els,data,opts,el) { - els.each(function(i,o) { - // remove via DOM calls so we don't lose event handlers - if (this.parentNode) - this.parentNode.removeChild(this); - }); - - if (data && data.el) { - data.el.style.display = data.display; - data.el.style.position = data.position; - if (data.parent) - data.parent.appendChild(data.el); - $(data.el).removeData('blockUI.history'); - } - - if (typeof opts.onUnblock == 'function') - opts.onUnblock(el,opts); -}; - -// bind/unbind the handler -function bind(b, el, opts) { - var full = el == window, $el = $(el); - - // don't bother unbinding if there is nothing to unbind - if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked'))) - return; - if (!full) - $el.data('blockUI.isBlocked', b); - - if (b && !opts.showOverlay) // don't prevent events when overlay not in use - return; - - // bind anchors and inputs for mouse and key events - var events = 'mousedown mouseup keydown keypress'; - b ? $(document).bind(events, opts, handler) : $(document).unbind(events, handler); - -// former impl... -// var $e = $('a,:input'); -// b ? $e.bind(events, opts, handler) : $e.unbind(events, handler); -}; - -// event handler to suppress keyboard/mouse events when blocking -function handler(e) { - // allow tab navigation (conditionally) - if (e.keyCode && e.keyCode == 9) { - if (pageBlock && e.data.constrainTabKey) { - var els = pageBlockEls; - var fwd = !e.shiftKey && e.target == els[els.length-1]; - var back = e.shiftKey && e.target == els[0]; - if (fwd || back) { - setTimeout(function(){focus(back)},10); - return false; - } - } - } - // allow events within the message content - if ($(e.target).parents('div.blockMsg').length > 0) - return true; - - // allow events for content that is not being blocked - return $(e.target).parents().children().filter('div.blockUI').length == 0; -}; - -function focus(back) { - if (!pageBlockEls) - return; - var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0]; - if (e) - e.focus(); -}; - -function center(el, x, y) { - var p = el.parentNode, s = el.style; - var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth'); - var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth'); - if (x) s.left = l > 0 ? (l+'px') : '0'; - if (y) s.top = t > 0 ? (t+'px') : '0'; -}; - -function sz(el, p) { - return parseInt($.css(el,p))||0; -}; - -})(jQuery); + +})(); \ No newline at end of file diff --git a/combo/example/src/main/webapp/stateless_json.html b/combo/example/src/main/webapp/stateless_json.html index 14fed56..8c565a9 100644 --- a/combo/example/src/main/webapp/stateless_json.html +++ b/combo/example/src/main/webapp/stateless_json.html @@ -1,81 +1,66 @@ - -
Stateless JSON Sample
-
+ + + + + + Template + + + + +
+
+ Stateless JSON Sample +
+
-
+
-
+
-

Here's the code:

- +

Here's the code:

- - -
-/**
- * Respond to JSON requests in a stateless dispatch
- */
-object StatelessJson {
-  def init() {
-    // register the JSON handler
-    LiftRules.statelessDispatchTable.append {
-      case r @ Req("stateless_json_call" :: Nil, _, PostRequest) => () => handleJson(r)
+    

+object StatelessJson {
+  def init() {
+    // register the JSON handler
+    LiftRules.statelessDispatch.append {
+      case r @ Req("stateless_json_call" :: Nil, _, PostRequest) =>
+        () =>
+          handleJson(r)
     }
   }
 
-  implicit def iterableToBox[X](in: Iterable[X]): Box[X] = in.toList.headOption
+  implicit def iterableToBox[X](in: Iterable[X]): Box[X] =
+    in.toList.headOption
 
-  def handleJson(req: Req): Box[LiftResponse] =
-  for {
-    json <- req.json // get the JSON
-    JObject(List(JField("command", JString(cmd)), JField("params", JString(params)))) <- json // extract the command
-  } yield JavaScriptResponse(SetHtml("json_result",cmd match { // build the response
-        case "show" => Text(params)
-        case "textile" =>  TextileParser.toHtml(params, Empty)
-        case "count" => Text(params.length+" Characters")
-        case x => <b>Problem... didn't handle JSON message {x}</b>
-      }))
+  def handleJson(req: Req): Box[LiftResponse] =
+    for {
+      json <- req.json // get the JSON
+      JObject(
+        List(JField("command", JString(cmd)),
+             JField("params", JString(params)))) <- json // extract the command
+    } yield
+      JavaScriptResponse(
+        SetHtml(
+          "json_result",
+          cmd match { // build the response
+            case "show"    => Text(params)
+            case "textile" => TextileParser.toHtml(params, Empty)
+            case "count"   => Text(params.length + " Characters")
+            case x         => <b>Problem... didn't handle JSON message {x}</b>
+          }
+        ))
 }
-
- - +
+
+ -
+ \ No newline at end of file diff --git a/combo/example/src/main/webapp/templates-hidden/default.html b/combo/example/src/main/webapp/templates-hidden/default.html index 9efdede..b62c7c2 100644 --- a/combo/example/src/main/webapp/templates-hidden/default.html +++ b/combo/example/src/main/webapp/templates-hidden/default.html @@ -6,10 +6,18 @@ Lift Web Framework: %*% - - - - + + + + + + + + + + + + - - - + + + + + + + + + + + +

+/**
+  * The singleton that has methods for accessing the database
+  */
+object User extends User with KeyedMetaMapper[Long, User] {
+  override def dbTableName = "users" // define the DB table name
 
-    
-/**
- * The singleton that has methods for accessing the database
- */
-object User extends User with KeyedMetaMapper[Long, User] {
-  override def dbTableName = "users" // define the DB table name
-
-  // define the order fields will appear in forms and output
-  override lazy val fieldOrder = List(id, firstName, lastName, 
-                                      email, password, textArea)
+  // define the order fields will appear in forms and output
+  override lazy val fieldOrder =
+    List(id, firstName, lastName, email, password, textArea)
 }
 
-/**
- * An O-R mapped "User" class that includes first name, last name,
- * password and we add a "Personal Essay" to it
- */
-class User extends ProtoUser[User] {
-  def getSingleton = User
-
-  // define an additional field for a personal essay
-  object textArea extends MappedTextarea(this, 2048) {
-    override def textareaRows  = 10
-    override def textareaCols = 50
-    override def displayName = "Personal Essay"
+/**
+  * An O-R mapped "User" class that includes first name, last name,
+  * password and we add a "Personal Essay" to it
+  */
+class User extends ProtoUser[User] {
+  def getSingleton = User
+
+  // define an additional field for a personal essay
+  object textArea extends MappedTextarea(this, 2048) {
+    override def textareaRows = 10
+    override def textareaCols = 50
+    override def displayName = "Personal Essay"
   }
 }
-
+

For this little bit of work, we get a complete user with first name, last name, password, email, and a personal essay. @@ -80,4 +56,7 @@ how to convert itself to a string, to XML, and even generate HTML forms for itself.

-
+
+ + + diff --git a/combo/example/src/main/webapp/json.html b/combo/example/src/main/webapp/json.html index ad3cfbc..ee06767 100644 --- a/combo/example/src/main/webapp/json.html +++ b/combo/example/src/main/webapp/json.html @@ -2,49 +2,87 @@ - - Template + + Template -
-
JSON Samples
-
-
+
+
+ JSON Samples +
+
+
+

Enter an addition question:

+ +
+
+ + + + + + +
+
+ +
+ + + +
+
- -
- -
- -
-
+
+ +
+
+ +
+ +
+
-

Here's the code:

+

Here's the code:

+

+/**
+  * The singleton that has methods for accessing the database
+  */
+object User extends User with KeyedMetaMapper[Long, User] {
+  override def dbTableName = "users" // define the DB table name
 
-    
-/**
- * The singleton that has methods for accessing the database
- */
-object User extends User with KeyedMetaMapper[Long, User] {
-  override def dbTableName = "users" // define the DB table name
-
-  // define the order fields will appear in forms and output
-  override lazy val fieldOrder = List(id, firstName, lastName, 
-                                      email, password, textArea)
+  // define the order fields will appear in forms and output
+  override lazy val fieldOrder =
+    List(id, firstName, lastName, email, password, textArea)
 }
 
-/**
- * An O-R mapped "User" class that includes first name, last name,
- * password and we add a "Personal Essay" to it
- */
-class User extends ProtoUser[User] {
-  def getSingleton = User
-
-  // define an additional field for a personal essay
-  object textArea extends MappedTextarea(this, 2048) {
-    override def textareaRows  = 10
-    override def textareaCols = 50
-    override def displayName = "Personal Essay"
+/**
+  * An O-R mapped "User" class that includes first name, last name,
+  * password and we add a "Personal Essay" to it
+  */
+class User extends ProtoUser[User] {
+  def getSingleton = User
+
+  // define an additional field for a personal essay
+  object textArea extends MappedTextarea(this, 2048) {
+    override def textareaRows = 10
+    override def textareaCols = 50
+    override def displayName = "Personal Essay"
   }
 }
-
+

For this little bit of work, we get a complete user with first name, last name, password, email, and a personal essay. @@ -80,4 +56,7 @@ how to convert itself to a string, to XML, and even generate HTML forms for itself.

- +
+ + + diff --git a/combo/example/src/main/webapp/json.html b/combo/example/src/main/webapp/json.html index ad3cfbc..ee06767 100644 --- a/combo/example/src/main/webapp/json.html +++ b/combo/example/src/main/webapp/json.html @@ -2,49 +2,87 @@ - - Template + + Template -
-
JSON Samples
-
-
+
+
+ JSON Samples +
+
+
+

Enter an addition question:

+ +
+
+ + + + + + +
+
+ +
+ + + +
+
- -
- -
- -
-
+
+ +
+
+ +
+ +
+
-

Here's the code:

+

Here's the code:

diff --git a/combo/example/src/main/webapp/json_more.html b/combo/example/src/main/webapp/json_more.html index 13c39cc..2bdea50 100644 --- a/combo/example/src/main/webapp/json_more.html +++ b/combo/example/src/main/webapp/json_more.html @@ -1,11 +1,28 @@ - -
More JSON Samples
-
- - -
- - This example calls the noParam function which - invokes a server-side JSON call that then calls a handler - back in the browser. - - -
- The client-side JavaScript for looks like: -
+        // ]]>
+      
+      This example calls the
+      noParam function which invokes a server-side JSON call that then calls a handler back in the browser.
+      
+
+      
The client-side JavaScript for looks like: +
     <script>
       // <![CDATA[
         function callNoParam() {
@@ -35,13 +50,13 @@
       // ]]>
     </script>
 
-
+
+ +
-
- -
- + // ]]> + - Let's send the contents of a test field and process it: + Let's send the contents of a test field and process it: - - -
-
-
- The Script to do this is: -
+      
+      
+      
+
+
The Script to do this is: +
     <script>
       // <![CDATA[
         function stringSender() {
@@ -78,13 +92,13 @@
     </script>
 
-
+
-
- -
- - - - Let's send some numbers to the server and have the server - add 1 to each of the numbers. -
- Enter comma separated numbers: - - -
- -
- - -
- The server-side code looks like: - -
+        // ]]>
+      
+
+
+      Let's send some numbers to the server and have the server add 1 to each of the numbers.
+      
Enter comma separated numbers: + + +
+ +
+ + +
+ The server-side code looks like: + +
   new JsonHandler {
     def apply(in: Any): JsCmd = in match {
       case JsonCmd("noParam", resp, _, _) =>
@@ -157,6 +174,9 @@
 
+
+
+ - + \ No newline at end of file diff --git a/combo/example/src/main/webapp/persistence.html b/combo/example/src/main/webapp/persistence.html index 70827cf..7f03848 100644 --- a/combo/example/src/main/webapp/persistence.html +++ b/combo/example/src/main/webapp/persistence.html @@ -10,6 +10,14 @@

Persistence

+

Lift comes with its own ActiveRecord-like OR mapper called mapper. It's great for small projects. Lift also supports JPA.

diff --git a/combo/example/src/main/webapp/rhodeisland.html b/combo/example/src/main/webapp/rhodeisland.html index 67b6b39..599fe75 100644 --- a/combo/example/src/main/webapp/rhodeisland.html +++ b/combo/example/src/main/webapp/rhodeisland.html @@ -11,6 +11,24 @@ +

Modal Dialog

+ + +

This section has two examples showing how you could work with modal dialogs that has there template data in a separate + file. The first example is a plain javascript dialog while the other one is taking advantage of bootstrap's javascript + modals.

+ +

JS dialog example

Destroy Rhode Island @@ -42,6 +60,59 @@ } }
+
+

Bootstrap dialog example

+ Destroy Rhode Island + +
+
The code: +

+class BSDialog {
+  // build the button... when pressed, present
+  // a dialog based on the _bsdialog_confirm
+  // template
+  def button(in: NodeSeq) =
+    ajaxButton(in,
+               () => injectDialogTemplate & openDialog,
+               "type" -> "button",
+               "class" -> "btn btn-primary",
+               "data-target" -> "#exampleModal")
+
+  def confirm = {
+    "#yes" #> ((b: NodeSeq) =>
+      ajaxButton(b, () => {
+        logger.debug("Rhode Island Destroyed")
+        showDestroyAlert & closeDialog
+      }, "type" -> "button", "class" -> "btn btn-primary")) &
+      "#no" #> ((b: NodeSeq) =>
+        ajaxButton(
+          b,
+          () => {
+            logger.debug("Rhode Island intact")
+            Noop //we could have used closeDialog here but we are instead using the data-dismiss attribute.
+          },
+          "type" -> "button",
+          "class" -> "btn btn-primary",
+          "data-dismiss" -> "modal"
+        ))
+  }
+
+  private val logger = Logger(classOf[BSDialog])
+  private val bsDialogTemplate: Box[NodeSeq] = Templates(
+    List("_bsdialog_confirm"))
+  private val openDialog
+    : JsCmd = JsRaw("""$('#exampleModal').modal('show')""").cmd
+  private val closeDialog
+    : JsCmd = JsRaw("""$('#exampleModal').modal('hide')""").cmd
+  private val showDestroyAlert = Alert("Rhode Island Destroyed")
+  private val showTemplateNotFoundAlert = Alert(
+    "Couldn't find _bsdialog_confirm template")
+
+  private def setAtPlaceholder(ns: NodeSeq) = SetHtml("modalPlaceholder", ns)
+  private def injectDialogTemplate =
+    bsDialogTemplate map setAtPlaceholder openOr showTemplateNotFoundAlert
+}
+    
diff --git a/combo/example/src/main/webapp/simple/add.html b/combo/example/src/main/webapp/simple/add.html index b980d97..3c80cc9 100644 --- a/combo/example/src/main/webapp/simple/add.html +++ b/combo/example/src/main/webapp/simple/add.html @@ -8,12 +8,30 @@
+

Add User

+ +
+
+
foo
- +
diff --git a/combo/example/src/main/webapp/simple/delete.html b/combo/example/src/main/webapp/simple/delete.html index 64a6ecf..b2a6d68 100644 --- a/combo/example/src/main/webapp/simple/delete.html +++ b/combo/example/src/main/webapp/simple/delete.html @@ -8,11 +8,24 @@
+

Delete User

+
- Do you really want to delete - - No - +

Do you really want to delete user: ''?

+
diff --git a/combo/example/src/main/webapp/simple/edit.html b/combo/example/src/main/webapp/simple/edit.html index 89b947a..6a29e6b 100644 --- a/combo/example/src/main/webapp/simple/edit.html +++ b/combo/example/src/main/webapp/simple/edit.html @@ -8,6 +8,24 @@
+

Edit User

+ +
+
+
diff --git a/combo/example/src/main/webapp/simple/index.html b/combo/example/src/main/webapp/simple/index.html index 050208d..d1b6c8e 100644 --- a/combo/example/src/main/webapp/simple/index.html +++ b/combo/example/src/main/webapp/simple/index.html @@ -5,15 +5,27 @@ Template -
- Add a User +

Simple Forms

+ +
foo
test
+ Add a User
diff --git a/combo/example/src/main/webapp/stateless_json.html b/combo/example/src/main/webapp/stateless_json.html index 31a48d0..679afd7 100644 --- a/combo/example/src/main/webapp/stateless_json.html +++ b/combo/example/src/main/webapp/stateless_json.html @@ -9,9 +9,20 @@
-
- Stateless JSON Sample -
+ +

Stateless JSON Sample

+ +
@@ -26,9 +37,7 @@
- -

Here's the code:

- +
Here's the code:

 object StatelessJson {
   def init() {
diff --git a/combo/example/src/main/webapp/templates-hidden/default2.html b/combo/example/src/main/webapp/templates-hidden/default2.html
index bd77988..8401a43 100644
--- a/combo/example/src/main/webapp/templates-hidden/default2.html
+++ b/combo/example/src/main/webapp/templates-hidden/default2.html
@@ -1,4 +1,4 @@
-
+
   
     
     
diff --git a/combo/example/src/main/webapp/templating/embed.html b/combo/example/src/main/webapp/templating/embed.html
index 3a39119..7b1638c 100644
--- a/combo/example/src/main/webapp/templating/embed.html
+++ b/combo/example/src/main/webapp/templating/embed.html
@@ -1,119 +1,63 @@
-
-  
-    Lift Example
-  
-  
-    
- - -

- In addition surrounding XHTML with a template, you - can also embed a template at the current point in - the page rendering with the <lift:embed/> tag. -

- - - -

- The above paragraph was embedded using this code: -

- - - -
-    <lift:embed what="/templating/_sample_embed"/>
-  
- -

- Templates that start with the underscore ('_') or period ('.') - characters will not be served directly by Lift, but may be - accessed using the surround and embed tags. -

- -

- Lift will select templates based on the current localization - setting for the session. Lift uses the function in - LiftRules.localeCalculator to determine the - current locale for template selection. By default the - function is: - -

-  def defaultLocaleCalculator(request: Box[HttpServletRequest]) = 
-    request.flatMap(_.getLocale() match 
-                    {case null => Empty 
-                     case l: Locale => Full(l)}).openOr(Locale.getDefault())
-
- -But you can customize the function to return the locale of -the currently logged in user, detect the IP address of the request, -etc. -

- -

- Based on the locale, Lift will look for templates based on - the base template name, in this case - '/templating/_sample_embed' and - then append an underscore ('_') followed by the complete locale, for - example 'en_US'. So, Lift will look for - '/templating/_sample_embed_en_US.html'. If that resource - is not available, Lift will look for - '/templating/_sample_embed_en.html' and finally - '/templating/_sample_embed.html'. -

-
- - + + + + + + Template + + + +
+

Embed

+ +

+ In addition surrounding HTML with a template, you can also embed a template at the current point in the page rendering a + tag with the + data-lift='embed?what=...' attribute. +

+ + + +

+ The above paragraph was embedded using this code: +

+
<span data-lift="embed?what=/templating/_sample_embed"></span>
+ +

+ Templates that start with the underscore ('_') or period ('.') characters will not be served directly by Lift, but may be + accessed using the surround and embed tags. +

+ +

+ Lift will select templates based on the current localization setting for the session. Lift uses the function in + LiftRules.localeCalculator to determine the current locale for template selection. By default the function is: + +

def defaultLocaleCalculator(request: Box[HttpServletRequest]) =
+    request.flatMap(_.getLocale() match
+                    {case null => Empty
+                     case l: Locale => Full(l)}).openOr(Locale.getDefault())
But you can customize the function to return the locale of the currently logged in user, detect the IP address of + the request, etc. +

+ +

+ Based on the locale, Lift will look for templates based on the base template name, in this case + '/templating/_sample_embed' and then append an underscore ('_') followed by the complete locale, for example + 'en_US'. So, Lift will look for + '/templating/_sample_embed_en_US.html'. If that resource is not available, Lift will look for + '/templating/_sample_embed_en.html' and finally + '/templating/_sample_embed.html'. +

+
+ + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/templating/index.html b/combo/example/src/main/webapp/templating/index.html index 693fb0b..73d7792 100644 --- a/combo/example/src/main/webapp/templating/index.html +++ b/combo/example/src/main/webapp/templating/index.html @@ -1,65 +1,118 @@ - - +

The following is David Pollak's take on web framework view technology:

+
+

The Lift design is born out of my experience (both good and bad) with a variety of technologies including Rails. +

+

I think the best paper on the subject is Terrence Parr's work on + + StringTemplate. +

+

My first design goal with Lift was to make sure that no programming logic and no programming symbols make it into the + static display templates. +

+

ERB and JSP and ASP all have the fatal flaw of allowing code in the view. This is bad for a bunch of reasons. First, + it makes editing the templates difficult with HTML layout tools unless those tools are familiar with the syntax being + used. Second, there are "foreign" symbols in the layout, which tends to be less than optimal for the HTML designers. + (On the Rails side of things, every Rails team I've seen has a Ruby coder that also does the design. While this is + the norm in the Rails community, it is the exception when team sizes are more than 2 or 3.) Third, every single Rails, + JSP, and ASP project I've ever seen (and I've been seeing them for a very long time) has some non-trivial amount + of business logic creep into the display. Fourth, Scala has a very nice type system and when the type system is used + correctly, the compiler finds a lot of program errors, but when the code is buried in templates, one has a much more + difficult time using the powerful Scala compiler tools. +

+

So, the static templates in Lift are strictly display only. They can be manipulated with standard design tools (e.g., + Dreamweaver). They can never contain program logic. +

+

Some asides. +

+

First, I rejected using StringTemplate (or something like it) because it introduced some programming into the templating + mechanism and it would have taken a lot of work to make it XMLTemplate (and all of Lift's templating is XHTML and + makes use of Scala's excellent support of XML.) +

+

Second, I've been referring to static templates. Lift has a little known and pretty much undocumented feature that + supports template generation from Scala code. One of these days, I'll document the feature and put up some examples. +

+

Third, Rails' "controller first" dispatch mechanism makes the assumption that there is only one piece of "logic" on + the page and the rest is decoration. My experience doing web work is just the opposite. There are typically 3 or + more of pieces of logic on a page (dynamic menu bars, search boxes, shopping cart, real-time chat, etc.) and having + to choose which piece of logic make the "controller" is less than optimal. +

+

So, the quintessential use of Lift's templates are as follows: +

+ +
+ + + + + +
+
+
+
+
+
-/* ]]> */ - -

The following is David Pollak's take on web framework view technology:

-
-

The Lift design is born out of my experience (both good and bad) -with a variety of technologies including Rails. -

I think the best paper on the subject is Terrence Parr's work on - -StringTemplate. -

My first design goal with Lift was to make sure that no programming -logic and no programming symbols make it into the static display -templates. -

ERB and JSP and ASP all have the fatal flaw of allowing code in the -view. This is bad for a bunch of reasons. First, it makes editing -the templates difficult with HTML layout tools unless those tools are -familiar with the syntax being used. Second, there are "foreign" -symbols in the layout, which tends to be less than optimal for the -HTML designers. (On the Rails side of things, every Rails team I've -seen has a Ruby coder that also does the design. While this is the -norm in the Rails community, it is the exception when team sizes are -more than 2 or 3.) Third, every single Rails, JSP, and ASP project -I've ever seen (and I've been seeing them for a very long time) has -some non-trivial amount of business logic creep into the display. -Fourth, Scala has a very nice type system and when the type system is -used correctly, the compiler finds a lot of program errors, but when -the code is buried in templates, one has a much more difficult time -using the powerful Scala compiler tools. -

So, the static templates in Lift are strictly display only. They can -be manipulated with standard design tools (e.g., Dreamweaver). They -can never contain program logic. -

Some asides. -

First, I rejected using StringTemplate (or something like it) because -it introduced some programming into the templating mechanism and it -would have taken a lot of work to make it XMLTemplate (and all of -Lift's templating is XHTML and makes use of Scala's excellent support -of XML.) -

Second, I've been referring to static templates. Lift has a little -known and pretty much undocumented feature that supports template -generation from Scala code. One of these days, I'll document the -feature and put up some examples. -

Third, Rails' "controller first" dispatch mechanism makes the -assumption that there is only one piece of "logic" on the page and -the rest is decoration. My experience doing web work is just the -opposite. There are typically 3 or more of pieces of logic on a page -(dynamic menu bars, search boxes, shopping cart, real-time chat, -etc.) and having to choose which piece of logic make the "controller" -is less than optimal. -

So, the quintessential use of Lift's templates are as follows: -

-
+      the new way
+      

+<html>
+...
+      <form data-lift="Show?form=post" id="myForm">
+          <label for="name">Name</label>
+          <input type="text" name="name" id="name" placeholder="Name">
+          <label for="birthyear">Birthyear</label>
+          <select id="birthyear">
+              <option>2006</option>
+              <option>2007</option>
+          </select>
+          <input type="submit" value="Submit"/>
+      </form>
+      <div id="selectedyear"></div>
+      <div id="namevalue"></div>
+      <div id="wrappedNotice">
+        <div data-lift="Msgs"></div>
+      </div>
+...
+</html>
+        
+ + + the old way +

 <html>
 ...
 <lift:show.myForm form="post">
@@ -71,30 +124,72 @@
 </lift:show.myForm>
 ...
 
-</html>
-
-

So we've got a Lift snippet invocation with the valid HTML form and -some additional tags. So far (with the proper namespace -declarations) this page is valid XHTML. This page can be viewed in a -browser or opened and edited in Dreamweaver. -

In Lift, the snippet is the equivalent of a Rails controller: it is -the instantiation of a class and invocation of a method on the -class. Because you can have multiple snippets on a page, you can -call out multiple logic streams on a given page and there's no need -to choose the "primary" logic stream. -

The 'form="post"' attribute is a shortcut. It automatically wraps -the enclosed XHTML in a <form method='post' target={current page... -it's a post-back}>...</form> tag. -

The <f:.../> tags are bind points for the business logic. They allow -your snippet to easily replace the tag and its children with what is -supposed to be displayed. This mechanism is a little heavier than -Wicket's mechanism for binding display elements to logic and I've got -a to-do to make Lift's mechanism work like Wickets as well... but -that's a digression. -

So, your Lift code will look like: -

- -
+</html>
+ +

So we've got a Lift snippet invocation with the valid HTML form and some additional tags. So far (with the proper namespace + declarations) this page is valid XHTML. This page can be viewed in a browser or opened and edited in Dreamweaver. +

+

In Lift, the snippet is the equivalent of a Rails controller: it is the instantiation of a class and invocation of + a method on the class. Because you can have multiple snippets on a page, you can call out multiple logic streams + on a given page and there's no need to choose the "primary" logic stream. +

+

The 'form="post"' attribute is a shortcut. It automatically wraps the enclosed XHTML in a <form method='post' target={current + page... it's a post-back}>...</form> tag. +

+

The <f:.../> tags are bind points for the business logic. They allow your snippet to easily replace the tag and + its children with what is supposed to be displayed. This mechanism is a little heavier than Wicket's mechanism for + binding display elements to logic and I've got a to-do to make Lift's mechanism work like Wickets as well... but + that's a digression. +

+

So, your Lift code will look like: +

+ + the new way +

+class Show {
+  def render = {
+    "#birthyear" #> SHtml.ajaxSelect(Show.yearsOptions,
+                                     Show.yearsDefault,
+                                     Show.yearsHandler) &
+      "name=name" #> SHtml.ajaxText("", false, Show.nameHandler _) &
+      "type=submit" #> submit("Submit", Show.submitHandler _)
+  }
+}
+
+object Show {
+  private var name: String = ""
+  private var year: Int = 0
+  val years: Map[String, Int] = Map("2006" -> 2006,
+                                    "2007" -> 2007,
+                                    "2008" -> 2008,
+                                    "2009" -> 2009,
+                                    "2010" -> 2010)
+  val yearsOptions: List[(String, String)] = ("" -> "") :: years.keys
+    .map(p => (p, p))
+    .toList
+  val yearsDefault = Empty
+  // The function to call when an option is picked:
+  def yearsHandler(selected: String): JsCmd = {
+    this.year = years(selected)
+    S.notice("Selected year '" + years(selected) + "'")
+    SetHtml("selectedyear",
+            <div>{"Selected year '" + years(selected) + "'"}</div>)
+  }
+  def nameHandler(name: String): JsCmd = {
+    this.name = name
+    S.notice("The name is '" + name + "'")
+    SetHtml("namevalue", <div>{"The name is '" + name + "'"}</div>)
+  }
+  def submitHandler(): JsCmd = {
+    S.notice(
+      "The name and year was set to (" + this.name + "," + this.year + ")")
+    S.redirectTo("#myForm")
+  }
+}
+        
+ + the old way +

 class Show {
    def myForm(xhtml: NodeSeq): NodeSeq = {
      var name = ""
@@ -106,19 +201,30 @@
                                        Empty, handleYear _))
    }
 }
+        
-
-

Note that no display code has crept into the snippet. You've simply -bound the HTML created by text() and select() to the <f:name/> and -<f:year/> tags in the incoming XHTML. -

Also, you've bound two functions (the anonymous function name = _ and -handleYear) to the HTML form elements. When the form is POSTed, -these functions (which are bound to local variables) will be -statefully invoked. -

If you are displaying a table rather than a -form, then the same binding logic still works. For example: -

-
+      

Note that no display code has crept into the snippet. You've simply bound the HTML created by text() and select() to + the <f:name/> and <f:year/> tags in the incoming XHTML. +

+

Also, you've bound two functions (the anonymous function + name = _ and handleYear) to the HTML form elements. When the form is POSTed, these functions (which are bound + to local variables) will be statefully invoked. +

+

If you are displaying a table rather than a form, then the same binding logic still works. For example: +

+ + the new way +

+<table data-lift="show:users">
+   <tr>
+     <td><span id="first_name">David</span></td>
+     <td><span id="last_name">Pollak</span></td>
+   </tr>
+</table>
+        
+ + the old way +

 <table>
 
 <lift:snippet type="show:users">
@@ -127,26 +233,34 @@
 </lift:snippet>
 
 </table>
-
-
+        
+ + +
+        
 class Show {
    def users(xhtml: NodeSeq): NodeSeq = Users.findAll.flatMap(user => bind("f", 
              xhtml, "first_name" --> user.firstName, "last_name" --> user.nameName))
 }
+        
+      
-
-

If you take the time to clearly define the bind points, then you can -have no display code at all in your snippets. -

Can display logic slip into a snippet? Yes, and as you've seen and -pointed out, the examples are less than optimal in this regard. -

Has display logic ever crept into a method called from an ERB -template? Yes, and very often it's a source of a potential Cross -Site Scripting vulnerability. -

Has business logic ever crept into an ERB template? Yes. -

In Lift, display can creep into a snippet, but business logic cannot -creep into a the static display template. Yes, your designers will -still have to police putting display logic in the snippet code, but -the coders will not have to police business logic in the templates. -

+

If you take the time to clearly define the bind points, then you can have no display code at all in your snippets. +

+

Can display logic slip into a snippet? Yes, and as you've seen and pointed out, the examples are less than optimal + in this regard. +

+

Has display logic ever crept into a method called from an ERB template? Yes, and very often it's a source of a potential + Cross Site Scripting vulnerability. +

+

Has business logic ever crept into an ERB template? Yes. +

+

In Lift, display can creep into a snippet, but business logic cannot creep into a the static display template. Yes, + your designers will still have to police putting display logic in the snippet code, but the coders will not have + to police business logic in the templates. +

+
-
+ + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/templating/surround.html b/combo/example/src/main/webapp/templating/surround.html index 5c5a1d3..b3b2dbd 100644 --- a/combo/example/src/main/webapp/templating/surround.html +++ b/combo/example/src/main/webapp/templating/surround.html @@ -1,117 +1,65 @@ - - - Lift Example - - -
- + +
+

Surround

+ +

+ Lift supports surrounding a given section of a page with a template from another file. This allows you to have master page + templates or even section templates that are accessed by many different pages. +

- -

- Lift supports surrounding a given section of a page with a template - from another file. This allows you to have master page templates - or even section templates that are accessed by many different pages. - -

-

- - -

-<lift:surround with="default" at="content">
+    
<div data-lift="surround?with=default;at=content">
   This is a simple page surrounded by the default template.
-</lift:surround>
-
- -

- -

Using the <lift:surround> tag, you can surround the body with -the contents of another template. The "with" attribute designates the -name of the template. By convension, templates are stored in the -/templates-hidden directory. Lift will not serve direct HTTP request -from any directory that contains "-hidden". The "at" attribute specifies -where the content should be placed in the loaded template. Lift will -look for a <lift:bind name="..."/> tag in the target -template and insert the body of the surround tag at the bind point. -

+</div>
- +

Using the + data-lift='surround...' attribute in a tag, you can surround it's body with the contents of another template. + The + "with" parameter designates the name of the template. By convension, templates are stored in the + /templates-hidden directory. Lift will not serve direct HTTP request from any directory that contains + "-hidden". The + "at" parameter specifies where the content should be placed in the loaded template. Lift will look for a tag + with a + id="content" attribute in the target template and insert the body of the surround tag at the bind point. +

-
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:lift='http://liftweb.net'>
-  <head>
-    <title>Lift Web Framework: <lift:Menu.title /></title>
-    
-    <lift:CSS.blueprint />
-    <lift:CSS.fancyType />
-    <script id="jquery" src="/classpath/jquery.js" type="text/javascript"/>
-    <script id="json" src="/classpath/json.js" type="text/javascript"/>
-  </head>
-  <body>
-    <div class="menu">
-      <lift:menu.builder/>
-    </div>
-    <div class="messages">
-      <lift:Msgs />
-    </div>
-    
-    <div class="contents">
-      <lift:bind name="content" />
-    </div>
-  </body>
-</html>
-
+ /templates-hidden/default.html +
<html>
+<head>
+  <title data-lift="Menu.title">Lift Web Framework: %*%</title>
+   :
+</head>
+<body>
+   :
+  <div class="sidebar">
+    <span data-lift="menu.builder"></span>
+  </div>
+   :
+  <div data-lift="Msgs"></div>
+   :
+  <div id="content">The main content will get bound here</div>
+   :
+</body>
+</html>
-
- - +
+ + \ No newline at end of file From 4fa7200700d22dc626638c8ceb344de4a2484f29 Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Sun, 31 Dec 2017 15:21:01 +0100 Subject: [PATCH 07/33] Templating / Web Services Work done mostly on templating and web services sections. --- combo/example/build.sbt | 2 +- .../net/liftweb/example/snippet/Ajax.scala | 159 +++++++------- .../liftweb/example/snippet/JSDialog.scala | 7 - .../net/liftweb/example/snippet/Misc.scala | 95 ++++---- .../example/snippet/SimpleWizard.scala | 120 ++++++++++ .../example/snippet/SimpleWizard.scalaREM | 104 --------- .../net/liftweb/example/view/XmlFun.scala | 3 +- combo/example/src/main/webapp/ajax-form.html | 11 +- combo/example/src/main/webapp/ajax.html | 146 ++++++------- .../src/main/webapp/assets/css/app.css | 21 +- combo/example/src/main/webapp/chat.html | 8 +- combo/example/src/main/webapp/database.html | 8 +- combo/example/src/main/webapp/form_ajax.html | 13 +- .../example/src/main/webapp/interactive.html | 6 +- combo/example/src/main/webapp/lazy.html | 74 ++++--- combo/example/src/main/webapp/parallel.html | 145 +++++------- .../example/src/main/webapp/rhodeisland.html | 67 +++--- .../src/main/webapp/simple/delete.html | 9 +- .../example/src/main/webapp/simple/index.html | 1 + .../src/main/webapp/simple_wizard.html | 206 ++++++++++-------- .../src/main/webapp/stateless_json.html | 11 +- .../src/main/webapp/templating/embed.html | 12 +- .../main/webapp/templating/eval_order.html | 107 +++++---- .../src/main/webapp/templating/head.html | 130 +++++------ .../src/main/webapp/templating/index.html | 203 ++++++++--------- .../main/webapp/templating/selectomatic.html | 180 ++++++++------- combo/example/src/main/webapp/ws.html | 205 ++++++++--------- 27 files changed, 1043 insertions(+), 1010 deletions(-) create mode 100644 combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWizard.scala delete mode 100644 combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWizard.scalaREM diff --git a/combo/example/build.sbt b/combo/example/build.sbt index 0a88075..609a9e1 100644 --- a/combo/example/build.sbt +++ b/combo/example/build.sbt @@ -4,7 +4,7 @@ organization := "net.liftweb" name := "demo" -version := "0.5.1-SNAPSHOT" +version := "0.5.2-SNAPSHOT" scalaVersion := "2.11.11" //2.12.2 diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Ajax.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Ajax.scala index 423206a..70ddb6a 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/Ajax.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Ajax.scala @@ -14,100 +14,93 @@ * limitations under the License. */ -package net.liftweb { - package example { - package snippet { +package net.liftweb.example.snippet - import net.liftweb.http._ - import net.liftmodules.widgets.autocomplete._ - import S._ - import SHtml._ - import js._ - import js.jquery._ - import JqJsCmds._ - import JsCmds._ - import common._ - import util._ - import Helpers._ - import _root_.scala.xml.{Text, NodeSeq} +import net.liftweb._ +import http._ +import SHtml._ +import js._ +import js.jquery._ +import JqJsCmds._ +import JsCmds._ +import common._ +import util._ +import Helpers._ +import net.liftmodules.widgets.autocomplete._ +import scala.xml.{Text, NodeSeq} - class Ajax extends Loggable { +class Ajax extends Loggable { - def sample(xhtml: NodeSeq): NodeSeq = { - // local state for the counter - var cnt = 0 + def sample(xhtml: NodeSeq): NodeSeq = { + // local state for the counter + var cnt = 0 - // get the id of some elements to update - val spanName: String = S.attr("id_name") openOr "cnt_id" - val msgName: String = S.attr("id_msgs") openOr "messages" + // get the id of some elements to update + val spanName: String = S.attr("id_name") openOr "cnt_id" + val msgName: String = S.attr("id_msgs") openOr "messages" - // build up an ajax tag to increment the counter - def doClicker(text: NodeSeq) = - a(() => { cnt = cnt + 1; SetHtml(spanName, Text(cnt.toString)) }, - text) + // build up an ajax tag to increment the counter + def doClicker(text: NodeSeq) = + a(() => { cnt = cnt + 1; SetHtml(spanName, Text(cnt.toString)) }, text) - // create an ajax select box - def doSelect(msg: NodeSeq) = - ajaxSelect( - (1 to 50).toList.map(i => (i.toString, i.toString)), - Full(1.toString), - v => { - val selectBind = "#selNumber" #> Text(v) - DisplayMessage( - msgName, - {selectBind(msg)}, - 5 seconds, - 1 second - ) - }, - "class" -> "form-control" - ) + // create an ajax select box + def doSelect(msg: NodeSeq) = + ajaxSelect( + (1 to 50).toList.map(i => (i.toString, i.toString)), + Full(1.toString), + v => { + val selectBind = "#selNumber" #> Text(v) + DisplayMessage( + msgName, + {selectBind(msg)}, + 5 seconds, + 1 second + ) + }, + "class" -> "form-control" + ) - // build up an ajax text box - def doText(msg: NodeSeq) = - ajaxText( - "", - v => { - val textBind = "#textValue" #> Text(v) - DisplayMessage(msgName, - {textBind(msg)}, - 6 seconds, - 1 second) - }, - "class" -> "form-control" - ) + // build up an ajax text box + def doText(msg: NodeSeq) = + ajaxText( + "", + v => { + val textBind = "#textValue" #> Text(v) + DisplayMessage(msgName, + {textBind(msg)}, + 6 seconds, + 1 second) + }, + "class" -> "form-control" + ) - // bind the view to the functionality - val viewBind = { - "#ajaxClicker" #> doClicker _ & - "#ajaxSelect" #> doSelect _ & - "#ajaxText" #> doText _ & - "#ajaxAuto" #> AutoComplete("", - buildQuery _, - (x:String) => (), - "class" -> "form-control") - } - viewBind(xhtml) - } + // bind the view to the functionality + val viewBind = { + "#ajaxClicker" #> doClicker _ & + "#ajaxSelect" #> doSelect _ & + "#ajaxText" #> doText _ & + "#ajaxAuto" #> AutoComplete("", + buildQuery _, + (x: String) => (), + "class" -> "form-control") + } + viewBind(xhtml) + } - private def buildQuery(current: String, limit: Int): Seq[String] = { - logger.info( - "Checking on server side with " + current + " limit " + limit) - (1 to limit).map(n => current + "" + n) - } + private def buildQuery(current: String, limit: Int): Seq[String] = { + logger.info("Checking on server side with " + current + " limit " + limit) + (1 to limit).map(n => current + "" + n) + } - def time = Text(now.toString) + def time = Text(now.toString) - def buttonClick = { - import js.JE._ + def buttonClick = { + import js.JE._ - "* [onclick]" #> SHtml.ajaxCall( - ValById("the_input"), - s => - SetHtml("bcmessages", - Latest Button click was with text box value '{s}')) - } - } - } + "* [onclick]" #> SHtml.ajaxCall( + ValById("the_input"), + s => + SetHtml("bcmessages", + Latest Button click was with text box value '{s}')) } } diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/JSDialog.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/JSDialog.scala index 1196133..08a6d60 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/JSDialog.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/JSDialog.scala @@ -43,13 +43,6 @@ package net.liftweb { Alert("Couldn't find _jsdialog_confirm template"), "class" -> "btn btn-primary") - /* def button(in: NodeSeq) = - ajaxButton(in, - () => - S.runTemplate(List("_bsdialog_confirm")).map(ns => JqSetHtml("modal",ns)) openOr - Alert("Couldn't find _jsdialog_confirm template"), - "class" -> "btn btn-primary", "data-toggle" -> "modal", "data-target" -> "modal") */ - // the template needs to bind to either server-side behavior // and unblock the UI def confirm = { diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Misc.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Misc.scala index e457727..6c9d0fd 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/Misc.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Misc.scala @@ -38,30 +38,36 @@ class Misc { private val logger = Logger(classOf[Misc]) /** - * Get the XHTML containing a list of users - */ + * Get the XHTML containing a list of users + */ def users: NodeSeq = { User.find() match { - case Empty => User.create.firstName("Archer").lastName("Dog").email("archer@dogfood.com").password("mypassword").save + case Empty => + User.create + .firstName("Archer") + .lastName("Dog") + .email("archer@dogfood.com") + .password("mypassword") + .save case _ => } // the header {User.htmlHeaders}EditDelete :: - // get and display each of the users - User.findAll(OrderBy(User.id, Ascending)).flatMap(u => {u.htmlLine} + // get and display each of the users + User.findAll(OrderBy(User.id, Ascending)).flatMap(u => {u.htmlLine} {link("/simple/edit", () => selectedUser(Full(u)), Text("Edit"), "class" -> "btn btn-sm btn-outline-secondary")} {link("/simple/delete", () => selectedUser(Full(u)), Text("Delete"), "class" -> "btn btn-sm btn-outline-secondary")} ) } /** - * Confirm deleting a user - */ + * Confirm deleting a user + */ def confirmDelete(xhtml: Group): NodeSeq = { (for (user <- selectedUser.is) // find the user - yield { + yield { def deleteUser() { - notice("User "+(user.firstName+" "+user.lastName)+" deleted") + notice("User " + (user.firstName + " " + user.lastName) + " deleted") user.delete_! redirectTo("/simple/index.html") } @@ -72,17 +78,16 @@ class Misc { // in the current content) val bindDelete = { "#username" #> (user.firstName.get + " " + user.lastName.get) & - "#delete" #> submit( - "Yes delete", - deleteUser _, - "type" -> "button", - "class" -> "btn btn-danger") + "#delete" #> submit("Yes delete", + deleteUser _, + "type" -> "button", + "class" -> "btn btn-danger") } - bindDelete(xhtml) + bindDelete(xhtml) // if the was no ID or the user couldn't be found, // display an error and redirect - }) openOr {error("User not found"); redirectTo("/simple/index.html")} + }) openOr { error("User not found"); redirectTo("/simple/index.html") } } // called when the form is submitted @@ -91,53 +96,54 @@ class Misc { // back to the "list" page case Nil => user.save; redirectTo("/simple/index.html") - // oops... validation errors - // display the errors and make sure our selected user is still the same + // oops... validation errors + // display the errors and make sure our selected user is still the same case x => { - logger.debug("SaveUser got a validation error="+x.toString()) + logger.debug("SaveUser got a validation error=" + x.toString()) S.error(x) selectedUser(Full(user)) } } /** - * Add a user - */ + * Add a user + */ def add(xhtml: Group): NodeSeq = - selectedUser.is.openOr(new User).toForm(Empty, saveUser _) ++ + selectedUser.is.openOr(new User).toForm(Empty, saveUser _) ++ Cancel /** - * Edit a user - */ + * Edit a user + */ def edit(xhtml: Group): NodeSeq = - selectedUser.map(_. - // get the form data for the user and when the form - // is submitted, call the passed function. - // That means, when the user submits the form, - // the fields that were typed into will be populated into - // "user" and "saveUser" will be called. The - // form fields are bound to the model's fields by this - // call. - toForm(Empty, saveUser _) ++ + selectedUser.map( + _. + // get the form data for the user and when the form + // is submitted, call the passed function. + // That means, when the user submits the form, + // the fields that were typed into will be populated into + // "user" and "saveUser" will be called. The + // form fields are bound to the model's fields by this + // call. + toForm(Empty, saveUser _) ++ Cancel - // bail out if the ID is not supplied or the user's not found - ) openOr {error("User not found"); redirectTo("/simple/index.html")} + // bail out if the ID is not supplied or the user's not found + ) openOr { error("User not found"); redirectTo("/simple/index.html") } // the request-local variable that hold the file parameter private object theUpload extends RequestVar[Box[FileParamHolder]](Empty) /** - * Bind the appropriate XHTML to the form - */ + * Bind the appropriate XHTML to the form + */ /*TODO PPE REM - * - * */ + * + * */ /* def upload(xhtml: Group): NodeSeq = if (S.get_?) bind("ul", chooseTemplate("choose", "get", xhtml), @@ -148,16 +154,19 @@ class Misc { "length" -> theUpload.is.map(v => Text(v.file.length.toString)), "md5" -> theUpload.is.map(v => Text(hexEncode(md5(v.file)))) ); - */ + */ def lang = { "#lang" #> locale.getDisplayLanguage(locale) & - "#select" #> SHtml.selectObj(locales.map(lo => (lo, lo.getDisplayName)), - definedLocale, setLocale) + "#select" #> SHtml.selectObj(locales.map(lo => (lo, lo.getDisplayName)), + definedLocale, + setLocale, + "class" -> "form-control") } private def locales = - Locale.getAvailableLocales.toList.sortWith(_.getDisplayName < _.getDisplayName) + Locale.getAvailableLocales.toList + .sortWith(_.getDisplayName < _.getDisplayName) private def setLocale(loc: Locale) = definedLocale(Full(loc)) } diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWizard.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWizard.scala new file mode 100644 index 0000000..3954340 --- /dev/null +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWizard.scala @@ -0,0 +1,120 @@ +/* + * Copyright 2007-2010 WorldWide Conferencing, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.liftweb.example.snippet + +import _root_.net.liftweb.http._ +import _root_.net.liftweb.http.S +import _root_.net.liftweb.http.SHtml._ +import _root_.net.liftweb.util.Helpers._ +import _root_.scala.xml.{NodeSeq} + +/** + * The Arc Challenge is Paul Graham's quest for web framework concision. + * + * http://www.paulgraham.com/arcchallenge.html + * + * This is one potential lift-based solution to it using StatefulSnippets. + * There are doubtless many other ways. + * + * @author: Steve Jenson + */ +class SimpleWizard extends StatefulSnippet { + val fromWhence = S.referer openOr "/" + var dispatch: DispatchIt = { + case _ => + xhtml => + pageOne + } + var name = "" + var quest = "" + var color = "" + + private def template(name: String, f: NodeSeq => NodeSeq): NodeSeq = + Templates(List("templating") ::: List(name)).map(f) openOr + NodeSeq.Empty + + /** + * pageOne -- Ask the name + */ + def pageOne = { + def validate() { + this.registerThisSnippet() + if (name.length < 2) S.error(S.?("Name too short")) + else + dispatch = { + case _ => + xhtml => + pageTwo + } + } + + template( + "pageOne", + ("#name" replaceWith text(name, name = _, "class" -> "form-control")) & + ("#submit" replaceWith submit(S ? "Next", + validate, + "class" -> "btn btn-primary"))) + } + + /** + * pageTwo -- Ask the quest + */ + def pageTwo = { + def validate() { + this.registerThisSnippet() + if (quest.length < 2) S.error(S.?("Quest too short")) + else + dispatch = { + case _ => + xhtml => + pageThree + } + } + + template( + "pageTwo", + ("#quest" replaceWith text(quest, quest = _, "class" -> "form-control")) & + ("#submit" replaceWith submit(S ? "Next", + validate, + "class" -> "btn btn-primary"))) + } + + /** + * pageThree -- Ask the color + */ + def pageThree = { + def validate() { + this.registerThisSnippet() + if (!List("red", "yellow", "blue").contains(color.toLowerCase)) + S.error(S.?("Color not red, yellow or blue")) + else { + S.notice( + "You, " + name + " on the quest " + quest + " may cross the bridge of sorrows") + S.redirectTo(fromWhence) + } + } + + template( + "pageThree", + ("#color" replaceWith text(color, color = _, "class" -> "form-control")) & + ("#submit" replaceWith submit(S ? "Finish", + validate, + "class" -> "btn btn-primary")) + ) + } + +} diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWizard.scalaREM b/combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWizard.scalaREM deleted file mode 100644 index bca42bf..0000000 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWizard.scalaREM +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2007-2010 WorldWide Conferencing, LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.liftweb { -package example { -package snippet { - -import _root_.net.liftweb.example.model._ -import _root_.net.liftweb.http._ -import _root_.net.liftweb.http.S -import _root_.net.liftweb.mapper._ -import _root_.net.liftweb.http.S._ -import _root_.net.liftweb.http.SHtml._ -import _root_.net.liftweb.util.Helpers._ -import _root_.net.liftweb.common._ -import _root_.net.liftweb.util._ -import _root_.scala.xml.{NodeSeq, Text, Group} - -/** - * The Arc Challenge is Paul Graham's quest for web framework concision. - * - * http://www.paulgraham.com/arcchallenge.html - * - * This is one potential lift-based solution to it using StatefulSnippets. - * There are doubtless many other ways. - * - * @author: Steve Jenson - */ -class SimpleWizard extends StatefulSnippet { - val fromWhence = S.referer openOr "/" - var dispatch: DispatchIt = {case _ => xhtml => pageOne} - var name = "" - var quest = "" - var color = "" - - private def template(name: String, f: NodeSeq => NodeSeq): NodeSeq = - Templates(List("templating") ::: List(name)).map(f) openOr - NodeSeq.Empty - - /** - * pageOne -- Ask the name - */ - def pageOne = { - def validate() { - this.registerThisSnippet() - if (name.length < 2) S.error(S.?("Name too short")) - else dispatch = {case _ => xhtml => pageTwo} - } - - template("pageOne", - ("#name" replaceWith text(name, name = _)) & - ("#submit" replaceWith submit(S ? "Next", validate))) - } - - /** - * pageTwo -- Ask the quest - */ - def pageTwo = { - def validate() { - this.registerThisSnippet() - if (quest.length < 2) S.error(S.?("Quest too short")) - else dispatch = {case _ => xhtml => pageThree} - } - - template("pageTwo", - ("#quest" replaceWith text(quest, quest = _)) & - ("#submit" replaceWith submit(S ? "Next", validate))) - } - - /** - * pageThree -- Ask the color - */ - def pageThree = { - def validate() { - this.registerThisSnippet() - if (!List("red", "yellow", "blue").contains(color.toLowerCase)) S.error(S.?("Color not red, yellow or blue")) - else { - S.notice("You, "+name+" on the quest "+quest+" may cross the bridge of sorrows") - S.redirectTo(fromWhence) - } - } - - template("pageThree", - ("#color" replaceWith text(color, color = _)) & - ("#submit" replaceWith submit(S ? "Finish", validate))) - } - -} -} -} -} diff --git a/combo/example/src/main/scala/net/liftweb/example/view/XmlFun.scala b/combo/example/src/main/scala/net/liftweb/example/view/XmlFun.scala index 45448cc..32f1154 100644 --- a/combo/example/src/main/scala/net/liftweb/example/view/XmlFun.scala +++ b/combo/example/src/main/scala/net/liftweb/example/view/XmlFun.scala @@ -51,7 +51,8 @@ class XmlFun extends LiftView { - The XML is + + The XML is
{addresses.map{e => Text(e.toString) :: 
:: Nil}}

The count for {toCount} nodes is {countryCount(toCount, addresses)}

Count US addresses.

diff --git a/combo/example/src/main/webapp/ajax-form.html b/combo/example/src/main/webapp/ajax-form.html index e126781..72eb94f 100644 --- a/combo/example/src/main/webapp/ajax-form.html +++ b/combo/example/src/main/webapp/ajax-form.html @@ -38,9 +38,10 @@

AJAX Form Update

- Here's the code: -

-class AjaxForm {
+
+    The
+      Lift Scala code
+    
class AjaxForm {
   var state = AjaxForm.state
   var city = ""
 
@@ -87,9 +88,7 @@ 

AJAX Form Update

def citiesFor(state: String): List[String] = citiesAndStates.filter(_._1 == state).map(_._2) -} - -
+}
diff --git a/combo/example/src/main/webapp/ajax.html b/combo/example/src/main/webapp/ajax.html index 6bb9bb8..ec8152c 100644 --- a/combo/example/src/main/webapp/ajax.html +++ b/combo/example/src/main/webapp/ajax.html @@ -80,89 +80,85 @@

AJAX Samples

-
The - Lift Scala code to render the controls:
-

-class Ajax extends Loggable {
-
-    def sample(xhtml: NodeSeq): NodeSeq = {
-      // local state for the counter
-      var cnt = 0
-
-      // get the id of some elements to update
-      val spanName: String = S.attr("id_name") openOr "cnt_id"
-      val msgName: String = S.attr("id_msgs") openOr "messages"
-
-      // build up an ajax <a> tag to increment the counter
-      def doClicker(text: NodeSeq) =
-        a(() => { cnt = cnt + 1; SetHtml(spanName, Text(cnt.toString)) },
-          text)
-
-      // create an ajax select box
-      def doSelect(msg: NodeSeq) =
-        ajaxSelect(
-          (1 to 50).toList.map(i => (i.toString, i.toString)),
-          Full(1.toString),
-          v => {
-            val selectBind = "#selNumber" #> Text(v)
-            DisplayMessage(
-              msgName,
-              <span>{selectBind(msg)}</span>,
-              5 seconds,
-              1 second
-            )
-          },
-          "class" -> "form-control"
-        )
-
-      // build up an ajax text box
-      def doText(msg: NodeSeq) =
-        ajaxText(
-          "",
-          v => {
-            val textBind = "#textValue" #> Text(v)
-            DisplayMessage(msgName,
-                           <span>{textBind(msg)}</span>,
-                           6 seconds,
-                           1 second)
-          },
-          "class" -> "form-control"
-        )
-
-      // bind the view to the functionality
-      val viewBind = {
-        "#ajaxClicker" #> doClicker _ &
-          "#ajaxSelect" #> doSelect _ &
-          "#ajaxText" #> doText _ &
-          "#ajaxAuto" #> AutoComplete("",
-                                      buildQuery _,
-                                      _ => (),
-                                      "class" -> "form-control")
-      }
-      viewBind(xhtml)
+        The
+            Lift Scala code
+        
class Ajax extends Loggable {
+
+  def sample(xhtml: NodeSeq): NodeSeq = {
+    // local state for the counter
+    var cnt = 0
+
+    // get the id of some elements to update
+    val spanName: String = S.attr("id_name") openOr "cnt_id"
+    val msgName: String = S.attr("id_msgs") openOr "messages"
+
+    // build up an ajax <a> tag to increment the counter
+    def doClicker(text: NodeSeq) =
+      a(() => { cnt = cnt + 1; SetHtml(spanName, Text(cnt.toString)) }, text)
+
+    // create an ajax select box
+    def doSelect(msg: NodeSeq) =
+      ajaxSelect(
+        (1 to 50).toList.map(i => (i.toString, i.toString)),
+        Full(1.toString),
+        v => {
+          val selectBind = "#selNumber" #> Text(v)
+          DisplayMessage(
+            msgName,
+            <span>{selectBind(msg)}</span>,
+            5 seconds,
+            1 second
+          )
+        },
+        "class" -> "form-control"
+      )
+
+    // build up an ajax text box
+    def doText(msg: NodeSeq) =
+      ajaxText(
+        "",
+        v => {
+          val textBind = "#textValue" #> Text(v)
+          DisplayMessage(msgName,
+                         <span>{textBind(msg)}</span>,
+                         6 seconds,
+                         1 second)
+        },
+        "class" -> "form-control"
+      )
+
+    // bind the view to the functionality
+    val viewBind = {
+      "#ajaxClicker" #> doClicker _ &
+        "#ajaxSelect" #> doSelect _ &
+        "#ajaxText" #> doText _ &
+        "#ajaxAuto" #> AutoComplete("",
+                                    buildQuery _,
+                                    (x: String) => (),
+                                    "class" -> "form-control")
     }
+    viewBind(xhtml)
+  }
 
-    private def buildQuery(current: String, limit: Int): Seq[String] = {
-      logger.info(
-        "Checking on server side with " + current + " limit " + limit)
-      (1 to limit).map(n => current + "" + n)
-    }
+  private def buildQuery(current: String, limit: Int): Seq[String] = {
+    logger.info("Checking on server side with " + current + " limit " + limit)
+    (1 to limit).map(n => current + "" + n)
+  }
 
-    def time = Text(now.toString)
+  def time = Text(now.toString)
 
-    def buttonClick = {
-      import js.JE._
+  def buttonClick = {
+    import js.JE._
 
-      "* [onclick]" #> SHtml.ajaxCall(
-        ValById("the_input"),
-        s =>
-          SetHtml("bcmessages",
-                  <i>Latest Button click was with text box value '{s}'</i>))
-    }
+    "* [onclick]" #> SHtml.ajaxCall(
+      ValById("the_input"),
+      s =>
+        SetHtml("bcmessages",
+                <i>Latest Button click was with text box value '{s}'</i>))
   }
-    
+}
diff --git a/combo/example/src/main/webapp/assets/css/app.css b/combo/example/src/main/webapp/assets/css/app.css index 2182261..818c3c3 100644 --- a/combo/example/src/main/webapp/assets/css/app.css +++ b/combo/example/src/main/webapp/assets/css/app.css @@ -84,7 +84,9 @@ border-left: none; } - +.dpp_stuff p { + padding-left: 15px; +} /*Lift stuff*/ @@ -134,6 +136,23 @@ border-bottom: 1px solid #ccc; } + +.bd-callout { + padding: 1.25rem; + margin-top: 1.25rem; + margin-bottom: 1.25rem; + border: 1px solid #eee; + border-left-width: .25rem; + border-radius: .25rem; +} + +.bd-callout-info { + border-left-color: #5bc0de; +} +.bd-callout-warning { + border-left-color: #f0ad4e; +} + /* bootstrap overrides */ .breadcrumb { font-size: 0.8rem; diff --git a/combo/example/src/main/webapp/chat.html b/combo/example/src/main/webapp/chat.html index a29e061..15b8db5 100644 --- a/combo/example/src/main/webapp/chat.html +++ b/combo/example/src/main/webapp/chat.html @@ -51,8 +51,9 @@

Comet Chat

widget for the name. Until the AskName comet widget provides a name, all rendering messages are forwarded to AskName. Here's the code for the "AskName":

-

-class Chat extends CometActor with CometListener {
+    The
+      Lift Scala code
+    
class Chat extends CometActor with CometListener {
   private var userName = ""
   private var chats: List[ChatLine] = Nil
 
@@ -134,8 +135,7 @@ 

Comet Chat

} } -} -
+}

This example demonstrates the power of Scala's Actors and diff --git a/combo/example/src/main/webapp/database.html b/combo/example/src/main/webapp/database.html index 059365a..6ab9fac 100644 --- a/combo/example/src/main/webapp/database.html +++ b/combo/example/src/main/webapp/database.html @@ -32,8 +32,9 @@

Database

to create more records.

-

-/**
+        The
+            Lift Scala code
+        
/**
   * The singleton that has methods for accessing the database
   */
 object User extends User with KeyedMetaMapper[Long, User] {
@@ -57,8 +58,7 @@ 

Database

override def textareaCols = 50 override def displayName = "Personal Essay" } -} -
+}

For this little bit of work, we get a complete user with first name, last name, password, email, and a personal essay. Each of the fields knows how to validate itself. The User (and any other "Mapped" class) knows how to Create, diff --git a/combo/example/src/main/webapp/form_ajax.html b/combo/example/src/main/webapp/form_ajax.html index 9251310..fb1b060 100644 --- a/combo/example/src/main/webapp/form_ajax.html +++ b/combo/example/src/main/webapp/form_ajax.html @@ -32,7 +32,7 @@

Example: Forms with Ajax callback on form element blur

- +
@@ -40,9 +40,10 @@

Example: Forms with Ajax callback on form element blur

-
Here's the code: -

-class FormWithAjax extends StatefulSnippet {
+    
+ The + Lift Scala code +
class FormWithAjax extends StatefulSnippet {
   private var firstName = ""
   private var lastName = ""
   private val from = S.referer openOr "/"
@@ -84,8 +85,8 @@ 

Example: Forms with Ajax callback on form element blur

"placeholder" -> "Last name") & "type=submit" #> submit("Send", validate _, "class" -> "btn btn-primary") } -} -
+}
+
diff --git a/combo/example/src/main/webapp/interactive.html b/combo/example/src/main/webapp/interactive.html index b06929b..0e4a05d 100644 --- a/combo/example/src/main/webapp/interactive.html +++ b/combo/example/src/main/webapp/interactive.html @@ -29,8 +29,7 @@

Ajax

Comet

Lift supports Comet-style long polling with very little work on the part of the developer. Here's the code the implements the clock that you see in this demo: -

-class ExampleClock(initSession: LiftSession,
+            
class ExampleClock(initSession: LiftSession,
                    initType: Box[String],
                    initName: Box[String],
                    initDefaultXml: NodeSeq,
@@ -56,8 +55,7 @@ 

Comet

initCometActor(creationInfo) } -case object Tick -
+case object Tick
diff --git a/combo/example/src/main/webapp/lazy.html b/combo/example/src/main/webapp/lazy.html index ed2585a..14f7be3 100644 --- a/combo/example/src/main/webapp/lazy.html +++ b/combo/example/src/main/webapp/lazy.html @@ -1,52 +1,66 @@ + Template + -
-

- Lift supports lazy loading of a snippet. This is - useful when a snippet may take a long time to render, - but you want to return the page to the browser quickly. -

- -
-
-
+
+

Lazy Loading

+ +

+ Lift supports lazy loading of a snippet. This is useful when a snippet may take a long time to render, but you want to return + the page to the browser quickly. +

-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
+ +
+
+
-
+
-

The Markup:

-
<div data-lift="lazy-load">
-    <div data-lift="LongTime"></div>
-  </div>
+ The Markup +
<div data-lift="lazy-load">
+  <div data-lift="LongTime"></div>
+</div>
-

The Scala Code:

-
object LongTime {
+    The
+      Lift Scala code
+    
object LongTime {
   def render = {
     val delay = 1000L + randomLong(10000)
 
     Thread.sleep(delay)
 
-    <div>
-    This thread delayed {delay / 1000L} seconds
-    </div>
+    <div>This thread delayed {delay / 1000L} seconds</div>
   }
 }
-
+
+ + \ No newline at end of file diff --git a/combo/example/src/main/webapp/parallel.html b/combo/example/src/main/webapp/parallel.html index 23e77fe..6e41afd 100644 --- a/combo/example/src/main/webapp/parallel.html +++ b/combo/example/src/main/webapp/parallel.html @@ -1,101 +1,70 @@ - -

- Lift allows you to execute multiple snippets in - parallel during the rendering of a page. This allows you - to fork off multiple long-running jobs (e.g., talking - to an external ad server) and run them in parallel. All - the jobs must complete before the final page render is sent back - to the browser. The lift:parallel="true" attribute - designates a snippet as parallel. -

+ + -
- Exec inline: -
-
+ + + Template + -
- Exec parallel: -
-
+ +
+

Parallel Snippets

+ -
- Exec parallel: -
-
+

+ Lift allows you to execute multiple snippets in parallel during the rendering of a page. This allows you to fork off multiple + long-running jobs (e.g., talking to an external ad server) and run them in parallel. All the jobs must complete before + the final page render is sent back to the browser. The lift:parallel="true" attribute designates a snippet as parallel. +

-
- Exec inline: -
-
+
+ Exec inline: +
+
+
+ Exec parallel: +
+
- + The Markup +
<div>
+  Exec parallel:
+  <div data-lift="Parallel?parallel=true"></div>
+</div>
-
- -

The Markup:

-
-  <div>
-    Exec parallel:
-    <lift:Parallel  lift:parallel="true"/>
-  </div>
-
- -

The Scala Code:

-
-object Parallel {
-  def render = {
-    <div>This snippet evaluated on 
-    {
-      Thread.currentThread.getName()
-    } 
-    </div>
+    The
+      Lift Scala code
+    
object Parallel {
+  def threadName() = Thread.currentThread.getName()
+  def render = {
+    <div>This snippet evaluated on {threadName()}</div>
   }
-}
-
+}
+
+ -
+ \ No newline at end of file diff --git a/combo/example/src/main/webapp/rhodeisland.html b/combo/example/src/main/webapp/rhodeisland.html index 599fe75..f3df54d 100644 --- a/combo/example/src/main/webapp/rhodeisland.html +++ b/combo/example/src/main/webapp/rhodeisland.html @@ -24,48 +24,51 @@

Modal Dialog

-

This section has two examples showing how you could work with modal dialogs that has there template data in a separate - file. The first example is a plain javascript dialog while the other one is taking advantage of bootstrap's javascript - modals.

+

This section has + two examples showing how you could work with modal dialogs that has there template data in a separate file. + The first example is a plain javascript dialog while the other one is taking advantage of bootstrap's javascript modals. +

JS dialog example

Destroy Rhode Island
-
The code: -

-      class JSDialog {
-        // build the button... when pressed, present
-        // a dialog based on running the _jsdialog_confirm
-        // template
-        def button(in: NodeSeq) =
-          ajaxButton(in,
-                     () =>
-                       S.runTemplate(List("_jsdialog_confirm"))
-                         .map(ns => ModalDialog(ns)) openOr
-                         Alert("Couldn't find _jsdialog_confirm template"))
+    
+ The + Lift Scala code +
class JSDialog {
+  // build the button... when pressed, present
+  // a dialog based on running the _jsdialog_confirm
+  // template
+  def button(in: NodeSeq) =
+    ajaxButton(in,
+               () =>
+                 S.runTemplate(List("_jsdialog_confirm"))
+                   .map(ns => ModalDialog(ns)) openOr
+                   Alert("Couldn't find _jsdialog_confirm template"))
 
-        // the template needs to bind to either server-side behavior
-        // and unblock the UI
-        def confirm = {
-          "#yes" #> ((b: NodeSeq) =>
-            ajaxButton(b, () => {
-              println("Rhode Island Destroyed")
-              Unblock & Alert("Rhode Island Destroyed")
-            })) &
-            "#no" #> ((b: NodeSeq) => <button onclick={Unblock.toJsCmd}>
-                {b}
-              </button>)
-        }
-      }
-    
+ // the template needs to bind to either server-side behavior + // and unblock the UI + def confirm = { + "#yes" #> ((b: NodeSeq) => + ajaxButton(b, () => { + println("Rhode Island Destroyed") + Unblock & Alert("Rhode Island Destroyed") + })) & + "#no" #> ((b: NodeSeq) => <button onclick={Unblock.toJsCmd}> + {b} + </button>) + } +}

Bootstrap dialog example

Destroy Rhode Island
-
The code: +
+ The + Lift Scala code

 class BSDialog {
   // build the button... when pressed, present
@@ -89,7 +92,9 @@ 

Bootstrap dialog example

b, () => { logger.debug("Rhode Island intact") - Noop //we could have used closeDialog here but we are instead using the data-dismiss attribute. + // we could have used closeDialog here but we + // are instead using the data-dismiss attribute. + Noop }, "type" -> "button", "class" -> "btn btn-primary", diff --git a/combo/example/src/main/webapp/simple/delete.html b/combo/example/src/main/webapp/simple/delete.html index b2a6d68..a75bc81 100644 --- a/combo/example/src/main/webapp/simple/delete.html +++ b/combo/example/src/main/webapp/simple/delete.html @@ -24,8 +24,13 @@

Delete User

-

Do you really want to delete user: ''?

- +

Do you really want to delete user: + ' + '?

+
+ No +
+
diff --git a/combo/example/src/main/webapp/simple/index.html b/combo/example/src/main/webapp/simple/index.html index d1b6c8e..44dd120 100644 --- a/combo/example/src/main/webapp/simple/index.html +++ b/combo/example/src/main/webapp/simple/index.html @@ -5,6 +5,7 @@ Template +

Simple Forms

diff --git a/combo/example/src/main/webapp/simple_wizard.html b/combo/example/src/main/webapp/simple_wizard.html index 395ddec..ba451b5 100644 --- a/combo/example/src/main/webapp/simple_wizard.html +++ b/combo/example/src/main/webapp/simple_wizard.html @@ -1,110 +1,132 @@ - -
+ + -
- Code to generate. It's easier to use Wizard, but - here's the "old fashioned" way. -
+ + + Template + - +
-
-/**
- * The Arc Challenge is Paul Graham's quest for web framework concision.
- *
- * http://www.paulgraham.com/arcchallenge.html
- *
- * This is one potential lift-based solution to it using StatefulSnippets.
- * There are doubtless many other ways.
- *
- * @author: Steve Jenson
- */
-class SimpleWizard extends StatefulSnippet {
-  val fromWhence = S.referer openOr "/"
-  var dispatch: DispatchIt = {case _ => xhtml => pageOne}
-  var name = ""
-  var quest = ""
-  var color = ""
+    

+ Code to generate. It's easier to use Wizard, but here's the "old fashioned" way. +

- /** - * pageOne -- Ask the name - */ - def pageOne = { - def validate() { - this.registerThisSnippet() - if (name.length < 2) S.error(S.?("Name too short")) - else dispatch = {case _ => xhtml => pageTwo} + The + Lift Scala code +
/**
+  * The Arc Challenge is Paul Graham's quest for web framework concision.
+  *
+  * http://www.paulgraham.com/arcchallenge.html
+  *
+  * This is one potential lift-based solution to it using StatefulSnippets.
+  * There are doubtless many other ways.
+  *
+  * @author: Steve Jenson
+  */
+class SimpleWizard extends StatefulSnippet {
+  val fromWhence = S.referer openOr "/"
+  var dispatch: DispatchIt = {
+    case _ =>
+      xhtml =>
+        pageOne
   }
+  var name = ""
+  var quest = ""
+  var color = ""
 
-  TemplateFinder.findAnyTemplate(List("templating", "pageOne")).map(html =>
-  bind("wizard", html, "name" -> text(name, s => name = s), "submit" -> submit(S.?("Next"), validate))) openOr NodeSeq.Empty
-  }
+  private def template(name: String, f: NodeSeq => NodeSeq): NodeSeq =
+    Templates(List("templating") ::: List(name)).map(f) openOr
+      NodeSeq.Empty
 
-  /**
-   * pageTwo -- Ask the quest
-   */
-  def pageTwo = {
-  def validate() {
-    this.registerThisSnippet()
-    if (quest.length < 2) S.error(S.?("Quest too short"))
-    else dispatch = {case _ => xhtml => pageThree}
-  }
+  /**
+    * pageOne -- Ask the name
+    */
+  def pageOne = {
+    def validate() {
+      this.registerThisSnippet()
+      if (name.length < 2) S.error(S.?("Name too short"))
+      else
+        dispatch = {
+          case _ =>
+            xhtml =>
+              pageTwo
+        }
+    }
 
-  TemplateFinder.findAnyTemplate(List("templating", "pageTwo")).map(html =>
-  bind("wizard", html, "quest" -> text(quest, s => quest = s), "submit" -> submit(S.?("Next"), validate))) openOr NodeSeq.Empty
+    template(
+      "pageOne",
+      ("#name" replaceWith text(name, name = _, "class" -> "form-control")) &
+        ("#submit" replaceWith submit(S ? "Next",
+                                      validate,
+                                      "class" -> "btn btn-primary")))
   }
 
-    /**
-   * pageThree -- Ask the color
-   */
-  def pageThree = {
-  def validate() {
-     this.registerThisSnippet()
-    if (!List("red", "yellow", "blue").contains(color.toLowerCase)) S.error(S.?("Color not red, yellow or blue"))
-    else {
-      S.notice("You, "+name+" on the quest "+quest+" may cross the bridge of sorrows")
-      S.redirectTo(fromWhence)
+  /**
+    * pageTwo -- Ask the quest
+    */
+  def pageTwo = {
+    def validate() {
+      this.registerThisSnippet()
+      if (quest.length < 2) S.error(S.?("Quest too short"))
+      else
+        dispatch = {
+          case _ =>
+            xhtml =>
+              pageThree
+        }
     }
+
+    template(
+      "pageTwo",
+      ("#quest" replaceWith text(quest, quest = _, "class" -> "form-control")) &
+        ("#submit" replaceWith submit(S ? "Next",
+                                      validate,
+                                      "class" -> "btn btn-primary")))
   }
 
-  TemplateFinder.findAnyTemplate(List("templating", "pageThree")).map(html =>
-  bind("wizard", html, "color" -> text(color, s => color = s), "submit" -> submit(S.?("Finish"), validate))) openOr NodeSeq.Empty
+  /**
+    * pageThree -- Ask the color
+    */
+  def pageThree = {
+    def validate() {
+      this.registerThisSnippet()
+      if (!List("red", "yellow", "blue").contains(color.toLowerCase))
+        S.error(S.?("Color not red, yellow or blue"))
+      else {
+        S.notice(
+          "You, " + name + " on the quest " + quest + " may cross the bridge of sorrows")
+        S.redirectTo(fromWhence)
+      }
+    }
+
+    template(
+      "pageThree",
+      ("#color" replaceWith text(color, color = _, "class" -> "form-control")) &
+        ("#submit" replaceWith submit(S ? "Finish",
+                                      validate,
+                                      "class" -> "btn btn-primary"))
+    )
   }
 
-}
-
+}
+
+ - + \ No newline at end of file diff --git a/combo/example/src/main/webapp/stateless_json.html b/combo/example/src/main/webapp/stateless_json.html index 679afd7..6c5bd09 100644 --- a/combo/example/src/main/webapp/stateless_json.html +++ b/combo/example/src/main/webapp/stateless_json.html @@ -37,9 +37,11 @@

Stateless JSON Sample

-
Here's the code: -

-object StatelessJson {
+
+    
+ + The Scala Code +
object StatelessJson {
   def init() {
     // register the JSON handler
     LiftRules.statelessDispatch.append {
@@ -69,8 +71,7 @@ 

Stateless JSON Sample

case x => <b>Problem... didn't handle JSON message {x}</b> } )) -} -
+}
diff --git a/combo/example/src/main/webapp/templating/embed.html b/combo/example/src/main/webapp/templating/embed.html index 7b1638c..8fe5b62 100644 --- a/combo/example/src/main/webapp/templating/embed.html +++ b/combo/example/src/main/webapp/templating/embed.html @@ -31,6 +31,8 @@

Embed

The above paragraph was embedded using this code:

+ + The Markup
<span data-lift="embed?what=/templating/_sample_embed"></span>

@@ -41,12 +43,16 @@

Embed

Lift will select templates based on the current localization setting for the session. Lift uses the function in LiftRules.localeCalculator to determine the current locale for template selection. By default the function is: +

-
def defaultLocaleCalculator(request: Box[HttpServletRequest]) =
+    The
+      Lift Scala code
+    
def defaultLocaleCalculator(request: Box[HttpServletRequest]) =
     request.flatMap(_.getLocale() match
                     {case null => Empty
-                     case l: Locale => Full(l)}).openOr(Locale.getDefault())
But you can customize the function to return the locale of the currently logged in user, detect the IP address of - the request, etc. + case l: Locale => Full(l)}).openOr(Locale.getDefault())
+

But you can customize the function to return the locale of the currently logged in user, detect the IP address of the + request, etc.

diff --git a/combo/example/src/main/webapp/templating/eval_order.html b/combo/example/src/main/webapp/templating/eval_order.html index 52617d5..6a4b78b 100644 --- a/combo/example/src/main/webapp/templating/eval_order.html +++ b/combo/example/src/main/webapp/templating/eval_order.html @@ -1,65 +1,58 @@ - - - Lift Example - - -

- + +
+

Evalutation Order

+ -

- Lift evaluates the <lift:xxx/> tags from the outside in, otherwise -knows as lazy evaluation. This means that the following code will only -embed a single template: - -

-<lift:TestCond.loggedout>
-  <lift:embed what="/templates/_login_panel"/>
-</lift:TestCond.loggedout>
+        

+ Lift evaluates the <xxx data-lift="yyy"/> <lift:xxx/> tags from the outside in, otherwise knows as lazy evaluation. + This means that the following code will only embed a single template:

-<lift:TestCond.loggedin> - <lift:embed what="/templates/_welcome_message"/> -</lift:TestCond.loggedin> -
+ HTML syntax +
<span data-lift="TestCond.loggedout">
+  <span data-lift="embed?what=/templates/_login_panel"/>
+</span>
 
-Depending on if the user is logged in.
-

+<span data-lift="TestCond.loggedin"> + <span data-lift="embed?what=/templates/_welcome_message"/> +</span>
+ + The older XHTML syntax +
<lift:TestCond.loggedout>
+  <lift:embed what="/templates/_login_panel"/>
+</lift:TestCond.loggedout>
+
+<lift:TestCond.loggedin>
+  <lift:embed what="/templates/_welcome_message"/>
+</lift:TestCond.loggedin>
+ + +

+ Depending on if the user is logged in. +

+ +

+ This also means that a snippet may return XML that contains other snippet tags. +

-

- This also means that a snippet may return XML that - contains other snippet tags. -

- - + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/templating/head.html b/combo/example/src/main/webapp/templating/head.html index f995344..7b7f009 100644 --- a/combo/example/src/main/webapp/templating/head.html +++ b/combo/example/src/main/webapp/templating/head.html @@ -1,75 +1,63 @@ - - - Lift Example - - -
- - -

- Lift keeps the page as an XML data structure throughout - the rendering process. This allows Lift to rewrite the - web page at different rendering phases. After the - rendering is complete, Lift looks for all the - <head/> - tags in the <body> tag. Lift - takes the contents of each <head/> - tag, removes any duplicates and places - the resulting tags in the top level <head/> - tag. -

- -

- So, if you have the following code in your page: - - -

-<span>
-  <head>
-    <script type="text/javascript" src="/classpath/jquery.js"/>
-  </head>
+
+
+
+
+  
+  Template
+
+
+
+  
+

<head/> tag

+ + +

+ Lift keeps the page as an XML data structure throughout the rendering process. This allows Lift to rewrite the web page at + different rendering phases. After the rendering is complete, Lift looks for all the + <head/> + tags in the + <body> tag. Lift takes the contents of each + <head/> + tag, removes any duplicates and places the resulting tags in the top level + <head/> + tag. +

+ +

+ So, if you have the following code in your page: +

+ The Markup +
<span>
+  <head>
+    <script type="text/javascript" src="/classpath/jquery.js"/>
+  </head>
   do some jQuery stuff here
-</span>
-
-
-<span>
-  <head>
-    <script type="text/javascript" src="/classpath/jquery.js"/>
-  </head>
+</span>
+...
+<span>
+  <head>
+    <script type="text/javascript" src="/classpath/jquery.js"/>
+  </head>
   Do some other jQuery stuff here
-</span>
-
-Lift will put a single <script type="text/javascript" src="/classpath/jquery.js"/> into the <head/> of the page. -

+</span>
+ +

+ Lift will put a single + <script type="text/javascript" src="/classpath/jquery.js"/> into the + <head/> of the page. +

-
- - +
+ + \ No newline at end of file diff --git a/combo/example/src/main/webapp/templating/index.html b/combo/example/src/main/webapp/templating/index.html index 73d7792..2775ab9 100644 --- a/combo/example/src/main/webapp/templating/index.html +++ b/combo/example/src/main/webapp/templating/index.html @@ -8,29 +8,27 @@
-

Templating

- - -

The following is David Pollak's take on web framework view technology:

The Lift design is born out of my experience (both good and bad) with a variety of technologies including Rails.

@@ -71,59 +69,69 @@

Templating

So, the quintessential use of Lift's templates are as follows:

-
- - - - - -
-
-
-
-
-
- - the new way -

-<html>
-...
-      <form data-lift="Show?form=post" id="myForm">
-          <label for="name">Name</label>
-          <input type="text" name="name" id="name" placeholder="Name">
-          <label for="birthyear">Birthyear</label>
-          <select id="birthyear">
-              <option>2006</option>
-              <option>2007</option>
-          </select>
-          <input type="submit" value="Submit"/>
-      </form>
-      <div id="selectedyear"></div>
-      <div id="namevalue"></div>
-      <div id="wrappedNotice">
-        <div data-lift="Msgs"></div>
-      </div>
-...
-</html>
-        
- +
+ + + + + +
+
+
+
+
+
- the old way -

-<html>
-...
-<lift:show.myForm form="post">
-
-<tr><td>Name</td><td><f:name><input type="text"/></f:name></td></tr>
-<tr><td>Birthyear</td><td><f:year><select><option>2007</option><option>2006</option></select></f:year></td></tr>
+      HTML syntax
+      
<html>
+  ...
+  <form data-lift="Show?form=post" id="myForm">
+     <label for="name">Name</label>
+     <input type="text" name="name" id="name" placeholder="Name">
+     <label for="birthyear">Birthyear</label>
+     <select id="birthyear">
+        <option>2006</option>
+        <option>2007</option>
+     </select>
+     <input type="submit" value="Submit"/>
+  </form>
+  <div id="selectedyear"></div>
+  <div id="namevalue"></div>
+  <div id="wrappedNotice">
+    <div data-lift="Msgs"></div>
+  </div>
+  ...
+</html>
-<tr><td>&nbsp;</td><td><input type="submit" value="Add"/></td></tr> -</lift:show.myForm> -... + The older XHTML syntax +
<html>
+  ...
+  <lift:show.myForm form="post">
+    <tr>
+      <td>Name</td>
+      <td><f:name><input type="text"/></f:name></td>
+    </tr>
+    <tr>
+      <td>Birthyear</td>
+      <td>
+        <f:year>
+          <select>
+            <option>2007</option>
+            <option>2006</option>
+          </select>
+        </f:year>
+      </td>
+    </tr>
+    <tr>
+      <td>&nbsp;</td>
+      <td><input type="submit" value="Add"/></td>
+    </tr>
+  </lift:show.myForm>
+  ...
 </html>

So we've got a Lift snippet invocation with the valid HTML form and some additional tags. So far (with the proper namespace @@ -144,9 +152,8 @@

Templating

So, your Lift code will look like:

- the new way -

-class Show {
+      Lift 3.x syntax
+      
class Show {
   def render = {
     "#birthyear" #> SHtml.ajaxSelect(Show.yearsOptions,
                                      Show.yearsDefault,
@@ -185,23 +192,26 @@ 

Templating

"The name and year was set to (" + this.name + "," + this.year + ")") S.redirectTo("#myForm") } -} -
+}
- the old way -

-class Show {
+      Older Lift syntax
+      
class Show {
    def myForm(xhtml: NodeSeq): NodeSeq = {
      var name = ""
      def handleYear(year: String) {
        ... the form's been submitted... do something
      }
-     Helpers.bind("f", xhtml, "name" -> SHtml.text(name, name = _),
-                   "year" -> SHtml.select((1900 to 2007).toList.reverse.map(v => (v.toString, v.toString)), 
-                                       Empty, handleYear _))
+     Helpers.bind("f",
+                  xhtml,
+                  "name" -> SHtml.text(name, name = _),
+                  "year" ->
+                    SHtml.select((1900 to 2007).toList.reverse
+                                   .map(v => (v.toString, v.toString)),
+                                 Empty,
+                                 handleYear _)
+                  )
    }
-}
-        
+}

Note that no display code has crept into the snippet. You've simply bound the HTML created by text() and select() to the <f:name/> and <f:year/> tags in the incoming XHTML. @@ -213,37 +223,29 @@

Templating

If you are displaying a table rather than a form, then the same binding logic still works. For example:

- the new way -

-<table data-lift="show:users">
+      HTML syntax
+      
<table data-lift="show:users">
    <tr>
      <td><span id="first_name">David</span></td>
      <td><span id="last_name">Pollak</span></td>
    </tr>
-</table>
-        
+</table>
- the old way -

-<table>
+      The older XHTML syntax
+      
<table>
 
 <lift:snippet type="show:users">
 <tr><td><f:first_name>David</f:first_name></ 
 td><td><f:last_name>Pollak</f:last_name></td></tr>
 </lift:snippet>
 
-</table>
-        
+</table>
-
-        
-class Show {
+      
class Show {
    def users(xhtml: NodeSeq): NodeSeq = Users.findAll.flatMap(user => bind("f", 
              xhtml, "first_name" --> user.firstName, "last_name" --> user.nameName))
-}
-        
-      
+}

If you take the time to clearly define the bind points, then you can have no display code at all in your snippets.

@@ -260,6 +262,7 @@

Templating

to police business logic in the templates.

+
diff --git a/combo/example/src/main/webapp/templating/selectomatic.html b/combo/example/src/main/webapp/templating/selectomatic.html index 919c1fc..f80d422 100644 --- a/combo/example/src/main/webapp/templating/selectomatic.html +++ b/combo/example/src/main/webapp/templating/selectomatic.html @@ -1,85 +1,98 @@ - - - -

- On this page, we'll allow the user to select a series - of <div> tags to display based on the results - of a form submission. -

- -
-
- - <div> number visible - -
-
- -
-
+ + -
-
This is div 0
-
This is div 1
-
This is div 2
-
This is div 3
-
This is div 4
-
This is div 5
-
-
-
-

- The code to store the state of the div tags is: - -

-private var whichDivs: Array[Boolean] = Array(true, true, true, true, true, true)
-
+ + + Template + + + +
+

Select <div>s

+ + +

+ On this page, we'll allow the user to select a series of <div> tags to display based on the results of a form submission. +

+ +
+
+ + <div> number + visible + +
+
+ +
+
+ +
+
This is div 0
+
This is div 1
+
This is div 2
+
This is div 3
+
This is div 4
+
This is div 5
+
+
+
+

+ The code to store the state of the div tags is: + + +

+private var whichDivs: Array[Boolean] = Array(true, true, true, true, true, true)
+
And to select the div tags: +
  def selectDivs(in: NodeSeq): NodeSeq = {
     def calcNum(in: String): Box[Int] = 
       if (in.startsWith("num_")) asInt(in.substring(4))
       else Empty
@@ -91,6 +104,9 @@
     } yield div
   }
 
-

+

+ +
+ -
+ \ No newline at end of file diff --git a/combo/example/src/main/webapp/ws.html b/combo/example/src/main/webapp/ws.html index f2ca47e..87ea498 100644 --- a/combo/example/src/main/webapp/ws.html +++ b/combo/example/src/main/webapp/ws.html @@ -1,110 +1,95 @@ - - - -

Normally, Lift will look for a "view" that -satifying an HTTP request. A view is an XHTML page -or segment of a page with special <lift:xxx> processing -instructions in it (e.g., an instruction to embed a controller, -an instruction to surround the XHTML with another template, etc.) -However, in certain cases, for example, when you want to -satisfy a Web Services (REST) request, you can use -Scala's pattern matching to intercept a request and service -it. -

- -

It's easy to "serve" incoming HTTP requests. -Using Lift's RestHelper class. For example, you can -serve "/webservices/all_users" as a JSON request as: -

- -
-  serve {
-    case "webservices" :: "all_users" :: _ JsonGet _ =>
-      AllUsers(User.findAll()).toJson
-  }
-
- -

- And we can serve the same request if it's an XML request: -

- -
-  serve {
-    case "webservices" :: "all_users" :: _ XmlGet _ =>
-      AllUsers(User.findAll()).toXml
-  }
-
- -

- If you have a request (in the case of this example, servicing - both a POST and a GET for /webservices/add_user), - then service the request and include a function to convert the - response into either JSON or XML: -

- -
-  serveJx {
-    case Req("webservices" :: "add_user" :: _, _, rt) if rt.post_? || rt.get_? =>
-      addUser()
-  } { // How do we convert a UserInfo to either XML or JSON?
-    case (JsonSelect, u, _) => u.toJson
-    case (XmlSelect, u, _) => u.toXml
-  }
-
- -

- And the addUser() method looks like: -

- -
-  def addUser(): Box[UserInfo] =
-    for {
-      firstname <- S.param("firstname") ?~ "firstname parameter missing" ~> 400
-      lastname <- S.param("lastname") ?~ "lastname parameter missing"
-      email <- S.param("email") ?~ "email parameter missing"
-    } yield {
-      val u = User.create.firstName(firstname).
-      lastName(lastname).email(email)
-
-      S.param("password") foreach u.password.set
-
-      u.saveMe
-    }
-
- -

- Note that if the firstname parameter is missing, a 400 response with - the message "firstname parameter missing" message in the body will - be returned. If the lastname parameter is missing, a 404 will be - returned with "lastname parameter missing" message in the body. -

- -
+ + + + + + Template + + + + +
+

Web Services

+ + +

Normally, + Lift will look for a "view" that satifying an HTTP request. A view is an XHTML page or segment of a page with special + <lift:xxx> processing instructions in it (e.g., an instruction to embed a controller, an instruction to surround + the XHTML with another template, etc.) However, in certain cases, for example, when you want to satisfy a Web Services + (REST) request, you can use Scala's pattern matching to intercept a request and service it. +

+ +

It's easy to "serve" incoming HTTP requests. Using Lift's RestHelper class. For example, you can serve "/webservices/all_users" + as a JSON request as: +

+ + The + Lift Scala code +
serve {
+  case "webservices" :: "all_users" :: _ JsonGet _ =>
+    AllUsers(User.findAll()).toJson
+}
+ +

+ And we can serve the same request if it's an XML request: +

+ + The + Lift Scala code +
serve {
+  case "webservices" :: "all_users" :: _ XmlGet _ =>
+    AllUsers(User.findAll()).toXml
+}
+ +

+ If you have a request (in the case of this example, servicing both a POST and a GET for /webservices/add_user), then service + the request and include a function to convert the response into either JSON or XML: +

+ + The + Lift Scala code +
serveJx {
+  case Req("webservices" :: "add_user" :: _, _, rt) if rt.post_? || rt.get_? =>
+    addUser()
+} { // How do we convert a UserInfo to either XML or JSON?
+  case (JsonSelect, u, _) => u.toJson
+  case (XmlSelect, u, _) => u.toXml
+}
+ +

+ And the addUser() method looks like: +

+ + The + Lift Scala code +
def addUser(): Box[UserInfo] =
+  for {
+    firstname <- S.param("firstname") ?~ "firstname parameter missing" ~> 400
+    lastname <- S.param("lastname") ?~ "lastname parameter missing"
+    email <- S.param("email") ?~ "email parameter missing"
+  } yield {
+    val u = User.create.firstName(firstname).
+    lastName(lastname).email(email)
+
+    S.param("password") foreach u.password.set
+
+    u.saveMe
+  }
+ +

+ Note that if the firstname parameter is missing, a 400 response with the message "firstname parameter missing" message in + the body will be returned. If the lastname parameter is missing, a 404 will be returned with "lastname parameter missing" + message in the body. +

+ +
+ + + \ No newline at end of file From ab2842347069441e42e3dbef4293507c58776da5 Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Mon, 1 Jan 2018 14:03:27 +0100 Subject: [PATCH 08/33] Localization and Menues --- combo/example/build.sbt | 2 +- .../net/liftweb/example/lib/WikiStuff.scala | 37 +++--- .../net/liftweb/example/snippet/Wiki.scala | 2 - combo/example/src/main/webapp/lang.html | 119 ++++++++++-------- combo/example/src/main/webapp/lang_ca.html | 62 ++++++--- combo/example/src/main/webapp/lang_de.html | 55 ++++++-- combo/example/src/main/webapp/lang_en_CA.html | 110 ++++++++-------- combo/example/src/main/webapp/lang_en_US.html | 99 +++++++++------ combo/example/src/main/webapp/lang_es.html | 55 ++++++-- combo/example/src/main/webapp/lang_fr.html | 55 ++++++-- combo/example/src/main/webapp/lang_it.html | 55 ++++++-- combo/example/src/main/webapp/lang_no.html | 54 ++++++-- combo/example/src/main/webapp/lang_sv.html | 77 +++++++++--- combo/example/src/main/webapp/menu/four.html | 21 +++- combo/example/src/main/webapp/menu/index.html | 29 ++++- combo/example/src/main/webapp/menu/one.html | 31 ++++- combo/example/src/main/webapp/menu/three.html | 31 ++++- combo/example/src/main/webapp/menu/two.html | 31 ++++- .../example/src/main/webapp/menu/two_one.html | 34 ++++- .../example/src/main/webapp/menu/two_two.html | 24 +++- 20 files changed, 706 insertions(+), 277 deletions(-) diff --git a/combo/example/build.sbt b/combo/example/build.sbt index 609a9e1..ffb5468 100644 --- a/combo/example/build.sbt +++ b/combo/example/build.sbt @@ -4,7 +4,7 @@ organization := "net.liftweb" name := "demo" -version := "0.5.2-SNAPSHOT" +version := "0.5.3-SNAPSHOT" scalaVersion := "2.11.11" //2.12.2 diff --git a/combo/example/src/main/scala/net/liftweb/example/lib/WikiStuff.scala b/combo/example/src/main/scala/net/liftweb/example/lib/WikiStuff.scala index d314c48..550556a 100644 --- a/combo/example/src/main/scala/net/liftweb/example/lib/WikiStuff.scala +++ b/combo/example/src/main/scala/net/liftweb/example/lib/WikiStuff.scala @@ -14,24 +14,22 @@ * limitations under the License. */ -package net.liftweb { -package example { -package lib { +package net.liftweb.example.lib - import _root_.net.liftweb._ - import net.liftmodules.textile._ - import common._ - import util._ - import Helpers._ - import http._ - import mapper._ - import sitemap._ - import Loc._ +import _root_.net.liftweb._ +import net.liftmodules.textile._ +import common._ +import util._ +import Helpers._ +import http._ +import mapper._ +import sitemap._ +import Loc._ - import example._ - import model._ +import example._ +import model._ - import scala.xml.{Text, NodeSeq} +import scala.xml.{Text, NodeSeq} /** * A wiki location @@ -142,7 +140,7 @@ object WikiStuff extends Loc[WikiLoc] { else Text("Edit entry named "+pageName) - val hobixLink =  Textile Markup Reference
+ val texMarkRefLink =  Textile Markup Reference
val cancelLink = Cancel val textarea = r.entry.toForm @@ -151,7 +149,7 @@ object WikiStuff extends Loc[WikiLoc] {
{ // the form tag message ++ - hobixLink ++ + texMarkRefLink ++ textarea ++ // display the form
++ cancelLink ++ @@ -181,8 +179,5 @@ object WikiStuff extends Loc[WikiLoc] { def stringUrl(page: String): String = url(page).map(_.text) getOrElse "" - -} -} -} } + diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Wiki.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Wiki.scala index 05f7923..9ec63e8 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/Wiki.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Wiki.scala @@ -17,9 +17,7 @@ package net.liftweb { package example { package snippet { - /* - import _root_.net.liftweb.example.model._ import _root_.scala.xml.{NodeSeq, Text, Group} import _root_.net.liftweb.http.{S, SHtml} diff --git a/combo/example/src/main/webapp/lang.html b/combo/example/src/main/webapp/lang.html index 918ebb7..38b39c6 100644 --- a/combo/example/src/main/webapp/lang.html +++ b/combo/example/src/main/webapp/lang.html @@ -1,58 +1,71 @@ - - - -

Hello

- My language: lang - -
- Set locale - -
- - -
-

- The Lift code to select and set the Locale: -

- - -
-  def lang = {
-    "#lang" #> locale.getDisplayLanguage(locale) &
-    "#select" #> SHtml.selectObj(locales.map(lo => (lo, lo.getDisplayName)),
-                                 definedLocale, setLocale)
+
+
+
+
+  
+  Template
+
+
+
+
+  
+

Localization

+ + +
+

+ Hello +

+ + + My language: + lang + + +
+ +
+ +
+
+ +
+
+
+ +
+

+ The Lift code to select and set the Locale: +

+ +
def lang = {
+    "#lang" #> locale.getDisplayLanguage(locale) &
+      "#select" #> SHtml.selectObj(locales.map(lo => (lo, lo.getDisplayName)),
+                                   definedLocale,
+                                   setLocale,
+                                   "class" -> "form-control")
   }
 
-  private def locales =
-  Locale.getAvailableLocales.toList.sort(_.getDisplayName < _.getDisplayName)
+  private def locales =
+    Locale.getAvailableLocales.toList
+      .sortWith(_.getDisplayName < _.getDisplayName)
 
-  private def setLocale(loc: Locale) = definedLocale(Full(loc))
-
+ private def setLocale(loc: Locale) = definedLocale(Full(loc))
+ + -
+ \ No newline at end of file diff --git a/combo/example/src/main/webapp/lang_ca.html b/combo/example/src/main/webapp/lang_ca.html index 7dc3032..23c3e9f 100644 --- a/combo/example/src/main/webapp/lang_ca.html +++ b/combo/example/src/main/webapp/lang_ca.html @@ -1,22 +1,44 @@ - - - Lift Example - - -
- -

Hola

- La meva llengua: lang - -
- Canviar de llengua - - -
-
+ + + + + + Template + + + + +
+

Localization

+ + +
+

Hola

+ La meva llengua: + lang + + +
+ +
+ +
+
+ +
+
+
+
- - + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/lang_de.html b/combo/example/src/main/webapp/lang_de.html index 8bb225a..6a882ad 100644 --- a/combo/example/src/main/webapp/lang_de.html +++ b/combo/example/src/main/webapp/lang_de.html @@ -1,13 +1,44 @@ - -
-

Hallo

- Meine Sprache: lang - -
- Sprache Ändern - + + + + + + Template + + + + +
+

Localization

+ + + +

Hallo

+ Meine Sprache: + lang + + +
+ +
+ +
+
+ +
+
+ +
- - + + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/lang_en_CA.html b/combo/example/src/main/webapp/lang_en_CA.html index 4996789..785b760 100644 --- a/combo/example/src/main/webapp/lang_en_CA.html +++ b/combo/example/src/main/webapp/lang_en_CA.html @@ -1,57 +1,63 @@ - -
-

Good Day, eh.

- My language: lang - -
- Set locale - -
-
- -
-

- The Lift code to select and set the Locale: -

- - -
-  def lang = {
-    "#lang" #> locale.getDisplayLanguage(locale) &
-    "#select" #> SHtml.selectObj(locales.map(lo => (lo, lo.getDisplayName)),
-                                 definedLocale, setLocale)
+
+
+
+
+  
+  Template
+
+
+
+
+  
+

Localization

+ + +
+

Good Day, eh.

+ My language: + lang + + +
+ +
+ +
+
+ +
+
+
+ +
+

+ The Lift code to select and set the Locale: +

+ +
def lang = {
+    "#lang" #> locale.getDisplayLanguage(locale) &
+      "#select" #> SHtml.selectObj(locales.map(lo => (lo, lo.getDisplayName)),
+                                   definedLocale,
+                                   setLocale,
+                                   "class" -> "form-control")
   }
 
-  private def locales =
-  Locale.getAvailableLocales.toList.sort(_.getDisplayName < _.getDisplayName)
+  private def locales =
+    Locale.getAvailableLocales.toList
+      .sortWith(_.getDisplayName < _.getDisplayName)
 
-  private def setLocale(loc: Locale) = definedLocale(Full(loc))
-
+ private def setLocale(loc: Locale) = definedLocale(Full(loc))
+
+ -
+ \ No newline at end of file diff --git a/combo/example/src/main/webapp/lang_en_US.html b/combo/example/src/main/webapp/lang_en_US.html index 8f7f223..0a1f9d0 100644 --- a/combo/example/src/main/webapp/lang_en_US.html +++ b/combo/example/src/main/webapp/lang_en_US.html @@ -1,8 +1,28 @@ - + + + + + + Template + + + + +
+

Localization

+ + +
+

Howdy Partner

+ My language: + lang + + +
+ +
+ +
+
+ +
+
+
+
+

+ The Lift code to select and set the Locale: +

-
-

- The Lift code to select and set the Locale: -

- - -
-  def lang = {
-    "#lang" #> locale.getDisplayLanguage(locale) &
-    "#select" #> SHtml.selectObj(locales.map(lo => (lo, lo.getDisplayName)),
-                                 definedLocale, setLocale)
+    
def lang = {
+    "#lang" #> locale.getDisplayLanguage(locale) &
+      "#select" #> SHtml.selectObj(locales.map(lo => (lo, lo.getDisplayName)),
+                                   definedLocale,
+                                   setLocale,
+                                   "class" -> "form-control")
   }
 
-  private def locales =
-  Locale.getAvailableLocales.toList.sort(_.getDisplayName < _.getDisplayName)
+  private def locales =
+    Locale.getAvailableLocales.toList
+      .sortWith(_.getDisplayName < _.getDisplayName)
 
-  private def setLocale(loc: Locale) = definedLocale(Full(loc))
-
+ private def setLocale(loc: Locale) = definedLocale(Full(loc))
+
+ -
+ \ No newline at end of file diff --git a/combo/example/src/main/webapp/lang_es.html b/combo/example/src/main/webapp/lang_es.html index eefac7b..b2c10a7 100644 --- a/combo/example/src/main/webapp/lang_es.html +++ b/combo/example/src/main/webapp/lang_es.html @@ -1,13 +1,44 @@ - -
-

Hola

- Mi idioma: lang - -
- Cambiar de idioma - + + + + + + Template + + + + +
+

Localization

+ + + +

Hola

+ Mi idioma: + lang + + +
+ +
+ +
+
+ +
+
+ +
- - + + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/lang_fr.html b/combo/example/src/main/webapp/lang_fr.html index b04c5df..ae37704 100644 --- a/combo/example/src/main/webapp/lang_fr.html +++ b/combo/example/src/main/webapp/lang_fr.html @@ -1,13 +1,44 @@ - -
-

Bonjour

- Ma langue: lang - -
- Changer de langue - + + + + + + Template + + + + +
+

Localization

+ + + +

Bonjour

+ Ma langue: + lang + + +
+ +
+ +
+
+ +
+
+ +
- - + + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/lang_it.html b/combo/example/src/main/webapp/lang_it.html index 0e4f244..66238b0 100644 --- a/combo/example/src/main/webapp/lang_it.html +++ b/combo/example/src/main/webapp/lang_it.html @@ -1,13 +1,44 @@ - -
-

Ciao

- Mia lingua: lang - -
- Cambia lingua - + + + + + + Template + + + + +
+

Localization

+ + + +

Ciao

+ Mia lingua: + lang + + +
+ +
+ +
+
+ +
+
+ +
- - + + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/lang_no.html b/combo/example/src/main/webapp/lang_no.html index 27e0c06..e298a69 100644 --- a/combo/example/src/main/webapp/lang_no.html +++ b/combo/example/src/main/webapp/lang_no.html @@ -1,12 +1,44 @@ - -
-

Hei

- Mitt språk: lang - -
- Endre språk - + + + + + + Template + + + + +
+

Localization

+ + + +

Hei

+ Mitt språk: + lang + + +
+ +
+ +
+
+ +
+
+ +
- + + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/lang_sv.html b/combo/example/src/main/webapp/lang_sv.html index e2ea989..94b7ec7 100644 --- a/combo/example/src/main/webapp/lang_sv.html +++ b/combo/example/src/main/webapp/lang_sv.html @@ -1,14 +1,63 @@ - -
-

Hej

- Mitt språk: lang - -
- Ändra språk -
-
- - -
+ + + + + + Template + + + + +
+

Localization

+ + +
+

Hej

+ Mitt språk: + lang + + +
+ +
+ +
+
+ +
+
+
+ +
+

+ Lift kod för att välja och sätta språk: +

+ +
def lang = {
+    "#lang" #> locale.getDisplayLanguage(locale) &
+      "#select" #> SHtml.selectObj(locales.map(lo => (lo, lo.getDisplayName)),
+                                   definedLocale,
+                                   setLocale,
+                                   "class" -> "form-control")
+  }
+
+  private def locales =
+    Locale.getAvailableLocales.toList
+      .sortWith(_.getDisplayName < _.getDisplayName)
+
+  private def setLocale(loc: Locale) = definedLocale(Full(loc))
+ +
+ + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/menu/four.html b/combo/example/src/main/webapp/menu/four.html index c4e12dd..fb1d1a9 100644 --- a/combo/example/src/main/webapp/menu/four.html +++ b/combo/example/src/main/webapp/menu/four.html @@ -1,3 +1,20 @@ - +
+

Forth Submenu

+ + Submenu Page 4 - + +
+ + + diff --git a/combo/example/src/main/webapp/menu/index.html b/combo/example/src/main/webapp/menu/index.html index d923127..8d9f3c2 100644 --- a/combo/example/src/main/webapp/menu/index.html +++ b/combo/example/src/main/webapp/menu/index.html @@ -1,3 +1,28 @@ - + + + + + + Template + + + + +
+

Menus

+ + + Main Menu page... note the submenus on the menu bar - + +
+ + + diff --git a/combo/example/src/main/webapp/menu/one.html b/combo/example/src/main/webapp/menu/one.html index 3b8b1d7..bb48e1c 100644 --- a/combo/example/src/main/webapp/menu/one.html +++ b/combo/example/src/main/webapp/menu/one.html @@ -1,3 +1,30 @@ - + + + + + + Template + + + + +
+

First Submenu

+ + Submenu Page 1 - + +
+ + + diff --git a/combo/example/src/main/webapp/menu/three.html b/combo/example/src/main/webapp/menu/three.html index f4d46dd..44acde4 100644 --- a/combo/example/src/main/webapp/menu/three.html +++ b/combo/example/src/main/webapp/menu/three.html @@ -1,3 +1,30 @@ - + + + + + + Template + + + + +
+

Third Submenu

+ + Submenu Page 3 - + +
+ + + diff --git a/combo/example/src/main/webapp/menu/two.html b/combo/example/src/main/webapp/menu/two.html index 9bab16b..a1daff4 100644 --- a/combo/example/src/main/webapp/menu/two.html +++ b/combo/example/src/main/webapp/menu/two.html @@ -1,3 +1,30 @@ - + + + + + + Template + + + + +
+

Second Submenu (has more)

+ + Submenu Page 2 - + +
+ + + diff --git a/combo/example/src/main/webapp/menu/two_one.html b/combo/example/src/main/webapp/menu/two_one.html index 0d7c836..210336e 100644 --- a/combo/example/src/main/webapp/menu/two_one.html +++ b/combo/example/src/main/webapp/menu/two_one.html @@ -1,3 +1,33 @@ - + + + + + + Template + + + + +
+

First (2) Submenu

+ + Submenu Page 2-1 - + +
+ + + diff --git a/combo/example/src/main/webapp/menu/two_two.html b/combo/example/src/main/webapp/menu/two_two.html index 0b8a243..3386786 100644 --- a/combo/example/src/main/webapp/menu/two_two.html +++ b/combo/example/src/main/webapp/menu/two_two.html @@ -1,3 +1,23 @@ - +
+

Second (2) Submenu

+ + Submenu Page 2-2 - + +
+ + + From 0bcbc0cfdac0befeeccba63c01d91e795a914028 Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Sun, 14 Jan 2018 17:47:37 +0100 Subject: [PATCH 09/33] WIP Misc code --- combo/example/build.sbt | 2 +- .../net/liftweb/example/comet/LongTime.scala | 10 +- .../snippet/{Wizard.scalaREM => Wizard.scala} | 177 ++++++++------- combo/example/src/main/webapp/guess.html | 201 ++++++++++-------- combo/example/src/main/webapp/index.html | 4 +- combo/example/src/main/webapp/longtime.html | 120 +++++------ combo/example/src/main/webapp/misc.html | 37 +++- .../example/src/main/webapp/rhodeisland.html | 69 +++--- .../src/main/webapp/simple_wizard.html | 2 +- .../webapp/templates-hidden/default2.html | 94 ++++---- .../webapp/templates-hidden/wizard-all.html | 119 ++++++----- .../templates-hidden/wizard-all.old.html | 57 +++++ .../src/main/webapp/templating/pageOne.html | 7 +- combo/example/src/main/webapp/wiz.html | 185 ++++++++-------- combo/example/src/main/webapp/wiz2.html | 120 +++++------ 15 files changed, 671 insertions(+), 533 deletions(-) rename combo/example/src/main/scala/net/liftweb/example/snippet/{Wizard.scalaREM => Wizard.scala} (55%) create mode 100644 combo/example/src/main/webapp/templates-hidden/wizard-all.old.html diff --git a/combo/example/build.sbt b/combo/example/build.sbt index ffb5468..f818aae 100644 --- a/combo/example/build.sbt +++ b/combo/example/build.sbt @@ -4,7 +4,7 @@ organization := "net.liftweb" name := "demo" -version := "0.5.3-SNAPSHOT" +version := "0.5.6-SNAPSHOT" scalaVersion := "2.11.11" //2.12.2 diff --git a/combo/example/src/main/scala/net/liftweb/example/comet/LongTime.scala b/combo/example/src/main/scala/net/liftweb/example/comet/LongTime.scala index 662be22..0cd8936 100644 --- a/combo/example/src/main/scala/net/liftweb/example/comet/LongTime.scala +++ b/combo/example/src/main/scala/net/liftweb/example/comet/LongTime.scala @@ -14,9 +14,9 @@ * limitations under the License. */ -package net.liftweb -package example -package comet +package net.liftweb.example.comet +// package example +// package comet import net.liftweb.actor._ import scala.xml.{NodeSeq, Text} @@ -90,9 +90,9 @@ class LongTime extends CometActor { def render = url match { case Full(where) => - Your job is complete. Click Me +
Your job is complete. Click Me
case _ => - We're working on your job... it's {progress}% complete +
We're working on your job... it's {progress}% complete
} } diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scalaREM b/combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scala similarity index 55% rename from combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scalaREM rename to combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scala index 3df412f..6f5ea34 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scalaREM +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scala @@ -14,23 +14,18 @@ * limitations under the License. */ -package net.liftweb { -package example { -package snippet { +package net.liftweb.example.snippet -import _root_.scala.xml.{NodeSeq, Text} -import _root_.net.liftweb.util._ -import _root_.net.liftweb.http._ -import _root_.net.liftweb.wizard._ -import _root_.net.liftweb.common._ -import _root_.java.util.Date -import Helpers._ +import scala.xml.{NodeSeq, Text} +import net.liftweb.util._ +import net.liftweb.http._ +import net.liftweb.common._ -import model._ +import net.liftweb.example.model._ /** - * An example of a wizard in Lift - */ + * An example of a wizard in Lift + */ object MyWizard extends Wizard { object completeInfo extends WizardVar(false) @@ -38,13 +33,20 @@ object MyWizard extends Wizard { val nameAndAge = new Screen { // it has a name field - val name = field(S ? "First Name", "", + val name = field(S ? "First Name", + "", valMinLen(2, S ? "Name Too Short"), - valMaxLen(40, S ? "Name Too Long")) + valMaxLen(40, S ? "Name Too Long"), + "type" -> "text", + "class" -> "form-control") // and an age field - val age = field(S ? "Age", 0, minVal(5, S ?? "Too young"), - maxVal(120, S ? "You should be dead")) + val age = field(S ? "Age", + 0, + minVal(5, S ? "Too young"), + maxVal(120, S ? "You should be dead"), + "type" -> "number", + "class" -> "form-control") // choose the next screen based on the age override def nextScreen = if (age.is < 18) parentName else favoritePet @@ -52,16 +54,22 @@ object MyWizard extends Wizard { // We ask the parent's name if the person is under 18 val parentName = new Screen { - val parentName = field(S ? "Mom or Dad's name", "", + val parentName = field(S ? "Mom or Dad's name", + "", valMinLen(2, S ? "Name Too Short"), - valMaxLen(40, S ? "Name Too Long")) + valMaxLen(40, S ? "Name Too Long"), + "type" -> "text", + "class" -> "form-control") } // we ask for the favorite pet val favoritePet = new Screen { - val petName = field(S ? "Pet's name", "", + val petName = field(S ? "Pet's name", + "", valMinLen(2, S ? "Name Too Short"), - valMaxLen(40, S ? "Name Too Long")) + valMaxLen(40, S ? "Name Too Long"), + "type" -> "text", + "class" -> "form-control") } // what to do on completion of the wizard @@ -73,17 +81,24 @@ object MyWizard extends Wizard { class WizardChallenge extends Wizard { val page1 = new Screen { - val info = field(S ? "Page one entry", "") + val info = field(S ? "Page one entry", + "", + "type" -> "text", + "class" -> "form-control") } val page2 = new Screen { override def screenTop = Page one field is {page1.info} - val info = field(S ? "Page two entry", "") + val info = field(S ? "Page two entry", + "", + "type" -> "text", + "class" -> "form-control") } val page3 = new Screen { - override def screenTop = Page one field is {page1.info}
Page two field is {page2.info}
+ override def screenTop = + Page one field is {page1.info}
Page two field is {page2.info}
} def finish() { @@ -94,21 +109,23 @@ class WizardChallenge extends Wizard { class PersonScreen extends LiftScreen { object person extends ScreenVar(Person.create) + override def formName = "PersonScreen" + override def screenTop = - A single screen with some input validation + A single screen with some input validation addFields(() => person.is) val shouldSave = field("Save ?", false) val likeCats = builder("Do you like cats?", "") ^/ - (s => if (Helpers.toBoolean(s)) Nil else "You have to like cats") make + (s => if (Helpers.toBoolean(s)) Nil else "You have to like cats") make def finish() { - S.notice("Thank you for adding "+person.is) + S.notice("Thank you for adding " + person.is) if (shouldSave.is) { person.is.save - S.notice(person.is.toString+" Saved in the database") + S.notice(person.is.toString + " Saved in the database") } } } @@ -128,17 +145,18 @@ object VariableScreenInfo { def validations = Nil - def set(v: String) = {_name = v; v} + def set(v: String) = { _name = v; v } def get = _name def is = get type ValueType = String - def name = "Name" + def name = "Name" override val uniqueFieldId: Box[String] = Full(Helpers.nextFuncName) - def validate = if (_name.length >= 4) Nil - else "Name must be 4 characters" + def validate = + if (_name.length >= 4) Nil + else "Name must be 4 characters" } def address: BaseField = new BaseField with FEP { @@ -149,7 +167,7 @@ object VariableScreenInfo { def validations = Nil - def set(v: String) = {address = v; v} + def set(v: String) = { address = v; v } def get = address def is = get type ValueType = String @@ -158,16 +176,17 @@ object VariableScreenInfo { override val uniqueFieldId: Box[String] = Full(Helpers.nextFuncName) - def validate = if (address.length >= 3) Nil - else "Address must be 3 characters" + def validate = + if (address.length >= 3) Nil + else "Address must be 3 characters" } def age: BaseField = new BaseField with FEP { private var age = 0 - def toForm: Box[NodeSeq] = Full(SHtml.text(age.toString, - s => Helpers.asInt(s).map(age = _))) + def toForm: Box[NodeSeq] = + Full(SHtml.text(age.toString, s => Helpers.asInt(s).map(age = _))) - def set(v: Int) = {age = v; v} + def set(v: Int) = { age = v; v } def get = age def is = get type ValueType = Int @@ -179,19 +198,20 @@ object VariableScreenInfo { override val uniqueFieldId: Box[String] = Full(Helpers.nextFuncName) - def validate = if (age > 10) Nil - else "Age must be greater than 10" + def validate = + if (age > 10) Nil + else "Age must be greater than 10" } def selection: BaseField = new BaseField { private val opts = List("A", "B", "C", "Last") private var sel = Full("C") - def toForm: Box[NodeSeq] = Full(SHtml.select(opts.map(a => a -> a), sel, - x => sel = Full(x))) - def set(v: String) = {sel = Full(v); v} + def toForm: Box[NodeSeq] = + Full(SHtml.select(opts.map(a => a -> a), sel, x => sel = Full(x))) + def set(v: String) = { sel = Full(v); v } def name = "Selection Thing" - def get = sel.open_! + def get = sel.openOrThrowException("Value should not be empty.") def is = get type ValueType = String @@ -204,22 +224,25 @@ object VariableScreenInfo { def validate = Nil } - def chooseFields: FieldContainer = - List(name, address, age, selection) filter { - ignore => Helpers.randomInt(100) > 50 + def chooseFields: FieldContainer = + List(name, address, age, selection) filter { ignore => + Helpers.randomInt(100) > 50 } match { case Nil => chooseFields - case xs => new FieldContainer { - def allFields = xs - } + case xs => + new FieldContainer { + def allFields = xs + } } } class VariableScreen extends LiftScreen { object fields extends ScreenVar(VariableScreenInfo.chooseFields) + override def formName = "VariableScreen" + override def screenTop = - A single screen with variable fields + A single screen with variable fields addFields(() => fields.is) @@ -229,61 +252,73 @@ class VariableScreen extends LiftScreen { } class AskAboutIceCream1 extends LiftScreen { + + override def formName = "AskAboutIceCream1" + val flavor = field(S ? "What's your favorite Ice cream flavor", "") def finish() { - S.notice("I like "+flavor.is+" too!") + S.notice("I like " + flavor.is + " too!") } } class AskAboutIceCream2 extends LiftScreen { - val flavor = field(S ? "What's your favorite Ice cream flavor", "", + + override def formName = "AskAboutIceCream2" + + val flavor = field(S ? "What's your favorite Ice cream flavor", + "", trim, valMinLen(2, "Name too short"), valMaxLen(40, "That's a long name")) def finish() { - S.notice("I like "+flavor.is+" too!") + S.notice("I like " + flavor.is + " too!") } } class AskAboutIceCream3 extends LiftScreen { - val flavor = field(S ? "What's your favorite Ice cream flavor", "", - trim, valMinLen(2,S ? "Name too short"), - valMaxLen(40,S ? "That's a long name")) + + override def formName = "AskAboutIceCream3" + + val flavor = field(S ? "What's your favorite Ice cream flavor", + "", + trim, + valMinLen(2, S ? "Name too short"), + valMaxLen(40, S ? "That's a long name")) val sauce = field(S ? "Like chocalate sauce?", false) def finish() { if (sauce) { - S.notice(flavor.is+" tastes especially good with chocolate sauce!") - } - else S.notice("I like "+flavor.is+" too!") + S.notice(flavor.is + " tastes especially good with chocolate sauce!") + } else S.notice("I like " + flavor.is + " too!") } } class AskAboutIceCream4 extends LiftScreen { - val flavor = field(S ? "What's your favorite Ice cream flavor", "", - trim, valMinLen(2,S ? "Name too short"), - valMaxLen(40,S ? "That's a long name")) + + override def formName = "AskAboutIceCream4" + + val flavor = field(S ? "What's your favorite Ice cream flavor", + "", + trim, + valMinLen(2, S ? "Name too short"), + valMaxLen(40, S ? "That's a long name")) val sauce = field(S ? "Like chocalate sauce?", false) override def validations = notTooMuchChocolate _ :: super.validations def notTooMuchChocolate(): Errors = { - if (sauce && flavor.toLowerCase.contains("chocolate")) "That's a lot of chocolate" + if (sauce && flavor.toLowerCase.contains("chocolate")) + "That's a lot of chocolate" else Nil } def finish() { if (sauce) { - S.notice(flavor.is+" tastes especially good with chocolate sauce!") - } - else S.notice("I like "+flavor.is+" too!") + S.notice(flavor.is + " tastes especially good with chocolate sauce!") + } else S.notice("I like " + flavor.is + " too!") } } - -} -} -} diff --git a/combo/example/src/main/webapp/guess.html b/combo/example/src/main/webapp/guess.html index 6a05db1..49c7f1e 100644 --- a/combo/example/src/main/webapp/guess.html +++ b/combo/example/src/main/webapp/guess.html @@ -1,46 +1,75 @@ - - - - Guess a number between 1 and 100.
- Last guess:
- Guess:
- -
- - - You Win!!
- You guessed after - guesses.
-
-
- -
-
- The view code: - - - - -
+
+
+
+
+  
+  Template
+
+
+
+  
+

Number Guessing

+ + + + + Guess a number between 1 and 100. +
Last guess: + +
Guess: + +
+ +
+ + + You Win!! +
You guessed + after + guesses. +
+
+
+ +
+
The view code: + + + + +
 <lift:surround with="default" at="content">
 <lift:snippet type="CountGame:run" form="post">
   <choose:guess>
@@ -56,46 +85,49 @@
   </choose:win>
 </lift:snippet>
 </lift:surround>
-  
+
The Snippet: + + - - -
+        .comment-delimiter {
+          /* font-lock-comment-delimiter-face */
+          color: #b22222;
+        }
+
+        .function-name {
+          /* font-lock-function-name-face */
+          color: #0000ff;
+        }
+
+        .keyword {
+          /* font-lock-keyword-face */
+          color: #a020f0;
+        }
+
+        .string {
+          /* font-lock-string-face */
+          color: #bc8f8f;
+        }
+
+        .type {
+          /* font-lock-type-face */
+          color: #228b22;
+        }
+
+        .variable-name {
+          /* font-lock-variable-name-face */
+          color: #b8860b;
+        }
+
+        ]]>
+      
+    
+
+    
 class CountGame extends StatefulSnippet {
   val dispatch: DispatchIt = {
     case "run" if lastGuess == number => 
@@ -128,4 +160,7 @@
   
- +
+ + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/index.html b/combo/example/src/main/webapp/index.html index a59fc81..fffc3a6 100644 --- a/combo/example/src/main/webapp/index.html +++ b/combo/example/src/main/webapp/index.html @@ -42,8 +42,8 @@

Welcome to the Lift is built on Scala, a hybrid Functional and O-O language that compiles code down to the Java Virtual Machine. Scala code can call any Java code and make use of all Java classes. Java code can call some Scala code. - Lift applications are packaged as WAR files and can be deployed on any Servlet 2.4 engine (e.g., Tomcat 5.5.xx, - Jetty 6.0, etc.) + Lift applications are packaged as WAR files and can be deployed on any Servlet 2.4 -- 3.1 engine (e.g., Tomcat + 5.5.xx -- 8.5.xx, Jetty 6.0 -- 8.x, etc.)

diff --git a/combo/example/src/main/webapp/longtime.html b/combo/example/src/main/webapp/longtime.html index 8f112fe..a75cc84 100644 --- a/combo/example/src/main/webapp/longtime.html +++ b/combo/example/src/main/webapp/longtime.html @@ -1,82 +1,70 @@ - -
+ + -

Here's the Lift CometActor code:

+ + + Template + - -
-// A CometActor that keeps the user updated
-class LongTime extends CometActor {
-  private var url: Box[String] = Empty
-  private var progress: Int = 0
+    
- // a CometActor that has not been displayed for - // 2 minutes is destroyed - override def lifespan: Box[TimeSpan] = Full(2 minutes) + The + Lift CometActor Scala code +
// A CometActor that keeps the user updated
+class LongTime extends CometActor {
+  private var url: Box[String] = Empty
+  private var progress: Int = 0
 
-  // get messages from the ThingBuilder
-  override def highPriority = {
-    case BuildStatus(p, Empty) =>
-      this.progress = p
-      reRender(false)
+  // a CometActor that has not been displayed for
+  // 2 minutes is destroyed
+  override def lifespan: Box[TimeSpan] = Full(2 minutes)
 
-    case BuildStatus(_, Full(u)) =>
+  // get messages from the ThingBuilder
+  override def highPriority = {
+    case BuildStatus(p, Empty) =>
+      this.progress = p
+      reRender(false)
+
+    case BuildStatus(_, Full(u)) =>
       url = Full(u)
       progress = 100
-      reRender(false)
+      reRender(false)
       partialUpdate(RedirectTo(u))
   }
 
-  // start the job
-  override def localSetup() {
-    ThingBuilder ! this
-    super.localSetup()
+  // start the job
+  override def localSetup() {
+    ThingBuilder ! this
+    super.localSetup()
   }
 
-  // display the progress or a link to the result
-  def render =
-  url match {
-    case Full(where) =>
-      <span>Your job is complete.  <a href={where}>Click Me</a></span>
-    case _ =>
-      <span>We're working on your job... it's {progress}% complete</span>
-  }  
-}
-
+ // display the progress or a link to the result + def render = + url match { + case Full(where) => + <div>Your job is complete. <a href={where}>Click Me</a></div> + case _ => + <div>We're working on your job... it's {progress}% complete</div> + } +}
+
+ - - + \ No newline at end of file diff --git a/combo/example/src/main/webapp/misc.html b/combo/example/src/main/webapp/misc.html index dcbe54a..f0076c8 100644 --- a/combo/example/src/main/webapp/misc.html +++ b/combo/example/src/main/webapp/misc.html @@ -1,9 +1,28 @@ - -

Misc code samples

- -

- This section contains misc code samples that demonstrate - different features and functions in Lift. -

- -
+ + + + + + Template + + + +
+

Misc code samples

+ + +

+ This section contains misc code samples that demonstrate different features and functions in Lift. +

+ +
+ + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/rhodeisland.html b/combo/example/src/main/webapp/rhodeisland.html index f3df54d..a79348f 100644 --- a/combo/example/src/main/webapp/rhodeisland.html +++ b/combo/example/src/main/webapp/rhodeisland.html @@ -26,42 +26,9 @@

Modal Dialog

This section has two examples showing how you could work with modal dialogs that has there template data in a separate file. - The first example is a plain javascript dialog while the other one is taking advantage of bootstrap's javascript modals. + The second example is a plain javascript dialog while the first one is taking advantage of bootstrap's javascript modals.

-

JS dialog example

- - Destroy Rhode Island - -
-
- The - Lift Scala code -
class JSDialog {
-  // build the button... when pressed, present
-  // a dialog based on running the _jsdialog_confirm
-  // template
-  def button(in: NodeSeq) =
-    ajaxButton(in,
-               () =>
-                 S.runTemplate(List("_jsdialog_confirm"))
-                   .map(ns => ModalDialog(ns)) openOr
-                   Alert("Couldn't find _jsdialog_confirm template"))
-
-  // the template needs to bind to either server-side behavior
-  // and unblock the UI
-  def confirm = {
-    "#yes" #> ((b: NodeSeq) =>
-      ajaxButton(b, () => {
-        println("Rhode Island Destroyed")
-        Unblock & Alert("Rhode Island Destroyed")
-      })) &
-      "#no" #> ((b: NodeSeq) => <button onclick={Unblock.toJsCmd}>
-          {b}
-        </button>)
-  }
-}
-

Bootstrap dialog example

Destroy Rhode Island @@ -118,6 +85,40 @@

Bootstrap dialog example

bsDialogTemplate map setAtPlaceholder openOr showTemplateNotFoundAlert }
+
+

JS dialog example

+ + Destroy Rhode Island + +
+
+ The + Lift Scala code +
class JSDialog {
+  // build the button... when pressed, present
+  // a dialog based on running the _jsdialog_confirm
+  // template
+  def button(in: NodeSeq) =
+    ajaxButton(in,
+               () =>
+                 S.runTemplate(List("_jsdialog_confirm"))
+                   .map(ns => ModalDialog(ns)) openOr
+                   Alert("Couldn't find _jsdialog_confirm template"))
+
+  // the template needs to bind to either server-side behavior
+  // and unblock the UI
+  def confirm = {
+    "#yes" #> ((b: NodeSeq) =>
+      ajaxButton(b, () => {
+        println("Rhode Island Destroyed")
+        Unblock & Alert("Rhode Island Destroyed")
+      })) &
+      "#no" #> ((b: NodeSeq) => <button onclick={Unblock.toJsCmd}>
+          {b}
+        </button>)
+  }
+}
+ diff --git a/combo/example/src/main/webapp/simple_wizard.html b/combo/example/src/main/webapp/simple_wizard.html index ba451b5..51fb33b 100644 --- a/combo/example/src/main/webapp/simple_wizard.html +++ b/combo/example/src/main/webapp/simple_wizard.html @@ -21,7 +21,7 @@

Simple Wizard

-
+

Code to generate. It's easier to use Wizard, but here's the "old fashioned" way. diff --git a/combo/example/src/main/webapp/templates-hidden/default2.html b/combo/example/src/main/webapp/templates-hidden/default2.html index 8401a43..57590e5 100644 --- a/combo/example/src/main/webapp/templates-hidden/default2.html +++ b/combo/example/src/main/webapp/templates-hidden/default2.html @@ -1,25 +1,28 @@ - - - - - Lift Web Framework: %*% + + + + + - - - + Lift Web Framework: %*% - + + + - + - - + - + + + + +

@@ -41,7 +44,7 @@
@@ -49,21 +52,26 @@
- Current Time: Missing Clock + Current Time: + Missing Clock
- Group Chat
+ + Group Chat + +
- Hello Your name -
    -
  • - 12:15 - dpp - My Message -
  • -
-
+ Hello + Your name +
    +
  • + 12:15 + dpp + My Message +
  • +
+
@@ -74,25 +82,33 @@
-
+
-
Lift is Copyright 2007-2010 WorldWide Conferencing, LLC. Distributed under an Apache 2.0 License. -
- Lift version built on
. -
- Stats: Total Memory: - - Free Memory: - Open Sessions: -
- Updated At: - Started At: +
+ + Lift + is Copyright 2007-2010 WorldWide Conferencing, LLC. Distributed under an Apache 2.0 License. +
Lift version + built on +
. +
Stats: Total Memory: + + + Free Memory: + + Open Sessions: + +
Updated At: + + Started At: +
- + - + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/templates-hidden/wizard-all.html b/combo/example/src/main/webapp/templates-hidden/wizard-all.html index e1ea727..865ddf7 100644 --- a/combo/example/src/main/webapp/templates-hidden/wizard-all.html +++ b/combo/example/src/main/webapp/templates-hidden/wizard-all.html @@ -1,57 +1,64 @@
-
Page of
-
-
-
-
- - - - - - - - -
- - - - - - - - - -
    - -
  • - -
  • -
    -
-
-
- -
-
-
-
- - - - - - -
- - - - - -
-
-
-
-
+
+ Page + -1 of + -1423123 +
+ +
+ top content for whole wizard +
+ +
+ top content for screen +
+ +
+
    +
  • global error
  • +
+
+ +
+ + + + + + + +
+ + field help +
    +
  • field error
  • +
+
+ current field value +
+
+ +
+ + + + + + +
+ + + + + +
+
+ +
+ bottom content for screen +
+ +
+ bottom content for whole wizard +
+ \ No newline at end of file diff --git a/combo/example/src/main/webapp/templates-hidden/wizard-all.old.html b/combo/example/src/main/webapp/templates-hidden/wizard-all.old.html new file mode 100644 index 0000000..e1ea727 --- /dev/null +++ b/combo/example/src/main/webapp/templates-hidden/wizard-all.old.html @@ -0,0 +1,57 @@ +
+
Page of
+
+
+
+
+ + + + + + + + +
+ + + + + + + + + +
    + +
  • + +
  • +
    +
+
+
+ +
+
+
+
+ + + + + + +
+ + + + + +
+
+
+
+
diff --git a/combo/example/src/main/webapp/templating/pageOne.html b/combo/example/src/main/webapp/templating/pageOne.html index 111cbdd..e289cb9 100644 --- a/combo/example/src/main/webapp/templating/pageOne.html +++ b/combo/example/src/main/webapp/templating/pageOne.html @@ -1,9 +1,10 @@
Welcome to the bridge of sorrows -
+
-
+ \ No newline at end of file diff --git a/combo/example/src/main/webapp/wiz.html b/combo/example/src/main/webapp/wiz.html index 3f924df..818746c 100644 --- a/combo/example/src/main/webapp/wiz.html +++ b/combo/example/src/main/webapp/wiz.html @@ -1,110 +1,95 @@ - -
- An example of a multi-screen input wizard. The - Wizard is defined declaratively. -
-
- -
- -
-/**
- * An example of a wizard in Lift
- */
-object MyWizard extends Wizard {
-  object completeInfo extends WizardVar(false)
-
-  // define the first screen
-  val nameAndAge = new Screen {
-
-    // it has a name field
-    val name = new Field with StringField {
-      def title = S ?? "First Name"
-
-      override def validation = minLen(2, S ?? "Name Too Short") ::
-          maxLen(40, S ?? "Name Too Long") :: super.validation
-    }
-
-    // and an age field
-    val age = new Field with IntField {
-      def title = S ?? "Age"
-
-      override def validation = minVal(5, S ?? "Too young") ::
-          maxVal(120, S ?? "You should be dead") :: super.validation
-    }
-
-    // choose the next screen based on the age
-    override def nextScreen = if (age.is < 18) parentName else favoritePet
-  }
+
+
+
+
+  
+  Template
+
+
+
+  
+

Wizard

+ + +
+ An example of a multi-screen input wizard. The Wizard is defined declaratively. +
+ +
+ +
- // We ask the parent's name if the person is under 18 - val parentName = new Screen { - val parentName = new Field with StringField { - def title = S ?? "Mom or Dad's name" +
- override def validation = minLen(2, S ?? "Name Too Short") :: - maxLen(40, S ?? "Name Too Long") :: super.validation - } + The + Lift Scala code +
/**
+  * An example of a wizard in Lift
+  */
+object MyWizard extends Wizard {
+  object completeInfo extends WizardVar(false)
+
+  // define the first screen
+  val nameAndAge = new Screen {
+
+    // it has a name field
+    val name = field(S ? "First Name",
+                     "",
+                     valMinLen(2, S ? "Name Too Short"),
+                     valMaxLen(40, S ? "Name Too Long"),
+                     "type" -> "text",
+                     "class" -> "form-control")
+
+    // and an age field
+    val age = field(S ? "Age",
+                    0,
+                    minVal(5, S ? "Too young"),
+                    maxVal(120, S ? "You should be dead"),
+                    "type" -> "number",
+                    "class" -> "form-control")
+
+    // choose the next screen based on the age
+    override def nextScreen = if (age.is < 18) parentName else favoritePet
   }
 
-  // we ask for the favorite pet
-  val favoritePet = new Screen {
-    val petName = new Field with StringField {
-      def title = S ?? "Pet's name"
+  // We ask the parent's name if the person is under 18
+  val parentName = new Screen {
+    val parentName = field(S ? "Mom or Dad's name",
+                           "",
+                           valMinLen(2, S ? "Name Too Short"),
+                           valMaxLen(40, S ? "Name Too Long"),
+                           "type" -> "text",
+                           "class" -> "form-control")
+  }
 
-      override def validation = minLen(2, S ?? "Name Too Short") ::
-          maxLen(40, S ?? "Name Too Long") :: super.validation
-    }
+  // we ask for the favorite pet
+  val favoritePet = new Screen {
+    val petName = field(S ? "Pet's name",
+                        "",
+                        valMinLen(2, S ? "Name Too Short"),
+                        valMaxLen(40, S ? "Name Too Long"),
+                        "type" -> "text",
+                        "class" -> "form-control")
   }
 
-  // what to do on completion of the wizard
-  def finish() {
-    S.notice("Thank you for registering your pet")
-    completeInfo.set(true)
+  // what to do on completion of the wizard
+  def finish() {
+    S.notice("Thank you for registering your pet")
+    completeInfo.set(true)
   }
-}
-
- +}
+
-
+ + \ No newline at end of file diff --git a/combo/example/src/main/webapp/wiz2.html b/combo/example/src/main/webapp/wiz2.html index 5340f17..b20e1ec 100644 --- a/combo/example/src/main/webapp/wiz2.html +++ b/combo/example/src/main/webapp/wiz2.html @@ -1,72 +1,66 @@ - -
- Getting state right in a multi-page wizard is - non-trivial. This wizard allows you to run Erwin's tests. -
-
-
- -
-object WizardChallenge extends Wizard {
-  val page1 = new Screen {
-    val info = new Field with StringField {
-      def title = S ?? "Page one entry"
-    }
+    
+ + The + Lift Scala code +
class WizardChallenge extends Wizard {
+  val page1 = new Screen {
+    val info = field(S ? "Page one entry",
+                     "",
+                     "type" -> "text",
+                     "class" -> "form-control")
   }
 
-  val page2 = new Screen {
-    override def screenTop = <span>Page one field is {page1.info}</span>
+  val page2 = new Screen {
+    override def screenTop = <span>Page one field is {page1.info}</span>
+
+    val info = field(S ? "Page two entry",
+                     "",
+                     "type" -> "text",
+                     "class" -> "form-control")
+  }
 
-    val info = new Field with StringField {
-      def title = S ?? "Page two entry"
-    }
+  val page3 = new Screen {
+    override def screenTop =
+    <span>Page one field is {page1.info}<br/>Page two field is {page2.info}</span>
   }
 
-  val page3 = new Screen {
-    override def screenTop = <span>Page one field is {page1.info}<br/>Page two field is {page2.info}</span>
+  def finish() {
+    S.notice("Finished the challenge")
   }
-}
-
+}
+
-
+ + \ No newline at end of file From 4378e684fc21db25a7c411a894e96f7dc2a4d6d8 Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Thu, 1 Feb 2018 20:56:57 +0100 Subject: [PATCH 10/33] Updating build to use newely released Lift v3.2 and FoBo v2.0. --- combo/example/build.sbt | 6 +++--- combo/example/src/main/scala/bootstrap/liftweb/Boot.scala | 2 +- .../src/main/webapp/templates-hidden/wizard-all.old.html | 6 +++--- combo/example/src/main/webapp/templating/index.html | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/combo/example/build.sbt b/combo/example/build.sbt index f818aae..fa79d0f 100644 --- a/combo/example/build.sbt +++ b/combo/example/build.sbt @@ -16,13 +16,13 @@ resolvers ++= Seq("snapshots" at "https://oss.sonatype.org/content/repositor ) libraryDependencies ++= { - val liftVersion = "3.1.1" + val liftVersion = "3.2.0" Seq( "net.liftweb" %% "lift-webkit" % liftVersion, "net.liftweb" %% "lift-db" % liftVersion, "net.liftweb" %% "lift-mapper" % liftVersion, - "net.liftmodules" %% "fobo_3.1" % "2.0-SNAPSHOT", - "net.liftmodules" %% "lift-jquery-module_3.1" % "2.10", + "net.liftmodules" %% "fobo_3.2" % "2.0", + // "net.liftmodules" %% "lift-jquery-module_3.1" % "2.10", "net.liftmodules" %% "widgets_3.1" % "1.6.0-SNAPSHOT", "net.liftmodules" %% "textile_3.1" % "1.4-SNAPSHOT", "org.eclipse.jetty" % "jetty-webapp" % "8.1.7.v20120910" % "test", diff --git a/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala b/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala index 3657742..ba91e1e 100644 --- a/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala @@ -168,7 +168,7 @@ class Boot { fobo.Toolkit.init = fobo.Toolkit.HighlightJS930 fobo.Toolkit.init = fobo.Toolkit.FontAwesome470 //update to latest fobo.Toolkit.init = fobo.Toolkit.Bootstrap400 - fobo.Toolkit.init = fobo.Toolkit.Popper1125 + fobo.Toolkit.init = fobo.Toolkit.Popper1129 fobo.Toolkit.init = fobo.Toolkit.JQueryMigrate141 ThingBuilder.boot() diff --git a/combo/example/src/main/webapp/templates-hidden/wizard-all.old.html b/combo/example/src/main/webapp/templates-hidden/wizard-all.old.html index e1ea727..c9bec39 100644 --- a/combo/example/src/main/webapp/templates-hidden/wizard-all.old.html +++ b/combo/example/src/main/webapp/templates-hidden/wizard-all.old.html @@ -25,7 +25,7 @@
  • - + @@ -39,9 +39,9 @@
    - + - From 4920181de77713736e6407b712638ba67f54c008 Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Sun, 29 Jul 2018 11:07:27 +0200 Subject: [PATCH 14/33] Adding messages tag to top of page. Making mobile view of messages more readable by adding lift messages to the top of the page. --- .../liftweb/example/snippet/AjaxForm.scala | 2 +- .../net/liftweb/example/snippet/Json2.scala | 7 +- combo/example/src/main/webapp/ajax-form.html | 6 +- combo/example/src/main/webapp/ajax.html | 72 ++++++++++++++- combo/example/src/main/webapp/arc.html | 3 + .../src/main/webapp/assets/css/app.css | 28 ++++++ combo/example/src/main/webapp/async_rest.html | 4 + combo/example/src/main/webapp/chat.html | 4 + combo/example/src/main/webapp/database.html | 5 + combo/example/src/main/webapp/form_ajax.html | 4 + combo/example/src/main/webapp/guess.html | 4 + combo/example/src/main/webapp/index.html | 7 +- .../example/src/main/webapp/interactive.html | 5 + .../src/main/webapp/invoice_wiring.html | 4 + combo/example/src/main/webapp/json.html | 91 +++++-------------- combo/example/src/main/webapp/json_more.html | 5 + combo/example/src/main/webapp/lang.html | 4 + combo/example/src/main/webapp/lang_ca.html | 4 + combo/example/src/main/webapp/lang_de.html | 4 + combo/example/src/main/webapp/lang_en_CA.html | 4 + combo/example/src/main/webapp/lang_en_US.html | 4 + combo/example/src/main/webapp/lang_es.html | 4 + combo/example/src/main/webapp/lang_fr.html | 4 + combo/example/src/main/webapp/lang_it.html | 4 + combo/example/src/main/webapp/lang_no.html | 4 + combo/example/src/main/webapp/lang_sv.html | 4 + combo/example/src/main/webapp/lazy.html | 5 + combo/example/src/main/webapp/longtime.html | 3 + combo/example/src/main/webapp/misc.html | 4 + combo/example/src/main/webapp/parallel.html | 4 + .../example/src/main/webapp/persistence.html | 5 + .../example/src/main/webapp/rhodeisland.html | 4 + combo/example/src/main/webapp/simple/add.html | 3 +- .../src/main/webapp/simple/delete.html | 5 + .../example/src/main/webapp/simple/edit.html | 2 + .../example/src/main/webapp/simple/index.html | 4 + .../src/main/webapp/simple_screen.html | 3 + .../src/main/webapp/simple_wiring.html | 4 + .../src/main/webapp/simple_wizard.html | 4 + .../src/main/webapp/stateless_json.html | 4 + .../webapp/templates-hidden/default2.html | 2 +- .../src/main/webapp/templating/embed.html | 5 + .../main/webapp/templating/eval_order.html | 4 + .../src/main/webapp/templating/head.html | 4 + .../src/main/webapp/templating/index.html | 10 +- .../src/main/webapp/templating/pageThree.html | 1 + .../src/main/webapp/templating/pageTwo.html | 1 + .../main/webapp/templating/selectomatic.html | 4 + .../src/main/webapp/templating/surround.html | 5 + .../src/main/webapp/variable_screen.html | 3 + combo/example/src/main/webapp/wiz.html | 4 + combo/example/src/main/webapp/wiz2.html | 4 + combo/example/src/main/webapp/ws.html | 4 + 53 files changed, 313 insertions(+), 83 deletions(-) diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/AjaxForm.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/AjaxForm.scala index ad50184..a5f4aaf 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/AjaxForm.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/AjaxForm.scala @@ -55,7 +55,7 @@ class AjaxForm { }, "class" -> "form-control") & "#city" #> cityChoice(state) & "type=submit" #> submit(?("Save"), () => { - S.notice("City: " + city + ", State: " + state); redirectTo("/") + S.notice("City: " + city + ", State: " + state); redirectTo("#stateForm") }, "class" -> "btn btn-primary") } } diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Json2.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Json2.scala index 7e22bf4..95808bf 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/Json2.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Json2.scala @@ -18,14 +18,15 @@ package net.liftweb.example.snippet import net.liftweb.util.Helpers._ import net.liftweb.http.SHtml -import net.liftweb.http.js.{JsCmd, JE} -import net.liftweb.common.Loggable +import net.liftweb.http.js.{JE, JsCmd, JsonCall} +import net.liftweb.common.Logger import net.liftweb.json.JsonAST._ import net.liftweb.http.js.JsCmds.Alert import net.liftweb.json.DefaultFormats -object JsonCall extends Loggable { +object JsonCall { + private val logger = Logger(classOf[JsonCall]) implicit val formats = DefaultFormats case class Question(first: Int, second: Int, answer: Int) { diff --git a/combo/example/src/main/webapp/ajax-form.html b/combo/example/src/main/webapp/ajax-form.html index 72eb94f..d13ccfe 100644 --- a/combo/example/src/main/webapp/ajax-form.html +++ b/combo/example/src/main/webapp/ajax-form.html @@ -21,7 +21,11 @@

    AJAX Form Update

    - Choose city and state: +
    +
    +
    + +
    Choose city and state:
    diff --git a/combo/example/src/main/webapp/ajax.html b/combo/example/src/main/webapp/ajax.html index ec8152c..f545571 100644 --- a/combo/example/src/main/webapp/ajax.html +++ b/combo/example/src/main/webapp/ajax.html @@ -27,6 +27,10 @@

    AJAX Samples

    +
    +
    +
    +
    Click me to increase the count (currently @@ -36,7 +40,7 @@

    AJAX Samples


    -
    +
    @@ -61,21 +65,81 @@

    AJAX Samples

    -
    +

    - And each time an Ajax or Comet update is made to the page, we update this span: + Each time an Ajax or Comet update is made to the page, we update this span: The time is

    + + + + + + +
    + +
    + +
    + +
    +
    +
    +
    diff --git a/combo/example/src/main/webapp/arc.html b/combo/example/src/main/webapp/arc.html index 0190ff2..8a5945d 100644 --- a/combo/example/src/main/webapp/arc.html +++ b/combo/example/src/main/webapp/arc.html @@ -21,6 +21,9 @@

    Arc Challenge #1

    +
    +
    +
    diff --git a/combo/example/src/main/webapp/assets/css/app.css b/combo/example/src/main/webapp/assets/css/app.css index 77351b3..f5de920 100644 --- a/combo/example/src/main/webapp/assets/css/app.css +++ b/combo/example/src/main/webapp/assets/css/app.css @@ -134,6 +134,34 @@ ul.errors { color: #B94A48; } +#wrappedNoticeAtHead #lift__noticesContainer___notice ul, +#wrappedNoticeAtHead #lift__noticesContainer___warning ul, +#wrappedNoticeAtHead #lift__noticesContainer___error ul { + padding: 5px; + list-style-type: none; +} + +#wrappedNoticeAtHead #lift__noticesContainer___notice { + border-radius: 5px; + background-color: #D9EDF7; + border-color: #BCE8F1; + color: #3A87AD; +} + +#wrappedNoticeAtHead #lift__noticesContainer___warning { + border-radius: 5px; + background-color: #FCF8E3; + border-color: #FBEED5; + color: #C09853; +} + +#wrappedNoticeAtHead #lift__noticesContainer___error { + border-radius: 5px; + background-color: #F2DEDE; + border-color: #EED3D7; + color: #B94A48; +} + .widget ul { padding: 5px; } diff --git a/combo/example/src/main/webapp/async_rest.html b/combo/example/src/main/webapp/async_rest.html index 8481134..351fe3e 100644 --- a/combo/example/src/main/webapp/async_rest.html +++ b/combo/example/src/main/webapp/async_rest.html @@ -21,6 +21,10 @@

    Async REST

    +
    +
    +
    +
    This example demonstrates how to use the container's Asynchronous (continuation) response capabilities such that no I/O threads are consumed during the calculation of the REST response value. diff --git a/combo/example/src/main/webapp/chat.html b/combo/example/src/main/webapp/chat.html index 15b8db5..e6a0b41 100644 --- a/combo/example/src/main/webapp/chat.html +++ b/combo/example/src/main/webapp/chat.html @@ -21,6 +21,10 @@

    Comet Chat

    +
    +
    +
    +
    Group Chat diff --git a/combo/example/src/main/webapp/database.html b/combo/example/src/main/webapp/database.html index 6ab9fac..4b23417 100644 --- a/combo/example/src/main/webapp/database.html +++ b/combo/example/src/main/webapp/database.html @@ -20,6 +20,11 @@

    Database

    + +
    +
    +
    +

    Lift allows you to create model objects that map themselves to and from the database. Here's an example of a "User" model object.

    diff --git a/combo/example/src/main/webapp/form_ajax.html b/combo/example/src/main/webapp/form_ajax.html index fb1b060..f2a96a4 100644 --- a/combo/example/src/main/webapp/form_ajax.html +++ b/combo/example/src/main/webapp/form_ajax.html @@ -21,6 +21,10 @@

    Ajax and Forms

    +
    +
    +
    +

    Example: Forms with Ajax callback on form element blur

    Enter your first and last name:
    diff --git a/combo/example/src/main/webapp/guess.html b/combo/example/src/main/webapp/guess.html index 49c7f1e..ecab445 100644 --- a/combo/example/src/main/webapp/guess.html +++ b/combo/example/src/main/webapp/guess.html @@ -21,6 +21,10 @@

    Number Guessing

    +
    +
    +
    + Guess a number between 1 and 100. diff --git a/combo/example/src/main/webapp/index.html b/combo/example/src/main/webapp/index.html index fffc3a6..145d5ef 100644 --- a/combo/example/src/main/webapp/index.html +++ b/combo/example/src/main/webapp/index.html @@ -15,6 +15,11 @@

    Welcome to the + +
    +
    +
    +
    Lift provides the best features for building interactive web applications:
      @@ -40,7 +45,7 @@

      Welcome to the

      Lift is built on - Scala, a hybrid Functional and O-O language that compiles code down to the Java Virtual Machine. Scala code + Scala, a hybrid Functional and OOP language that compiles code down to the Java Virtual Machine. Scala code can call any Java code and make use of all Java classes. Java code can call some Scala code. Lift applications are packaged as WAR files and can be deployed on any Servlet 2.4 -- 3.1 engine (e.g., Tomcat 5.5.xx -- 8.5.xx, Jetty 6.0 -- 8.x, etc.) diff --git a/combo/example/src/main/webapp/interactive.html b/combo/example/src/main/webapp/interactive.html index 0e4a05d..4bdfd63 100644 --- a/combo/example/src/main/webapp/interactive.html +++ b/combo/example/src/main/webapp/interactive.html @@ -17,6 +17,11 @@

      Interactive Stuff

      + +
      +
      +
      +

      Lift provides powerful facilities to build highly interactive web applications.

      diff --git a/combo/example/src/main/webapp/invoice_wiring.html b/combo/example/src/main/webapp/invoice_wiring.html index 05243d4..bc9566f 100644 --- a/combo/example/src/main/webapp/invoice_wiring.html +++ b/combo/example/src/main/webapp/invoice_wiring.html @@ -21,6 +21,10 @@

      Wiring Invoice

      +
      +
      +
      +
      An example of using Lift's Wiring feature to build an invoice system.
      diff --git a/combo/example/src/main/webapp/json.html b/combo/example/src/main/webapp/json.html index 82ffdfb..cccba90 100644 --- a/combo/example/src/main/webapp/json.html +++ b/combo/example/src/main/webapp/json.html @@ -22,6 +22,10 @@

      JSON Samples

      +
      +
      +
      +

      Example 1

      Enter an addition question:

      @@ -50,81 +54,32 @@

      Example 1

      } // ]]> + The + Lift Scala code +
      object JsonCall {
       
      -    
      -

      Example 2

      -
      -
      -
      - -
      -
      - -
      - -
      -
      - -

      Here's the code:

      - - + } -
      -class Json {
      -  object json extends JsonHandler {
      -    def apply(in: Any): JsCmd =
      -    SetHtml("json_result", in match {
      -        case JsonCmd("show", _, p: String, _) => Text(p)
      -        case JsonCmd("textile", _, p: String, _) =>
      -          TextileParser.toHtml(p, Empty)
      -        case JsonCmd("count", _, p: String, _) => Text(p.length+" Characters")
      -        case x => <b>Problem... didn't handle JSON message {x}</b>
      -      })
      +    "button [onclick]" #>
      +      SHtml.jsonCall( JE.Call("currentQuestion"), validate _ )
         }
      -
      -  def sample(in: NodeSeq): NodeSeq =
      -  bind("json", in,
      -       "script" -> Script(json.jsCmd),
      -       AttrBindParam("onclick",
      -                     Text(json.call(ElemById("json_select")~>Value,
      -                                    ElemById("json_question")~>Value).toJsCmd),
      -                     "onclick"))
      -}
      -
      - +}
      diff --git a/combo/example/src/main/webapp/json_more.html b/combo/example/src/main/webapp/json_more.html index 2bdea50..f9cf140 100644 --- a/combo/example/src/main/webapp/json_more.html +++ b/combo/example/src/main/webapp/json_more.html @@ -17,6 +17,11 @@

      More JSON Samples

      + +
      +
      +
      +
      diff --git a/combo/example/src/main/webapp/lang.html b/combo/example/src/main/webapp/lang.html index 38b39c6..a0797bf 100644 --- a/combo/example/src/main/webapp/lang.html +++ b/combo/example/src/main/webapp/lang.html @@ -19,6 +19,10 @@

      Localization

      +
      +
      +
      +

      Hello diff --git a/combo/example/src/main/webapp/lang_ca.html b/combo/example/src/main/webapp/lang_ca.html index 23c3e9f..0556295 100644 --- a/combo/example/src/main/webapp/lang_ca.html +++ b/combo/example/src/main/webapp/lang_ca.html @@ -19,6 +19,10 @@

      Localization

      +
      +
      +
      +

      Hola

      La meva llengua: diff --git a/combo/example/src/main/webapp/lang_de.html b/combo/example/src/main/webapp/lang_de.html index 6a882ad..16cd90a 100644 --- a/combo/example/src/main/webapp/lang_de.html +++ b/combo/example/src/main/webapp/lang_de.html @@ -19,6 +19,10 @@

      Localization

      +
      +
      +
      +

      Hallo

      Meine Sprache: diff --git a/combo/example/src/main/webapp/lang_en_CA.html b/combo/example/src/main/webapp/lang_en_CA.html index 785b760..eb0c493 100644 --- a/combo/example/src/main/webapp/lang_en_CA.html +++ b/combo/example/src/main/webapp/lang_en_CA.html @@ -19,6 +19,10 @@

      Localization

      +
      +
      +
      +

      Good Day, eh.

      My language: diff --git a/combo/example/src/main/webapp/lang_en_US.html b/combo/example/src/main/webapp/lang_en_US.html index 0a1f9d0..bffbc30 100644 --- a/combo/example/src/main/webapp/lang_en_US.html +++ b/combo/example/src/main/webapp/lang_en_US.html @@ -18,6 +18,10 @@

      Localization

      + +
      +
      +
      + + + <span data-lift="Menu.title">%*%</span> + + + +
      +

      %*%

      + + +
      : Wiki
      +
      +
      + + + diff --git a/combo/example/src/main/webapp/wiz.html b/combo/example/src/main/webapp/wiz.html index b30df36..b14c524 100644 --- a/combo/example/src/main/webapp/wiz.html +++ b/combo/example/src/main/webapp/wiz.html @@ -8,7 +8,7 @@
      -

      Wizard

      +

      %*%

      diff --git a/combo/example/src/main/webapp/wiz2.html b/combo/example/src/main/webapp/wiz2.html index 82a554e..f953daf 100644 --- a/combo/example/src/main/webapp/wiz2.html +++ b/combo/example/src/main/webapp/wiz2.html @@ -8,7 +8,7 @@
      -

      Wizard Challenge

      +

      %*%

      diff --git a/combo/example/src/main/webapp/ws.html b/combo/example/src/main/webapp/ws.html index 8bfb0d8..6d4e788 100644 --- a/combo/example/src/main/webapp/ws.html +++ b/combo/example/src/main/webapp/ws.html @@ -9,13 +9,13 @@
      -

      Web Services

      +

      %*%

      From 59e4565ad757124d754003f9c760b2f31fa7048f Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Sat, 1 Sep 2018 19:14:11 +0200 Subject: [PATCH 18/33] Converting bind and choceTemplate code Replacing deprecated and removed scala code with css selector alternatives, plus some rendom cleanup. --- combo/example/build.sbt | 2 +- .../{CountGame.scalaREM => CountGame.scala} | 48 ++++---- combo/example/src/main/webapp/ajax.html | 59 ---------- combo/example/src/main/webapp/guess.html | 105 ++++++++++++++---- combo/example/src/main/webapp/misc.html | 2 +- 5 files changed, 111 insertions(+), 105 deletions(-) rename combo/example/src/main/scala/net/liftweb/example/snippet/{CountGame.scalaREM => CountGame.scala} (60%) diff --git a/combo/example/build.sbt b/combo/example/build.sbt index 5a6e536..f251b3e 100644 --- a/combo/example/build.sbt +++ b/combo/example/build.sbt @@ -4,7 +4,7 @@ organization := "net.liftweb" name := "demo" -version := "0.5.8-SNAPSHOT" +version := "0.6.0-SNAPSHOT" scalaVersion := "2.11.11" //2.12.2 diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/CountGame.scalaREM b/combo/example/src/main/scala/net/liftweb/example/snippet/CountGame.scala similarity index 60% rename from combo/example/src/main/scala/net/liftweb/example/snippet/CountGame.scalaREM rename to combo/example/src/main/scala/net/liftweb/example/snippet/CountGame.scala index e6c9c91..d5479bb 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/CountGame.scalaREM +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/CountGame.scala @@ -14,15 +14,10 @@ * limitations under the License. */ -package net.liftweb { -package example { -package snippet { +package net.liftweb.example.snippet -import _root_.net.liftweb.example.model._ -import _root_.scala.xml.{NodeSeq, Text, Group, Node} +import _root_.scala.xml.{NodeSeq, Text, Node} import _root_.net.liftweb.http._ -import _root_.net.liftweb.http.S -import _root_.net.liftweb.mapper._ import _root_.net.liftweb.http.S._ import _root_.net.liftweb.http.SHtml._ import _root_.net.liftweb.util.Helpers._ @@ -30,31 +25,36 @@ import _root_.net.liftweb.common._ import _root_.net.liftweb.util._ -class CountGameREM extends StatefulSnippet { +class CountGame extends StatefulSnippet { val dispatch: DispatchIt = { - case "run" if lastGuess == number => - xhtml => win(chooseTemplate("choose", "win", xhtml)) + case "run" if lastGuess == number => xhtml => win(xhtml) - case "run" => - xhtml => nextGuess(chooseTemplate("choose", "guess", xhtml)) + case "run" => xhtml => nextGuess(xhtml) case "count_down" => xhtml => countDown(attr("from").map(Helpers.toInt).openOr(0)) } - def win(xhtml: NodeSeq) = bind("count", xhtml, "number" -> number, - "count" -> count) ++

      Counting backward: {countDown(number)}

      + def win(xhtml: NodeSeq) = + ( "#win ^*" #> "#choose" & + ".number" #> Text(number.toString) & + ".count" #> Text(count.toString) + ).apply(xhtml) ++ +

      Counting backward: + {countDown(number)} +

      def countDown(number: Int): Node = if (number <= 0) Text("") - else {number} + else {number} - def nextGuess(xhtml: NodeSeq) = bind("count", xhtml, - "input" -> text("", guess _), - "last" -> - lastGuess.map(v => - if (v < number) v+" is low" - else v+" is high"). - openOr("Make first Guess")) + def nextGuess(xhtml: NodeSeq): NodeSeq = + ("#guess ^*" #> "#choose" & + ".input" #> text("", guess _, "class" -> "form-control") & + ".last *" #> lastGuess.map(v => + if (v < number) v + " is low" + else v + " is high"). + openOr("Make first Guess") + ).apply(xhtml) private def guess(in: String) { count += 1 @@ -65,6 +65,4 @@ class CountGameREM extends StatefulSnippet { private var lastGuess: Box[Int] = Empty private var count = 0 } -} -} -} + diff --git a/combo/example/src/main/webapp/ajax.html b/combo/example/src/main/webapp/ajax.html index cb0f13a..6ddc69e 100644 --- a/combo/example/src/main/webapp/ajax.html +++ b/combo/example/src/main/webapp/ajax.html @@ -71,65 +71,6 @@

      %*%

      The time is

      - - - - - - - -
      diff --git a/combo/example/src/main/webapp/guess.html b/combo/example/src/main/webapp/guess.html index 56f2a1c..8733e94 100644 --- a/combo/example/src/main/webapp/guess.html +++ b/combo/example/src/main/webapp/guess.html @@ -25,27 +25,94 @@

      %*%

      - - - Guess a number between 1 and 100. -
      Last guess: - -
      Guess: - -
      - -
      - - - You Win!! -
      You guessed - after - guesses. -
      -
      -
      +
      + + + Guess a number between 1 and 100.
      + Last guess: +
      + +
      + +
      +
      + +
      + + + You Win!!
      + You guessed after guesses.
      +
      +
      +

      + The Markup +
      <div data-lift="CountGame.run?form=post">
      +  <span id="choose">
      +    <span id="guess">
      +      <span>Guess a number between 1 and 100.</span><br>
      +      <span>Last guess: </span><span class="last"></span>
      +      <div class="form-group row">
      +        <label for="nbr" class="col-sm-1 col-form-label">Guess:</label>
      +        <div class="col-sm-2">
      +          <span class="input" id="nbr"></span>
      +        </div>
      +      </div>
      +      <input class="btn btn-primary" type="submit" value="Guess">
      +    </span>
      +
      +    <span id="win">
      +      <span>You Win!!</span><br>
      +      <span>You guessed <span class="number"></span> after
      +      <span class="count"></span> guesses.</span><br>
      +    </span>
      +  </span>
      +</div>
      + + The + Lift Scala code +
      class CountGame extends StatefulSnippet {
      +  val dispatch: DispatchIt = {
      +    case "run" if lastGuess == number => xhtml => win(xhtml)
      +
      +    case "run" => xhtml => nextGuess(xhtml)
      +
      +    case "count_down" =>
      +    xhtml => countDown(attr("from").map(Helpers.toInt).openOr(0))
      +  }
      +
      +  def win(xhtml: NodeSeq) =
      +    ( "#win ^*" #> "#choose" &
      +      ".number" #> Text(number.toString) &
      +      ".count" #> Text(count.toString)
      +      ).apply(xhtml) ++
      +      <p>Counting backward:
      +        {countDown(number)}
      +      </p>
      +
      +  def countDown(number: Int): Node = if (number <= 0) Text("")
      +  else <xml:group>{number} <lift:count_game.count_down from={(number - 1).toString}/></xml:group>
      +
      +  def nextGuess(xhtml: NodeSeq): NodeSeq =
      +    ("#guess ^*" #> "#choose" &
      +     ".input" #> text("", guess _, "class" -> "form-control") &
      +      ".last *" #> lastGuess.map(v =>
      +        if (v < number) v + " is low"
      +        else v + " is high").
      +        openOr("Make first Guess")
      +      ).apply(xhtml)
      +
      +  private def guess(in: String) {
      +    count += 1
      +    lastGuess = Full(toInt(in))
      +  }
      +
      +  private val number = 1 + randomInt(100)
      +  private var lastGuess: Box[Int] = Empty
      +  private var count = 0
      +}
      +
      The view code: - + + + Template + -
      -/**
      +
      +
      +

      %*%

      + + +

      The Number Counting Example

      +

      +

      The first value is + + Increment + Decrement +
      +

      + +

      +

      The second value is + + Increment + Decrement +
      +

      + + The + Lift Scala code +
      /**
         * This session variable holds a set of Names and Integers, with a default value
         * of 0 (zero)
      -  */
      -object CountHolder extends SessionVar[HashMap[String, Int]](new HashMap[String, Int] {
      -  override def default(key: String): Int = 0
      +  */
      +object CountHolder extends SessionVar[HashMap[String, Int]](new HashMap[String, Int] {
      +  override def default(key: String): Int = 0
       })
       
      -/**
      +/**
         * This snippet handles counting
      -  */
      -class Count {
      -  
      -  /**
      -    * This method is invoked by the &lt;lift:Count /&gt; tag
      -    */
      -  def render(in: NodeSeq): NodeSeq = {
      -    // get the attribute name (the name of the thing to count)
      -    val attr: String = S.attr("name").openOr("N/A")
      -    
      -    // Get the value from the session variable
      -    val value = CountHolder.is(attr)
      -    
      -    // Bind the incoming view data and the model data
      -    bind("count", in, "value" --> value,
      -    "incr" --> link("/count", () => CountHolder.is(attr) = value + 1, Text("++")),
      -    "decr" --> link("/count", () => CountHolder.is(attr) = 0 max (value - 1), Text("--")))
      +  */
      +class Count {
      +
      +  /**
      +    * This method is invoked by the <lift:Count /> tag
      +    */
      +  def render = {
      +    // get the attribute name (the name of the thing to count)
      +    val attr: String = S.attr("name").openOr("N/A")
      +
      +    // Get the value from the session variable
      +    val value = CountHolder.is(attr)
      +
      +    // Bind the incoming view data and the model data
      +    ".value" #> Text(value.toString) &
      +      ".incr" #> link("/count", () => CountHolder.is(attr) = value + 1, Text("++")) &
      +      ".decr" #> link("/count", () => CountHolder.is(attr) = 0 max (value - 1), Text("--"))
         }
      -}
      -
      +}
      + +
      + - + diff --git a/combo/example/src/main/webapp/file_upload.html b/combo/example/src/main/webapp/file_upload.html index 1a0bccc..775d9e4 100644 --- a/combo/example/src/main/webapp/file_upload.html +++ b/combo/example/src/main/webapp/file_upload.html @@ -26,30 +26,66 @@

      %*%

      type, the number of bytes in the file and the MD5 checksum.

      - - - + + +

      - File name: - -
      MIME Type: - -
      File length: - -
      MD5 Hash: - -
      + File name:
      + MIME Type:
      + File length:
      + MD5 Hash:

      -
      + + + + Select a file to upload:
      + +
      + + + +
      + The Markup +
      <form data-lift="misc.upload?form=post&multipart=true">
      +    <span id="choose">
      +        <span id="post">
      +        <p>
      +            File name: <span class="file_name"></span><br>
      +            MIME Type: <span class="mime_type"></span><br>
      +            File length: <span class="length"></span><br>
      +            MD5 Hash: <span class="md5"></span><br>
      +        </p>
      +        </span>
      +
      +        <span id="get">
      +          Select a file to upload: <input class="file_upload"><br>
      +          <input type="submit" value="Upload File" class="btn btn btn-primary">
      +        </span>
      +    </span>
      +</form>
      - - Select a file to upload: - -
      - -
      + The + Lift Scala code +
      // the request-local variable that hold the file parameter
      +private object theUpload extends RequestVar[Box[FileParamHolder]](Empty)
       
      -        
      +def upload(xhtml: Group): NodeSeq =
      +if (S.get_?)
      +  (
      +    "#get ^*" #> "#choose" &
      +      ".file_upload" #> fileUpload(ul => theUpload(Full(ul)))
      +  ) apply (xhtml)
      +else
      +  ("#post ^*" #> "#choose" &
      +    ".file_name" #> theUpload.is.map(v => Text(v.fileName)) &
      +    ".mime_type" #> theUpload.is.map(
      +      v =>
      +        Box
      +          .legacyNullTest(v.mimeType)
      +          .map(Text.apply)
      +          .openOr(Text("No mime type supplied"))) &
      +    ".length" #> theUpload.is.map(v => Text(v.file.length.toString)) &
      +    ".md5" #> theUpload.is.map(v => Text(hexEncode(md5(v.file))))) apply (xhtml)
      diff --git a/combo/example/src/main/webapp/templating/_sample_embed.html b/combo/example/src/main/webapp/templating/_sample_embed.html index 1e646d9..0c27437 100644 --- a/combo/example/src/main/webapp/templating/_sample_embed.html +++ b/combo/example/src/main/webapp/templating/_sample_embed.html @@ -1,8 +1,8 @@ - + Lift Example - +

      I am an example of a template that is embedded!!

      diff --git a/combo/example/src/main/webapp/templating/eval_order.html b/combo/example/src/main/webapp/templating/eval_order.html index 56cb510..7d191f8 100644 --- a/combo/example/src/main/webapp/templating/eval_order.html +++ b/combo/example/src/main/webapp/templating/eval_order.html @@ -26,7 +26,7 @@

      %*%

      - Lift evaluates the <xxx data-lift="yyy"/> <lift:xxx/> tags from the outside in, otherwise knows as lazy evaluation. + Lift evaluates the <tagName data-lift="xxx"/> <lift:xxx/> tags from the outside in, otherwise knows as lazy evaluation. This means that the following code will only embed a single template:

      HTML syntax From 26ab2bfc702c7d4db69bb7292c1d3867e6866c1c Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Sun, 2 Dec 2018 19:33:09 +0100 Subject: [PATCH 20/33] Using FoBo's Bootstrap breadCrumb snippet function. Adding font awesome v5.5.0 using it's svg images. --- combo/example/build.sbt | 4 +- .../main/scala/bootstrap/liftweb/Boot.scala | 26 +- .../net/liftweb/example/lib/WikiStuff.scala | 5 +- .../example/snippet/FormWithAjax.scala | 13 +- .../net/liftweb/example/snippet/Json2.scala | 1 + .../net/liftweb/example/snippet/Misc.scala | 5 +- .../net/liftweb/example/view/XmlFun.scala | 14 +- combo/example/src/main/webapp/ajax-form.html | 15 +- combo/example/src/main/webapp/ajax.html | 15 +- combo/example/src/main/webapp/arc.html | 15 +- combo/example/src/main/webapp/async_rest.html | 15 +- combo/example/src/main/webapp/chat.html | 17 +- combo/example/src/main/webapp/count.html | 15 +- combo/example/src/main/webapp/database.html | 21 +- .../example/src/main/webapp/file_upload.html | 15 +- combo/example/src/main/webapp/form_ajax.html | 21 +- combo/example/src/main/webapp/guess.html | 15 +- combo/example/src/main/webapp/index.html | 18 +- .../example/src/main/webapp/interactive.html | 12 +- .../src/main/webapp/invoice_wiring.html | 15 +- combo/example/src/main/webapp/json.html | 73 +++-- combo/example/src/main/webapp/json_more.html | 15 +- combo/example/src/main/webapp/lang.html | 12 +- combo/example/src/main/webapp/lang_ca.html | 12 +- combo/example/src/main/webapp/lang_de.html | 12 +- combo/example/src/main/webapp/lang_en_CA.html | 12 +- combo/example/src/main/webapp/lang_en_US.html | 12 +- combo/example/src/main/webapp/lang_es.html | 12 +- combo/example/src/main/webapp/lang_fr.html | 12 +- combo/example/src/main/webapp/lang_it.html | 12 +- combo/example/src/main/webapp/lang_no.html | 12 +- combo/example/src/main/webapp/lang_sv.html | 12 +- combo/example/src/main/webapp/lazy.html | 23 +- .../example/src/main/webapp/login/index.html | 15 +- .../src/main/webapp/login/validate.html | 15 +- combo/example/src/main/webapp/longtime.html | 15 +- combo/example/src/main/webapp/menu/four.html | 17 +- combo/example/src/main/webapp/menu/index.html | 14 +- combo/example/src/main/webapp/menu/one.html | 17 +- combo/example/src/main/webapp/menu/three.html | 17 +- combo/example/src/main/webapp/menu/two.html | 17 +- .../example/src/main/webapp/menu/two_one.html | 20 +- .../example/src/main/webapp/menu/two_two.html | 20 +- combo/example/src/main/webapp/misc.html | 12 +- combo/example/src/main/webapp/parallel.html | 15 +- .../example/src/main/webapp/persistence.html | 12 +- .../example/src/main/webapp/rhodeisland.html | 15 +- combo/example/src/main/webapp/simple.html | 2 +- combo/example/src/main/webapp/simple/add.html | 18 +- .../src/main/webapp/simple/delete.html | 18 +- .../example/src/main/webapp/simple/edit.html | 18 +- .../example/src/main/webapp/simple/index.html | 15 +- .../src/main/webapp/simple_screen.html | 15 +- .../src/main/webapp/simple_wiring.html | 15 +- .../src/main/webapp/simple_wizard.html | 15 +- .../src/main/webapp/stateless_json.html | 15 +- combo/example/src/main/webapp/template.html | 2 +- .../webapp/templates-hidden/_resources.html | 23 ++ .../templates-hidden/_spinner_template.html | 1 + .../main/webapp/templates-hidden/default.html | 250 +++++++----------- .../webapp/templates-hidden/default2.html | 114 -------- .../src/main/webapp/templating/embed.html | 15 +- .../main/webapp/templating/eval_order.html | 15 +- .../src/main/webapp/templating/head.html | 15 +- .../src/main/webapp/templating/index.html | 52 ++-- .../main/webapp/templating/selectomatic.html | 15 +- .../src/main/webapp/templating/surround.html | 15 +- .../src/main/webapp/variable_screen.html | 15 +- combo/example/src/main/webapp/wiki.html | 15 +- combo/example/src/main/webapp/wikibind.html | 2 +- combo/example/src/main/webapp/wiz.html | 15 +- combo/example/src/main/webapp/wiz2.html | 15 +- combo/example/src/main/webapp/ws.html | 12 +- 73 files changed, 411 insertions(+), 1040 deletions(-) create mode 100644 combo/example/src/main/webapp/templates-hidden/_resources.html create mode 100644 combo/example/src/main/webapp/templates-hidden/_spinner_template.html delete mode 100644 combo/example/src/main/webapp/templates-hidden/default2.html diff --git a/combo/example/build.sbt b/combo/example/build.sbt index 4d41d4f..176f3ba 100644 --- a/combo/example/build.sbt +++ b/combo/example/build.sbt @@ -4,7 +4,7 @@ organization := "net.liftweb" name := "demo" -version := "0.6.0-SNAPSHOT" +version := "0.6.1-SNAPSHOT" scalaVersion := "2.11.11" //2.12.2 @@ -22,7 +22,7 @@ libraryDependencies ++= { "net.liftweb" %% "lift-json" % liftVersion, "net.liftweb" %% "lift-db" % liftVersion, "net.liftweb" %% "lift-mapper" % liftVersion, - "net.liftmodules" %% "fobo_3.3" % "2.0", + "net.liftmodules" %% "fobo_3.3" % "2.1.0-SNAPSHOT", "net.liftmodules" %% "widgets_3.1" % "1.6.0-SNAPSHOT", "net.liftmodules" %% "textile_3.1" % "1.4-SNAPSHOT", "org.eclipse.jetty" % "jetty-webapp" % "8.1.7.v20120910" % "test", diff --git a/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala b/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala index 44c04f9..1de8028 100644 --- a/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala @@ -26,7 +26,6 @@ import Helpers._ import example._ import net.liftmodules.widgets.autocomplete._ -// import net.liftmodules.JQueryModule import net.liftmodules.fobo import comet._ import model._ @@ -166,8 +165,8 @@ class Boot { // FoBo init fobo.Toolkit.init = fobo.Toolkit.JQuery224 fobo.Toolkit.init = fobo.Toolkit.HighlightJS930 - fobo.Toolkit.init = fobo.Toolkit.FontAwesome470 //update to latest - fobo.Toolkit.init = fobo.Toolkit.Bootstrap400 + fobo.Toolkit.init = fobo.Toolkit.FontAwesome550 + fobo.Toolkit.init = fobo.Toolkit.Bootstrap413 fobo.Toolkit.init = fobo.Toolkit.Popper1129 fobo.Toolkit.init = fobo.Toolkit.JQueryMigrate141 @@ -209,8 +208,8 @@ object MenuInfo { lazy val noGAE = Unless(() => Props.inGAE, "Disabled for GAE") def sitemap() = SiteMap( - Menu("Home") / "index", - Menu("Interactive Stuff") / "interactive" submenus( + Menu.i("Home") / "index", + Menu.i("Interactive Stuff") / "interactive" submenus( Menu("Comet Chat") / "chat" >> noGAE, Menu("Ajax Samples") / "ajax", Menu("Ajax Form") / "ajax-form", @@ -219,13 +218,13 @@ object MenuInfo { Menu("Stateless JSON Messaging") / "stateless_json", // Menu("More JSON") / "json_more", Menu("Ajax and Forms") / "form_ajax") , - Menu("Persistence") / "persistence" >> noGAE submenus ( + Menu.i("Persistence") / "persistence" >> noGAE submenus ( Menu("XML Fun") / "xml_fun" >> noGAE, Menu("Database") / "database" >> noGAE, Menu(Loc("simple", Link(List("simple"), true, "/simple/index"), "Simple Forms", noGAE)) //, // Menu("Templates") / "template" >> noGAE ), - Menu("Templating") / "templating" / "index" submenus( + Menu.i("Templating") / "templating" / "index" submenus( Menu("Surround") / "templating" / "surround", Menu("Embed") / "templating" / "embed", Menu("Evalutation Order") / "templating" / "eval_order", @@ -234,9 +233,9 @@ object MenuInfo { Menu("Lazy Loading") / "lazy", Menu("Parallel Snippets") / "parallel", Menu(" tag") / "templating"/ "head"), - Menu("Web Services") / "ws" >> noGAE, - Menu("Localization") / "lang", - Menu("Menus") / "menu" / "index" submenus( + Menu.i("Web Services") / "ws" >> noGAE, + Menu.i("Localization") / "lang", + Menu.i("Menus") / "menu" / "index" submenus( Menu("First Submenu") / "menu" / "one", Menu("Second Submenu (has more)") / "menu" / "two" submenus( Menu("First (2) Submenu") / "menu" / "two_one", @@ -244,7 +243,7 @@ object MenuInfo { Menu("Third Submenu") / "menu" / "three", Menu("Forth Submenu") / "menu" / "four"), Menu(WikiStuff), - Menu("Misc code") / "misc" submenus( + Menu.i("Misc code") / "misc" submenus( Menu("Long Time") / "longtime", Menu("Number Guessing") / "guess", Menu("Wizard") / "wiz", @@ -260,9 +259,10 @@ object MenuInfo { Requiring LoginSiteMap )), Menu("Counting") / "count"), Menu(Loc("lift", ExtLink("http://liftweb.net"), - Lift project home)), + S.loc( "lift", Lift project home))), Menu(Loc("src", ExtLink("https://github.com/lift/examples/tree/master/combo/example"), - "Source code for this site"))) + S.loc("src", scala.xml.Text("Source code for this site")))) + ) } /** diff --git a/combo/example/src/main/scala/net/liftweb/example/lib/WikiStuff.scala b/combo/example/src/main/scala/net/liftweb/example/lib/WikiStuff.scala index 1101b4c..d20b6d1 100644 --- a/combo/example/src/main/scala/net/liftweb/example/lib/WikiStuff.scala +++ b/combo/example/src/main/scala/net/liftweb/example/lib/WikiStuff.scala @@ -60,6 +60,7 @@ object WikiStuff extends Loc[WikiLoc] { // the default parameters (used for generating the menu listing) def defaultValue = Full(WikiLoc("HomePage", false)) + // def text = S.loc("Wiki HomePage") // no extra parameters def params = List(Unless(() => Props.inGAE || Props.productionMode, "Disabled for GAE")) @@ -101,9 +102,9 @@ object WikiStuff extends Loc[WikiLoc] { def calcLinkText(in: WikiLoc): NodeSeq = if (in.edit) - Text("Wiki edit "+in.page) + S.loc("Wiki edit "+in.page, Text("Wiki edit "+in.page)) else - Text("Wiki "+in.page) + S.loc("Wiki "+in.page, Text("Wiki "+in.page)) /** * Rewrite the request and emit the type-safe parameter diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/FormWithAjax.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/FormWithAjax.scala index c3efcad..94c7eff 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/FormWithAjax.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/FormWithAjax.scala @@ -23,13 +23,12 @@ import js._ import JsCmds._ import util._ import Helpers._ - -import scala.xml.NodeSeq +// import scala.xml.{NodeSeq, Text, Group} class FormWithAjax extends StatefulSnippet { private var firstName = "" private var lastName = "" - private val from = S.referer openOr "/" + private val to = "#wrappedNoticeAtHead" def dispatch = { case _ => render @@ -44,8 +43,8 @@ class FormWithAjax extends StatefulSnippet { case (f, _) if f < 2 => S.error("fwaErrMsg","First name too short") case (_, n) if n < 2 => S.error("fwaErrMsg","Last name too short") case _ => { - S.notice("Ajax form says Thanks!") - S.redirectTo(from) + S.notice("fwaNoticeMsg", "Ajax form says Thanks!") + S.redirectTo(to) } } } @@ -53,7 +52,7 @@ class FormWithAjax extends StatefulSnippet { "#first" #> textAjaxTest(firstName, s => firstName = s, s => { - S.notice("First name " + s); Noop + S.notice("fwaNoticeMsg","First name " + s); Noop }, "class" -> "form-control", "type" -> "text", @@ -61,7 +60,7 @@ class FormWithAjax extends StatefulSnippet { "#last" #> textAjaxTest(lastName, s => lastName = s, s => { - S.notice("Last name " + s); Noop + S.notice("fwaNoticeMsg", "Last name " + s); Noop }, "class" -> "form-control", "type" -> "text", diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Json2.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Json2.scala index 95808bf..4bfcda0 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/Json2.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Json2.scala @@ -24,6 +24,7 @@ import net.liftweb.json.JsonAST._ import net.liftweb.http.js.JsCmds.Alert import net.liftweb.json.DefaultFormats +// Example taken from http://cookbook.liftweb.net/book.html#id-rjCJTYFwuYtbh9 object JsonCall { private val logger = Logger(classOf[JsonCall]) diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Misc.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Misc.scala index dd6e82a..3015bfd 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/Misc.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Misc.scala @@ -33,7 +33,6 @@ import java.util.Locale class Misc { private object selectedUser extends RequestVar[Box[User]](Empty) private val logger = Logger(classOf[Misc]) - /** * Get the XHTML containing a list of users */ @@ -52,8 +51,8 @@ class Misc {

    {User.htmlHeaders} :: // get and display each of the users User.findAll(OrderBy(User.id, Ascending)).flatMap(u => {u.htmlLine} - - + + ) } diff --git a/combo/example/src/main/scala/net/liftweb/example/view/XmlFun.scala b/combo/example/src/main/scala/net/liftweb/example/view/XmlFun.scala index 32f1154..116d412 100644 --- a/combo/example/src/main/scala/net/liftweb/example/view/XmlFun.scala +++ b/combo/example/src/main/scala/net/liftweb/example/view/XmlFun.scala @@ -20,6 +20,7 @@ import scala.xml.{Text, NodeSeq} import net.liftweb.http._ import S._ import net.liftweb.common._ +//import net.liftweb.example.lib.Util.{breadcrumb} class XmlFun extends LiftView { def dispatch = Map("index" -> render _) @@ -36,22 +37,17 @@ class XmlFun extends LiftView { val toCount = param("country") openOr "US" + //List(("index.html", Text("Home")),("persistence.html", Text("Persistance"))),("",Text("XML Fun")) + Full(Template -
    +

    XML Fun

    - - + The XML is
    {addresses.map{e => Text(e.toString) :: 
    :: Nil}}

    The count for {toCount} nodes is {countryCount(toCount, addresses)}

    diff --git a/combo/example/src/main/webapp/ajax-form.html b/combo/example/src/main/webapp/ajax-form.html index 5135b5b..e1d8047 100644 --- a/combo/example/src/main/webapp/ajax-form.html +++ b/combo/example/src/main/webapp/ajax-form.html @@ -7,19 +7,10 @@ -
    +

    %*%

    - + +
    Choose city and state:
    diff --git a/combo/example/src/main/webapp/ajax.html b/combo/example/src/main/webapp/ajax.html index 6ddc69e..7fca64b 100644 --- a/combo/example/src/main/webapp/ajax.html +++ b/combo/example/src/main/webapp/ajax.html @@ -7,7 +7,7 @@ -
    +
    @@ -15,17 +15,8 @@

    %*%

    - + +
    diff --git a/combo/example/src/main/webapp/arc.html b/combo/example/src/main/webapp/arc.html index 02a8d1e..261a40e 100644 --- a/combo/example/src/main/webapp/arc.html +++ b/combo/example/src/main/webapp/arc.html @@ -7,19 +7,10 @@ -
    +

    %*%

    - + +
    diff --git a/combo/example/src/main/webapp/async_rest.html b/combo/example/src/main/webapp/async_rest.html index bd71a94..ce6e13c 100644 --- a/combo/example/src/main/webapp/async_rest.html +++ b/combo/example/src/main/webapp/async_rest.html @@ -7,19 +7,10 @@ -
    +

    %*%

    - + +
    diff --git a/combo/example/src/main/webapp/chat.html b/combo/example/src/main/webapp/chat.html index e6eb556..14d4ab8 100644 --- a/combo/example/src/main/webapp/chat.html +++ b/combo/example/src/main/webapp/chat.html @@ -7,19 +7,10 @@ -
    +

    %*%

    - + +
    @@ -27,7 +18,7 @@

    %*%

    - Group Chat + Group Chat

    diff --git a/combo/example/src/main/webapp/count.html b/combo/example/src/main/webapp/count.html index 93c80f1..f0fe23a 100644 --- a/combo/example/src/main/webapp/count.html +++ b/combo/example/src/main/webapp/count.html @@ -7,19 +7,10 @@ -
    +

    %*%

    - + +

    The Number Counting Example

    diff --git a/combo/example/src/main/webapp/database.html b/combo/example/src/main/webapp/database.html index a9a899f..fa9c7b6 100644 --- a/combo/example/src/main/webapp/database.html +++ b/combo/example/src/main/webapp/database.html @@ -7,19 +7,10 @@ -

    +

    %*%

    - + +
    @@ -29,12 +20,12 @@

    %*%

    Lift allows you to create model objects that map themselves to and from the database. Here's an example of a "User" model object.

    - object User extends User +
    object User extends User
    There are records in the "Person" table. The first one is - . Press - to create more records. + . +

    Press to create more records.


    The diff --git a/combo/example/src/main/webapp/file_upload.html b/combo/example/src/main/webapp/file_upload.html index 775d9e4..8fc7cc3 100644 --- a/combo/example/src/main/webapp/file_upload.html +++ b/combo/example/src/main/webapp/file_upload.html @@ -7,19 +7,10 @@ -
    +

    %*%

    - + +

    This is the file upload sample page. You choose a file to upload and the application will tell you the file name, the mime diff --git a/combo/example/src/main/webapp/form_ajax.html b/combo/example/src/main/webapp/form_ajax.html index d0474e8..c0d0523 100644 --- a/combo/example/src/main/webapp/form_ajax.html +++ b/combo/example/src/main/webapp/form_ajax.html @@ -7,22 +7,13 @@ -

    +

    %*%

    - + +
    -
    +

    Example: Forms with Ajax callback on form element blur

    @@ -54,7 +45,7 @@

    Example: Forms with Ajax callback on form element blur

    class FormWithAjax extends StatefulSnippet {
       private var firstName = ""
       private var lastName = ""
    -  private val from = S.referer openOr "/"
    +  private val to = "#wrappedNoticeAtHead"
     
       def dispatch = {
         case _ => render
    @@ -70,7 +61,7 @@ 

    Example: Forms with Ajax callback on form element blur

    case (_, n) if n < 2 => S.error("fwaErrMsg", "Last name too short") case _ => { S.notice("Ajax form says Thanks!") - S.redirectTo(from) + S.redirectTo(to) } } } diff --git a/combo/example/src/main/webapp/guess.html b/combo/example/src/main/webapp/guess.html index 8733e94..22e9271 100644 --- a/combo/example/src/main/webapp/guess.html +++ b/combo/example/src/main/webapp/guess.html @@ -7,19 +7,10 @@ -
    +

    %*%

    - + +
    diff --git a/combo/example/src/main/webapp/index.html b/combo/example/src/main/webapp/index.html index 35749be..6857c14 100644 --- a/combo/example/src/main/webapp/index.html +++ b/combo/example/src/main/webapp/index.html @@ -7,14 +7,10 @@ -
    +

    Welcome to the Lift Web Framework

    - +
    @@ -22,21 +18,21 @@

    Welcome to the
    Lift provides the best features for building interactive web applications: -
      -
    • +
        +
      • Super simple and wicked powerful Ajax and Comet coding. This lets you build more interactive, user-friendly sites.
      • -
      • +
      • Amazingly concise code with the powerful type-safety of Scala. This means more time spent coding features and less time writing tests or chasing parameter mis-matches.
      • -
      • +
      • Runs on all standard JEE (Java) application servers including Jetty, Tomcat, WebLogic, etc. This means you get the performance, scalability and compatibility of the best web infrastructure around.
      • -
      • +
      • Built-in security means more time focusing on your application and less time being defensive about parameter tampering, SQL injection, Cross Site Scripting and other nasty attacks.
      • diff --git a/combo/example/src/main/webapp/interactive.html b/combo/example/src/main/webapp/interactive.html index 7765b68..e845fb4 100644 --- a/combo/example/src/main/webapp/interactive.html +++ b/combo/example/src/main/webapp/interactive.html @@ -7,16 +7,10 @@ -
        +

        %*%

        - + +
        diff --git a/combo/example/src/main/webapp/invoice_wiring.html b/combo/example/src/main/webapp/invoice_wiring.html index 7efb4c9..3761906 100644 --- a/combo/example/src/main/webapp/invoice_wiring.html +++ b/combo/example/src/main/webapp/invoice_wiring.html @@ -7,19 +7,10 @@ -
        +

        %*%

        - + +
        diff --git a/combo/example/src/main/webapp/json.html b/combo/example/src/main/webapp/json.html index ca6f0a1..0995dd1 100644 --- a/combo/example/src/main/webapp/json.html +++ b/combo/example/src/main/webapp/json.html @@ -7,19 +7,10 @@ -
        +

        %*%

        - + +
        @@ -27,6 +18,7 @@

        %*%

        Example 1

        +

        From the Lift cookbook

        Enter an addition question:

        @@ -42,6 +34,7 @@

        Example 1

        +

        %*%

        - + +
        diff --git a/combo/example/src/main/webapp/simple.html b/combo/example/src/main/webapp/simple.html index f2cdd5b..3762ec4 100644 --- a/combo/example/src/main/webapp/simple.html +++ b/combo/example/src/main/webapp/simple.html @@ -1,4 +1,4 @@ -
        +

        Lift's approach to dynamic content

        Lift has a "view first" rendering strategy. When a diff --git a/combo/example/src/main/webapp/simple/add.html b/combo/example/src/main/webapp/simple/add.html index 9b0f469..9d8e296 100644 --- a/combo/example/src/main/webapp/simple/add.html +++ b/combo/example/src/main/webapp/simple/add.html @@ -7,22 +7,10 @@ -

        +

        %*%

        - + +
        diff --git a/combo/example/src/main/webapp/simple/delete.html b/combo/example/src/main/webapp/simple/delete.html index ccb7fc9..fc009cd 100644 --- a/combo/example/src/main/webapp/simple/delete.html +++ b/combo/example/src/main/webapp/simple/delete.html @@ -7,22 +7,10 @@ -
        +

        %*%

        - + +
        diff --git a/combo/example/src/main/webapp/simple/edit.html b/combo/example/src/main/webapp/simple/edit.html index 6fec0b6..109a557 100644 --- a/combo/example/src/main/webapp/simple/edit.html +++ b/combo/example/src/main/webapp/simple/edit.html @@ -7,22 +7,10 @@ -
        +

        %*%

        - + +
        diff --git a/combo/example/src/main/webapp/simple/index.html b/combo/example/src/main/webapp/simple/index.html index ec7cad6..0d2fa8f 100644 --- a/combo/example/src/main/webapp/simple/index.html +++ b/combo/example/src/main/webapp/simple/index.html @@ -7,19 +7,10 @@ -
        +

        %*%

        - + +
        diff --git a/combo/example/src/main/webapp/simple_screen.html b/combo/example/src/main/webapp/simple_screen.html index 5738daa..83a9eee 100644 --- a/combo/example/src/main/webapp/simple_screen.html +++ b/combo/example/src/main/webapp/simple_screen.html @@ -7,19 +7,10 @@ -
        +

        %*%

        - + +
        diff --git a/combo/example/src/main/webapp/simple_wiring.html b/combo/example/src/main/webapp/simple_wiring.html index b88bbda..cfa8d29 100644 --- a/combo/example/src/main/webapp/simple_wiring.html +++ b/combo/example/src/main/webapp/simple_wiring.html @@ -7,19 +7,10 @@ -
        +

        %*%

        - + +
        diff --git a/combo/example/src/main/webapp/simple_wizard.html b/combo/example/src/main/webapp/simple_wizard.html index b967b1f..072f615 100644 --- a/combo/example/src/main/webapp/simple_wizard.html +++ b/combo/example/src/main/webapp/simple_wizard.html @@ -7,19 +7,10 @@ -
        +

        %*%

        - + +
        diff --git a/combo/example/src/main/webapp/stateless_json.html b/combo/example/src/main/webapp/stateless_json.html index 1aee090..6909ab2 100644 --- a/combo/example/src/main/webapp/stateless_json.html +++ b/combo/example/src/main/webapp/stateless_json.html @@ -8,20 +8,11 @@ -
        +

        %*%

        - + +
        diff --git a/combo/example/src/main/webapp/template.html b/combo/example/src/main/webapp/template.html index 7da56e9..7c51110 100644 --- a/combo/example/src/main/webapp/template.html +++ b/combo/example/src/main/webapp/template.html @@ -1,3 +1,3 @@ - +
        diff --git a/combo/example/src/main/webapp/templates-hidden/_resources.html b/combo/example/src/main/webapp/templates-hidden/_resources.html new file mode 100644 index 0000000..222f553 --- /dev/null +++ b/combo/example/src/main/webapp/templates-hidden/_resources.html @@ -0,0 +1,23 @@ + + + Home + Persistence + + Interactive Stuff + + Templating + Web Services + + Localization + + Menus + + Wiki HomePage + Wiki all + Wiki edit HomePage + + Misc code + + Source code for this site + Lift project home + diff --git a/combo/example/src/main/webapp/templates-hidden/_spinner_template.html b/combo/example/src/main/webapp/templates-hidden/_spinner_template.html new file mode 100644 index 0000000..247e93e --- /dev/null +++ b/combo/example/src/main/webapp/templates-hidden/_spinner_template.html @@ -0,0 +1 @@ +
        \ No newline at end of file diff --git a/combo/example/src/main/webapp/templates-hidden/default.html b/combo/example/src/main/webapp/templates-hidden/default.html index b62c7c2..9bcfd9c 100644 --- a/combo/example/src/main/webapp/templates-hidden/default.html +++ b/combo/example/src/main/webapp/templates-hidden/default.html @@ -1,162 +1,116 @@ - - - - - - - Lift Web Framework: %*% - - - - - - - - - - - - - - - + + + + + + + + + Lift Web Framework: %*% + + + + + + + + + + + + + + + + +
        -
        - Lift Web Framework - -
        -
        -
        -
        -

    The - Lift Scala code + Lift Scala code
    // the request-local variable that hold the file parameter
     private object theUpload extends RequestVar[Box[FileParamHolder]](Empty)
     
    @@ -64,7 +80,7 @@ 

    %*%

    if (S.get_?) ( "#get ^*" #> "#choose" & - ".file_upload" #> fileUpload(ul => theUpload(Full(ul))) + ".custom-file-input" #> fileUpload(ul => theUpload(Full(ul))) ) apply (xhtml) else ("#post ^*" #> "#choose" & diff --git a/combo/example/src/main/webapp/form_ajax.html b/combo/example/src/main/webapp/form_ajax.html index c0d0523..445ea2d 100644 --- a/combo/example/src/main/webapp/form_ajax.html +++ b/combo/example/src/main/webapp/form_ajax.html @@ -10,7 +10,7 @@

    %*%

    - +
    diff --git a/combo/example/src/main/webapp/guess.html b/combo/example/src/main/webapp/guess.html index 22e9271..e34c821 100644 --- a/combo/example/src/main/webapp/guess.html +++ b/combo/example/src/main/webapp/guess.html @@ -82,14 +82,14 @@

    %*%

    {countDown(number)} </p> - def countDown(number: Int): Node = if (number <= 0) Text("") + def countDown(number: Int): Node = if (number <= 0) Text("") else <xml:group>{number} <lift:count_game.count_down from={(number - 1).toString}/></xml:group> def nextGuess(xhtml: NodeSeq): NodeSeq = ("#guess ^*" #> "#choose" & ".input" #> text("", guess _, "class" -> "form-control") & ".last *" #> lastGuess.map(v => - if (v < number) v + " is low" + if (v < number) v + " is low" else v + " is high"). openOr("Make first Guess") ).apply(xhtml) @@ -104,124 +104,6 @@

    %*%

    private var count = 0 }
    -
    The view code: - - - - -
    -<lift:surround with="default" at="content">
    -<lift:snippet type="CountGame:run" form="post">
    -  <choose:guess>
    -    Guess a number between 1 and 100.<br/>
    -    Last guess: <count:last/><br />
    -    Guess: <count:input/><br/>
    -    <input type="submit" value="Guess"/>
    -  </choose:guess>
    -
    -  <choose:win>
    -    You Win!!<br />
    -    You guessed <count:number/> after <count:count/> guesses.<br/>
    -  </choose:win>
    -</lift:snippet>
    -</lift:surround>
    -  
    The Snippet: - - - - -
    -class CountGame extends StatefulSnippet {
    -  val dispatch: DispatchIt = {
    -    case "run" if lastGuess == number => 
    -      xhtml => win(chooseTemplate("choose", "win", xhtml))
    -    
    -    case "run" => 
    -      xhtml => nextGuess(chooseTemplate("choose", "guess", xhtml))
    -  }
    -  
    -  def win(xhtml: NodeSeq) = bind("count", xhtml, "number" --> number, 
    -                                 "count" --> count)
    -  
    -  def nextGuess(xhtml: NodeSeq) =  bind("count", xhtml, 
    -                                        "input" --> text("", guess _),
    -                                        "last" --> 
    -                                        lastGuess.map(v => 
    -                                          if (v < number) v+" is low" 
    -                                          else v+" is high").
    -                                        openOr("Make first Guess"))
    -  
    -  private def guess(in: String) {
    -    count += 1
    -    lastGuess = Full(toInt(in))
    -  }
    -    
    -  private val number = 1 + randomInt(100)
    -  private var lastGuess: Can[Int] = Empty
    -  private var count = 0
    -}
    -  
    - -
    diff --git a/combo/example/src/main/webapp/index.html b/combo/example/src/main/webapp/index.html index 6857c14..25d156f 100644 --- a/combo/example/src/main/webapp/index.html +++ b/combo/example/src/main/webapp/index.html @@ -19,38 +19,45 @@

    Welcome to the
    Lift provides the best features for building interactive web applications:
      -
    • +
    • Super simple and wicked powerful - Ajax and Comet coding. This lets you build more interactive, user-friendly sites. + Ajax and Comet coding. This lets you build more interactive, user-friendly + sites.
    • -
    • +
    • Amazingly concise code with the powerful type-safety of - Scala. This means more time spent coding features and less time writing tests or chasing parameter + Scala. This means more time spent coding + features and less time writing tests or chasing parameter mis-matches.
    • -
    • - Runs on all standard JEE (Java) application servers including Jetty, Tomcat, WebLogic, etc. This means you get the performance, +
    • + Runs on all standard JEE (Java) application servers including Jetty, Tomcat, WebLogic, etc. This + means you get the performance, scalability and compatibility of the best web infrastructure around.
    • -
    • - Built-in security means more time focusing on your application and less time being defensive about parameter tampering, SQL +
    • + Built-in security means more time focusing on your application and less time being defensive about + parameter tampering, SQL injection, Cross Site Scripting and other nasty attacks.
    -
    +

    Lift is built on - Scala, a hybrid Functional and OOP language that compiles code down to the Java Virtual Machine. Scala code + Scala, a hybrid Functional and OOP language that + compiles code down to the Java Virtual Machine. Scala code can call any Java code and make use of all Java classes. Java code can call some Scala code. - Lift applications are packaged as WAR files and can be deployed on any Servlet 2.4 -- 3.1 engine (e.g., Tomcat + Lift applications are packaged as WAR files and can be deployed on any Servlet 2.4 -- 3.1 engine + (e.g., Tomcat 5.5.xx -- 8.5.xx, Jetty 6.0 -- 8.x, etc.)

    - Lift code is as clean and brief as Rails, yet performs at least 6 times faster and is multithreaded. Additionally, + Lift code is as clean and brief as Rails, yet performs at least 6 times faster and is multithreaded. + Additionally, because Scala is strongly typed, the compiler catches type errors. For example: -
    +
     User.find(By(User.email, "foo@bar.com")) // legal
      User.find(By(User.birthday, new Date("Jan 4, 1975"))) // legal
    diff --git a/combo/example/src/main/webapp/interactive.html b/combo/example/src/main/webapp/interactive.html
    index e845fb4..26b13dc 100644
    --- a/combo/example/src/main/webapp/interactive.html
    +++ b/combo/example/src/main/webapp/interactive.html
    @@ -21,12 +21,14 @@ 

    %*%

    Ajax

    - Lift has powerful set of Ajax features that all you to create Ajax controls with as little as 1 line of code: + Lift has powerful set of Ajax features that all you to create Ajax controls with as little as 1 line of + code:
    ajaxButton("", s => {println("you said: "+s); SetHtml("place", <b>{s}</b>)})

    Comet

    - Lift supports Comet-style long polling with very little work on the part of the developer. Here's the code the implements + Lift supports Comet-style long polling with very little work on the part of the developer. Here's the code + the implements the clock that you see in this demo:
    class ExampleClock(initSession: LiftSession,
                        initType: Box[String],
    diff --git a/combo/example/src/main/webapp/json_more.html b/combo/example/src/main/webapp/json_more.html
    index f9bc179..8e3a4b0 100644
    --- a/combo/example/src/main/webapp/json_more.html
    +++ b/combo/example/src/main/webapp/json_more.html
    @@ -29,7 +29,8 @@ 

    %*%

    // ]]> This example calls the - noParam function which invokes a server-side JSON call that then calls a handler back in the browser. + noParam function which invokes a server-side JSON call that then calls a handler back in the + browser.
    The client-side JavaScript for looks like: @@ -150,6 +151,7 @@

    %*%

    /* font-lock-variable-name-face */ color: #b8860b; } + /* ]]> */
    diff --git a/combo/example/src/main/webapp/lazy.html b/combo/example/src/main/webapp/lazy.html
    index 5161b60..bb9fde0 100644
    --- a/combo/example/src/main/webapp/lazy.html
    +++ b/combo/example/src/main/webapp/lazy.html
    @@ -17,7 +17,8 @@ 

    %*%

    - Lift supports lazy loading of a snippet. This is useful when a snippet may take a long time to render, but you want to return + Lift supports lazy loading of a snippet. This is useful when a snippet may take a long time to render, but you + want to return the page to the browser quickly.

    diff --git a/combo/example/src/main/webapp/login/index.html b/combo/example/src/main/webapp/login/index.html index 013f20f..5356aff 100644 --- a/combo/example/src/main/webapp/login/index.html +++ b/combo/example/src/main/webapp/login/index.html @@ -7,17 +7,17 @@ -
    -

    %*%

    +
    +

    %*%

    - + -

    -You have reached this page, but you can only get here if you've logged in -first. -

    +

    + You have reached this page, but you can only get here if you've logged in + first. +

    -
    +
    \ No newline at end of file diff --git a/combo/example/src/main/webapp/login/validate.html b/combo/example/src/main/webapp/login/validate.html index c8bb8a6..d588371 100644 --- a/combo/example/src/main/webapp/login/validate.html +++ b/combo/example/src/main/webapp/login/validate.html @@ -7,38 +7,39 @@ -
    -

    %*%

    +
    +

    %*%

    - + -

    -This is the "validation" page. In order to view any content under /login/*, you -must be validated (logged in.) This is achieved with the following code in -Boot.scala: -

    +

    + This is the "validation" page. In order to view any content under /login/*, you + must be validated (logged in.) This is achieved with the following code in + Boot.scala: +

    -

    - + + ignore => Full(RedirectResponse("/login/validate")) } ]]> - -

    + +

    -

    -If the user is not validated/logged in, all pages under /login/* will be directed -to /login/validate. -

    +

    + If the user is not validated/logged in, all pages under /login/* will be directed + to /login/validate. +

    -

    -Press the button to validate the session and access -/login/index. -

    +

    + Press the button to validate the session and access + /login/index. +

    -
    +
    - + \ No newline at end of file diff --git a/combo/example/src/main/webapp/menu/four.html b/combo/example/src/main/webapp/menu/four.html index a367ef3..f7773d6 100644 --- a/combo/example/src/main/webapp/menu/four.html +++ b/combo/example/src/main/webapp/menu/four.html @@ -1,11 +1,20 @@ -
    -

    %*%

    + + - + + + Template + -

    Submenu Page 4

    + +
    +

    %*%

    -
    + + +

    Submenu Page 4

    + +
    - + \ No newline at end of file diff --git a/combo/example/src/main/webapp/menu/index.html b/combo/example/src/main/webapp/menu/index.html index a7e372a..b54ff01 100644 --- a/combo/example/src/main/webapp/menu/index.html +++ b/combo/example/src/main/webapp/menu/index.html @@ -7,16 +7,14 @@ +
    +

    %*%

    -
    -

    %*%

    + - +

    Main Menu page... note the submenus on the menu bar

    - -

    Main Menu page... note the submenus on the menu bar

    - -
    +
    - + \ No newline at end of file diff --git a/combo/example/src/main/webapp/menu/one.html b/combo/example/src/main/webapp/menu/one.html index 899a261..fd6061c 100644 --- a/combo/example/src/main/webapp/menu/one.html +++ b/combo/example/src/main/webapp/menu/one.html @@ -8,14 +8,14 @@ -
    -

    %*%

    +
    +

    %*%

    - + -

    Submenu Page 1

    +

    Submenu Page 1

    -
    +
    - + \ No newline at end of file diff --git a/combo/example/src/main/webapp/menu/three.html b/combo/example/src/main/webapp/menu/three.html index fcd8910..29d8df7 100644 --- a/combo/example/src/main/webapp/menu/three.html +++ b/combo/example/src/main/webapp/menu/three.html @@ -8,14 +8,14 @@ -
    -

    %*%

    +
    +

    %*%

    - + -

    Submenu Page 3

    +

    Submenu Page 3

    -
    +
    - + \ No newline at end of file diff --git a/combo/example/src/main/webapp/menu/two.html b/combo/example/src/main/webapp/menu/two.html index e926c55..a01e55b 100644 --- a/combo/example/src/main/webapp/menu/two.html +++ b/combo/example/src/main/webapp/menu/two.html @@ -8,14 +8,14 @@ -
    -

    %*%

    +
    +

    %*%

    - + -

    Submenu Page 2

    +

    Submenu Page 2

    -
    +
    - + \ No newline at end of file diff --git a/combo/example/src/main/webapp/menu/two_one.html b/combo/example/src/main/webapp/menu/two_one.html index 4815e3d..c0aa4e9 100644 --- a/combo/example/src/main/webapp/menu/two_one.html +++ b/combo/example/src/main/webapp/menu/two_one.html @@ -8,14 +8,14 @@ -
    -

    %*%

    +
    +

    %*%

    - + -

    Submenu Page 2-1

    +

    Submenu Page 2-1

    -
    +
    - + \ No newline at end of file diff --git a/combo/example/src/main/webapp/menu/two_two.html b/combo/example/src/main/webapp/menu/two_two.html index f8bbbb1..e737995 100644 --- a/combo/example/src/main/webapp/menu/two_two.html +++ b/combo/example/src/main/webapp/menu/two_two.html @@ -6,15 +6,15 @@ Template - -
    -

    %*%

    + +
    +

    %*%

    - + -

    Submenu Page 2-2

    +

    Submenu Page 2-2

    -
    +
    - + \ No newline at end of file diff --git a/combo/example/src/main/webapp/parallel.html b/combo/example/src/main/webapp/parallel.html index 740f5a4..e79230f 100644 --- a/combo/example/src/main/webapp/parallel.html +++ b/combo/example/src/main/webapp/parallel.html @@ -17,9 +17,12 @@

    %*%

    - Lift allows you to execute multiple snippets in parallel during the rendering of a page. This allows you to fork off multiple - long-running jobs (e.g., talking to an external ad server) and run them in parallel. All the jobs must complete before - the final page render is sent back to the browser. The lift:parallel="true" attribute designates a snippet as parallel. + Lift allows you to execute multiple snippets in parallel during the rendering of a page. This allows you to fork + off multiple + long-running jobs (e.g., talking to an external ad server) and run them in parallel. All the jobs must complete + before + the final page render is sent back to the browser. The lift:parallel="true" attribute designates a snippet as + parallel.

    diff --git a/combo/example/src/main/webapp/persistence.html b/combo/example/src/main/webapp/persistence.html index 35b0b0d..babb62c 100644 --- a/combo/example/src/main/webapp/persistence.html +++ b/combo/example/src/main/webapp/persistence.html @@ -17,7 +17,8 @@

    %*%

    -

    Lift comes with its own ActiveRecord-like OR mapper called mapper. It's great for small projects. Lift also supports +

    Lift comes with its own ActiveRecord-like OR mapper called mapper. It's great for small projects. Lift also + supports JPA.

    diff --git a/combo/example/src/main/webapp/redirect.html b/combo/example/src/main/webapp/redirect.html index 82728f5..08442cf 100644 --- a/combo/example/src/main/webapp/redirect.html +++ b/combo/example/src/main/webapp/redirect.html @@ -1,12 +1,15 @@ - - Lift Example - - -
    -
    - The Redirect example -
    + + + Lift Example + + + +
    +
    + The Redirect example
    - - +
    + + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/resources-hidden/_resources_lang.html b/combo/example/src/main/webapp/resources-hidden/_resources_lang.html index 1aa6f00..86a4791 100644 --- a/combo/example/src/main/webapp/resources-hidden/_resources_lang.html +++ b/combo/example/src/main/webapp/resources-hidden/_resources_lang.html @@ -7,4 +7,4 @@ Set Locale Změnit Change - + \ No newline at end of file diff --git a/combo/example/src/main/webapp/rhodeisland.html b/combo/example/src/main/webapp/rhodeisland.html index 48cf707..09e8bfc 100644 --- a/combo/example/src/main/webapp/rhodeisland.html +++ b/combo/example/src/main/webapp/rhodeisland.html @@ -20,8 +20,10 @@

    %*%

    This section has - two examples showing how you could work with modal dialogs that has there template data in a separate file. - The second example is a plain javascript dialog while the first one is taking advantage of bootstrap's javascript modals. + two examples showing how you could work with modal dialogs that has there template data in a + separate file. + The second example is a plain javascript dialog while the first one is taking advantage of bootstrap's javascript + modals.

    Bootstrap dialog example

    diff --git a/combo/example/src/main/webapp/simple.html b/combo/example/src/main/webapp/simple.html index 3762ec4..be6f241 100644 --- a/combo/example/src/main/webapp/simple.html +++ b/combo/example/src/main/webapp/simple.html @@ -1,91 +1,91 @@

    Lift's approach to dynamic content

    - Lift has a "view first" rendering strategy. When a + Lift has a "view first" rendering strategy. When a request comes in, an appropriate XHTML page is selected to - satisfy the request. If there is a file in the path + satisfy the request. If there is a file in the path that matches the name (e.g., '/foo/bar.html'), that file - is used. If there's no matching file, lift + is used. If there's no matching file, lift looks for a class named "Bar" in a number of packages. If the class isn't found, Lift looks for foo.Bar - in a number of packages. Once the rendering class is located, - an instance is created and the "render" method is called. This allows + in a number of packages. Once the rendering class is located, + an instance is created and the "render" method is called. This allows HTML designers or programmers to maintain the files.

    Lift processes a number of special tags: -

      -
    • <lift:surround> -- surround the XHTML with -a template. This allows a site-wide template to be used, but -individual pages to have different contents. Multiple surround -tags can be used within a single document.
    • -
    • <lift:embed> -- embed a template in the XHTML.
    • -
    • <lift:comet> -- Insert the rendered output of -a long-lived comet widget. Also, the comet widget can notify a page that -content has changed and the next time the page services an AJAX request, -it will update the comet widget's content in the browser. -Comet widgets are handed "recommended" XHTML that they can "bind" -to. This is Wicket's and TurboGears' approach. -A comet widget may also choose to ignore the recommendation -and render its own XHTML.
    • -
    • <lift:snippet> -- create a -short-lived "snippet" (kind of like a Rails controller) and insert -the output of the snippet into the page.
    • +
        +
      • <lift:surround> -- surround the XHTML with + a template. This allows a site-wide template to be used, but + individual pages to have different contents. Multiple surround + tags can be used within a single document.
      • +
      • <lift:embed> -- embed a template in the XHTML.
      • +
      • <lift:comet> -- Insert the rendered output of + a long-lived comet widget. Also, the comet widget can notify a page that + content has changed and the next time the page services an AJAX request, + it will update the comet widget's content in the browser. + Comet widgets are handed "recommended" XHTML that they can "bind" + to. This is Wicket's and TurboGears' approach. + A comet widget may also choose to ignore the recommendation + and render its own XHTML.
      • +
      • <lift:snippet> -- create a + short-lived "snippet" (kind of like a Rails controller) and insert + the output of the snippet into the page.
      • -

      +
    +

    -

    Once a page has composed its XHTML by getting renderings from each of -widgets, links on the page are adjusted to the context page -of the request. Additionally, if AJAX is enabled, forms marked -as AJAX ready are re-written to use AJAX.

    +

    Once a page has composed its XHTML by getting renderings from each of + widgets, links on the page are adjusted to the context page + of the request. Additionally, if AJAX is enabled, forms marked + as AJAX ready are re-written to use AJAX.

    -

    The code to embed the AJAX clock on the page is:

    - -
    +    .string {
    +      /* font-lock-string-face */
    +      color: #32cd32;
    +    }
    +  
    +  
     <lift:comet type="Clock">Current Time: 
     <clk:time>Missing Clock</clk:time></lift:comet>
     
    -

    The Clock comet widget is:

    - -
    +  
    +  
       def render = bind("time" -> Text(timeNow.toString))
     
    -

    Which binds a string containing the current time to the "time" part -of the XHTML and returns this as XHTML.

    +

    Which binds a string containing the current time to the "time" part + of the XHTML and returns this as XHTML.

    -
    +
    \ No newline at end of file diff --git a/combo/example/src/main/webapp/stateless_json.html b/combo/example/src/main/webapp/stateless_json.html index 6909ab2..3eba499 100644 --- a/combo/example/src/main/webapp/stateless_json.html +++ b/combo/example/src/main/webapp/stateless_json.html @@ -52,10 +52,10 @@

    %*%

    def handleJson(req: Req): Box[LiftResponse] = for { - json <- req.json // get the JSON + json <- req.json // get the JSON JObject( List(JField("command", JString(cmd)), - JField("params", JString(params)))) <- json // extract the command + JField("params", JString(params)))) <- json // extract the command } yield JavaScriptResponse( SetHtml( diff --git a/combo/example/src/main/webapp/templates-hidden/stateless-default.html b/combo/example/src/main/webapp/templates-hidden/stateless-default.html index cc03428..bc31d15 100644 --- a/combo/example/src/main/webapp/templates-hidden/stateless-default.html +++ b/combo/example/src/main/webapp/templates-hidden/stateless-default.html @@ -1,51 +1,52 @@ - - - - - Lift Web Framework + + + + - - - - - + Lift Web Framework - - - + + + + + - + + + + + +
    @@ -102,9 +104,10 @@
    -
    +
    -
    Lift is Copyright 2007-2010 WorldWide Conferencing, LLC. Distributed under an Apache 2.0 License. +
    Lift is Copyright 2007-2010 WorldWide Conferencing, LLC. + Distributed under an Apache 2.0 License.
    Lift version - built on . + built on .
    Stats: Total Memory: Free Memory: @@ -131,4 +135,5 @@
    Lift is Copyright 2007-2 - + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/templates-hidden/wizard-all.old.html b/combo/example/src/main/webapp/templates-hidden/wizard-all.old.html deleted file mode 100644 index c9bec39..0000000 --- a/combo/example/src/main/webapp/templates-hidden/wizard-all.old.html +++ /dev/null @@ -1,57 +0,0 @@ -
    -
    Page of
    -
    -
    -
    -
    - -
    - + diff --git a/combo/example/src/main/webapp/templating/index.html b/combo/example/src/main/webapp/templating/index.html index 2775ab9..5a8dfcf 100644 --- a/combo/example/src/main/webapp/templating/index.html +++ b/combo/example/src/main/webapp/templating/index.html @@ -24,7 +24,7 @@

    Templating

    In the beginning there was XHTML

    The text on this page is written in the era of xhtml so it's associated examples uses xhtml namespace syntax, but don't worry alongside with the older examples there are - equivalent modern examples written in pure HTML using + equivalent modern examples written in HTML5 using data-lift and CSS-selectors for data bindings.

    From 66e39a332e0aa9d11b0afb92db2d1bd68eebe6fb Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Sat, 3 Feb 2018 19:06:12 +0100 Subject: [PATCH 11/33] WIP Misc code --- .../net/liftweb/example/model/Person.scala | 43 ++++++++++--------- .../net/liftweb/example/snippet/Wizard.scala | 19 +++++--- .../src/main/webapp/assets/css/app.css | 34 +++++++++++---- .../src/main/webapp/simple_screen.html | 33 ++++++++++++-- .../src/main/webapp/variable_screen.html | 42 +++++++++++++----- 5 files changed, 122 insertions(+), 49 deletions(-) diff --git a/combo/example/src/main/scala/net/liftweb/example/model/Person.scala b/combo/example/src/main/scala/net/liftweb/example/model/Person.scala index 4231d22..f68ab27 100644 --- a/combo/example/src/main/scala/net/liftweb/example/model/Person.scala +++ b/combo/example/src/main/scala/net/liftweb/example/model/Person.scala @@ -14,37 +14,38 @@ * limitations under the License. */ -package net.liftweb { -package example { -package model { +package net.liftweb.example.model import _root_.net.liftweb._ import mapper._ -import common._ import util._ object Person extends Person with LongKeyedMetaMapper[Person] class Person extends LongKeyedMapper[Person] with IdPK { - def getSingleton = Person - - object firstName extends MappedString(this, 100) { - override def validations = valMinLen(2, "First name must be 2 characters long") _ :: super.validations - } - object lastName extends MappedString(this, 100) - - object personalityType extends MappedEnum(this, Personality) + val common_bs_classes = Seq("class" -> "form-control") + def getSingleton = Person + + object firstName extends MappedString(this, 100) { + override def formElemAttrs = common_bs_classes + override def validations = + valMinLen(2, "First name must be 2 characters long") _ :: super.validations + } + object lastName extends MappedString(this, 100) { + override def formElemAttrs = common_bs_classes + } + + object personalityType extends MappedEnum(this, Personality) { + override def formElemAttrs = common_bs_classes + } } object Personality extends Enumeration { - val TypeA = Value(1, "Type A") - val TypeB = Value(2, "Type B") - val ENTJ = Value(3, "ENTJ") - val INTJ = Value(4, "INTJ") + val TypeA = Value(1, "Type A") + val TypeB = Value(2, "Type B") + val ENTJ = Value(3, "ENTJ") + val INTJ = Value(4, "INTJ") - val allTypes = Array(TypeA, TypeB, ENTJ, INTJ) - def rand = allTypes(Helpers.randomInt(allTypes.length)) -} -} -} + val allTypes = Array(TypeA, TypeB, ENTJ, INTJ) + def rand = allTypes(Helpers.randomInt(allTypes.length)) } diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scala index 6f5ea34..ff47588 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scala @@ -118,7 +118,7 @@ class PersonScreen extends LiftScreen { val shouldSave = field("Save ?", false) - val likeCats = builder("Do you like cats?", "") ^/ + val likeCats = builder("Do you like cats?", "", "class" -> "form-control") ^/ (s => if (Helpers.toBoolean(s)) Nil else "You have to like cats") make def finish() { @@ -139,7 +139,8 @@ object VariableScreenInfo { def name: BaseField = new BaseField with FEP { private var _name = "" - def toForm: Box[NodeSeq] = Full(SHtml.text(_name, _name = _)) + def toForm: Box[NodeSeq] = + Full(SHtml.text(_name, _name = _, "class" -> "form-control")) def setFilter = Nil @@ -161,7 +162,8 @@ object VariableScreenInfo { def address: BaseField = new BaseField with FEP { private var address = "" - def toForm: Box[NodeSeq] = Full(SHtml.text(address, address = _)) + def toForm: Box[NodeSeq] = + Full(SHtml.text(address, address = _, "class" -> "form-control")) def setFilter = Nil @@ -184,7 +186,10 @@ object VariableScreenInfo { def age: BaseField = new BaseField with FEP { private var age = 0 def toForm: Box[NodeSeq] = - Full(SHtml.text(age.toString, s => Helpers.asInt(s).map(age = _))) + Full( + SHtml.text(age.toString, + s => Helpers.asInt(s).map(age = _), + "class" -> "form-control")) def set(v: Int) = { age = v; v } def get = age @@ -208,7 +213,11 @@ object VariableScreenInfo { private val opts = List("A", "B", "C", "Last") private var sel = Full("C") def toForm: Box[NodeSeq] = - Full(SHtml.select(opts.map(a => a -> a), sel, x => sel = Full(x))) + Full( + SHtml.select(opts.map(a => a -> a), + sel, + x => sel = Full(x), + "class" -> "form-control")) def set(v: String) = { sel = Full(v); v } def name = "Selection Thing" def get = sel.openOrThrowException("Value should not be empty.") diff --git a/combo/example/src/main/webapp/assets/css/app.css b/combo/example/src/main/webapp/assets/css/app.css index 818c3c3..77351b3 100644 --- a/combo/example/src/main/webapp/assets/css/app.css +++ b/combo/example/src/main/webapp/assets/css/app.css @@ -70,6 +70,18 @@ font-weight: normal; } +div.globalErrors ul { + list-style-type: none; + margin: 0; + padding: 0; +} + +ul.errors { + list-style-type: none; + margin: 0; + padding: 0; +} + .lift_error { color: red; } @@ -87,6 +99,8 @@ .dpp_stuff p { padding-left: 15px; } + + /*Lift stuff*/ @@ -136,24 +150,26 @@ border-bottom: 1px solid #ccc; } - .bd-callout { - padding: 1.25rem; - margin-top: 1.25rem; - margin-bottom: 1.25rem; - border: 1px solid #eee; - border-left-width: .25rem; - border-radius: .25rem; + padding: 1.25rem; + margin-top: 1.25rem; + margin-bottom: 1.25rem; + border: 1px solid #eee; + border-left-width: .25rem; + border-radius: .25rem; } .bd-callout-info { - border-left-color: #5bc0de; + border-left-color: #5bc0de; } + .bd-callout-warning { - border-left-color: #f0ad4e; + border-left-color: #f0ad4e; } + /* bootstrap overrides */ + .breadcrumb { font-size: 0.8rem; padding: 0.2rem 0.8rem; diff --git a/combo/example/src/main/webapp/simple_screen.html b/combo/example/src/main/webapp/simple_screen.html index fc65875..4853e1d 100644 --- a/combo/example/src/main/webapp/simple_screen.html +++ b/combo/example/src/main/webapp/simple_screen.html @@ -1,3 +1,30 @@ - -
    -
    + + + + + + Template + + + +
    +

    Simple Screen

    + + + +
    + +
    + + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/variable_screen.html b/combo/example/src/main/webapp/variable_screen.html index 07e2133..09da677 100644 --- a/combo/example/src/main/webapp/variable_screen.html +++ b/combo/example/src/main/webapp/variable_screen.html @@ -1,12 +1,32 @@ - - - Lift Example - - -
    -
    - The Variable Screen Example -
    + + + + + + Lift Example + + + +
    +

    Variable Screen

    + + + +
    + The Variable Screen Example
    - - + +
    + + + \ No newline at end of file From 492e363ee6809fbc5f6081dbc2a0b2ae33a793f0 Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Sun, 4 Feb 2018 10:03:35 +0100 Subject: [PATCH 12/33] WIP Misc code --- .../src/main/webapp/simple_screen.html | 98 ++++++++++--- .../src/main/webapp/variable_screen.html | 134 ++++++++++++++++++ 2 files changed, 212 insertions(+), 20 deletions(-) diff --git a/combo/example/src/main/webapp/simple_screen.html b/combo/example/src/main/webapp/simple_screen.html index 4853e1d..931a1f7 100644 --- a/combo/example/src/main/webapp/simple_screen.html +++ b/combo/example/src/main/webapp/simple_screen.html @@ -2,29 +2,87 @@ - - Template + + Template -
    -

    Simple Screen

    - - - -
    - -
    +
    +

    Simple Screen

    + + + +
    + +
    + + The + Lift Scala code +
    class PersonScreen extends LiftScreen {
    +  object person extends ScreenVar(Person.create)
    +
    +  override def formName = "PersonScreen"
    +
    +  override def screenTop =
    +    <b>A single screen with some input validation</b>
    +
    +  addFields(() => person.is)
    +
    +  val shouldSave = field("Save ?", false)
    +
    +  val likeCats = builder("Do you like cats?", "", "class" -> "form-control") ^/
    +    (s => if (Helpers.toBoolean(s)) Nil else "You have to like cats") make
    +
    +  def finish() {
    +    S.notice("Thank you for adding " + person.is)
    +    if (shouldSave.is) {
    +      person.is.save
    +      S.notice(person.is.toString + " Saved in the database")
    +    }
    +  }
    +}
    +
    +object Person extends Person with LongKeyedMetaMapper[Person]
    +
    +class Person extends LongKeyedMapper[Person] with IdPK {
    +  val common_bs_classes = Seq("class" -> "form-control")
    +  def getSingleton = Person
    +
    +  object firstName extends MappedString(this, 100) {
    +    override def formElemAttrs = common_bs_classes
    +    override def validations =
    +      valMinLen(2, "First name must be 2 characters long") _ :: super.validations
    +  }
    +  object lastName extends MappedString(this, 100) {
    +    override def formElemAttrs = common_bs_classes
    +  }
    +
    +  object personalityType extends MappedEnum(this, Personality) {
    +    override def formElemAttrs = common_bs_classes
    +  }
    +}
    +
    +object Personality extends Enumeration {
    +  val TypeA = Value(1, "Type A")
    +  val TypeB = Value(2, "Type B")
    +  val ENTJ = Value(3, "ENTJ")
    +  val INTJ = Value(4, "INTJ")
    +
    +  val allTypes = Array(TypeA, TypeB, ENTJ, INTJ)
    +  def rand = allTypes(Helpers.randomInt(allTypes.length))
    +}
    + +
    \ No newline at end of file diff --git a/combo/example/src/main/webapp/variable_screen.html b/combo/example/src/main/webapp/variable_screen.html index 09da677..d47ec21 100644 --- a/combo/example/src/main/webapp/variable_screen.html +++ b/combo/example/src/main/webapp/variable_screen.html @@ -26,6 +26,140 @@

    Variable Screen

    The Variable Screen Example
    +
    + + The + Lift Scala code +
    class VariableScreen extends LiftScreen {
    +  object fields extends ScreenVar(VariableScreenInfo.chooseFields)
    +
    +  override def formName = "VariableScreen"
    +
    +  override def screenTop =
    +    A single screen with variable fields
    +
    +  addFields(() => fields.is)
    +
    +  def finish() {
    +    S.notice("You've completed the screen")
    +  }
    +}
    +
    +object VariableScreenInfo {
    +  private trait FEP {
    +    self: FieldIdentifier =>
    +    implicit def strToListFieldError(msg: String): List[FieldError] =
    +      List(FieldError(self, Text(msg)))
    +  }
    +
    +  def name: BaseField = new BaseField with FEP {
    +    private var _name = ""
    +    def toForm: Box[NodeSeq] =
    +      Full(SHtml.text(_name, _name = _, "class" -> "form-control"))
    +
    +    def setFilter = Nil
    +
    +    def validations = Nil
    +
    +    def set(v: String) = { _name = v; v }
    +    def get = _name
    +    def is = get
    +    type ValueType = String
    +
    +    def name = "Name"
    +
    +    override val uniqueFieldId: Box[String] = Full(Helpers.nextFuncName)
    +
    +    def validate =
    +      if (_name.length >= 4) Nil
    +      else "Name must be 4 characters"
    +  }
    +
    +  def address: BaseField = new BaseField with FEP {
    +    private var address = ""
    +    def toForm: Box[NodeSeq] =
    +      Full(SHtml.text(address, address = _, "class" -> "form-control"))
    +
    +    def setFilter = Nil
    +
    +    def validations = Nil
    +
    +    def set(v: String) = { address = v; v }
    +    def get = address
    +    def is = get
    +    type ValueType = String
    +
    +    def name = "Address"
    +
    +    override val uniqueFieldId: Box[String] = Full(Helpers.nextFuncName)
    +
    +    def validate =
    +      if (address.length >= 3) Nil
    +      else "Address must be 3 characters"
    +  }
    +
    +  def age: BaseField = new BaseField with FEP {
    +    private var age = 0
    +    def toForm: Box[NodeSeq] =
    +      Full(
    +        SHtml.text(age.toString,
    +                   s => Helpers.asInt(s).map(age = _),
    +                   "class" -> "form-control"))
    +
    +    def set(v: Int) = { age = v; v }
    +    def get = age
    +    def is = get
    +    type ValueType = Int
    +
    +    def setFilter = Nil
    +
    +    def validations = Nil
    +    def name = "Age"
    +
    +    override val uniqueFieldId: Box[String] = Full(Helpers.nextFuncName)
    +
    +    def validate =
    +      if (age > 10) Nil
    +      else "Age must be greater than 10"
    +
    +  }
    +
    +  def selection: BaseField = new BaseField {
    +    private val opts = List("A", "B", "C", "Last")
    +    private var sel = Full("C")
    +    def toForm: Box[NodeSeq] =
    +      Full(
    +        SHtml.select(opts.map(a => a -> a),
    +                     sel,
    +                     x => sel = Full(x),
    +                     "class" -> "form-control"))
    +    def set(v: String) = { sel = Full(v); v }
    +    def name = "Selection Thing"
    +    def get = sel.openOrThrowException("Value should not be empty.")
    +    def is = get
    +    type ValueType = String
    +
    +    def setFilter = Nil
    +
    +    def validations = Nil
    +
    +    override val uniqueFieldId: Box[String] = Full(Helpers.nextFuncName)
    +
    +    def validate = Nil
    +  }
    +
    +  def chooseFields: FieldContainer =
    +    List(name, address, age, selection) filter { ignore =>
    +      Helpers.randomInt(100) > 50
    +    } match {
    +      case Nil => chooseFields
    +      case xs =>
    +        new FieldContainer {
    +          def allFields = xs
    +        }
    +    }
    +}
    + From 20a2effbccf3160746b85d7e1d2fe4b00f096fa4 Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Sat, 10 Feb 2018 18:33:25 +0100 Subject: [PATCH 13/33] WIP Misc code --- .../net/liftweb/example/comet/AskName.scala | 20 +- .../net/liftweb/example/lib/AsyncRest.scala | 2 - .../example/snippet/ArcChallenge.scala | 79 +++---- .../example/snippet/InvoiceWiring.scala | 68 +++--- .../net/liftweb/example/snippet/Misc.scala | 19 +- .../example/snippet/SimpleWiring.scala | 62 +++--- .../net/liftweb/example/snippet/Wizard.scala | 2 +- .../example/src/main/webapp/_chat_fixed.html | 8 +- combo/example/src/main/webapp/arc.html | 152 +++++++------- combo/example/src/main/webapp/async_rest.html | 119 +++++------ .../example/src/main/webapp/file_upload.html | 82 +++++--- .../src/main/webapp/invoice_wiring.html | 193 +++++++++++------- .../src/main/webapp/simple_wiring.html | 107 +++++++++- .../webapp/templates-hidden/wizard-all.html | 4 +- 14 files changed, 541 insertions(+), 376 deletions(-) diff --git a/combo/example/src/main/scala/net/liftweb/example/comet/AskName.scala b/combo/example/src/main/scala/net/liftweb/example/comet/AskName.scala index 6e44c4e..1681050 100644 --- a/combo/example/src/main/scala/net/liftweb/example/comet/AskName.scala +++ b/combo/example/src/main/scala/net/liftweb/example/comet/AskName.scala @@ -14,30 +14,20 @@ * limitations under the License. */ -package net.liftweb { -package example { -package comet { +package net.liftweb.example.comet -import _root_.net.liftweb.http._ -import S._ +import net.liftweb.http._ import SHtml._ -import _root_.net.liftweb.common._ -import _root_.net.liftweb.util._ -import _root_.scala.xml._ class AskName extends CometActor { def render = ajaxForm(
    What is your username?
    ++ -
    -
    +
    +
    {text("",name => answer(name.trim), "class" -> "form-control form-control-sm")}
    -
    +
    ) } - -} -} -} diff --git a/combo/example/src/main/scala/net/liftweb/example/lib/AsyncRest.scala b/combo/example/src/main/scala/net/liftweb/example/lib/AsyncRest.scala index 8795fb9..3a9e436 100644 --- a/combo/example/src/main/scala/net/liftweb/example/lib/AsyncRest.scala +++ b/combo/example/src/main/scala/net/liftweb/example/lib/AsyncRest.scala @@ -18,9 +18,7 @@ package net.liftweb package example package lib -import net.liftweb._ import http._ -import common._ import rest._ /** diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/ArcChallenge.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/ArcChallenge.scala index 2b1f596..333dbfe 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/ArcChallenge.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/ArcChallenge.scala @@ -14,58 +14,63 @@ * limitations under the License. */ -package net.liftweb { -package example { -package snippet { +package net.liftweb.example.snippet -import _root_.net.liftweb.example.model._ import _root_.net.liftweb.http._ -import _root_.net.liftweb.http.S -import _root_.net.liftweb.mapper._ -import _root_.net.liftweb.http.S._ import _root_.net.liftweb.http.SHtml._ -import _root_.net.liftweb.util.Helpers._ -import _root_.net.liftweb.common._ -import _root_.net.liftweb.util._ -import _root_.scala.xml.{NodeSeq, Text, Group} /** - * The Arc Challenge is Paul Graham's quest for web framework concision. - * - * http://www.paulgraham.com/arcchallenge.html - * - * This is one potential lift-based solution to it using StatefulSnippets. - * There are doubtless many other ways. - * - * @author: Steve Jenson - */ + * The Arc Challenge is Paul Graham's quest for web framework concision. + * + * http://www.paulgraham.com/arcchallenge.html + * + * This is one potential lift-based solution to it using StatefulSnippets. + * There are doubtless many other ways. + * + * @author: Steve Jenson + */ class ArcChallenge extends StatefulSnippet { - var dispatch: DispatchIt = {case _ => xhtml => ask} + var dispatch: DispatchIt = { + case _ => + xhtml => + ask + } /** - * Step 1: Type in a Phrase. - */ + * Step 1: Type in a Phrase. + */ def ask = { -

    - Say Anything: - {text("", p => phrase = p)} - {submit("Submit", () => dispatch = {case _ => xhtml => think})} -

    +
    +
    + + {text("", + p => phrase = p, + "id" -> "answer", + "class" -> "form-control mx-sm-3")} +
    + {submit("Submit", + () => dispatch = {case _ => xhtml => think}, + "class" -> "btn btn-primary")} +
    } /** - * Step 2: Show a link that takes you to the Phrase you entered. - */ - def think = submit("Click here to see what you said", - () => dispatch = {case _ => xhtml => answer}) + * Step 2: Show a link that takes you to the Phrase you entered. + */ + def think = + submit("Click here to see what you said", + () => + dispatch = { + case _ => + xhtml => + answer + }, + "class" -> "btn btn-primary") /** - * Step 3: Show the phrase. - */ + * Step 3: Show the phrase. + */ def answer =

    You said: {phrase}

    private var phrase = "" } -} -} -} diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/InvoiceWiring.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/InvoiceWiring.scala index 9d1bccb..f6a4ce0 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/InvoiceWiring.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/InvoiceWiring.scala @@ -14,9 +14,7 @@ * limitations under the License. */ -package net.liftweb { -package example { -package snippet { +package net.liftweb.example.snippet import _root_.net.liftweb._ import http._ @@ -29,19 +27,18 @@ import _root_.scala.xml.{NodeSeq, Text} case class Line(guid: String, name: String, price: Double, taxable: Boolean) /** - * An invoice system with subtotals, tax, etc. - */ + * An invoice system with subtotals, tax, etc. + */ class InvoiceWiring { private object Info { val invoices = ValueCell(List(newLine)) val taxRate = ValueCell(0.05d) val subtotal = invoices.lift(_.foldLeft(0d)(_ + _.price)) - val taxable = invoices.lift(_.filter(_.taxable). - foldLeft(0D)(_ + _.price)) + val taxable = invoices.lift(_.filter(_.taxable).foldLeft(0D)(_ + _.price)) - val tax = taxRate.lift(taxable) {_ * _} + val tax = taxRate.lift(taxable) { _ * _ } - val total = subtotal.lift(tax) {_ + _} + val total = subtotal.lift(tax) { _ + _ } } def subtotal(in: NodeSeq) = WiringUI.asText(in, Info.subtotal) @@ -52,13 +49,13 @@ class InvoiceWiring { def total(in: NodeSeq) = WiringUI.asText(in, Info.total, JqWiringSupport.fade) - def taxRate = SHtml.ajaxText(Info.taxRate.get.toString, - s => { - Helpers.asDouble(s).foreach { - Info.taxRate.set - } - Noop - }) + def taxRate = + SHtml.ajaxText(Info.taxRate.get.toString, s => { + Helpers.asDouble(s).foreach { + Info.taxRate.set + } + Noop + }, "class" -> "form-control") def showLines = "* *" #> (Info.invoices.get.flatMap(renderLine): NodeSeq) @@ -68,18 +65,18 @@ class InvoiceWiring { val theLine = appendLine val guid = theLine.guid JqJsCmds.AppendHtml(div, renderLine(theLine)) - }) + }, "class" -> "btn btn-primary mx-sm-2 mb-2") } private def renderLine(theLine: Line): NodeSeq = -
    { +
    { SHtml.ajaxText(theLine.name, s => { mutateLine(theLine.guid) { l => Line(l.guid, s, l.price, l.taxable) } Noop - }) + },"class" -> "form-control mx-sm-2 mb-2") //mx-sm-2 mb-2 } { @@ -92,7 +89,7 @@ class InvoiceWiring { } } Noop - }) + },"class" -> "form-control mx-sm-2 mb-2") //mx-sm-2 mb-2 } { @@ -102,28 +99,23 @@ class InvoiceWiring { l => Line(l.guid, l.name, l.price, b) } Noop - }) + }, "class" -> "mx-sm-2 mb-2") //mx-sm-2 mb-2 }
    - private def newLine = Line(nextFuncName, "", 0, false) - - private def appendLine: Line = { - val ret = newLine - Info.invoices.set(ret :: Info.invoices.get) - ret - } - - private def mutateLine(guid: String)(f: Line => Line) { - val all = Info.invoices.get - val head = all.filter(_.guid == guid).map(f) - val rest = all.filter(_.guid != guid) - Info.invoices.set(head ::: rest) - } + private def newLine = Line(nextFuncName, "", 0, false) + private def appendLine: Line = { + val ret = newLine + Info.invoices.set(ret :: Info.invoices.get) + ret + } -} + private def mutateLine(guid: String)(f: Line => Line) { + val all = Info.invoices.get + val head = all.filter(_.guid == guid).map(f) + val rest = all.filter(_.guid != guid) + Info.invoices.set(head ::: rest) + } } -} -} diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Misc.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Misc.scala index 6c9d0fd..9aac14e 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/Misc.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Misc.scala @@ -14,24 +14,21 @@ * limitations under the License. */ -package net.liftweb -package example -package snippet +package net.liftweb.example.snippet -import model._ +import net.liftweb.example.model._ -import _root_.net.liftweb._ -import http._ -import mapper._ +import net.liftweb.http._ +import net.liftweb.mapper._ import S._ import SHtml._ -import common._ -import util._ +import net.liftweb.common._ +import net.liftweb.util._ import Helpers._ -import _root_.scala.xml.{NodeSeq, Text, Group} -import _root_.java.util.Locale +import scala.xml.{NodeSeq, Text, Group} +import java.util.Locale class Misc { private object selectedUser extends RequestVar[Box[User]](Empty) diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWiring.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWiring.scala index 22ff47c..55f0cbc 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWiring.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWiring.scala @@ -14,22 +14,19 @@ * limitations under the License. */ -package net.liftweb { -package example { -package snippet { +package net.liftweb.example.snippet -import _root_.net.liftweb._ +import net.liftweb._ import http._ import util._ import Helpers._ -import js.JsCmds._ import js.jquery._ -import _root_.scala.xml.{NodeSeq, Text} +import scala.xml.NodeSeq /** - * A simple example of Wiring. The count of done - * To-do items - */ + * A simple example of Wiring. + * The count of done To-do items + */ class SimpleWiring { // define the cells private val feedFish = ValueCell(false) @@ -38,30 +35,47 @@ class SimpleWiring { private val watchTv = ValueCell(false) // Our count is the collection of cells and we sum them up - private val count = SeqCell(feedFish, - walkDog, - doDishes, - watchTv).lift { - _.map(_.toInt).reduceLeft(_ + _)} + private val count = SeqCell(feedFish, walkDog, doDishes, watchTv).lift { + _.map(_.toInt).sum + } - private class BtoI(b: Boolean) {def toInt: Int = if (b) 1 else 0} + private class BtoI(b: Boolean) { def toInt: Int = if (b) 1 else 0 } private implicit def bToI(b: Boolean): BtoI = new BtoI(b) // define the count transformation - def count(in: NodeSeq): NodeSeq = + def count(in: NodeSeq): NodeSeq = WiringUI.asText(in, count, JqWiringSupport.fade) + //def toDo = { + // import SHtml._ + // "* *" #> List[NodeSeq]( + // Feed Fish {ajaxCheckboxElem(feedFish)}, + // Walk Dog {ajaxCheckboxElem(walkDog)}, + // Do Dishes {ajaxCheckboxElem(doDishes)}, + // Watch TV {ajaxCheckboxElem(watchTv)} + // ) + //} + def toDo = { import SHtml._ "* *" #> List[NodeSeq]( - Feed Fish {ajaxCheckboxElem(feedFish)}, - Walk Dog {ajaxCheckboxElem(walkDog)}, - Do Dishes {ajaxCheckboxElem(doDishes)}, - Watch TV {ajaxCheckboxElem(watchTv)}) +
    + {ajaxCheckboxElem(feedFish)} + +
    , +
    + {ajaxCheckboxElem(walkDog)} + +
    , +
    + {ajaxCheckboxElem(doDishes)} + +
    , +
    + {ajaxCheckboxElem(watchTv)} + +
    + ) } - -} } -} -} diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scala index ff47588..9cf8f23 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Wizard.scala @@ -116,7 +116,7 @@ class PersonScreen extends LiftScreen { addFields(() => person.is) - val shouldSave = field("Save ?", false) + val shouldSave = field("Save ?", false, "style" -> "vertical-align:bottom") val likeCats = builder("Do you like cats?", "", "class" -> "form-control") ^/ (s => if (Helpers.toBoolean(s)) Nil else "You have to like cats") make diff --git a/combo/example/src/main/webapp/_chat_fixed.html b/combo/example/src/main/webapp/_chat_fixed.html index 6498573..4ffd4fa 100644 --- a/combo/example/src/main/webapp/_chat_fixed.html +++ b/combo/example/src/main/webapp/_chat_fixed.html @@ -1,10 +1,10 @@
    -
    -
    +
    +
    -
    +
    - + \ No newline at end of file diff --git a/combo/example/src/main/webapp/arc.html b/combo/example/src/main/webapp/arc.html index 8526c22..0190ff2 100644 --- a/combo/example/src/main/webapp/arc.html +++ b/combo/example/src/main/webapp/arc.html @@ -1,82 +1,90 @@ - -
    + + -

    The Code

    - - - + +
    +

    Arc Challenge #1

    + -
    -/**
    - * The Arc Challenge is Paul Graham's quest for web framework concision.
    - *
    - * http://www.paulgraham.com/arcchallenge.html
    - *
    - * This is one potential lift-based solution to it using StatefulSnippets.
    - * There are doubtless many other ways.
    - *
    - * @author: Steve Jenson
    - */
    -class ArcChallenge extends StatefulSnippet {
    -  var dispatch: DispatchIt = {case _ => xhtml => ask}
     
    -  /**
    -   * Step 1: Type in a Phrase.
    -   */
    -  def ask = {
    -    <p>
    -    Say Anything:
    -    {text("", p => phrase = p)}
    -    {submit("Submit", () => dispatch = {case _ => xhtml => think})}
    -    </p>
    +    
    + +
    + + The + Lift Scala code +
    /**
    +  * The Arc Challenge is Paul Graham's quest for web framework concision.
    +  *
    +  * http://www.paulgraham.com/arcchallenge.html
    +  *
    +  * This is one potential lift-based solution to it using StatefulSnippets.
    +  * There are doubtless many other ways.
    +  *
    +  * @author: Steve Jenson
    +  */
    +class ArcChallenge extends StatefulSnippet {
    +  var dispatch: DispatchIt = {
    +    case _ =>
    +      xhtml =>
    +        ask
    +  }
    +
    +  /**
    +    * Step 1: Type in a Phrase.
    +    */
    +  def ask = {
    +    <form class="form-inline">
    +      <div class="form-group">
    +        <label for="answer">Say Anything:</label>
    +        {text("",
    +              p => phrase = p,
    +              "id" -> "answer",
    +              "class" -> "form-control mx-sm-3")}
    +      </div>
    +      {submit("Submit",
    +              () => dispatch = {case _ => xhtml => think},
    +              "class" -> "btn btn-primary")}
    +    </form>
       }
     
    -  /**
    -   * Step 2: Show a link that takes you to the Phrase you entered.
    -   */
    -  def think = submit("Click here to see what you said",
    -                     () => dispatch = {case _ => xhtml => answer})
    +  /**
    +    * Step 2: Show a link that takes you to the Phrase you entered.
    +    */
    +  def think =
    +    submit("Click here to see what you said",
    +           () =>
    +             dispatch = {
    +               case _ =>
    +                 xhtml =>
    +                   answer
    +           },
    +           "class" -> "btn btn-primary")
     
    -  /**
    -   * Step 3: Show the phrase.
    -   */
    -  def answer = <p>You said: {phrase}</p>
    +  /**
    +    * Step 3: Show the phrase.
    +    */
    +  def answer = <p>You said: {phrase}</p>
     
    -  private var phrase = ""
    -}
    -  
    + private var phrase = "" +}
    +
    + -
    + \ No newline at end of file diff --git a/combo/example/src/main/webapp/async_rest.html b/combo/example/src/main/webapp/async_rest.html index 13b304f..8481134 100644 --- a/combo/example/src/main/webapp/async_rest.html +++ b/combo/example/src/main/webapp/async_rest.html @@ -1,75 +1,66 @@ - -
    - This example demonstrates how to use the container's - Asynchronous (continuation) response capabilities such - that no I/O threads are consumed during the calculation - of the REST response value. - Click to see the response - after a 2 second delay. -
    + + + + + + Template + -
    - + +
    +

    Async REST

    + -
    -/**
    +    
    + This example demonstrates how to use the container's Asynchronous (continuation) response capabilities such that no I/O threads + are consumed during the calculation of the REST response value. + Click to see the response after a 2 second delay. +
    + + The + Lift Scala code +
    /**
      * An example of Lift's RestHelper and RestContinuation
    - */
    -object AsyncRest extends RestHelper {
    + */
    +object AsyncRest extends RestHelper {
    +
    +  // serve the URL /async/:id
    +  serve {
    +    case "async" :: id :: _ Get _ =>
     
    -  // serve the URL /async/:id
    -  serve {
    -    case "async" :: id :: _ Get _ => 
    +      // move the calculation to another thread
    +      RestContinuation.async(
    +        reply => {
    +          Thread.sleep(2000) // sleep for 2 seconds
    +          val name1 = Thread.currentThread.getName
    +          val outerSesStr = S.session.toString // this should be Empty
     
    -      // move the calculation to another thread
    -      RestContinuation.async(
    -        reply => {
    -          Thread.sleep(2000) // sleep for 2 seconds
    -          val name1 = Thread.currentThread.getName
    -          val outerSesStr = S.session.toString // this should be Empty
    -          
    -          // the code block for reply will be executed in the
    -          // scope of the original request and that may mean
    -          // that JDBC connections are consumed, etc.
    -          reply{
    -            val name2 = Thread.currentThread.getName
    -            val innerSesStr = S.session.toString // this should be Full()
    -            <i id={id}>name1: {name1} outer: {outerSesStr} name2: {name2}
    +          // the code block for reply will be executed in the
    +          // scope of the original request and that may mean
    +          // that JDBC connections are consumed, etc.
    +          reply{
    +            val name2 = Thread.currentThread.getName
    +            val innerSesStr = S.session.toString // this should be Full()
    +            <i id={id}>name1: {name1} outer: {outerSesStr} name2: {name2}
                 inner: {innerSesStr}</i>
               }
             })
       }
    -}
    -
    +}
    +
    - + + + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/file_upload.html b/combo/example/src/main/webapp/file_upload.html index 791270b..399826d 100644 --- a/combo/example/src/main/webapp/file_upload.html +++ b/combo/example/src/main/webapp/file_upload.html @@ -1,25 +1,57 @@ - -

    -This is the file upload sample page. You choose a file -to upload and the application will tell you the file name, the mime type, -the number of bytes in the file and the MD5 checksum. -

    - - - -

    -File name:
    -MIME Type:
    -File length:
    -MD5 Hash:
    -

    -
    - - -Select a file to upload:
    - -
    - -
    - -
    + + + + + + Template + + + +
    +

    File Upload

    + + +

    + This is the file upload sample page. You choose a file to upload and the application will tell you the file name, the mime + type, the number of bytes in the file and the MD5 checksum. +

    + + + + +

    + File name: + +
    MIME Type: + +
    File length: + +
    MD5 Hash: + +
    +

    +
    + + + Select a file to upload: + +
    + +
    + +
    + +
    + + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/invoice_wiring.html b/combo/example/src/main/webapp/invoice_wiring.html index ea0939a..05243d4 100644 --- a/combo/example/src/main/webapp/invoice_wiring.html +++ b/combo/example/src/main/webapp/invoice_wiring.html @@ -1,93 +1,146 @@ - + + + + + + Template + + + +
    +

    Wiring Invoice

    + -
    - An example of using Lift's Wiring feature to build an invoice system. -
    - -
    input goes here
    -
    Add
    - -
    Subtotal: subtotal
    - -
    Tax Rate:
    - -
    Taxable: taxable
    +
    + An example of using Lift's Wiring feature to build an invoice system. +
    -
    Tax: Tax
    +
    input goes here
    -
    Total: Total
    +
    +
    +
    Add
    +
    +
    -
    -
    - The code for managing the invoice page is very simple. - We define the relationship between the data: -
    - +
    + +
    + taxable +
    +
    -
    -case class Line(guid: String, name: String, price: Double, taxable: Boolean)
    +    
    + +
    + Tax +
    +
    - private object Info { - val invoices = ValueCell(List(newLine)) - val taxRate = ValueCell(0.05d) - val subtotal = invoices.lift(_.foldLeft(0d)(_ + _.price)) - val taxable = invoices.lift(_.filter(_.taxable). - foldLeft(0D)(_ + _.price)) +
    + +
    + Total +
    +
    - val tax = taxRate.lift(taxable) {_ * _} +
    - val total = subtotal.lift(tax) {_ + _} - } +
    + The code for managing the invoice page is very simple. We define the relationship between the data: +
    -
    -
    - Next we create snippets to display the data: -
    -
    -  def subtotal(in: NodeSeq) = WiringUI.asText(in, Info.subtotal)
    +    
    case class Line(guid: String, name: String, price: Double, taxable: Boolean)
     
    -  def taxable(in: NodeSeq) = WiringUI.asText(in, Info.taxable)
    +  private object Info {
    +    val invoices = ValueCell(List(newLine))
    +    val taxRate = ValueCell(0.05d)
    +    val subtotal = invoices.lift(_.foldLeft(0d)(_ + _.price))
    +    val taxable = invoices.lift(_.filter(_.taxable).foldLeft(0D)(_ + _.price))
     
    -  def tax(in: NodeSeq) = WiringUI.asText(in, Info.tax, JqWiringSupport.fade)
    +    val tax = taxRate.lift(taxable) { _ * _ }
     
    -  def total(in: NodeSeq) = WiringUI.asText(in, Info.total, JqWiringSupport.fade)
    -
    -
    - And hook the snippets into our view: -
    + val total = subtotal.lift(tax) { _ + _ } + }
    -
    -  <div>Subtotal: <span class="lift:InvoiceWiring.subtotal">subtotal</span></div>
    +    
    + Next we create snippets to display the data: +
    +
      def subtotal(in: NodeSeq) = WiringUI.asText(in, Info.subtotal)
     
    -  <div>Tax Rate: <input class="lift:InvoiceWiring.taxRate"></div>
    +  def taxable(in: NodeSeq) = WiringUI.asText(in, Info.taxable)
     
    -  <div>Taxable: <span class="lift:InvoiceWiring.taxable">taxable</span></div>
    +  def tax(in: NodeSeq) = WiringUI.asText(in, Info.tax, JqWiringSupport.fade)
     
    -  <div>Tax: <span class="lift:InvoiceWiring.tax">Tax</span></div>
    +  def total(in: NodeSeq) = WiringUI.asText(in, Info.total, JqWiringSupport.fade)
    - <div>Total: <span class="lift:InvoiceWiring.total">Total</span></div> -
    +
    + And hook the snippets into our view: +
    +
      <div class="form-group row">
    +    <label class="col-3">Subtotal:</label>
    +    <div class="col-2">
    +      <span data-lift="InvoiceWiring.subtotal">subtotal</span>
    +    </div>
    +  </div>
    +
    +  <div class="form-group row">
    +    <label class="col-sm-3 col-form-label">Tax Rate:</label>
    +    <div class="col-sm-2">
    +      <input data-lift="InvoiceWiring.taxRate">
    +    </div>
    +  </div>
    +
    +  <div class="form-group row">
    +    <label class="col-3">Taxable:</label>
    +    <div class="col-2">
    +      <span data-lift="InvoiceWiring.taxable">taxable</span>
    +    </div>
    +  </div>
    +
    +  <div class="form-group row">
    +    <label class="col-3">Tax:</label>
    +    <div class="col-2">
    +      <span data-lift="InvoiceWiring.tax">Tax</span>
    +    </div>
    +  </div>
    +
    +  <div class="form-group row">
    +    <label class="col-3">Total:</label>
    +    <div class="col-2">
    +      <span data-lift="InvoiceWiring.total">Total</span>
    +    </div>
    +  </div>
    - And each time any of the cells changes, the display is automatically - updated. + And each time any of the cells changes, the display is automatically updated.
    - +
    + + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/simple_wiring.html b/combo/example/src/main/webapp/simple_wiring.html index c3c660d..2811ac7 100644 --- a/combo/example/src/main/webapp/simple_wiring.html +++ b/combo/example/src/main/webapp/simple_wiring.html @@ -1,16 +1,101 @@ - + + -
    - An example of using Lift's Wiring feature to update - many different UI elements on the page. Todo count is - unknown. -
    -
    + + + Template + -
    to-dos here
    -
    + +
    +

    Simple Wiring

    + + +

    + An example of using Lift's Wiring feature to update many different UI elements on the page. Todo count is + unknown. +

    +
    to-dos here

    - I say again, our Todo count is unknown. +

    + I say again, our Todo count is + unknown. +

    + +
    + + The Markup +
      <p>
    +    An example of using Lift's Wiring feature to update
    +    many different UI elements on the page.  Todo count is
    +    <span data-lift="SimpleWiring.count" id="count1">unknown</span>.
    +  </p>
    +  <div data-lift="SimpleWiring.toDo">to-dos here</div>
    +  <br>
    +  <p>
    +    I say again, our Todo count is <span data-lift="SimpleWiring.count" id="count2">unknown</span>.
    +  </p>
    + + The + Lift Scala code +
    /**
    +  * A simple example of Wiring.
    +  * The count of done To-do items
    +  */
    +class SimpleWiring {
    +  // define the cells
    +  private val feedFish = ValueCell(false)
    +  private val walkDog = ValueCell(false)
    +  private val doDishes = ValueCell(false)
    +  private val watchTv = ValueCell(false)
    +
    +  // Our count is the collection of cells and we sum them up
    +  private val count = SeqCell(feedFish, walkDog, doDishes, watchTv).lift {
    +    _.map(_.toInt).sum
    +  }
    +
    +  private class BtoI(b: Boolean) { def toInt: Int = if (b) 1 else 0 }
    +  private implicit def bToI(b: Boolean): BtoI = new BtoI(b)
    +
    +  // define the count transformation
    +  def count(in: NodeSeq): NodeSeq =
    +    WiringUI.asText(in, count, JqWiringSupport.fade)
    +
    +  def toDo = {
    +    import SHtml._
    +    "* *" #> List[NodeSeq](
    +      <div class="form-check">
    +        {ajaxCheckboxElem(feedFish)}
    +        <label class="form-check-label">Feed Fish</label>
    +      </div>,
    +      <div class="form-check">
    +        {ajaxCheckboxElem(walkDog)}
    +        <label class="form-check-label">Walk Dog</label>
    +      </div>,
    +      <div class="form-check">
    +        {ajaxCheckboxElem(doDishes)}
    +        <label class="form-check-label">Do Dishes</label>
    +      </div>,
    +      <div class="form-check">
    +        {ajaxCheckboxElem(watchTv)}
    +        <label class="form-check-label">Watch TV</label>
    +      </div>
    +    )
    +  }
    +
    +}
    +
    + - + \ No newline at end of file diff --git a/combo/example/src/main/webapp/templates-hidden/wizard-all.html b/combo/example/src/main/webapp/templates-hidden/wizard-all.html index 865ddf7..5523c5d 100644 --- a/combo/example/src/main/webapp/templates-hidden/wizard-all.html +++ b/combo/example/src/main/webapp/templates-hidden/wizard-all.html @@ -24,13 +24,13 @@
    - + field help
    • field error
    + current field value
    EditDelete
    {link("/simple/edit", () => selectedUser(Full(u)), Text("Edit"), "class" -> "btn btn-sm btn-outline-secondary")}{link("/simple/delete", () => selectedUser(Full(u)), Text("Delete"), "class" -> "btn btn-sm btn-outline-secondary")}{link("/simple/edit", () => selectedUser(Full(u)), , "class" -> "btn btn-sm btn-outline-secondary")}{link("/simple/delete", () => selectedUser(Full(u)), , "class" -> "btn btn-sm btn-outline-secondary")}
    - - - - - - -
    - - - - - - - - - -
      - -
    • - -
    • -
      -
    -
    -
    - -
    - -
    -
    - - - - - - -
    - - - - - -
    -
    -
    -
    - diff --git a/combo/example/src/main/webapp/templating/_sample_embed.html b/combo/example/src/main/webapp/templating/_sample_embed.html index 0c27437..6198f29 100644 --- a/combo/example/src/main/webapp/templating/_sample_embed.html +++ b/combo/example/src/main/webapp/templating/_sample_embed.html @@ -1,10 +1,13 @@ - - Lift Example - - -

    - I am an example of a template that is embedded!! -

    - - + + + Lift Example + + + +

    + I am an example of a template that is embedded!! +

    + + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/templating/embed.html b/combo/example/src/main/webapp/templating/embed.html index 304171c..7866e3e 100644 --- a/combo/example/src/main/webapp/templating/embed.html +++ b/combo/example/src/main/webapp/templating/embed.html @@ -17,7 +17,8 @@

    %*%

    - In addition surrounding HTML with a template, you can also embed a template at the current point in the page rendering a + In addition surrounding HTML with a template, you can also embed a template at the current point in the page + rendering a tag with the data-lift='embed?what=...' attribute.

    @@ -32,13 +33,15 @@

    %*%

    <span data-lift="embed?what=/templating/_sample_embed"></span>

    - Templates that start with the underscore ('_') or period ('.') characters will not be served directly by Lift, but may be + Templates that start with the underscore ('_') or period ('.') characters will not be served directly by Lift, + but may be accessed using the surround and embed tags.

    Lift will select templates based on the current localization setting for the session. Lift uses the function in - LiftRules.localeCalculator to determine the current locale for template selection. By default the function is: + LiftRules.localeCalculator to determine the current locale for template selection. By default the + function is:

    The @@ -47,13 +50,15 @@

    %*%

    request.flatMap(_.getLocale() match {case null => Empty case l: Locale => Full(l)}).openOr(Locale.getDefault())
    -

    But you can customize the function to return the locale of the currently logged in user, detect the IP address of the +

    But you can customize the function to return the locale of the currently logged in user, detect the IP address + of the request, etc.

    Based on the locale, Lift will look for templates based on the base template name, in this case - '/templating/_sample_embed' and then append an underscore ('_') followed by the complete locale, for example + '/templating/_sample_embed' and then append an underscore ('_') followed by the complete locale, for + example 'en_US'. So, Lift will look for '/templating/_sample_embed_en_US.html'. If that resource is not available, Lift will look for '/templating/_sample_embed_en.html' and finally diff --git a/combo/example/src/main/webapp/templating/eval_order.html b/combo/example/src/main/webapp/templating/eval_order.html index 7571e4d..fb2919d 100644 --- a/combo/example/src/main/webapp/templating/eval_order.html +++ b/combo/example/src/main/webapp/templating/eval_order.html @@ -17,7 +17,8 @@

    %*%

    - Lift evaluates the <tagName data-lift="xxx"/> <lift:xxx/> tags from the outside in, otherwise knows as lazy evaluation. + Lift evaluates the <tagName data-lift="xxx"/> <lift:xxx/> tags from the outside + in, otherwise knows as lazy evaluation. This means that the following code will only embed a single template:

    HTML syntax diff --git a/combo/example/src/main/webapp/templating/head.html b/combo/example/src/main/webapp/templating/head.html index 67698d3..ca43e06 100644 --- a/combo/example/src/main/webapp/templating/head.html +++ b/combo/example/src/main/webapp/templating/head.html @@ -17,7 +17,8 @@

    %*%

    - Lift keeps the page as an XML data structure throughout the rendering process. This allows Lift to rewrite the web page at + Lift keeps the page as an XML data structure throughout the rendering process. This allows Lift to rewrite the + web page at different rendering phases. After the rendering is complete, Lift looks for all the <head/> tags in the diff --git a/combo/example/src/main/webapp/templating/index.html b/combo/example/src/main/webapp/templating/index.html index c1a567f..3fc1a4c 100644 --- a/combo/example/src/main/webapp/templating/index.html +++ b/combo/example/src/main/webapp/templating/index.html @@ -9,7 +9,7 @@

    %*%

    - +
    @@ -17,7 +17,8 @@

    %*%

    In the beginning there was XHTML
    -

    The text on this page is written in the era of xhtml so it's associated examples uses xhtml namespace syntax, but don't +

    The text on this page is written in the era of xhtml so + it's associated examples uses xhtml namespace syntax, but don't worry alongside with the older examples there are equivalent html5 examples using data-lift and CSS-selectors for data bindings. @@ -27,40 +28,59 @@

    In the beginning there was XHTML

    The following is David Pollak's take on web framework view technology:

    -

    The Lift design is born out of my experience (both good and bad) with a variety of technologies including Rails. +

    The Lift design is born out of my experience (both good and bad) with a variety of technologies including + Rails.

    I think the best paper on the subject is Terrence Parr's work on StringTemplate.

    -

    My first design goal with Lift was to make sure that no programming logic and no programming symbols make it into the +

    My first design goal with Lift was to make sure that no programming logic and no programming symbols make it + into the static display templates.

    -

    ERB and JSP and ASP all have the fatal flaw of allowing code in the view. This is bad for a bunch of reasons. First, - it makes editing the templates difficult with HTML layout tools unless those tools are familiar with the syntax being - used. Second, there are "foreign" symbols in the layout, which tends to be less than optimal for the HTML designers. - (On the Rails side of things, every Rails team I've seen has a Ruby coder that also does the design. While this is - the norm in the Rails community, it is the exception when team sizes are more than 2 or 3.) Third, every single Rails, - JSP, and ASP project I've ever seen (and I've been seeing them for a very long time) has some non-trivial amount - of business logic creep into the display. Fourth, Scala has a very nice type system and when the type system is used - correctly, the compiler finds a lot of program errors, but when the code is buried in templates, one has a much more +

    ERB and JSP and ASP all have the fatal flaw of allowing code in the view. This is bad for a bunch of reasons. + First, + it makes editing the templates difficult with HTML layout tools unless those tools are familiar with the syntax + being + used. Second, there are "foreign" symbols in the layout, which tends to be less than optimal for the HTML + designers. + (On the Rails side of things, every Rails team I've seen has a Ruby coder that also does the design. While this + is + the norm in the Rails community, it is the exception when team sizes are more than 2 or 3.) Third, every single + Rails, + JSP, and ASP project I've ever seen (and I've been seeing them for a very long time) has some non-trivial + amount + of business logic creep into the display. Fourth, Scala has a very nice type system and when the type system is + used + correctly, the compiler finds a lot of program errors, but when the code is buried in templates, one has a much + more difficult time using the powerful Scala compiler tools.

    -

    So, the static templates in Lift are strictly display only. They can be manipulated with standard design tools (e.g., +

    So, the static templates in Lift are strictly display only. They can be manipulated with standard design tools + (e.g., Dreamweaver). They can never contain program logic.

    Some asides.

    -

    First, I rejected using StringTemplate (or something like it) because it introduced some programming into the templating - mechanism and it would have taken a lot of work to make it XMLTemplate (and all of Lift's templating is XHTML and - makes use of Scala's excellent support of XML.) Note: Nowadays most templating is based on HTML5. +

    First, I rejected using StringTemplate (or something like it) because it introduced some programming into the + templating + mechanism and it would have taken a lot of work to make it XMLTemplate (and all of Lift's templating is XHTML + and + makes use of Scala's excellent support of XML.) Note: Nowadays most templating is based on + HTML5.

    -

    Second, I've been referring to static templates. Lift has a little known and pretty much undocumented feature that - supports template generation from Scala code. One of these days, I'll document the feature and put up some examples. +

    Second, I've been referring to static templates. Lift has a little known and pretty much undocumented feature + that + supports template generation from Scala code. One of these days, I'll document the feature and put up some + examples.

    -

    Third, Rails' "controller first" dispatch mechanism makes the assumption that there is only one piece of "logic" on - the page and the rest is decoration. My experience doing web work is just the opposite. There are typically 3 or - more of pieces of logic on a page (dynamic menu bars, search boxes, shopping cart, real-time chat, etc.) and having +

    Third, Rails' "controller first" dispatch mechanism makes the assumption that there is only one piece of + "logic" on + the page and the rest is decoration. My experience doing web work is just the opposite. There are typically 3 + or + more of pieces of logic on a page (dynamic menu bars, search boxes, shopping cart, real-time chat, etc.) and + having to choose which piece of logic make the "controller" is less than optimal.

    So, the quintessential use of Lift's templates are as follows: @@ -68,27 +88,27 @@

    In the beginning there was XHTML

    Simple form example

    -
    - -
    - -
    - -
    - -
    -
    - -
    +
    + +
    +
    + +
    + +
    +
    + +
    +
    -
    +

    HTML syntax @@ -140,19 +160,27 @@

    Simple form example

    ... </html>
    -

    So we've got a Lift snippet invocation with the valid HTML form and some additional tags. So far (with the proper namespace - declarations) this page is valid XHTML. This page can be viewed in a browser or opened and edited in Dreamweaver. +

    So we've got a Lift snippet invocation with the valid HTML form and some additional tags. So far (with the + proper namespace + declarations) this page is valid XHTML. This page can be viewed in a browser or opened and edited in + Dreamweaver.

    -

    In Lift, the snippet is the equivalent of a Rails controller: it is the instantiation of a class and invocation of - a method on the class. Because you can have multiple snippets on a page, you can call out multiple logic streams +

    In Lift, the snippet is the equivalent of a Rails controller: it is the instantiation of a class and + invocation of + a method on the class. Because you can have multiple snippets on a page, you can call out multiple logic + streams on a given page and there's no need to choose the "primary" logic stream.

    -

    The 'form="post"' attribute is a shortcut. It automatically wraps the enclosed XHTML in a <form method='post' target={current +

    The 'form="post"' attribute is a shortcut. It automatically wraps the enclosed XHTML in a <form + method='post' target={current page... it's a post-back}>...</form> tag.

    -

    The <f:.../> tags are bind points for the business logic. They allow your snippet to easily replace the tag and - its children with what is supposed to be displayed. This mechanism is a little heavier than Wicket's mechanism for - binding display elements to logic and I've got a to-do to make Lift's mechanism work like Wickets as well... but +

    The <f:.../> tags are bind points for the business logic. They allow your snippet to easily replace the + tag and + its children with what is supposed to be displayed. This mechanism is a little heavier than Wicket's mechanism + for + binding display elements to logic and I've got a to-do to make Lift's mechanism work like Wickets as well... + but that's a digression.

    So, your Lift code will look like: @@ -219,11 +247,13 @@

    Simple form example

    } }
    -

    Note that no display code has crept into the snippet. You've simply bound the HTML created by text() and select() to +

    Note that no display code has crept into the snippet. You've simply bound the HTML created by text() and + select() to the <f:name/> and <f:year/> tags in the incoming XHTML.

    Also, you've bound two functions (the anonymous function - name = _ and handleYear) to the HTML form elements. When the form is POSTed, these functions (which are bound + name = _ and handleYear) to the HTML form elements. When the form is POSTed, these functions + (which are bound to local variables) will be statefully invoked.

    If you are displaying a table rather than a form, then the same binding logic still works. For example: @@ -253,18 +283,23 @@

    Simple form example

    xhtml, "first_name" --> user.firstName, "last_name" --> user.nameName)) } -

    If you take the time to clearly define the bind points, then you can have no display code at all in your snippets. +

    If you take the time to clearly define the bind points, then you can have no display code at all in your + snippets.

    -

    Can display logic slip into a snippet? Yes, and as you've seen and pointed out, the examples are less than optimal +

    Can display logic slip into a snippet? Yes, and as you've seen and pointed out, the examples are less than + optimal in this regard.

    -

    Has display logic ever crept into a method called from an ERB template? Yes, and very often it's a source of a potential +

    Has display logic ever crept into a method called from an ERB template? Yes, and very often it's a source of a + potential Cross Site Scripting vulnerability.

    Has business logic ever crept into an ERB template? Yes.

    -

    In Lift, display can creep into a snippet, but business logic cannot creep into a the static display template. Yes, - your designers will still have to police putting display logic in the snippet code, but the coders will not have +

    In Lift, display can creep into a snippet, but business logic cannot creep into a the static display template. + Yes, + your designers will still have to police putting display logic in the snippet code, but the coders will not + have to police business logic in the templates.

    diff --git a/combo/example/src/main/webapp/templating/pageThree.html b/combo/example/src/main/webapp/templating/pageThree.html index 6142fe9..4acacc0 100644 --- a/combo/example/src/main/webapp/templating/pageThree.html +++ b/combo/example/src/main/webapp/templating/pageThree.html @@ -7,4 +7,4 @@ -
    +
    \ No newline at end of file diff --git a/combo/example/src/main/webapp/templating/pageTwo.html b/combo/example/src/main/webapp/templating/pageTwo.html index 4192c89..1f46a13 100644 --- a/combo/example/src/main/webapp/templating/pageTwo.html +++ b/combo/example/src/main/webapp/templating/pageTwo.html @@ -7,4 +7,4 @@ -
    +
    \ No newline at end of file diff --git a/combo/example/src/main/webapp/templating/selectomatic.html b/combo/example/src/main/webapp/templating/selectomatic.html index 1b7ddb6..f45a467 100644 --- a/combo/example/src/main/webapp/templating/selectomatic.html +++ b/combo/example/src/main/webapp/templating/selectomatic.html @@ -17,7 +17,8 @@

    %*%

    - On this page, we'll allow the user to select a series of <div> tags to display based on the results of a form submission. + On this page, we'll allow the user to select a series of <div> tags to display based on the results of a + form submission.

    diff --git a/combo/example/src/main/webapp/templating/surround.html b/combo/example/src/main/webapp/templating/surround.html index 8c6068c..0c0e4b0 100644 --- a/combo/example/src/main/webapp/templating/surround.html +++ b/combo/example/src/main/webapp/templating/surround.html @@ -8,16 +8,17 @@
    -

    %*%

    +

    %*%

    - + -
    -
    -
    +
    +
    +

    - Lift supports surrounding a given section of a page with a template from another file. This allows you to have master page + Lift supports surrounding a given section of a page with a template from another file. This allows you to have + master page templates or even section templates that are accessed by many different pages.

    @@ -26,14 +27,18 @@

    %*%

    </div>

    Using the - data-lift='surround...' attribute in a tag, you can surround it's body with the contents of another template. + data-lift='surround...' attribute in a tag, you can surround it's body with the contents of another + template. The "with" parameter designates the name of the template. By convension, templates are stored in the - /templates-hidden directory. Lift will not serve direct HTTP request from any directory that contains + /templates-hidden directory. Lift will not serve direct HTTP request from any directory that + contains "-hidden". The - "at" parameter specifies where the content should be placed in the loaded template. Lift will look for a tag + "at" parameter specifies where the content should be placed in the loaded template. Lift will look + for a tag with a - id="content" attribute in the target template and insert the body of the surround tag at the bind point. + id="content" attribute in the target template and insert the body of the surround tag at the bind + point.

    /templates-hidden/default.html diff --git a/combo/example/src/main/webapp/wiki.html b/combo/example/src/main/webapp/wiki.html index ff60a37..4dd678a 100644 --- a/combo/example/src/main/webapp/wiki.html +++ b/combo/example/src/main/webapp/wiki.html @@ -7,14 +7,14 @@ -
    -

    %*%

    +
    +

    %*%

    - + -
    : Wiki
    -
    -
    +
    : Wiki
    +
    +
    - + \ No newline at end of file diff --git a/combo/example/src/main/webapp/wiz.html b/combo/example/src/main/webapp/wiz.html index e644d2f..4dd7fc8 100644 --- a/combo/example/src/main/webapp/wiz.html +++ b/combo/example/src/main/webapp/wiz.html @@ -10,17 +10,17 @@

    %*%

    - + -
    -
    -
    +
    +
    +
    An example of a multi-screen input wizard. The Wizard is defined declaratively.
    -
    +
    diff --git a/combo/example/src/main/webapp/wiz2.html b/combo/example/src/main/webapp/wiz2.html index 5f086d5..53108ab 100644 --- a/combo/example/src/main/webapp/wiz2.html +++ b/combo/example/src/main/webapp/wiz2.html @@ -18,10 +18,11 @@

    %*%

    Getting state right in a multi-page wizard is - non-trivial. This wizard allows you to run Erwin's tests. + non-trivial. This wizard + allows you to run Erwin's tests.
    -
    +

    diff --git a/combo/example/src/main/webapp/ws.html b/combo/example/src/main/webapp/ws.html index 90c81a9..402dd35 100644 --- a/combo/example/src/main/webapp/ws.html +++ b/combo/example/src/main/webapp/ws.html @@ -18,13 +18,17 @@

    %*%

    Normally, - Lift will look for a "view" that satifying an HTTP request. A view is an XHTML page or segment of a page with special - <lift:xxx> processing instructions in it (e.g., an instruction to embed a controller, an instruction to surround - the XHTML with another template, etc.) However, in certain cases, for example, when you want to satisfy a Web Services + Lift will look for a "view" that satifying an HTTP request. A view is an XHTML page or segment of a page + with special + <lift:xxx> processing instructions in it (e.g., an instruction to embed a controller, an instruction to + surround + the XHTML with another template, etc.) However, in certain cases, for example, when you want to satisfy a Web + Services (REST) request, you can use Scala's pattern matching to intercept a request and service it.

    -

    It's easy to "serve" incoming HTTP requests. Using Lift's RestHelper class. For example, you can serve "/webservices/all_users" +

    It's easy to "serve" incoming HTTP requests. Using Lift's RestHelper class. For example, you can serve + "/webservices/all_users" as a JSON request as:

    @@ -47,7 +51,8 @@

    %*%

    }

    - If you have a request (in the case of this example, servicing both a POST and a GET for /webservices/add_user), then service + If you have a request (in the case of this example, servicing both a POST and a GET for /webservices/add_user), + then service the request and include a function to convert the response into either JSON or XML:

    @@ -82,8 +87,10 @@

    %*%

    }

    - Note that if the firstname parameter is missing, a 400 response with the message "firstname parameter missing" message in - the body will be returned. If the lastname parameter is missing, a 404 will be returned with "lastname parameter missing" + Note that if the firstname parameter is missing, a 400 response with the message "firstname parameter missing" + message in + the body will be returned. If the lastname parameter is missing, a 404 will be returned with "lastname parameter + missing" message in the body.

    From 746aedb2090ed3dca2ad0a392e332bb8712377a6 Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Wed, 26 Dec 2018 14:38:25 +0100 Subject: [PATCH 24/33] Building using Scala 2.12 Makes Lift version and build information shown in the app work again. --- combo/example/build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/combo/example/build.sbt b/combo/example/build.sbt index b26f56e..f8a84f7 100644 --- a/combo/example/build.sbt +++ b/combo/example/build.sbt @@ -8,7 +8,7 @@ lazy val projectSettings = Seq( organization := "net.liftweb", version := "0.9.0-SNAPSHOT", name := "demo", - scalaVersion := "2.11.11", + scalaVersion := "2.12.7", scalacOptions ++= Seq("-unchecked", "-deprecation"), autoAPIMappings := true ) From 7f831e73748be9ba8f4fe9693d78fa57cb95a7cc Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Sun, 30 Dec 2018 14:52:40 +0100 Subject: [PATCH 25/33] Adding top nav for use on tablet/mobile view --- combo/example/.gitignore | 3 + .../main/scala/bootstrap/liftweb/Boot.scala | 601 +++++++++++++----- .../net/liftweb/example/lib/WikiStuff.scala | 2 +- .../src/main/webapp/assets/css/app.css | 120 +++- .../templates-hidden/_embeddedTopNavbar.html | 26 + .../webapp/templates-hidden/_resources.html | 28 +- .../main/webapp/templates-hidden/default.html | 19 +- 7 files changed, 602 insertions(+), 197 deletions(-) create mode 100644 combo/example/.gitignore create mode 100644 combo/example/src/main/webapp/templates-hidden/_embeddedTopNavbar.html diff --git a/combo/example/.gitignore b/combo/example/.gitignore new file mode 100644 index 0000000..eeea5d1 --- /dev/null +++ b/combo/example/.gitignore @@ -0,0 +1,3 @@ +.ensime_cache +ensime-langserver.log +pc.stdout.log \ No newline at end of file diff --git a/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala b/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala index 4ad3530..0bad146 100644 --- a/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala @@ -16,47 +16,49 @@ package bootstrap.liftweb import net.liftweb._ -import common.{Box, Full, Empty, Failure, Loggable} +import common.{Box, Empty, Failure, Full, Loggable} import util.{Helpers, NamedPF, Props} import http._ import actor._ import provider._ import sitemap._ import Helpers._ - import example._ import net.liftmodules.widgets.autocomplete._ -import net.liftmodules.{fobobs4,fobofa,fobohl,fobojq,fobopop} +import net.liftmodules.{fobobs4, fobofa, fobohl, fobojq, fobopop} import comet._ import model._ import lib._ import net.liftweb.mapper.{ -DB, -ConnectionManager, -Schemifier, -DefaultConnectionIdentifier, -ConnectionIdentifier + ConnectionIdentifier, + ConnectionManager, + DB, + DefaultConnectionIdentifier, + Schemifier } +import scala.xml.Text import _root_.java.sql.{Connection, DriverManager} -import snippet._ +import snippet._ /** - * A class that's instantiated early and run. It allows the application - * to modify lift's environment - */ + * A class that's instantiated early and run. It allows the application + * to modify lift's environment + */ class Boot { def boot { DB.defineConnectionManager(DefaultConnectionIdentifier, DBVendor) LiftRules.addToPackages("net.liftweb.example") /** - * We're doing this as HTML5 - */ - LiftRules.htmlProperties.default.set((r: Req) => new Html5Properties(r.userAgent)) + * We're doing this as HTML5 + */ + LiftRules.htmlProperties.default.set((r: Req) => + new Html5Properties(r.userAgent)) - LiftRules.localeCalculator = r => definedLocale.openOr(LiftRules.defaultLocaleCalculator(r)) + LiftRules.localeCalculator = r => + definedLocale.openOr(LiftRules.defaultLocaleCalculator(r)) if (!Props.inGAE) { // No DB stuff in GAE @@ -72,17 +74,19 @@ class Boot { XmlServer.init() LiftRules.statelessDispatch.append { - case r@Req("stateless" :: _, "", GetRequest) => StatelessHtml.render(r) _ + case r @ Req("stateless" :: _, "", GetRequest) => + StatelessHtml.render(r) _ } LiftRules.dispatch.prepend(NamedPF("Login Validation") { case Req("login" :: page, "", _) - if !LoginStuff.is && page.head != "validate" => - () => Full(RedirectResponse("/login/validate")) + if !LoginStuff.is && page.head != "validate" => + () => + Full(RedirectResponse("/login/validate")) }) SessionMaster.sessionCheckFuncs = SessionMaster.sessionCheckFuncs ::: - List(SessionChecker) + List(SessionChecker) // Uncomment the lines below to see how // a Lift app looks when it's stateless @@ -94,28 +98,23 @@ class Boot { LiftRules.snippetDispatch.append(Map("runtime_stats" -> RuntimeStats)) /* - * Show the spinny image when an Ajax call starts - */ - LiftRules.ajaxStart = - Full(() => LiftRules.jsArtifacts.show("ajax-loader").cmd) + * Show the spinny image when an Ajax call starts + */ + LiftRules.ajaxStart = Full( + () => LiftRules.jsArtifacts.show("ajax-loader").cmd) /* - * Make the spinny image go away when it ends - */ - LiftRules.ajaxEnd = - Full(() => LiftRules.jsArtifacts.hide("ajax-loader").cmd) + * Make the spinny image go away when it ends + */ + LiftRules.ajaxEnd = Full( + () => LiftRules.jsArtifacts.hide("ajax-loader").cmd) LiftRules.early.append(makeUtf8) LiftRules.cometCreation.append { - case CometCreationInfo("Clock", - name, - defaultXml, - attributes, - session) => - new ExampleClock(session, Full("Clock"), - name, defaultXml, attributes) - + case CometCreationInfo("Clock", name, defaultXml, attributes, session) => + new ExampleClock(session, Full("Clock"), name, defaultXml, attributes) + } LiftRules.noticesAutoFadeOut.default.set((notices: NoticeType.Value) => { @@ -126,31 +125,31 @@ class Boot { }) LiftSession.onBeginServicing = RequestLogger.beginServicing _ :: - LiftSession.onBeginServicing + LiftSession.onBeginServicing LiftSession.onEndServicing = RequestLogger.endServicing _ :: - LiftSession.onEndServicing + LiftSession.onEndServicing - LiftRules.setSiteMapFunc(MenuInfo.sitemap) + LiftRules.setSiteMapFunc(() => MenuInfo.sitemap()) LiftRules.securityRules = () => { SecurityRules( content = Some( ContentSecurityPolicy( scriptSources = List(ContentSourceRestriction.UnsafeEval, - ContentSourceRestriction.UnsafeInline, - ContentSourceRestriction.Self), + ContentSourceRestriction.UnsafeInline, + ContentSourceRestriction.Self), styleSources = List(ContentSourceRestriction.UnsafeInline, - ContentSourceRestriction.Self) + ContentSourceRestriction.Self) ))) } // FoBo init - fobojq.Toolkit.init = fobojq.Toolkit.JQuery224 - fobohl.Toolkit.init = fobohl.Toolkit.HighlightJS930 - fobofa.Toolkit.init = fobofa.Toolkit.FontAwesome550 + fobojq.Toolkit.init = fobojq.Toolkit.JQuery224 + fobohl.Toolkit.init = fobohl.Toolkit.HighlightJS930 + fobofa.Toolkit.init = fobofa.Toolkit.FontAwesome550 fobobs4.Toolkit.init = fobobs4.Toolkit.Bootstrap413 fobopop.Toolkit.init = fobopop.Toolkit.Popper1129 - fobojq.Toolkit.init = fobojq.Toolkit.JQueryMigrate141 + fobojq.Toolkit.init = fobojq.Toolkit.JQueryMigrate141 ThingBuilder.boot() @@ -158,14 +157,16 @@ class Boot { // Dump information about session every 10 minutes SessionMaster.sessionWatchers = SessionInfoDumper :: - SessionMaster.sessionWatchers + SessionMaster.sessionWatchers // Dump browser information each time a new connection is made LiftSession.onBeginServicing = BrowserLogger.haveSeenYou _ :: LiftSession.onBeginServicing } - private def makeUtf8(req: HTTPRequest): Unit = {req.setCharacterEncoding("UTF-8")} + private def makeUtf8(req: HTTPRequest): Unit = { + req.setCharacterEncoding("UTF-8") + } } object RequestLogger extends Loggable { @@ -175,23 +176,27 @@ object RequestLogger extends Loggable { startTime(millis) } - def endServicing(session: LiftSession, req: Req, + def endServicing(session: LiftSession, + req: Req, response: Box[LiftResponse]) { val delta = millis - startTime.is - logger.info("At " + (now) + " Serviced " + req.uri + " in " + (delta) + "ms " + ( - response.map(r => " Headers: " + r.toResponse.headers) openOr "" - )) + logger.info( + "At " + (now) + " Serviced " + req.uri + " in " + (delta) + "ms " + ( + response.map(r => " Headers: " + r.toResponse.headers) openOr "" + )) } } object MenuInfo { import Loc._ - lazy val noGAE = Unless(() => Props.inGAE, "Disabled for GAE") + private lazy val noGAE = Unless(() => Props.inGAE, "Disabled for GAE") + private val topNavLG = LocGroup("topNav") - def sitemap() = SiteMap( - Menu.i("Home") / "index", - Menu.i("Interactive Stuff") / "interactive" submenus( + private val siteMapList = List( + Menu.i("Home") / "index" >> topNavLG, + TopNav.interactiveMenuPart, + Menu.i("Interactive Stuff") / "interactive" submenus ( Menu("Comet Chat") / "chat" >> noGAE, Menu("Ajax Samples") / "ajax", Menu("Ajax Form") / "ajax-form", @@ -199,14 +204,21 @@ object MenuInfo { Menu("JSON Messaging") / "json", Menu("Stateless JSON Messaging") / "stateless_json", // Menu("More JSON") / "json_more", - Menu("Ajax and Forms") / "form_ajax") , + Menu("Ajax and Forms") / "form_ajax" + ), + TopNav.persistenceMenuPart, Menu.i("Persistence") / "persistence" >> noGAE submenus ( Menu("XML Fun") / "xml_fun" >> noGAE, Menu("Database") / "database" >> noGAE, - Menu(Loc("simple", Link(List("simple"), true, "/simple/index"), "Simple Forms", noGAE)) //, + Menu( + Loc("simple", + Link(List("simple"), true, "/simple/index"), + "Simple Forms", + noGAE)) //, // Menu("Templates") / "template" >> noGAE - ), - Menu.i("Templating") / "templating" / "index" submenus( + ), + TopNav.templatingMenuPart, + Menu.i("Templating") / "templating" / "index" submenus ( Menu("Surround") / "templating" / "surround", Menu("Embed") / "templating" / "embed", Menu("Evalutation Order") / "templating" / "eval_order", @@ -214,18 +226,23 @@ object MenuInfo { Menu("Simple Wizard") / "simple_wizard", Menu("Lazy Loading") / "lazy", Menu("Parallel Snippets") / "parallel", - Menu(" tag") / "templating"/ "head"), - Menu.i("Web Services") / "ws" >> noGAE, - Menu.i("Localization") / "lang", - Menu.i("Menus") / "menu" / "index" submenus( + Menu(" tag") / "templating" / "head" + ), + Menu.i("Web Services") / "ws" >> noGAE >> topNavLG, + Menu.i("Localization") / "lang" >> topNavLG, + TopNav.menusMenuPart, + Menu.i("Menus") / "menu" / "index" submenus ( Menu("First Submenu") / "menu" / "one", - Menu("Second Submenu (has more)") / "menu" / "two" submenus( + Menu("Second Submenu (has more)") / "menu" / "two" submenus ( Menu("First (2) Submenu") / "menu" / "two_one", - Menu("Second (2) Submenu") / "menu" / "two_two"), + Menu("Second (2) Submenu") / "menu" / "two_two" + ), Menu("Third Submenu") / "menu" / "three", - Menu("Forth Submenu") / "menu" / "four"), + Menu("Forth Submenu") / "menu" / "four" + ), Menu(WikiStuff), - Menu.i("Misc code") / "misc" submenus( + TopNav.miscMenuPart, + Menu.i("Misc code") / "misc" submenus ( Menu("Long Time") / "longtime", Menu("Number Guessing") / "guess", Menu("Wizard") / "wiz", @@ -237,19 +254,309 @@ object MenuInfo { Menu("Wiring Invoice") / "invoice_wiring", Menu("File Upload") / "file_upload", Menu("Async REST") / "async_rest", - Menu(Loc("login", Link(List("login"), true, "/login/index"), - Requiring LoginSiteMap )), - Menu("Counting") / "count"), - Menu(Loc("lift", ExtLink("http://liftweb.net"), - S.loc( "lift", Lift project home))), - Menu(Loc("src", ExtLink("https://github.com/lift/examples/tree/master/combo/example"), - S.loc("src", scala.xml.Text("Source code for this site")))) + Menu( + Loc("login", + Link(List("login"), true, "/login/index"), + Requiring LoginSiteMap )), + Menu("Counting") / "count" + ), + Menu( + Loc("lift", + ExtLink("http://liftweb.net"), + S.loc("lift", Lift project home), + topNavLG)), + Menu( + Loc("src", + ExtLink("https://github.com/lift/examples/tree/master/combo/example"), + S.loc("src", Text("Source code for this site")), + topNavLG)) ) + + private object TopNav { + // Interactive stuff + private val interactiveLoc = Loc( + "topNavInteractive", + Link(List("topNavInteractive"), true, "/interactive"), + S.loc("topNavInteractive", Text("Interactive Stuff"))) + private val cometChatLoc = Loc("topNavCometChat", + Link(List("topNavCometChat"), true, "/chat"), + S.loc("topNavCometChat", Text("Comet Chat")), + noGAE) + private val ajaxSamplesLoc = Loc( + "topNavAjaxSamples", + Link(List("topNavAjaxSamples"), true, "/ajax"), + S.loc("topNavAjaxSamples", Text("Ajax Samples"))) + private val ajaxFormLoc = Loc( + "topNavAjaxForm", + Link(List("topNavAjaxForm"), true, "/ajax-form"), + S.loc("topNavAjaxForm", Text("Ajax Form"))) + private val modalDialogLoc = Loc( + "topNavModalDialog", + Link(List("topNavModalDialog"), true, "/rhodeisland"), + S.loc("topNavModalDialog", Text("Modal Dialog"))) + private val jSONMessagingLoc = Loc( + "topNavJSONMessaging", + Link(List("topNavJSONMessaging"), true, "/json"), + S.loc("topNavJSONMessaging", Text("JSON Messaging"))) + private val statelessJSONMessagingLoc = Loc( + "topNavStatelessJSONMessaging", + Link(List("topNavStatelessJSONMessaging"), true, "/stateless_json"), + S.loc("topNavStatelessJSONMessaging", Text("Stateless JSON Messaging")) + ) + private val AjaxAndFormsLoc = Loc( + "topNavAjaxAndForms", + Link(List("topNavAjaxAndForms"), true, "/form_ajax"), + S.loc("topNavAjaxAndForms", Text("Ajax and Forms"))) + private val interactiveDD = Menu.i("topNavInteractiveDD") / "/dddlabel2" + private val interactive = Menu(interactiveLoc) + private val cometChat = Menu(cometChatLoc) + private val ajaxSamples = Menu(ajaxSamplesLoc) + private val ajaxForm = Menu(ajaxFormLoc) + private val modalDialog = Menu(modalDialogLoc) + private val jSONMessaging = Menu(jSONMessagingLoc) + private val statelessJSONMessaging = Menu(statelessJSONMessagingLoc) + private val ajaxAndForms = Menu(AjaxAndFormsLoc) + + // Persistence + private val persistenceLoc = Loc( + "topNavPersistence", + Link(List("topNavPersistence"), true, "/persistence"), + S.loc("topNavPersistence", Text("Persistence")), + noGAE) + private val xMLFunLoc = Loc("topNavXMLFun", + Link(List("topNavXMLFun"), true, "/xml_fun"), + S.loc("topNavXMLFun", Text("XML Fun")), + noGAE) + private val databaseLoc = Loc( + "topNavDatabase", + Link(List("topNavDatabase"), true, "/database"), + S.loc("topNavDatabase", Text("Database")), + noGAE) + private val simpleLoc = Loc( + "topNavSimple", + Link(List("topNavSimple"), true, "/simple/index"), + S.loc("topNavSimple", Text("Simple Forms")), + noGAE) + private val persistenceDD = Menu.i("topNavPersistenceDD") / "/ddlabel3" >> noGAE + private val persistence = Menu(persistenceLoc) + private val xMLFun = Menu(xMLFunLoc) + private val database = Menu(databaseLoc) + private val simple = Menu(simpleLoc) + + // Templating + private val teplatingLoc = Loc( + "topNavTeplating", + Link(List("topNavTeplating"), true, "/templating/index"), + S.loc("topNavTeplating", Text("Templating")) + ) + private val surroundLoc = Loc( + "topNavSurround", + Link(List("topNavSurround"), true, "/templating/surround"), + S.loc("topNavSurround", Text("Surround")) + ) + private val embedLoc = Loc( + "topNavEmbed", + Link(List("topNavEmbed"), true, "/templating/embed"), + S.loc("topNavEmbed", Text("Embed")) + ) + private val evaluationOrderLoc = Loc( + "topNavEvaluationOrder", + Link(List("topNavEvaluationOrder"), true, "/templating/eval_order"), + S.loc("topNavEvaluationOrder", Text("Evalutation Order")) + ) + private val selectDivsLoc = Loc( + "tomNavSelectDivs", + Link(List("tomNavSelectDivs"), true, "/templating/selectomatic"), + S.loc("tomNavSelectDivs", Text("Select
    s")) + ) + private val simpleWizardLoc = Loc( + "topNavSimpleWizard", + Link(List("topNavSimpleWizard"), true, "/simple_wizard"), + S.loc("topNavSimpleWizard", Text("Simple Wizard")) + ) + private val lazyLoadingLoc = Loc( + "topNavLazyLoading", + Link(List("topNavLazyLoading"), true, "/lazy"), + S.loc("topNavLazyLoading", Text("Lazy Loading")) + ) + private val parallelSnippetsLoc = Loc( + "topNavParallelSnippets", + Link(List("topNavParallelSnippets"), true, "/parallel"), + S.loc("topNavParallelSnippets", Text("Parallel Snippets")) + ) + // tag + private val headTagLoc = Loc( + "topNavHeadTagLoc", + Link(List("topNavHeadTagLoc"), true, "/templating/head"), + S.loc("topNavHeadTagLoc", Text(" tag")) + ) + private val templatingDD = Menu.i("topNavTemplatingDD") / "/ddlabel4" + private val templating = Menu(teplatingLoc) + private val surround = Menu(surroundLoc) + private val embed = Menu(embedLoc) + private val evaluationOrder = Menu(evaluationOrderLoc) + private val selectDivs = Menu(selectDivsLoc) + private val simpleWizard = Menu(simpleWizardLoc) + private val lazyLoading = Menu(lazyLoadingLoc) + private val parallelSnippets = Menu(parallelSnippetsLoc) + private val headTag = Menu(headTagLoc) + + // Menu + private val menusLoc = Loc( + "topNavMenus", + Link(List("topNavMenus"), true, "/menu/index"), + S.loc("topNavMenus", Text("Menus")) + ) + private val firstSubmenuLoc = Loc( + "topNavFirstSubmenu", + Link(List("topNavFirstSubmenu"), true, "/menu/one"), + S.loc("topNavFirstSubmenu", Text("First Submenu")) + ) + private val secondSubmenuLoc = Loc( + "topNavSecondSubmenu", + Link(List("topNavSecondSubmenu"), true, "/menu/two"), + S.loc("topNavSecondSubmenu", Text("Second Submenu (has more)")) + ) + private val first2SubmenuLoc = Loc( + "topNavFirst2Submenu", + Link(List("topNavFirst2Submenu"), true, "/menu/two_one"), + S.loc("topNavFirst2Submenu", Text("First (2) Submenu")) + ) + private val second2SubmenuLoc = Loc( + "topNavSecond2Submenu", + Link(List("topNavSecond2Submenu"), true, "/menu/two_two"), + S.loc("topNavSecond2Submenu", Text("Second (2) Submenu")) + ) + private val thirdSubmenuLoc = Loc( + "topNavThirdSubmenu", + Link(List("topNavThirdSubmenu"), true, "/menu/three"), + S.loc("topNavThirdSubmenu", Text("Third Submenu")) + ) + private val forthSubmenuLoc = Loc( + "topNavForthSubmenu", + Link(List("topNavForthSubmenu"), true, "/menu/four"), + S.loc("topNavForthSubmenu", Text("Forth Submenu")) + ) + private val menusDD = Menu.i("topNavMenusDD") / "/ddlabel5" + private val menus = Menu(menusLoc) + private val firstSubmenu = Menu(firstSubmenuLoc) + private val secondSubmenu = Menu(secondSubmenuLoc) + private val first2Submenu = Menu(first2SubmenuLoc) + private val second2Submenu = Menu(second2SubmenuLoc) + private val thirdSubmenu = Menu(thirdSubmenuLoc) + private val forthSubmenu = Menu(forthSubmenuLoc) + + // Misc + private val miscLoc = Loc( + "topNavMisc", + Link(List("topNavMisc"), true, "/misc"), + S.loc("topNavMisc", Text("Misc code")) + ) + private val longTimeLoc = Loc( + "topNavLongTime", + Link(List("topNavLongTime"), true, "/longtime"), + S.loc("topNavLongTime", Text("Long Time")) + ) + private val numberGuessingLoc = Loc( + "topNavNumberGuessing", + Link(List("topNavNumberGuessing"), true, "/guess"), + S.loc("topNavNumberGuessing", Text("Number Guessing")) + ) + private val wizardLoc = Loc( + "topNavWizard", + Link(List("topNavWizard"), true, "/wiz"), + S.loc("topNavWizard", Text("Wizard")) + ) + private val wizardChallengeLoc = Loc( + "topNavWizardChallenge", + Link(List("topNavWizardChallenge"), true, "/wiz2"), + S.loc("topNavWizardChallenge", Text("Wizard Challenge")) + ) + private val simpleScreenLoc = Loc( + "topNavSimpleScreen", + Link(List("topNavSimpleScreen"), true, "/simple_screen"), + S.loc("topNavSimpleScreen", Text("Simple Screen")) + ) + private val variableScreenLoc = Loc( + "topNavVariableScreen", + Link(List("topNavVariableScreen"), true, "/variable_screen"), + S.loc("topNavVariableScreen", Text("Variable Screen")) + ) + private val arcChallenge1Loc = Loc( + "topNavArcChallenge1", + Link(List("topNavArcChallenge1"), true, "/arc"), + S.loc("topNavArcChallenge1", Text("Arc Challenge #1")) + ) + private val simpleWiringLoc = Loc( + "topNavSimpleWiring", + Link(List("topNavSimpleWiring"), true, "/simple_wiring"), + S.loc("topNavSimpleWiring", Text("Simple Wiring")) + ) + private val wiringInvoiceLoc = Loc( + "topNavWiringInvoice", + Link(List("topNavWiringInvoice"), true, "/invoice_wiring"), + S.loc("topNavWiringInvoice", Text("Wiring Invoice")) + ) + private val fileUploadLoc = Loc( + "topNavFileUpload", + Link(List("topNavFileUpload"), true, "/file_upload"), + S.loc("topNavFileUpload", Text("File Upload")) + ) + private val asyncRESTLoc = Loc( + "topNavAsyncREST", + Link(List("topNavAsyncREST"), true, "/async_rest"), + S.loc("topNavAsyncREST", Text("Async REST")) + ) + private val loginLoc = Loc( + "topNavLogin", + Link(List("topNavLogin"), true, "/login/index"), + S.loc("topNavLogin", Requiring LoginSiteMap ) + ) + private val countingLoc = Loc( + "topNavCounting", + Link(List("topNavCounting"), true, "/count"), + S.loc("topNavCounting", Text("Counting")) + ) + private val miscDD = Menu.i("topNavMiscDD") / "/ddlabel6" + private val misc = Menu(miscLoc) + private val longTime = Menu(longTimeLoc) + private val numberGuessing = Menu(numberGuessingLoc) + private val wizard = Menu(wizardLoc) + private val simpleScreen = Menu(simpleScreenLoc) + private val variableScreen = Menu(variableScreenLoc) + private val arcChallenge = Menu(arcChallenge1Loc) + private val simpleWiring = Menu(simpleWiringLoc) + private val wiringInvoice = Menu(wiringInvoiceLoc) + private val fileUpload = Menu(fileUploadLoc) + private val asyncRest = Menu(asyncRESTLoc) + private val login = Menu(loginLoc) + private val counting = Menu(countingLoc) + + // Public stuff + val interactiveMenuPart = interactiveDD >> topNavLG >> PlaceHolder submenus (interactive, + cometChat, ajaxSamples, ajaxForm, modalDialog, jSONMessaging, statelessJSONMessaging, ajaxAndForms) + + val persistenceMenuPart = persistenceDD >> topNavLG >> PlaceHolder submenus (persistence, + xMLFun, database, simple) + + val templatingMenuPart = templatingDD >> topNavLG >> PlaceHolder submenus (templating, + surround, embed, evaluationOrder, selectDivs, simpleWizard, lazyLoading, parallelSnippets, headTag) + + // Note: The bootstrap navigator dose only handle one level of sub-menus + val menusMenuPart = menusDD >> topNavLG >> PlaceHolder submenus (menus, + firstSubmenu, secondSubmenu, first2Submenu, second2Submenu, thirdSubmenu, forthSubmenu) + + val miscMenuPart = miscDD >> topNavLG >> PlaceHolder submenus (misc, longTime, numberGuessing, + wizard, simpleScreen, variableScreen, arcChallenge, simpleWiring, wiringInvoice, fileUpload, asyncRest, + login, counting) + } + + def sitemap() = SiteMap(siteMapList: _*) } /** -* Database connection calculation -*/ + * Database connection calculation + */ object DBVendor extends ConnectionManager { private var pool: List[Connection] = Nil private var poolSize = 0 @@ -257,58 +564,58 @@ object DBVendor extends ConnectionManager { private lazy val chooseDriver = Props.mode match { case Props.RunModes.Production => "org.apache.derby.jdbc.EmbeddedDriver" - case _ => "org.h2.Driver" + case _ => "org.h2.Driver" } - private lazy val chooseURL = Props.mode match { case Props.RunModes.Production => "jdbc:derby:lift_example;create=true" - case _ => "jdbc:h2:mem:lift;DB_CLOSE_DELAY=-1" + case _ => "jdbc:h2:mem:lift;DB_CLOSE_DELAY=-1" } + private def createOne: Box[Connection] = + try { + val driverName: String = Props.get("db.driver") openOr chooseDriver + val dbUrl: String = Props.get("db.url") openOr chooseURL - private def createOne: Box[Connection] = try { - val driverName: String = Props.get("db.driver") openOr chooseDriver - val dbUrl: String = Props.get("db.url") openOr chooseURL - + Class.forName(driverName) - Class.forName(driverName) + val dm = (Props.get("db.user"), Props.get("db.password")) match { + case (Full(user), Full(pwd)) => + DriverManager.getConnection(dbUrl, user, pwd) - val dm = (Props.get("db.user"), Props.get("db.password")) match { - case (Full(user), Full(pwd)) => - DriverManager.getConnection(dbUrl, user, pwd) + case _ => DriverManager.getConnection(dbUrl) + } - case _ => DriverManager.getConnection(dbUrl) + Full(dm) + } catch { + case e: Exception => e.printStackTrace; Empty } - Full(dm) - } catch { - case e: Exception => e.printStackTrace; Empty - } - def newConnection(name: ConnectionIdentifier): Box[Connection] = synchronized { pool match { case Nil if poolSize < maxPoolSize => val ret = createOne - poolSize = poolSize + 1 - ret.foreach(c => pool = c :: pool) - ret + poolSize = poolSize + 1 + ret.foreach(c => pool = c :: pool) + ret case Nil => wait(1000L); newConnection(name) - case x :: xs => try { - x.setAutoCommit(false) - Full(x) - } catch { - case e => try { - pool = xs - poolSize = poolSize - 1 - x.close - newConnection(name) + case x :: xs => + try { + x.setAutoCommit(false) + Full(x) } catch { - case e => newConnection(name) + case e => + try { + pool = xs + poolSize = poolSize - 1 + x.close + newConnection(name) + } catch { + case e => newConnection(name) + } } - } } } @@ -323,7 +630,9 @@ object BrowserLogger extends Loggable { def haveSeenYou(session: LiftSession, request: Req) { if (!HaveSeenYou.is) { - logger.info("Created session " + session.uniqueId + " IP: {" + request.request.remoteAddress + "} UserAgent: {{" + request.userAgent.openOr("N/A") + "}}") + logger.info( + "Created session " + session.uniqueId + " IP: {" + request.request.remoteAddress + "} UserAgent: {{" + request.userAgent + .openOr("N/A") + "}}") HaveSeenYou(true) } } @@ -336,46 +645,46 @@ object SessionInfoDumper extends LiftActor with Loggable { import net.liftweb.example.lib.SessionChecker - protected def messageHandler = - { - case SessionWatcherInfo(sessions) => - if ((millis - cyclePeriod) > lastTime) { - lastTime = millis - val rt = Runtime.getRuntime - rt.gc - - RuntimeStats.lastUpdate = now - RuntimeStats.totalMem = rt.totalMemory - RuntimeStats.freeMem = rt.freeMemory - RuntimeStats.sessions = sessions.size - - val percent = (RuntimeStats.freeMem * 100L) / RuntimeStats.totalMem - - // get more aggressive about purging if we're - // at less than 35% free memory - if (percent < 35L) { - SessionChecker.killWhen /= 2L - if (SessionChecker.killWhen < 5000L) - SessionChecker.killWhen = 5000L - SessionChecker.killCnt *= 2 - } else { - SessionChecker.killWhen *= 2L - if (SessionChecker.killWhen > + protected def messageHandler = { + case SessionWatcherInfo(sessions) => + if ((millis - cyclePeriod) > lastTime) { + lastTime = millis + val rt = Runtime.getRuntime + rt.gc + + RuntimeStats.lastUpdate = now + RuntimeStats.totalMem = rt.totalMemory + RuntimeStats.freeMem = rt.freeMemory + RuntimeStats.sessions = sessions.size + + val percent = (RuntimeStats.freeMem * 100L) / RuntimeStats.totalMem + + // get more aggressive about purging if we're + // at less than 35% free memory + if (percent < 35L) { + SessionChecker.killWhen /= 2L + if (SessionChecker.killWhen < 5000L) + SessionChecker.killWhen = 5000L + SessionChecker.killCnt *= 2 + } else { + SessionChecker.killWhen *= 2L + if (SessionChecker.killWhen > SessionChecker.defaultKillWhen) - SessionChecker.killWhen = SessionChecker.defaultKillWhen - val newKillCnt = SessionChecker.killCnt / 2 - if (newKillCnt > 0) SessionChecker.killCnt = newKillCnt - } - - val dateStr: String = now.toString - logger.info("[MEMDEBUG] At " + dateStr + " Number of open sessions: " + sessions.size) - logger.info("[MEMDEBUG] Free Memory: " + pretty(RuntimeStats.freeMem)) - logger.info("[MEMDEBUG] Total Memory: " + pretty(RuntimeStats.totalMem)) - logger.info("[MEMDEBUG] Kill Interval: " + (SessionChecker.killWhen / 1000L)) - logger.info("[MEMDEBUG] Kill Count: " + (SessionChecker.killCnt)) + SessionChecker.killWhen = SessionChecker.defaultKillWhen + val newKillCnt = SessionChecker.killCnt / 2 + if (newKillCnt > 0) SessionChecker.killCnt = newKillCnt } - } + val dateStr: String = now.toString + logger.info( + "[MEMDEBUG] At " + dateStr + " Number of open sessions: " + sessions.size) + logger.info("[MEMDEBUG] Free Memory: " + pretty(RuntimeStats.freeMem)) + logger.info("[MEMDEBUG] Total Memory: " + pretty(RuntimeStats.totalMem)) + logger.info( + "[MEMDEBUG] Kill Interval: " + (SessionChecker.killWhen / 1000L)) + logger.info("[MEMDEBUG] Kill Count: " + (SessionChecker.killCnt)) + } + } private def pretty(in: Long): String = if (in > 1000L) pretty(in / 1000L) + "," + (in % 1000L) diff --git a/combo/example/src/main/scala/net/liftweb/example/lib/WikiStuff.scala b/combo/example/src/main/scala/net/liftweb/example/lib/WikiStuff.scala index 51eee4c..daae4a4 100644 --- a/combo/example/src/main/scala/net/liftweb/example/lib/WikiStuff.scala +++ b/combo/example/src/main/scala/net/liftweb/example/lib/WikiStuff.scala @@ -63,7 +63,7 @@ object WikiStuff extends Loc[WikiLoc] { // def text = S.loc("Wiki HomePage") // no extra parameters def params = - List(Unless(() => Props.inGAE || Props.productionMode, "Disabled for GAE")) + List(Unless(() => Props.inGAE || Props.productionMode, "Disabled for GAE"), LocGroup("topNav")) // is the current page an "edit" or "view" def currentEdit = requestValue.is.map(_.edit) openOr false diff --git a/combo/example/src/main/webapp/assets/css/app.css b/combo/example/src/main/webapp/assets/css/app.css index 3aa624f..bb0d5a7 100644 --- a/combo/example/src/main/webapp/assets/css/app.css +++ b/combo/example/src/main/webapp/assets/css/app.css @@ -1,3 +1,14 @@ +.navbar { + background-color: #e9ecef; + background-image: linear-gradient( #e9ecef, #eff4fa); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 -1px 0 rgba(0, 0, 0, 0.1) inset; +} +.dropdown-menu { + /* color: inherit; */ + background-color: inherit; + border: none; /*1px solid rgba(0, 0, 0, 0.15);*/ +} + .widget { border: 1px solid #ccc; background: #f5f5f5; @@ -6,59 +17,110 @@ font-size: 8pt; } -.sidebar ul { - padding: 5px; -} - -.sidebar ul li { - margin: 0; +.sidebar > ul { + min-width: 200px; + padding: 0; + background-color: #fff; + margin-left: 0; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 1px 4px rgba(0,0,0,.065); + -moz-box-shadow: 0 1px 4px rgba(0,0,0,.065); + box-shadow: 0 1px 4px rgba(0,0,0,.065); +} +.sidebar > ul > li > ul, +.sidebar > ul > li > ul > li > ul { + min-width: 200px; padding: 0; + background-color: #fff; + margin-left: 0; +} +.sidebar ul li, +.sidebar ul li ul li, +.sidebar ul li ul li ul li { list-style: none; - border: 1px solid #ccc; - border-bottom: none; +} +.sidebar > ul > li > a, +.sidebar > ul > li > span, +.sidebar > ul > li > ul > li > a, +.sidebar > ul > li > ul > li > span, +.sidebar > ul > li > ul > li > ul > li > a, +.sidebar > ul > li > ul > li > ul > li > span { + display: block; + margin: 0 0 -1px; + font-size: 14px; + padding: 3px; + text-indent: 10px; + text-decoration: none; + border: 1px solid #e5e5e5; } -.sidebar>ul>li:last-child { - border-bottom: 1px solid #ccc; +.sidebar > ul > li > ul > li > a , +.sidebar > ul > li > ul > li > span { + padding-left: 15px; +} +.sidebar > ul > li > ul > li > ul > li > a , +.sidebar > ul > li > ul > li > ul > li > span { + padding-left: 25px; +} + +.sidebar > ul > li:first-child > a, +.sidebar > ul > li:first-child > span { + -webkit-border-radius: 6px 6px 0 0; + -moz-border-radius: 6px 6px 0 0; + border-radius: 6px 6px 0 0; +} +.sidebar ul li:last-child > a, +.sidebar ul li:last-child > span { + -webkit-border-radius: 0 0 6px 6px; + -moz-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; } +/* .sidebar>ul>li:last-child { + border-bottom: 1px solid #ccc; +} */ -.sidebar ul li ul li { + +/* .sidebar > ul > li > ul > li > a, +.sidebar > ul > li > ul > li > span { + margin: 0; + padding: 0; + border-style: none; + list-style-position: inside; + padding-left: 10px; +} */ +/* .sidebar ul li ul li { margin: 0; padding: 0; border-style: none; list-style-position: inside; padding-left: 10px; -} +} */ -.sidebar ul li ul li a { +/* .sidebar ul li ul li a { display: inline; -} - -.sidebar ul li ul { +} */ +/* padding-left: 20px; */ +/* .sidebar ul li ul { margin: 0; border-bottom: none; -} +} */ -.sidebar ul li ul li span { +/* .sidebar ul li ul li span { display: inline; padding: 0px; -} - -.sidebar ul li a { - display: block; - padding: 3px; - text-indent: 10px; - text-decoration: none; -} +} */ -.sidebar ul li span { +/* .sidebar ul li span { display: block; padding: 3px; text-indent: 10px; text-decoration: none; -} +} */ -.sidebar ul li a:hover { +.sidebar ul li a:hover, +.sidebar ul li span:hover { background-color: #eee; } diff --git a/combo/example/src/main/webapp/templates-hidden/_embeddedTopNavbar.html b/combo/example/src/main/webapp/templates-hidden/_embeddedTopNavbar.html new file mode 100644 index 0000000..87aacfc --- /dev/null +++ b/combo/example/src/main/webapp/templates-hidden/_embeddedTopNavbar.html @@ -0,0 +1,26 @@ + + + + + +Embedded Topbar template + + + + + + + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/templates-hidden/_resources.html b/combo/example/src/main/webapp/templates-hidden/_resources.html index 222f553..1fae614 100644 --- a/combo/example/src/main/webapp/templates-hidden/_resources.html +++ b/combo/example/src/main/webapp/templates-hidden/_resources.html @@ -1,23 +1,29 @@ Home - Persistence + Persistence - Interactive Stuff + Interactive Stuff - Templating - Web Services + Templating + Web Services - Localization + Localization - Menus + Menus - Wiki HomePage - Wiki all - Wiki edit HomePage + Wiki HomePage + Wiki all + Wiki edit HomePage Misc code - Source code for this site - Lift project home + Source code for this site + Lift project home + + Interactive Stuff + Persistence + Templating + Menus + Misc code diff --git a/combo/example/src/main/webapp/templates-hidden/default.html b/combo/example/src/main/webapp/templates-hidden/default.html index 56a34d4..2a80d42 100644 --- a/combo/example/src/main/webapp/templates-hidden/default.html +++ b/combo/example/src/main/webapp/templates-hidden/default.html @@ -16,7 +16,7 @@ - + @@ -24,23 +24,22 @@
    + Embedding the TopNavbar here
    -
    - Lift Web Framework -
    -
    - +
    + Lift Web Framework +
    -
    +

    -
    +
    -
    -
    The main content will get bound here
    +
    +
    The main content will get bound here
    From 46a7563b6abf7ec0271c1cd4f5fd6c05c7ad75eb Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Sun, 30 Dec 2018 14:54:19 +0100 Subject: [PATCH 26/33] Some cleanup --- combo/example/src/main/webapp/file_upload.html | 7 +++++++ combo/example/src/main/webapp/index.html | 8 ++++---- combo/example/src/main/webapp/templating/eval_order.html | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/combo/example/src/main/webapp/file_upload.html b/combo/example/src/main/webapp/file_upload.html index fd1cb70..102bf50 100644 --- a/combo/example/src/main/webapp/file_upload.html +++ b/combo/example/src/main/webapp/file_upload.html @@ -43,6 +43,13 @@

    %*%

    + +
    The Markup
    <form data-lift="misc.upload?form=post&multipart=true">
    diff --git a/combo/example/src/main/webapp/index.html b/combo/example/src/main/webapp/index.html
    index 25d156f..9bee54c 100644
    --- a/combo/example/src/main/webapp/index.html
    +++ b/combo/example/src/main/webapp/index.html
    @@ -19,23 +19,23 @@ 

    Welcome to the
    Lift provides the best features for building interactive web applications:
      -
    • +
    • Super simple and wicked powerful Ajax and Comet coding. This lets you build more interactive, user-friendly sites.
    • -
    • +
    • Amazingly concise code with the powerful type-safety of Scala. This means more time spent coding features and less time writing tests or chasing parameter mis-matches.
    • -
    • +
    • Runs on all standard JEE (Java) application servers including Jetty, Tomcat, WebLogic, etc. This means you get the performance, scalability and compatibility of the best web infrastructure around.
    • -
    • +
    • Built-in security means more time focusing on your application and less time being defensive about parameter tampering, SQL injection, Cross Site Scripting and other nasty attacks. diff --git a/combo/example/src/main/webapp/templating/eval_order.html b/combo/example/src/main/webapp/templating/eval_order.html index fb2919d..56784e4 100644 --- a/combo/example/src/main/webapp/templating/eval_order.html +++ b/combo/example/src/main/webapp/templating/eval_order.html @@ -17,7 +17,7 @@

      %*%

    - Lift evaluates the <tagName data-lift="xxx"/> <lift:xxx/> tags from the outside + Lift evaluates the <tagName data-lift="xxx"/> tags from the outside in, otherwise knows as lazy evaluation. This means that the following code will only embed a single template:

    From a1edaed49b22271ca04e5b986bd0a9f5ceb0e66e Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Sun, 30 Dec 2018 15:30:22 +0100 Subject: [PATCH 27/33] Some cleanup --- combo/example/build.sbt | 2 +- combo/example/geronimo-web.xml | 2 +- combo/example/project/plugins.sbt | 3 +- .../src/main/webapp/assets/css/app.css | 44 +------------------ 4 files changed, 5 insertions(+), 46 deletions(-) diff --git a/combo/example/build.sbt b/combo/example/build.sbt index f8a84f7..df2365e 100644 --- a/combo/example/build.sbt +++ b/combo/example/build.sbt @@ -6,7 +6,7 @@ lazy val projectSettings = Seq( organization := "net.liftweb", - version := "0.9.0-SNAPSHOT", + version := "0.9.1-SNAPSHOT", name := "demo", scalaVersion := "2.12.7", scalacOptions ++= Seq("-unchecked", "-deprecation"), diff --git a/combo/example/geronimo-web.xml b/combo/example/geronimo-web.xml index 3772f66..07fcdc6 100644 --- a/combo/example/geronimo-web.xml +++ b/combo/example/geronimo-web.xml @@ -7,7 +7,7 @@ net.liftweb demo - 0.9.0-SNAPSHOT + 0.9.1-SNAPSHOT diff --git a/combo/example/project/plugins.sbt b/combo/example/project/plugins.sbt index ec4312c..6cc81ca 100644 --- a/combo/example/project/plugins.sbt +++ b/combo/example/project/plugins.sbt @@ -1,3 +1,4 @@ addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "4.0.1") addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.2") -addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.9.0") \ No newline at end of file +addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.9.0") +addSbtPlugin("org.ensime" % "sbt-ensime" % "2.5.1") diff --git a/combo/example/src/main/webapp/assets/css/app.css b/combo/example/src/main/webapp/assets/css/app.css index bb0d5a7..965addd 100644 --- a/combo/example/src/main/webapp/assets/css/app.css +++ b/combo/example/src/main/webapp/assets/css/app.css @@ -4,9 +4,8 @@ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 -1px 0 rgba(0, 0, 0, 0.1) inset; } .dropdown-menu { - /* color: inherit; */ background-color: inherit; - border: none; /*1px solid rgba(0, 0, 0, 0.15);*/ + border: none; } .widget { @@ -77,47 +76,6 @@ -moz-border-radius: 0 0 6px 6px; border-radius: 0 0 6px 6px; } -/* .sidebar>ul>li:last-child { - border-bottom: 1px solid #ccc; -} */ - - -/* .sidebar > ul > li > ul > li > a, -.sidebar > ul > li > ul > li > span { - margin: 0; - padding: 0; - border-style: none; - list-style-position: inside; - padding-left: 10px; -} */ -/* .sidebar ul li ul li { - margin: 0; - padding: 0; - border-style: none; - list-style-position: inside; - padding-left: 10px; -} */ - -/* .sidebar ul li ul li a { - display: inline; -} */ -/* padding-left: 20px; */ -/* .sidebar ul li ul { - margin: 0; - border-bottom: none; -} */ - -/* .sidebar ul li ul li span { - display: inline; - padding: 0px; -} */ - -/* .sidebar ul li span { - display: block; - padding: 3px; - text-indent: 10px; - text-decoration: none; -} */ .sidebar ul li a:hover, .sidebar ul li span:hover { From 7945dd41b88a803e9334eb0914696448504c5ca6 Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Tue, 1 Jan 2019 14:45:13 +0100 Subject: [PATCH 28/33] Adding BSLocInfo to MenuInfo --- combo/example/build.sbt | 4 +-- combo/example/geronimo-web.xml | 2 +- .../main/scala/bootstrap/liftweb/Boot.scala | 30 +++++++++++++------ .../src/main/webapp/assets/css/app.css | 5 ++++ 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/combo/example/build.sbt b/combo/example/build.sbt index df2365e..c44a55b 100644 --- a/combo/example/build.sbt +++ b/combo/example/build.sbt @@ -6,7 +6,7 @@ lazy val projectSettings = Seq( organization := "net.liftweb", - version := "0.9.1-SNAPSHOT", + version := "0.9.2-SNAPSHOT", name := "demo", scalaVersion := "2.12.7", scalacOptions ++= Seq("-unchecked", "-deprecation"), @@ -61,7 +61,7 @@ libraryDependencies ++= { "net.liftweb" %% "lift-db" % liftVersion, "net.liftweb" %% "lift-mapper" % liftVersion, // "net.liftmodules" %% "fobo_3.3" % "2.1.0", - "net.liftmodules" %% "fobo-twbs-bootstrap4_3.3" % "2.1.0", + "net.liftmodules" %% "fobo-twbs-bootstrap4_3.3" % "2.1.1-SNAPSHOT", "net.liftmodules" %% "fobo-popper_3.3" % "2.1.0", "net.liftmodules" %% "fobo-font-awesome_3.3" % "2.1.0", "net.liftmodules" %% "fobo-highlightjs_3.3" % "2.1.0", diff --git a/combo/example/geronimo-web.xml b/combo/example/geronimo-web.xml index 07fcdc6..1398fdf 100644 --- a/combo/example/geronimo-web.xml +++ b/combo/example/geronimo-web.xml @@ -7,7 +7,7 @@ net.liftweb demo - 0.9.1-SNAPSHOT + 0.9.2-SNAPSHOT diff --git a/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala b/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala index 0bad146..1f4e241 100644 --- a/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala @@ -189,6 +189,7 @@ object RequestLogger extends Loggable { object MenuInfo { import Loc._ + import net.liftmodules.fobobs4.lib.BSLocInfo private lazy val noGAE = Unless(() => Props.inGAE, "Disabled for GAE") private val topNavLG = LocGroup("topNav") @@ -264,12 +265,12 @@ object MenuInfo { Loc("lift", ExtLink("http://liftweb.net"), S.loc("lift", Lift project home), - topNavLG)), + topNavLG, BSLocInfo.LinkTargetBlank)), Menu( Loc("src", ExtLink("https://github.com/lift/examples/tree/master/combo/example"), S.loc("src", Text("Source code for this site")), - topNavLG)) + topNavLG, BSLocInfo.LinkTargetBlank)) ) private object TopNav { @@ -308,6 +309,7 @@ object MenuInfo { Link(List("topNavAjaxAndForms"), true, "/form_ajax"), S.loc("topNavAjaxAndForms", Text("Ajax and Forms"))) private val interactiveDD = Menu.i("topNavInteractiveDD") / "/dddlabel2" + private val interactiveDivider1 = Menu("interactiveDivider1") / "interactiveDivider1" private val interactive = Menu(interactiveLoc) private val cometChat = Menu(cometChatLoc) private val ajaxSamples = Menu(ajaxSamplesLoc) @@ -338,6 +340,7 @@ object MenuInfo { S.loc("topNavSimple", Text("Simple Forms")), noGAE) private val persistenceDD = Menu.i("topNavPersistenceDD") / "/ddlabel3" >> noGAE + private val persistenceDivider1 = Menu("persistenceDivider1") / "persistenceDivider1" private val persistence = Menu(persistenceLoc) private val xMLFun = Menu(xMLFunLoc) private val database = Menu(databaseLoc) @@ -391,6 +394,7 @@ object MenuInfo { S.loc("topNavHeadTagLoc", Text(" tag")) ) private val templatingDD = Menu.i("topNavTemplatingDD") / "/ddlabel4" + private val templatingDivider1 = Menu("templatingDivider1") / "templatingDivider1" private val templating = Menu(teplatingLoc) private val surround = Menu(surroundLoc) private val embed = Menu(embedLoc) @@ -438,6 +442,7 @@ object MenuInfo { S.loc("topNavForthSubmenu", Text("Forth Submenu")) ) private val menusDD = Menu.i("topNavMenusDD") / "/ddlabel5" + private val menusDivider1 = Menu("menusDivider1") / "menusDivider1" private val menus = Menu(menusLoc) private val firstSubmenu = Menu(firstSubmenuLoc) private val secondSubmenu = Menu(secondSubmenuLoc) @@ -518,7 +523,9 @@ object MenuInfo { S.loc("topNavCounting", Text("Counting")) ) private val miscDD = Menu.i("topNavMiscDD") / "/ddlabel6" + private val miscDivider1 =Menu("miscDivider1") / "miscDivider1" private val misc = Menu(miscLoc) + private val longTime = Menu(longTimeLoc) private val numberGuessing = Menu(numberGuessingLoc) private val wizard = Menu(wizardLoc) @@ -534,21 +541,26 @@ object MenuInfo { // Public stuff val interactiveMenuPart = interactiveDD >> topNavLG >> PlaceHolder submenus (interactive, - cometChat, ajaxSamples, ajaxForm, modalDialog, jSONMessaging, statelessJSONMessaging, ajaxAndForms) + interactiveDivider1 >> topNavLG >> BSLocInfo.Divider, + cometChat, ajaxSamples, ajaxForm, modalDialog, jSONMessaging, statelessJSONMessaging, ajaxAndForms) val persistenceMenuPart = persistenceDD >> topNavLG >> PlaceHolder submenus (persistence, - xMLFun, database, simple) + persistenceDivider1 >> topNavLG >> BSLocInfo.Divider, + xMLFun, database, simple) val templatingMenuPart = templatingDD >> topNavLG >> PlaceHolder submenus (templating, - surround, embed, evaluationOrder, selectDivs, simpleWizard, lazyLoading, parallelSnippets, headTag) + templatingDivider1 >> topNavLG >> BSLocInfo.Divider, + surround, embed, evaluationOrder, selectDivs, simpleWizard, lazyLoading, parallelSnippets, headTag) // Note: The bootstrap navigator dose only handle one level of sub-menus val menusMenuPart = menusDD >> topNavLG >> PlaceHolder submenus (menus, - firstSubmenu, secondSubmenu, first2Submenu, second2Submenu, thirdSubmenu, forthSubmenu) + menusDivider1 >> topNavLG >> BSLocInfo.Divider, + firstSubmenu, secondSubmenu, first2Submenu, second2Submenu, thirdSubmenu, forthSubmenu) - val miscMenuPart = miscDD >> topNavLG >> PlaceHolder submenus (misc, longTime, numberGuessing, - wizard, simpleScreen, variableScreen, arcChallenge, simpleWiring, wiringInvoice, fileUpload, asyncRest, - login, counting) + val miscMenuPart = miscDD >> topNavLG >> PlaceHolder submenus (misc, + miscDivider1 >> BSLocInfo.Divider, + longTime, numberGuessing, wizard, simpleScreen, variableScreen, arcChallenge, simpleWiring, + wiringInvoice, fileUpload, asyncRest, login, counting) } def sitemap() = SiteMap(siteMapList: _*) diff --git a/combo/example/src/main/webapp/assets/css/app.css b/combo/example/src/main/webapp/assets/css/app.css index 965addd..28296f7 100644 --- a/combo/example/src/main/webapp/assets/css/app.css +++ b/combo/example/src/main/webapp/assets/css/app.css @@ -1,3 +1,4 @@ + .navbar { background-color: #e9ecef; background-image: linear-gradient( #e9ecef, #eff4fa); @@ -8,6 +9,10 @@ border: none; } +.dropdown-divider { + border-top: 1px solid #fff; +} + .widget { border: 1px solid #ccc; background: #f5f5f5; From 88b469cdb8c576732232f41ddf8f47b32f68ac7f Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Thu, 3 Jan 2019 20:47:00 +0100 Subject: [PATCH 29/33] updating dependencies --- combo/example/build.sbt | 14 +++++++------- combo/example/geronimo-web.xml | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/combo/example/build.sbt b/combo/example/build.sbt index c44a55b..ceef18c 100644 --- a/combo/example/build.sbt +++ b/combo/example/build.sbt @@ -6,7 +6,7 @@ lazy val projectSettings = Seq( organization := "net.liftweb", - version := "0.9.2-SNAPSHOT", + version := "0.9.3-SNAPSHOT", name := "demo", scalaVersion := "2.12.7", scalacOptions ++= Seq("-unchecked", "-deprecation"), @@ -60,12 +60,12 @@ libraryDependencies ++= { "net.liftweb" %% "lift-json" % liftVersion, "net.liftweb" %% "lift-db" % liftVersion, "net.liftweb" %% "lift-mapper" % liftVersion, - // "net.liftmodules" %% "fobo_3.3" % "2.1.0", - "net.liftmodules" %% "fobo-twbs-bootstrap4_3.3" % "2.1.1-SNAPSHOT", - "net.liftmodules" %% "fobo-popper_3.3" % "2.1.0", - "net.liftmodules" %% "fobo-font-awesome_3.3" % "2.1.0", - "net.liftmodules" %% "fobo-highlightjs_3.3" % "2.1.0", - "net.liftmodules" %% "fobo-jquery_3.3" % "2.1.0", + // "net.liftmodules" %% "fobo_3.3" % "2.1.1", + "net.liftmodules" %% "fobo-twbs-bootstrap4_3.3" % "2.1.1", + "net.liftmodules" %% "fobo-popper_3.3" % "2.1.1", + "net.liftmodules" %% "fobo-font-awesome_3.3" % "2.1.1", + "net.liftmodules" %% "fobo-highlightjs_3.3" % "2.1.1", + "net.liftmodules" %% "fobo-jquery_3.3" % "2.1.1", "net.liftmodules" %% "widgets_3.1" % "1.6.0-SNAPSHOT", "net.liftmodules" %% "textile_3.1" % "1.4-SNAPSHOT", "org.eclipse.jetty" % "jetty-webapp" % "8.1.7.v20120910" % "test", diff --git a/combo/example/geronimo-web.xml b/combo/example/geronimo-web.xml index 1398fdf..25f1515 100644 --- a/combo/example/geronimo-web.xml +++ b/combo/example/geronimo-web.xml @@ -7,7 +7,7 @@ net.liftweb demo - 0.9.2-SNAPSHOT + 0.9.3-SNAPSHOT From fd2b748e85d88e3ff1ec01b4f8d7d76ccc4b0c19 Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Sat, 5 Jan 2019 16:43:01 +0100 Subject: [PATCH 30/33] Using webjars to get resources The FoBo module usage has been reduced to only load it's bootstrap 4 API. --- combo/example/build.sbt | 16 +++++---- .../main/scala/bootstrap/liftweb/Boot.scala | 9 ++--- combo/example/src/main/webapp/ajax.html | 2 +- .../main/webapp/templates-hidden/default.html | 34 +++++++++++-------- 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/combo/example/build.sbt b/combo/example/build.sbt index ceef18c..7b6edb5 100644 --- a/combo/example/build.sbt +++ b/combo/example/build.sbt @@ -60,14 +60,18 @@ libraryDependencies ++= { "net.liftweb" %% "lift-json" % liftVersion, "net.liftweb" %% "lift-db" % liftVersion, "net.liftweb" %% "lift-mapper" % liftVersion, - // "net.liftmodules" %% "fobo_3.3" % "2.1.1", - "net.liftmodules" %% "fobo-twbs-bootstrap4_3.3" % "2.1.1", - "net.liftmodules" %% "fobo-popper_3.3" % "2.1.1", - "net.liftmodules" %% "fobo-font-awesome_3.3" % "2.1.1", - "net.liftmodules" %% "fobo-highlightjs_3.3" % "2.1.1", - "net.liftmodules" %% "fobo-jquery_3.3" % "2.1.1", + + "net.liftmodules" %% "fobo-twbs-bootstrap4-api_3.3" % "2.1.1", "net.liftmodules" %% "widgets_3.1" % "1.6.0-SNAPSHOT", "net.liftmodules" %% "textile_3.1" % "1.4-SNAPSHOT", + + "org.webjars" % "bootstrap" % "4.2.1", + "org.webjars" % "jquery" % "3.0.0", + "org.webjars" % "jquery-migrate" % "1.4.1", + "org.webjars" % "popper.js" % "1.14.3", + "org.webjars" % "font-awesome" % "5.6.1", + "org.webjars" % "highlightjs" % "9.6.0", + "org.eclipse.jetty" % "jetty-webapp" % "8.1.7.v20120910" % "test", "junit" % "junit" % "4.10" % "test", "ch.qos.logback" % "logback-classic" % "1.2.3", diff --git a/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala b/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala index 1f4e241..6c4485e 100644 --- a/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala @@ -25,7 +25,7 @@ import sitemap._ import Helpers._ import example._ import net.liftmodules.widgets.autocomplete._ -import net.liftmodules.{fobobs4, fobofa, fobohl, fobojq, fobopop} +import net.liftmodules.{fobobs4api => fobo} import comet._ import model._ import lib._ @@ -144,12 +144,7 @@ class Boot { } // FoBo init - fobojq.Toolkit.init = fobojq.Toolkit.JQuery224 - fobohl.Toolkit.init = fobohl.Toolkit.HighlightJS930 - fobofa.Toolkit.init = fobofa.Toolkit.FontAwesome550 - fobobs4.Toolkit.init = fobobs4.Toolkit.Bootstrap413 - fobopop.Toolkit.init = fobopop.Toolkit.Popper1129 - fobojq.Toolkit.init = fobojq.Toolkit.JQueryMigrate141 + fobo.API.init = fobo.API.Bootstrap4 ThingBuilder.boot() diff --git a/combo/example/src/main/webapp/ajax.html b/combo/example/src/main/webapp/ajax.html index df35a8f..dca03ed 100644 --- a/combo/example/src/main/webapp/ajax.html +++ b/combo/example/src/main/webapp/ajax.html @@ -12,7 +12,7 @@ - +

    %*%

    diff --git a/combo/example/src/main/webapp/templates-hidden/default.html b/combo/example/src/main/webapp/templates-hidden/default.html index 2a80d42..ec6d342 100644 --- a/combo/example/src/main/webapp/templates-hidden/default.html +++ b/combo/example/src/main/webapp/templates-hidden/default.html @@ -1,25 +1,26 @@ - - - - + + + + - Lift Web Framework: %*% + Lift Web Framework: %*% - - + + - + + - + + + - - - + - + @@ -105,8 +106,11 @@

    - - + + + + \ No newline at end of file From 81d7c0f888d30b5102cd18d2513e646adc1a92c2 Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Sat, 5 Jan 2019 20:04:47 +0100 Subject: [PATCH 31/33] Adjusting webjar versions. --- combo/example/build.sbt | 4 ++-- combo/example/src/main/webapp/templates-hidden/default.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/combo/example/build.sbt b/combo/example/build.sbt index 7b6edb5..55701c3 100644 --- a/combo/example/build.sbt +++ b/combo/example/build.sbt @@ -66,9 +66,9 @@ libraryDependencies ++= { "net.liftmodules" %% "textile_3.1" % "1.4-SNAPSHOT", "org.webjars" % "bootstrap" % "4.2.1", - "org.webjars" % "jquery" % "3.0.0", + "org.webjars" % "jquery" % "3.3.1", "org.webjars" % "jquery-migrate" % "1.4.1", - "org.webjars" % "popper.js" % "1.14.3", + "org.webjars" % "popper.js" % "1.14.6", "org.webjars" % "font-awesome" % "5.6.1", "org.webjars" % "highlightjs" % "9.6.0", diff --git a/combo/example/src/main/webapp/templates-hidden/default.html b/combo/example/src/main/webapp/templates-hidden/default.html index ec6d342..c5ccc7b 100644 --- a/combo/example/src/main/webapp/templates-hidden/default.html +++ b/combo/example/src/main/webapp/templates-hidden/default.html @@ -12,7 +12,7 @@ - + @@ -109,7 +109,7 @@
    - + From 7c50795f6707791f110c00c109402de5e5e688f5 Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Sat, 5 Jan 2019 20:04:47 +0100 Subject: [PATCH 32/33] Adjusting webjar versions. --- combo/example/build.sbt | 4 ++-- combo/example/{ => src/main/webapp/WEB-INF}/geronimo-web.xml | 0 combo/example/src/main/webapp/templates-hidden/default.html | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename combo/example/{ => src/main/webapp/WEB-INF}/geronimo-web.xml (100%) diff --git a/combo/example/build.sbt b/combo/example/build.sbt index 7b6edb5..55701c3 100644 --- a/combo/example/build.sbt +++ b/combo/example/build.sbt @@ -66,9 +66,9 @@ libraryDependencies ++= { "net.liftmodules" %% "textile_3.1" % "1.4-SNAPSHOT", "org.webjars" % "bootstrap" % "4.2.1", - "org.webjars" % "jquery" % "3.0.0", + "org.webjars" % "jquery" % "3.3.1", "org.webjars" % "jquery-migrate" % "1.4.1", - "org.webjars" % "popper.js" % "1.14.3", + "org.webjars" % "popper.js" % "1.14.6", "org.webjars" % "font-awesome" % "5.6.1", "org.webjars" % "highlightjs" % "9.6.0", diff --git a/combo/example/geronimo-web.xml b/combo/example/src/main/webapp/WEB-INF/geronimo-web.xml similarity index 100% rename from combo/example/geronimo-web.xml rename to combo/example/src/main/webapp/WEB-INF/geronimo-web.xml diff --git a/combo/example/src/main/webapp/templates-hidden/default.html b/combo/example/src/main/webapp/templates-hidden/default.html index ec6d342..c5ccc7b 100644 --- a/combo/example/src/main/webapp/templates-hidden/default.html +++ b/combo/example/src/main/webapp/templates-hidden/default.html @@ -12,7 +12,7 @@ - + @@ -109,7 +109,7 @@
    - + From 943d8d8a3a3c18c4e144665e86ed5de6398382e1 Mon Sep 17 00:00:00 2001 From: karma4u101 Date: Sun, 10 Feb 2019 13:22:28 +0100 Subject: [PATCH 33/33] Cleanup Cleaner nav css and formating cleanup. --- combo/example/build.sbt | 2 +- .../src/main/webapp/WEB-INF/geronimo-web.xml | 2 +- .../src/main/webapp/assets/css/app.css | 7 +++--- .../templates-hidden/_embeddedTopNavbar.html | 2 +- .../src/main/webapp/templating/index.html | 24 +++++++++---------- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/combo/example/build.sbt b/combo/example/build.sbt index 55701c3..7b9618a 100644 --- a/combo/example/build.sbt +++ b/combo/example/build.sbt @@ -6,7 +6,7 @@ lazy val projectSettings = Seq( organization := "net.liftweb", - version := "0.9.3-SNAPSHOT", + version := "0.9.4-SNAPSHOT", name := "demo", scalaVersion := "2.12.7", scalacOptions ++= Seq("-unchecked", "-deprecation"), diff --git a/combo/example/src/main/webapp/WEB-INF/geronimo-web.xml b/combo/example/src/main/webapp/WEB-INF/geronimo-web.xml index 25f1515..46ca12c 100644 --- a/combo/example/src/main/webapp/WEB-INF/geronimo-web.xml +++ b/combo/example/src/main/webapp/WEB-INF/geronimo-web.xml @@ -7,7 +7,7 @@ net.liftweb demo - 0.9.3-SNAPSHOT + 0.9.4-SNAPSHOT diff --git a/combo/example/src/main/webapp/assets/css/app.css b/combo/example/src/main/webapp/assets/css/app.css index 28296f7..1be8667 100644 --- a/combo/example/src/main/webapp/assets/css/app.css +++ b/combo/example/src/main/webapp/assets/css/app.css @@ -1,10 +1,9 @@ .navbar { - background-color: #e9ecef; - background-image: linear-gradient( #e9ecef, #eff4fa); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 -1px 0 rgba(0, 0, 0, 0.1) inset; + background-color: #fff; + box-shadow: 0px 1px 1px 0px rgba(0,0,0,0.25); } -.dropdown-menu { +.navbar .dropdown-menu { background-color: inherit; border: none; } diff --git a/combo/example/src/main/webapp/templates-hidden/_embeddedTopNavbar.html b/combo/example/src/main/webapp/templates-hidden/_embeddedTopNavbar.html index 87aacfc..bfd9c12 100644 --- a/combo/example/src/main/webapp/templates-hidden/_embeddedTopNavbar.html +++ b/combo/example/src/main/webapp/templates-hidden/_embeddedTopNavbar.html @@ -9,7 +9,7 @@ -