Created
September 16, 2013 11:00
-
-
Save alebon/6579232 to your computer and use it in GitHub Desktop.
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 demo.js | |
import net.liftweb.http.js.JsCmd | |
object JsCommands30 { | |
/** | |
* JsSchedule the execution of the JsCmd using setTimeout() | |
* @param what the code to execute | |
*/ | |
case class JsSchedule(what: JsCmd) extends JsCmd { | |
def toJsCmd = s"""setTimeout(function() | |
{ | |
${what.toJsCmd} | |
} , 0);""" | |
} | |
} |
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
/* | |
* 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 http | |
import js._ | |
import net.liftweb.json.JsonAST.JValue | |
import net.liftweb.http.js.JE.JsObj | |
import net.liftweb.http.js.JsCmds._Noop | |
import scala.xml.NodeSeq | |
import net.liftweb.common.{Empty, Box} | |
import net.liftweb.actor.LAScheduler | |
import net.liftweb.json.{JsonAST, Printer, DefaultFormats, Extraction} | |
import net.liftweb.util.Helpers | |
import demo.js.JsCommands30 | |
import net.liftweb.util.Helpers._ | |
import net.liftweb.common.Full | |
import scala.Some | |
import net.liftweb.http.js.JE.JsRaw | |
import net.liftweb.json.JsonAST.JString | |
object Lift30Session { | |
/** | |
* Build a bunch of round-trip calls between the client and the server. | |
* The client calls the server with a parameter and the parameter gets | |
* marshalled to the server and the code is executed on the server. | |
* The result can be an item (JValue) or a Stream of Items. | |
* | |
* If the | |
* The // HERE | |
*/ | |
def buildRoundtrip(info: Seq[RoundTripInfo], liftSession: LiftSession): JsExp = { | |
val ca = new CometActor { | |
/** | |
* It's the main method to override, to define what is rendered by the CometActor | |
* | |
* There are implicit conversions for a bunch of stuff to | |
* RenderOut (including NodeSeq). Thus, if you don't declare the return | |
* turn to be something other than RenderOut and return something that's | |
* coercible into RenderOut, the compiler "does the right thing"(tm) for you. | |
* <br/> | |
* There are implicit conversions for NodeSeq, so you can return a pile of | |
* XML right here. There's an implicit conversion for NodeSeq => NodeSeq, | |
* so you can return a function (e.g., a CssBindFunc) that will convert | |
* the defaultHtml to the correct output. There's an implicit conversion | |
* from JsCmd, so you can return a pile of JavaScript that'll be shipped | |
* to the browser.<br/> | |
* Note that the render method will be called each time a new browser tab | |
* is opened to the comet component or the comet component is otherwise | |
* accessed during a full page load (this is true if a partialUpdate | |
* has occurred.) You may want to look at the fixedRender method which is | |
* only called once and sets up a stable rendering state. | |
*/ | |
def render: RenderOut = NodeSeq.Empty | |
//override def lifespan = Full(LiftRules.clientActorLifespan.vend.apply(this)) | |
override def hasOuter = false | |
override def parentTag = <div style="display: none"/> | |
override def lowPriority: PartialFunction[Any, Unit] = { | |
case jsCmd: JsCmd => partialUpdate(JsCommands30.JsSchedule(JsCmds.JsTry(jsCmd, false))) | |
case jsExp: JsExp => partialUpdate(JsCommands30.JsSchedule(JsCmds.JsTry(jsExp.cmd, false))) | |
case ItemMsg(guid, value) => | |
partialUpdate(JsCommands30.JsSchedule(JsRaw(s"liftAjax.sendEvent(${guid.encJs}, {'success': ${Printer.compact(JsonAST.render(value))}} )").cmd)) | |
case DoneMsg(guid) => | |
partialUpdate(JsCommands30.JsSchedule(JsRaw(s"liftAjax.sendEvent(${guid.encJs}, {'done': true} )").cmd)) | |
case FailMsg(guid, msg) => | |
partialUpdate(JsCommands30.JsSchedule(JsRaw(s"liftAjax.sendEvent(${guid.encJs}, {'failure': ${msg.encJs} })").cmd)) | |
case _ => | |
} | |
} | |
//nasyncComponents.put(ca.theType -> ca.name, ca) | |
//nasyncById.put(ca.uniqueId, ca) | |
ca.callInitCometActor(S.session.get, Full(Helpers.nextFuncName), Full(Helpers.nextFuncName), NodeSeq.Empty, Map.empty) | |
implicit val defaultFormats = DefaultFormats | |
ca ! PerformSetupComet2(Empty) | |
//ca ! SetDeltaPruner(lastWhenDeltaPruner) | |
//val node: Elem = ca.buildSpan(ca.renderClock, NodeSeq.Empty) | |
//S.addCometAtEnd(node) | |
val currentReq: Box[Req] = S.request.map(_.snapshot) | |
val renderVersion = RenderVersion.get | |
val jvmanifest: Manifest[JValue] = implicitly | |
val map = Map(info.map(i => i.name -> i) :_*) | |
def fixIt(in: Any): JValue = { | |
in match { | |
case jv: JValue => jv | |
case a => Extraction.decompose(a) | |
} | |
} | |
def localFunc(in: JValue): JsCmd = { | |
LAScheduler.execute(() => { | |
liftSession.executeInScope(currentReq, renderVersion)( | |
for { | |
JString(guid) <- in \ "guid" | |
JString(name) <- in \ "name" | |
func <- map.get(name) | |
payload = in \ "payload" | |
reified <- if (func.manifest == jvmanifest) Some(payload) else { | |
try {Some(payload.extract(defaultFormats, func.manifest))} catch { | |
case e: Exception => | |
//logger.error("Failed to extract "+payload+" as "+func.manifest, e) | |
ca ! FailMsg(guid, "Failed to extract payload as "+func.manifest+" exception "+ e.getMessage) | |
None | |
} | |
} | |
} { | |
func match { | |
case StreamRoundTrip(_, func) => | |
try { | |
for (v <- func.asInstanceOf[Function1[Any, Stream[Any]]](reified)) { | |
v match { | |
case jsCmd: JsCmd => ca ! jsCmd | |
case jsExp: JsExp => ca ! jsExp | |
case v => ca ! ItemMsg(guid,fixIt(v)) | |
} | |
} | |
ca ! DoneMsg(guid) | |
} catch { | |
case e: Exception => ca ! FailMsg(guid, e.getMessage) | |
} | |
case SimpleRoundTrip(_, func) => | |
try { | |
func.asInstanceOf[Function1[Any, Any]](reified ) match { | |
case jsCmd: JsCmd => ca ! jsCmd | |
case jsExp: JsExp => ca ! jsExp | |
case v => ca ! ItemMsg(guid, fixIt(v)) | |
} | |
ca ! DoneMsg(guid) | |
} catch { | |
case e: Exception => ca ! FailMsg(guid, e.getMessage) | |
} | |
case HandledRoundTrip(_, func) => | |
try { | |
func.asInstanceOf[Function2[Any, RoundTripHandlerFunc, Unit]](reified, new RoundTripHandlerFunc { | |
@volatile private var done_? = false | |
def done() { | |
if (!done_?) { | |
done_? = true | |
ca ! DoneMsg(guid) | |
} | |
} | |
def failure(msg: String) { | |
if (!done_?) { | |
done_? = true | |
ca ! FailMsg(guid, msg) | |
} | |
} | |
/** | |
* Send some JavaScript to execute on the client side | |
* @param value | |
*/ | |
def send(value: JsCmd): Unit = { | |
if (!done_?) { | |
ca ! value | |
} | |
} | |
/** | |
* Send some javascript to execute on the client side | |
* @param value | |
*/ | |
def send(value: JsExp): Unit = { | |
if (!done_?) { | |
ca ! value | |
} | |
} | |
def send(value: JValue) { | |
if (!done_?) { | |
ca ! ItemMsg(guid, value) | |
} | |
} | |
}) | |
} catch { | |
case e: Exception => ca ! FailMsg(guid, e.getMessage) | |
} | |
} | |
}) | |
}) | |
_Noop | |
} | |
lazy val theFunc = JsRaw(s"""function(v) {${SHtml.jsonCall(JsRaw("v"), localFunc(_)).toJsCmd}}""") | |
lazy val build: (String, JsExp) = "_call_server" -> theFunc | |
JsObj(build :: info.map(info => info.name -> JsRaw( | |
s""" | |
|function(param) { | |
| var promise = new liftAjax.Promise(); | |
| liftAjax.associate(promise); | |
| this._call_server({guid: promise.guid, name: ${info.name.encJs}, payload: param}); | |
| return promise; | |
|} | |
|""".stripMargin)).toList :_*) | |
} | |
private case class ItemMsg(guid: String, item: JValue) | |
private case class DoneMsg(guid: String) | |
private case class FailMsg(guid: String, msg: String) | |
} | |
/** | |
* Stuff related to round trip messages | |
*/ | |
sealed trait RoundTripInfo { | |
def name: String | |
def manifest: Manifest[_] | |
} | |
/** | |
* The companion objects. Has tasty implicits | |
*/ | |
object RoundTripInfo { | |
implicit def streamBuilder[T](in: (String, T => Stream[Any]))(implicit m: Manifest[T]): RoundTripInfo = | |
StreamRoundTrip(in._1, in._2)(m) | |
implicit def simpleBuilder[T](in: (String, T => Any))(implicit m: Manifest[T]): RoundTripInfo = | |
SimpleRoundTrip(in._1, in._2)(m) | |
implicit def handledBuilder[T](in: (String, (T, RoundTripHandlerFunc) => Unit))(implicit m: Manifest[T]): RoundTripInfo = | |
HandledRoundTrip(in._1, in._2)(m) | |
} | |
/** | |
* A function (well, an interface with a bunch of methods on it) to call | |
* depending on the state of the round trip function. | |
*/ | |
trait RoundTripHandlerFunc { | |
/** | |
* Send data back to the client. This may be called | |
* many times and each time, more data gets sent back to the client. | |
* @param value the data to send back. | |
*/ | |
def send(value: JValue): Unit | |
/** | |
* Send some JavaScript to execute on the client side | |
* @param value | |
*/ | |
def send(value: JsCmd): Unit | |
/** | |
* Send some javascript to execute on the client side | |
* @param value | |
*/ | |
def send(value: JsExp): Unit | |
/** | |
* When you are done sending data back to the client, call this method | |
*/ | |
def done(): Unit | |
/** | |
* If there's a failure related to the computation, call this method. | |
* @param msg | |
*/ | |
def failure(msg: String): Unit | |
} | |
final case class StreamRoundTrip[T](name: String, func: T => Stream[Any])(implicit val manifest: Manifest[T]) extends RoundTripInfo | |
final case class SimpleRoundTrip[T](name: String, func: T => Any)(implicit val manifest: Manifest[T]) extends RoundTripInfo | |
final case class HandledRoundTrip[T](name: String, func: (T, RoundTripHandlerFunc) => Unit)(implicit val manifest: Manifest[T]) extends RoundTripInfo |
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
(function() { | |
window.liftAjax = { | |
lift_ajaxQueue: [], | |
lift_ajaxInProcess: null, | |
lift_doCycleQueueCnt: 0, | |
lift_ajaxShowing: false, | |
lift_ajaxRetryCount: 3, | |
lift_ajaxHandler: function(theData, theSuccess, theFailure, responseType){ | |
var toSend = {retryCnt: 0}; | |
toSend.when = (new Date()).getTime(); | |
toSend.theData = theData; | |
toSend.onSuccess = theSuccess; | |
toSend.onFailure = theFailure; | |
toSend.responseType = responseType; | |
toSend.version = liftAjax.lift_ajaxVersion++; | |
// Make sure we wrap when we hit JS max int. | |
var version = liftAjax.lift_ajaxVersion | |
if ((version - (version + 1) != -1) || (version - (version - 1) != 1)) | |
liftAjax.lift_ajaxVersion = 0; | |
if (liftAjax.lift_uriSuffix) { | |
theData += '&' + liftAjax.lift_uriSuffix; | |
toSend.theData = theData; | |
liftAjax.lift_uriSuffix = undefined; | |
} | |
liftAjax.lift_ajaxQueue.push(toSend); | |
liftAjax.lift_ajaxQueueSort(); | |
liftAjax.lift_doCycleQueueCnt++; | |
liftAjax.lift_doAjaxCycle(); | |
return false; // buttons in forms don't trigger the form | |
}, | |
knownPromises: {}, | |
randStr: function() { | |
return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);}, | |
makeGuid: function() {return this.randStr() + this.randStr() + '-' + this.randStr() + '-' + this.randStr() + '-' + | |
this.randStr() + '-' + this.randStr() + this.randStr() + this.randStr();}, | |
Promise: function() { | |
return { | |
guid: liftAjax.makeGuid(), | |
"_values": [], | |
'_events': [], | |
'_failMsg': "", | |
'_valueFuncs': [], | |
'_doneFuncs': [], | |
'_failureFuncs': [], | |
'_eventFuncs': [], | |
'_done': false, | |
'_failed': false, | |
processMsg: function(evt) {if (this._done || this._failed) return; | |
this._events.push(evt); | |
for (var v in this._eventFuncs) {try {this._eventFuncs[v](evt);} catch (e) {liftAjax.lift_defaultLogError(e);}}; | |
if (evt.done) {this.doneMsg();} else if (evt.success) {this.successMsg(evt.success);} else if (evt.failure) {this.failMsg(evt.failure);}}, | |
successMsg: function(value) {if (this._done || this._failed) return; this._values.push(value); for (var f in this._valueFuncs) {this._valueFuncs[f](value);}}, | |
failMsg: function(msg) {if (this._done || this._failed) return; liftAjax._removeIt(this.guid); this._failed = true; this._failMsg = msg; for (var f in this._failureFuncs) {this._failureFuncs[f](msg);}}, | |
doneMsg: function() {if (this._done || this._failed) return; liftAjax._removeIt(this.guid); this._done = true; for (var f in this._doneFuncs) {this._doneFuncs[f]();}}, | |
then: function(f) {this._valueFuncs.push(f); for (var v in this._values) {try {f(this._values[v]);} catch (e) {liftAjax.lift_defaultLogError(e);;}} return this;}, | |
fail: function(f) {this._failureFuncs.push(f); if (this._failed) {try {f(this._failMsg);} catch (e) {liftAjax.lift_defaultLogError(e);;}}; return this;}, | |
done: function(f) {this._doneFuncs.push(f); if (this._done) {try {f();} catch (e) {liftAjax.lift_defaultLogError(e);;}} return this;}, | |
onEvent: function(f) {this._eventFuncs.push(f); for (var v in this._events) {try {f(this._events[v]);} catch (e) {liftAjax.lift_defaultLogError(e);;}}; return this;}, | |
map: function(f) {var ret = new liftAjax.Promise(); this.done(function() {ret.doneMsg();}); this.fail(function (m) {ret.failMsg(m);}); this.then(function (v) {ret.successMsg(f(v));}); return ret;} | |
}; | |
}, | |
_removeIt: function(g) {this.knownPromises[g] = undefined;}, | |
sendEvent: function(g, evt) { | |
var p = this.knownPromises[g]; | |
if (p) { | |
p.processMsg(evt); | |
} | |
}, | |
associate: function(promise) {this.knownPromises[promise.guid] = promise;}, | |
lift_uriSuffix: undefined, | |
lift_logError: function(msg) { | |
liftAjax.lift_defaultLogError(msg); | |
}, | |
lift_defaultLogError: function(msg) { | |
if (console && typeof console.error == 'function') | |
console.error(msg); | |
else | |
alert(msg); | |
}, | |
lift_ajaxQueueSort: function() { | |
liftAjax.lift_ajaxQueue.sort(function (a, b) {return a.when - b.when;}); | |
}, | |
lift_defaultFailure: function() { | |
alert("The server cannot be contacted at this time"); | |
}, | |
lift_startAjax: function() { | |
liftAjax.lift_ajaxShowing = true; | |
jQuery('#'+"ajax-spinner").show(); | |
}, | |
lift_endAjax: function() { | |
liftAjax.lift_ajaxShowing = false; | |
jQuery('#'+"ajax-spinner").hide(); | |
}, | |
lift_testAndShowAjax: function() { | |
if (liftAjax.lift_ajaxShowing && liftAjax.lift_ajaxQueue.length == 0 && liftAjax.lift_ajaxInProcess == null) { | |
liftAjax.lift_endAjax(); | |
} else if (!liftAjax.lift_ajaxShowing && (liftAjax.lift_ajaxQueue.length > 0 || liftAjax.lift_ajaxInProcess != null)) { | |
liftAjax.lift_startAjax(); | |
} | |
}, | |
lift_traverseAndCall: function(node, func) { | |
if (node.nodeType == 1) func(node); | |
var i = 0; | |
var cn = node.childNodes; | |
for (i = 0; i < cn.length; i++) { | |
liftAjax.lift_traverseAndCall(cn.item(i), func); | |
} | |
}, | |
lift_successRegisterGC: function() { | |
setTimeout("liftAjax.lift_registerGC()", 75000); | |
}, | |
lift_failRegisterGC: function() { | |
setTimeout("liftAjax.lift_registerGC()", 15000); | |
}, | |
lift_registerGC: function() { | |
var data = "__lift__GC=_", | |
version = null; | |
jQuery.ajax({ url : liftAjax.addPageNameAndVersion("/ajax_request/", version), data : data, type : "POST", dataType : "script", timeout : 5000, cache : false, success : liftAjax.lift_successRegisterGC, error : liftAjax.lift_failRegisterGC }); | |
}, | |
lift_sessionLost: function() { | |
location.reload(); | |
}, | |
lift_doAjaxCycle: function() { | |
if (liftAjax.lift_doCycleQueueCnt > 0) liftAjax.lift_doCycleQueueCnt--; | |
var queue = liftAjax.lift_ajaxQueue; | |
if (queue.length > 0) { | |
var now = (new Date()).getTime(); | |
if (liftAjax.lift_ajaxInProcess == null && queue[0].when <= now) { | |
var aboutToSend = queue.shift(); | |
liftAjax.lift_ajaxInProcess = aboutToSend; | |
var successFunc = function(data) { | |
liftAjax.lift_ajaxInProcess = null; | |
if (aboutToSend.onSuccess) { | |
aboutToSend.onSuccess(data); | |
} | |
liftAjax.lift_doCycleQueueCnt++; | |
liftAjax.lift_doAjaxCycle(); | |
}; | |
var failureFunc = function() { | |
liftAjax.lift_ajaxInProcess = null; | |
var cnt = aboutToSend.retryCnt; | |
if (arguments.length == 3 && arguments[1] == 'parsererror') { | |
liftAjax.lift_logError('The server call succeeded, but the returned Javascript contains an error: '+arguments[2]) | |
} else | |
if (cnt < liftAjax.lift_ajaxRetryCount) { | |
aboutToSend.retryCnt = cnt + 1; | |
var now = (new Date()).getTime(); | |
aboutToSend.when = now + (1000 * Math.pow(2, cnt)); | |
queue.push(aboutToSend); | |
liftAjax.lift_ajaxQueueSort(); | |
} else { | |
if (aboutToSend.onFailure) { | |
aboutToSend.onFailure(); | |
} else { | |
liftAjax.lift_defaultFailure(); | |
} | |
} | |
liftAjax.lift_doCycleQueueCnt++; | |
liftAjax.lift_doAjaxCycle(); | |
}; | |
if (aboutToSend.responseType != undefined && | |
aboutToSend.responseType != null && | |
aboutToSend.responseType.toLowerCase() === "json") { | |
liftAjax.lift_actualJSONCall(aboutToSend.theData, successFunc, failureFunc); | |
} else { | |
var theData = aboutToSend.theData, | |
version = aboutToSend.version; | |
liftAjax.lift_actualAjaxCall(theData, version, successFunc, failureFunc); | |
} | |
} | |
} | |
liftAjax.lift_testAndShowAjax(); | |
if (liftAjax.lift_doCycleQueueCnt <= 0) liftAjax.lift_doCycleIn200() | |
}, | |
lift_doCycleIn200: function() { | |
liftAjax.lift_doCycleQueueCnt++; | |
setTimeout("liftAjax.lift_doAjaxCycle();", 200); | |
}, | |
lift_ajaxVersion: 0, | |
addPageNameAndVersion: function(url, version) { | |
var replacement = 'ajax_request/'+lift_page; | |
if (version!=null) | |
replacement += ('-'+version.toString(36)) + (liftAjax.lift_ajaxQueue.length > 35 ? 35 : liftAjax.lift_ajaxQueue.length).toString(36); | |
return url.replace('ajax_request', replacement); | |
}, | |
lift_actualAjaxCall: function(data, version, onSuccess, onFailure) { | |
jQuery.ajax({ url : liftAjax.addPageNameAndVersion("/ajax_request/", version), data : data, type : "POST", dataType : "script", timeout : 5000, cache : false, success : onSuccess, error : onFailure }); | |
}, | |
lift_actualJSONCall: function(data, onSuccess, onFailure) { | |
var version = null; | |
jQuery.ajax({ url : liftAjax.addPageNameAndVersion("/ajax_request/", version), data : data, type : "POST", dataType : "json", timeout : 5000, cache : false, success : onSuccess, error : onFailure }); | |
} | |
}; | |
window.liftUtils = { | |
lift_blurIfReturn: function(e) { | |
var code; | |
if (!e) var e = window.event; | |
if (e.keyCode) code = e.keyCode; | |
else if (e.which) code = e.which; | |
var targ; | |
if (e.target) targ = e.target; | |
else if (e.srcElement) targ = e.srcElement; | |
if (targ.nodeType == 3) // defeat Safari bug | |
targ = targ.parentNode; | |
if (code == 13) {targ.blur(); return false;} else {return true;}; | |
} | |
}; | |
})(); | |
jQuery(document).ready(function() {liftAjax.lift_doCycleIn200();}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment