Created
March 8, 2012 08:26
-
-
Save tuhlmann/1999655 to your computer and use it in GitHub Desktop.
AsyncRender - Asynchronously render parts of a template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package me.sgrouples.snippet | |
/* | |
* Copyright 2007-2011 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. | |
*/ | |
import net.liftweb.http.DispatchSnippet | |
import net.liftweb.http.TransientRequestVar | |
import net.liftweb.util.Helpers | |
import net.liftweb.util.Helpers._ | |
import net.liftweb.common._ | |
import net.liftweb.http.js.JsCmd | |
import scala.xml.NodeSeq | |
import net.liftweb.http.LiftSession | |
import net.liftweb.util.Schedule | |
import net.liftweb.http.CometActor | |
import net.liftweb.http.S | |
import net.liftweb.http.LiftRules | |
import net.liftweb.http.CometCreationInfo | |
import net.liftweb.http.PerformSetupComet2 | |
import net.liftweb.http.js.JsCmds.Replace | |
import scala.xml.Comment | |
import scala.collection.mutable.ListBuffer | |
import java.util.Date | |
import net.liftweb.http.js.JsCmds._ | |
import net.liftweb.http.SHtml | |
import net.liftweb.http.js.JE.JsRaw | |
import net.liftweb.http.RequestVar | |
import net.liftweb.http.SessionVar | |
/** | |
* | |
*/ | |
object AsyncRender extends DispatchSnippet { | |
private object myFuncName extends RequestVar(Helpers.nextFuncName) | |
private object myActor extends RequestVar[Box[CometActor]](Empty) | |
def dispatch: DispatchIt = { | |
case _ => render _ | |
} | |
def apply(nodeSeqFunc: ()=>NodeSeq): NodeSeq = render(nodeSeqFunc) | |
def render(xhtml: NodeSeq): NodeSeq = render(()=>{ xhtml }) | |
def render(nodeSeqFunc: ()=>NodeSeq): NodeSeq = { | |
(for (session <- S.session ?~ ("FIXME: Invalid session")) yield { | |
// if we haven't created the actor yet, register on this | |
// thread to create the AsyncRenderComet actor | |
if (myActor.isEmpty) { | |
LiftRules.cometCreationFactory.request.set( | |
(ccinfo: CometCreationInfo) => | |
ccinfo match { | |
case CometCreationInfo(theType @ "SgrouplesAsyncRenderComet", name, defaultXml, attributes, session) => { | |
val ret = new SgrouplesAsyncRenderComet() | |
ret.initCometActor(session, Full(theType), name, defaultXml, attributes) | |
ret ! PerformSetupComet2(if (ret.sendInitialReq_?) S.request.map(_.snapshot) else Empty) | |
// and save it in the request var | |
myActor.set(Full(ret)) | |
Full(ret) | |
} | |
case _ => Empty | |
}) | |
} | |
val id = Helpers.nextFuncName | |
val func: () => JsCmd = | |
session.buildDeferredFunction(() => Replace(id, nodeSeqFunc())) | |
<div id={id}> | |
{ | |
S.attr("template") match { | |
case Full(template) if (template == "empty") => <span></span> | |
case Full(template) => <lift:embed what={template}/> | |
case _ => <img src="/images/ajax-loader.gif" alt="Loading"/> | |
} | |
} | |
</div> ++ Script(SnippetHelpers.DocumentReady(SHtml.ajaxInvoke(() => contentIdAvailable(id))._2) | |
) ++ (myActor.is match { | |
case Full(actor) => actor ! Ready(id, func); NodeSeq.Empty | |
case _ => session.setupComet("SgrouplesAsyncRenderComet", | |
Full(myFuncName.is), Ready(id, func) ) | |
NodeSeq.Empty | |
<tail><lift:comet type="SgrouplesAsyncRenderComet" name={myFuncName.is}/></tail> | |
}) | |
}) match { | |
case Full(x) => x | |
case Empty => Comment("FIX"+ "ME: session or request are invalid") | |
case Failure(msg, _, _) => Comment(msg) | |
} | |
} | |
def contentIdAvailable(id: String): Unit = { | |
//println("We have ID "+id) | |
myActor.is match { | |
case Full(actor) => actor ! IdIsAvailable(id, true) | |
case _ => "No Actor Found, Should never happen." | |
} | |
} | |
} | |
private case class Ready(id: String, js: () => JsCmd) | |
private case class Render(id: String, js: JsCmd) | |
private case class IdIsAvailable(id: String, isAvailable: Boolean) | |
/** | |
* The Comet Actor for sending down the computed page fragments | |
* | |
*/ | |
class SgrouplesAsyncRenderComet extends CometActor { | |
private var isCallbackFuntionInitialized = false | |
private val waitingElements = collection.mutable.Map[String, JsCmd]() | |
private val readyIds = ListBuffer[String]() | |
//override def lifespan: Box[TimeSpan] = Full(90 seconds) | |
def render = NodeSeq.Empty | |
// make this method visible so that we can initialize the actor | |
override def initCometActor(theSession: LiftSession, theType: Box[String], name: Box[String], | |
defaultXml: NodeSeq, attributes: Map[String, String]) { | |
super.initCometActor(theSession, theType, name, defaultXml, attributes) | |
} | |
override def lowPriority : PartialFunction[Any, Unit] = { | |
// farm the request off to another thread | |
case Ready(id, js) => Schedule.schedule(() => this ! Render(id, js()), 0 seconds) | |
// render it | |
case Render(id, js) => { | |
readyIds.find(_ == id) match { | |
case Some(listId) => partialUpdate(js); readyIds -= id | |
case _ => waitingElements += (id -> js) | |
} | |
} | |
case IdIsAvailable(id, isAvailable) => { | |
//println("ID AVAILABLE: "+id) | |
waitingElements.get(id) match { | |
case Some(js) => partialUpdate(js); waitingElements.remove(id) | |
case _ => readyIds += id | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment