Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

xxf:script: fewer inline scripts by using hash of script bodies

Other small improvements along the way.
  • Loading branch information...
commit d1b026087807b00438500fd64647a3e01d61b370 1 parent 43d562b
@ebruchez ebruchez authored
View
8 src/java/org/orbeon/oxf/xforms/XFormsContainingDocument.java
@@ -772,22 +772,22 @@ public String getLevel() {
}
}
- public void addScriptToRun(String scriptId, XFormsEvent event, XFormsEventObserver eventObserver) {
+ public void addScriptToRun(org.orbeon.oxf.xforms.Script script, XFormsEvent event, XFormsEventObserver eventObserver) {
if (activeSubmissionFirstPass != null && StringUtils.isBlank(activeSubmissionFirstPass.getResolvedXXFormsTarget())) {
// Scripts occurring after a submission without a target takes place should not run
// TODO: Should we allow scripts anyway? Don't we allow value changes updates on the client anyway?
- indentedLogger.logWarning("", "xxforms:script will be ignored because two-pass submission started", "script id", scriptId);
+ indentedLogger.logWarning("", "xxforms:script will be ignored because two-pass submission started", "script id", script.prefixedId());
return;
}
// Warn that scripts won't run in noscript mode (duh)
if (staticState.isNoscript())
- indentedLogger.logWarning("noscript", "script won't run in noscript mode", "script id", scriptId);
+ indentedLogger.logWarning("noscript", "script won't run in noscript mode", "script id", script.prefixedId());
if (scriptsToRun == null)
scriptsToRun = new ArrayList<Script>();
- scriptsToRun.add(new Script(XFormsUtils.scriptIdToScriptName(scriptId), event, eventObserver));
+ scriptsToRun.add(new Script(script.clientName(), event, eventObserver));
}
public static class Script {
View
10 src/java/org/orbeon/oxf/xforms/XFormsUtils.java
@@ -870,16 +870,6 @@ public static boolean isXPath2Expression(Configuration configuration, String xpa
return true;
}
-
- /**
- * Create a JavaScript function name based on a script id.
- *
- * @param scriptId id of the script
- * @return JavaScript function name
- */
- public static String scriptIdToScriptName(String scriptId) {
- return scriptId.replace('-', '_').replace('$', '_') + "_xforms_function";
- }
private static String[] voidElementsNames = {
// HTML 5: http://www.w3.org/TR/html5/syntax.html#void-elements
View
4 src/java/org/orbeon/oxf/xforms/processor/handlers/xhtml/XHTMLHeadHandlerBase.java
@@ -120,11 +120,11 @@ protected abstract void outputJavaScriptResources(ContentHandlerHelper helper, S
private void outputScriptDeclarations(ContentHandlerHelper helper, String xhtmlPrefix, String focusElementId, List<XFormsContainingDocument.Message> messagesToRun, List<XXFormsDialogControl> dialogsToOpen) {
- if (containingDocument.getStaticOps().scripts().size() > 0 || focusElementId != null || messagesToRun != null || dialogsToOpen.size() > 0) {
+ if (containingDocument.getStaticOps().uniqueClientScripts().size() > 0 || focusElementId != null || messagesToRun != null || dialogsToOpen.size() > 0) {
helper.startElement(xhtmlPrefix, XMLConstants.XHTML_NAMESPACE_URI, "script", new String[] {
"type", "text/javascript"});
- XHTMLHeadHandler.outputScripts(helper, containingDocument.getStaticOps().scripts().values());
+ XHTMLHeadHandler.outputScripts(helper, containingDocument.getStaticOps().uniqueClientScripts());
final List<XFormsContainingDocument.Script> scriptsToRun = containingDocument.getScriptsToRun();
View
12 src/resources-packaged/ops/unit-tests/xforms-server/tests-xforms-components.xml
@@ -271,7 +271,7 @@
<xxf:attribute id="my-tabview$fr-tabview-select-2" for="my-tabview$fr-tabview-select-2" name="style">display: none</xxf:attribute>
<xxf:attribute id="my-tabview$fr-tabview-deselect-2" for="my-tabview$fr-tabview-deselect-2" name="style">display: none</xxf:attribute>
</xxf:control-values>
- <xxf:script name="my_tabview_xf_en_xforms_function" target-id="my-tabview$fr-tabview-group" observer-id="my-tabview$fr-tabview-group"/>
+ <xxf:script name="xf_fa3c6018b6aca63d2389f350f65e272530d03568" target-id="my-tabview$fr-tabview-group" observer-id="my-tabview$fr-tabview-group"/>
</xxf:action>
</xxf:event-response>
</output>
@@ -386,7 +386,7 @@
<xxf:attribute id="my-tabview$fr-tabview-select-7" for="my-tabview$fr-tabview-select-7" name="style">display: none</xxf:attribute>
<xxf:attribute id="my-tabview$fr-tabview-deselect-7" for="my-tabview$fr-tabview-deselect-7" name="style">display: none</xxf:attribute>
</xxf:control-values>
- <xxf:script name="my_tabview_xf_en_xforms_function" target-id="my-tabview$fr-tabview-group" observer-id="my-tabview$fr-tabview-group"/>
+ <xxf:script name="xf_fa3c6018b6aca63d2389f350f65e272530d03568" target-id="my-tabview$fr-tabview-group" observer-id="my-tabview$fr-tabview-group"/>
</xxf:action>
</xxf:event-response>
</output>
@@ -648,8 +648,8 @@
<xxf:attribute id="my-currency$xf-22" for="my-currency$xf-22" name="style">display: none</xxf:attribute>
<xxf:control id="my-currency$number-input" class="xbl-fr-number-xforms-input">$ 12.34</xxf:control>
</xxf:control-values>
- <xxf:script name="my_currency_xf_6_xforms_function" target-id="my-currency$xf-5" observer-id="my-currency$xf-5"/>
- <xxf:script name="my_currency_xf_sf_xforms_function" target-id="my-currency" observer-id="my-currency"/>
+ <xxf:script name="xf_bb240fc3ea943af5cb3019cdb5d43148021201ec" target-id="my-currency$xf-5" observer-id="my-currency$xf-5"/>
+ <xxf:script name="xf_ee48e71b3ea5811ae186431b61fae15a32a302b5" target-id="my-currency" observer-id="my-currency"/>
</xxf:action>
</xxf:event-response>
</output>
@@ -732,8 +732,8 @@
<xxf:control id="my-autocomplete$value-selected" class="fr-autocomplete-value-selected"/>
<xxf:attribute id="my-autocomplete$value-selected" for="my-autocomplete$value-selected" name="style">display: none</xxf:attribute>
</xxf:control-values>
- <xxf:script name="my_autocomplete_init_xforms_function" target-id="my-autocomplete$component-inner-group" observer-id="my-autocomplete$component-inner-group"/>
- <xxf:script name="my_autocomplete_show_suggestions_button_xf_88_xforms_function" target-id="my-autocomplete$show-suggestions-button$trigger" observer-id="my-autocomplete$show-suggestions-button$trigger"/>
+ <xxf:script name="xf_586bd855f1f823eb047ce1f64fc89bcd15e36a85" target-id="my-autocomplete$component-inner-group" observer-id="my-autocomplete$component-inner-group"/>
+ <xxf:script name="xf_8ba62560f51cbde692a8536df3e54bb1c2eba121" target-id="my-autocomplete$show-suggestions-button$trigger" observer-id="my-autocomplete$show-suggestions-button$trigger"/>
<xxf:setfocus control-id="my-autocomplete$search"/>
</xxf:action>
</xxf:event-response>
View
3  src/scala/org/orbeon/oxf/xforms/PartGlobalOps.scala
@@ -60,7 +60,8 @@ trait PartGlobalOps {
def getAttributeControl(prefixedForAttribute: String, attributeName: String): AttributeControl
// Client-side resources
- def scripts: collection.Map[String, Script]
+ def scripts: Map[String, Script]
+ def uniqueClientScripts: Seq[(String, String)]
def getXBLStyles: Seq[Element]
def getXBLScripts: Seq[Element]
def baselineResources: (collection.Set[String], collection.Set[String])
View
7 src/scala/org/orbeon/oxf/xforms/Script.scala
@@ -13,4 +13,9 @@
*/
package org.orbeon.oxf.xforms
-class Script(val prefixedId: String, val isClient: Boolean, val scriptType: String, val body: String)
+import org.orbeon.oxf.util.SecureUtils
+
+case class Script(prefixedId: String, isClient: Boolean, scriptType: String, body: String) {
+ val digest = SecureUtils.digestString(body, "SHA1", "hex")
+ val clientName = "xf_" + digest // digest must be JavaScript-safe (e.g. a hex string)
+}
View
3  src/scala/org/orbeon/oxf/xforms/StaticStateGlobalOps.scala
@@ -80,7 +80,8 @@ class StaticStateGlobalOps(topLevelPart: PartAnalysis) extends PartGlobalOps {
def getGlobals = collectInParts(_.getGlobals) toMap
- def scripts = parts flatMap (_.scripts) toMap
+ def scripts = collectInParts(_.scripts) toMap
+ def uniqueClientScripts = collectInParts(_.uniqueClientScripts)
def getXBLStyles = collectInParts(_.getXBLStyles)
def getXBLScripts = collectInParts(_.getXBLScripts)
View
22 src/scala/org/orbeon/oxf/xforms/action/actions/XXFormsScriptAction.scala
@@ -16,6 +16,7 @@ package org.orbeon.oxf.xforms.action.actions;
import org.orbeon.oxf.xforms._
import action.{DynamicActionContext, XFormsAction}
import org.orbeon.oxf.common.OXFException
+import script.ServerScript
/**
* Extension xxforms:script action.
@@ -29,23 +30,26 @@ class XXFormsScriptAction extends XFormsAction {
val mediatype = actionElement.attributeValue(XFormsConstants.TYPE_QNAME)
mediatype match {
- case "javascript" | "text/javascript" | "application/javascript" | null =>
+ case "javascript" | "text/javascript" | "application/javascript" | null
// Get prefixed id of the xxforms:script element based on its location
- val actionPrefixedId = actionInterpreter.getActionPrefixedId(actionElement)
+ val script = {
+ val partAnalysis = actionInterpreter.actionXPathContext.container.getPartAnalysis
+ partAnalysis.scripts(actionInterpreter.getActionPrefixedId(actionElement))
+ }
val containingDocument = actionInterpreter.containingDocument
// Run Script on server or client
- actionElement.attributeValue("runat") match {
- case "server" =>
- containingDocument.getScriptInterpreter.runScript(actionPrefixedId)
- case _ =>
- containingDocument.addScriptToRun(actionPrefixedId, actionContext.interpreter.event, actionContext.interpreter.eventObserver)
+ script match {
+ case serverScript: ServerScript
+ containingDocument.getScriptInterpreter.runScript(serverScript)
+ case clientScript
+ containingDocument.addScriptToRun(clientScript, actionContext.interpreter.event, actionContext.interpreter.eventObserver)
}
- case "xpath" | "text/xpath" | "application/xpath" => // "unofficial" type
+ case "xpath" | "text/xpath" | "application/xpath" // "unofficial" type
// Evaluate XPath expression for its side effects only
val bindingContext = actionInterpreter.actionXPathContext.getCurrentBindingContext
actionInterpreter.evaluateExpression(actionElement, bindingContext.getNodeset, bindingContext.getPosition, actionElement.getText)
- case other =>
+ case other
throw new OXFException("Unsupported script type: " + other)
}
}
View
4 src/scala/org/orbeon/oxf/xforms/analysis/PartControlsAnalysis.scala
@@ -32,10 +32,10 @@ trait PartControlsAnalysis extends TransientState {
private val controlAppearances = HashMap[String, HashSet[QName]]();
// Special handling of attributes
- private var attributeControls: Map[String, Map[String, AttributeControl]] = _
+ private[PartControlsAnalysis] var attributeControls: Map[String, Map[String, AttributeControl]] = _
// Special handling of input placeholder
- private var _hasInputPlaceholder = false
+ private[PartControlsAnalysis] var _hasInputPlaceholder = false
def hasInputPlaceholder = _hasInputPlaceholder
protected def indexNewControl(elementAnalysis: ElementAnalysis, externalLHHA: Buffer[ExternalLHHAAnalysis], eventHandlers: Buffer[EventHandlerImpl]) {
View
36 src/scala/org/orbeon/oxf/xforms/analysis/PartEventHandlerAnalysis.scala
@@ -15,9 +15,8 @@ package org.orbeon.oxf.xforms.analysis
import scala.collection.JavaConverters._
import org.orbeon.oxf.xforms.event.{XFormsEvents, EventHandlerImpl, EventHandler}
-import collection.mutable.LinkedHashMap
-import org.orbeon.oxf.xforms.{Script, XFormsConstants}
import org.orbeon.oxf.xforms.script.ServerScript
+import org.orbeon.oxf.xforms.{Script, XFormsConstants}
// Part analysis: event handlers information
trait PartEventHandlerAnalysis {
@@ -29,8 +28,10 @@ trait PartEventHandlerAnalysis {
private var keyHandlers: List[EventHandler] = _
// Scripts
- private var _scripts: LinkedHashMap[String, Script] = _
- def scripts = _scripts
+ private[PartEventHandlerAnalysis] var _scriptsByPrefixedId: Map[String, Script] = _
+ def scripts = _scriptsByPrefixedId
+ private[PartEventHandlerAnalysis] var _uniqueClientScripts: Seq[(String, String)] = _
+ def uniqueClientScripts = _uniqueClientScripts
// Register all event handlers
def registerEventHandlers(eventHandlers: Seq[EventHandlerImpl]) {
@@ -57,19 +58,30 @@ trait PartEventHandlerAnalysis {
// Gather all keypress handlers
keyHandlers = (eventHandlers filter (_.eventNames(XFormsEvents.KEYPRESS)) toList)
- // Gather all scripts
- def scriptMapping(eventHandlerImpl: ElementAnalysis) = {
+ // Gather all scripts in deterministic order
+ def makeScript(eventHandlerImpl: ElementAnalysis) = {
val element = eventHandlerImpl.element
val isClient = element.attributeValue("runat") != "server"
- val newScript = if (isClient) new Script(_, _, _, _) else new ServerScript(_, _, _, _)
- val script = newScript(eventHandlerImpl.prefixedId, isClient, element.attributeValue("type"), element.getStringValue)
- eventHandlerImpl.prefixedId script
+ val make = if (isClient) new Script(_, _, _, _) else new ServerScript(_, _, _, _)
+ make(eventHandlerImpl.prefixedId, isClient, element.attributeValue("type"), element.getStringValue)
+ }
+
+ val scriptMappings = {
+ val scriptHandlers = controlTypes.get("script").toSeq flatMap (_.values)
+ scriptHandlers map (makeScript(_))
}
- // Use LinkedHashMap as we want to output scripts in the HTML in a deterministic order.
- // Could use List + Map to avoid using a mutable collection?
- _scripts = LinkedHashMap(controlTypes.get("script").toSeq flatMap (_.values) map (scriptMapping(_)): _*)
+ // Index scripts by prefixed id
+ _scriptsByPrefixedId = scriptMappings map { case script script.prefixedId script } toMap
+
+ // Keep only one script body for a given digest
+ val distinctNames = scriptMappings collect
+ { case script if script.isClient script.clientName script.digest } distinct
+
+ val scriptBodiesByDigest = (scriptMappings map { case script script.digest script.body } toMap)
+
+ _uniqueClientScripts = distinctNames map { case (clientName, digest) clientName scriptBodiesByDigest(digest) } toSeq
}
def getEventHandlers(observerPrefixedId: String) =
View
6 src/scala/org/orbeon/oxf/xforms/analysis/PartModelAnalysis.scala
@@ -24,9 +24,9 @@ trait PartModelAnalysis extends TransientState {
this: PartAnalysisImpl =>
- private val modelsByScope = LinkedHashMap[Scope, Buffer[Model]]()
- private val modelsByPrefixedId = LinkedHashMap[String, Model]()
- private val modelByInstancePrefixedId = LinkedHashMap[String, Model]()
+ private[PartModelAnalysis] val modelsByScope = LinkedHashMap[Scope, Buffer[Model]]()
+ private[PartModelAnalysis] val modelsByPrefixedId = LinkedHashMap[String, Model]()
+ private[PartModelAnalysis] val modelByInstancePrefixedId = LinkedHashMap[String, Model]()
def getModel(prefixedId: String) =
modelsByPrefixedId.get(prefixedId).orNull
View
4 src/scala/org/orbeon/oxf/xforms/analysis/PartXBLAnalysis.scala
@@ -25,8 +25,8 @@ trait PartXBLAnalysis extends TransientState {
this: PartAnalysisImpl =>
val xblBindings = new XBLBindings(getIndentedLogger, this, metadata, staticStateDocument.xblElements)
- private val scopesById = HashMap[String, Scope]()
- private val prefixedIdToXBLScopeMap = HashMap[String, Scope]()
+ private[PartXBLAnalysis] val scopesById = HashMap[String, Scope]()
+ private[PartXBLAnalysis] val prefixedIdToXBLScopeMap = HashMap[String, Scope]()
protected def initializeScopes() {
// Add existing ids to scope map
View
57 src/scala/org/orbeon/oxf/xforms/processor/handlers/xhtml/XHTMLHeadHandler.scala
@@ -25,7 +25,7 @@ import org.xml.sax.helpers.AttributesImpl
import scala.collection.JavaConversions._
import scala.collection.JavaConverters._
-import java.util.{List => JList, Map => JMap}
+import java.util.{List JList, Map JMap}
import org.dom4j.Element
import collection.mutable.{Buffer, HashMap, LinkedHashSet}
import state.XFormsStateManager
@@ -37,9 +37,16 @@ import org.orbeon.oxf.util.URLRewriterUtils._
class XHTMLHeadHandler extends XHTMLHeadHandlerBase {
// Output an element
- private def outputElement(helper: ContentHandlerHelper, xhtmlPrefix: String, attributesImpl: AttributesImpl,
- getElementDetails: (Option[String], Option[String]) => (String, Array[String]))
- (resource: Option[String], cssClass: Option[String], content: Option[String]) {
+ private def outputElement(
+ helper: ContentHandlerHelper,
+ xhtmlPrefix: String,
+ attributesImpl: AttributesImpl,
+ getElementDetails: (Option[String], Option[String]) (String, Array[String])
+ )(
+ resource: Option[String],
+ cssClass: Option[String],
+ content: Option[String]
+ ) {
val (elementName, attributes) = getElementDetails(resource, cssClass)
@@ -52,35 +59,38 @@ class XHTMLHeadHandler extends XHTMLHeadHandlerBase {
}
// Output baseline, remaining, and inline resources
- private def outputResources(outputElement: (Option[String], Option[String], Option[String]) => Unit,
- getBuiltin: StaticStateGlobalOps => JList[XFormsFeatures.ResourceConfig],
- getXBL: => Seq[Element], xblBaseline: collection.Set[String], minimal: Boolean) {
+ private def outputResources(
+ outputElement: (Option[String], Option[String], Option[String]) Unit,
+ getBuiltin: StaticStateGlobalOps JList[XFormsFeatures.ResourceConfig],
+ getXBL: Seq[Element],
+ xblBaseline: collection.Set[String],
+ minimal: Boolean) {
// For now, actual builtin resources always include the baseline builtin resources
val builtinBaseline = LinkedHashSet(getBuiltin(null) map (_.getResourcePath(minimal)): _*)
val allBaseline = builtinBaseline ++ xblBaseline
// Output baseline resources with a CSS class
- allBaseline foreach (s => outputElement(Some(s), Some("xforms-baseline"), None))
+ allBaseline foreach (s outputElement(Some(s), Some("xforms-baseline"), None))
val builtinUsed = LinkedHashSet(getBuiltin(containingDocument.getStaticOps) map (_.getResourcePath(minimal)): _*)
val xblUsed = LinkedHashSet(XHTMLHeadHandler.xblResourcesToSeq(getXBL): _*)
// Output remaining resources if any, with no CSS class
- builtinUsed ++ xblUsed -- allBaseline foreach (s => outputElement(Some(s), None, None))
+ builtinUsed ++ xblUsed -- allBaseline foreach (s outputElement(Some(s), None, None))
// Output inline XBL resources
getXBL filter (_.attributeValue(XFormsConstants.SRC_QNAME) eq null) foreach
- { e => outputElement(None, None, Some(e.getText)) }
+ { e outputElement(None, None, Some(e.getText)) }
}
override def outputCSSResources(helper: ContentHandlerHelper, xhtmlPrefix: String, minimal: Boolean, attributesImpl: AttributesImpl) {
// Function to output either a <link> or <style> element
def outputCSSElement = outputElement(helper, xhtmlPrefix, attributesImpl,
- (resource, cssClass) => resource match {
- case Some(resource) => ("link", Array("rel", "stylesheet", "href", resource, "type", "text/css", "media", "all", "class", cssClass.orNull))
- case None => ("style", Array("type", "text/css", "media", "all", "class", cssClass.orNull))
+ (resource, cssClass) resource match {
+ case Some(resource) ("link", Array("rel", "stylesheet", "href", resource, "type", "text/css", "media", "all", "class", cssClass.orNull))
+ case None ("style", Array("type", "text/css", "media", "all", "class", cssClass.orNull))
}) _
// Output all CSS
@@ -94,7 +104,7 @@ class XHTMLHeadHandler extends XHTMLHeadHandlerBase {
// Function to output either a <script> element
def outputJSElement = outputElement(helper, xhtmlPrefix, attributesImpl,
- (resource, cssClass) => ("script", Array("type", "text/javascript", "src", resource.orNull, "class", cssClass.orNull))) _
+ (resource, cssClass) ("script", Array("type", "text/javascript", "src", resource.orNull, "class", cssClass.orNull))) _
// Output all JS
outputResources(outputJSElement, XFormsFeatures.getJavaScriptResources,
@@ -187,17 +197,14 @@ object XHTMLHeadHandler {
// All XBL resources use the @src attribute
def xblResourcesToSeq(elements: Iterable[Element]) =
- elements flatMap (e => Option(e.attributeValue(XFormsConstants.SRC_QNAME))) toSeq
-
- def outputScripts(helper: ContentHandlerHelper, scripts: Iterable[Script]) {
- for (script <- scripts) {
- if (script.isClient) {
- helper.text("\nfunction " + XFormsUtils.scriptIdToScriptName(script.prefixedId) + "(event) {\n")
- helper.text(script.body)
- helper.text("}\n")
- }
+ elements flatMap (e Option(e.attributeValue(XFormsConstants.SRC_QNAME))) toSeq
+
+ def outputScripts(helper: ContentHandlerHelper, scripts: Iterable[(String, String)]) =
+ for ((clientName, body) scripts) {
+ helper.text("\nfunction " + clientName + "(event) {\n")
+ helper.text(body)
+ helper.text("}\n")
}
- }
def gatherJavascriptControls(containingDocument: XFormsContainingDocument): JMap[String, JMap[String, JList[String]]] = {
@@ -212,7 +219,7 @@ object XHTMLHeadHandler {
// Don't run JavaScript initialization if the control is static readonly (could change in the
// future if some static readonly controls require JS initialization)
if (! control.isStaticReadonly)
- Option(control.getJavaScriptInitialization) foreach { init =>
+ Option(control.getJavaScriptInitialization) foreach { init
val appearanceToId = javaScriptControlsAppearancesMap.getOrElseUpdate(init._1, HashMap[String, Buffer[String]]())
val ids = appearanceToId.getOrElseUpdate(init._2, Buffer[String]())
View
2  src/scala/org/orbeon/oxf/xforms/processor/handlers/xhtml/XXFormsDynamicHandler.scala
@@ -48,7 +48,7 @@ class XXFormsDynamicHandler extends XFormsBaseHandler(false, false) {
if (!containingDocument.isInitializing && control.newScripts.nonEmpty) {
val helper = new ContentHandlerHelper(contentHandler)
helper.startElement(xhtmlPrefix, XMLConstants.XHTML_NAMESPACE_URI, "script", Array("type", "text/javascript"))
- XHTMLHeadHandler.outputScripts(helper, control.newScripts)
+ XHTMLHeadHandler.outputScripts(helper, control.newScripts map (script script.clientName script.body))
helper.endElement()
control.newScripts = Seq.empty
}
View
6 src/scala/org/orbeon/oxf/xforms/script/ScriptInterpreter.scala
@@ -14,8 +14,8 @@
package org.orbeon.oxf.xforms.script
import org.mozilla.javascript.Context
-import org.orbeon.oxf.xforms.XFormsContainingDocument
import org.orbeon.oxf.xforms.control.XFormsValueControl
+import org.orbeon.oxf.xforms.XFormsContainingDocument
class ScriptInterpreter(containingDocument: XFormsContainingDocument) {
@@ -47,9 +47,9 @@ class ScriptInterpreter(containingDocument: XFormsContainingDocument) {
}
}
- def runScript(scriptId: String): Unit = {
+ def runScript(script: ServerScript): Unit = {
// 1. Get compiled script
- val compiledScript = containingDocument.getStaticOps.scripts.get(scriptId).asInstanceOf[ServerScript].compiledScript
+ val compiledScript = script.compiledScript
// 2. Execute script
val result = {
Please sign in to comment.
Something went wrong with that request. Please try again.