Skip to content

Instantly share code, notes, and snippets.

@rjungemann
Created November 16, 2010 20:34
Show Gist options
  • Save rjungemann/702461 to your computer and use it in GitHub Desktop.
Save rjungemann/702461 to your computer and use it in GitHub Desktop.
pretty_print_xslt in Ruby
#!/usr/bin/env ruby
# usage:
#
# ruby extra_steps.rb SCRIPT ROOT_URL RELATIVE_URL
require 'nokogiri' rescue puts "Needs nokogiri. Type in `gem install nokogiri` to install."
require 'erb'
def pretty_print_xslt
%{
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="ISO-8859-1"/>
<xsl:param name="indent-increment" select="' '"/>
<xsl:template name="newline">
<xsl:text disable-output-escaping="yes">
</xsl:text>
</xsl:template>
<xsl:template match="comment() | processing-instruction()">
<xsl:param name="indent" select="''"/>
<xsl:call-template name="newline"/>
<xsl:value-of select="$indent"/>
<xsl:copy />
</xsl:template>
<xsl:template match="text()">
<xsl:param name="indent" select="''"/>
<xsl:call-template name="newline"/>
<xsl:value-of select="$indent"/>
<xsl:value-of select="normalize-space(.)"/>
</xsl:template>
<xsl:template match="text()[normalize-space(.)='']"/>
<xsl:template match="*">
<xsl:param name="indent" select="''"/>
<xsl:call-template name="newline"/>
<xsl:value-of select="$indent"/>
<xsl:choose>
<xsl:when test="count(child::*) > 0">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="*|text()">
<xsl:with-param name="indent" select="concat ($indent, $indent-increment)"/>
</xsl:apply-templates>
<xsl:call-template name="newline"/>
<xsl:value-of select="$indent"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
}
end
def default_original_script
%{
<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="http://www.spicefactory.org/parsley"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:log="http://www.spicefactory.org/parsley/flash/logging"
xsi:schemaLocation="http://www.spicefactory.org/parsley
http://www.spicefactory.org/parsley/schema/2.0/parsley-core.xsd
http://www.spicefactory.org/parsley/flash/logging
http://www.spicefactory.org/parsley/schema/2.0/parsley-logging-flash.xsd"
>
<!--
This xml file defines the structure and flow of the module, including dialogue text,
audio, and animation.
-->
<object id="models" type="com.trustnode.assistant.models.MainModel">
<!--
DefaultOverlays can be used to place UI elements and other
animations that should start when the module begins.
-->
<property name="defaultOverlays">
<array>
<object type="com.trustnode.assistant.models.Overlay">
<property name="animationUrl" value="assets/animations/DEFAULT_BACKGROUND.swf"/>
<property name="removeAfterComplete" value="false"/>
<property name="removeAfterStopped" value="false"/>
<property name="layerName" value="backgrounds"/>
</object>
<object type="com.trustnode.assistant.models.Overlay">
<property name="animationUrl" value="assets/animations/ui_original.swf"/>
<property name="removeAfterComplete" value="false"/>
<property name="removeAfterStopped" value="false"/>
</object>
</array>
</property>
<property name="steps">
<array>
<!--
This first step plays an animation and a sound, then switches to a "continue" dialog.
It is the most common use case for the interactive assistent.
The dialog stays visible until the audio completes.
If a dialog has not audio it will just stay, then clicking on the text will advance.
Note:
- To attach audio or animations you need to define a Dialog or preDialog
as Steps don't have any visual representation.
-->
<object type="com.trustnode.assistant.models.Step">
<property name="id" value="1"/>
<property name="preDialogues">
<array>
<object type="com.trustnode.assistant.models.Dialogue">
<property name="animationUrl" value="assets/animations/harveyKeck.swf"/>
<property name="soundUrl" value="assets/audio/HarveyKeck.mp3"/>
<property name="lines">
<array>
<object type="com.trustnode.assistant.models.Line">
<property name="text">
<string>Hey, I am Harvey Keck.</string>
</property>
</object>
</array>
</property>
</object>
</array>
</property>
<property name="question">
<object type="com.trustnode.assistant.models.Question">
<property name="form">
<!--
RadioForm allows one or more selectable text entries to allow
the user to continue or branch.
Use x,y properties to position the form.
Use the autoPosition property to automatically stack multiple responses
(not really shown here as there is only one response).
-->
<object type="com.trustnode.assistant.controls.RadioForm">
<property name="autoPosition" value="true"/>
<property name="x" value="50"/>
<property name="y" value="0"/>
</object>
</property>
<property name="responses">
<array>
<object type="com.trustnode.assistant.models.Response">
<property name="text" value="Click Here too Continue. This should be multiline. Is it?"/>
<property name="next" value="2"/>
</object>
<object type="com.trustnode.assistant.models.Response">
<property name="text" value="Click Here too Continue. This should be multiline. Is it?"/>
<property name="next" value="2"/>
</object>
</array>
</property>
</object>
</property>
</object>
<!--
This second step shows how to switch animation scenes between dialogs.
Each animation is a root animation of a swf loaded from a local or remote url.
-->
<object type="com.trustnode.assistant.models.Step">
<property name="id" value="2"/>
<property name="preDialogues">
<array>
<object type="com.trustnode.assistant.models.Dialogue">
<property name="animationUrl" value="assets/animations/scene1.swf"/>
<property name="lines">
<array>
<object type="com.trustnode.assistant.models.Line">
<property name="text">
<string>Here's a dialogue that grabs an animation from an external swf.</string>
</property>
</object>
</array>
</property>
<!-- This dialogue causes a new set of steps to be loaded -->
<property name="commands">
<string>
[
[
"com.trustnode.assistant.controllers.MainController",
"loadStepsFromUrl",
"assets/xml/next_steps_context.xml"
]
]
</string>
</property>
</object>
<object type="com.trustnode.assistant.models.Dialogue">
<property name="animationUrl" value="assets/animations/scene2.swf"/>
<property name="lines">
<array>
<object type="com.trustnode.assistant.models.Line">
<property name="text">
<string>Here's another dialogue with a different animation.</string>
</property>
</object>
</array>
</property>
</object>
</array>
</property>
<property name="question">
<object type="com.trustnode.assistant.models.Question">
<property name="animationUrl" value="assets/animations/scene2.swf|blue"/>
<property name="lines">
<array>
<object type="com.trustnode.assistant.models.Line">
<property name="text">
<string>FINALLY! A question!</string>
</property>
</object>
</array>
</property>
<property name="form">
<!--
In this RadioForm the individual responses are positioned individually by
switching autoPosition off and giving "y" properties to the responses.
If autoPosition is set to "true" the responses will be stacked predictably.
You can still position them relatively. Note, that when autoPosition is on
any y-value supplied to a response will overwrite the default value specified
in config.xml. Hence, if the default value is 100 and you specify 20 for the,
say, second response, it might appear above the first.
Also, note that the question positions the form.
-->
<object type="com.trustnode.assistant.controls.RadioForm">
<property name="autoPosition" value="false"/>
</object>
</property>
<property name="responses">
<array>
<object type="com.trustnode.assistant.models.Response">
<property name="text" value="Start from Beginning"/>
<property name="y" value="5"/>
<property name="next" value="1"/>
</object>
<object type="com.trustnode.assistant.models.Response">
<property name="text" value="Go to the next step"/>
<property name="x" value="50"/>
<property name="y" value="50"/>
<property name="next" value="3"/>
</object>
</array>
</property>
</object>
</property>
</object>
<!--
This simple step uses the "next" property to jump to the next step.
Since there is no audio in this step, the dialog stays up until you click on the text.
If audio would be used it would advance to the questions once the audio completes.
We position the responses by adding x,y-values to the form. Also, we overwrite the
default y-value of the responses by setting them to "0" for each element.
Some more recommendations for positioning responses:
- If you want to position individual responses, we recommend to swith autoPositioning off
- You can set height of first element, it will influence all subsequent
- If you want to position the block of responses, position the form only
(note that the default response y-position is used)
-->
<object type="com.trustnode.assistant.models.Step">
<property name="id" value="3"/>
<property name="preDialogues">
<array>
<object type="com.trustnode.assistant.models.Dialogue">
<property name="animationUrl" value="assets/animations/scene3.swf"/>
<property name="lines">
<array>
<object type="com.trustnode.assistant.models.Line">
<property name="text">
<string>Click here to continue (we have no audio, so dialog stays).</string>
</property>
</object>
</array>
</property>
</object>
</array>
</property>
<property name="question">
<object type="com.trustnode.assistant.models.Question">
<property name="form">
<object type="com.trustnode.assistant.controls.RadioForm">
<property name="autoPosition" value="true"/>
<property name="x" value="50"/>
<property name="y" value="-20"/>
</object>
</property>
<property name="responses">
<array>
<object type="com.trustnode.assistant.models.Response">
<property name="text" value="Play again"/>
<property name="y" value="0"/>
<property name="next" value="3"/>
</object>
<object type="com.trustnode.assistant.models.Response">
<property name="text" value="Next step"/>
<property name="y" value="0"/>
<property name="next" value="4"/>
</object>
</array>
</property>
</object>
</property>
</object>
</array>
</property>
</object>
</objects>
}
end
def extra_steps_template
%{
<?xml version="1.0" encoding="utf-8"?>
<objects xmlns="http://www.spicefactory.org/parsley"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:log="http://www.spicefactory.org/parsley/flash/logging"
xsi:schemaLocation="http://www.spicefactory.org/parsley
http://www.spicefactory.org/parsley/schema/2.0/parsley-core.xsd
http://www.spicefactory.org/parsley/flash/logging
http://www.spicefactory.org/parsley/schema/2.0/parsley-logging-flash.xsd"
>
<object type="com.trustnode.assistant.models.ExtraSteps">
<property name="rootUrl"/>
<property name="steps"><array/></property>
</object>
</objects>
}
end
def join_path root_url, url
if(
url.index("http") != 0 &&
url.index("ftp") != 0 &&
url != "default" &&
url != "same" &&
url != "next" &&
url.index("goto-") != 0
)
File.join(root_url, url)
else
url
end
end
original_script = open(ARGV[0]).read rescue default_original_script
root_path = ARGV[0] || "../../LifeModule"
relative_path = ARGV[1] || ""
xslt = Nokogiri::XSLT(pretty_print_xslt)
n = Nokogiri::XML(original_script)
steps = n.search("object[type='com.trustnode.assistant.models.Step']")
elements_set = [
n.search("object[type='com.trustnode.assistant.models.Dialogue']"),
n.search("object[type='com.trustnode.assistant.models.Question']"),
n.search("object[type='com.trustnode.assistant.models.Response']"),
n.search("object[type='com.trustnode.assistant.models.Overlay']")
]
elements_set.each do |elements|
elements.each do |element|
animation_url = element.search("property[name=animationUrl]").first
next unless animation_url
path = animation_url["value"]
animation_url.attribute("value").value = join_path(relative_path, path)
end
end
n_template = Nokogiri::XML(extra_steps_template)
n_template_steps_array = n_template.search("property[name=steps] > array").first
steps.each do |step|
n_template_steps_array.add_child(step.dup)
end
root_url_element = n_template.search("property[name=rootUrl]").first
root_url_element.set_attribute("value", root_path)
puts xslt.apply_to(n_template)
#<?xml version="1.0" encoding="ISO-8859-1"?>
#
# <?xml version="1.0" encoding="utf-8"?>
# <objects xmlns="http://www.spicefactory.org/parsley" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:log="http://www.spicefactory.org/parsley/flash/logging" xsi:schemaLocation="http://www.spicefactory.org/parsley http://www.spicefactory.org/parsley/schema/2.0/parsley-core.xsd http://www.spicefactory.org/parsley/flash/logging http://www.spicefactory.org/parsley/schema/2.0/parsley-logging-flash.xsd">
# <object type="com.trustnode.assistant.models.ExtraSteps">
# <property name="rootUrl" value="../../LifeModule"/>
# <property name="steps">
# <array>
# <object type="com.trustnode.assistant.models.Step">
# <property name="id" value="1"/>
# <property name="preDialogues">
# <array>
# <object type="com.trustnode.assistant.models.Dialogue">
# <property name="animationUrl" value="../../LifeModule/assets/animations/harveyKeck.swf"/>
# <property name="soundUrl" value="assets/audio/HarveyKeck.mp3"/>
# <property name="lines">
# <array>
# <object type="com.trustnode.assistant.models.Line">
# <property name="text">
# <string>Hey, I am Harvey Keck.</string>
# </property>
# </object>
# </array>
# </property>
# </object>
# </array>
# </property>
# <property name="question">
# <object type="com.trustnode.assistant.models.Question">
# <property name="form">
# <object type="com.trustnode.assistant.controls.RadioForm">
# <property name="autoPosition" value="true"/>
# <property name="x" value="50"/>
# <property name="y" value="0"/>
# </object>
# </property>
# <property name="responses">
# <array>
# <object type="com.trustnode.assistant.models.Response">
# <property name="text" value="Click Here too Continue. This should be multiline. Is it?"/>
# <property name="next" value="2"/>
# </object>
# <object type="com.trustnode.assistant.models.Response">
# <property name="text" value="Click Here too Continue. This should be multiline. Is it?"/>
# <property name="next" value="2"/>
# </object>
# </array>
# </property>
# </object>
# </property>
# </object>
# <object type="com.trustnode.assistant.models.Step">
# <property name="id" value="2"/>
# <property name="preDialogues">
# <array>
# <object type="com.trustnode.assistant.models.Dialogue">
# <property name="animationUrl" value="../../LifeModule/assets/animations/scene1.swf"/>
# <property name="lines">
# <array>
# <object type="com.trustnode.assistant.models.Line">
# <property name="text">
# <string>Here's a dialogue that grabs an animation from an external swf.</string>
# </property>
# </object>
# </array>
# </property>
# <property name="commands">
# <string>
# [
# [
# "com.trustnode.assistant.controllers.MainController",
# "loadStepsFromUrl",
# "assets/xml/next_steps_context.xml"
# ]
# ]
# </string>
# </property>
# </object>
# <object type="com.trustnode.assistant.models.Dialogue">
# <property name="animationUrl" value="../../LifeModule/assets/animations/scene2.swf"/>
# <property name="lines">
# <array>
# <object type="com.trustnode.assistant.models.Line">
# <property name="text">
# <string>Here's another dialogue with a different animation.</string>
# </property>
# </object>
# </array>
# </property>
# </object>
# </array>
# </property>
# <property name="question">
# <object type="com.trustnode.assistant.models.Question">
# <property name="animationUrl" value="../../LifeModule/assets/animations/scene2.swf|blue"/>
# <property name="lines">
# <array>
# <object type="com.trustnode.assistant.models.Line">
# <property name="text">
# <string>FINALLY! A question!</string>
# </property>
# </object>
# </array>
# </property>
# <property name="form">
# <object type="com.trustnode.assistant.controls.RadioForm">
# <property name="autoPosition" value="false"/>
# </object>
# </property>
# <property name="responses">
# <array>
# <object type="com.trustnode.assistant.models.Response">
# <property name="text" value="Start from Beginning"/>
# <property name="y" value="5"/>
# <property name="next" value="1"/>
# </object>
# <object type="com.trustnode.assistant.models.Response">
# <property name="text" value="Go to the next step"/>
# <property name="x" value="50"/>
# <property name="y" value="50"/>
# <property name="next" value="3"/>
# </object>
# </array>
# </property>
# </object>
# </property>
# </object>
# <object type="com.trustnode.assistant.models.Step">
# <property name="id" value="3"/>
# <property name="preDialogues">
# <array>
# <object type="com.trustnode.assistant.models.Dialogue">
# <property name="animationUrl" value="../../LifeModule/assets/animations/scene3.swf"/>
# <property name="lines">
# <array>
# <object type="com.trustnode.assistant.models.Line">
# <property name="text">
# <string>Click here to continue (we have no audio, so dialog stays).</string>
# </property>
# </object>
# </array>
# </property>
# </object>
# </array>
# </property>
# <property name="question">
# <object type="com.trustnode.assistant.models.Question">
# <property name="form">
# <object type="com.trustnode.assistant.controls.RadioForm">
# <property name="autoPosition" value="true"/>
# <property name="x" value="50"/>
# <property name="y" value="-20"/>
# </object>
# </property>
# <property name="responses">
# <array>
# <object type="com.trustnode.assistant.models.Response">
# <property name="text" value="Play again"/>
# <property name="y" value="0"/>
# <property name="next" value="3"/>
# </object>
# <object type="com.trustnode.assistant.models.Response">
# <property name="text" value="Next step"/>
# <property name="y" value="0"/>
# <property name="next" value="4"/>
# </object>
# </array>
# </property>
# </object>
# </property>
# </object>
# </array>
# </property>
# </object>
# </objects>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment