/*
 * 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 {
package http {

import S._
import _root_.net.liftweb.common._
import _root_.net.liftweb.util._
import _root_.net.liftweb.util.Helpers._
import _root_.net.liftweb.http.js._
import _root_.net.liftweb.http.js.AjaxInfo
import JE._
import JsCmds._
import _root_.scala.xml._

/**
 * The SHtml object defines a suite of XHTML element generator methods
 * to simplify the creation of markup, particularly with forms and AJAX.
 */
object SHtml {

  /**
   * Invokes the Ajax request
   * @param in the JsExp that returns the request data
   */
  def makeAjaxCall(in: JsExp): JsExp = new JsExp {
    def toJsCmd = "liftAjax.lift_ajaxHandler(" + in.toJsCmd + ", null, null, null)"
  }

  /**
   * Invokes the Ajax request
   * @param in the JsExp that returns the request data
   * @param context defines the response callback functions and the response type (JavaScript or JSON)
   */
  def makeAjaxCall(in: JsExp, context: AjaxContext): JsExp = new JsExp {
    def toJsCmd = "liftAjax.lift_ajaxHandler(" + in.toJsCmd + ", " + (context.success openOr "null") +
            ", " + (context.failure openOr "null") +
            ", " + context.responseType.toString.encJs +
            ")"
  }

  /**
   * Build a JavaScript function that will perform an AJAX call based on a value calculated in JavaScript
   * 
   * @param jsCalcValue the JavaScript that will be executed on the client to calculate the value to be sent to the server
   * @param func the function to call when the data is sent
   *
   * @return the function ID and JavaScript that makes the call
   */
  def ajaxCall(jsCalcValue: JsExp, func: String => JsCmd): (String, JsExp) = ajaxCall_*(jsCalcValue, SFuncHolder(func))

  /**
   * Build a JavaScript function that will perform an AJAX call based on a value calculated in JavaScript
   * 
   * @param jsCalcValue the JavaScript that will be executed on the client to calculate the value to be sent to the server
   * @param jsContext the context instance that defines JavaScript to be executed on call success or failure
   * @param func the function to call when the data is sent
   *
   * @return the function ID and JavaScript that makes the call
   */
  def ajaxCall(jsCalcValue: JsExp, jsContext: JsContext, func: String => JsCmd): (String, JsExp) =
    ajaxCall_*(jsCalcValue, jsContext, SFuncHolder(func))

  /**
   * Build a JavaScript function that will perform a JSON call based on a value calculated in JavaScript
   * 
   * @param jsCalcValue the JavaScript to calculate the value to be sent to the server
   * @param jsContext the context instance that defines JavaScript to be executed on call success or failure
   * @param func the function to call when the data is sent
   *
   * @return the function ID and JavaScript that makes the call
   */
  def jsonCall(jsCalcValue: JsExp, func: Any => JsCmd): (String, JsExp) =
    jsonCall_*(jsCalcValue, SFuncHolder(s => JSONParser.parse(s).map(func) openOr Noop))

  /**
   * Build a JavaScript function that will perform a JSON call based on a value calculated in JavaScript
   * 
   * @param jsCalcValue the JavaScript to calculate the value to be sent to the server
   * @param jsContext the context instance that defines JavaScript to be executed on call success or failure
   * @param func the function to call when the data is sent
   *
   * @return the function ID and JavaScript that makes the call
   */
  def jsonCall(jsCalcValue: JsExp, jsContext: JsContext, func: Any => JsCmd): (String, JsExp) =
    jsonCall_*(jsCalcValue, jsContext, SFuncHolder(s => JSONParser.parse(s).map(func) openOr Noop))


  /**
   * Build a JavaScript function that will perform an AJAX call based on a value calculated in JavaScript
   * @param jsCalcValue -- the JavaScript to calculate the value to be sent to the server
   * @param func -- the function to call when the data is sent
   *
   * @return the function ID and JavaScript that makes the call
   */
  private def jsonCall_*(jsCalcValue: JsExp, func: AFuncHolder): (String, JsExp) =
    fmapFunc(contextFuncBuilder(func))(name =>
            (name, makeAjaxCall(JsRaw("'" + name + "=' + encodeURIComponent(JSON.stringify(" + jsCalcValue.toJsCmd + "))"))))

  /**
   * Build a JavaScript function that will perform an AJAX call based on a value calculated in JavaScript
   * @param jsCalcValue -- the JavaScript to calculate the value to be sent to the server
   * @param ajaxContext -- the context defining the javascript callback functions and the response type
   * @param func -- the function to call when the data is sent
   *
   * @return the function ID and JavaScript that makes the call
   */
  private def jsonCall_*(jsCalcValue: JsExp,
                         ajaxContext: AjaxContext,
                         func: AFuncHolder): (String, JsExp) =
    fmapFunc(contextFuncBuilder(func))(name =>
            (name, makeAjaxCall(JsRaw("'" + name + "=' + encodeURIComponent(JSON.stringify(" + jsCalcValue.toJsCmd + "))"), ajaxContext)))

  def fajaxCall[T](jsCalcValue: JsExp, func: String => JsCmd)(f: (String, JsExp) => T): T = {
    val (name, js) = ajaxCall(jsCalcValue, func)
    f(name, js)
  }

  def jsonCall(jsCalcValue: JsExp,
               jsonContext: JsonContext,
               func: String => JsObj): (String, JsExp) = ajaxCall_*(jsCalcValue, jsonContext, SFuncHolder(func))

  def fjsonCall[T](jsCalcValue: JsExp, jsonContext: JsonContext, func: String => JsObj)(f: (String, JsExp) => T): T = {
    val (name, js) = jsonCall(jsCalcValue, jsonContext, func)
    f(name, js)
  }

  /**
   * Build a JavaScript function that will perform an AJAX call based on a value calculated in JavaScript
   * @param jsCalcValue -- the JavaScript to calculate the value to be sent to the server
   * @param func -- the function to call when the data is sent
   *
   * @return the JavaScript that makes the call
   */
  private def ajaxCall_*(jsCalcValue: JsExp, func: AFuncHolder): (String, JsExp) =
    fmapFunc(contextFuncBuilder(func))(name =>
            (name, makeAjaxCall(JsRaw("'" + name + "=' + encodeURIComponent(" + jsCalcValue.toJsCmd + ")"))))

  /**
   * Build a JavaScript function that will perform an AJAX call based on a value calculated in JavaScript
   * @param jsCalcValue -- the JavaScript to calculate the value to be sent to the server
   * @param ajaxContext -- the context defining the javascript callback functions and the response type
   * @param func -- the function to call when the data is sent
   *
   * @return the JavaScript that makes the call
   */
  private def ajaxCall_*(jsCalcValue: JsExp,
                         ajaxContext: AjaxContext,
                         func: AFuncHolder): (String, JsExp) =
    fmapFunc(contextFuncBuilder(func))(name =>
            (name, makeAjaxCall(JsRaw("'" + name + "=' + encodeURIComponent(" + jsCalcValue.toJsCmd + ")"), ajaxContext)))


