File Coverage

blib/lib/App/XUL.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package App::XUL;
2              
3 1     1   5154422 use 5.010000;
  1         4  
  1         41  
4 1     1   7 use strict;
  1         1  
  1         33  
5 1     1   29 use warnings;
  1         6  
  1         42  
6 1     1   1084 use Directory::Scratch::Structured qw(create_structured_tree);
  0            
  0            
7             use File::Copy::Recursive qw(fcopy dircopy);
8             #use Data::Dumper;
9             use Data::Dumper::Concise;
10             use App::XUL::XML;
11              
12             require Exporter;
13             our @ISA = qw(Exporter);
14             our @EXPORT = qw(AUTOLOAD);
15              
16             our $VERSION = '0.07';
17             our $AUTOLOAD;
18              
19             our $Singleton;
20              
21             ################################################################################
22              
23             sub AUTOLOAD
24             {
25             #print "auto1\n";
26             $App::XUL::XML::AUTOLOAD = $AUTOLOAD;
27             return App::XUL::XML::AUTOLOAD(@_);
28             }
29              
30             ################################################################################
31              
32             sub new
33             {
34               my ($class, @args) = @_;
35               my $self = bless {}, $class;
36               $Singleton = $self;
37               return $self->init(@args);
38             }
39              
40             sub init
41             {
42               my ($self, %opts) = @_;
43               $self->{'name'} = $opts{'name'} || do { die "Error: no app name given - new(name => <string>)\n" };
44             $self->{'windows'} = [];
45             $self->{'bindings'} = {};
46               return $self
47             }
48              
49             sub bind
50             {
51             my ($id, $event, $coderef) = @_;
52             $Singleton->{'bindings'}->{$id.':'.$event} = $coderef;
53             return $Singleton;
54             }
55              
56             sub add
57             {
58             my ($self, $window_xml) = @_;
59             die "Error: add() only accepts a single window tag as first argument\n"
60             if $window_xml !~ /^<window/;
61             push @{$self->{'windows'}}, $window_xml;
62             return $self;
63             }
64              
65             sub bundle
66             {
67               my ($self, %opts) = @_;
68               
69               my $os = $opts{'os'} || die "Error: no os given - bundle(os => <string>)\n";
70               my $path = $opts{'path'} || die "Error: no path given - bundle(path => <string>)\n";
71               my $utilspath = $opts{'utilspath'} || die "Error: no utils path given - bundle(utilspath => <string>)\n";
72               $self->{'debug'} = $opts{'debug'} || 0;
73               
74             #print Dumper($self);
75             #exit;
76            
77             ##############################################################################
78             if ($os eq 'chrome') {
79            
80             my $name = $self->{'name'};
81            
82                my $tmpdir = create_structured_tree(
83             $name => {
84             'start_macosx.pl' => [$self->_get_file_startpl('chrome','macosx')],
85             'start_win.pl' => [$self->_get_file_startpl('chrome','win')],
86             'start_linux.pl' => [$self->_get_file_startpl('chrome','linux')],
87             'chrome.manifest' => ['manifest chrome/chrome.manifest'."\n"],
88             'application.ini' => [$self->_get_file_macosx_appini()],
89             #'MyApp.icns' => [],
90             'chrome' => {
91             # for older XUL.framework's we need the chrome.manifest here!
92             'chrome.manifest' => [$self->_get_file_macosx_chromemanifest()],
93             'content' => {
94             #'AppXUL.js' => [],
95             #'AppXULServer.js' => [],
96             $self->_get_file_macosx_xulfiles(),
97             #'main.xul' => [$self->_get_file_macosx_mainxul()],
98             },
99             },
100             'defaults' => {
101             'preferences' => {
102             'prefs.js' => [$self->_get_file_macosx_prefs()],
103             },
104             },
105             'perl' => {
106             'server' => {
107             #'server.pl' => [$self->_get_file_macosx_serverpl()],
108             },
109             'modules' => {
110             'Eventhandlers.pm' => [$self->_get_file_macosx_eventhandlers()],
111             'App' => {
112             'XUL' => {
113             #'XML' => [],
114             #'Object' => [],
115             },
116             },
117             },
118             },
119             'extensions' => {},
120             'updates' => {
121             '0' => {},
122             },
123             }
124             );
125            
126             # copy misc files into tmpdir
127              
128             fcopy($utilspath.'/AppXUL.js', 
129             $tmpdir->base().'/'.$name.'/chrome/content/AppXUL.js');
130              
131             fcopy($utilspath.'/server.pl', 
132             $tmpdir->base().'/'.$name.'/perl/server/server.pl');
133              
134             fcopy($utilspath.'/../lib/App/XUL/XML.pm',
135             $tmpdir->base().'/'.$name.'/perl/modules/App/XUL/XML.pm');
136              
137             fcopy($utilspath.'/../lib/App/XUL/Object.pm', 
138             $tmpdir->base().'/'.$name.'/perl/modules/App/XUL/Object.pm');
139              
140             # chmod certain files
141             chmod(0755, $tmpdir->base().'/'.$name.'/start_macosx.pl');
142             chmod(0755, $tmpdir->base().'/'.$name.'/start_win.pl');
143             chmod(0755, $tmpdir->base().'/'.$name.'/start_linux.pl');
144              
145             # move tmpdir to final destination
146             rename($tmpdir->base().'/'.$name, $path);
147            
148             }
149             ##############################################################################
150             elsif ($os eq 'macosx') {
151              
152             my $name = $self->{'name'};
153              
154                my $tmpdir = create_structured_tree(
155             $name.'.app' => {
156             'Contents' => {
157             'Info.plist' => [$self->_get_file_maxosx_infoplist()],
158             'Frameworks' => {
159             #'XUL.framework' => {},
160             },
161             'MacOS' => {
162             'start.pl' => [$self->_get_file_startpl('macosx')],
163             },
164             'Resources' => {
165             'chrome.manifest' => ['manifest chrome/chrome.manifest'."\n"],
166             'application.ini' => [$self->_get_file_macosx_appini()],
167             #'MyApp.icns' => [],
168             'chrome' => {
169             # for older XUL.framework's we need the chrome.manifest here!
170             'chrome.manifest' => [$self->_get_file_macosx_chromemanifest()],
171             'content' => {
172             #'AppXUL.js' => [],
173             #'AppXULServer.js' => [],
174             $self->_get_file_macosx_xulfiles(),
175             #'main.xul' => [$self->_get_file_macosx_mainxul()],
176             },
177             },
178             'defaults' => {
179             'preferences' => {
180             'prefs.js' => [$self->_get_file_macosx_prefs()],
181             },
182             },
183             'perl' => {
184             'server' => {
185             #'server.pl' => [$self->_get_file_macosx_serverpl()],
186             },
187             'modules' => {
188             'Eventhandlers.pm' => [$self->_get_file_macosx_eventhandlers()],
189             'App' => {
190             'XUL' => {
191             #'XML' => [],
192             #'Object' => [],
193             },
194             },
195             },
196             },
197             'extensions' => {},
198             'updates' => {
199             '0' => {},
200             },
201             },
202             }
203             }
204             );
205            
206             # copy misc files into tmpdir
207             die "Error: no XUL.framework found in /Library/Frameworks - please install XUL framework from mozilla.org\n"
208             unless -d '/Library/Frameworks/XUL.framework';
209             dircopy('/Library/Frameworks/XUL.framework', 
210             $tmpdir->base().'/'.$name.'.app/Contents/Frameworks/XUL.framework');
211            
212             fcopy($utilspath.'/Appicon.icns',
213             $tmpdir->base().'/'.$name.'.app/Contents/Resources/'.$name.'.icns');
214              
215             fcopy($utilspath.'/AppXUL.js', 
216             $tmpdir->base().'/'.$name.'.app/Contents/Resources/chrome/content/AppXUL.js');
217              
218             #fcopy('../../misc/AppXULServer.js',
219             # $tmpdir->base().'/'.$name.'.app/Contents/Resources/chrome/content/AppXULServer.js');
220              
221             fcopy($utilspath.'/server.pl', 
222             $tmpdir->base().'/'.$name.'.app/Contents/Resources/perl/server/server.pl');
223              
224             fcopy($utilspath.'/../lib/App/XUL/XML.pm',
225             $tmpdir->base().'/'.$name.'.app/Contents/Resources/perl/modules/App/XUL/XML.pm');
226              
227             fcopy($utilspath.'/../lib/App/XUL/Object.pm', 
228             $tmpdir->base().'/'.$name.'.app/Contents/Resources/perl/modules/App/XUL/Object.pm');
229              
230             # chmod certain files
231             chmod(0755, $tmpdir->base().'/'.$name.'.app/Contents/MacOS/start.pl');
232              
233             # move tmpdir to final destination
234             rename($tmpdir->base().'/'.$name.'.app', $path);
235             }
236             else {
237             die "Error: os '$os' not implemented yet\n";
238             }
239             }
240              
241             ################################################################################
242              
243             sub _get_file_macosx_eventhandlers
244             {
245             my ($self) = @_;
246             my $eventhandlers = '';
247             foreach my $name (keys %{$self->{'bindings'}}) {
248             $eventhandlers .= "'".$name."' => \n".Dumper($self->{'bindings'}->{$name}).",\n";
249             }
250             return
251             'package Eventhandlers;'."\n".
252             'use App::XUL::XML;'."\n".
253             '$App::XUL::XML::RunInsideServer = 1;'."\n".
254             'our $AUTOLOAD;'."\n".
255             'sub AUTOLOAD {'."\n".
256             ' $App::XUL::XML::AUTOLOAD = $AUTOLOAD;'."\n".
257             ' return App::XUL::XML::AUTOLOAD(@_);'."\n".
258             '}'."\n".
259             'sub get {'."\n".
260             ' return {'."\n".
261             $eventhandlers.
262             ' };'."\n".
263             '}'."\n".
264             '1;'."\n";
265             }
266              
267             sub _get_file_macosx_prefs
268             {
269             my ($self) = @_;
270             return <<EOFSRC
271             pref("toolkit.defaultChromeURI", "chrome://$self->{'name'}/content/main.xul");
272            
273             /* debugging prefs */
274             pref("browser.dom.window.dump.enabled", true);
275             pref("javascript.options.showInConsole", true);
276             pref("javascript.options.strict", true);
277             pref("nglayout.debug.disable_xul_cache", true);
278             pref("nglayout.debug.disable_xul_fastload", true);
279             EOFSRC
280             }
281              
282             sub _get_file_macosx_xulfiles
283             {
284             my ($self) = @_;
285             my @files = ();
286             my $w = 0;
287             foreach my $window_xml (@{$self->{'windows'}}) {
288             my $xml =
289             '<?xml version="1.0"?>'."\n".
290             '<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>'."\n".
291             $window_xml;
292             push @files, ($w == 0 ? 'main' : 'sub'.$w).'.xul', [$xml];
293             $w++;
294             }
295             return @files;
296             # return
297             # '<?xml version="1.0"?>'."\n".
298             # '<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>'."\n".
299             # $self->{'xml'};
300             #<window id="mw" title="$self->{'name'}" width="800" height="200"
301             # xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
302             # xmlns:html="http://www.w3.org/1999/xhtml">
303             # <script src="AppXUL.js"/>
304             # ...
305             #</window>
306             }
307              
308             sub _get_file_macosx_chromemanifest
309             {
310             my ($self) = @_;
311             return 'content '.$self->{'name'}.' file:content/'."\n";
312             }
313              
314             sub _get_file_macosx_appini
315             {
316             my ($self) = @_;
317             return <<EOFSRC
318             [App]
319             Version=1.0
320             Vendor=Me
321             Name=$self->{'name'}
322             BuildID=myid
323             ID={generated id}
324            
325             [Gecko]
326             MinVersion=1.8
327             MaxVersion=2.*
328             EOFSRC
329             }
330              
331             sub _get_file_startpl
332             {
333             my ($self, $os, $type) = @_;
334            
335             if ($os eq 'chrome') {
336             my $xulrunnerpath = {
337             macosx => '/Library/Frameworks/XUL.framework/xulrunner-bin',
338             win    => 'c:\Program Files\xulrunner\xulrunner',
339             linux  => 'xulrunner',
340             };
341             return
342             '#!/usr/bin/perl -w'."\n".
343             q{use strict;
344             use Cwd 'abs_path';
345             my $path = abs_path($0);
346             $path =~ s/\/start\_[a-z]+.pl$//;
347             system(
348             '"'.$path."/perl/server/server.pl".'" '.
349             '"'.$path."/perl/modules/".'" 3000 &'
350             );
351             exec(
352             "}.$xulrunnerpath->{$type}.q{",
353             "-app", $path."/application.ini",}.
354             ($self->{'debug'} ? '"-jsconsole"' : '').
355             ');'."\n";
356             }
357             elsif ($os eq 'macosx') {
358             return 
359             '#!/usr/bin/perl -w'."\n".
360             q{use strict;
361             use Cwd 'abs_path';
362             my $path = abs_path($0);
363             $path =~ s/\/MacOS\/[^\/]+//;
364             system(
365             '"'.$path."/Resources/perl/server/server.pl".'" '.
366             '"'.$path."/Resources/perl/modules/".'" 3000 &'
367             );
368             exec(
369             $path."/Frameworks/XUL.framework/xulrunner-bin",
370             "-app", $path."/Resources/application.ini",}.
371             ($self->{'debug'} ? '"-jsconsole"' : '').
372             ');'."\n";
373             }
374             else {
375             return '';
376             }
377             }
378              
379             sub _get_file_maxosx_infoplist
380             {
381             my ($self) = @_;
382             return <<EOFSRC
383             <?xml version="1.0" encoding="UTF-8"?>
384             <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
385             <plist version="1.0">
386             <dict>
387             <key>CFBundleDevelopmentRegion</key>
388             <string>English</string>
389             <key>CFBundleExecutable</key>
390             <string>start.pl</string>
391             <key>CFBundleGetInfoString</key>
392             <string>XULExplorer 1.0a1pre, © 2007-2008 Contributors</string>
393             <key>CFBundleIconFile</key>
394             <string>$self->{'name'}</string>
395             <key>CFBundleIdentifier</key>
396             <string>org.mozilla.mccoy</string>
397             <key>CFBundleInfoDictionaryVersion</key>
398             <string>6.0</string>
399             <key>CFBundleName</key>
400             <string>$self->{'name'}</string>
401             <key>CFBundlePackageType</key>
402             <string>APPL</string>
403             <key>CFBundleShortVersionString</key>
404             <string>1.0a1pre</string>
405             <key>CFBundleSignature</key>
406             <string>MOZB</string>
407             <key>CFBundleVersion</key>
408             <string>1.0a1pre</string>
409             <key>NSAppleScriptEnabled</key>
410             <true/>
411             </dict>
412             </plist>
413             EOFSRC
414             }
415              
416             1;
417             __END__
418            
419             =head1 NAME
420            
421             App::XUL - Perl extension for creating deployable platform-independent
422             standalone desktop applications based on XUL and XULRunner.
423            
424             =for html <span style="color:red">WARNING: PRE-ALPHA - DON'T USE FOR PRODUCTION!</span>
425            
426             =head1 SYNOPSIS
427            
428             use App::XUL;
429             my $app = App::XUL->new(name => 'MyApp');
430            
431             $app->add(
432             Window(id => 'main',
433             Div(id => 'container', 'style' => 'background:black',
434             Button(label => 'click', oncommand => sub {
435             ID('container')->style('background:red');
436             }),
437             ),
438             ),
439             );
440            
441             $app->bundle(path => '/path/to/myapp.app', os => 'macosx');
442            
443             XUL (+ XULRunner) makes it easy to create applications based
444             on XML, CSS and JavaScript. App::XUL tries to simplify this
445             even more by exchanging XML and JavaScript with Perl and
446             providing an easy way to create deployable applications for the
447             platforms XULRunner exists for.
448            
449             =head1 WHY XUL/XULRUNNER
450            
451             XUL provides a set of powerful user widgets that look good
452             and work as expected. They can be created with minimal effort
453             and their appearance can be manipulated using CSS.
454            
455             XUL is based on B<web technologies like XML, CSS and JavaScript>.
456             So anyone who is able to use these is able to create cool
457             desktop applications.
458            
459             Here is the homepage of the L<XUL|https://developer.mozilla.org/En/XUL>
460             and L<XULRunner|https://developer.mozilla.org/en/xulrunner> projects at Mozilla.
461            
462             =head1 DESCRIPTION
463            
464             =head2 new() - Creating an application
465            
466             The constructor new() is used to create a new application
467            
468             my $app = App::XUL->new(name => 'MyApp');
469            
470             =head3 Options
471            
472             =head4 name => I<string>
473            
474             The name of the application. Later also used as the application executable's name.
475            
476             =head2 add() - Add a window to an application
477            
478             add() adds a window to the XUL application. The XML for the window tag
479             and its contained tags is created using Perl functions. The names of
480             the Perl functions used to create the XML tags corresponds to the
481             tagnames, just the first letter is uppercase:
482            
483             $app->add(
484             Window(id => 'main',
485             Div(id => 'container', 'style' => 'background:black',
486             Button(label => 'click', oncommand => sub {
487             ID('container')->style('background:red');
488             }),
489             );
490             )
491             );
492            
493             Keep in mind, that add will fail if the added tag is NOT a window tag.
494             In XUL the root is always a window tag.
495            
496             The first window beeing added is considered the main window and shown
497             on startup.
498            
499             =head2 bundle() - Creating a deployable executable
500            
501             $app->bundle(path => '/path/to/myapp.app', os => 'macosx');
502            
503             This will create a complete standalone XUL application containing all XML code.
504            
505             Some general information about
506             L<XUL application deployment|https://wiki.mozilla.org/XUL:XUL_Application_Packaging>.
507            
508             =head3 Options
509            
510             =head4 path => I<string>
511            
512             =head4 os => I<string>
513            
514             The systems currently supported are:
515            
516             =over 1
517            
518             =item chrome (native Chrome application)
519            
520             This creates the usual directory for a XULRunner based application containing
521             all needed files, except the XULRunner executable itself. A start Perl
522             script for various operation systems is created as well, e.g. you can start
523             the application by executing the start_macosx.pl file for Mac OS X.
524            
525             Note for Linux users: You have to add the directory containing the
526             xulrunner (or xulrunner-bin) executable to your $PATH variable or
527             adjust the start_linux.pl script accordingly. This is due to the fact
528             that XULRunner for Linux us currently in beta phase and can only
529             be used by unpacking it into a local directory.
530            
531             =item macosx (Mac OS X)
532            
533             L<Apple Documentation|http://developer.apple.com/library/mac/#documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW1>
534            
535             =item win (Windows)
536            
537             coming soon
538            
539             =item deb or rpm (Ubuntu Linux)
540            
541             tbd. Either a *.deb or *.rpm Paket.
542            
543             =back
544            
545             =head4 debug => I<1/0>
546            
547             If debug is set to 1, a jsconsole is started together with the application
548             which can be used to debugging messages. The default is debug = 0, so no
549             jsconsole is started.
550            
551             =head2 Creating interface components
552            
553             =head2 Handling events
554            
555             Events can be handled by attaching an event handler to
556             an interface component. Event handlers can either be written
557             in Perl or in JavaScript.
558            
559             Here is an example of a Perl event handler that reacts on
560             the mouse click of a button:
561            
562             Button(label => 'click', oncommand => sub {
563             # access environment and evtl. change it
564             # ...
565             });
566            
567             Here is a similar JavaScript event handler:
568            
569             Button(id => 'btn', label => 'click', oncommand => <<EOFJS);
570             // here is some js code
571             $('btn').update('Alrighty!');
572             EOFJS
573            
574             JavaScript event handlers are executed faster than the Perl ones,
575             due to the architecture (see below).
576            
577            
578             =head2 Changing the environment from Perl
579            
580             This refers to all activities within Perl event handlers that
581             change the DOM of the XUL application. An example is the
582             addition of another window, the insertion or update of a button
583             label or the deletion of a style attribute etc.
584            
585             Some things are important here:
586            
587             =over 1
588            
589             =item Changes happen on the server side first and are
590             transferred to the client side (the XUL application)
591             when the event handler terminates.
592            
593             =item To manually transfer the latest changes to the client side
594             use the PUSH() function.
595            
596             =back
597            
598             =head4 Get (XML) element
599            
600             The first step of changing the DOM is to get an element on which
601             the changes are applied. The ID() function is used for that:
602            
603             my $elem = ID('main');
604            
605             The ID() function only works WHILE the application is running.
606             Any changes to the object returned by the ID() function are transferred
607             immedietly (asynchronous) to the XUL application/client.
608            
609             =head4 Get child (XML) elements
610            
611             my $child1 = ID('main')->child(0);
612             my $numchildren = ID('main')->numchildren();
613            
614             =head4 Create/insert/append (XML) elements
615            
616             my $e = Div(id => 'container', 'style' => 'background:black',
617             Button(label => 'click'));
618             ID('main')->insert($e, 'end'); # end|start|...
619            
620             =head4 Edit (XML) element
621            
622             ID('container')->style('background:red')->content(Span('Hello!'));
623            
624             =head4 Delete/remove (XML) element
625            
626             my $e = ID('container')->remove();
627            
628             =head4 Call event handler on (XML) element
629            
630             ID('container')->click();
631            
632             =head4 Register event handler on (XML) element
633            
634             ID('container')->oncommand(sub {
635             # do stuff here
636             # ...
637             });
638            
639             =head4 Un-register event handler on (XML) element
640            
641             ID('container')->oncommand(undef);
642            
643            
644             =head2 EXPORT
645            
646             None by default.
647            
648             =head1 INTERNALS
649            
650             This chapter is meant for informational purposes. Sometimes it is nessessary
651             to know how things are implemented to decide, for example, if you should
652             use a Perl or a JavaScript event handler etc.
653            
654             App::XUL is client-server based. The client is the instance of
655             XULRunner running and the server is a pure Perl based webserver
656             that reacts on the events that are triggered by the XUL interface.
657            
658             =head3 Event handling
659            
660             Essentially all events are dispatched from XUL as Ajax calls to the
661             Perl webserver which handles the event, makes changes to the DOM etc.
662             The changes are then transferred back to the XUL app where they
663             are applied.
664            
665             Here is a rough workflow for event handling:
666            
667             =over 1
668            
669             =item 1. Client registers an event (e.g. "mouseover", "click", "idle" etc.)
670            
671             =item 2. Client sends message to server (incl. parameters and environment)
672            
673             =item 3. Server calls appropriate Perl event handler subroutine
674             (which may manipulate the environment)
675            
676             =item 4. Server sends the environment changes to the client as response
677            
678             =item 5. Client integrates environment changes
679            
680             =back
681            
682             =head3 Communication protocol
683            
684             The communication between XUL and server is based on a simple
685             JSON based protocol. The following syntax definition tries to
686             define the protocol. Everything in curly brackets is a JSON object,
687             strings are quoted and non-terminals are written within "<",">" brackets.
688             The pipe symbol ("|") means "OR".
689            
690             <CLIENT-REQUEST> := <EVENT>
691            
692             <SERVER-RESPONSE> := <ACTION>
693            
694             <SERVER-REQUEST> := <ACTION>
695            
696             <CLIENT-RESPONSE> := <STRING>
697            
698             <EVENT> := {
699             event: <EVENTNAME>,
700             id: <ID>
701             }
702            
703             <EVENTNAME> :=
704             "abort" |
705             "blur" |
706             "change" |
707             "click" |
708             "dblclick" |
709             "dragdrop" |
710             "error" |
711             "focus" |
712             "keydown" |
713             "keypress" |
714             "keyup" |
715             "load" |
716             "mousedown" |
717             "mousemove" |
718             "mouseout" |
719             "mouseover" |
720             "mouseup" |
721             "move" |
722             "reset" |
723             "resize" |
724             "select" |
725             "submit" |
726             "unload"
727            
728             <ACTION> :=
729             <UPDATE> |
730             <REMOVE> |
731             <CREATE> |
732             <QUIT> |
733             <CHILD> |
734             <NUMCHILDREN> |
735             <INSERT> |
736             <TRIGGER> |
737             <REGISTER> |
738             <UNREGISTER> |
739             <SETATTR> |
740             <GETATTR>
741            
742             <UPDATE> := {
743             action: "update",
744             id: <ID>,
745             attributes: <ATTRIBUTES>,
746             subactions: [ <ACTION>, ... ]
747             }
748            
749             <ATTRIBUTES> := {<NAME>: <STRING>, ...}
750            
751             <REMOVE> := {
752             action: "remove",
753             id: <ID>,
754             subactions: [ <ACTION>, ... ]
755             }
756            
757             <CREATE> := {
758             action: "create",
759             parent: <ID>,
760             attributes: <ATTRIBUTES>,
761             content: <STRING>,
762             subactions: [ <ACTION>, ... ]
763             }
764            
765             <QUIT> := {
766             action: "quit"
767             }
768            
769             <CHILD> := {
770             action: "child",
771             id: <ID>,
772             number: <NUMBER>
773             }
774            
775             <NUMCHILDREN> := {
776             action: "numchildren",
777             id: <ID>
778             }
779            
780             <INSERT> := {
781             action: "insert",
782             id: <ID>,
783             position: <POSITION>,
784             content: <STRING>
785             }
786            
787             <TRIGGER> := {
788             action: "trigger",
789             id: <ID>,
790             name: <STRING>
791             }
792            
793             <REGISTER> := {
794             action: "register",
795             id: <ID>,
796             name: <STRING>,
797             callback: <STRING>
798             }
799            
800             <UNREGISTER> := {
801             action: "unregister",
802             id: <ID>,
803             name: <STRING>
804             }
805            
806             <SETATTR> := {
807             action: "setattr",
808             id: <ID>,
809             name: <STRING>,
810             value: <STRING>
811             }
812            
813             <GETATTR> := {
814             action: "getattr",
815             id: <ID>,
816             name: <STRING>
817             }
818            
819             Here are some examples of client requests:
820            
821             {event:"click", id:"btn"}
822            
823             Here are some examples of server responses:
824            
825             {action:"update", id:"btn", attributes:{label:"Alrighty!"}}
826            
827             {action:"remove", id:"btn"}
828            
829             {action:"create", parent:"main", content:"<button .../>"}
830            
831             =head3 Application bundling for Mac OS X
832            
833             Mac applications are simply directories whose names end with ".app"
834             and have a certain structure and demand certain files to exist.
835            
836             This is the structure of a XUL application wrapped inside a Mac application
837             as created by App::XUL (files are blue, directories are black):
838            
839             =begin html
840            
841             <pre>
842             MyApp.app/
843             Contents/
844             <span style="color:blue;font-weight:bold">Info.plist</span>
845             Frameworks/
846             XUL.framework/
847             <i>The XUL Mac framework</i>
848             MacOS
849             <span style="color:blue;font-weight:bold">start.pl</span> (Perl-Script)
850             Resources
851             <span style="color:blue;font-weight:bold">application.ini</span>
852             <span style="color:blue;font-weight:bold">MyApp.icns</span>
853             chrome/
854             <span style="color:blue;font-weight:bold">chrome.manifest</span>
855             content/
856             <span style="color:blue;font-weight:bold">AppXUL.js</span>
857             <span style="color:blue;font-weight:bold">myapp.xul</span>
858             defaults/
859             preferences/
860             <span style="color:blue;font-weight:bold">prefs.js</span>
861             perl/
862             server/
863             <span style="color:blue;font-weight:bold">server.pl</span>
864             modules/
865             <i>All Perl modules the server depends on</i>
866             extensions/
867             updates/
868             0/
869             </pre>
870            
871             =end html
872            
873             The various files have specific functions. When the MyApp.app is
874             clicked, the B<start.pl> program is executed which then starts the
875             server and the client:
876            
877             =over 1
878            
879             =item Info.plist
880            
881             Required by Mac OS X. This is the place where certain basic information
882             about the application is read by Mac OS X, before anything else is done.
883             For example, here the start.pl program is defined as the entry point
884             of the application.
885            
886             =item start.pl
887            
888             First program to be executed. Starts server and client.
889            
890             =item application.ini
891            
892             Setups the XUL application. Defines which *.xul files to load,
893             name of application etc.
894            
895             =item AppXUL.js
896            
897             Defines all Javascript functions used by App::XUL to manage the
898             communication with the server.
899            
900             =item myapp.xul
901            
902             Defines the basic UI for the XUL application.
903            
904             =item prefs.js
905            
906             Sets some preferences for the XUL application.
907            
908             =item server.pl
909            
910             This starts the server.
911            
912             =back
913            
914             =head3 Application bundling for Windows
915            
916             tbd. Use L<NSIS|http://nsis.sourceforge.net/Main_Page> or
917             L<InstallJammer|http://www.installjammer.com/>.
918            
919             =head3 Application bundling as DEB package
920            
921             tbd. See L<Link|http://www.webupd8.org/2010/01/how-to-create-deb-package-ubuntu-debian.html>.
922            
923             =head3 Application bundling as RPM package
924            
925             tbd.
926            
927             =head1 ROADMAP
928            
929             One thing on the todo list is to create a full-duplex connection
930             between client and server so that the client can react on
931             server events directly. This may be implemented using the HTML5
932             WebSocket protocol. For now all communication is iniciated from
933             the client using AJAX calls.
934            
935             =head1 SEE ALSO
936            
937             This module actually stands a bit on its own with its approach.
938             XUL modules exist though - XUL::Gui, XUL::Node and a few more.
939            
940             =head1 AUTHOR
941            
942             Tom Kirchner, E<lt>tom@tomkirchner.comE<gt>
943            
944             =head1 COPYRIGHT AND LICENSE
945            
946             Copyright (C) 2011 by Tom Kirchner
947            
948             This library is free software; you can redistribute it and/or modify
949             it under the same terms as Perl itself, either Perl version 5.10.0 or,
950             at your option, any later version of Perl 5 you may have available.
951            
952            
953             =cut
954