Skip to content
This repository
Browse code

Add CSS bound LiftScreen

  • Loading branch information...
commit 5d1174851c67ebcfd6e24b32d176bc43055f5539 1 parent 4a4dbd3
pbrant authored January 24, 2012
92  web/webkit/src/main/scala/net/liftweb/http/CssBoundLiftScreen.scala
... ...
@@ -0,0 +1,92 @@
  1
+/*
  2
+ * Copyright 2011-2012 WorldWide Conferencing, LLC
  3
+ *
  4
+ * Licensed under the Apache License, Version 2.0 (the "License");
  5
+ * you may not use this file except in compliance with the License.
  6
+ * You may obtain a copy of the License at
  7
+ *
  8
+ *     http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  10
+ * Unless required by applicable law or agreed to in writing, software
  11
+ * distributed under the License is distributed on an "AS IS" BASIS,
  12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13
+ * See the License for the specific language governing permissions and
  14
+ * limitations under the License.
  15
+ */
  16
+package net.liftweb
  17
+package http
  18
+
  19
+import net.liftweb.http.js.JsCmds._
  20
+import net.liftweb.http.js.JsCmd
  21
+import net.liftweb.common._
  22
+import util._
  23
+
  24
+import xml._
  25
+
  26
+trait CssBoundLiftScreen extends LiftScreen with CssBoundScreen {
  27
+  protected object SavedDefaultXml extends ScreenVar[NodeSeq](defaultXml) {
  28
+    override lazy val __nameSalt = Helpers.nextFuncName
  29
+  }
  30
+
  31
+  protected object LocalAction extends TransientRequestVar[String]("") {
  32
+    override lazy val __nameSalt = Helpers.nextFuncName
  33
+  }
  34
+
  35
+  protected object LocalActionRef extends ScreenVar[String](S.fmapFunc(setLocalAction _)(s => s)) {
  36
+    override lazy val __nameSalt = Helpers.nextFuncName
  37
+  }
  38
+
  39
+  protected object PrevId extends TransientRequestVar[Box[String]](Empty) {
  40
+    override lazy val __nameSalt = Helpers.nextFuncName
  41
+  }
  42
+
  43
+  protected object CancelId extends TransientRequestVar[String]("") {
  44
+    override lazy val __nameSalt = Helpers.nextFuncName
  45
+  }
  46
+
  47
+  protected object LocalActions extends ScreenVar[Map[String, () => JsCmd]](Map[String, () => JsCmd]()) {
  48
+    override lazy val __nameSalt = Helpers.nextFuncName
  49
+  }
  50
+
  51
+  override def localSetup() {
  52
+    SavedDefaultXml.get
  53
+    LocalActionRef.get
  54
+  }
  55
+
  56
+  override def allTemplate = SavedDefaultXml.get
  57
+
  58
+  protected def defaultAllTemplate = super.allTemplate
  59
+
  60
+  override protected def doFinish(): JsCmd= {
  61
+    val fMap: Map[String, () => JsCmd] = LocalActions.get
  62
+    if (! LocalAction.get.isEmpty)
  63
+      fMap.get(LocalAction.get) map (_()) getOrElse (
  64
+        throw new IllegalArgumentException("No local action available with that binding"))
  65
+    else {
  66
+      validate match {
  67
+        case Nil =>
  68
+          val snapshot = createSnapshot
  69
+          PrevSnapshot.set(Full(snapshot))
  70
+          finish()
  71
+          redirectBack()
  72
+        case xs => {
  73
+          S.error(xs)
  74
+          if (ajaxForms_?) {
  75
+            replayForm
  76
+          } else {
  77
+            Noop
  78
+          }
  79
+        }
  80
+      }
  81
+    }
  82
+  }
  83
+
  84
+  protected def renderWithErrors(errors: List[FieldError]) {
  85
+    S.error(errors)
  86
+    AjaxOnDone.set(replayForm)
  87
+  }
  88
+
  89
+  protected def renderFormCmd: JsCmd = SetHtml(FormGUID, renderHtml())
  90
+
  91
+  protected def replayForm: JsCmd = renderFormCmd
  92
+}
377  web/webkit/src/main/scala/net/liftweb/http/CssBoundScreen.scala
... ...
@@ -0,0 +1,377 @@
  1
+/*
  2
+ * Copyright 2011-2012 WorldWide Conferencing, LLC
  3
+ *
  4
+ * Licensed under the Apache License, Version 2.0 (the "License");
  5
+ * you may not use this file except in compliance with the License.
  6
+ * You may obtain a copy of the License at
  7
+ *
  8
+ *     http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  10
+ * Unless required by applicable law or agreed to in writing, software
  11
+ * distributed under the License is distributed on an "AS IS" BASIS,
  12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13
+ * See the License for the specific language governing permissions and
  14
+ * limitations under the License.
  15
+ */
  16
+package net.liftweb
  17
+package http
  18
+
  19
+import js.jquery.JqJE.JqId
  20
+import js.JsCmd
  21
+import js.JsCmds._
  22
+import js.jquery.JqJsCmds._
  23
+import js.JE
  24
+import net.liftweb.util._
  25
+import Helpers._
  26
+import net.liftweb.common._
  27
+import xml._
  28
+
  29
+import FieldBinding._
  30
+
  31
+trait CssBoundScreen extends ScreenWizardRendered with Loggable {
  32
+  self: AbstractScreen =>
  33
+
  34
+  def formName: String
  35
+
  36
+  def labelSuffix: NodeSeq = Text(":")
  37
+
  38
+  protected lazy val cssClassBinding = new CssClassBinding
  39
+
  40
+  protected val LocalActionRef: AnyVar[String, _]
  41
+  protected val LocalAction: AnyVar[String, _]
  42
+  protected val LocalActions: AnyVar[Map[String, () => JsCmd], _]
  43
+
  44
+  val NextId: AnyVar[String, _]
  45
+
  46
+  protected val PrevId: AnyVar[Box[String], _]
  47
+  protected val CancelId: AnyVar[String, _]
  48
+
  49
+  protected def additionalFormBindings: Box[CssSel] = Empty
  50
+
  51
+  private def traceInline[T](msg: => String, v: T): T = {
  52
+    logger.trace(msg)
  53
+    v
  54
+  }
  55
+
  56
+  private object FieldBindingUtils {
  57
+    def sel(f: CssClassBinding => String, sel: String) = sel format (f(cssClassBinding))
  58
+    def replace(f: CssClassBinding => String) = ".%s" format (f(cssClassBinding))
  59
+    def replaceChildren(f: CssClassBinding => String) = ".%s *" format (f(cssClassBinding))
  60
+
  61
+    def remove(f: CssClassBinding => String) =
  62
+      traceInline("Removing %s".format(f(cssClassBinding)), ".%s".format(f(cssClassBinding))) #> NodeSeq.Empty
  63
+
  64
+    def nsSetChildren(f: CssClassBinding => String, value: NodeSeq) =
  65
+      traceInline("Binding %s to %s".format(replaceChildren(f), value), replaceChildren(f) #> value)
  66
+
  67
+    def funcSetChildren(f: CssClassBinding => String, value: NodeSeq => NodeSeq) =
  68
+      traceInline("Binding %s to function".format(replaceChildren(f)), replaceChildren(f) #> value)
  69
+
  70
+    def optSetChildren(f: CssClassBinding => String, value: Box[NodeSeq]) =
  71
+      traceInline("Binding %s to %s".format(replaceChildren(f), value), replaceChildren(f) #> value)
  72
+
  73
+    def nsReplace(f: CssClassBinding=>String, value: NodeSeq) =
  74
+      traceInline("Binding %s to %s".format(replace(f), value), replace(f) #> value)
  75
+
  76
+    def funcReplace(f: CssClassBinding=>String, value: NodeSeq => NodeSeq) =
  77
+      traceInline("Binding %s to function".format(replace(f)), replace(f) #> value)
  78
+
  79
+    def optReplace(f: CssClassBinding=>String, value: Box[NodeSeq]) =
  80
+      traceInline("Binding %s to %s".format(replace(f), value), replace(f) #> value)
  81
+
  82
+    def updateAttrs(metaData: MetaData): NodeSeq => NodeSeq = {
  83
+      case e:Elem => e % metaData
  84
+    }
  85
+
  86
+    def update(f: CssClassBinding => String, metaData: MetaData) =
  87
+      traceInline("Update %s with %s".format(f(cssClassBinding), metaData),
  88
+        ".%s".format(f(cssClassBinding)) #> updateAttrs(metaData))
  89
+  }
  90
+
  91
+  protected def bindLocalAction(selector: String, func: () => JsCmd): CssSel = {
  92
+    mapLocalAction(func)(name =>
  93
+      selector #> (
  94
+        SHtml.makeAjaxCall(LiftRules.jsArtifacts.serialize(NextId.get) + ("&" + LocalActionRef.get + "=" + name)).cmd
  95
+      ).toJsCmd)
  96
+  }
  97
+
  98
+  protected def mapLocalAction[T](func: () => JsCmd)(f: String => T): T = {
  99
+    val name = randomString(20)
  100
+    LocalActions.set(LocalActions.is + (name -> func))
  101
+    f(name)
  102
+  }
  103
+
  104
+  protected def setLocalAction(s: String) {
  105
+    logger.debug("Setting LocalAction (%s) to %s".format(
  106
+      Integer.toString(System.identityHashCode(LocalAction), 16), s))
  107
+    LocalAction.set(s)
  108
+  }
  109
+
  110
+  override protected def renderAll(currentScreenNumber: Box[NodeSeq],
  111
+                          screenCount: Box[NodeSeq],
  112
+                          wizardTop: Box[Elem],
  113
+                          screenTop: Box[Elem],
  114
+                          fields: List[ScreenFieldInfo],
  115
+                          prev: Box[Elem],
  116
+                          cancel: Box[Elem],
  117
+                          next: Box[Elem],
  118
+                          finish: Box[Elem],
  119
+                          screenBottom: Box[Elem],
  120
+                          wizardBottom: Box[Elem],
  121
+                          nextId: (String, () => JsCmd),
  122
+                          prevId: Box[(String, () => JsCmd)],
  123
+                          cancelId: (String, () => JsCmd),
  124
+                          theScreen: AbstractScreen,
  125
+                          ajax_? : Boolean): NodeSeq = {
  126
+
  127
+    import FieldBindingUtils._
  128
+
  129
+    NextId.set(nextId._1)
  130
+    PrevId.set(prevId map (_._1))
  131
+    CancelId.set(cancelId._1)
  132
+
  133
+    val notices: List[(NoticeType.Value, NodeSeq, Box[String])] = S.getAllNotices
  134
+
  135
+    def fieldsWithStyle(style: BindingStyle, includeMissing: Boolean) =
  136
+      logger.trace("Looking for fields with style %s, includeMissing = %s".format(style, includeMissing),
  137
+        fields filter (field => field.binding map (_.bindingStyle == style) openOr (includeMissing)))
  138
+
  139
+    def bindingInfoWithFields(style: BindingStyle) =
  140
+      logger.trace("Looking for fields with style %s".format(style),
  141
+        (for {
  142
+          field <- fields;
  143
+          bindingInfo <- field.binding if bindingInfo.bindingStyle == style
  144
+        } yield (bindingInfo, field)).toList)
  145
+
  146
+    def templateFields: List[CssBindFunc] = List(sel(_.fieldContainer, ".%s") #> (fieldsWithStyle(Template, true) map (field => bindField(field))))
  147
+
  148
+    def selfFields: List[CssBindFunc] =
  149
+      for ((bindingInfo, field) <- bindingInfoWithFields(Self))
  150
+      yield traceInline("Binding self field %s".format(bindingInfo.selector(formName)),
  151
+        bindingInfo.selector(formName) #> bindField(field))
  152
+
  153
+    def defaultFields: List[CssBindFunc] =
  154
+      for ((bindingInfo, field) <- bindingInfoWithFields(Default))
  155
+      yield traceInline("Binding default field %s to %s".format(bindingInfo.selector(formName), defaultFieldNodeSeq),
  156
+        bindingInfo.selector(formName) #> bindField(field)(defaultFieldNodeSeq))
  157
+
  158
+    def customFields: List[CssBindFunc] =
  159
+      for {
  160
+        field <- fields
  161
+        bindingInfo <- field.binding
  162
+        custom <- Some(bindingInfo.bindingStyle) collect { case c:Custom => c }
  163
+      } yield traceInline("Binding custom field %s to %s".format(bindingInfo.selector(formName), custom.template),
  164
+        bindingInfo.selector(formName) #> bindField(field)(custom.template))
  165
+
  166
+    def bindFields: CssBindFunc = {
  167
+      logger.trace("Binding fields", fields)
  168
+      List(templateFields, selfFields, defaultFields, customFields).flatten.reduceLeft(_ & _)
  169
+    }
  170
+
  171
+    def bindField(f: ScreenFieldInfo): NodeSeq => NodeSeq = {
  172
+      val theFormEarly = f.input
  173
+      val curId = theFormEarly.flatMap(Helpers.findId) or
  174
+        f.field.uniqueFieldId openOr Helpers.nextFuncName
  175
+
  176
+      val theForm = theFormEarly.map{
  177
+        fe => {
  178
+          val f = Helpers.deepEnsureUniqueId(fe)
  179
+          val id = Helpers.findBox(f)(_.attribute("id").
  180
+            map(_.text).
  181
+            filter(_ == curId))
  182
+          if (id.isEmpty) {
  183
+            Helpers.ensureId(f, curId)
  184
+          } else {
  185
+            f
  186
+          }
  187
+        }
  188
+      }
  189
+
  190
+      val myNotices = notices.filter(fi => fi._3.isDefined && fi._3 == curId)
  191
+
  192
+      def bindLabel(): CssBindFunc = {
  193
+        val basicLabel = sel(_.label, ".%s [for]") #> curId & nsSetChildren(_.label, f.text ++ labelSuffix)
  194
+        myNotices match {
  195
+          case Nil => basicLabel
  196
+          case _ =>
  197
+            val maxN = myNotices.map(_._1).sortWith{_.id > _.id}.head // get the maximum type of notice (Error > Warning > Notice)
  198
+            val metaData: MetaData = noticeTypeToAttr(theScreen).map(_(maxN)) openOr Null
  199
+            basicLabel & update(_.label, metaData)
  200
+        }
  201
+      }
  202
+
  203
+      def bindForm(): CssBindFunc =
  204
+        traceInline("Replacing %s with %s".format(replace(_.value), theForm),
  205
+          replace(_.value) #> theForm)
  206
+
  207
+      def bindHelp(): CssBindFunc =
  208
+        f.help match {
  209
+          case Full(hlp) => nsSetChildren(_.help, hlp)
  210
+          case _ => remove(_.help)
  211
+        }
  212
+
  213
+      def bindErrors(): CssBindFunc =
  214
+        myNotices match {
  215
+          case Nil => remove(_.errors)
  216
+          case xs => replaceChildren(_.errors) #> xs.map { case(noticeType, msg, _) =>
  217
+            val metaData: MetaData = noticeTypeToAttr(theScreen).map(_(noticeType)) openOr Null
  218
+            nsSetChildren(_.error, msg) & update(_.error, metaData)
  219
+          }
  220
+        }
  221
+
  222
+      def bindAll() = bindLabel() & bindForm() & bindHelp() & bindErrors()
  223
+
  224
+      f.transform map (func => bindAll() andThen func()) openOr (bindAll())
  225
+    }
  226
+
  227
+    def url = S.uri
  228
+
  229
+    val savAdditionalFormBindings = additionalFormBindings
  230
+
  231
+    def bindErrors: CssBindFunc = notices.filter(_._3.isEmpty) match {
  232
+      case Nil => remove(_.globalErrors)
  233
+      case xs => replaceChildren(_.globalErrors) #> xs.map { case(noticeType, msg, _) =>
  234
+        val metaData: MetaData = noticeTypeToAttr(theScreen).map(_(noticeType)) openOr Null
  235
+        nsSetChildren(_.error, msg) & update(_.error, metaData)
  236
+      }
  237
+    }
  238
+
  239
+    def bindFieldsWithAdditional(xhtml: NodeSeq) =
  240
+      (savAdditionalFormBindings map (bindFields & _) openOr (bindFields))(xhtml)
  241
+
  242
+    def liftScreenAttr(s: String) =
  243
+      new UnprefixedAttribute("data-lift-screen-control", Text(s), Null)
  244
+
  245
+    def bindForm(xhtml: NodeSeq): NodeSeq = {
  246
+      val fields = bindFieldsWithAdditional(xhtml)
  247
+
  248
+      val snapshot = createSnapshot
  249
+
  250
+      val ret =
  251
+        (<form id={nextId._1} action={url}
  252
+               method="post">{S.formGroup(-1)(SHtml.hidden(() =>
  253
+          snapshot.restore()) % liftScreenAttr("restoreAction"))}{fields}{
  254
+          S.formGroup(4)(
  255
+            SHtml.hidden(() =>
  256
+            {val res = nextId._2();
  257
+              if (!ajax_?) {
  258
+                val localSnapshot = createSnapshot
  259
+                S.seeOther(url, () => {
  260
+                  localSnapshot.restore
  261
+                })}
  262
+              res
  263
+            })) % liftScreenAttr("nextAction") }</form> %
  264
+          theScreen.additionalAttributes) ++
  265
+          prevId.toList.map{case (id, func) =>
  266
+            <form id={id} action={url} method="post">{
  267
+              SHtml.hidden(() => {snapshot.restore();
  268
+                val res = func();
  269
+                if (!ajax_?) {
  270
+                  val localSnapshot = createSnapshot;
  271
+                  S.seeOther(url, () => localSnapshot.restore)
  272
+                }
  273
+                res
  274
+              }) % liftScreenAttr("restoreAction")}</form>
  275
+          } ++
  276
+          <form id={cancelId._1} action={url} method="post">{SHtml.hidden(() => {
  277
+            snapshot.restore();
  278
+            val res = cancelId._2() // WizardRules.deregisterWizardSession(CurrentSession.is)
  279
+            if (!ajax_?) {
  280
+              S.seeOther(Referer.get)
  281
+            }
  282
+            res
  283
+          }) % liftScreenAttr("restoreAction")}</form>
  284
+
  285
+      if (ajax_?) {
  286
+        SHtml.makeFormsAjax(ret)
  287
+      } else {
  288
+        ret
  289
+      }
  290
+    }
  291
+
  292
+    def bindScreenInfo: CssBindFunc = (currentScreenNumber, screenCount) match {
  293
+      case (Full(num), Full(cnt)) =>
  294
+        replaceChildren(_.screenInfo) #> (nsSetChildren(_.screenNumber, num) & nsSetChildren(_.totalScreens, cnt))
  295
+      case _ => remove(_.screenInfo)
  296
+    }
  297
+
  298
+    logger.trace("Preparing to bind", fields)
  299
+
  300
+    val bindingFunc: CssBindFunc =
  301
+      bindScreenInfo &
  302
+      optSetChildren(_.wizardTop, wizardTop) &
  303
+      optSetChildren(_.screenTop, screenTop) &
  304
+      optSetChildren(_.wizardBottom, wizardBottom) &
  305
+      optSetChildren(_.screenBottom, screenBottom) &
  306
+      nsReplace(_.prev, prev openOr EntityRef("nbsp")) &
  307
+      nsReplace(_.next, ((next or finish) openOr EntityRef("nbsp"))) &
  308
+      nsReplace(_.cancel, cancel openOr EntityRef("nbsp")) &
  309
+      bindErrors &
  310
+      funcSetChildren(_.fields, bindForm _)
  311
+
  312
+    val processed = S.session map (_.runTemplate("css-bound-screen", allTemplate)) openOr (allTemplate)
  313
+
  314
+    (savAdditionalFormBindings map (bindingFunc & _) openOr (bindingFunc))(processed)
  315
+  }
  316
+
  317
+  override protected def allTemplateNodeSeq: NodeSeq = {
  318
+    <div>
  319
+      <div class="screenInfo">
  320
+        Page <span class="screenNumber"></span> of <span class="totalScreens"></span>
  321
+      </div>
  322
+      <div class="wizardTop"></div>
  323
+      <div class="screenTop"></div>
  324
+      <div class="globalErrors">
  325
+        <div class="error"></div>
  326
+      </div>
  327
+      <div class="fields">
  328
+        <table>
  329
+          <tr class="fieldContainer">
  330
+            <td>
  331
+              <label class="label field"></label>
  332
+              <span class="help"></span>
  333
+              <div class="errors">
  334
+                <div class="error"></div>
  335
+              </div>
  336
+            </td>
  337
+            <td><span class="value fieldValue"></span></td>
  338
+          </tr>
  339
+        </table>
  340
+      </div>
  341
+      <div>
  342
+        <table>
  343
+          <tr>
  344
+            <td><button class="prev"></button></td>
  345
+            <td><button class="cancel"></button></td>
  346
+            <td><button class="next"></button> </td>
  347
+          </tr>
  348
+        </table>
  349
+      </div>
  350
+      <div class="screenBottom"></div>
  351
+      <div class="wizardBottom"></div>
  352
+    </div>
  353
+  }
  354
+
  355
+  def defaultFieldNodeSeq: NodeSeq = NodeSeq.Empty
  356
+
  357
+  class CssClassBinding {
  358
+    def screenInfo = "screenInfo"
  359
+    def wizardTop = "wizardTop"
  360
+    def screenTop = "screenTop"
  361
+    def globalErrors = "globalErrors"
  362
+    def fields = "fields"
  363
+    def fieldContainer = "fieldContainer"
  364
+    def label = "label"
  365
+    def help = "help"
  366
+    def errors = "errors"
  367
+    def error = "error"
  368
+    def value = "value"
  369
+    def prev = "prev"
  370
+    def cancel = "cancel"
  371
+    def next = "next"
  372
+    def screenBottom = "screenBottom"
  373
+    def wizardBottom = "wizardBottom"
  374
+    def screenNumber = "screenNumber"
  375
+    def totalScreens = "totalScreens"
  376
+  }
  377
+}
192  web/webkit/src/main/scala/net/liftweb/http/LiftScreen.scala
@@ -169,6 +169,8 @@ trait AbstractScreen extends Factory {
169 169
      */
170 170
     def otherValue: OtherValueType = _otherValue.get
171 171
 
  172
+    def setOtherValue(v: OtherValueType) = _otherValue.set(v)
  173
+
172 174
     def default: ValueType
173 175
 
174 176
     def is = _currentValue.is
@@ -224,6 +226,10 @@ trait AbstractScreen extends Factory {
224 226
       vendAVar(Helpers.nextFuncName)
225 227
 
226 228
     override def toString = is.toString
  229
+
  230
+    def binding: Box[FieldBinding] = Empty
  231
+
  232
+    def transform: Box[() => CssSel] = Empty
227 233
   }
228 234
 
229 235
   protected object currentField extends ThreadGlobal[FieldIdentifier]
@@ -271,6 +277,22 @@ trait AbstractScreen extends Factory {
271 277
           case OnConfirmScreen => true
272 278
         }.headOption
273 279
 
  280
+      val newBinding: Box[FieldBinding] = (stuff.collect {
  281
+        case AFieldBinding(i) => i
  282
+      }).headOption
  283
+
  284
+      val newHelp: Box[NodeSeq] = help or (stuff.collect {
  285
+        case Help(ns) => ns
  286
+      }).headOption
  287
+
  288
+      val newTransform: Box[() => CssSel] = (stuff.collect {
  289
+        case FieldTransform(func) => func
  290
+      }).headOption
  291
+
  292
+      val newShow: Box[() => Boolean] = (stuff.collect {
  293
+        case DisplayIf(func) => func
  294
+      }).headOption
  295
+
274 296
       new Field {
275 297
         type ValueType = T
276 298
 
@@ -290,7 +312,7 @@ trait AbstractScreen extends Factory {
290 312
 
291 313
         override implicit def manifest: Manifest[ValueType] = FieldBuilder.this.manifest
292 314
 
293  
-        override def helpAsHtml = help
  315
+        override def helpAsHtml = newHelp
294 316
 
295 317
         override def validations = FieldBuilder.this.validations
296 318
 
@@ -299,8 +321,14 @@ trait AbstractScreen extends Factory {
299 321
         override def uniqueFieldId: Box[String] =
300 322
           paramFieldId or Full(_theFieldId.get)
301 323
 
  324
+        override def binding = newBinding
  325
+
302 326
         private lazy val _theFieldId: NonCleanAnyVar[String] =
303 327
           vendAVar(Helpers.nextFuncName)
  328
+
  329
+        override def transform = newTransform
  330
+
  331
+        override def show_? = newShow map (_()) openOr (super.show_?)
304 332
       }
305 333
     }
306 334
   }
@@ -345,6 +373,8 @@ trait AbstractScreen extends Factory {
345 373
     implicit def promoteToFormParam(a: SHtml.ElemAttr): FilterOrValidate[Nothing] = FormParam(a)
346 374
 
347 375
     implicit def promoteToFormParam(a: (String, String)): FilterOrValidate[Nothing] = FormParam(a)
  376
+
  377
+    implicit def promoteFieldBinding(binding: FieldBinding): FilterOrValidate[Nothing] = AFieldBinding(binding)
348 378
   }
349 379
 
350 380
   sealed protected trait FilterOrValidate[+T]
@@ -370,6 +400,14 @@ trait AbstractScreen extends Factory {
370 400
 
371 401
   protected final case class AVal[T](v: T => List[FieldError]) extends FilterOrValidate[T]
372 402
 
  403
+  protected final case class AFieldBinding(binding: FieldBinding) extends FilterOrValidate[Nothing]
  404
+
  405
+  protected final case class Help(ns: NodeSeq) extends FilterOrValidate[Nothing]
  406
+
  407
+  protected final case class FieldTransform(func: () => CssSel) extends FilterOrValidate[Nothing]
  408
+
  409
+  protected final case class DisplayIf(func: () => Boolean) extends FilterOrValidate[Nothing]
  410
+
373 411
   protected def field[T](underlying: => BaseField {type ValueType = T},
374 412
                          stuff: FilterOrValidate[T]*)(implicit man: Manifest[T]): Field {type ValueType = T} = {
375 413
     val paramFieldId: Box[String] = (stuff.collect {
@@ -383,6 +421,22 @@ trait AbstractScreen extends Factory {
383 421
         case OnConfirmScreen => true
384 422
       }.headOption
385 423
 
  424
+    val newBinding: Box[FieldBinding] = (stuff.collect {
  425
+      case AFieldBinding(i) => i
  426
+    }).headOption
  427
+
  428
+    val newHelp: Box[NodeSeq] = (stuff.collect {
  429
+      case Help(ns) => ns
  430
+    }).headOption
  431
+
  432
+    val newTransform: Box[() => CssSel] = (stuff.collect {
  433
+      case FieldTransform(func) => func
  434
+    }).headOption
  435
+
  436
+    val newShow: Box[() => Boolean] = (stuff.collect {
  437
+      case DisplayIf(func) => func
  438
+    }).headOption
  439
+
386 440
     new Field {
387 441
       type ValueType = T
388 442
 
@@ -396,7 +450,7 @@ trait AbstractScreen extends Factory {
396 450
       /**
397 451
        * Give the current state of things, should the this field be shown
398 452
        */
399  
-      override def show_? = underlying.show_?
  453
+      override def show_? = newShow map (_()) openOr underlying.show_?
400 454
 
401 455
       /**
402 456
        * What form elements are we going to add to this field?
@@ -420,7 +474,7 @@ trait AbstractScreen extends Factory {
420 474
 
421 475
       override implicit def manifest: Manifest[ValueType] = man
422 476
 
423  
-      override def helpAsHtml = underlying.helpAsHtml
  477
+      override def helpAsHtml = newHelp or underlying.helpAsHtml
424 478
 
425 479
       override def validate: List[FieldError] = underlying.validate
426 480
 
@@ -441,6 +495,10 @@ trait AbstractScreen extends Factory {
441 495
       override def set(v: T) = underlying.set(setFilter.foldLeft(v)((v, f) => f(v)))
442 496
 
443 497
       override def uniqueFieldId: Box[String] = paramFieldId or underlying.uniqueFieldId or super.uniqueFieldId
  498
+
  499
+      override def binding = newBinding or super.binding
  500
+
  501
+      override def transform = newTransform or super.transform
444 502
     }
445 503
   }
446 504
 
@@ -458,6 +516,22 @@ trait AbstractScreen extends Factory {
458 516
       case FormFieldId(id) => id
459 517
     }).headOption
460 518
 
  519
+    val newBinding: Box[FieldBinding] = (stuff.collect {
  520
+      case AFieldBinding(i) => i
  521
+    }).headOption
  522
+
  523
+    val newHelp: Box[NodeSeq] = (stuff.collect {
  524
+      case Help(ns) => ns
  525
+    }).headOption
  526
+
  527
+    val newTransform: Box[() => CssSel] = (stuff.collect {
  528
+      case FieldTransform(func) => func
  529
+    }).headOption
  530
+
  531
+    val newShow: Box[() => Boolean] = (stuff.collect {
  532
+      case DisplayIf(func) => func
  533
+    }).headOption
  534
+
461 535
     val confirmInfo = stuff.collect {
462 536
       case NotOnConfirmScreen => false
463 537
     }.headOption orElse
@@ -478,7 +552,7 @@ trait AbstractScreen extends Factory {
478 552
       /**
479 553
        * Give the current state of things, should the this field be shown
480 554
        */
481  
-      override def show_? = underlying.map(_.show_?) openOr false
  555
+      override def show_? = newShow map (_()) openOr (underlying.map(_.show_?) openOr false)
482 556
 
483 557
       /**
484 558
        * What form elements are we going to add to this field?
@@ -502,7 +576,7 @@ trait AbstractScreen extends Factory {
502 576
 
503 577
       override implicit def manifest: Manifest[ValueType] = man
504 578
 
505  
-      override def helpAsHtml = underlying.flatMap(_.helpAsHtml)
  579
+      override def helpAsHtml = newHelp or underlying.flatMap(_.helpAsHtml)
506 580
 
507 581
       override def validate: List[FieldError] = underlying.toList.flatMap(_.validate)
508 582
 
@@ -517,6 +591,10 @@ trait AbstractScreen extends Factory {
517 591
       override def set(v: T) = underlying.open_!.set(setFilter.foldLeft(v)((v, f) => f(v)))
518 592
 
519 593
       override def uniqueFieldId: Box[String] = paramFieldId or underlying.flatMap(_.uniqueFieldId) or super.uniqueFieldId
  594
+
  595
+      override def binding = newBinding or super.binding
  596
+
  597
+      override def transform = newTransform or super.transform
520 598
     }
521 599
   }
522 600
 
@@ -636,6 +714,22 @@ trait AbstractScreen extends Factory {
636 714
                                  otherValue: OtherValueInitializer[OV],
637 715
                                  stuff: FilterOrValidate[T]*):
638 716
   Field {type ValueType = T; type OtherValueType = OV} = {
  717
+    val newBinding: Box[FieldBinding] = (stuff.collect {
  718
+      case AFieldBinding(i) => i
  719
+    }).headOption
  720
+
  721
+    val newHelp: Box[NodeSeq] = (stuff.collect {
  722
+      case Help(ns) => ns
  723
+    }).headOption
  724
+
  725
+    val newTransform: Box[() => CssSel] = (stuff.collect {
  726
+      case FieldTransform(func) => func
  727
+    }).headOption
  728
+
  729
+    val newShow: Box[() => Boolean] = (stuff.collect {
  730
+      case DisplayIf(func) => func
  731
+    }).headOption
  732
+
639 733
     otherValue match {
640 734
       case OtherValueInitializerImpl(otherValueInitFunc) => {
641 735
         new Field {
@@ -664,7 +758,15 @@ trait AbstractScreen extends Factory {
664 758
             case _ => Nil
665 759
           }.toList
666 760
 
  761
+          override def binding = newBinding
  762
+
  763
+          override def helpAsHtml = newHelp
  764
+
667 765
           override def toForm: Box[NodeSeq] = theToForm(this)
  766
+
  767
+          override def transform = newTransform
  768
+
  769
+          override def show_? = newShow map (_()) openOr (super.show_?)
668 770
         }
669 771
       }
670 772
 
@@ -693,7 +795,15 @@ trait AbstractScreen extends Factory {
693 795
             case _ => Nil
694 796
           }.toList
695 797
 
  798
+          override def binding = newBinding
  799
+
  800
+          override def helpAsHtml = newHelp
  801
+
696 802
           override def toForm: Box[NodeSeq] = theToForm(this)
  803
+
  804
+          override def transform = newTransform
  805
+
  806
+          override def show_? = newShow map (_()) openOr (super.show_?)
697 807
         }
698 808
       }
699 809
     }
@@ -870,6 +980,7 @@ trait AbstractScreen extends Factory {
870 980
       OtherValueInitializerImpl[Seq[String]](() => choices),
871 981
       stuff: _*)
872 982
   }
  983
+
873 984
 }
874 985
 
875 986
 
@@ -1205,7 +1316,21 @@ trait ScreenWizardRendered {
1205 1316
 }
1206 1317
 
1207 1318
 
1208  
-case class ScreenFieldInfo(field: FieldIdentifier, text: NodeSeq, help: Box[NodeSeq], input: Box[NodeSeq])
  1319
+case class ScreenFieldInfo(
  1320
+    field: FieldIdentifier,
  1321
+    text: NodeSeq,
  1322
+    help: Box[NodeSeq],
  1323
+    input: Box[NodeSeq],
  1324
+    binding: Box[FieldBinding],
  1325
+    transform: Box[() => CssSel]) {
  1326
+  def this(field: FieldIdentifier, text: NodeSeq, help: Box[NodeSeq], input: Box[NodeSeq]) =
  1327
+    this(field, text, help, input, Empty, Empty)
  1328
+ }
  1329
+
  1330
+object ScreenFieldInfo {
  1331
+  def apply(field: FieldIdentifier, text: NodeSeq, help: Box[NodeSeq], input: Box[NodeSeq]) =
  1332
+    new ScreenFieldInfo(field, text, help, input)
  1333
+}
1209 1334
 
1210 1335
 trait LiftScreen extends AbstractScreen with StatefulSnippet with ScreenWizardRendered {
1211 1336
   def dispatch = {
@@ -1237,7 +1362,7 @@ trait LiftScreen extends AbstractScreen with StatefulSnippet with ScreenWizardRe
1237 1362
     override lazy val __nameSalt = Helpers.nextFuncName
1238 1363
   }
1239 1364
 
1240  
-  private object PrevSnapshot extends TransientRequestVar[Box[ScreenSnapshot]](Empty) {
  1365
+  protected object PrevSnapshot extends TransientRequestVar[Box[ScreenSnapshot]](Empty) {
1241 1366
     override lazy val __nameSalt = Helpers.nextFuncName
1242 1367
   }
1243 1368
 
@@ -1257,6 +1382,10 @@ trait LiftScreen extends AbstractScreen with StatefulSnippet with ScreenWizardRe
1257 1382
     override lazy val __nameSalt = Helpers.nextFuncName
1258 1383
   }
1259 1384
 
  1385
+  object NextId extends ScreenVar[String](Helpers.nextFuncName) {
  1386
+    override lazy val __nameSalt = Helpers.nextFuncName
  1387
+  }
  1388
+
1260 1389
   /**
1261 1390
    * What to do when the Screen is done.  By default, will
1262 1391
    * do a redirect back to Whence, but you can change this behavior,
@@ -1344,6 +1473,7 @@ trait LiftScreen extends AbstractScreen with StatefulSnippet with ScreenWizardRe
1344 1473
     Referer.get // touch to capture the referer
1345 1474
     Ajax_?.get // capture the ajaxiness of these forms
1346 1475
     FormGUID.get
  1476
+    NextId.get
1347 1477
 
1348 1478
     if (FirstTime) {
1349 1479
       FirstTime.set(false)
@@ -1366,7 +1496,7 @@ trait LiftScreen extends AbstractScreen with StatefulSnippet with ScreenWizardRe
1366 1496
   }
1367 1497
 
1368 1498
   protected def renderHtml(): NodeSeq = {
1369  
-    val finishId = Helpers.nextFuncName
  1499
+    val finishId = NextId.get
1370 1500
     val cancelId = Helpers.nextFuncName
1371 1501
 
1372 1502
     val theScreen = this
@@ -1389,13 +1519,25 @@ trait LiftScreen extends AbstractScreen with StatefulSnippet with ScreenWizardRe
1389 1519
 
1390 1520
     val url = S.uri
1391 1521
 
  1522
+    def fieldBinding(field: BaseField): Box[FieldBinding] =
  1523
+      field match {
  1524
+        case f: Field => f.binding
  1525
+        case _ => Empty
  1526
+      }
  1527
+
  1528
+    def fieldTransform(field: BaseField): Box[() => CssSel] =
  1529
+      field match {
  1530
+        case f: Field => f.transform
  1531
+        case _ => Empty
  1532
+      }
  1533
+
1392 1534
     renderAll(
1393 1535
       Empty, //currentScreenNumber: Box[NodeSeq],
1394 1536
       Empty, //screenCount: Box[NodeSeq],
1395 1537
       Empty, // wizardTop: Box[Elem],
1396 1538
       theScreen.screenTop, //screenTop: Box[Elem],
1397 1539
       theScreen.screenFields.filter(_.shouldDisplay_?).flatMap(f =>
1398  
-        if (f.show_?) List(ScreenFieldInfo(f, f.displayHtml, f.helpAsHtml, f.toForm)) else Nil), //fields: List[ScreenFieldInfo],
  1540
+        if (f.show_?) List(ScreenFieldInfo(f, f.displayHtml, f.helpAsHtml, f.toForm, fieldBinding(f), fieldTransform(f))) else Nil), //fields: List[ScreenFieldInfo],
1399 1541
       Empty, // prev: Box[Elem],
1400 1542
       Full(cancelButton), // cancel: Box[Elem],
1401 1543
       Empty, // next: Box[Elem],
@@ -1500,3 +1642,35 @@ object LiftScreenRules extends Factory with FormVendor {
1500 1642
 
1501 1643
 }
1502 1644
 
  1645
+case class FieldBinding(val fieldName: String, val bindingStyle: FieldBinding.BindingStyle) {
  1646
+  def fieldId(formName: String) = "%s_%s_field" format (formName, fieldName)
  1647
+  def selector(formName: String) = "#%s" format (fieldId(formName))
  1648
+  def childSelector(formName: String) = "#%s *" format (fieldId(formName))
  1649
+  def idSelector(formName: String) = selector(formName) + " [id]"
  1650
+}
  1651
+
  1652
+object FieldBinding {
  1653
+  sealed abstract class BindingStyle
  1654
+
  1655
+  /**
  1656
+   * Bind the field using the default template defined in an external template (as in Bind.helpers()-based binding)
  1657
+   */
  1658
+  case object Template extends BindingStyle
  1659
+
  1660
+  /**
  1661
+   * Bind the field using the template defined in the body of the field reference
  1662
+   */
  1663
+  case object Self extends BindingStyle
  1664
+
  1665
+  /**
  1666
+   * Bind the field using the template returned by the <code>defaultFieldNodeSeq</code> method
  1667
+   */
  1668
+  case object Default extends BindingStyle
  1669
+
  1670
+  /**
  1671
+   * Bind the field using the template provided
  1672
+   */
  1673
+  case class Custom(template: NodeSeq) extends BindingStyle
  1674
+
  1675
+  def apply(fieldName: String) = new FieldBinding(fieldName, Default)
  1676
+}

0 notes on commit 5d11748

Please sign in to comment.
Something went wrong with that request. Please try again.