  private def deferCall(data: JsExp, jsFunc: Call): Call =
    Call(jsFunc.function, (jsFunc.params ++ List(AnonFunc(makeAjaxCall(data)))): _*)

  /**
   * Create an Ajax button. When it's pressed, the function is executed
   *
   * @param text -- the name/text of the button
   * @param func -- the function to execute when the button is pushed.  Return Noop if nothing changes on the browser.
   * @param attrs -- the list of node attributes
   *
   * @return a button to put on your page
   */
  def ajaxButton(text: NodeSeq, func: () => JsCmd, attrs: (String, String)*): Elem = {
    attrs.foldLeft(fmapFunc(contextFuncBuilder(func))(name =>
            <button onclick={makeAjaxCall(Str(name + "=true")).toJsCmd +
                    "; return false;"}>{text}</button>))(_ % _)
  }

  /**
   * Create an Ajax buttun that when it's pressed it submits an Ajax request and expects back a JSON
   * construct which will be passed to the <i>success</i> function
   *
   * @param text -- the name/text of the button
   * @param func -- the function to execute when the button is pushed.  Return Noop if nothing changes on the browser.
   * @param ajaxContext -- defines the callback functions and the JSON response type
   * @param attrs -- the list of node attributes
   *
   * @return a button to put on your page
   *
   */
  def jsonButton(text: NodeSeq, func: () => JsObj, ajaxContext: JsonContext, attrs: (String, String)*): Elem = {
    attrs.foldLeft(fmapFunc(contextFuncBuilder(func))(name =>
            <button onclick={makeAjaxCall(Str(name + "=true"), ajaxContext).toJsCmd +
                    "; return false;"}>{text}</button>))(_ % _)
  }

  /**
   * Create an Ajax button. When it's pressed, the function is executed
   *
   * @param text -- the name/text of the button
   * @param func -- the function to execute when the button is pushed.  Return Noop if nothing changes on the browser.
   * @param attrs -- the list of node attributes
   *
   * @return a button to put on your page
   */
  def ajaxButton(text: NodeSeq, jsExp: JsExp, func: String => JsCmd, attrs: (String, String)*): Elem = {
    attrs.foldLeft(fmapFunc(contextFuncBuilder(SFuncHolder(func)))(name =>
            <button onclick={makeAjaxCall(JsRaw(name.encJs + "+'='+encodeURIComponent(" + jsExp.toJsCmd + ")")).toJsCmd +
                    "; return false;"}>{text}</button>))(_ % _)
  }

  /**
   * Create an Ajax buttun that when it's pressed it submits an Ajax request and expects back a JSON
   * construct which will be passed to the <i>success</i> function
   *
   * @param text -- the name/text of the button
   * @param func -- the function to execute when the button is pushed.  Return Noop if nothing changes on the browser.
   * @param ajaxContext -- defines the callback functions and the JSON response type
   * @param attrs -- the list of node attributes
   *
   * @return a button to put on your page
   *
   */
  def jsonButton(text: NodeSeq, jsExp: JsExp, func: Any => JsObj, ajaxContext: JsonContext, attrs: (String, String)*): Elem = {
    attrs.foldLeft(jsonFmapFunc(func)(name =>
            <button onclick={makeAjaxCall(JsRaw(name.encJs + "+'='+ encodeURIComponent(JSON.stringify(" + jsExp.toJsCmd + "))"), ajaxContext).toJsCmd +
                    "; return false;"}>{text}</button>))(_ % _)
  }

  /**
   * Create an Ajax button. When it's pressed, the function is executed
   *
   * @param text -- the name/text of the button
   * @param jsFunc -- the user function that will be executed. This function will receive as last parameter
   *                  the function that will actually do the ajax call. Hence the user function can decide when
   * 				  to make the ajax request.
   * @param func -- the function to execute when the button is pushed.  Return Noop if nothing changes on the browser.
   *
   * @return a button to put on your pagejsFunc.params ++ List(AnonFunc(makeAjaxCall(Str(name+"=true"))))
   */
  def ajaxButton(text: NodeSeq, jsFunc: Call, func: () => JsCmd, attrs: (String, String)*): Elem = {
    attrs.foldLeft(fmapFunc(contextFuncBuilder(func))(name =>
            <button onclick={deferCall(Str(name + "=true"), jsFunc).toJsCmd + "; return false;"}>{text}</button>))(_ % _)
  }

  /**
   * Create an Ajax button. When it's pressed, the function is executed
   *
   * @param text -- the name/text of the button
   * @param jsFunc -- the user function that will be executed. This function will receive as last parameter
   *                  the function that will actually do the ajax call. Hence the user function can decide when
   * 				  to make the ajax request.
   * @param func -- the function to execute when the button is pushed.  Return Noop if nothing changes on the browser.
   *
   * @return a button to put on your page
   */
  def ajaxButton(text: String, func: () => JsCmd, attrs: (String, String)*): Elem =
    ajaxButton(Text(text), func, attrs: _*)

  /**
   * Create an Ajax button. When it's pressed, the function is executed
   *
   * @param text -- the name/text of the button
   * @param func -- the function to execute when the button is pushed.  Return Noop if nothing changes on the browser.
   *
   * @return a button to put on your page
   */
  def ajaxButton(text: String, jsFunc: Call, func: () => JsCmd, attrs: (String, String)*): Elem =
    ajaxButton(Text(text), jsFunc, func, attrs: _*)

  /**
   * This method generates an AJAX editable field. Normally, the displayContents
   * will be shown, with an "Edit" button. If the "Edit" button is clicked, the field
   * will be replaced with the edit form, along with an "OK" and "Cancel" button.
   * If the OK button is pressed, the form fields are submitted and the onSubmit
   * function is called, and then the displayContents are re-run to get a new display.
   * If cancel is pressed then the original displayContents are re-shown.
   */
  def ajaxEditable (displayContents : => NodeSeq, editForm : => NodeSeq, onSubmit : () => Unit) : NodeSeq = {
    import _root_.net.liftweb.http.js
    import js.{jquery,JsCmd,JsCmds,JE}
    import jquery.JqJsCmds
    import JsCmds.{Noop,SetHtml}
    import JE.Str
    import JqJsCmds.{Hide,Show}

    val divName = Helpers.nextFuncName
    val dispName = divName + "_display"
    val editName = divName + "_edit"

    def swapJsCmd (show : String, hide : String) : JsCmd = Show(show) & Hide(hide)

    def setAndSwap (show : String, showContents : => NodeSeq, hide : String) : JsCmd =
      (SHtml.ajaxCall(Str("ignore"), {ignore : String => SetHtml(show, showContents)})._2.cmd & swapJsCmd(show,hide))

    def displayMarkup : NodeSeq =
      displayContents ++ Text(" ") ++
      <input value={S.??("edit")} type="button" onclick={setAndSwap(editName, editMarkup, dispName).toJsCmd + " return false;"} />

    def editMarkup : NodeSeq = {
      val formData : NodeSeq =
        editForm ++
        <input type="submit" value={S.??("ok")} /> ++
        hidden(onSubmit) ++
        <input type="button" onclick={swapJsCmd(dispName,editName).toJsCmd + " return false;"} value={S.??("cancel")} />

      ajaxForm(formData,
               Noop,
               setAndSwap(dispName, displayMarkup, editName))
    }

    <div>
      <div id={dispName}>
        {displayMarkup}
      </div>
      <div id={editName} style="display: none;">
        {editMarkup}
      </div>
    </div>
  }

  /**
   * Create an anchor tag around a body which will do an AJAX call and invoke the function
   *
   * @param func - the function to invoke when the link is clicked
   * @param body - the NodeSeq to wrap in the anchor tag
   * @param attrs - the anchor node attributes
   */
  def a(func: () => JsCmd, body: NodeSeq, attrs: (String, String)*): Elem = {
    val key = formFuncName
    addFunctionMap(key, contextFuncBuilder((a: List[String]) => func()))
    attrs.foldLeft(<lift:a key={key}>{body}</lift:a>)(_ % _)
  }

  /**
   * Create an anchor tag around a body which will do an AJAX call and invoke the function
   *
   * @param jsFunc -- the user function that will be executed. This function will receive as last parameter
   *                  the function that will actually do the ajax call. Hence the user function can decide when
   * 				  to make the ajax request.
   * @param func - the function to invoke when the link is clicked
   * @param body - the NodeSeq to wrap in the anchor tag
   * @param attrs - the anchor node attributes
   */
  def a(jsFunc: Call, func: () => JsCmd, body: NodeSeq, attrs: (String, String)*): Elem = {
    attrs.foldLeft(fmapFunc(contextFuncBuilder(func))(name =>
            <a href="javascript://" onclick={deferCall(Str(name + "=true"), jsFunc).toJsCmd + "; return false;"}>{body}</a>))(_ % _)
  }

  def a(func: () => JsObj,
        jsonContext: JsonContext,
        body: NodeSeq,
        attrs: (String, String)*): Elem = {

    attrs.foldLeft(fmapFunc(contextFuncBuilder(func))(name =>
            <a href="javascript://" onclick={makeAjaxCall(Str(name + "=true"), jsonContext).toJsCmd + "; return false;"}>{body}</a>))(_ % _)
  }

  /**
   * Create an anchor with a body and the function to be executed when the anchor is clicked
   */
  def a(body: NodeSeq, attrs: (String, String)*)(func: => JsCmd): Elem =
    a(() => func, body, attrs: _*)

  /**
   * Create an anchor with a body and the function to be executed when the anchor is clicked
   * @param jsFunc -- the user function that will be executed. This function will receive as last parameter
   *                  the function that will actually do the ajax call. Hence the user function can decide when
   * 				  to make the ajax request.
   * @param body - the NodeSeq to wrap in the anchor tag
   * @param attrs - the anchor node attributes
   */
  def a(jsFunc: Call, body: NodeSeq, attrs: (String, String)*)(func: => JsCmd): Elem =
    a(jsFunc, () => func, body, attrs: _*)

  /**
   * Create an anchor that will run a JavaScript command when clicked
   */
  def a(body: NodeSeq, cmd: JsCmd, attrs: (String, String)*): Elem =
    attrs.foldLeft(<a href="javascript://"
    onclick={cmd.toJsCmd + "; return false;"}>{body}</a>)(_ % _)

  /**
   * Create a span that will run a JavaScript command when clicked
   */
  def span(body: NodeSeq, cmd: JsCmd, attrs: (String, String)*): Elem =
    attrs.foldLeft(<span onclick={cmd.toJsCmd}>{body}</span>)(_ % _)


  def toggleKids(head: Elem, visible: Boolean, func: () => JsCmd, kids: Elem): NodeSeq = {
    fmapFunc(contextFuncBuilder(func)) {
      funcName =>

              val (nk, id) = findOrAddId(kids)
              val rnk = if (visible) nk else nk % ("style" -> "display: none")
              val nh = head %
                      ("onclick" -> (LiftRules.jsArtifacts.toggle(id).cmd & makeAjaxCall(JsRaw("'" + funcName + "=true'")).cmd))
              nh ++ rnk
    }
  }

  /**
   * This function does not really submit a JSON request to server instead json is a function
   * that allows you to build a more complex JsCmd based on the JsExp <i>JE.JsRaw("this.value")</i>.
   * This function is called by the overloaded version of jsonText.
   *
   * @param value - the initial value of the text field
   * @param ignoreBlur - ignore the onblur event and only do the event if the enter key is pressed
   * @param json - takes a JsExp which describes how to recover the
   * value of the text field and returns a JsExp containing the thing
   * to execute on blur/return
   *
   * @return a text field
   */
  def jsonText(value: String, ignoreBlur: Boolean, json: JsExp => JsCmd, attrs: (String, String)*): Elem = 
  (attrs.foldLeft(<input type="text" value={value}/>)(_ % _)) %
  ("onkeypress" -> """liftUtils.lift_blurIfReturn(event)""") %
  (if (ignoreBlur) Null else ("onblur" -> (json(JE.JsRaw("this.value")))))
  


  /**
   * This function does not really submit a JSON request to server instead json is a function
   * that allows you to build a more complex JsCmd based on the JsExp <i>JE.JsRaw("this.value")</i>.
   * This function is called by the overloaded version of jsonText.
   *
   * @param value - the initial value of the text field
   * @param json - takes a JsExp which describes how to recover the
   * value of the text field and returns a JsExp containing the thing
   * to execute on blur/return
   *
   * @return a text field
   */
  def jsonText(value: String, json: JsExp => JsCmd, attrs: (String, String)*): Elem = jsonText(value, false, json, attrs :_*)

  /**
   * Create a JSON text widget that makes a JSON call on blur or "return".
   *
   * @param value - the initial value of the text field
   * @param cmd - the json command name
   * @param json - the JsonCall returned from S.buildJsonFunc
   *
   * @return a text field
   */
  def jsonText(value: String, cmd: String, json: JsonCall, attrs: (String, String)*): Elem =
  jsonText(value, exp => json(cmd, exp), attrs: _*)

  def ajaxText(value: String, func: String => JsCmd, attrs: (String, String)*): Elem = 
  ajaxText_*(value, false, Empty, SFuncHolder(func), attrs: _*)

  def ajaxText(value: String, jsFunc: Call, func: String => JsCmd, attrs: (String, String)*): Elem = 
  ajaxText_*(value, false, Full(jsFunc), SFuncHolder(func), attrs: _*)

  def ajaxText(value: String, ignoreBlur: Boolean, func: String => JsCmd, attrs: (String, String)*): Elem =
  ajaxText_*(value, ignoreBlur, Empty, SFuncHolder(func), attrs: _*)

  def ajaxText(value: String, ignoreBlur: Boolean, jsFunc: Call, func: String => JsCmd, attrs: (String, String)*): Elem =
  ajaxText_*(value, ignoreBlur, Full(jsFunc), SFuncHolder(func), attrs: _*)

