App-XUL

 view release on metacpan or  search on metacpan

lib/App/XUL.pm  view on Meta::CPAN

  my ($self, %opts) = @_;
  $self->{'name'} = $opts{'name'} || do { die "Error: no app name given - new(name => <string>)\n" };
	$self->{'windows'} = [];
	$self->{'bindings'} = {};
  return $self
}

sub bind
{
	my ($id, $event, $coderef) = @_;
	$Singleton->{'bindings'}->{$id.':'.$event} = $coderef;
	return $Singleton;
}

sub add
{
	my ($self, $window_xml) = @_;
	die "Error: add() only accepts a single window tag as first argument\n"
		if $window_xml !~ /^<window/;
	push @{$self->{'windows'}}, $window_xml;
	return $self;
}

sub bundle
{
  my ($self, %opts) = @_;
  
  my $os = $opts{'os'} || die "Error: no os given - bundle(os => <string>)\n";
  my $path = $opts{'path'} || die "Error: no path given - bundle(path => <string>)\n";
  my $utilspath = $opts{'utilspath'} || die "Error: no utils path given - bundle(utilspath => <string>)\n";
  $self->{'debug'} = $opts{'debug'} || 0;
  
	#print Dumper($self);
	#exit;
	
	##############################################################################
	if ($os eq 'chrome') {
	
		my $name = $self->{'name'};
	
  	my $tmpdir = create_structured_tree(
			$name => {
				'start_macosx.pl' => [$self->_get_file_startpl('chrome','macosx')],
				'start_win.pl' => [$self->_get_file_startpl('chrome','win')],
				'start_linux.pl' => [$self->_get_file_startpl('chrome','linux')],
				'chrome.manifest' => ['manifest chrome/chrome.manifest'."\n"],
				'application.ini' => [$self->_get_file_macosx_appini()],
				#'MyApp.icns' => [],
				'chrome' => {
					# for older XUL.framework's we need the chrome.manifest here!
					'chrome.manifest' => [$self->_get_file_macosx_chromemanifest()],
					'content' => {
						#'AppXUL.js' => [],
						#'AppXULServer.js' => [],
						$self->_get_file_macosx_xulfiles(),
						#'main.xul' => [$self->_get_file_macosx_mainxul()],
					},
				},
				'defaults' => {
					'preferences' => {
						'prefs.js' => [$self->_get_file_macosx_prefs()],
					},
				},
				'perl' => {
					'server' => {
						#'server.pl' => [$self->_get_file_macosx_serverpl()],
					},
					'modules' => {
						'Eventhandlers.pm' => [$self->_get_file_macosx_eventhandlers()],
						'App' => {
							'XUL' => {
								#'XML' => [],
								#'Object' => [],
							},
						},
					},
				},
				'extensions' => {},
				'updates' => {
					'0' => {},
				},
			}
		);
		
		# copy misc files into tmpdir

		fcopy($utilspath.'/AppXUL.js', 
			$tmpdir->base().'/'.$name.'/chrome/content/AppXUL.js');

		fcopy($utilspath.'/server.pl', 
			$tmpdir->base().'/'.$name.'/perl/server/server.pl');

		fcopy($utilspath.'/../lib/App/XUL/XML.pm',
			$tmpdir->base().'/'.$name.'/perl/modules/App/XUL/XML.pm');

		fcopy($utilspath.'/../lib/App/XUL/Object.pm', 
			$tmpdir->base().'/'.$name.'/perl/modules/App/XUL/Object.pm');

		# chmod certain files
		chmod(0755, $tmpdir->base().'/'.$name.'/start_macosx.pl');
		chmod(0755, $tmpdir->base().'/'.$name.'/start_win.pl');
		chmod(0755, $tmpdir->base().'/'.$name.'/start_linux.pl');

		# move tmpdir to final destination		
		rename($tmpdir->base().'/'.$name, $path);
		
	}
	##############################################################################
	elsif ($os eq 'macosx') {

		my $name = $self->{'name'};

  	my $tmpdir = create_structured_tree(
			$name.'.app' => {
				'Contents' => {
					'Info.plist' => [$self->_get_file_maxosx_infoplist()],
					'Frameworks' => {
						#'XUL.framework' => {},
					},
					'MacOS' => {
						'start.pl' => [$self->_get_file_startpl('macosx')],
					},
					'Resources' => {
						'chrome.manifest' => ['manifest chrome/chrome.manifest'."\n"],
						'application.ini' => [$self->_get_file_macosx_appini()],
						#'MyApp.icns' => [],
						'chrome' => {
							# for older XUL.framework's we need the chrome.manifest here!
							'chrome.manifest' => [$self->_get_file_macosx_chromemanifest()],
						  'content' => {
							  #'AppXUL.js' => [],
							  #'AppXULServer.js' => [],
							  $self->_get_file_macosx_xulfiles(),
							  #'main.xul' => [$self->_get_file_macosx_mainxul()],
							},
						},
						'defaults' => {
							'preferences' => {
								'prefs.js' => [$self->_get_file_macosx_prefs()],
							},
						},
						'perl' => {
							'server' => {
								#'server.pl' => [$self->_get_file_macosx_serverpl()],
							},
							'modules' => {
								'Eventhandlers.pm' => [$self->_get_file_macosx_eventhandlers()],
								'App' => {
									'XUL' => {
										#'XML' => [],
										#'Object' => [],
									},
								},
							},
						},
						'extensions' => {},
						'updates' => {
							'0' => {},
						},
					},
				}
			}
		);
		
		# copy misc files into tmpdir
		die "Error: no XUL.framework found in /Library/Frameworks - please install XUL framework from mozilla.org\n"
			unless -d '/Library/Frameworks/XUL.framework';
		dircopy('/Library/Frameworks/XUL.framework', 
			$tmpdir->base().'/'.$name.'.app/Contents/Frameworks/XUL.framework');
			
		fcopy($utilspath.'/Appicon.icns',
			$tmpdir->base().'/'.$name.'.app/Contents/Resources/'.$name.'.icns');

		fcopy($utilspath.'/AppXUL.js', 
			$tmpdir->base().'/'.$name.'.app/Contents/Resources/chrome/content/AppXUL.js');

		#fcopy('../../misc/AppXULServer.js', 
		#	$tmpdir->base().'/'.$name.'.app/Contents/Resources/chrome/content/AppXULServer.js');

		fcopy($utilspath.'/server.pl', 
			$tmpdir->base().'/'.$name.'.app/Contents/Resources/perl/server/server.pl');

		fcopy($utilspath.'/../lib/App/XUL/XML.pm',
			$tmpdir->base().'/'.$name.'.app/Contents/Resources/perl/modules/App/XUL/XML.pm');

		fcopy($utilspath.'/../lib/App/XUL/Object.pm', 
			$tmpdir->base().'/'.$name.'.app/Contents/Resources/perl/modules/App/XUL/Object.pm');

		# chmod certain files
		chmod(0755, $tmpdir->base().'/'.$name.'.app/Contents/MacOS/start.pl');

		# move tmpdir to final destination		
		rename($tmpdir->base().'/'.$name.'.app', $path);
	}
	else {
		die "Error: os '$os' not implemented yet\n";
	}
}

