-
Notifications
You must be signed in to change notification settings - Fork 275
/
LazyLoad.scala
161 lines (136 loc) · 4.73 KB
/
LazyLoad.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/*
* Copyright 2007-2012 WorldWide Conferencing, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.liftweb
package builtin
package snippet
import xml._
import http._
import common._
import actor._
import util._
import http.js._
import JsCmds._
import JE._
import S._
import Helpers._
/**
* Enclose your snippet tags on your template with LazyLoad and the snippet will execute
* on a different thread, which avoids blocking the page render.
*/
object LazyLoad extends DispatchSnippet {
private object myFuncName extends TransientRequestVar(Helpers.nextFuncName)
private object myActor extends TransientRequestVar[Box[CometActor]](Empty)
def dispatch: DispatchIt = {
case _ => render _
}
/**
* Enclose your snippet like this:
*
* <pre name="code" class="xml">
* <div class="lift:LazyLoad">
* <div class="lift:MyLongRunningSnippet"></div>
* </div>
* </pre>
*
* You can add the template attribute to the LazyLoad tag and instead of
* showing the spinning circle, it will render your template.
*
*
* <pre name="code" class="xml">
* <div class="lift:LazyLoad?template='my-nice-wait-message-template'">
* <div class="lift:MyLongRunningSnippet"></div>
* </div>
* </pre>
*
*
*/
def render(xhtml: 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 @ "AsyncRenderComet",
name,
defaultXml,
attributes,
session) => {
val ret = new AsyncRenderComet()
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, xhtml))
<div id={id}>
{
S.attr("template") match {
case Full(template) => <lift:embed what={template}/>
case _ => <img src="/images/ajax-loader.gif" alt="Loading"/>
}
}
</div>++ (myActor.is match {
case Full(actor) => actor ! Ready(func); NodeSeq.Empty
case _ => session.setupComet("AsyncRenderComet", Full(myFuncName.is), Ready(func))
<tail><lift:comet type="AsyncRenderComet" name={myFuncName.is}/></tail>
})
}) match {
case Full(x) => x
case Empty => Comment("FIX"+ "ME: session or request are invalid")
case Failure(msg, _, _) => Comment(msg)
}
}
}
private case class Ready(js: () => JsCmd)
private case class Render(js: JsCmd)
/**
* The Comet Actor for sending down the computed page fragments
*
*/
class AsyncRenderComet extends CometActor {
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(js) =>
Schedule.schedule(() => this ! Render(js()), 0 seconds)
// render it
case Render(js) =>
partialUpdate(js)
}
}