  private def ajaxText_*(value: String, ignoreBlur: Boolean, jsFunc: Box[Call], func: AFuncHolder, attrs: (String, String)*): Elem = {
    val raw = (funcName: String, value: String) => JsRaw("'" + funcName + "=' + encodeURIComponent(" + value + ".value)")
    val key = formFuncName

    fmapFunc(contextFuncBuilder(func)) {
      funcName =>
      (attrs.foldLeft(<input type="text" value={value}/>)(_ % _)) %
      ("onkeypress" -> """liftUtils.lift_blurIfReturn(event)""") %
      (if (ignoreBlur) Null else
       ("onblur" -> (jsFunc match {
              case Full(f) => JsCrVar(key, JsRaw("this")) & deferCall(raw(funcName, key), f)
              case _ => makeAjaxCall(raw(funcName, "this"))
            })
        ))
    }
  }

  /**
   * This function does not really submit a JSON request to server instead json is a function
   * that allows you to build a more complex JsCmd based on the JsExp <i>JE.JsRaw("this.value")</i>.
   * This function is called by the overloaded version of jsonTextarea.
   *
   * @param value - the initial value of the text area field
   * @param json - takes a JsExp which describes how to recover the
   * value of the text area field and returns a JsExp containing the thing
   * to execute on blur
   *
   * @return a text area field
   */
  def jsonTextarea(value: String, json: JsExp => JsCmd, attrs: (String, String)*): Elem = 
  (attrs.foldLeft(<textarea>{value}</textarea>)(_ % _)) %
  ("onblur" -> (json(JE.JsRaw("this.value"))))
  

  /**
   * Create a JSON text area widget that makes a JSON call on blur
   *
   * @param value - the initial value of the text field
   * @param cmd - the json command name
   * @param json - the JsonCall returned from S.buildJsonFunc
   *
   * @return a text field
   */
  def jsonTextarea(value: String, cmd: String, json: JsonCall, attrs: (String, String)*): Elem =
  jsonTextarea(value, exp => json(cmd, exp), attrs: _*)

  def ajaxTextarea(value: String, func: String => JsCmd, attrs: (String, String)*): Elem = 
  ajaxTextarea_*(value, Empty, SFuncHolder(func), attrs: _*)

  def ajaxTextarea(value: String, jsFunc: Call, func: String => JsCmd, attrs: (String, String)*): Elem = 
  ajaxTextarea_*(value, Full(jsFunc), SFuncHolder(func), attrs: _*)

  private def ajaxTextarea_*(value: String, jsFunc: Box[Call], func: AFuncHolder, attrs: (String, String)*): Elem = {
    val raw = (funcName: String, value: String) => JsRaw("'" + funcName + "=' + encodeURIComponent(" + value + ".value)")
    val key = formFuncName

    fmapFunc(contextFuncBuilder(func)) {
      funcName =>
      (attrs.foldLeft(<textarea>{value}</textarea>)(_ % _)) %
      ("onblur" -> (jsFunc match {
              case Full(f) => JsCrVar(key, JsRaw("this")) & deferCall(raw(funcName, key), f)
              case _ => makeAjaxCall(raw(funcName, "this"))
            })
        )
    }
  }


  trait AreaShape {
    def shape: String
    def coords: String
  }
  case class RectShape(left: Int, top: Int, right: Int, bottom: Int) extends AreaShape {
    def shape: String = "rect"
    def coords: String = ""+left+", "+top+", "+right+", "+bottom
  }
  case class CircleShape(centerX: Int, centerY: Int, radius: Int) extends AreaShape  {
    def shape: String = "circle"
    def coords: String = ""+centerX+", "+centerY+", "+radius
  }
  case class CirclePercentShape(centerX: Int, centerY: Int, radiusPercent: Int) extends AreaShape  {
    def shape: String = "circle"
    def coords: String = ""+centerX+", "+centerY+", "+radiusPercent+"%"
  }
  case class PolyShape(polyCoords: (Int, Int)*) extends AreaShape {
    def shape: String = "poly"
    def coords: String = polyCoords.map{ case (x, y) => ""+x+", "+y}.mkString(", ")
  }

  /**
   * Generate an Area tag
   *
   * @param shape - the shape of the area (RectShape, CircleShape, CirclePercentShape, PolyShape)
   * @param alt - the contents of the alt attribute
   * @param attrs - the balance of the attributes for the tag
   */
  def area(shape: AreaShape, alt: String, attrs: (String, String)*): Elem =
  attrs.foldLeft(<area alt={alt} shape={shape.shape} coords={shape.coords} />)(_ % _)

  /**
   * Generate an Area tag
   *
   * @param shape - the shape of the area (RectShape, CircleShape, CirclePercentShape, PolyShape)
   * @param jsCmd - the JavaScript to execute on the client when the area is clicked
   * @param alt - the contents of the alt attribute
   * @param attrs - the balance of the attributes for the tag
   */
  def area(shape: AreaShape, jsCmd: JsCmd, alt: String, attrs: (String, String)*): Elem =
  area(shape, alt, ("onclick" -> jsCmd.toJsCmd) :: attrs.toList :_*)

  /**
   * Generate an Area tag
   *
   * @param shape - the shape of the area (RectShape, CircleShape, CirclePercentShape, PolyShape)
   * @param func - The server side function to execute when the area is clicked on.
   * @param alt - the contents of the alt attribute
   * @param attrs - the balance of the attributes for the tag
   */
  def area(shape: AreaShape, func: () => JsCmd, alt: String, attrs: (String, String)*): Elem = {
    fmapFunc(contextFuncBuilder(func)) {
      funcName =>
      area(shape, alt, ("onclick" -> (makeAjaxCall(Str(funcName + "=true")).toJsCmd +
                                      "; return false;")) :: attrs.toList :_*)
    }
  }


  def ajaxCheckbox(value: Boolean, func: Boolean => JsCmd, attrs: (String, String)*): Elem =
    ajaxCheckbox_*(value, Empty, LFuncHolder(in => func(in.exists(toBoolean(_)))), attrs: _*)

  def ajaxCheckbox(value: Boolean, jsFunc: Call, func: Boolean => JsCmd, attrs: (String, String)*): Elem =
    ajaxCheckbox_*(value, Full(jsFunc), LFuncHolder(in => func(in.exists(toBoolean(_)))), attrs: _*)

  private def ajaxCheckbox_*(value: Boolean, jsFunc: Box[Call], func: AFuncHolder, attrs: (String, String)*): Elem = {
    val raw = (funcName: String, value: String) => JsRaw("'" + funcName + "=' + " + value + ".checked")
    val key = formFuncName

    fmapFunc(contextFuncBuilder(func)) {
      funcName =>
              (attrs.foldLeft(<input type="checkbox"/>)(_ % _)) %
                      checked(value) %
                      ("onclick" -> (jsFunc match {
                        case Full(f) => JsCrVar(key, JsRaw("this")) & deferCall(raw(funcName, key), f)
                        case _ => makeAjaxCall(raw(funcName, "this"))
                      }))
    }
  }


