JQuery-Mobile

 view release on metacpan or  search on metacpan

lib/JQuery/Mobile.pm  view on Meta::CPAN

package JQuery::Mobile;
use strict;
use warnings;
no warnings 'uninitialized';
use Exporter 'import';
our @EXPORT_OK = qw(new head header footer table panel popup page pages form listview collapsible collapsible_set navbar button controlgroup input rangeslider select checkbox radio textarea);

use Clone qw(clone);
use HTML::Entities qw(encode_entities);

our $VERSION = 0.03;
# 54.4

sub new {
	my ($class, %args) = (@_);
	my $self = bless {}, $class;
	$args{config} ||= {};
	
	$self->{config} = {
		'head' => 1, # include <html>, <head>, and <body> tag when rendering a page
		'viewport' => 'width=device-width, initial-scale=1', # default viewport
		'apple-mobile-web-app-capable' => 1,  # enable as apple web app
		'apple-touch-icon' => '', # path to apple web app icon image
		'apple-touch-icon-72' => '', # path to apple web app icon image (72x72 pixels)
		'apple-touch-icon-114' => '', # path to apple web app icon image (114x114 pixels)
		'apple-touch-startup-image' => '', # path to apple web app startup image
		'jquery-mobile-css' => 'http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css',
		'jquery-mobile-js' => 'http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js',
		'jquery' => 'http://code.jquery.com/jquery-1.9.1.min.js',
		'app-css' => [], # global application CSS files
		'app-js' => [], # global application JS files
		'app-inline-css' => '      span.invalid{color:#F00000;line-height: 1.5;}', # inline CSS code
		'app-inline-js' => '', # inline JS code
		'app-title' => '', # <title> in <head>
		# a list of default allowed HTML and data-* attributes for UI components (Reference: http://api.jquerymobile.com/data-attribute/)
		'header-footer-html-attribute' => ['id', 'class'],
		'header-footer-data-attribute' => ['id', 'fullscreen', 'position', 'theme'],
		'navbar-html-attribute' => ['id', 'class'],
		'navbar-data-attribute' => ['disable-page-zoom', 'enhance', 'fullscreen', 'iconpos', 'tap-toggle', 'theme', 'transition', 'update-page-padding', 'visible-on-page-show'],
		'navbar-item-html-attribute' => ['id', 'class', 'target'],
		'navbar-item-data-attribute' => ['ajax', 'icon', 'iconpos', 'iconshadow','prefetch', 'theme'],
		'page-html-attribute' => ['id', 'class'],
		# combine data-attributes for page and dialog
		'page-data-attribute' => ['add-back-btn', 'back-btn-text', 'back-btn-theme', 'close-btn', 'close-btn-text', 'corners', 'dom-cache', 'enhance', 'overlay-theme', 'role', 'shadow','theme', 'title', 'tolerance', 'url'],
		'table-html-attribute' => ['id', 'class'],
		'table-data-attribute' => ['mode'],
		'table-head-html-attribute' => ['id', 'class'],
		'table-head-data-attribute' => ['priority'],
		'panel-html-attribute' => ['id', 'class'],
		'panel-data-attribute' => ['corners', 'overlay-theme', 'shadow', 'theme', 'tolerance', 'position-to', 'rel', 'role', 'transition'],
		'popup-html-attribute' => ['id', 'class'],
		'popup-data-attribute' => ['animate', 'dismissible', 'display', 'position', 'position-fixed', 'swipe-close', 'role', 'theme'],
		'listview-html-attribute' => ['id', 'class'],
		'listview-data-attribute' => ['autodividers', 'count-theme', 'divider-theme', 'enhance', 'filter', 'filter-placeholder', 'filter-reveal', 'filter-theme', 'filtertext', 'header-theme', 'inset', 'split-icon', 'split-theme', 'theme'],
		'listview-item-html-attribute' => ['id', 'class'],
		'listview-item-data-attribute' => ['ajax', 'mini', 'rel', 'theme', 'transition'],
		'collapsible-html-attribute' => ['id', 'class'],
		'collapsible-data-attribute' => ['collapsed', 'collapsed-icon', 'content-theme', 'expanded-icon', 'iconpos', 'inset', 'mini', 'theme'],
		'collapsible-set-html-attribute' => ['id', 'class'],
		'collapsible-set-data-attribute' => ['collapsed-icon', 'content-theme', 'expanded-icon', 'iconpos', 'inset', 'mini', 'theme'],
		'controlgroup-html-attribute' => ['id', 'class'],
		'controlgroup-data-attribute' => ['enhance', 'iconpos', 'mini', 'theme', 'type'],
		'button-html-attribute' => ['id', 'name', 'class', 'maxlength', 'size', 'type', 'value'],
		'button-html-anchor-attribute' => ['id', 'class', 'href', 'target'],
		'button-data-attribute' => ['ajax', 'corners', 'dialog', 'direction', 'dom-cache', 'external', 'icon', 'iconpos', 'iconshadow', 'inline', 'mini', 'position-to', 'prefetch', 'rel', 'role', 'shadow', 'theme', 'transition'],
		'form-html-attribute' => ['id', 'action', 'class', 'enctype', 'method'],
		'form-data-attribute' => ['enhance', 'theme', 'ajax'],
		'input-html-attribute' => ['id', 'class', 'disabled', 'max', 'maxlength', 'min', 'name', 'pattern', 'placeholder', 'readonly', 'required', 'size', 'type', 'value', 'accept', 'capture'],
		'input-data-attribute' => ['clear-btn', 'clear-btn-text', 'corners', 'highlight', 'icon', 'iconpos', 'iconshadow', 'inline', 'mini', 'shadow', 'theme', 'track-theme'],
		'textarea-html-attribute' => ['id', 'name', 'class', 'rows', 'cols', 'readonly', 'disabled', 'title', 'required', 'placeholder', 'title', 'pattern'],
		'textarea-data-attribute' => ['clear-btn', 'clear-btn-text', 'mini', 'theme'],
		'select-html-attribute' => ['id', 'class', 'size', 'maxlength', 'readonly', 'disabled', 'title', 'required', 'placeholder', 'title', 'pattern', 'multiple'],
		'select-data-attribute' => ['icon', 'iconpos', 'inline', 'mini', 'native-menu', 'overlay-theme', 'theme', 'role'],
		'radio-checkbox-html-attribute' => ['id', 'class', 'readonly', 'disabled', 'title', 'required', 'placeholder', 'title', 'pattern', 'value'],
		'radio-checkbox-data-attribute' => ['mini', 'theme'],
		'rangeslider-html-attribute' => ['id', 'name', 'class'],
		'rangeslider-data-attribute' => ['highlight', 'mini', 'theme', 'track-theme'],
		'label' => sub {
			my $args = shift;
			return '<strong>' . $args->{label} . '</strong>' if $args->{required};
			return $args->{label};
		},
		'invalid' => sub {
			my $args = shift;
			my $message = $args->{message};

			if (! $message && $args->{type}) {
				$message = {
					'input' => 'Enter "%FIELDNAME%"',
					'checkbox' => 'Check one or more "%FIELDNAME%"',
					'radio' => 'Choose "%FIELDNAME%"',
					'select' => 'Select an option from "%FIELDNAME%"',
					'textarea' => 'Fill in the "%FIELDNAME%"'
				}->{$args->{type}};
			}

			$message ||= 'Enter "%FIELDNAME%"';
			$message =~ s/\%FIELDNAME\%/$args->{label}/g;

			return '<span class="invalid">' . $message . '</span>' if $args->{invalid};
		},
		%{$args{config}},
	};

	return $self;
}

