Catalyst+Ajax: How to get a preview pane for a textarea which even supports live previewing of JavaScript
Published · Monday, 16 March 2009 (Updated · 8 June 2020)
Another shorty. Click on command lines to see results or further needed input/action.
This approach may seem a bit strange—and could certainly be refined to auto-size the iframe as well as smooth loading and apply an intent timer—but it’s the only way to get a preview pane to work with arbitrary rich content; i.e., JS and Flash. I originally wrote a more invovled version of this for a client to do live previews of rich media advertisements before saving.
Create your test application
jinx@jasper[467]~>catalyst.pl MyApp
created "MyApp" created "MyApp/script" created "MyApp/lib" created "MyApp/root" created "MyApp/root/static" created "MyApp/root/static/images" created "MyApp/t" created "MyApp/lib/MyApp" created "MyApp/lib/MyApp/Model" created "MyApp/lib/MyApp/View" created "MyApp/lib/MyApp/Controller" created "MyApp/myapp.conf" created "MyApp/lib/MyApp.pm" created "MyApp/lib/MyApp/Controller/Root.pm" created "MyApp/README" created "MyApp/Changes" created "MyApp/t/01app.t" created "MyApp/t/02pod.t" created "MyApp/t/03podcoverage.t" created "MyApp/root/static/images/catalyst_logo.png" created "MyApp/root/static/images/btn_120x50_built.png" created "MyApp/root/static/images/btn_120x50_built_shadow.png" created "MyApp/root/static/images/btn_120x50_powered.png" created "MyApp/root/static/images/btn_120x50_powered_shadow.png" created "MyApp/root/static/images/btn_88x31_built.png" created "MyApp/root/static/images/btn_88x31_built_shadow.png" created "MyApp/root/static/images/btn_88x31_powered.png" created "MyApp/root/static/images/btn_88x31_powered_shadow.png" created "MyApp/root/favicon.ico" created "MyApp/Makefile.PL" created "MyApp/script/myapp_cgi.pl" created "MyApp/script/myapp_fastcgi.pl" created "MyApp/script/myapp_server.pl" created "MyApp/script/myapp_test.pl" created "MyApp/script/myapp_create.pl"
jinx@jasper[468]~>cd MyApp/
Create a Template view
jinx@jasper[469]~/MyApp>./script/myapp_create.pl view TT TT
created "/Users/jinx/MyApp/script/../lib/MyApp/View" exists "/Users/jinx/MyApp/script/../t" created "/Users/jinx/MyApp/script/../lib/MyApp/View/TT.pm" exists "/Users/jinx/MyApp/script/../t/view_TT.t"
Make the index template
jinx@jasper[470]~/MyApp>emacs root/index.tt
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US"> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> <title>Ajax+Catalyst live preview</title> <style type="text/css" media="screen"> /* style stub for you */ </style> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js"> </script> </head> <body> <h1>Preview pane with Catalyst+Ajax</h1> <div style="text-align:center;margin: 5px auto;"> <iframe style="border:0; width:98%; padding: 0; margin: 0; height:110px;" class="preview" src="[% c.uri_for("/preview") %]" ></iframe> </div> [% form %] <h3>Here is some input to try-</h3> <pre> <script type="text/javascript" src="http://elektrum.org/js/greeking.js?pc=1;delim=blockquote;wc=17"> </script> </pre> <pre> <a href="http://somafm.com/"><img src="http://somafm.com/linktous/728x90sfm.jpg" style="border:0" alt="SomaFM independent internet radio"></a> </pre> <pre> <script type="text/javascript" src="http://sedition.com/js/blog.js?t=random"> </script> </pre> <script type="text/javascript"><!--//--><![CDATA[//><!-- jQuery(function($){ $("textarea[name='ad']") .bind("keyup", // "change" would be much lower impact. function(){ var xhtml = $(this).val(); $.ajax({ type: "POST" ,url: "[% c.uri_for("/preview") %]" ,data: { preview: xhtml } ,success: function(ok){ var fr = $("iframe.preview").clone(); // Amounts to a hard refresh by replacing node with its clone. $("iframe.preview").after(fr).remove(); } }); }); }); //--><!]]> </script> </body> </html>
Make its FormFu configuration file
jinx@jasper[471]~/MyApp>emacs root/forms/index.yml
--- auto_fieldset: legend: Preview your textarea elements: - type: Textarea name: ad label: Enter raw XHTML to preview above add_attributes: style: "width:100%; height: 8em; padding: 2px;"
Edit the application file proper to have cache
available.
jinx@jasper[472]~/MyApp>emacs lib/MyApp.pm
package MyApp; use strict; use warnings; use Catalyst::Runtime '5.70'; use parent qw/Catalyst/; use Catalyst qw( -Debug ConfigLoader Cache::FastMmap Static::Simple ); __PACKAGE__->setup(); 1;
Edit the Root controller to display the index and deliver the preview to the iframe
jinx@jasper[473]~/MyApp>emacs lib/MyApp/Controller/Root.pm
package MyApp::Controller::Root; use strict; use warnings; use parent 'Catalyst::Controller::HTML::FormFu'; use XML::LibXML; __PACKAGE__->config->{namespace} = ""; sub index :Path Args(0) FormConfig { my ( $self, $c ) = @_; $c->forward($c->view("TT")); } sub preview : Local Args(0) { my ( $self, $c ) = @_; my $parser = XML::LibXML->new(); if ( $c->request->method eq 'POST' ) { my $doc = eval { $parser->parse_html_string("<body>" . $c->request->param("preview") . "</body>") }; $c->cache->set("preview", $doc->serialize()) if $doc; $c->response->content_type("text/plain"); $c->response->body("OK"); } else { $c->response->content_type("text/html"); $c->response->body( $c->cache->get("preview") ); } } 1;
Start your app and start playing
jinx@jasper[474]~/MyApp>./script/myapp_server.pl -r
[debug] Debug messages enabled [debug] Statistics enabled [debug] Loaded plugins: .----------------------------------------------------------------------------. | Catalyst::Plugin::Cache::FastMmap 0.6 | | Catalyst::Plugin::ConfigLoader 0.22 | | Catalyst::Plugin::Static::Simple 0.20 | '----------------------------------------------------------------------------' [debug] Loaded dispatcher "Catalyst::Dispatcher" [debug] Loaded engine "Catalyst::Engine::HTTP::Restarter" [debug] Found home "/Users/jinx/MyApp" [debug] Loaded Config "/Users/jinx/MyApp/myapp.conf" [debug] Loaded components: .-----------------------------------------------------------------+----------. | Class | Type | +-----------------------------------------------------------------+----------+ | MyApp::Controller::Root | instance | | MyApp::View::TT | instance | '-----------------------------------------------------------------+----------' [debug] Loaded Private actions: .----------------------+--------------------------------------+--------------. | Private | Class | Method | +----------------------+--------------------------------------+--------------+ | /index | MyApp::Controller::Root | index | | /preview | MyApp::Controller::Root | preview | '----------------------+--------------------------------------+--------------' [debug] Loaded Path actions: .-------------------------------------+--------------------------------------. | Path | Private | +-------------------------------------+--------------------------------------+ | / | /index | | /preview | /preview | '-------------------------------------+--------------------------------------' [info] MyApp powered by Catalyst 5.71000 You can connect to your server at http://jasper.local:3000
See also: jQuery to make a preview pane of a textfield.






« The shortest Ajax+Catalyst tutorial in the world
« Catalyst related articles »