The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

HTML::XHTML::DVSM - Dynamic Visual Software Modelling, XML/XHTML template system that does not screw up your templates. V1.2

SYNOPSIS

The HTML - getstarted.html

Illustrates all but one of the the DVSM commands that can be embedded into xml or xhtml markup, and the missing one is explained below. All markup must be xhtml not html.

    <html>
    <head>
    <title>Getting Started with HTML::XHTML::DVSM</title>
    </head>
    <body>
    <h1>Getting Started with HTML::XHTML::DVSM</h1>
    This html markup illustrated the script commands available in HTML::XHTML::DVSM<br/>
    run printheader has output the header<br/>
    <h2>RUN</h2>
    run dorun will output "doRun called 1 times"<Br/>
    <span id="sid">hello world</span><br/>
    run dorun will output "doRun called 2 times"<br/>
    <span id="sid2">hello world</span>
    <!--DVSM
    run "printHeader"
    run "doRun()" where id = "sid"
    run "doRun()" where id = "sid2"
    -->
    <!--DSUBS
    sub printHeader { print "Content-type: text/html\n\n"; }
    my $count = 0;
    sub doRun {
        ++$count;
        print "doRun called $count times<br/>\n";
    }
    -->
    <h2>SET</h2>
    textnode is set from hello world to hello sid<br/>
    <span id="set">hello world</span><br/>
    value of input is set to "set by set"<br/>
    <input type="text" value="" name="set2">
    <!--DVSM
    set textnode to "sayHello()" where id = "set"
    set value to "return 'set by set';" where name = "set2"
    -->
    <!--DSUBS
    sub sayHello {
        return "hello sid";
    }
    -->
    <h2>TOGGLE</h2>
    Current value set to Option 2 by TOGGLE command<br/>
    <select name="myselect">
    <option value="1" select="myselect">Option 1</option>
    <option value="2" select="myselect">Option 2</option>
    <option value="3" select="myselect">Otpion 3</option>
    </select><br/>
    <!--DVSM
    toggle selected to "doToggle()" where select = "myselect"
    -->
    <!--DSUBS
    my $selected = 2;
    sub doToggle {
        my $sb = getSB();
        my $value = $sb->sbGetCurrentTagValue( "value" );
        return ( $value eq $selected );
    }
    -->
    <h2>DELETE</h2>
    Option 2 and Option 3 removed by DELETE command<br/>
    <select name="myselect">
    <option value="1" select="myselect">Option 1</option>
    <option value="2" select="deleteme">Option 2</option>
    <option value="3" select="deleteme">Otpion 3</option>
    </select>
    <!--DVSM
    delete where select = "deleteme"
    -->
    <h2>WHILE</h2>
    WHILE command is used to populate the table with a data set of customers:
    148842 => "Mr J Smith", 848488 => "Ms S Jones", 484848 => "Mrs P Cook", 982828 => "Joe Bloggs"
    <table>
    <tr><th>Customer Number</th><th>Customer Name</th></tr>
    <tbody>
    <tr name="customers"><td name="custid">12345</td><td name="custname">Mr Bloggs</td></tr>
    <tr name="deleteme"><td>23456</td><td>Mrs Soap</td></tr>
    <tr name="deleteme"><td>67890</td><td>Mr A N Other</td></tr>
    </tbody>
    </table>
    <!--DVSM
    delete where name = "deleteme"
    while "moreCustomers()" where name = "customers"
       set textnode to "getCustid()" where name = "custid"
       set textnode to "getCustname()" where name = "custname"
    end while
    -->
    <!--DSUBS
    my %db = ( 148842 => "Mr J Smith", 848488 => "Ms S Jones", 484848 => "Mrs P Cook", 982828 => "Joe Bloggs" );
    my $cursor = -1;
    sub moreCustomers {
        $cursor++;
        my @keys = keys( %db );
        return ( $cursor < @keys );
    }
    sub getCustid {
        my @keys = sort keys( %db );
        return $keys[$cursor];
    }
    sub getCustname {
        return $db{getCustid()};
    }
    -->
    <h2>IF</h2>
    <p>This paragraph always happens</p>
    <p id="datepara_even">This paragraph only happens if the date of the month
    (<span id="thedate"></span>) is an even number</p>
    <p id="datepara_odd">This version of the paragraph only happens if the date of the month
    (<span id="thedate"></span>) is an odd number</p>
    <!--DVSM
    if "dateiseven()" where id = "datepara_even"
       set textnode to "getdate()" where id = "thedate"
    end if
    if "dateisodd()" where id = "datepara_odd"
       set textnode to "getdate()" where id="thedate"
    end if
    -->
    <!--DSUBS
    sub dateisodd { return ( ! dateiseven() ); }
    sub dateiseven {
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
            localtime(time);
        return ( $mday % 2 == 0 );
    }
    sub getdate {
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
            localtime(time);
        return sprintf( "%.2d/%.2d/%.4d", $mday, $mon + 1, 1900 + $year );
    }
    -->
    </body>
    </html>