sub head {
	my ($self, %args) = @_;

	my $head = '  <head>' . "\n" . '    <title>' . $self->{config}->{'app-title'} . '</title>' . "\n" . 
	'    <meta name="viewport" content="' . $self->{config}->{'viewport'} . '" />' . "\n";
	# apple icons, startup image
	$head .= '    <meta name="apple-mobile-web-app-capable" content="yes" />' . "\n" if $self->{config}->{'apple-mobile-web-app-capable'};
	$head .= '    <link rel="apple-touch-icon" href="' . $self->{config}->{'apple-touch-icon'} . '" />' . "\n" if $self->{config}->{'apple-touch-icon'};
	$head .= '    <link rel="apple-touch-icon" sizes="72x72" href="' . $self->{config}->{'apple-touch-icon-72'} . '" />' . "\n" if $self->{config}->{'apple-touch-icon-72'};
	$head .= '    <link rel="apple-touch-icon" sizes="114x114" href="' . $self->{config}->{'apple-touch-icon-114'} . '" />' . "\n" if $self->{config}->{'apple-touch-icon-114'};
	$head .= '    <link rel="apple-touch-startup-image" href="' . $self->{config}->{'apple-touch-startup-image'} . '" />' . "\n" if $self->{config}->{'apple-touch-startup-image'};

	my $css_sources = [$self->{config}->{'jquery-mobile-css'}];
	push @{$css_sources}, @{$self->{config}->{'app-css'}}, if @{$self->{config}->{'app-css'}};

	foreach my $css (@{$css_sources}) {
		$head .= '    <link rel="stylesheet" href="' . $css . '" />' . "\n";
	}

	my $js_sources = [$self->{config}->{'jquery'}, $self->{config}->{'jquery-mobile-js'}];
	push @{$js_sources}, @{$self->{config}->{'app-js'}}, if @{$self->{config}->{'app-js'}};

	foreach my $js (@{$js_sources}) {
		$head .= '    <script src="' . $js . '"></script>' . "\n" if $js;
	}

lib/JQuery/Mobile.pm  view on Meta::CPAN

      	id => 'home', 
      	header => {content => '<h1>Home</h1>'}, 
      	content => $list
      },
      {
      	id => 'item-link', 
      	header => {content => '<h1>Item Heading</h1>'}, 
      	content => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit'
      },
    ]
  );

=head1 DESCRIPTION

JQuery::Mobile is an interface to jQuery Mobile. It generates HTML markups, such as navbars, forms, and listviews, that are compatible with jQuery Mobile.

=head1 METHODS

=head2 C<new>

To instantiate a new JQuery::Mobile object:

  my $jquery_mobile = JQuery::Mobile->new();

Here is a list of optional parameters when instantiating a JQuery::Mobile object:

  # default values are shown
  my $jquery_mobile = JQuery::Mobile->new(
     config => {
      'head' => 1, # include <html>, <head>, and <body> tag when rendering a page
      'viewport' => 'width=device-width, initial-scale=1', # default viewport
      'apple-mobile-web-app-capable' => 1, # enable as apple web app
      'apple-touch-icon' => '', # path to apple web app icon image
      'apple-touch-icon-72' => '', # path to apple web app icon image (72x72 pixels)
      'apple-touch-icon-114' => '', # path to apple web app icon image (114x114 pixels)
      'apple-touch-startup-image' => '', # path to apple web app startup image
      'jquery-mobile-css' => 'http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css',
      'jquery-mobile-js' => 'http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js',
      'jquery' => 'http://code.jquery.com/jquery-1.9.1.min.js',
      'app-css' => [], # global application CSS files
      'app-js' => [], # global application JS files
      'app-inline-css' => '      span.invalid{color:#F00000;line-height: 1.5;}', # inline CSS code
      'app-inline-js' => '', # inline JS code
      'app-title' => '', # <title> in <head>
     }
  );

The allowed HTML and data-* attributes for each UI component can be customised. By default, HTML attributes are very strict to ensure a clean markup. For data-* attributes, see reference: http://api.jquerymobile.com/data-attribute/.

  # default values are shown
  my $jquery_mobile = JQuery::Mobile->new(
    config => {
      'header-footer-html-attribute' => ['id', 'class'],
      'header-footer-data-attribute' => ['id', 'fullscreen', 'position', 'theme'],
      'navbar-html-attribute' => ['id', 'class'],
      'navbar-data-attribute' => ['disable-page-zoom', 'enhance', 'fullscreen', 'iconpos', 'tap-toggle', 'theme', 'transition', 'update-page-padding', 'visible-on-page-show'],
      'navbar-item-html-attribute' => ['id', 'class', 'target'],
      'navbar-item-data-attribute' => ['ajax', 'icon', 'iconpos', 'iconshadow','prefetch', 'theme'],
      'page-html-attribute' => ['id', 'class'],
      # combine data-attributes for page and dialog
      'page-data-attribute' => ['add-back-btn', 'back-btn-text', 'back-btn-theme', 'close-btn', 'close-btn-text', 'corners', 'dom-cache', 'enhance', 'overlay-theme', 'role', 'shadow','theme', 'title', 'tolerance', 'url'],
      'popup-html-attribute' => ['id', 'class'],
      'popup-data-attribute' => ['corners', 'overlay-theme', 'shadow', 'theme', 'tolerance', 'position-to', 'rel', 'role', 'transition'],
      'listview-html-attribute' => ['id', 'class'],
      'listview-data-attribute' => ['autodividers', 'count-theme', 'divider-theme', 'enhance', 'filter', 'filter-placeholder', 'filter-theme', 'filtertext', 'header-theme', 'inset', 'split-icon', 'split-theme', 'theme'],
      'listview-item-html-attribute' => ['id', 'class'],
      'listview-item-data-attribute' => ['ajax', 'mini', 'rel', 'theme', 'transition'],
      'collapsible-html-attribute' => ['id', 'class'],
      'collapsible-data-attribute' => ['collapsed', 'collapsed-icon', 'content-theme', 'expanded-icon', 'iconpos', 'inset', 'mini', 'theme'],
      'collapsible-set-html-attribute' => ['id', 'class'],
      'collapsible-set-data-attribute' => ['collapsed-icon', 'content-theme', 'expanded-icon', 'iconpos', 'inset', 'mini', 'theme'],
      'controlgroup-html-attribute' => ['id', 'class'],
      'controlgroup-data-attribute' => ['enhance', 'iconpos', 'theme', 'type'],
      'button-html-attribute' => ['id', 'name', 'class', 'maxlength', 'size', 'type', 'value'],
      'button-html-anchor-attribute' => ['id', 'class', 'href', 'target'],
      'button-data-attribute' => ['ajax', 'corners', 'dialog', 'direction', 'dom-cache', 'external', 'icon', 'iconpos', 'iconshadow', 'inline', 'mini', 'position-to', 'prefetch', 'rel', 'role', 'shadow', 'theme', 'transition'],
      'form-html-attribute' => ['id', 'action', 'class', 'enctype', 'method'],
      'form-data-attribute' => ['enhance', 'theme', 'ajax'],
      'input-html-attribute' => ['id', 'class', 'disabled', 'max', 'maxlength', 'min', 'name', 'pattern', 'placeholder', 'readonly', 'required', 'size', 'type', 'value', 'accept', 'capture'],
      'input-data-attribute' => ['clear-btn', 'clear-btn-text', 'corners', 'highlight', 'icon', 'iconpos', 'iconshadow', 'inline', 'mini', 'shadow', 'theme', 'track-theme'],
      'textarea-html-attribute' => ['id', 'name', 'class', 'rows', 'cols', 'readonly', 'disabled', 'title', 'required', 'placeholder', 'title', 'pattern'],
      'textarea-data-attribute' => ['clear-btn', 'clear-btn-text', 'mini', 'theme'],
      'select-html-attribute' => ['id', 'class', 'size', 'maxlength', 'readonly', 'disabled', 'title', 'required', 'placeholder', 'title', 'pattern'],
      'select-data-attribute' => ['icon', 'iconpos', 'inline', 'mini', 'native-menu', 'overlay-theme', 'placeholder', 'theme'],
      'radio-checkbox-html-attribute' => ['id', 'class', 'readonly', 'disabled', 'title', 'required', 'placeholder', 'title', 'pattern', 'value'],
      'radio-checkbox-data-attribute' => ['mini', 'theme']
     }
  );

The C<label> parameter accepts a sub callback to alter how form field labels are being generated:

  my $jquery_mobile = JQuery::Mobile->new(
    config => {
      'label' => sub {
        my $args = shift;
        return '<strong>' . $args->{label} . '*</strong>' if $args->{required};
        return $args->{label};
      },
    }
  );

=head2 C<head>

C<head()> is called by C<page()> and C<pages()> internally to render the HTML header. Parameters via C<new()> controlls the output of C<head()>.

=head2 C<header>

C<header()> generates header toolbars. Text, buttons, or C<navbar()> can be passed to the C<content> parameter.

  print $jquery_mobile->header(
    content => $jquery_mobile->button(href => '#', value => 'Home', icon => 'home', iconpos => 'notext') . '<h1>Main Title</h1>',
  );

prints:

  <div data-role="header">
    <a data-icon="home" data-iconpos="notext" data-role="button" href="#">Home</a>
    <h1>Main Title</h1>
  </div><!-- /header -->

Attributes defined in C<header-footer-html-attribute> and C<header-footer-data-attribute> can be passed to C<header()>, based on the default configuration:

  print $jquery_mobile->header(
    'content' => '<h1>Header Content</h1>',
    'id' => 'home-main-header',
    'class' => 'site-header',
    'data-id' => 'main-header', # use the 'data-*' prefix since 'id' is both a HTML and data atrribute
    'fullscreen' => 'true',
    'position' => 'fixed',
    'theme' => 'e'
  );

=head2 C<footer>

Similar to C<header()>, C<footer()> generates footer toolbars. Attributes defined in C<header-footer-html-attribute> and C<header-footer-data-attribute> can be passed to C<footer()>.

  print $jquery_mobile->footer(
    position => 'fixed',
    content => 'Footer content'
  );

=head2 C<popup>

C<popup()> generates popup container divs. Content can be passed via the C<content> parameter:



( run in 1.246 second using v1.01-cache-2.11-cpan-df04353d9ac )