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/build.sbt b/combo/example/build.sbt new file mode 100644 index 0000000..7b9618a --- /dev/null +++ b/combo/example/build.sbt @@ -0,0 +1,87 @@ +//################################################################## +//## +//## Build settings +//## +//############## + +lazy val projectSettings = Seq( + organization := "net.liftweb", + version := "0.9.4-SNAPSHOT", + name := "demo", + scalaVersion := "2.12.7", + scalacOptions ++= Seq("-unchecked", "-deprecation"), + autoAPIMappings := true +) + +lazy val meta = (project in file(".")) + .enablePlugins(JettyPlugin) + .enablePlugins(BuildInfoPlugin) + .settings(projectSettings: _*) + .settings( + buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion), + buildInfoKeys ++= Seq[BuildInfoKey]( + BuildInfoKey.action("buildTime") { + System.currentTimeMillis + } // re-computed each time at compile + ), + buildInfoPackage := "net.liftweb.example.lib" + ) + +//## +//## +//################################################################## + +//################################################################## +//## +//## Resolvers +//## +//############## + + +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" +) + +//## +//## +//################################################################## + +//################################################################## +//## +//## Dependencies +//## +//############## + +libraryDependencies ++= { + val liftVersion = "3.3.0" + Seq( + "net.liftweb" %% "lift-webkit" % liftVersion, + "net.liftweb" %% "lift-json" % liftVersion, + "net.liftweb" %% "lift-db" % liftVersion, + "net.liftweb" %% "lift-mapper" % liftVersion, + + "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.3.1", + "org.webjars" % "jquery-migrate" % "1.4.1", + "org.webjars" % "popper.js" % "1.14.6", + "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", + "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..c46277f --- /dev/null +++ b/combo/example/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.2.7 \ 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..6cc81ca --- /dev/null +++ b/combo/example/project/plugins.sbt @@ -0,0 +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") +addSbtPlugin("org.ensime" % "sbt-ensime" % "2.5.1") diff --git a/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala b/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala index 1637964..6c4485e 100644 --- a/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/combo/example/src/main/scala/bootstrap/liftweb/Boot.scala @@ -16,40 +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.{fobobs4api => fobo} import comet._ import model._ import lib._ -import net.liftweb.mapper.{DB, ConnectionManager, Schemifier, DefaultConnectionIdentifier, ConnectionIdentifier} +import net.liftweb.mapper.{ + 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 @@ -65,29 +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")) }) - LiftRules.snippetDispatch.append(NamedPF("Template") - (Map("Template" -> Template, - "AllJson" -> AllJson))) - - /* - LiftRules.snippetDispatch.append { - case "MyWizard" => MyWizard - case "WizardChallenge" => WizardChallenge - case "ScreenForm" => PersonScreen - } - */ - SessionMaster.sessionCheckFuncs = SessionMaster.sessionCheckFuncs ::: - List(SessionChecker) + List(SessionChecker) // Uncomment the lines below to see how // a Lift app looks when it's stateless @@ -99,37 +98,53 @@ 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) => { + notices match { + case NoticeType.Notice => Full((8 seconds, 4 seconds)) + case _ => Empty + } + }) + LiftSession.onBeginServicing = RequestLogger.beginServicing _ :: - LiftSession.onBeginServicing + LiftSession.onBeginServicing LiftSession.onEndServicing = RequestLogger.endServicing _ :: - LiftSession.onEndServicing + 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) + ))) + } - LiftRules.setSiteMapFunc(MenuInfo.sitemap) + // FoBo init + fobo.API.init = fobo.API.Bootstrap4 ThingBuilder.boot() @@ -137,14 +152,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 { @@ -154,37 +171,50 @@ 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 " + (timeNow) + " 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._ + import net.liftmodules.fobobs4.lib.BSLocInfo - 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("Home") / "index", - Menu("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", Menu("Modal Dialog") / "rhodeisland", Menu("JSON Messaging") / "json", Menu("Stateless JSON Messaging") / "stateless_json", - Menu("More JSON") / "json_more", - Menu("Ajax and Forms") / "form_ajax") , - Menu("Persistence") / "persistence" >> noGAE submenus ( + // Menu("More JSON") / "json_more", + 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("Templates") / "template" >> noGAE), - Menu("Templating") / "templating" / "index" submenus( + Menu( + Loc("simple", + Link(List("simple"), true, "/simple/index"), + "Simple Forms", + noGAE)) //, + // Menu("Templates") / "template" >> noGAE + ), + TopNav.templatingMenuPart, + Menu.i("Templating") / "templating" / "index" submenus ( Menu("Surround") / "templating" / "surround", Menu("Embed") / "templating" / "embed", Menu("Evalutation Order") / "templating" / "eval_order", @@ -192,18 +222,23 @@ object MenuInfo { Menu("Simple Wizard") / "simple_wizard", 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(" 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("Misc code") / "misc" submenus( + TopNav.miscMenuPart, + Menu.i("Misc code") / "misc" submenus ( Menu("Long Time") / "longtime", Menu("Number Guessing") / "guess", Menu("Wizard") / "wiz", @@ -215,18 +250,320 @@ 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"), - Lift project home)), - Menu(Loc("src", ExtLink("https://github.com/lift/examples/tree/master/combo/example"), - "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, 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, BSLocInfo.LinkTargetBlank)) + ) + + 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 interactiveDivider1 = Menu("interactiveDivider1") / "interactiveDivider1" + 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 persistenceDivider1 = Menu("persistenceDivider1") / "persistenceDivider1" + 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 templatingDivider1 = Menu("templatingDivider1") / "templatingDivider1" + 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 menusDivider1 = Menu("menusDivider1") / "menusDivider1" + 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 miscDivider1 =Menu("miscDivider1") / "miscDivider1" + 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, + interactiveDivider1 >> topNavLG >> BSLocInfo.Divider, + cometChat, ajaxSamples, ajaxForm, modalDialog, jSONMessaging, statelessJSONMessaging, ajaxAndForms) + + val persistenceMenuPart = persistenceDD >> topNavLG >> PlaceHolder submenus (persistence, + persistenceDivider1 >> topNavLG >> BSLocInfo.Divider, + xMLFun, database, simple) + + val templatingMenuPart = templatingDD >> topNavLG >> PlaceHolder submenus (templating, + 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, + menusDivider1 >> topNavLG >> BSLocInfo.Divider, + firstSubmenu, secondSubmenu, first2Submenu, second2Submenu, thirdSubmenu, forthSubmenu) + + 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: _*) } /** -* Database connection calculation -*/ + * Database connection calculation + */ object DBVendor extends ConnectionManager { private var pool: List[Connection] = Nil private var poolSize = 0 @@ -234,60 +571,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 + 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 dbUrl: String = Props.get("db.url") openOr chooseURL - - - Class.forName(driverName) - - 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) + } } - } } } @@ -302,7 +637,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) } } @@ -311,50 +648,50 @@ object BrowserLogger extends Loggable { object SessionInfoDumper extends LiftActor with Loggable { private var lastTime = millis - private def cyclePeriod = 1 minute + private def cyclePeriod = 10 minute 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 = timeNow - 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 = timeNow.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/comet/AskName.scala b/combo/example/src/main/scala/net/liftweb/example/comet/AskName.scala index 4692da4..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,24 +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)) ++ - ) -} - -} -} +
+
+ {text("",name => answer(name.trim), "class" -> "form-control form-control-sm")} +
+
+ +
+
) } 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..e49b94f 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,101 @@ */ 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 http._ + import common._ + import util._ + import Helpers._ + import _root_.scala.xml._ + import js._ + import JsCmds._ + 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): NodeSeq = { + ("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..7073433 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 @@ -15,52 +15,51 @@ */ package net.liftweb { -package example { -package comet { + package example { + package comet { -import _root_.net.liftweb._ -import http._ -import common._ -import actor._ -import util._ -import Helpers._ -import _root_.scala.xml.{NodeSeq, Text} -import net.liftmodules.textile.TextileParser -import _root_.java.util.Date + import http._ + import common._ + import actor._ + import util._ + import Helpers._ + import _root_.scala.xml.{NodeSeq, Text} + import net.liftmodules.textile.TextileParser + import _root_.java.util.Date -/** - * A chat server. It gets messages and returns them - */ - -object ChatServer extends LiftActor with ListenerManager { - private var chats: List[ChatLine] = List(ChatLine("System", Text("Welcome"), now)) + /** + * A chat server. It gets messages and returns them + */ + object ChatServer extends LiftActor with ListenerManager { + private var chats: List[ChatLine] = List( + ChatLine("System", Text("Welcome"), now)) - override def lowPriority = { - case ChatServerMsg(user, msg) if msg.length > 0 => - chats ::= ChatLine(user, toHtml(msg), timeNow) - chats = chats.take(50) - updateListeners() + override def lowPriority = { + case ChatServerMsg(user, msg) if msg.length > 0 => + chats ::= ChatLine(user, toHtml(msg), now) + chats = chats.take(50) + updateListeners() - case _ => - } + case _ => + } - def createUpdate = ChatServerUpdate(chats.take(15)) + def createUpdate = ChatServerUpdate(chats.take(15)) - /** - * Convert an incoming string into XHTML using Textile Markup - * - * @param msg the incoming string - * - * @return textile markup for the incoming string - */ - def toHtml(msg: String): NodeSeq = TextileParser.paraFixer(TextileParser.toHtml(msg, Empty)) + /** + * Convert an incoming string into XHTML using Textile Markup + * + * @param msg the incoming string + * + * @return textile markup for the incoming string + */ + def toHtml(msg: String): NodeSeq = + TextileParser.paraFixer(TextileParser.toHtml(msg, Empty)) -} + } -case class ChatLine(user: String, msg: NodeSeq, when: Date) -case class ChatServerMsg(user: String, msg: String) -case class ChatServerUpdate(msgs: List[ChatLine]) -} -} + case class ChatLine(user: String, msg: NodeSeq, when: Date) + case class ChatServerMsg(user: String, msg: String) + case class ChatServerUpdate(msgs: List[ChatLine]) + } + } } - 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..bd1f584 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 @@ -18,7 +18,6 @@ package net.liftweb package example package comet -import _root_.net.liftweb._ import http._ import common._ import util._ @@ -27,22 +26,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/comet/LongTime.scala b/combo/example/src/main/scala/net/liftweb/example/comet/LongTime.scala index 662be22..220e671 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,7 @@ * limitations under the License. */ -package net.liftweb -package example -package comet +package net.liftweb.example.comet import net.liftweb.actor._ import scala.xml.{NodeSeq, Text} @@ -90,9 +88,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/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/lib/LoginStuff.scala b/combo/example/src/main/scala/net/liftweb/example/lib/LoginStuff.scala index 2b04e8c..1a28ff1 100644 --- a/combo/example/src/main/scala/net/liftweb/example/lib/LoginStuff.scala +++ b/combo/example/src/main/scala/net/liftweb/example/lib/LoginStuff.scala @@ -15,14 +15,13 @@ */ package net.liftweb { -package example { -package lib { + package example { + package lib { -import _root_.net.liftweb._ -import http._ + import http._ -object LoginStuff extends SessionVar(false) + object LoginStuff extends SessionVar(false) -} -} + } + } } diff --git a/combo/example/src/main/scala/net/liftweb/example/lib/SessionChecker.scala b/combo/example/src/main/scala/net/liftweb/example/lib/SessionChecker.scala index a37b45b..45834e6 100644 --- a/combo/example/src/main/scala/net/liftweb/example/lib/SessionChecker.scala +++ b/combo/example/src/main/scala/net/liftweb/example/lib/SessionChecker.scala @@ -15,38 +15,37 @@ */ package net.liftweb { -package example { -package lib { + package example { + package lib { -import _root_.net.liftweb._ -import http._ -import util._ -import Helpers._ -import common._ + import http._ + import util._ + import Helpers._ + import common._ -object SessionChecker extends Function2[Map[String, SessionInfo], - SessionInfo => Unit, Unit] with Logger -{ - def defaultKillWhen = 180000L - // how long do we wait to kill single browsers - @volatile var killWhen = defaultKillWhen + object SessionChecker + extends Function2[Map[String, SessionInfo], SessionInfo => Unit, Unit] + with Logger { + def defaultKillWhen = 180000L + // how long do we wait to kill single browsers + @volatile var killWhen = defaultKillWhen - @volatile var killCnt = 1 + @volatile var killCnt = 1 - def apply(sessions: Map[String, SessionInfo], - destroyer: SessionInfo => Unit): Unit = { - val cutoff = millis - 180000L - - sessions.foreach { - case (name, si @ SessionInfo(session, agent, _, cnt, lastAccess)) => - if (cnt <= killCnt && lastAccess < cutoff) { - info("Purging "+agent) - destroyer(si) + def apply(sessions: Map[String, SessionInfo], + destroyer: SessionInfo => Unit): Unit = { + val cutoff = millis - 180000L + + sessions.foreach { + case (name, si @ SessionInfo(session, agent, _, cnt, lastAccess)) => + if (cnt <= killCnt && lastAccess < cutoff) { + info("Purging " + agent) + destroyer(si) + } + } } + } + } } } - -} -} -} 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..196645b 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 @@ -15,29 +15,28 @@ */ package net.liftweb { -package example { -package lib { + package example { + package lib { -import _root_.net.liftweb._ -import http._ -import common._ + import http._ + import common._ -import scala.xml._ + import scala.xml._ -/** - * Use Lift's templating without a session and without state - */ -object StatelessHtml { - private val fakeSession = new LiftSession("/", "fakeSession", Empty) + /** + * Use Lift's templating without a session and without state + */ + object StatelessHtml { + private val fakeSession = new LiftSession("/", "fakeSession", Empty) + + def render(req: Req)(): Box[LiftResponse] = { + val xml: Box[NodeSeq] = S.init(Full(req), fakeSession) { + S.runTemplate(List("stateless")) + } + xml.map(ns => XhtmlResponse(ns(0), Empty, Nil, Nil, 200, false)) + } + } - def render(req: Req)(): Box[LiftResponse] = { - val xml: Box[NodeSeq] = S.init(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/StatelessJson.scala b/combo/example/src/main/scala/net/liftweb/example/lib/StatelessJson.scala index 2545929..f6eaec3 100644 --- a/combo/example/src/main/scala/net/liftweb/example/lib/StatelessJson.scala +++ b/combo/example/src/main/scala/net/liftweb/example/lib/StatelessJson.scala @@ -14,11 +14,9 @@ * limitations under the License. */ -package net.liftweb { - package example { - package lib { +package net.liftweb.example.lib -import _root_.net.liftweb._ +import net.liftweb._ import http._ import js._ import JsCmds._ @@ -29,29 +27,36 @@ import net.liftmodules.textile._ import scala.xml._ /** - * Respond to JSON requests in a stateless dispatch - */ + * Respond to JSON requests in a stateless dispatch + */ object StatelessJson { def init() { // register the JSON handler - LiftRules.statelessDispatch.append{ - case r @ Req("stateless_json_call" :: Nil, _, PostRequest) => () => handleJson(r) + 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 => Problem... didn't handle JSON message {x} - })) -} - } - } + 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 => Problem... didn't handle JSON message {x} + } + )) } 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..fe34df4 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 @@ -15,92 +15,91 @@ */ package net.liftweb { -package example { -package lib { + package example { + package lib { -import _root_.net.liftweb._ -import http._ -import http.rest._ -import common._ -import json._ -import util._ -import _root_.net.liftweb.example.model._ + import http._ + import http.rest._ + import common._ + import json._ + import _root_.net.liftweb.example.model._ -object WebServices extends RestHelper { - // a JSON-able class that holds a User - case class UserInfo(firstName: String, lastName: String, - email: String) { - def toXml = - def toJson = Extraction.decompose(this) - } - - // a JSON-able class that holds all the users - case class AllUsers(users: List[UserInfo]) { - def toJson = Extraction.decompose(this) - def toXml = {users.map(_.toXml)} - } + def toJson = Extraction.decompose(this) + } - // define a REST handler for an XML request - serve { - case "webservices" :: "all_users" :: _ XmlGet _ => - AllUsers(User.findAll()).toXml - } + // a JSON-able class that holds all the users + case class AllUsers(users: List[UserInfo]) { + def toJson = Extraction.decompose(this) + def toXml = {users.map(_.toXml)} + } - // define a REST handler for a JSON reqest - serve { - case "webservices" :: "all_users" :: _ JsonGet _ => - AllUsers(User.findAll()).toJson - } + // define a REST handler for an XML request + serve { + case "webservices" :: "all_users" :: _ XmlGet _ => + AllUsers(User.findAll()).toXml + } + // define a REST handler for a JSON reqest + serve { + case "webservices" :: "all_users" :: _ JsonGet _ => + AllUsers(User.findAll()).toJson + } - /* - * While many on the Web use GET requests in this way, a client shouldn't - * be given the expectation of resource state change or creation - * through a GET. GET should be idempotent and safe. This doesn't mean - * that a service couldn't create or modify state as as result - * (e.g. logging, counting the number of requests, creating business - * objects). It's just that any such state-related operations should - * not be visible through GET. In the above example, it is implied - * that a client could send a GET request in order to create a user. - * - * AKA -- don't do it this way in the real world, this is an example - * of using Scala's guards - */ + /* + * While many on the Web use GET requests in this way, a client shouldn't + * be given the expectation of resource state change or creation + * through a GET. GET should be idempotent and safe. This doesn't mean + * that a service couldn't create or modify state as as result + * (e.g. logging, counting the number of requests, creating business + * objects). It's just that any such state-related operations should + * not be visible through GET. In the above example, it is implied + * that a client could send a GET request in order to create a user. + * + * AKA -- don't do it this way in the real world, this is an example + * of using Scala's guards + */ - serveJx[UserInfo] { - 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 - } - - // a couple of helpful conversion rules - implicit def userToInfo(u: User): UserInfo = - UserInfo(u.firstName, u.lastName, u.email) + serveJx[UserInfo] { + 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 + } - implicit def uLstToInfo(ul: List[User]): List[UserInfo] = - ul.map(userToInfo) + // a couple of helpful conversion rules + implicit def userToInfo(u: User): UserInfo = + UserInfo(u.firstName.get, u.lastName.get, u.email.get) - // extract the parameters, create a user - // return the appropriate response - 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) + implicit def uLstToInfo(ul: List[User]): List[UserInfo] = + ul.map(userToInfo) - S.param("password") foreach u.password.set + // extract the parameters, create a user + // return the appropriate response + 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) - u.saveMe - } -} + S.param("password") foreach u.password.set -} -} + u.saveMe + } + } + + } + } } 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..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 @@ -14,9 +14,7 @@ * limitations under the License. */ -package net.liftweb { -package example { -package lib { +package net.liftweb.example.lib import _root_.net.liftweb._ import net.liftmodules.textile._ @@ -34,25 +32,25 @@ import model._ import scala.xml.{Text, NodeSeq} /** - * A wiki location - * - * @param page - the name of the page - * @param edit - are we viewing or editing the page? - */ + * A wiki location + * + * @param page - the name of the page + * @param edit - are we viewing or editing the page? + */ case class WikiLoc(page: String, edit: Boolean) { /** - * Get the underly database record for this page - */ + * Get the underly database record for this page + */ lazy val record: WikiEntry = - WikiEntry.find(By(WikiEntry.name, page)) openOr - WikiEntry.create.name(page) + WikiEntry.find(By(WikiEntry.name, page)) openOr + WikiEntry.create.name(page) } /** - * The WikiStuff object that provides menu, URL rewriting, - * and snippet support for the page that displays wiki contents - */ + * The WikiStuff object that provides menu, URL rewriting, + * and snippet support for the page that displays wiki contents + */ object WikiStuff extends Loc[WikiLoc] { object AllLoc extends WikiLoc("all", false) @@ -62,79 +60,80 @@ 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")) + def params = + 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 /** - * Check for page-specific snippets and - * do appropriate dispatching - */ + * Check for page-specific snippets and + * do appropriate dispatching + */ override val snippets: SnippetTest = { - case ("wiki", Full(AllLoc)) => showAll _ - case ("wiki", Full(wp @ WikiLoc(_ , true))) => editRecord(wp.record) _ - case ("wiki", Full(wp @ WikiLoc(_ , false))) - if !wp.record.saved_? => editRecord(wp.record) _ + case ("wiki", Full(AllLoc)) => showAll _ + case ("wiki", Full(wp @ WikiLoc(_, true))) => editRecord(wp.record) _ + case ("wiki", Full(wp @ WikiLoc(_, false))) if !wp.record.saved_? => + editRecord(wp.record) _ case ("wiki", Full(wp: WikiLoc)) => displayRecord(wp.record) _ } - /** - * Generate a link based on the current page - */ + * Generate a link based on the current page + */ val link = - new Loc.Link[WikiLoc](List("wiki"), false) { - override def createLink(in: WikiLoc) = { - if (in.edit) - Full(Text("/wiki/edit/"+urlEncode(in.page))) - else - Full(Text("/wiki/"+urlEncode(in.page))) + new Loc.Link[WikiLoc](List("wiki"), false) { + override def createLink(in: WikiLoc) = { + if (in.edit) + Full(Text("/wiki/edit/" + urlEncode(in.page))) + else + Full(Text("/wiki/" + urlEncode(in.page))) + } } - } /** - * What's the text of the link? - */ + * What's the text of the link? + */ val text = new Loc.LinkText(calcLinkText _) - def calcLinkText(in: WikiLoc): NodeSeq = - if (in.edit) - Text("Wiki edit "+in.page) - else - Text("Wiki "+in.page) + if (in.edit) + S.loc("Wiki edit " + in.page, Text("Wiki edit " + in.page)) + else + S.loc("Wiki " + in.page, Text("Wiki " + in.page)) /** - * Rewrite the request and emit the type-safe parameter - */ + * Rewrite the request and emit the type-safe parameter + */ override val rewrite: LocRewrite = - Full(NamedPF("Wiki Rewrite") { - case RewriteRequest(ParsePath("wiki" :: "edit" :: page :: Nil, _, _,_), - _, _) => - (RewriteResponse("wiki" :: Nil), WikiLoc(page, true)) + Full(NamedPF("Wiki Rewrite") { + case RewriteRequest(ParsePath("wiki" :: "edit" :: page :: Nil, _, _, _), + _, + _) => + (RewriteResponse("wiki" :: Nil), Full(WikiLoc(page, true))) - case RewriteRequest(ParsePath("wiki" :: page :: Nil, _, _,_), - _, _) => - (RewriteResponse("wiki" :: Nil), WikiLoc(page, false)) + case RewriteRequest(ParsePath("wiki" :: page :: Nil, _, _, _), _, _) => + (RewriteResponse("wiki" :: Nil), Full(WikiLoc(page, false))) }) def showAll(in: NodeSeq): NodeSeq = - WikiEntry.findAll(OrderBy(WikiEntry.name, Ascending)).flatMap(entry => -
{entry.name}
) + WikiEntry + .findAll(OrderBy(WikiEntry.name, Ascending)) + .flatMap(entry => +
{entry.name}
) def url(page: String) = createLink(WikiLoc(page, false)) - def editRecord(r: WikiEntry)(in: NodeSeq): NodeSeq = - + 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) @@ -142,16 +141,16 @@ 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 cancelLink = Cancel val textarea = r.entry.toForm - val submitButton = SHtml.submit(isNew ? "Add" | "Edit", () => r.save) + val submitButton = SHtml.submit(isNew ? "Add" | "Edit", () => r.save, "class" -> "btn btn-primary")
{ // the form tag message ++ - hobixLink ++ + texMarkRefLink ++ textarea ++ // display the form
++ cancelLink ++ @@ -163,11 +162,11 @@ 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._ @@ -176,13 +175,9 @@ object WikiStuff extends Loc[WikiLoc] { info match { case WikiURLInfo(page, _) => (stringUrl(page), Text(page), None) - }) + }) def stringUrl(page: String): String = - url(page).map(_.text) getOrElse "" - + url(page).map(_.text) getOrElse "" } -} -} -} 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..d6a2189 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 @@ -15,30 +15,28 @@ */ package net.liftweb { -package example { -package lib { - -import _root_.net.liftweb._ -import http._ -import common._ -import util._ -import Helpers._ - -object XmlServer { - def init() { - LiftRules.dispatch.prepend(NamedPF("Web Services Example") { - // if the url is "showcities" then return the showCities function - case Req("showcities":: Nil, _, GetRequest) => XmlServer.showCities - - // if the url is "showstates" "curry" the showStates function with the optional second parameter - case Req("showstates":: xs, _, GetRequest) => - XmlServer.showStates(if (xs.isEmpty) "default" else xs.head) - }) - } - - def showStates(which: String)(): Box[XmlResponse] = - Full(XmlResponse( - { + package example { + package lib { + + import http._ + import common._ + import util._ + import Helpers._ + + object XmlServer { + def init() { + LiftRules.dispatch.prepend(NamedPF("Web Services Example") { + // if the url is "showcities" then return the showCities function + case Req("showcities" :: Nil, _, GetRequest) => XmlServer.showCities + + // if the url is "showstates" "curry" the showStates function with the optional second parameter + case Req("showstates" :: xs, _, GetRequest) => + XmlServer.showStates(if (xs.isEmpty) "default" else xs.head) + }) + } + + def showStates(which: String)(): Box[XmlResponse] = + Full(XmlResponse({ which match { case "red" => @@ -48,16 +46,15 @@ object XmlServer { } } )) - def showCities(): Box[XmlResponse] = - Full(XmlResponse( - + def showCities(): Box[XmlResponse] = + Full(XmlResponse( )) -} -} -} + } + } + } } 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/model/User.scala b/combo/example/src/main/scala/net/liftweb/example/model/User.scala index 4cb8262..9ea8380 100644 --- a/combo/example/src/main/scala/net/liftweb/example/model/User.scala +++ b/combo/example/src/main/scala/net/liftweb/example/model/User.scala @@ -14,38 +14,33 @@ * limitations under the License. */ -package net.liftweb { -package example { -package model { +package net.liftweb.example.model import _root_.net.liftweb._ import mapper._ /** - * The singleton that has methods for accessing the database - */ + * 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) + 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 - */ + * 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 textareaRows = 10 override def textareaCols = 50 override def displayName = "Personal Essay" } } -} -} -} diff --git a/combo/example/src/main/scala/net/liftweb/example/model/WikiEntry.scala b/combo/example/src/main/scala/net/liftweb/example/model/WikiEntry.scala index fd607e7..f357bad 100644 --- a/combo/example/src/main/scala/net/liftweb/example/model/WikiEntry.scala +++ b/combo/example/src/main/scala/net/liftweb/example/model/WikiEntry.scala @@ -15,33 +15,33 @@ */ package net.liftweb { -package example { -package model { + package example { + package model { -import _root_.net.liftweb.mapper._ + import _root_.net.liftweb.mapper._ -/** - * The singleton that has methods for accessing the database - */ -object WikiEntry extends WikiEntry with LongKeyedMetaMapper[WikiEntry] + /** + * The singleton that has methods for accessing the database + */ + object WikiEntry extends WikiEntry with LongKeyedMetaMapper[WikiEntry] -/** - * An O-R mapped wiki entry - */ -class WikiEntry extends LongKeyedMapper[WikiEntry] with IdPK { - def getSingleton = WikiEntry // what's the "meta" object + /** + * An O-R mapped wiki entry + */ + class WikiEntry extends LongKeyedMapper[WikiEntry] with IdPK { + def getSingleton = WikiEntry // what's the "meta" object - // the name of the entry - object name extends MappedString(this, 32) { - override def dbIndexed_? = true // indexed in the DB - } + // the name of the entry + object name extends MappedString(this, 32) { + override def dbIndexed_? = true // indexed in the DB + } - // the text of the entry - object entry extends MappedTextarea(this, 8192) { - override def textareaRows = 10 - override def textareaCols = 50 + // the text of the entry + object entry extends MappedTextarea(this, 8192) { + override def textareaRows = 10 + override def textareaCols = 50 + } + } + } } } -} -} -} 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..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,23 +14,20 @@ * limitations under the License. */ -package net.liftweb { -package example { -package snippet { +package net.liftweb.example.snippet -import _root_.net.liftweb.http._ -import _root_.net.liftmodules.widgets.autocomplete._ -import S._ +import net.liftweb._ +import http._ 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.liftmodules.widgets.autocomplete._ +import scala.xml.{Text, NodeSeq} class Ajax extends Loggable { @@ -44,47 +41,66 @@ class Ajax extends Loggable { // build up an ajax tag to increment the counter def doClicker(text: NodeSeq) = - a(() => {cnt = cnt + 1; SetHtml(spanName, Text( cnt.toString))}, text) + 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)) + 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 => DisplayMessage(msgName, - bind("text", msg, "value" -> Text(v)), - 4 seconds, 1 second)) - - + ajaxText( + "", + v => { + val textBind = "#textValue" #> Text(v) + DisplayMessage(msgName, + {textBind(msg)}, + 6 seconds, + 1 second) + }, + "class" -> "form-control" + ) // bind the view to the functionality - bind("ajax", xhtml, - "clicker" -> doClicker _, - "select" -> doSelect _, - "text" -> doText _, - "auto" -> AutoComplete("", buildQuery _, _ => ())) + 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) + 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._ - "* [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.scala index aa2714a..7f34d8a 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 @@ -19,7 +19,6 @@ package example package snippet import _root_.scala.xml._ -import _root_.net.liftweb._ import http._ import S._ import SHtml._ @@ -38,295 +37,296 @@ class AjaxForm { 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) + untrustedSelect(cities.map(s => (s, s)), + Full(first), + s => city = s, + "class" -> "form-control") } 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)) + ReplaceOptions("city", cities.map(s => (s, s)), Full(first)) } - - // bind the view to the dynamic HTML - def show(xhtml: Group): NodeSeq = { - val (name: String, js): (String, JsExp) = ajaxCall(JE.JsRaw("this.value"), - s => After(200, replace(s))) - bind("select", xhtml, - "state" -> select(AjaxForm.states.map(s => (s,s)), - Full(state), s => state = s, "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)) + }, "class" -> "form-control") & + "#city" #> cityChoice(state) & + "type=submit" #> submit(?("Save"), () => { + S.notice("City: " + city + ", State: " + state); + redirectTo("#stateForm") + }, "class" -> "btn btn-primary") } } object AjaxForm { - val citiesAndStates = List("Alabama" -> "Birmingham", - "Alabama" -> "Huntsville", - "Alabama" -> "Mobile", - "Alabama" -> "Montgomery", - "Alaska" -> "Anchorage municipality", - "Arizona" -> "Chandler", - "Arizona" -> "Gilbert town", - "Arizona" -> "Glendale", - "Arizona" -> "Mesa", - "Arizona" -> "Peoria", - "Arizona" -> "Phoenix", - "Arizona" -> "Scottsdale", - "Arizona" -> "Tempe", - "Arizona" -> "Tucson", - "Arkansas" -> "Little Rock", - "California" -> "Anaheim", - "California" -> "Antioch", - "California" -> "Bakersfield", - "California" -> "Berkeley", - "California" -> "Burbank", - "California" -> "Chula Vista", - "California" -> "Concord", - "California" -> "Corona", - "California" -> "Costa Mesa", - "California" -> "Daly City", - "California" -> "Downey", - "California" -> "El Monte", - "California" -> "Elk Grove", - "California" -> "Escondido", - "California" -> "Fairfield", - "California" -> "Fontana", - "California" -> "Fremont", - "California" -> "Fresno", - "California" -> "Fullerton", - "California" -> "Garden Grove", - "California" -> "Glendale", - "California" -> "Hayward", - "California" -> "Huntington Beach", - "California" -> "Inglewood", - "California" -> "Irvine", - "California" -> "Lancaster", - "California" -> "Long Beach", - "California" -> "Los Angeles", - "California" -> "Modesto", - "California" -> "Moreno Valley", - "California" -> "Norwalk", - "California" -> "Oakland", - "California" -> "Oceanside", - "California" -> "Ontario", - "California" -> "Orange", - "California" -> "Oxnard", - "California" -> "Palmdale", - "California" -> "Pasadena", - "California" -> "Pomona", - "California" -> "Rancho Cucamonga", - "California" -> "Richmond", - "California" -> "Riverside", - "California" -> "Roseville", - "California" -> "Sacramento", - "California" -> "Salinas", - "California" -> "San Bernardino", - "California" -> "San Buenaventura (Ventura)", - "California" -> "San Diego", - "California" -> "San Francisco", - "California" -> "San Jose", - "California" -> "Santa Ana", - "California" -> "Santa Clara", - "California" -> "Santa Clarita", - "California" -> "Santa Rosa", - "California" -> "Simi Valley", - "California" -> "Stockton", - "California" -> "Sunnyvale", - "California" -> "Thousand Oaks", - "California" -> "Torrance", - "California" -> "Vallejo", - "California" -> "Visalia", - "California" -> "West Covina", - "Colorado" -> "Arvada", - "Colorado" -> "Aurora", - "Colorado" -> "Colorado Springs", - "Colorado" -> "Denver", - "Colorado" -> "Fort Collins", - "Colorado" -> "Lakewood", - "Colorado" -> "Pueblo", - "Colorado" -> "Thornton", - "Colorado" -> "Westminster", - "Connecticut" -> "Bridgeport", - "Connecticut" -> "Hartford", - "Connecticut" -> "New Haven", - "Connecticut" -> "Stamford", - "Connecticut" -> "Waterbury", - "District of Columbia" -> "Washington", - "Florida" -> "Cape Coral", - "Florida" -> "Clearwater", - "Florida" -> "Coral Springs", - "Florida" -> "Fort Lauderdale", - "Florida" -> "Gainesville", - "Florida" -> "Hialeah", - "Florida" -> "Hollywood", - "Florida" -> "Jacksonville", - "Florida" -> "Miami", - "Florida" -> "Miramar", - "Florida" -> "Orlando", - "Florida" -> "Pembroke Pines", - "Florida" -> "Pompano Beach", - "Florida" -> "Port St. Lucie", - "Florida" -> "St. Petersburg", - "Florida" -> "Tallahassee", - "Florida" -> "Tampa", - "Georgia" -> "Athens-Clarke County (balance)", - "Georgia" -> "Atlanta", - "Georgia" -> "Augusta-Richmond County (balance)", - "Georgia" -> "Columbus", - "Georgia" -> "Savannah", - "Hawaii" -> "Honolulu CDP", - "Idaho" -> "Boise City", - "Illinois" -> "Aurora", - "Illinois" -> "Chicago", - "Illinois" -> "Elgin", - "Illinois" -> "Joliet", - "Illinois" -> "Naperville", - "Illinois" -> "Peoria", - "Illinois" -> "Rockford", - "Illinois" -> "Springfield", - "Indiana" -> "Evansville", - "Indiana" -> "Fort Wayne", - "Indiana" -> "Indianapolis city (balance)", - "Indiana" -> "South Bend", - "Iowa" -> "Cedar Rapids", - "Iowa" -> "Des Moines", - "Kansas" -> "Kansas City", - "Kansas" -> "Olathe", - "Kansas" -> "Overland Park", - "Kansas" -> "Topeka", - "Kansas" -> "Wichita", - "Kentucky" -> "Lexington-Fayette", - "Kentucky" -> "Louisville/Jefferson County (balance)", - "Louisiana" -> "Baton Rouge", - "Louisiana" -> "Lafayette", - "Louisiana" -> "New Orleans", - "Louisiana" -> "Shreveport", - "Maryland" -> "Baltimore", - "Massachusetts" -> "Boston", - "Massachusetts" -> "Cambridge", - "Massachusetts" -> "Lowell", - "Massachusetts" -> "Springfield", - "Massachusetts" -> "Worcester", - "Michigan" -> "Ann Arbor", - "Michigan" -> "Detroit", - "Michigan" -> "Flint", - "Michigan" -> "Grand Rapids", - "Michigan" -> "Lansing", - "Michigan" -> "Sterling Heights", - "Michigan" -> "Warren", - "Minnesota" -> "Minneapolis", - "Minnesota" -> "St. Paul", - "Mississippi" -> "Jackson", - "Missouri" -> "Independence", - "Missouri" -> "Kansas City", - "Missouri" -> "Springfield", - "Missouri" -> "St. Louis", - "Montana" -> "Billings", - "Nebraska" -> "Lincoln", - "Nebraska" -> "Omaha", - "Nevada" -> "Henderson", - "Nevada" -> "Las Vegas", - "Nevada" -> "North Las Vegas", - "Nevada" -> "Reno", - "New Hampshire" -> "Manchester", - "New Jersey" -> "Elizabeth", - "New Jersey" -> "Jersey City", - "New Jersey" -> "Newark", - "New Jersey" -> "Paterson", - "New Mexico" -> "Albuquerque", - "New York" -> "Buffalo", - "New York" -> "New York", - "New York" -> "Rochester", - "New York" -> "Syracuse", - "New York" -> "Yonkers", - "North Carolina" -> "Cary town", - "North Carolina" -> "Charlotte", - "North Carolina" -> "Durham", - "North Carolina" -> "Fayetteville", - "North Carolina" -> "Greensboro", - "North Carolina" -> "Raleigh", - "North Carolina" -> "Winston-Salem", - "Ohio" -> "Akron", - "Ohio" -> "Cincinnati", - "Ohio" -> "Cleveland", - "Ohio" -> "Columbus", - "Ohio" -> "Dayton", - "Ohio" -> "Toledo", - "Oklahoma" -> "Norman", - "Oklahoma" -> "Oklahoma City", - "Oklahoma" -> "Tulsa", - "Oregon" -> "Eugene", - "Oregon" -> "Portland", - "Oregon" -> "Salem", - "Pennsylvania" -> "Allentown", - "Pennsylvania" -> "Erie", - "Pennsylvania" -> "Philadelphia", - "Pennsylvania" -> "Pittsburgh", - "Rhode Island" -> "Providence", - "South Carolina" -> "Charleston", - "South Carolina" -> "Columbia", - "South Dakota" -> "Sioux Falls", - "Tennessee" -> "Chattanooga", - "Tennessee" -> "Clarksville", - "Tennessee" -> "Knoxville", - "Tennessee" -> "Memphis", - "Tennessee" -> "Nashville-Davidson (balance)", - "Texas" -> "Abilene", - "Texas" -> "Amarillo", - "Texas" -> "Arlington", - "Texas" -> "Austin", - "Texas" -> "Beaumont", - "Texas" -> "Brownsville", - "Texas" -> "Carrollton", - "Texas" -> "Corpus Christi", - "Texas" -> "Dallas", - "Texas" -> "Denton", - "Texas" -> "El Paso", - "Texas" -> "Fort Worth", - "Texas" -> "Garland", - "Texas" -> "Grand Prairie", - "Texas" -> "Houston", - "Texas" -> "Irving", - "Texas" -> "Killeen", - "Texas" -> "Laredo", - "Texas" -> "Lubbock", - "Texas" -> "McAllen", - "Texas" -> "McKinney", - "Texas" -> "Mesquite", - "Texas" -> "Midland", - "Texas" -> "Pasadena", - "Texas" -> "Plano", - "Texas" -> "San Antonio", - "Texas" -> "Waco", - "Utah" -> "Provo", - "Utah" -> "Salt Lake City", - "Utah" -> "West Valley City", - "Virginia" -> "Alexandria", - "Virginia" -> "Arlington CDP", - "Virginia" -> "Chesapeake", - "Virginia" -> "Hampton", - "Virginia" -> "Newport News", - "Virginia" -> "Norfolk", - "Virginia" -> "Portsmouth", - "Virginia" -> "Richmond", - "Virginia" -> "Virginia Beach", - "Washington" -> "Bellevue", - "Washington" -> "Seattle", - "Washington" -> "Spokane", - "Washington" -> "Tacoma", - "Washington" -> "Vancouver", - "Wisconsin" -> "Green Bay", - "Wisconsin" -> "Madison", - "Wisconsin" -> "Milwaukee") + val citiesAndStates = List( + "Alabama" -> "Birmingham", + "Alabama" -> "Huntsville", + "Alabama" -> "Mobile", + "Alabama" -> "Montgomery", + "Alaska" -> "Anchorage municipality", + "Arizona" -> "Chandler", + "Arizona" -> "Gilbert town", + "Arizona" -> "Glendale", + "Arizona" -> "Mesa", + "Arizona" -> "Peoria", + "Arizona" -> "Phoenix", + "Arizona" -> "Scottsdale", + "Arizona" -> "Tempe", + "Arizona" -> "Tucson", + "Arkansas" -> "Little Rock", + "California" -> "Anaheim", + "California" -> "Antioch", + "California" -> "Bakersfield", + "California" -> "Berkeley", + "California" -> "Burbank", + "California" -> "Chula Vista", + "California" -> "Concord", + "California" -> "Corona", + "California" -> "Costa Mesa", + "California" -> "Daly City", + "California" -> "Downey", + "California" -> "El Monte", + "California" -> "Elk Grove", + "California" -> "Escondido", + "California" -> "Fairfield", + "California" -> "Fontana", + "California" -> "Fremont", + "California" -> "Fresno", + "California" -> "Fullerton", + "California" -> "Garden Grove", + "California" -> "Glendale", + "California" -> "Hayward", + "California" -> "Huntington Beach", + "California" -> "Inglewood", + "California" -> "Irvine", + "California" -> "Lancaster", + "California" -> "Long Beach", + "California" -> "Los Angeles", + "California" -> "Modesto", + "California" -> "Moreno Valley", + "California" -> "Norwalk", + "California" -> "Oakland", + "California" -> "Oceanside", + "California" -> "Ontario", + "California" -> "Orange", + "California" -> "Oxnard", + "California" -> "Palmdale", + "California" -> "Pasadena", + "California" -> "Pomona", + "California" -> "Rancho Cucamonga", + "California" -> "Richmond", + "California" -> "Riverside", + "California" -> "Roseville", + "California" -> "Sacramento", + "California" -> "Salinas", + "California" -> "San Bernardino", + "California" -> "San Buenaventura (Ventura)", + "California" -> "San Diego", + "California" -> "San Francisco", + "California" -> "San Jose", + "California" -> "Santa Ana", + "California" -> "Santa Clara", + "California" -> "Santa Clarita", + "California" -> "Santa Rosa", + "California" -> "Simi Valley", + "California" -> "Stockton", + "California" -> "Sunnyvale", + "California" -> "Thousand Oaks", + "California" -> "Torrance", + "California" -> "Vallejo", + "California" -> "Visalia", + "California" -> "West Covina", + "Colorado" -> "Arvada", + "Colorado" -> "Aurora", + "Colorado" -> "Colorado Springs", + "Colorado" -> "Denver", + "Colorado" -> "Fort Collins", + "Colorado" -> "Lakewood", + "Colorado" -> "Pueblo", + "Colorado" -> "Thornton", + "Colorado" -> "Westminster", + "Connecticut" -> "Bridgeport", + "Connecticut" -> "Hartford", + "Connecticut" -> "New Haven", + "Connecticut" -> "Stamford", + "Connecticut" -> "Waterbury", + "District of Columbia" -> "Washington", + "Florida" -> "Cape Coral", + "Florida" -> "Clearwater", + "Florida" -> "Coral Springs", + "Florida" -> "Fort Lauderdale", + "Florida" -> "Gainesville", + "Florida" -> "Hialeah", + "Florida" -> "Hollywood", + "Florida" -> "Jacksonville", + "Florida" -> "Miami", + "Florida" -> "Miramar", + "Florida" -> "Orlando", + "Florida" -> "Pembroke Pines", + "Florida" -> "Pompano Beach", + "Florida" -> "Port St. Lucie", + "Florida" -> "St. Petersburg", + "Florida" -> "Tallahassee", + "Florida" -> "Tampa", + "Georgia" -> "Athens-Clarke County (balance)", + "Georgia" -> "Atlanta", + "Georgia" -> "Augusta-Richmond County (balance)", + "Georgia" -> "Columbus", + "Georgia" -> "Savannah", + "Hawaii" -> "Honolulu CDP", + "Idaho" -> "Boise City", + "Illinois" -> "Aurora", + "Illinois" -> "Chicago", + "Illinois" -> "Elgin", + "Illinois" -> "Joliet", + "Illinois" -> "Naperville", + "Illinois" -> "Peoria", + "Illinois" -> "Rockford", + "Illinois" -> "Springfield", + "Indiana" -> "Evansville", + "Indiana" -> "Fort Wayne", + "Indiana" -> "Indianapolis city (balance)", + "Indiana" -> "South Bend", + "Iowa" -> "Cedar Rapids", + "Iowa" -> "Des Moines", + "Kansas" -> "Kansas City", + "Kansas" -> "Olathe", + "Kansas" -> "Overland Park", + "Kansas" -> "Topeka", + "Kansas" -> "Wichita", + "Kentucky" -> "Lexington-Fayette", + "Kentucky" -> "Louisville/Jefferson County (balance)", + "Louisiana" -> "Baton Rouge", + "Louisiana" -> "Lafayette", + "Louisiana" -> "New Orleans", + "Louisiana" -> "Shreveport", + "Maryland" -> "Baltimore", + "Massachusetts" -> "Boston", + "Massachusetts" -> "Cambridge", + "Massachusetts" -> "Lowell", + "Massachusetts" -> "Springfield", + "Massachusetts" -> "Worcester", + "Michigan" -> "Ann Arbor", + "Michigan" -> "Detroit", + "Michigan" -> "Flint", + "Michigan" -> "Grand Rapids", + "Michigan" -> "Lansing", + "Michigan" -> "Sterling Heights", + "Michigan" -> "Warren", + "Minnesota" -> "Minneapolis", + "Minnesota" -> "St. Paul", + "Mississippi" -> "Jackson", + "Missouri" -> "Independence", + "Missouri" -> "Kansas City", + "Missouri" -> "Springfield", + "Missouri" -> "St. Louis", + "Montana" -> "Billings", + "Nebraska" -> "Lincoln", + "Nebraska" -> "Omaha", + "Nevada" -> "Henderson", + "Nevada" -> "Las Vegas", + "Nevada" -> "North Las Vegas", + "Nevada" -> "Reno", + "New Hampshire" -> "Manchester", + "New Jersey" -> "Elizabeth", + "New Jersey" -> "Jersey City", + "New Jersey" -> "Newark", + "New Jersey" -> "Paterson", + "New Mexico" -> "Albuquerque", + "New York" -> "Buffalo", + "New York" -> "New York", + "New York" -> "Rochester", + "New York" -> "Syracuse", + "New York" -> "Yonkers", + "North Carolina" -> "Cary town", + "North Carolina" -> "Charlotte", + "North Carolina" -> "Durham", + "North Carolina" -> "Fayetteville", + "North Carolina" -> "Greensboro", + "North Carolina" -> "Raleigh", + "North Carolina" -> "Winston-Salem", + "Ohio" -> "Akron", + "Ohio" -> "Cincinnati", + "Ohio" -> "Cleveland", + "Ohio" -> "Columbus", + "Ohio" -> "Dayton", + "Ohio" -> "Toledo", + "Oklahoma" -> "Norman", + "Oklahoma" -> "Oklahoma City", + "Oklahoma" -> "Tulsa", + "Oregon" -> "Eugene", + "Oregon" -> "Portland", + "Oregon" -> "Salem", + "Pennsylvania" -> "Allentown", + "Pennsylvania" -> "Erie", + "Pennsylvania" -> "Philadelphia", + "Pennsylvania" -> "Pittsburgh", + "Rhode Island" -> "Providence", + "South Carolina" -> "Charleston", + "South Carolina" -> "Columbia", + "South Dakota" -> "Sioux Falls", + "Tennessee" -> "Chattanooga", + "Tennessee" -> "Clarksville", + "Tennessee" -> "Knoxville", + "Tennessee" -> "Memphis", + "Tennessee" -> "Nashville-Davidson (balance)", + "Texas" -> "Abilene", + "Texas" -> "Amarillo", + "Texas" -> "Arlington", + "Texas" -> "Austin", + "Texas" -> "Beaumont", + "Texas" -> "Brownsville", + "Texas" -> "Carrollton", + "Texas" -> "Corpus Christi", + "Texas" -> "Dallas", + "Texas" -> "Denton", + "Texas" -> "El Paso", + "Texas" -> "Fort Worth", + "Texas" -> "Garland", + "Texas" -> "Grand Prairie", + "Texas" -> "Houston", + "Texas" -> "Irving", + "Texas" -> "Killeen", + "Texas" -> "Laredo", + "Texas" -> "Lubbock", + "Texas" -> "McAllen", + "Texas" -> "McKinney", + "Texas" -> "Mesquite", + "Texas" -> "Midland", + "Texas" -> "Pasadena", + "Texas" -> "Plano", + "Texas" -> "San Antonio", + "Texas" -> "Waco", + "Utah" -> "Provo", + "Utah" -> "Salt Lake City", + "Utah" -> "West Valley City", + "Virginia" -> "Alexandria", + "Virginia" -> "Arlington CDP", + "Virginia" -> "Chesapeake", + "Virginia" -> "Hampton", + "Virginia" -> "Newport News", + "Virginia" -> "Norfolk", + "Virginia" -> "Portsmouth", + "Virginia" -> "Richmond", + "Virginia" -> "Virginia Beach", + "Washington" -> "Bellevue", + "Washington" -> "Seattle", + "Washington" -> "Spokane", + "Washington" -> "Tacoma", + "Washington" -> "Vancouver", + "Wisconsin" -> "Green Bay", + "Wisconsin" -> "Madison", + "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) + def citiesFor(state: String): List[String] = + citiesAndStates.filter(_._1 == state).map(_._2) } 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 99% 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 index c63eab3..5580925 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/AllJson.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/AllJson.scalaREM @@ -19,6 +19,7 @@ package example { package snippet { import net.liftweb._ +import json._ import http._ import common._ import util._ 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/BSDialog.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/BSDialog.scala new file mode 100644 index 0000000..0e2dd2f --- /dev/null +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/BSDialog.scala @@ -0,0 +1,72 @@ +/* + * Copyright 2009-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 net.liftweb.http.SHtml._ +import net.liftweb.http._ +import net.liftweb.http.js.JsCmds._ +import net.liftweb.http.js._ +import net.liftweb.http.js.JE.JsRaw +import net.liftweb.util.Helpers._ +import net.liftweb.common._ +import scala.xml.NodeSeq + +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/scala/net/liftweb/example/snippet/CacheBust.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/CacheBust.scala new file mode 100644 index 0000000..c13e59a --- /dev/null +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/CacheBust.scala @@ -0,0 +1,18 @@ +package net.liftweb.example.snippet + +import java.text.SimpleDateFormat +import java.util.Date + +import net.liftweb.util.Helpers._ +import net.liftweb.util._ + +import net.liftweb.example.lib.BuildInfo + +class CacheBust { + private lazy val date: Date = new Date(BuildInfo.buildTime) + private lazy val formatter = new SimpleDateFormat("yyMMddHHmmss") + private lazy val strDate: String = formatter.format(date) + + def usingBuildTime: CssSel = + "link [href+]" #> s"?bt=$strDate" & "script [src+]" #> s"?bt=$strDate" +} 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.scala index ff1b772..5f55619 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/Count.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Count.scala @@ -14,30 +14,23 @@ * 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.{Text} 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.collection.mutable.HashMap - /** * 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 @@ -47,7 +40,7 @@ class Count { /** * This method is invoked by the <lift:Count /> tag */ - def render(in: NodeSeq): NodeSeq = { + def render = { // get the attribute name (the name of the thing to count) val attr: String = S.attr("name").openOr("N/A") @@ -55,11 +48,12 @@ class Count { 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("--"))) + ".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/scala/net/liftweb/example/snippet/CountGame.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/CountGame.scala index af8653c..77cae98 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/CountGame.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/CountGame.scala @@ -14,47 +14,52 @@ * 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._ import _root_.net.liftweb.common._ import _root_.net.liftweb.util._ - class CountGame extends StatefulSnippet { val dispatch: DispatchIt = { case "run" if lastGuess == number => - xhtml => win(chooseTemplate("choose", "win", xhtml)) + xhtml => + win(xhtml) case "run" => - xhtml => nextGuess(chooseTemplate("choose", "guess", xhtml)) + xhtml => + nextGuess(xhtml) case "count_down" => - xhtml => countDown(attr("from").map(Helpers.toInt).openOr(0)) + 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} + def countDown(number: Int): Node = + if (number <= 0) Text("") + 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 +70,3 @@ class CountGame extends StatefulSnippet { private var lastGuess: Box[Int] = Empty private var count = 0 } -} -} -} 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.scala index 3a446c8..7f0a85b 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/Database.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Database.scala @@ -14,41 +14,38 @@ * 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_.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._ /** * This snippet handles counting */ class Database { - /** - * This method is invoked by the <lift:Count /> tag - */ - def render(in: NodeSeq): NodeSeq = { - val count = Person.count() - val first = Person.find(OrderBy(Person.firstName, Ascending), MaxRows[Person](1)) + def render = { + val count = Person.count() + val first = + Person.find(OrderBy(Person.firstName, Ascending), MaxRows[Person](1)) - bind("database", in, "count" -> count, - "first" -> first.map(_.asHtml).openOr(No Persons in the system), - "submit" -> submit("Create More Records", () => { - val cnt = 10 + randomInt(50) - for (x <- 1 to cnt) Person.create.firstName(randomString(20)).lastName(randomString(20)).personalityType(Personality.rand).save - notice("Added "+cnt+" records to the Person table") - })) + "#count" #> count & + "#first" #> first.map(_.asHtml).openOr(No Persons in the system) & + "type=submit" #> submit( + "Create More Records", + () => { + val cnt = 10 + randomInt(50) + for (x <- 1 to cnt) + Person.create + .firstName(randomString(20)) + .lastName(randomString(20)) + .personalityType(Personality.rand) + .save + notice("Added " + cnt + " records to the Person table") + } + ) } } -} -} -} 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.scala index 127ec70..2b92056 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/DivSelector.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/DivSelector.scala @@ -14,39 +14,31 @@ * 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} +package net.liftweb.example.snippet +import net.liftweb.http._ +import net.liftweb.http.SHtml._ +import net.liftweb.util.Helpers._ +import net.liftweb.common._ +import net.liftweb.util._ +import scala.xml.{NodeSeq, Text} class DivSelector extends StatefulSnippet { - private var whichDivs: Array[Boolean] = Array(true, true, true, true, true, true) - def dispatch: DispatchIt = - { - case "select" => selectDivs + private val whichDivs: Array[Boolean] = + Array(true, true, true, true, true, true) + + def dispatch: DispatchIt = { + case "select" => selectDivs case "populate" => populate } - def populate(in: NodeSeq): NodeSeq = - bind("div", in, "line" -> - (line => - whichDivs.toList.zipWithIndex.flatMap{ - case (value, num) => - bind("div", line, - "number" -> Text(num.toString), - "checkbox" -> checkbox(value, v => whichDivs(num) = v))})) + def populate: CssSel = "#line" #> whichDivs.toList.zipWithIndex.flatMap { + case (value, num) => + List( + "#number" #> Text(num.toString) & + "#checkbox" #> checkbox(value, v => whichDivs(num) = v)) + } def selectDivs(in: NodeSeq): NodeSeq = { def calcNum(in: String): Box[Int] = @@ -60,6 +52,3 @@ class DivSelector extends StatefulSnippet { } yield div } } -} -} -} 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 a4b7e3d..fa07781 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 @@ -14,50 +14,56 @@ * 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 SHtml._ import js._ import JsCmds._ -import common._ import util._ import Helpers._ -import scala.xml.NodeSeq - class FormWithAjax extends StatefulSnippet { private var firstName = "" private var lastName = "" - private val from = S.referer openOr "/" + private val to = "#wrappedNoticeAtHead" def dispatch = { - case _ => render _ + case _ => render } - def render(xhtml: NodeSeq): NodeSeq = - { - + 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("Thanks!"); S.redirectTo(from) + case (f, n) if f < 2 && n < 2 => + S.error("fwaErrMsg", "First and last names too short") + 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("fwaNoticeMsg", "Ajax form says Thanks!") + S.redirectTo(to) + } } } - bind("form", xhtml, - "first" -> textAjaxTest(firstName, s => firstName = s, s => {S.notice("First name "+s); Noop}), - "last" -> textAjaxTest(lastName, s => lastName = s, s => {S.notice("Last name "+s); Noop}), - "submit" -> submit("Send", validate _) - ) + "#first" #> textAjaxTest(firstName, + s => firstName = s, + s => { + S.notice("fwaNoticeMsg", "First name " + s); Noop + }, + "class" -> "form-control", + "type" -> "text", + "placeholder" -> "First name") & + "#last" #> textAjaxTest(lastName, + s => lastName = s, + s => { + S.notice("fwaNoticeMsg", "Last name " + s); Noop + }, + "class" -> "form-control", + "type" -> "text", + "placeholder" -> "Last name") & + "type=submit" #> submit("Send", validate _, "class" -> "btn btn-primary") } } -} -} -} 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..e85d0f7 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._ @@ -24,24 +22,23 @@ import util._ import Helpers._ import js.JsCmds._ import js.jquery._ -import _root_.scala.xml.{NodeSeq, Text} +import _root_.scala.xml.{NodeSeq} 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/JSDialog.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/JSDialog.scala index b1ccf01..64189fa 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 @@ -15,41 +15,47 @@ */ package net.liftweb { -package example { -package snippet { + package example { + package snippet { -import _root_.net.liftweb._ -import http._ -import SHtml._ -import js._ -import JsCmds._ -import js.jquery._ -import JqJsCmds._ -import common._ -import util._ -import Helpers._ + import http._ + import SHtml._ + import js._ + import JsCmds._ + import js.jquery._ + import JqJsCmds._ + import util._ + import Helpers._ -import _root_.scala.xml.NodeSeq + import _root_.scala.xml.NodeSeq -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")) + 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"), + "class" -> "btn btn-primary" + ) - // 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) => )) -} -} -} + // 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) => ) + } + } + } + } } 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.scala index 955af8d..5b090ea 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/Json.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Json.scala @@ -14,41 +14,39 @@ * limitations under the License. */ -package net.liftweb { -package example { -package snippet { +package net.liftweb.example.snippet -import _root_.net.liftweb.http._ -import S._ +import net.liftweb.http._ import js._ import JsCmds._ import JE._ import net.liftmodules.textile._ -import _root_.net.liftweb.common._ -import _root_.net.liftweb.util._ +import net.liftweb.common._ +import net.liftweb.util._ import Helpers._ -import _root_.scala.xml._ + +import scala.xml._ class Json { - object json extends JsonHandler { + object json { 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 => Problem... didn't handle JSON message {x} - }) + 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.toString + " Characters") + case x => Problem... didn't handle JSON message {x} + } + ) } - 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")) -} -} -} + def sample = + "#jsonscript" #> Script(JsRaw(json.apply()).cmd) & + ".json [onclick]" #> Text( + json(ElemById("json_select") ~> Value, + ElemById("json_question") ~> Value).toJsCmd) + } 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 new file mode 100644 index 0000000..cc341a1 --- /dev/null +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Json2.scala @@ -0,0 +1,51 @@ +/* + * 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 net.liftweb.util.Helpers._ +import net.liftweb.http.SHtml +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 + +// Example taken from http://cookbook.liftweb.net/book.html#id-rjCJTYFwuYtbh9 +object JsonCall { + + private val logger = Logger(classOf[JsonCall]) + implicit val formats = DefaultFormats + + case class Question(first: Int, second: Int, answer: Int) { + def valid_? = first + second == answer + } + + def render = { + + def validate(value: JValue): JsCmd = { + logger.info(value) + value.extractOpt[Question].map(_.valid_?) match { + case Some(true) => Alert("Looks good") + case Some(false) => Alert("That doesn't add up") + case None => Alert("That doesn't make sense") + } + } + + "button [onclick]" #> + SHtml.jsonCall(JE.Call("currentQuestion"), validate _) + } +} diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/LongTime.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/LongTime.scala index c366de5..4275135 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/LongTime.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/LongTime.scala @@ -15,22 +15,20 @@ */ package net.liftweb { -package example { -package snippet { + package example { + package snippet { -import net.liftweb.util.Helpers._ + import net.liftweb.util.Helpers._ -object LongTime { - def render = { - val delay = 1000L + randomLong(10000) + object LongTime { + def render = { + val delay = 1000L + randomLong(10000) - Thread.sleep(delay) - -
- This thread delayed {delay / 1000L} seconds -
+ Thread.sleep(delay) + +
This thread delayed {delay / 1000L} seconds
+ } + } + } } } -} -} -} 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..7eb73ca 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,53 +14,57 @@ * 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) + 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} - {link("/simple/edit", () => selectedUser(Full(u)), Text("Edit"))} - {link("/simple/delete", () => selectedUser(Full(u)), Text("Delete"))} + {User.htmlHeaders}EditDelete :: + // get and display each of the users + User.findAll(OrderBy(User.id, Ascending)).flatMap(u => {u.htmlLine} + {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")} ) } /** - * 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") } @@ -69,12 +73,18 @@ 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("Yes delete", + deleteUser _, + "type" -> "button", + "class" -> "btn btn-danger") + } + 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 @@ -83,65 +93,80 @@ 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 - case x => error(x); selectedUser(Full(user)) + // 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()) + 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 _) ++ -
Cancel - + 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 _) ++ - Cancel - + 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 + */ def upload(xhtml: Group): NodeSeq = - if (S.get_?) bind("ul", chooseTemplate("choose", "get", xhtml), - "file_upload" -> fileUpload(ul => theUpload(Full(ul)))) - else bind("ul", chooseTemplate("choose", "post", xhtml), - "file_name" -> theUpload.is.map(v => Text(v.fileName)), - "mime_type" -> theUpload.is.map(v => Box.legacyNullTest(v.mimeType).map(Text).openOr(Text("No mime type supplied"))), // Text(v.mimeType)), - "length" -> theUpload.is.map(v => Text(v.file.length.toString)), - "md5" -> theUpload.is.map(v => Text(hexEncode(md5(v.file)))) - ); - + if (S.get_?) + ( + "#get ^*" #> "#choose" & + ".custom-file-input" #> 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) 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/Parallel.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Parallel.scala index e703823..5538367 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/Parallel.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Parallel.scala @@ -15,18 +15,18 @@ */ package net.liftweb { -package example { -package snippet { + package example { + package snippet { -object Parallel { - def render = { -
This snippet evaluated on + object Parallel { + def render = { +
This snippet evaluated on { Thread.currentThread.getName() }
+ } + } + } } } -} -} -} 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..a7bc83d 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 @@ -15,44 +15,52 @@ */ package net.liftweb { -package example { -package snippet { + package example { + package snippet { -import _root_.net.liftweb._ -import http._ -import common._ -import util._ -import Helpers._ + import http._ + import util._ + import Helpers._ -import _root_.java.text.NumberFormat -import _root_.scala.xml.{NodeSeq, Text} + import _root_.java.text.NumberFormat + import _root_.scala.xml.{Text} -object RuntimeStats extends DispatchSnippet { - @volatile - var totalMem: Long = Runtime.getRuntime.totalMemory - @volatile - var freeMem: Long = Runtime.getRuntime.freeMemory + object RuntimeStats extends DispatchSnippet { + @volatile + var totalMem: Long = Runtime.getRuntime.totalMemory + @volatile + var freeMem: Long = Runtime.getRuntime.freeMemory - @volatile - var sessions = 1 + @volatile + var sessions = 1 - @volatile - var lastUpdate = timeNow + @volatile + var lastUpdate = now - val startedAt = timeNow + val startedAt = now - private def nf(in: Long): String = NumberFormat.getInstance.format(in) + private def nf(in: Long): String = NumberFormat.getInstance.format(in) - def dispatch = { - case "total_mem" => i => Text(nf(totalMem)) - case "free_mem" => i => Text(nf(freeMem)) - case "sessions" => i => Text(sessions.toString) - case "updated_at" => i => Text(lastUpdate.toString) - case "started_at" => i => Text(startedAt.toString) - } + def dispatch = { + case "total_mem" => + i => + Text(nf(totalMem)) + case "free_mem" => + i => + Text(nf(freeMem)) + case "sessions" => + i => + Text(sessions.toString) + case "updated_at" => + i => + Text(lastUpdate.toString) + case "started_at" => + i => + Text(startedAt.toString) + } -} + } -} -} + } + } } diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/Show.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Show.scala new file mode 100644 index 0000000..69409b1 --- /dev/null +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Show.scala @@ -0,0 +1,51 @@ +package net.liftweb.example.snippet + +import net.liftweb._ +import http._ +import SHtml._ +import js._ +import common._ +import JsCmds._ +import util._ +import Helpers._ + +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", +
{"Selected year '" + years(selected) + "'"}
) + } + def nameHandler(name: String): JsCmd = { + this.name = name + S.notice("The name is '" + name + "'") + SetHtml("namevalue",
{"The name is '" + name + "'"}
) + } + def submitHandler(): JsCmd = { + S.notice( + "The name and year was set to (" + this.name + "," + this.year + ")") + S.redirectTo("#myForm") + } +} 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..8693bff 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,37 @@ 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)}) +
+ {ajaxCheckboxElem(feedFish)} + +
, +
+ {ajaxCheckboxElem(walkDog)} + +
, +
+ {ajaxCheckboxElem(doDishes)} + +
, +
+ {ajaxCheckboxElem(watchTv)} + +
+ ) } - -} } -} -} 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 index bca42bf..3954340 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWizard.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/SimpleWizard.scala @@ -14,91 +14,107 @@ * 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} +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 - */ + * 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 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 + NodeSeq.Empty /** - * pageOne -- Ask the name - */ + * 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} + else + dispatch = { + case _ => + xhtml => + pageTwo + } } - - template("pageOne", - ("#name" replaceWith text(name, name = _)) & - ("#submit" replaceWith submit(S ? "Next", validate))) + + template( + "pageOne", + ("#name" replaceWith text(name, name = _, "class" -> "form-control")) & + ("#submit" replaceWith submit(S ? "Next", + validate, + "class" -> "btn btn-primary"))) } - + /** - * pageTwo -- Ask the quest - */ + * 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} + else + dispatch = { + case _ => + xhtml => + pageThree + } } - template("pageTwo", - ("#quest" replaceWith text(quest, quest = _)) & - ("#submit" replaceWith submit(S ? "Next", validate))) + template( + "pageTwo", + ("#quest" replaceWith text(quest, quest = _, "class" -> "form-control")) & + ("#submit" replaceWith submit(S ? "Next", + validate, + "class" -> "btn btn-primary"))) } - + /** - * pageThree -- Ask the color - */ + * 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")) + 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.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))) + + 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/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/Timely.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/Timely.scala index 1dd3d61..9c5ffdc 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/Timely.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/Timely.scala @@ -14,40 +14,42 @@ * limitations under the License. */ -package net.liftweb -package example +package net.liftweb +package example package snippet -import net.liftweb._ import http._ import util._ import js._ import JsCmds._ -import JE._ import scala.xml._ /** - * A demo of the LiftSession.addPostPageJavaScript feature. - * This snippet sets the innerHTML of the specified ID - * to the current time and the count of how - * many times the message was updated. - */ + * A demo of the LiftSession.addPostPageJavaScript feature. + * This snippet sets the innerHTML of the specified ID + * to the current time and the count of how + * many times the message was updated. + */ object Timely { def render(in: NodeSeq): NodeSeq = { var x = 0 + def timesStr(x: Int): String = if (x == 1) "time" else "times" + for { theId <- (in \ "@id") session <- S.session } { session.addPostPageJavaScript(() => { x += 1 - SetHtml(theId.text, - The time of the last update to + SetHtml( + theId.text, + The time of the last update to this page is {Helpers.now.toString}, - and this section was updated {x} times. - ) + and this section was updated {x} {timesStr(x)}. + + ) }) } diff --git a/combo/example/src/main/scala/net/liftweb/example/snippet/ValidateSession.scala b/combo/example/src/main/scala/net/liftweb/example/snippet/ValidateSession.scala index eeaa682..2252f16 100644 --- a/combo/example/src/main/scala/net/liftweb/example/snippet/ValidateSession.scala +++ b/combo/example/src/main/scala/net/liftweb/example/snippet/ValidateSession.scala @@ -15,41 +15,31 @@ */ package net.liftweb { -package example { -package snippet { + package example { + package snippet { -import _root_.net.liftweb.example.model._ -import _root_.net.liftweb.example.lib._ -import _root_.scala.xml.{NodeSeq, Text, Group, Node} -import _root_.net.liftweb.http._ -import _root_.net.liftweb.http.S -import _root_.net.liftweb.mapper._ -import _root_.net.liftweb.http.S._ -import js._ -import JsCmds._ -import _root_.net.liftweb.util.Helpers._ -import _root_.net.liftweb.common._ -import _root_.net.liftweb.util._ -import _root_.scala.collection.mutable.HashMap + import _root_.net.liftweb.example.lib._ + import _root_.scala.xml.{NodeSeq} + import _root_.net.liftweb.http._ + import _root_.net.liftweb.http.S + import js._ + import JsCmds._ + /** + * This snippet handles counting + */ + class ValidateSession { - -/** - * This snippet handles counting - */ -class ValidateSession { - - /** - * This method is invoked by the <lift:Count /> tag - */ - def render(in: NodeSeq): NodeSeq = - SHtml.ajaxButton("Validate", - () => { - LoginStuff(true) - S.notice("Your session is validated") - RedirectTo("/login/index") - }) -} -} -} + /** + * This method is invoked by the <lift:Count /> tag + */ + def render(in: NodeSeq): NodeSeq = + SHtml.ajaxButton("Validate", () => { + LoginStuff(true) + S.notice("Your session is validated") + RedirectTo("/login/index") + }, "class" -> "btn btn-outline-primary") + } + } + } } 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..14060c5 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 @@ -15,97 +15,144 @@ */ package net.liftweb { -package example { -package snippet { - + package example { + package snippet { /* - -import _root_.net.liftweb.example.model._ -import _root_.scala.xml.{NodeSeq, Text, Group} -import _root_.net.liftweb.http.{S, SHtml} -import _root_.net.liftweb.mapper._ -import _root_.net.liftweb.http.S._ -import _root_.net.liftweb.util._ -import _root_.net.liftweb.util.Helpers._ -import _root_.net.liftweb.textile._ + import _root_.net.liftweb.example.model._ + import _root_.scala.xml.{NodeSeq, Text, Group} + import _root_.net.liftweb.http.{S, SHtml} + import _root_.net.liftweb.mapper._ + import _root_.net.liftweb.http.S._ + import _root_.net.liftweb.util._ + import _root_.net.liftweb.util.Helpers._ + import _root_.net.liftweb.textile._ // show determines which one is used. bind hooks the content into the lift view -case class BindChoice(show: Boolean, bind: () => NodeSeq) - -class Wiki extends MetaWikiEntry { - - def uriFor(path:String) = "/wiki/" + path - - /** - * Display the Textile marked up wiki or an edit box - */ - def main: NodeSeq = { - val pageName = S.param("wiki_page") openOr "HomePage" // set the name of the page - def showAll = { - findAll(OrderBy(WikiEntry.name, Ascending)).flatMap(entry => - ) - } - - if (pageName == "all") showAll // if the page is "all" display all the pages - else { - // find the entry in the database or create a new one - val entry = find(By(WikiEntry.name, pageName)) openOr create.name(pageName) - - // is it a new entry? - val isNew = !entry.saved_? - - // show edit or just display - val edit = isNew || (S.param("param1").map(_ == "edit") openOr false) - - Show All Pages
{ + case class BindChoice(show: Boolean, bind: () => NodeSeq) + + class Wiki extends MetaWikiEntry { + + def uriFor(path: String) = "/wiki/" + path + + /** + * Display the Textile marked up wiki or an edit box + */ + def main: NodeSeq = { + val pageName = S.param("wiki_page") openOr "HomePage" // set the name of the page + def showAll = { + findAll(OrderBy(WikiEntry.name, Ascending)).flatMap(entry => + ) + } + + if (pageName == "all") + showAll // if the page is "all" display all the pages + else { + // find the entry in the database or create a new one + val entry = find(By(WikiEntry.name, pageName)) openOr create.name( + pageName) + + // is it a new entry? + val isNew = !entry.saved_? + + // show edit or just display + val edit = isNew || (S + .param("param1") + .map(_ == "edit") openOr false) + + Show All Pages
{ if (edit) editEntry(entry, isNew, pageName) else TextileParser.toHtml(entry.entry, Some(TextileParser.DefaultRewriter("/wiki"))) ++
Edit // and add an "edit" link }
- } - } - - def choosebind(xhtml : NodeSeq) = { - def pageName = S.param("wiki_page") openOr "HomePage" // set the name of the page - - def showAll = BindChoice((pageName == "all"), () => bind("pages", - (xhtml \\ "showAll").filter(_.prefix == "wiki").toList.head.child, - TheBindParam("all", findAll(OrderBy(WikiEntry.name, Ascending)).flatMap(entry => - )))) - - // find the entry in the database or create a new one - def entry = find(By(WikiEntry.name, pageName)) openOr create.name(pageName) - - // is it a new entry? - def isNew = !entry.saved_? - def toEdit = isNew || (S.param("param1").map(_ == "edit") openOr false) - - def edit = BindChoice(toEdit, () => bind("edit", - (xhtml \\ "editting").filter(_.prefix == "wiki").toList.head.child, - "form" -> editEntry(entry, isNew, pageName))) - - def view = BindChoice(!toEdit, () => bind("view", - (xhtml \\ "displaying").filter(_.prefix == "wiki").toList.head.child, - TheBindParam("name", Text(pageName)), - TheBindParam("value", (TextileParser.toHtml(entry.entry, - Some(TextileParser.DefaultRewriter("/wiki"))) ++ -
Edit)))) - - (showAll :: edit :: view :: Nil).find(_.show == true).map(_.bind()) match { - case Some(x) => x - case _ => - } - } - - private def editEntry(entry: WikiEntry, isNew: Boolean, pageName: String) = { - val action = uriFor(pageName) - val message = if (isNew) Text("Create Entry named "+pageName) else Text("Edit entry named "+pageName) - val hobixLink =  Textile Markup Reference
- val cancelLink = Cancel - val textarea = entry.entry.toForm - val submitButton = SHtml.submit(isNew ? "Add" | "Edit", () => entry.save) -
{ // the form tag + } + } + + def choosebind(xhtml: NodeSeq) = { + def pageName = + S.param("wiki_page") openOr "HomePage" // set the name of the page + + def showAll = + BindChoice( + (pageName == "all"), + () => + bind( + "pages", + (xhtml \\ "showAll") + .filter(_.prefix == "wiki") + .toList + .head + .child, + TheBindParam( + "all", + findAll(OrderBy(WikiEntry.name, Ascending)).flatMap(entry => + )) + ) + ) + + // find the entry in the database or create a new one + def entry = + find(By(WikiEntry.name, pageName)) openOr create.name(pageName) + + // is it a new entry? + def isNew = !entry.saved_? + def toEdit = + isNew || (S.param("param1").map(_ == "edit") openOr false) + + def edit = + BindChoice(toEdit, + () => + bind("edit", + (xhtml \\ "editting") + .filter(_.prefix == "wiki") + .toList + .head + .child, + "form" -> editEntry(entry, isNew, pageName))) + + def view = + BindChoice( + !toEdit, + () => + bind( + "view", + (xhtml \\ "displaying") + .filter(_.prefix == "wiki") + .toList + .head + .child, + TheBindParam("name", Text(pageName)), + TheBindParam( + "value", + (TextileParser.toHtml( + entry.entry, + Some(TextileParser.DefaultRewriter("/wiki"))) ++ +
Edit)) + ) + ) + + (showAll :: edit :: view :: Nil) + .find(_.show == true) + .map(_.bind()) match { + case Some(x) => x + case _ => + } + } + + private def editEntry(entry: WikiEntry, + isNew: Boolean, + pageName: String) = { + val action = uriFor(pageName) + val message = + if (isNew) Text("Create Entry named " + pageName) + else Text("Edit entry named " + pageName) + val hobixLink = +  Textile Markup Reference
+ val cancelLink = Cancel + val textarea = entry.entry.toForm + val submitButton = + SHtml.submit(isNew ? "Add" | "Edit", () => entry.save) + { // the form tag message ++ hobixLink ++ textarea ++ // display the form @@ -114,9 +161,9 @@ class Wiki extends MetaWikiEntry { Text(" ") ++ submitButton } - } -} + } + } */ -} -} + } + } } 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 3df412f..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 @@ -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 shouldSave = field("Save ?", false, "style" -> "vertical-align:bottom") - val likeCats = builder("Do you like cats?", "") ^/ - (s => if (Helpers.toBoolean(s)) Nil else "You have to like cats") make + 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) + 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") } } } @@ -122,34 +139,37 @@ 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 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 { 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 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 +178,20 @@ 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 = _), + "class" -> "form-control")) - 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 +203,24 @@ 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), + "class" -> "form-control")) + 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 +233,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 +261,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/scala/net/liftweb/example/view/XmlFun.scala b/combo/example/src/main/scala/net/liftweb/example/view/XmlFun.scala index 0cc1f6d..530d7d0 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 @@ -14,15 +14,12 @@ * limitations under the License. */ -package net.liftweb { -package example { -package view { +package net.liftweb.example.view -import _root_.scala.xml.{Text, Node, NodeSeq} -import _root_.net.liftweb.http._ +import scala.xml.{Text, NodeSeq} +import net.liftweb.http._ import S._ -import _root_.net.liftweb.common._ -import _root_.net.liftweb.util._ +import net.liftweb.common._ class XmlFun extends LiftView { def dispatch = Map("index" -> render _) @@ -30,45 +27,50 @@ class XmlFun extends LiftView { def render = { val addresses = List( addressNode("123 any street", null, "SF", "CA", "94122", "US"), - addressNode("456 other lane", "flat 3", "London", "", "NW3", "GB"), - addressNode("14 gordon st", "#204", "Brighton", "MA", "02135", "US"), - addressNode("37 foo lane", null, "Ixtapa", "MX", "ABC", "MX"), - addressNode("44 sheep st", "#1", "Liverpool", "", "GE1", "GB"), - addressNode("74 nice st", "#1801", "Chicago", "IL", "60606", "US")) + addressNode("456 other lane", "flat 3", "London", "", "NW3", "GB"), + addressNode("14 gordon st", "#204", "Brighton", "MA", "02135", "US"), + addressNode("37 foo lane", null, "Ixtapa", "MX", "ABC", "MX"), + addressNode("44 sheep st", "#1", "Liverpool", "", "GE1", "GB"), + addressNode("74 nice st", "#1801", "Chicago", "IL", "60606", "US") + ) val toCount = param("country") openOr "US" - Full( - - -

The XML is -

{addresses.map{e => Text(e.toString) :: 
:: Nil}} -

-

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

- -

Count US addresses.

-

Count GB addresses.

- - -
) + Full( + + + Template + + +
+

XML Fun

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

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

+

Count US addresses.

+

Count GB addresses.

+
+ + ) } - private def addressNode(line1: String, line2: String, city: String, - state: String, zip_pc: String, country: String) = -
+ private def addressNode(line1: String, + line2: String, + city: String, + state: String, + zip_pc: String, + country: String) = +
{line1}{ if (line2 != null && line2.length > 0) {line2} else Text("")} {city} {state} {country}
- private def countryCount(toMatch: String, xml: NodeSeq) = - (for { + (for { addr <- xml \\ "address" - country <- addr \\ "country" if country.text == toMatch} - yield country).length + country <- addr \\ "country" if country.text == toMatch + } yield country).length } -} -} -} diff --git a/combo/example/src/main/webapp/WEB-INF/geronimo-web.xml b/combo/example/src/main/webapp/WEB-INF/geronimo-web.xml new file mode 100644 index 0000000..46ca12c --- /dev/null +++ b/combo/example/src/main/webapp/WEB-INF/geronimo-web.xml @@ -0,0 +1,21 @@ + + + + + + + + net.liftweb + demo + 0.9.4-SNAPSHOT + + + + + !org.apache.commons.codec* + + + /demo + + + diff --git a/combo/example/src/main/webapp/_bsdialog_confirm.html b/combo/example/src/main/webapp/_bsdialog_confirm.html new file mode 100644 index 0000000..30cef6f --- /dev/null +++ b/combo/example/src/main/webapp/_bsdialog_confirm.html @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/combo/example/src/main/webapp/_chat_fixed.html b/combo/example/src/main/webapp/_chat_fixed.html index daa76de..4ffd4fa 100644 --- a/combo/example/src/main/webapp/_chat_fixed.html +++ b/combo/example/src/main/webapp/_chat_fixed.html @@ -1,4 +1,10 @@ -
- - -
+
+
+
+ +
+
+ +
+
+
\ No newline at end of file diff --git a/combo/example/src/main/webapp/_jsdialog_confirm.html b/combo/example/src/main/webapp/_jsdialog_confirm.html index c0746b7..1a6ca63 100644 --- a/combo/example/src/main/webapp/_jsdialog_confirm.html +++ b/combo/example/src/main/webapp/_jsdialog_confirm.html @@ -1,7 +1,22 @@ -