The one DVSM script command not shown here is: <!--DVSM_include file/path.html--> which allows you to load a snippet of markup into the main document at the point of the include directive. The snippet can contain DVSM script and DSUBS perl sections.

The perl script - getstarted.pl

Shows how to use HTML::XHTML::DVSM to animate getstarted.html

    #!/usr/bin/perl -w
    package sb_getstarted;
    use HTML::XHTML::DVSM;
    my $scriptdir = "."; #may have to change this for mod_perl
    my $htmldir = "$scriptdir";
    our $sb; #for mod_perl
    sub getSB { return $sb; }
    $sb = HTML::XHTML::DVSM->new( SCRIPT_TAG => "DVSM", SUBS_TAG => "DSUBS" ) if ( ! $sb );
    $sb->sbInit();
    #$sb->sbClearCache(); #can be used if modify html and using mod_perl
    $sb->sbSetEvalPackage( "sb_getstarted" ); #when running code in getstarted.html this is the package name
    $sb->sbSetStopOnError( 1 );
    eval {
        $sb->sbInitPage( "", "$htmldir", "getstarted.html", "" );
        $sb->sbPrintDocument();
    };
    if ( $@ ) {
        print "Content-type: text/plain\n\n";
        print "ERROR: $@";
    }
Instructions

Put both getstarted.html and getstarted.pl in the same directory. cd to that directory.

Run:

    perl getstarted.pl
    

and view the output.

Once you have viewed the output try putting both files in your cgi-bin directory. Then run the demo by loading the getstarted.pl url in your browser: e.g. http://localhost/cgi-bin/getstarted.pl. Make sure getstarted.pl has execute permissions and that #!/usr/bin/perl is the correct path for perl - you may have to change this shell directive to give the correct path.

Notice that getstarted.html is good html that any browser can load. The html before transformation is not corrupted by the DVSM instructions, unlike almost all other html template systems.

CONSTRUCTOR

new([SCRIPT_TAG => "MyTag"],[SUBS_TAG => "MySubs"],[Stream => *STDOUT ] )

Creates new instance of HTML::XHTML::DVSM. When specified, the given SCRIPT_TAG sets the script marker in the markup to: <!--MyTag --> and the SUBS_TAG sets the perl subroutines section marker to <!--MySubs -->. The defaults are DVSM and DSUBS. Stream holds the file handle to print output to. It defaults to STDOUT.

METHODS

sbInit()

Initialises internal structures. This should be called for each request for a page.

sbClearCache([documentname])

Clears the cache of parsed markup. When running in mod_perl (ModPerl::Registry), HTML::XHTML::DVSM caches markup after it has been parsed, so it won't be necessary to parse it again. If documentname is specified, only that document will be cleared from the cache. The document name is the same value given to the first parameter of sbInitPage().

sbSetEvalPage(packagename)

Sets the package name for subroutines that are run from <!--DSUBS --> sections. Usually this is the same as the package of the main runner cgi (getstarted.pl in our example).

sbSetStopOnError( 1|0 )

If set to true (1) then HTML::XHTML::DVSM will die if it comes across an error while parsing or running DVSM script or DSUBS routines. If set to false (0) HTML::XHTML::DVSM will cache errors and continue parsing the markup and running DVSM script and DSUBS routines. The last error can then be obtained using sbGetLastError().

sbInitPage( pagename, htmldir, htmlfile, dvsm_dsubs_file )

Reads in the markup from htmlfile, parses the page, caches it, and then runs the DVSM script and DSUBS perl subroutines to animate the page. If the page has already been cached, the parsed markup is retrieved from the cache.

pagename a name to give the page. Used for cacheing the parsed markup. If "" is given, then the pagename will be the name of the html file. htmldir the filesystem directory where the html is stored. htmlfile the relative path of the file in htmldir. dvsm_dsubs_file the relative path from htmldir of a file holding DVSM and DSUBS sections. This is blank "" if the DVSM and DSUBS sections are in the xml/xhtml file.

sbPrintDocument()

Prints out the parsed and animated markup. Animated markup is markup after applying the instructions in DVSM sections and running the DSUBS sections. There are various debug forms of output: sbDebugPrint() and sbDebugDump(). These can be useful to try and catch bugs or problems with a page.

sbGetCurrentTagValue(attribute_name)

Returns the value of attribute_name of the current tag being processed. This is usually used by subroutines in DSUBS sections when they need to know the current value of an attribute of the currently processed tag. attribute_name can be two special values: 1) 'tagname' when the actual tag name being processed is returned (so for <span> that would be 'span', and for <table> it would be 'table'); 2) 'textnode' which returns the text between the opening and closing tag.

DVSM SCRIPT COMMANDS

The script is extremely simple. The script parser is not very sophisticated. At this stage it is only a proof of concept. Most script lines are attached to an html tag using a where clause.

    where attribute = "value"

Attribute can be a tag attribute such as name or id, or the special value "tagname" for the actual name of the tag (e.g. "span" for a <span> tag). When specifying the part of a tag to act on, such as with the set command, you can provide a tag attribute to set such as value or href, or the special value "textnode" to indicate the text between the opening and closing tag should be set. set attribute to "function()" where attribute = value

DVSM_include
    <!--DVSM_include file/path.html-->

Includes snippets of html with or without DVSM and DSUBS sections. In a web application this is used to include a footer.html in every page. File pathnames are relative to htmldir passed to sbInitPage().

run
    run "function()" [where attribute = "value"]

Unconditionally runs a subroutine. If a where clause is provided, all tags that satisfy the criteria have the run statement attached to them, and the function will be run when each tag is arrived at. If the where clause is not provided, the function is run on the very first tag.

Example:

    <html><body>    
    <span id="sid">hello world</span>
    </body></html>
    <!--DVSM
    run "doRun()"
    run "doRun()" where id = "sid"
    -->
    <!--DSUBS
    my $count = 0;
    sub doRun {
        ++$count;
        my $stream = getSB()->{Stream};
        print $stream "doRun called $count\n";
    }
    -->

Result:

    doRun called 1
    <html><body>    
    doRun called 2
    <span id="sid">hello world</span>
    </body></html>
set
    set attribute1 to "function()" where attribute2 = "value"

Sets a tag's attribute or its child textnode to the return value of function(). attribute1 can be a tag attribute or the special value textnode. attribute2 can be a tag attribute or the special value tagname.

Example:

    <html><body>    
    <span id="sid">hello world</span>
    </body></html>
    <!--DVSM
    set textnode to "sayHello()" where id = "sid"
    set id to "return 'sidney'" where id = "sid"
    -->
    <!--DSUBS
    sub sayHello {
        return "hello sid";
    }
    -->

Result:

    <html><body>    
    <span id="sidney">hello sid</span>
    </body></html>
toggle
    toggle attribute_1 to "boolean_function()" where attribute_2 = "value"

Toggles the existence of an attribute - such as checked or selected. The attribute does not exist if boolean_function() returns false. Otherwise it is created and to satisfy xhtml requirements it is set to a value of "true".

Example:

    <html><body>    
    <select name="myselect">
    <option value="1" select="myselect">Option 1</option>
    <option value="2" select="myselect">Option 2</option>
    <option value="3" select="myselect">Otpion 3</option>
    </select>
    </body></html>
    <!--DVSM
    toggle selected to "doToggle()" where select = "myselect"
    -->
    <!--DSUBS
    my $selected = 2;
    sub doToggle {
        my $sb = getSB();
        my $value = $sb->sbGetCurrentTagValue( "value" );
        return ( $value == $selected );
    }
    -->    

The getSB() function merely returns the HTML::XHTML::DVSM instance - we'll assume it is defined by the code that created the HTML::XHTML::DVSM instance. Notice we've had to create an attribute that all the options share (select="myselect"), so doToggle() is run against each one. The sbGetCurrentTagValue() sub is in HTML::XHTML::DVSM and is used to return attributes of the current tag.

Result:

    <html><body>    
    <select name="myselect">
    <option value="1" select="myselect">Option 1</option>
    <option value="2" selected="true" select="myselect">Option 2</option>
    <option value="3" select="myselect">Otpion 3</option>
    </select>
    </body></html>
delete
    delete where attribute = "value" 

Deletes tags where their xml/html attribute or tagname equals value.

Example:

    <html><body>    
    <select name="myselect">
    <option value="1" select="myselect">Option 1</option>
    <option value="2" select="deleteme">Option 2</option>
    <option value="3" select="deleteme">Otpion 3</option>
    </select>
    </body></html>
    <!--DVSM
    delete where select = "deleteme"
    -->

Result:

    <html><body>    
    <select name="myselect">
    <option value="1" select="myselect">Option 1</option>
    </select>
    </body></html>
if
    if "boolean_function()" where attribute = "value"
        [child instructions] 
    end if

Conditionally includes the tag where attribute or the tagname equals "value". If boolean_function() returns true then the tag is output and its children. If there are child instructions between if ... end if, they are executed against the child tags.

Example:

    <html><body>
    <p>This paragraph always happens</p>
    <p id="datepara_even">This paragraph only happens if the date of the month
    (<span id="thedate"></span>) is an even number</p>
    <p id="datepara_odd">This version of the paragraph only happens if the date of the month
    (<span id="thedate"></span>) is an odd number</p>
    </body></html>
    <!--DVSM
    if "dateiseven()" where id = "datepara_even"
       set textnode to "getdate()" where id = "thedate"
    end if
    if "dateisodd()" where id = "datepara_odd"
       set textnode to "getdate()" where id="thedate"
    end if
    -->
    <!--DSUBS
    sub dateisodd { return ( ! dateiseven() ); }
    sub dateiseven {
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
            localtime(time);
        return ( $mday % 2 == 0 );
    }
    sub getdate {
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
            localtime(time);
        return sprintf( "%.2d/%.2d/%.4d", $mday, $mon + 1, 1900 + $year );
    }
    -->

Result:

    <html><body>
    <p>This paragraph always happens</p>
    <p id="datepara_even">This paragraph only happens if the date of the month
    (<span id="thedate">12/01/2009</span>) is an even number</p>
    </body></html>
while
    while "boolean_function()" where attribute = "value"
       [child instructions...]
    end while 

Attaches itself to tags where attribute is equal to "value". It keeps repeating the tag while boolean_function() returns true. For each copy of the tag, it runs child instructions to the child tags.

Example:

    <html><body>
    <table>
    <tr><th>Customer Number</th><th>Customer Name</th></tr>
    <tbody>
    <tr name="customers"><td name="custid">12345</td><td name="custname">Mr Bloggs</td></tr>
    <tr name="deleteme"><td>23456</td><td>Mrs Soap</td></tr>
    <tr name="deleteme"><td>67890</td><td>Mr A N Other</td></tr>
    </tbody>
    </table>
    </body></html>
    <!--DVSM
    delete where name = "deleteme"
    while "moreCustomers()" where name = "customers"
       set textnode to "getCustid()" where name = "custid"
       set textnode to "getCustname()" where name = "custname"
    end while
    -->
    <!--DSUBS
    my %db = ( 148842 => "Mr J Smith", 848488 => "Ms S Jones", 484848 => "Mrs P Cook" );
    my $cursor = -1;
    sub moreCustomers {
        $cursor++;
        my @keys = keys( %db );
        return ( $cursor < @keys );
    }
    sub getCustid {
        my @keys = sort keys( %db );
        return $keys[$cursor];
    }
    sub getCustname {
        return $db{getCustid()};
    }
    --> 

Result:

    <html><body>
    <table>
    <tr><th>Customer Number</th><th>Customer Name</th></tr>
    <tbody>
    <tr name="customers"><td name="custid">148842</td><td name="custname">Mr J Smith</td></tr>
    <tr name="customers"><td name="custid">484848</td><td name="custname">Mrs P Cook</td></tr>
    <tr name="customers"><td name="custid">848488</td><td name="custname">Ms S Jones</td></tr>
    </tbody>
    </table>
    </body></html>

README

HTML::XHTML::DVSM A perl module that uses a simple scripting language embedded within XML/XHTML markup to change the markup by adding, removing and changing tags and attributes. The obvious application is for generating dynamic web sites. But other applications would be generating XUL gui screens (using XULs such as thinlet), or B2B XML documents.

The unique thing about DVSM is it does NOT corrupt your original XML/XHTML. After XHTML is developed to run dynamically in your website you can still load the template xhtml into your dreamweaver or other html tool and it will look exactly the same as it did before. You can do a storyboard of your whole website and the story board will be preserved even when it is used as a template for your dynamic, live website.

PREREQUISITES

Other than for the use of strict there are actually no dependancies in HTML::XHTML::DVSM. It uses simple regular expressions to parse script elements and xml/xhtml. In future should there be demand, it would probably be amended to allow the use of a proper XML parser and perhaps a robust mini-language compiler/interpreter. But the aim is to allow HTML::XHTML::DVSM to be used with the most primitive web hoster. If there is perl, HTML::XHTML::DVSM will run.

IO::String is required for the unit tests. It is not needed by HTML::XHTML::DVSM itself.

OSNAMES

any

SCRIPT CATEGORIES

AUTHORS

Copyright © 2007-2010 Stuart Butler (perldev@yahoo.co.uk) and Grant Holman (grant@collegeroad.eclipse.co.uk).

1 POD Error

The following errors were encountered while parsing the POD:

Around line 1498:

Non-ASCII character seen before =encoding in '©'. Assuming CP1252