  /**
   * Create a select box based on the list with a default value and the function
   * to be executed on form submission
   *
   * @param options -- a list of value and text pairs (value, text to display)
   * @param default -- the default value (or Empty if no default value)
   * @param onSubmit -- the function to execute on form submission
   */
  def ajaxSelectObj[T](options: Seq[(T, String)], default: Box[T],
                       onSubmit: T => JsCmd, attrs: (String, String)*): Elem = {

    val secure = options.map {case (obj, txt) => (obj, randomString(20), txt)}
    val defaultNonce = default.flatMap(d => secure.find(_._1 == d).map(_._2))
    val nonces = secure.map {case (obj, nonce, txt) => (nonce, txt)}
    def process(nonce: String): JsCmd =
      secure.find(_._2 == nonce).map(x => onSubmit(x._1)) getOrElse Noop
    //  (nonces, defaultNonce, SFuncHolder(process))

    ajaxSelect_*(nonces,
      defaultNonce,
      Empty,
      SFuncHolder(process _),
      attrs: _*)
  }

  /**
   * Create a select box based on the list with a default value and the function
   * to be executed on form submission
   *
   * @param options -- a list of value and text pairs (value, text to display)
   * @param default -- the default value (or Empty if no default value)
   * @param onSubmit -- the function to execute on form submission
   */
  def ajaxSelectObj[T](options: Seq[(T, String)], default: Box[T],
                       jsFunc: Call,
                       onSubmit: T => JsCmd, attrs: (String, String)*): Elem = {

    val secure = options.map {case (obj, txt) => (obj, randomString(20), txt)}
    val defaultNonce = default.flatMap(d => secure.find(_._1 == d).map(_._2))
    val nonces = secure.map {case (obj, nonce, txt) => (nonce, txt)}
    def process(nonce: String): JsCmd =
      secure.find(_._2 == nonce).map(x => onSubmit(x._1)) getOrElse Noop
    //  (nonces, defaultNonce, SFuncHolder(process))

    ajaxSelect_*(nonces,
      defaultNonce,
      Full(jsFunc),
      SFuncHolder(process _),
      attrs: _*)
  }

  def ajaxSelect(opts: Seq[(String, String)], deflt: Box[String],
                 func: String => JsCmd, attrs: (String, String)*): Elem =
    ajaxSelect_*(opts, deflt, Empty, SFuncHolder(func), attrs: _*)

  def ajaxSelect(opts: Seq[(String, String)], deflt: Box[String],
                 jsFunc: Call, func: String => JsCmd, attrs: (String, String)*): Elem =
    ajaxSelect_*(opts, deflt, Full(jsFunc), SFuncHolder(func), attrs: _*)

  private def ajaxSelect_*(opts: Seq[(String, String)], deflt: Box[String],
                           jsFunc: Box[Call], func: AFuncHolder, attrs: (String, String)*): Elem = {
    val raw = (funcName: String, value: String) => JsRaw("'" + funcName + "=' + this.options[" + value + ".selectedIndex].value")
    val key = formFuncName

    val vals = opts.map(_._1)
    val testFunc = LFuncHolder(in => in.filter(v => vals.contains(v)) match {case Nil => false case xs => func(xs)}, func.owner)
    fmapFunc(contextFuncBuilder(testFunc)) {
      funcName =>
              (attrs.foldLeft(<select>{opts.flatMap {case (value, text) => (<option value={value}>{text}</option>) % selected(deflt.exists(_ == value))}}</select>)(_ % _)) %
                      ("onchange" -> (jsFunc match {
                        case Full(f) => JsCrVar(key, JsRaw("this")) & deferCall(raw(funcName, key), f)
                        case _ => makeAjaxCall(raw(funcName, "this"))
                      }))
    }
  }

  def ajaxInvoke(func: () => JsCmd): (String, JsExp) =
    fmapFunc(contextFuncBuilder(NFuncHolder(func)))(name => (name, makeAjaxCall(name + "=true")))

  /**
   * Build a swappable visual element.  If the shown element is clicked on, it turns into the hidden element and when
   * the hidden element blurs, it swaps into the shown element.
   */
  def swappable(shown: Elem, hidden: Elem): Elem = {
    val (rs, sid) = findOrAddId(shown)
    val (rh, hid) = findOrAddId(hidden)
    val ui = LiftRules.jsArtifacts
    (<span>{rs % ("onclick" -> (ui.hide(sid).cmd &
            ui.showAndFocus(hid).cmd & JsRaw("return false;")))}{dealWithBlur(rh % ("style" -> "display: none"), (ui.show(sid).cmd & ui.hide(hid).cmd))}</span>)
  }

  def swappable(shown: Elem, hidden: String => Elem): Elem = {
    val (rs, sid) = findOrAddId(shown)
    val hid = formFuncName
    val ui = LiftRules.jsArtifacts

    val rh = <span id={hid}>{hidden(ui.show(sid).toJsCmd + ";" + ui.hide(hid).toJsCmd + ";")}</span>
    (<span>{rs % ("onclick" -> (ui.hide(sid).toJsCmd + ";" + ui.show(hid).toJsCmd + "; return false;"))}{(rh % ("style" -> "display: none"))}</span>)
  }

  private def dealWithBlur(elem: Elem, blurCmd: String): Elem = {
    (elem \ "@onblur").toList match {
      case Nil => elem % ("onblur" -> blurCmd)
      case x :: xs => val attrs = elem.attributes.filter(_.key != "onblur")
      Elem(elem.prefix, elem.label, new UnprefixedAttribute("onblur", Text(blurCmd + x.text), attrs), elem.scope, elem.child: _*)
    }
  }


  private[http] def appendFuncToURL(url: String, funcStr: String): String =
  splitAtHash(url){to => to + (if (to.indexOf("?") >= 0) "&" else "?") + funcStr}

  private def splitAtHash(str: String)(f: String => String): String =
  str.indexOf("#") match {
    case idx if idx < 0 => f(str)
    case idx => f(str.substring(0, idx)) + str.substring(idx)
  }


  /**
   * create an anchor tag around a body
   *
   * @param func - the function to invoke when the link is clicked
   * @param body - the NodeSeq to wrap in the anchor tag
   */
  def link(to: String, func: () => Any, body: NodeSeq,
           attrs: (String, String)*): Elem = {
    fmapFunc((a: List[String]) => {func(); true})(key =>
            attrs.foldLeft(<a href={appendFuncToURL(to, key + "=_")}>{body}</a>)(_ % _))
  }

  private def makeFormElement(name: String, func: AFuncHolder,
                              attrs: (String, String)*): Elem =
    fmapFunc(func)(funcName =>
            attrs.foldLeft(<input type={name} name={funcName}/>)(_ % _))

  def text_*(value: String, func: AFuncHolder, attrs: (String, String)*): Elem =
    text_*(value, func, Empty, attrs: _*)

  def text_*(value: String, func: AFuncHolder, ajaxTest: String => JsCmd, attrs: (String, String)*): Elem =
    text_*(value, func, Full(ajaxTest), attrs: _*)

  private def buildOnBlur(bf: Box[String => JsCmd]): MetaData = bf match {
    case Full(func) =>
      new UnprefixedAttribute("onblur", Text(ajaxCall(JsRaw("this.value"), func)._2.toJsCmd), Null)

    case _ => Null
  }


  def text_*(value: String, ignoreBlur: Boolean, func: AFuncHolder, ajaxTest: Box[String => JsCmd], attrs: (String, String)*): Elem =
    makeFormElement("text", func, attrs: _*) % new UnprefixedAttribute("value", Text(value), Null) % (
      if (ignoreBlur) Null else buildOnBlur(ajaxTest))

  def text_*(value: String, func: AFuncHolder, ajaxTest: Box[String => JsCmd], attrs: (String, String)*): Elem =
    text_*(value, false, func, ajaxTest, attrs :_*)

  def password_*(value: String, func: AFuncHolder, attrs: (String, String)*): Elem =
    makeFormElement("password", func, attrs: _*) % ("value" -> value)

  def hidden_*(func: AFuncHolder, attrs: (String, String)*): Elem =
    makeFormElement("hidden", func, attrs: _*) % ("value" -> "true")

  def submit_*(value: String, func: AFuncHolder, attrs: (String, String)*): Elem =
    {
      def doit = makeFormElement("submit", func, attrs: _*) % ("value" -> value)

      _formGroup.is match {
        case Empty => formGroup(1)(doit)
        case _ => doit
      }
    }

  def text(value: String, func: String => Any, attrs: (String, String)*): Elem =
    text_*(value, SFuncHolder(func), attrs: _*)

  def textAjaxTest(value: String, func: String => Any, ajaxTest: String => JsCmd, attrs: (String, String)*): Elem =
    text_*(value, SFuncHolder(func), ajaxTest, attrs: _*)

  def textAjaxTest(value: String, func: String => Any, ajaxTest: Box[String => JsCmd], attrs: (String, String)*): Elem =
    text_*(value, SFuncHolder(func), ajaxTest, attrs: _*)


  def password(value: String, func: String => Any, attrs: (String, String)*): Elem =
    makeFormElement("password", SFuncHolder(func), attrs: _*) % new UnprefixedAttribute("value", Text(value), Null)

  def hidden(func: () => Any, attrs: (String, String)*): Elem =
    makeFormElement("hidden", NFuncHolder(func), attrs: _*) % ("value" -> "true")

  def hidden(func: (String) => Any, defaultlValue: String, attrs: (String, String)*): Elem =
    makeFormElement("hidden", SFuncHolder(func), attrs: _*) % ("value" -> defaultlValue)

  /**
   * Generates a form submission button.
   *
   * @param value The label for the button
   * @param func The function that will be executed on form submission
   * @param attrs Optional XHTML element attributes that will be applied to the button
   */
  def submit(value: String, func: () => Any, attrs: (String, String)*): Elem = {

    def doit = {
      makeFormElement("submit", NFuncHolder(func), attrs: _*) %
              new UnprefixedAttribute("value", Text(value), Null)
    }

    _formGroup.is match {
      case Empty => formGroup(1)(doit)
      case _ => doit
    }
  }

  /**
   * Constructs an Ajax submit button that can be used inside ajax forms.
   * Multiple buttons can be used in the same form.
   * 
   * @param value - the button text
   * @param func - the ajax function to be called
   * @param attrs - button attributes
   *
   */
  def ajaxSubmit(value: String, func: () => JsCmd, attrs: (String, String)*): Elem = {
    val funcName = "z" + Helpers.nextFuncName
    addFunctionMap(funcName, contextFuncBuilder(func))

    (attrs.foldLeft(<input type="submit" name={funcName}/>)(_ % _)) %
      new UnprefixedAttribute("value", Text(value), Null) %
      ("onclick" -> ("liftAjax.lift_uriSuffix = '"+funcName+"=_'; return true;"))
    
  }

  /**
   * Generates a form submission button with a default label.
   *
   * @param func The function that will be executed on form submission
   * @param attrs Optional XHTML element attributes that will be applied to the button
   */
  def submitButton(func: () => Any, attrs: (String, String)*): Elem = makeFormElement("submit", NFuncHolder(func), attrs: _*)

  /**
   * Takes a form and wraps it so that it will be submitted via AJAX.
   *
   * @param body The form body. This should not include the <form> tag.
   */
  def ajaxForm(body: NodeSeq) = (<lift:form>{body}</lift:form>)

  /**
   * Takes a form and wraps it so that it will be submitted via AJAX.
   *
   * @param body The form body. This should not include the <form> tag.
   * @param onSubmit JavaScript code to execute on the client prior to submission
   *
   * @deprecated Use ajaxForm(NodeSeq,JsCmd) instead
   */
  def ajaxForm(onSubmit: JsCmd, body: NodeSeq) = (<lift:form onsubmit={onSubmit.toJsCmd}>{body}</lift:form>)

  /**
   * Takes a form and wraps it so that it will be submitted via AJAX.
   *
   * @param body The form body. This should not include the <form> tag.
   * @param onSubmit JavaScript code to execute on the client prior to submission
   */
  def ajaxForm(body: NodeSeq, onSubmit: JsCmd) = (<lift:form onsubmit={onSubmit.toJsCmd}>{body}</lift:form>)

  /**
   * Takes a form and wraps it so that it will be submitted via AJAX. This also
   * takes a parameter for script code that will be executed after the form has been submitted.
   *
   * @param body The form body. This should not include the <form> tag.
   * @param postSubmit Code that should be executed after a successful submission
   */
  def ajaxForm(body : NodeSeq, onSubmit : JsCmd, postSubmit : JsCmd) = (<lift:form onsubmit={onSubmit.toJsCmd} postsubmit={postSubmit.toJsCmd}>{body}</lift:form>)

  /**
   * Takes a form and wraps it so that it will be submitted via AJAX and processed by
   * a JSON handler. This can be useful if you may have dynamic client-side modification
   * of the form (addition or removal).
   *
   * @param jsonHandler The handler that will process the form
   * @param body The form body. This should not include the <form> tag.
   */
  def jsonForm(jsonHandler: JsonHandler, body: NodeSeq): NodeSeq = jsonForm(jsonHandler, Noop, body)

  /**
   * Takes a form and wraps it so that it will be submitted via AJAX and processed by
   * a JSON handler. This can be useful if you may have dynamic client-side modification
   * of the form (addition or removal).
   *
   * @param jsonHandler The handler that will process the form
   * @param onSubmit JavaScript code that will be executed on the client prior to submitting
   * the form
   * @param body The form body. This should not include the <form> tag.
   */
  def jsonForm(jsonHandler: JsonHandler, onSubmit: JsCmd, body: NodeSeq): NodeSeq = {
    val id = formFuncName
    <form onsubmit={(onSubmit & jsonHandler.call("processForm", FormToJSON(id)) & JsReturn(false)).toJsCmd} id={id}>{body}</form>
  }

  /**
   * Having a regular form, this method can be used to send the content of the form as JSON.
   * the request will be processed by the jsonHandler
   *
   * @param jsonHandler - the handler that process this request
   * @oaram formId - the id of the form
   */
  def submitJsonForm(jsonHandler: JsonHandler, formId: String):JsCmd = jsonHandler.call("processForm", FormToJSON(formId))

  /**
   * Having a regular form, this method can be used to send the serialized content of the form.
   *
   * @oaram formId - the id of the form
   */
  def submitAjaxForm(formId: String):JsCmd = SHtml.makeAjaxCall(LiftRules.jsArtifacts.serialize(formId))

  /** 
   * Submits a form denominated by a formId and execute the func function
   * after form fields functions are executed.
   */ 
  def submitAjaxForm(formId: String, func: () => JsCmd): JsCmd = {
    val funcName = "Z" + Helpers.nextFuncName
    addFunctionMap(funcName, contextFuncBuilder(func))

    makeAjaxCall(JsRaw(
       LiftRules.jsArtifacts.serialize(formId).toJsCmd + " + " + 
       Str("&" + funcName + "=true").toJsCmd))
  }

  /**
   * Having a regular form, this method can be used to send the serialized content of the form.
   *
   * @oaram formId - the id of the form
   * @param postSubmit - the function that needs to be called after a successfull request
   */
  def submitAjaxForm(formId: String, postSubmit: Call):JsCmd =
    SHtml.makeAjaxCall(LiftRules.jsArtifacts.serialize(formId), AjaxContext.js(Full(postSubmit.toJsCmd)))


  private def secureOptions[T](options: Seq[(T, String)], default: Box[T],
                               onSubmit: T => Unit): (Seq[(String, String)], Box[String], AFuncHolder) = {
    val secure = options.map {case (obj, txt) => (obj, randomString(20), txt)}
    val defaultNonce = default.flatMap(d => secure.find(_._1 == d).map(_._2))
    val nonces = secure.map {case (obj, nonce, txt) => (nonce, txt)}
    def process(nonce: String): Unit =
      secure.find(_._2 == nonce).map(x => onSubmit(x._1))
    (nonces, defaultNonce, SFuncHolder(process))
  }

  /**
   * Create a select box based on the list with a default value and the function to be executed on
   * form submission
   *
   * @param opts -- the options.  A list of value and text pairs (value, text to display)
   * @param deflt -- the default value (or Empty if no default value)
   * @param func -- the function to execute on form submission
   */
  def select(opts: Seq[(String, String)], deflt: Box[String], func: String => Any, attrs: (String, String)*): Elem =
    select_*(opts, deflt, SFuncHolder(func), attrs: _*)

  /**
   * Create a select box based on the list with a default value and the function
   * to be executed on form submission
   *
   * @param options -- a list of value and text pairs (value, text to display)
   * @param default -- the default value (or Empty if no default value)
   * @param onSubmit -- the function to execute on form submission
   */
  def selectObj[T](options: Seq[(T, String)], default: Box[T],
                   onSubmit: T => Unit, attrs: (String, String)*): Elem = {
    val (nonces, defaultNonce, secureOnSubmit) =
    secureOptions(options, default, onSubmit)

    select_*(nonces, defaultNonce, secureOnSubmit, attrs: _*)
  }

  /**
   * Create a select box based on the list with a default value and the function to be executed on
   * form submission
   *
   * @param opts -- the options.  A list of value and text pairs
   * @param deflt -- the default value (or Empty if no default value)
   * @param func -- the function to execute on form submission
   */
  def select_*(opts: Seq[(String, String)], deflt: Box[String],
               func: AFuncHolder, attrs: (String, String)*): Elem = {
    val vals = opts.map(_._1)
    val testFunc = LFuncHolder(in => in.filter(v => vals.contains(v)) match {case Nil => false case xs => func(xs)}, func.owner)

    attrs.foldLeft(fmapFunc(testFunc)(fn => <select name={fn}>{opts.flatMap {case (value, text) => (<option value={value}>{text}</option>) % selected(deflt.exists(_ == value))}}</select>))(_ % _)
  }

  /**
   * Create a select box based on the list with a default value and the function to be executed on
   * form submission.  No check is made to see if the resulting value was in the original list.
   * For use with DHTML form updating.
   *
   * @param opts -- the options.  A list of value and text pairs
   * @param deflt -- the default value (or Empty if no default value)
   * @param func -- the function to execute on form submission
   */
  def untrustedSelect(opts: Seq[(String, String)], deflt: Box[String],
                      func: String => Any, attrs: (String, String)*): Elem =
    untrustedSelect_*(opts, deflt, SFuncHolder(func), attrs: _*)

  /**
   * Create a select box based on the list with a default value and the function to be executed on
   * form submission.  No check is made to see if the resulting value was in the original list.
   * For use with DHTML form updating.
   *
   * @param opts -- the options.  A list of value and text pairs
   * @param deflt -- the default value (or Empty if no default value)
   * @param func -- the function to execute on form submission
   */
  def untrustedSelect_*(opts: Seq[(String, String)], deflt: Box[String],
                        func: AFuncHolder, attrs: (String, String)*): Elem =
    fmapFunc(func)(funcName =>
            attrs.foldLeft(<select name={funcName}>{opts.flatMap {case (value, text) => (<option value={value}>{text}</option>) % selected(deflt.exists(_ == value))}}</select>)(_ % _))


  private def selected(in: Boolean) = if (in) new UnprefixedAttribute("selected", "selected", Null) else Null

  def multiSelect(opts: Seq[(String, String)], deflt: Seq[String],
                  func: List[String] => Any, attrs: (String, String)*): Elem =
    multiSelect_*(opts, deflt, LFuncHolder(func), attrs: _*)

  /**
   * Create a select box based on the list with a default value and the function
   * to be executed on form submission
   *
   * @param options -- a list of value and text pairs (value, text to display)
   * @param default -- the default value (or Empty if no default value)
   * @param onSubmit -- the function to execute on form submission
   */
  def multiSelectObj[T](options: Seq[(T, String)], default: Seq[T],
                        onSubmit: List[T] => Unit, attrs: (String, String)*): Elem = {
    val (nonces, defaultNonce, secureOnSubmit) =
    secureMultiOptions(options, default, onSubmit)

    multiSelect_*(nonces, defaultNonce, secureOnSubmit, attrs: _*)
  }

  private[http] def secureMultiOptions[T](options: Seq[(T, String)], default: Seq[T],
                                          onSubmit: List[T] => Unit): (Seq[(String, String)],
          Seq[String], AFuncHolder) =
    {
      val o2 = options.toList

      val secure: List[(T, String, String)] = o2.map {case (obj, txt) => (obj, randomString(20), txt)}
      val sm: Map[String, T] = Map(secure.map(v => (v._2, v._1)): _*)
      val defaultNonce: Seq[String] = default.flatMap(d => secure.find(_._1 == d).map(_._2))
      val nonces: List[(String, String)] = secure.map {case (obj, nonce, txt) => (nonce, txt)}.toList
      def process(info: List[String]): Unit = onSubmit(info.flatMap(sm.get))

      (nonces, defaultNonce, LFuncHolder(process))
    }

