/*
* Copyright 2007-2008 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.http.js
import _root_.scala.xml._
import _root_.net.liftweb.util._
import Helpers._
import JE._
import JsCmds._
trait JxYieldFunc {
this: JxBase =>
def yieldFunction: JsExp
}
trait JxBase {
self: Node =>
def appendToParent(parentName: String): JsCmd
def label = throw new UnsupportedOperationException("Xml2Js does not have a label")
def addAttrs(varName: String, attrs: List[MetaData]): JsCmd = attrs.map {
m =>
m.value.map{
case exp: JsExp =>
JsRaw( varName+"."+m.key+" = "+exp.toJsCmd).cmd
case cmd: JsCmd => val varName = "v"+Helpers.nextFuncName
JsCrVar(varName, AnonFunc(cmd)) &
JsRaw(varName+"."+m.key+" = "+varName+"()")
case JxAttr(cmd) =>
JsRaw(varName+"."+m.key+" = "+ cmd.toJsCmd).cmd
case JxFuncAttr(cmd) =>
JsRaw(varName+"."+m.key+" = "+ AnonFunc(cmd).toJsCmd).cmd
case x =>
if (m.key == "class") {
// JsRaw(varName+".setAttribute('className',"+x.text.encJs+");").cmd
JsRaw(varName+".className = "+x.text.encJs).cmd &
JsRaw(varName+".setAttribute("+m.key.encJs+","+x.text.encJs+");").cmd
} else {
JsRaw(varName+".setAttribute("+m.key.encJs+","+x.text.encJs+");").cmd
}
}.foldLeft(Noop)(_ & _)
}.foldLeft(Noop)(_ & _)
private def fixText(in: String): String = (in, in.trim) match {
case (x, y) if x == y => x
case (x, y) if x startsWith y => y + " "
case (x, y) if y.length == 0 => " "
case (x, y) if x endsWith y => " "+y
case (_, y) => " "+y+" "
}
def addToDocFrag(parent: String, elems: List[Node]): JsCmd = elems.map{
case Jx(kids) => addToDocFrag(parent, kids.toList)
case jb: JxBase => jb.appendToParent(parent)
case Group(nodes) => addToDocFrag(parent, nodes.toList)
case Text(txt) => JsRaw(parent+".appendChild(document.createTextNode("+fixText(txt).encJs+"));").cmd
case a: Atom[_] => JsRaw(parent+".appendChild(document.createTextNode("+a.text.encJs+"));").cmd
case e: _root_.scala.xml.Elem =>
val varName = "v"+Helpers.nextFuncName
JsCrVar(varName, JsRaw("document.createElement("+e.label.encJs+")")) &
addAttrs(varName, e.attributes.toList) &
JsRaw(parent+".appendChild("+varName+")") &
addToDocFrag(varName, e.child.toList)
case ns: NodeSeq =>
if (ns.length == 0) Noop
else if (ns.length == 1) {
Log.error("In addToDocFrag, got a "+ns+" of type "+ns.getClass.getName)
Noop
} else addToDocFrag(parent, ns.toList)
}.foldLeft(Noop)(_ & _)
}
abstract class JxNodeBase extends Node with JxBase {
}
case class JxAttr(in: JsCmd) extends Node with JxBase {
def child = Nil
def appendToParent(parentName: String): JsCmd = {
Noop
}
}
case class JxFuncAttr(in: JsCmd) extends Node with JxBase {
def child = Nil
def appendToParent(parentName: String): JsCmd = {
Noop
}
}
case class JxMap(in: JsExp, what: JxYieldFunc) extends Node with JxBase {
def child = Nil
def appendToParent(parentName: String): JsCmd = {
val ran = "v"+Helpers.nextFuncName
val fr = "f"+Helpers.nextFuncName
val cr = "c"+Helpers.nextFuncName
JsCrVar(ran, in) &
JsCrVar(fr, what.yieldFunction) &
JsRaw("for ("+cr+" = 0; "+cr+" < "+ran+".length; "+cr+"++) {"+
parentName+".appendChild("+fr+"("+ran+"["+cr+"]));"+
"}")
}
}
case class JxCmd(in: JsCmd) extends Node with JxBase {
def child = Nil
def appendToParent(parentName: String) = in
}
case class JxMatch(exp: JsExp, cases: JxCase*) extends Node with JxBase {
def child = Nil
def appendToParent(parentName: String): JsCmd = {
val vn = "v" + Helpers.nextFuncName
JsCrVar(vn, exp) &
JsRaw("if (false) {\n} "+
cases.map{c =>
" else if ("+vn+" == "+c.toMatch.toJsCmd+") {"+
addToDocFrag(parentName, c.toDo.toList).toJsCmd+
"\n}"
}.mkString("")+
" else {throw new Exception('Unmatched: '+"+vn+");}")
}
}
case class JxCase(toMatch: JsExp, toDo: NodeSeq)
case class JxIf(toTest: JsExp, ifTrue: NodeSeq) extends Node with JxBase {
def child = Nil
def appendToParent(parentName: String): JsCmd = {
JsRaw("if ("+toTest.toJsCmd+") {\n"+
addToDocFrag(parentName, ifTrue.toList).toJsCmd+
"}\n")
}
}
case class JxIfElse(toTest: JsExp, ifTrue: NodeSeq, ifFalse: NodeSeq) extends Node with JxBase {
def child = Nil
def appendToParent(parentName: String): JsCmd = {
JsRaw("if ("+toTest.toJsCmd+") {\n"+
addToDocFrag(parentName, ifTrue.toList).toJsCmd+
"} else {\n" +
addToDocFrag(parentName, ifFalse.toList).toJsCmd+
"}\n")
}
}
case class Jx(child: NodeSeq) extends Node with JxBase with JxYieldFunc {
def appendToParent(parentName: String): JsCmd =
addToDocFrag(parentName,child.toList)
def yieldFunction: JsExp = toJs
def toJs: JsExp = AnonFunc("it",
JsCrVar("df", JsRaw("document.createDocumentFragment()")) &
addToDocFrag("df", child.toList) &
JsRaw("return df"))
}