- Do you really want to destroy Rhode Island?
-
- Yes - No way, dude + + + + + + Template + + + + + -

+ + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/ajax-form.html b/combo/example/src/main/webapp/ajax-form.html index 9e95a78..e1d8047 100644 --- a/combo/example/src/main/webapp/ajax-form.html +++ b/combo/example/src/main/webapp/ajax-form.html @@ -1,84 +1,91 @@ - -
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 = _)
-  }
+
+
+
+
+  
+  Template
+
+
+
+  
+

%*%

+ + + +
Choose city and state:
+ +
+
+
+ +
+
+ +
+ +
+ +
+ +
+
+ +
+
+
- 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)) + The + Lift Scala 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),
+                    s => city = s,
+                    "class" -> "form-control")
   }
 
+  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))
+    }, "class" -> "form-control") &
+      "#city" #> cityChoice(state) &
+      "type=submit" #> submit(?("Save"), () => {
+        S.notice("City: " + city + ", State: " + state); redirectTo("/")
+      }, "class" -> "btn btn-primary")
   }
 }
-
+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..dca03ed 100644 --- a/combo/example/src/main/webapp/ajax.html +++ b/combo/example/src/main/webapp/ajax.html @@ -1,134 +1,163 @@ - - -
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 + + + +
+ + + + + +