  def multiSelect_*(opts: Seq[(String, String)],
                    deflt: Seq[String],
                    func: AFuncHolder, attrs: (String, String)*): Elem =
    fmapFunc(func)(funcName =>
            attrs.foldLeft(<select multiple="true" name={funcName}>{opts.flatMap(o => (<option value={o._1}>{o._2}</option>) % selected(deflt.contains(o._1)))}</select>)(_ % _))


  def textarea(value: String, func: String => Any, attrs: (String, String)*): Elem =
    textarea_*(value, SFuncHolder(func), attrs: _*)

  def textarea_*(value: String, func: AFuncHolder, attrs: (String, String)*): Elem =
    fmapFunc(func)(funcName =>
            attrs.foldLeft(<textarea name={funcName}>{value}</textarea>)(_ % _))

  def radio(opts: Seq[String], deflt: Box[String], func: String => Any,
            attrs: (String, String)*): ChoiceHolder[String] =
    radio_*(opts, deflt, SFuncHolder(func), attrs: _*)

  def radio_*(opts: Seq[String], deflt: Box[String],
              func: AFuncHolder, attrs: (String, String)*): ChoiceHolder[String] = {
    fmapFunc(func) {
      name =>
              val itemList = opts.map(v => ChoiceItem(v,
                attrs.foldLeft(<input type="radio" name={name} value={v}/>)(_ % _) %
                        checked(deflt.filter((s: String) => s == v).isDefined)))
              ChoiceHolder(itemList)
    }
  }

  def fileUpload(func: FileParamHolder => Unit): Elem = {
    val f2: FileParamHolder => Unit = fp =>
            if (fp.file != null && fp.file.length > 0) func(fp)

    fmapFunc(BinFuncHolder(f2))(name => <input type="file" name={name}/>)
  }

  /** Holds a form control as HTML along with some user defined value */
  case class ChoiceItem[T](key: T, xhtml: NodeSeq)

  /** Holds a series of choices: HTML for input controls alongside some user defined value */
  case class ChoiceHolder[T](items: Seq[ChoiceItem[T]]) {
    /** Retrieve the ChoiceItem that has the given key, throwing NoSuchElementException if there is no matching ChoiceItem */
    def apply(in: T) = items.filter(_.key == in).first.xhtml

    /** Retrieve the nth ChoiceItem, 0-based */
    def apply(in: Int) = items(in).xhtml

    /** Apply a function to each ChoiceItem, collecting the results */
    def map[A](f: ChoiceItem[T] => A) = items.map(f)

    /** Apply a function to each ChoiceItem, concatenating the results */
    def flatMap[A](f: ChoiceItem[T] => Iterable[A]) = items.flatMap(f)

    /** Return the ChoiceItems that the given function returns true for */
    def filter(f: ChoiceItem[T] => Boolean) = items.filter(f)

    /** Generate a simple form by calling ChoiceItem.htmlize on each ChoiceItem and concatenating the resulting HTML */
    def toForm: NodeSeq = flatMap(ChoiceHolder.htmlize)
  }

   
  object ChoiceHolder {
    /** Convert a ChoiceItem into a span containing the control and the toString of the key */
    var htmlize: ChoiceItem[_] => NodeSeq = c => (<span>{c.xhtml} {c.key.toString}<br/> </span>)
  }

  private def checked(in: Boolean) = if (in) new UnprefixedAttribute("checked", "checked", Null) else Null

  private def setId(in: Box[String]) = in match {case Full(id) => new UnprefixedAttribute("id", Text(id), Null); case _ => Null}

  /**
   * Generate a ChoiceHolder of possible checkbox type inputs that calls back to the given function when the form is submitted.
   *
   * @param possible complete sequence of possible values, each a separate checkbox when rendered
   * @param actual values to be preselected
   * @param func function to receive all values corresponding to the checked boxes
   * @param attrs sequence of attributes to apply to each checkbox input element
   * @return ChoiceHolder containing the checkboxes and values in order
   */
  def checkbox[T](possible: Seq[T], actual: Seq[T], func: Seq[T] => Any, attrs: (String, String)*): ChoiceHolder[T] = {
    val len = possible.length
    fmapFunc(LFuncHolder((strl: List[String]) => {func(strl.map(toInt(_)).filter(x => x >= 0 && x < len).map(possible(_))); true})) {
      name =>
              ChoiceHolder(possible.toList.zipWithIndex.map(p =>
                      ChoiceItem(p._1,
                        attrs.foldLeft(<input type="checkbox" name={name} value={p._2.toString}/>)(_ % _) %
                                checked(actual.contains(p._1)) ++ (if (p._2 == 0) (<input type="hidden" name={name} value="-1"/>) else Nil))))
    }
  }

  /**
   * Defines a new checkbox set to  { @code value } and running  { @code func } when the
   * checkbox is submitted.
   */
  def checkbox(value: Boolean, func: Boolean => Any, attrs: (String, String)*): NodeSeq = {
    checkbox_id(value, func, Empty, attrs: _*)
  }

  /**
   * Defines a new checkbox set to  { @code value } and running  { @code func } when the
   * checkbox is submitted. Has an id of  { @code id }.
   */
  def checkbox_id(value: Boolean, func: Boolean => Any,
                  id: Box[String], attrs: (String, String)*): NodeSeq = {
    def from(f: Boolean => Any): List[String] => Boolean = (in: List[String]) => {
      f(in.exists(toBoolean(_)))
      true
    }
    checkbox_*(value, LFuncHolder(from(func)), id, attrs: _*)
  }

  def checkbox_*(value: Boolean, func: AFuncHolder, id: Box[String],
                 attrs: (String, String)*): NodeSeq = {
    fmapFunc(func)(name =>
            (<input type="hidden" name={name} value="false"/>) ++
                    (attrs.foldLeft(<input type="checkbox" name={name} value="true"/>)(_ % _) % checked(value) % setId(id))
      )
  }

}

object AjaxType extends Enumeration("javascript", "json") {
  val JavaScript, JSON = Value
}

object AjaxContext {
  def js(success: Box[String], failure: Box[String]) = new JsContext(success, failure)

  def js(success: Box[String]) = new JsContext(success, Empty)

  def json(success: Box[String], failure: Box[String]) = new JsonContext(success, failure)

  def json(success: Box[String]) = new JsonContext(success, Empty)
}


case class AjaxContext(success: Box[String], failure: Box[String], responseType: AjaxType.Value)

case class JsContext(override val success: Box[String], override val failure: Box[String]) extends AjaxContext(success, failure, AjaxType.JavaScript)

case class JsonContext(override val success: Box[String], override val failure: Box[String]) extends AjaxContext(success, failure, AjaxType.JSON)

}
}