################################################################################

sub _get_file_macosx_eventhandlers
{
	my ($self) = @_;
	my $eventhandlers = '';
	foreach my $name (keys %{$self->{'bindings'}}) {
		$eventhandlers .= "'".$name."' => \n".Dumper($self->{'bindings'}->{$name}).",\n";
	}
	return
		'package Eventhandlers;'."\n".
		'use App::XUL::XML;'."\n".
		'$App::XUL::XML::RunInsideServer = 1;'."\n".
		'our $AUTOLOAD;'."\n".
		'sub AUTOLOAD {'."\n".
		'	$App::XUL::XML::AUTOLOAD = $AUTOLOAD;'."\n".
		'	return App::XUL::XML::AUTOLOAD(@_);'."\n".
		'}'."\n".
		'sub get {'."\n".
		'	return {'."\n".
				$eventhandlers.		
		'	};'."\n".
		'}'."\n".
		'1;'."\n";
}

sub _get_file_macosx_prefs
{
	my ($self) = @_;
	return <<EOFSRC
pref("toolkit.defaultChromeURI", "chrome://$self->{'name'}/content/main.xul");

/* debugging prefs */
pref("browser.dom.window.dump.enabled", true);
pref("javascript.options.showInConsole", true);
pref("javascript.options.strict", true);
pref("nglayout.debug.disable_xul_cache", true);
pref("nglayout.debug.disable_xul_fastload", true);
EOFSRC
}

sub _get_file_macosx_xulfiles
{
	my ($self) = @_;
	my @files = ();
	my $w = 0;
	foreach my $window_xml (@{$self->{'windows'}}) {
		my $xml = 
			'<?xml version="1.0"?>'."\n".
			'<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>'."\n".
			$window_xml;
		push @files, ($w == 0 ? 'main' : 'sub'.$w).'.xul', [$xml];
		$w++;
	}
	return @files;
#	return
#		'<?xml version="1.0"?>'."\n".
#		'<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>'."\n".
#		$self->{'xml'};
#<window id="mw" title="$self->{'name'}" width="800" height="200"
#     xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
#     xmlns:html="http://www.w3.org/1999/xhtml">
#  <script src="AppXUL.js"/>
#  ...
#</window>
}