%*%

+ + + +
+
+
+ +
+
+ Click me to increase the count (currently + 0) +
+ +
+ +
+ + +
+
+
+ You selected ' + ' from the select box. +
+
+
+
+
+
+ You entered ' + ' in the text box. +
+
+
+
+ +
+
+
+
+
+ +

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

+ +
+ +
+ +
+ +
+
+
+
+ +
+
+ +
+ + 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)
+  }
 
-  
- -

- 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] = {
+    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] = {
-    Log.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>))
   }
+}
+ +
+ - def time = Text(timeNow.toString) -} - - -
+ \ 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..261a40e 100644 --- a/combo/example/src/main/webapp/arc.html +++ b/combo/example/src/main/webapp/arc.html @@ -1,82 +1,84 @@ - -
+ + -

The 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 = {
-    <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/assets/css/app.css b/combo/example/src/main/webapp/assets/css/app.css new file mode 100644 index 0000000..1be8667 --- /dev/null +++ b/combo/example/src/main/webapp/assets/css/app.css @@ -0,0 +1,249 @@ + +.navbar { + background-color: #fff; + box-shadow: 0px 1px 1px 0px rgba(0,0,0,0.25); +} +.navbar .dropdown-menu { + background-color: inherit; + border: none; +} + +.dropdown-divider { + border-top: 1px solid #fff; +} + +.widget { + border: 1px solid #ccc; + background: #f5f5f5; + padding: 5px; + margin: 10px 0 0 0; + font-size: 8pt; +} + +.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; +} +.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 > 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 a:hover, +.sidebar ul li span: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; +} + +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; +} + +.lift_warning { + color: yellow; +} + +.lift_notice {} + +.main-border { + border-left: none; +} + +.dpp_stuff p { + padding-left: 15px; +} + + +/*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; +} + +#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; +} + +.error_class { + border-radius: 5px; + padding: 5px; + background-color: #F2DEDE; + border-color: #EED3D7; + color: #B94A48; +} + +.notice_class { + border-radius: 5px; + padding: 5px; + background-color: #D9EDF7; + border-color: #BCE8F1; + color: #3A87AD; +} + +.widget ul { + padding: 5px; +} + +.widget ul li { + margin: 0; + padding: 0; + list-style: none; + border: 1px solid #ccc; + border-bottom: none; +} + +.widget ul li:last-child { + 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; + padding: 0.2rem 0.8rem; +} + +@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/async_rest.html b/combo/example/src/main/webapp/async_rest.html index 13b304f..2602660 100644 --- a/combo/example/src/main/webapp/async_rest.html +++ b/combo/example/src/main/webapp/async_rest.html @@ -1,75 +1,62 @@ - -
- 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 + + + +
+

%*%

+ + + +
+
+
-
- +
+ 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
-          
-          // 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}
+      // 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}
             inner: {innerSesStr}</i>
           }
         })
   }
-}
-
+}
+
- + + + + \ 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..b306087 100644 --- a/combo/example/src/main/webapp/chat.html +++ b/combo/example/src/main/webapp/chat.html @@ -1,159 +1,148 @@ - - -

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

- -

- When the Chat comet widget is added to the page, it needs to solict the - user for a "chat name". It asks the "AskName" comet 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 {
-  private var userName = ""
-  private var chats: List[ChatLine] = Nil
+    
+
+
- /* need these vals to be set eagerly, within the scope +
+ +
Group Chat
+ +
+
+ Hello + Your name +
    +
  • + 12:15 + dpp + + My Message + +
  • +
+
+
+

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

+ +

+ When the Chat comet widget is added to the page, it needs to solict the user for a "chat name". It asks the + "AskName" comet + 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":

+ + The + Lift Scala code +
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 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. +

+
+ -

- 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/count.html b/combo/example/src/main/webapp/count.html index 3af6e76..29e662e 100644 --- a/combo/example/src/main/webapp/count.html +++ b/combo/example/src/main/webapp/count.html @@ -1,73 +1,67 @@ - -

The Number Counting Example

-

The first value is Increment Decrement

+ + -

The second value is Increment Decrement

- - - + + + 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("--"))
   }
-}
-
+}
+ +
+ - + \ No newline at end of file diff --git a/combo/example/src/main/webapp/database.html b/combo/example/src/main/webapp/database.html index 90f2628..c7080d1 100644 --- a/combo/example/src/main/webapp/database.html +++ b/combo/example/src/main/webapp/database.html @@ -1,83 +1,71 @@ - -

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

+ + -

-

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

+ + + Template + - + +
+

%*%

-
-/**
- * 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)
+        
+
+        
+
+
+ +

+ 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
+
+ There are + records in the "Person" table. The first one is + . +

Press 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] {
+  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)
 }
 
-/**
- * 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
+/**
+  * 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"
+  // 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. +

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, + Update, Read, and Delete itself from the database. It knows how to convert itself to a string, to XML, and + even + generate HTML forms for itself.

-Each of the fields knows how to validate itself. The -User (and any other "Mapped" class) knows how to Create, -Update, Read, and Delete itself from the database. It knows -how to convert itself to a string, to XML, and even generate -HTML forms for itself.

+
+ -
+ \ 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..102bf50 100644 --- a/combo/example/src/main/webapp/file_upload.html +++ b/combo/example/src/main/webapp/file_upload.html @@ -1,25 +1,107 @@ - -

-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 + + + +
+

%*%

+ + + +

+ 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:
+

+
+ + +
+
+
+ + +
+
+
+ +
+
+
+ + + +
+ 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">
+        <div class="form-group row">
+          <div class="col-md-8">
+             <div class="custom-file">
+               <input type="file" class="custom-file-input" id="customFile">
+               <label class="custom-file-label" for="customFile">Select a file to upload</label>
+            </div>
+          </div>
+        </div>
+        <input type="submit" value="Upload File" class="btn btn btn-primary">
+        </span>
+
+    </span>
+</form>
+ + 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" &
+      ".custom-file-input" #> 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)
+ +
+ + + \ 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..445ea2d 100644 --- a/combo/example/src/main/webapp/form_ajax.html +++ b/combo/example/src/main/webapp/form_ajax.html @@ -1,10 +1,92 @@ - -
Forms with Ajax callback on form element blur
-
- Enter your first and last name:
-
- First Name:
- Last Name:
- -
-
+ + + + + + Template + + + +
+

%*%

+ + + +
+
+
+ +

Example: Forms with Ajax callback on form element blur

+ Enter your first and last name: +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ + + + + + +
+
+ The + Lift Scala code +
class FormWithAjax extends StatefulSnippet {
+  private var firstName = ""
+  private var lastName = ""
+  private val to = "#wrappedNoticeAtHead"
+
+  def dispatch = {
+    case _ => render
+  }
+
+  def render = {
+
+    def validate() {
+      (firstName.length, lastName.length) match {
+        case (f, n) if f < 2 && n < 2 =>
+          S.error("fwaErrMsg", "First and last names too short")
+        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(to)
+        }
+      }
+    }
+
+    "#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 _, "class" -> "btn btn-primary")
+  }
+}
+ +
+ + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/guess.html b/combo/example/src/main/webapp/guess.html index 6a05db1..e34c821 100644 --- a/combo/example/src/main/webapp/guess.html +++ b/combo/example/src/main/webapp/guess.html @@ -1,131 +1,110 @@ - - - - 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>
-    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))
+
+
+
+
+  
+  Template
+
+
+
+  
+

%*%

+ + + +
+
+
+ +
+ + + 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) = 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) {
+
+  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: Can[Int] = Empty
-  private var count = 0
-}
-  
+ private val number = 1 + randomInt(100) + private var lastGuess: Box[Int] = Empty + private var count = 0 +}
+ +
+ - + \ 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..9bee54c 100644 --- a/combo/example/src/main/webapp/index.html +++ b/combo/example/src/main/webapp/index.html @@ -1,54 +1,72 @@ - -

Welcome to the Lift Web Framework

-
- Lift provides the best features for building interactive - web applications: - -
-
-

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 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
+
+
+
+
+    
+    Template
+
+
+
+    
+

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 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.) +

+ +
+ 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..26b13dc 100644 --- a/combo/example/src/main/webapp/interactive.html +++ b/combo/example/src/main/webapp/interactive.html @@ -1,81 +1,64 @@ - -

- 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>)})
-  
-  
+ + + Template + -
+ +
+

%*%

-

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) 
+        

+ 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)
 
-  private lazy val spanId = uniqueId+"_timespan"
+  def render = "#clock_time *" #> Text(now.toString)
 
-  def render = bind("time" -> timeSpan)
+  override def lowPriority = {
+    case Tick =>
+      partialUpdate(SetHtml("clock_time", Text(now.toString)))
+      Schedule.schedule(this, Tick, 10 seconds)
+  }
 
-  def timeSpan = (<span id={spanId}>{timeNow}</span>)
+  val creationInfo = new CometCreationInfo(initType.orNull,
+                                           initName,
+                                           initDefaultXml,
+                                           initAttributes,
+                                           initSession)
 
-  override def lowPriority = {
-    case Tick =>
-      partialUpdate(SetHtml(spanId, Text(timeNow.toString)))
-    ActorPing.schedule(this, Tick, 10 seconds) 
-  }
+  initCometActor(creationInfo)
 }
-  
-
- + +case object Tick + +
+ + + \ 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..3761906 100644 --- a/combo/example/src/main/webapp/invoice_wiring.html +++ b/combo/example/src/main/webapp/invoice_wiring.html @@ -1,93 +1,141 @@ - + + -
- An example of using Lift's Wiring feature to build an invoice system. -
+ + + Template + -
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/json.html b/combo/example/src/main/webapp/json.html index 07540e3..0995dd1 100644 --- a/combo/example/src/main/webapp/json.html +++ b/combo/example/src/main/webapp/json.html @@ -1,71 +1,106 @@ - -
JSON Samples
-
-
- - -
- -
- -
-
+ + -

Here's the code:

+ + + Template + - - -
-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")) -} - + The Markup +
<div data-lift="JsonCall">
+  <p>Enter an addition question:</p>
 
+  <div class="form-row">
+    <div class="form-inline">
+      <input id="x" class="form-control">
+      <label">+</label>
+      <input id="y" class="form-control">
+      <label">=</label>
+      <input id="z" class="form-control">
+      <button class="btn btn-primary form-control">Check</button>
+    </div>
+  </div>
+
+</div>
+
+<script type="text/javascript">
+  // <![CDATA[
+  function currentQuestion() {
+    return {
+      first: parseInt($('#x').val()),
+      second: parseInt($('#y').val()),
+      answer: parseInt($('#z').val())
+    };
+  }
+  // ]]>
+</script>
+ + + -
+ \ No newline at end of file diff --git a/combo/example/src/main/webapp/json_more.html b/combo/example/src/main/webapp/json_more.html index 13c39cc..8e3a4b0 100644 --- a/combo/example/src/main/webapp/json_more.html +++ b/combo/example/src/main/webapp/json_more.html @@ -1,11 +1,24 @@ - -
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 +47,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 +89,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 +172,9 @@
 
+
+ + -
+ \ No newline at end of file diff --git a/combo/example/src/main/webapp/lang.html b/combo/example/src/main/webapp/lang.html index 918ebb7..3882aec 100644 --- a/combo/example/src/main/webapp/lang.html +++ b/combo/example/src/main/webapp/lang.html @@ -1,58 +1,68 @@ - - -
-

Hello

- My language: lang - -
- Set locale - + + + + + + Template + + + +
+

%*%

+ + + +
+
- - -
-

- 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)
+
+    
+

+ 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..d179e67 100644 --- a/combo/example/src/main/webapp/lang_ca.html +++ b/combo/example/src/main/webapp/lang_ca.html @@ -1,22 +1,42 @@ - - - Lift Example - - -
- -

Hola

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

%*%

+ + + +
+
+
+ +
+

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..78caa0e 100644 --- a/combo/example/src/main/webapp/lang_de.html +++ b/combo/example/src/main/webapp/lang_de.html @@ -1,13 +1,42 @@ - -
-

Hallo

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

%*%

+ + + +
+
+
+ + +

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..9c50138 100644 --- a/combo/example/src/main/webapp/lang_en_CA.html +++ b/combo/example/src/main/webapp/lang_en_CA.html @@ -1,57 +1,61 @@ - -
-

Good Day, eh.

- My language: lang - -
- Set locale - + + + + + + Template + + + + +
+

%*%

+ + + +
+
- - -
-

- 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)
+
+    
+

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..7ba5749 100644 --- a/combo/example/src/main/webapp/lang_en_US.html +++ b/combo/example/src/main/webapp/lang_en_US.html @@ -1,8 +1,26 @@ - + + + + + + Template + + + + +
+

%*%

+ + + +
+
+
+ +
+

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..c4e2767 100644 --- a/combo/example/src/main/webapp/lang_es.html +++ b/combo/example/src/main/webapp/lang_es.html @@ -1,13 +1,42 @@ - -
-

Hola

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

%*%

+ + + +
+
+
+ + +

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..aa9a94e 100644 --- a/combo/example/src/main/webapp/lang_fr.html +++ b/combo/example/src/main/webapp/lang_fr.html @@ -1,13 +1,42 @@ - -
-

Bonjour

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

%*%

+ + + +
+
+
+ + +

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..4e7e9b5 100644 --- a/combo/example/src/main/webapp/lang_it.html +++ b/combo/example/src/main/webapp/lang_it.html @@ -1,13 +1,42 @@ - -
-

Ciao

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

%*%

+ + + +
+
+
+ + +

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..a3499e8 100644 --- a/combo/example/src/main/webapp/lang_no.html +++ b/combo/example/src/main/webapp/lang_no.html @@ -1,12 +1,42 @@ - -
-

Hei

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

%*%

+ + + +
+
+
+ + +

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..6e3f661 100644 --- a/combo/example/src/main/webapp/lang_sv.html +++ b/combo/example/src/main/webapp/lang_sv.html @@ -1,14 +1,61 @@ - -
-

Hej

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

%*%

+ + + +
+
+
+ +
+

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/lazy.html b/combo/example/src/main/webapp/lazy.html index bc3e0e1..bb9fde0 100644 --- a/combo/example/src/main/webapp/lazy.html +++ b/combo/example/src/main/webapp/lazy.html @@ -1,95 +1,63 @@ - -

- 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. -

- - -
-
-
+ + -
-
-
+ + + 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>
-    <lift:lazy-load><div class="lift:LongTime"></div></lift:lazy-load>
-  </div>
-
+ The Markup +
<div data-lift="lazy-load">
+  <div data-lift="LongTime"></div>
+</div>
-

The Scala Code:

-
-object LongTime {
-  def render = {
-    val delay = 1000L + randomLong(10000)
+    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/login/index.html b/combo/example/src/main/webapp/login/index.html index b2e4812..5356aff 100644 --- a/combo/example/src/main/webapp/login/index.html +++ b/combo/example/src/main/webapp/login/index.html @@ -1,9 +1,23 @@ - + + -

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

+ + + Template + + +
+

%*%

- \ No newline at end of file + + +

+ 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 db03f5c..d588371 100644 --- a/combo/example/src/main/webapp/login/validate.html +++ b/combo/example/src/main/webapp/login/validate.html @@ -1,29 +1,45 @@ - + + -

-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: -

+ + + Template + -

- +

+

%*%

+ + + +

+ 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/longtime.html b/combo/example/src/main/webapp/longtime.html index 8f112fe..e117756 100644 --- a/combo/example/src/main/webapp/longtime.html +++ b/combo/example/src/main/webapp/longtime.html @@ -1,82 +1,64 @@ - -
+ + -

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) +
- // get messages from the ThingBuilder - override def highPriority = { - case BuildStatus(p, Empty) => - this.progress = p - reRender(false) + 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
 
-    case BuildStatus(_, Full(u)) =>
+  // a CometActor that has not been displayed for
+  // 2 minutes is destroyed
+  override def lifespan: Box[TimeSpan] = Full(2 minutes)
+
+  // 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/menu/four.html b/combo/example/src/main/webapp/menu/four.html index 4e65fbc..f7773d6 100644 --- a/combo/example/src/main/webapp/menu/four.html +++ b/combo/example/src/main/webapp/menu/four.html @@ -1,3 +1,20 @@ - - Submenu Page 4 - + + + + + + Template + + + +
+

%*%

+ + + +

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 3e7b0e1..b54ff01 100644 --- a/combo/example/src/main/webapp/menu/index.html +++ b/combo/example/src/main/webapp/menu/index.html @@ -1,3 +1,20 @@ - - Main Menu page... note the submenus on the menu bar - + + + + + + Template + + + +
+

%*%

+ + + +

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 39b3634..fd6061c 100644 --- a/combo/example/src/main/webapp/menu/one.html +++ b/combo/example/src/main/webapp/menu/one.html @@ -1,3 +1,21 @@ - - Submenu Page 1 - + + + + + + Template + + + + +
+

%*%

+ + + +

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 832bf09..29d8df7 100644 --- a/combo/example/src/main/webapp/menu/three.html +++ b/combo/example/src/main/webapp/menu/three.html @@ -1,3 +1,21 @@ - - Submenu Page 3 - + + + + + + Template + + + + +
+

%*%

+ + + +

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 560db3e..a01e55b 100644 --- a/combo/example/src/main/webapp/menu/two.html +++ b/combo/example/src/main/webapp/menu/two.html @@ -1,3 +1,21 @@ - - Submenu Page 2 - + + + + + + Template + + + + +
+

%*%

+ + + +

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 6d2f9e9..c0aa4e9 100644 --- a/combo/example/src/main/webapp/menu/two_one.html +++ b/combo/example/src/main/webapp/menu/two_one.html @@ -1,3 +1,21 @@ - - Submenu Page 2-1 - + + + + + + Template + + + + +
+

%*%

+ + + +

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 03cb015..e737995 100644 --- a/combo/example/src/main/webapp/menu/two_two.html +++ b/combo/example/src/main/webapp/menu/two_two.html @@ -1,3 +1,20 @@ - - Submenu Page 2-2 - + + + + + + Template + + + +
+

%*%

+ + + +

Submenu Page 2-2

+ +
+ + + \ 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..5d97678 100644 --- a/combo/example/src/main/webapp/misc.html +++ b/combo/example/src/main/webapp/misc.html @@ -1,9 +1,26 @@ - -

Misc code samples

- -

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

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

%*%

+ + + +
+
+
+ +

+ 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/parallel.html b/combo/example/src/main/webapp/parallel.html index 23e77fe..e79230f 100644 --- a/combo/example/src/main/webapp/parallel.html +++ b/combo/example/src/main/webapp/parallel.html @@ -1,101 +1,68 @@ - -

- 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: -
-
+ +
+

%*%

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

+ 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: +
+
-
+
-

The Markup:

-
-  <div>
-    Exec parallel:
-    <lift:Parallel  lift:parallel="true"/>
-  </div>
-
+ The Markup +
<div>
+  Exec parallel:
+  <div data-lift="Parallel?parallel=true"></div>
+</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/persistence.html b/combo/example/src/main/webapp/persistence.html index 3ac8c3e..babb62c 100644 --- a/combo/example/src/main/webapp/persistence.html +++ b/combo/example/src/main/webapp/persistence.html @@ -1,7 +1,27 @@ - -

Persistence

-

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

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

%*%

+ + + +
+
+
+ +

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

+ +
+ + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/redirect.html b/combo/example/src/main/webapp/redirect.html index 5d09c21..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 dc249f3..09e8bfc 100644 --- a/combo/example/src/main/webapp/rhodeisland.html +++ b/combo/example/src/main/webapp/rhodeisland.html @@ -1,68 +1,122 @@ - - - - - - Destroy Rhode Island - + + -

The code:

- - + + + Template + -
-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(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>)) + + +
+
+
+ +

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. +

+ +

Bootstrap dialog example

+ Destroy Rhode Island + +
+
+ The + Lift Scala 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")
+            // we could have used closeDialog here but we
+            // are instead using the data-dismiss attribute.
+            Noop
+          },
+          "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
 }
-
+
+
+

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>)
+  }
+}
-
+
+ + \ 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/simple.html b/combo/example/src/main/webapp/simple.html index 30f41f9..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/simple/add.html b/combo/example/src/main/webapp/simple/add.html index b747b6d..9d8e296 100644 --- a/combo/example/src/main/webapp/simple/add.html +++ b/combo/example/src/main/webapp/simple/add.html @@ -1,5 +1,27 @@ - - -
-
-
+ + + + + + Template + + + +
+

%*%

+ + + +
+
+
+ + + + +
foo
+ +
+ + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/simple/delete.html b/combo/example/src/main/webapp/simple/delete.html index cfb02cf..fc009cd 100644 --- a/combo/example/src/main/webapp/simple/delete.html +++ b/combo/example/src/main/webapp/simple/delete.html @@ -1,6 +1,31 @@ - -
- Do you really want to delete - No -
-
+ + + + + + Template + + + +
+

%*%

+ + + +
+
+
+ +
+

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

+
+ No +
+
+
+
+ + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/simple/edit.html b/combo/example/src/main/webapp/simple/edit.html index c35fd26..109a557 100644 --- a/combo/example/src/main/webapp/simple/edit.html +++ b/combo/example/src/main/webapp/simple/edit.html @@ -1,7 +1,27 @@ - - - - - -
foo
-
+ + + + + + Template + + + +
+

%*%

+ + + +
+
+
+ + + + + +
foo
+
+ + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/simple/index.html b/combo/example/src/main/webapp/simple/index.html index 9d92b91..0d2fa8f 100644 --- a/combo/example/src/main/webapp/simple/index.html +++ b/combo/example/src/main/webapp/simple/index.html @@ -1,6 +1,28 @@ - - Add a User - - -
test
-
+ + + + + + Template + + + +
+

%*%

+ + + +
+
+
+ + + + + +
test
+ Add a User +
+ + + \ No newline at end of file diff --git a/combo/example/src/main/webapp/simple_screen.html b/combo/example/src/main/webapp/simple_screen.html index fc65875..83a9eee 100644 --- a/combo/example/src/main/webapp/simple_screen.html +++ b/combo/example/src/main/webapp/simple_screen.html @@ -1,3 +1,82 @@ - -
-
+ + + + + + Template + + + +
+

%*%

+ + + +
+
+
+ +
+ +
+ + 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/simple_wiring.html b/combo/example/src/main/webapp/simple_wiring.html index c3c660d..cfa8d29 100644 --- a/combo/example/src/main/webapp/simple_wiring.html +++ b/combo/example/src/main/webapp/simple_wiring.html @@ -1,16 +1,96 @@ - + + -
- 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
-
+ +
+

%*%

+ + + +
+
+
+ +

+ 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/simple_wizard.html b/combo/example/src/main/webapp/simple_wizard.html index 395ddec..072f615 100644 --- a/combo/example/src/main/webapp/simple_wizard.html +++ b/combo/example/src/main/webapp/simple_wizard.html @@ -1,110 +1,127 @@ - -
+ + -
- 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 = ""
-
-  /**
-   * 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}
-  }
+    

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

- TemplateFinder.findAnyTemplate(List("templating", "pageOne")).map(html => - bind("wizard", html, "name" -> text(name, s => name = s), "submit" -> submit(S.?("Next"), validate))) openOr NodeSeq.Empty + 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 = ""
 
-  /**
-   * 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}
-  }
+  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
+        }
+    }
 
-  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 14fed56..3eba499 100644 --- a/combo/example/src/main/webapp/stateless_json.html +++ b/combo/example/src/main/webapp/stateless_json.html @@ -1,81 +1,74 @@ - -
Stateless JSON Sample
-
- -
- -
- -
+ + -

Here's the code:

+ + + Template + + - +
-
-/**
- * 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)
-    }
-  }
+    

%*%

+ + + +
+
+
- 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> - })) -} -
+
+ +
+ + The Scala Code +
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
 
+  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/template.html b/combo/example/src/main/webapp/template.html index 7008fc5..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/_embeddedTopNavbar.html b/combo/example/src/main/webapp/templates-hidden/_embeddedTopNavbar.html new file mode 100644 index 0000000..bfd9c12 --- /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 new file mode 100644 index 0000000..1fae614 --- /dev/null +++ b/combo/example/src/main/webapp/templates-hidden/_resources.html @@ -0,0 +1,29 @@ + + + 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 + + Interactive Stuff + Persistence + Templating + Menus + Misc code + 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 9efdede..c5ccc7b 100644 --- a/combo/example/src/main/webapp/templates-hidden/default.html +++ b/combo/example/src/main/webapp/templates-hidden/default.html @@ -1,151 +1,116 @@ - - - - - - - Lift Web Framework: %*% - - - - - - - + + + + + + + + + Lift Web Framework: %*% + + + + + + + + + + + + + + +
-
- Lift Web Framework - -
-
-
-
-