sub _get_file_macosx_chromemanifest
{
	my ($self) = @_;
	return 'content '.$self->{'name'}.' file:content/'."\n";
}

sub _get_file_macosx_appini
{
	my ($self) = @_;
	return <<EOFSRC
[App]
Version=1.0
Vendor=Me
Name=$self->{'name'}
BuildID=myid
ID={generated id}

[Gecko]
MinVersion=1.8
MaxVersion=2.*
EOFSRC
}

sub _get_file_startpl
{
	my ($self, $os, $type) = @_;

lib/App/XUL.pm  view on Meta::CPAN

    <UNREGISTER> := {
      action: "unregister",
      id: <ID>,
      name: <STRING>
    }
    
    <SETATTR> := {
      action: "setattr",
      id: <ID>,
      name: <STRING>,
      value: <STRING>
    }
    
    <GETATTR> := {
      action: "getattr",
      id: <ID>,
      name: <STRING>
    }

Here are some examples of client requests:

  {event:"click", id:"btn"}

Here are some examples of server responses:

  {action:"update", id:"btn", attributes:{label:"Alrighty!"}}

  {action:"remove", id:"btn"}

  {action:"create", parent:"main", content:"<button .../>"}

=head3 Application bundling for Mac OS X

Mac applications are simply directories whose names end with ".app"
and have a certain structure and demand certain files to exist.

This is the structure of a XUL application wrapped inside a Mac application
as created by App::XUL (files are blue, directories are black):

=begin html

<pre>
  MyApp.app/
    Contents/
      <span style="color:blue;font-weight:bold">Info.plist</span>
      Frameworks/
        XUL.framework/
          <i>The XUL Mac framework</i>
      MacOS
        <span style="color:blue;font-weight:bold">start.pl</span> (Perl-Script)
      Resources
        <span style="color:blue;font-weight:bold">application.ini</span>
        <span style="color:blue;font-weight:bold">MyApp.icns</span>
        chrome/
          <span style="color:blue;font-weight:bold">chrome.manifest</span>
          content/
            <span style="color:blue;font-weight:bold">AppXUL.js</span>
            <span style="color:blue;font-weight:bold">myapp.xul</span>
        defaults/
          preferences/
            <span style="color:blue;font-weight:bold">prefs.js</span>
        perl/
          server/
            <span style="color:blue;font-weight:bold">server.pl</span>
          modules/
            <i>All Perl modules the server depends on</i>
        extensions/
        updates/
          0/
</pre>

=end html

The various files have specific functions. When the MyApp.app is
clicked, the B<start.pl> program is executed which then starts the
server and the client:

=over 1

=item Info.plist

Required by Mac OS X. This is the place where certain basic information
about the application is read by Mac OS X, before anything else is done.
For example, here the start.pl program is defined as the entry point
of the application.

=item start.pl

First program to be executed. Starts server and client.

=item application.ini

Setups the XUL application. Defines which *.xul files to load,
name of application etc.

=item AppXUL.js

Defines all Javascript functions used by App::XUL to manage the
communication with the server.

=item myapp.xul

Defines the basic UI for the XUL application.

=item prefs.js

Sets some preferences for the XUL application.

=item server.pl

This starts the server.

=back

=head3 Application bundling for Windows

tbd. Use L<NSIS|http://nsis.sourceforge.net/Main_Page> or
L<InstallJammer|http://www.installjammer.com/>.

=head3 Application bundling as DEB package

tbd. See L<Link|http://www.webupd8.org/2010/01/how-to-create-deb-package-ubuntu-debian.html>.

=head3 Application bundling as RPM package

tbd.

=head1 ROADMAP

One thing on the todo list is to create a full-duplex connection
between client and server so that the client can react on
server events directly. This may be implemented using the HTML5
WebSocket protocol. For now all communication is iniciated from
the client using AJAX calls.

=head1 SEE ALSO

This module actually stands a bit on its own with its approach.
XUL modules exist though - XUL::Gui, XUL::Node and a few more.

=head1 AUTHOR

Tom Kirchner, E<lt>tom@tomkirchner.comE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2011 by Tom Kirchner

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.10.0 or,
at your option, any later version of Perl 5 you may have available.


=cut



( run in 1.689 second using v1.01-cache-2.11-cpan-0bb4e1dffa6 )