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";
lib/JQuery/Mobile.pm view on Meta::CPAN
}
$item_attributes = _html_attribute ($item_attributes, $self->{config}->{'navbar-item-html-attribute'}, $item);
$navbar .= ' <li><a href="' . $item->{href} . '"' . $item_attributes . '>' . $item->{value} . '</a></li>' . "\n";
}
$navbar .= ' </ul>' . "\n";
$navbar .= ' </div><!-- /navbar -->';
return $navbar;
}
sub panel {
my ($self, %args) = @_;
$args{content} ||= ' <p>Panel Content</p>';
$args{role} = 'panel';
my $attributes = _html_attribute('', $self->{config}->{'panel-html-attribute'}, \%args);
$attributes = _data_attribute($attributes, $self->{config}->{'panel-data-attribute'}, \%args);
my $panel = ' <div' . $attributes . '>' . "\n";
$panel .= $args{content} . "\n";
$panel .= ' </div><!-- /panel -->' . "\n";
return $panel;
}
sub table {
my ($self, %args) = @_;
unless ($args{content}) {
$args{content} = ' <thead>' . "\n" . ' <tr>' . "\n";
foreach my $header (@{$args{headers}}) {
my $head_attributes = '';
if ($args{th} && exists $args{th}->{$header}) {
$head_attributes = _html_attribute($head_attributes, $self->{config}->{'table-head-html-attribute'}, $args{th}->{$header});
$head_attributes = _data_attribute($head_attributes, $self->{config}->{'table-head-data-attribute'}, $args{th}->{$header});
}
$args{content} .= ' <th' . $head_attributes . '>' . $header . '</th>' . "\n";
}
$args{content} .= ' </tr>' . "\n" . ' </thead>' . "\n";
$args{content} .= ' <tbody>'. "\n";
foreach my $row (@{$args{rows}}) {
$args{content} .= ' <tr>' . "\n" . join("\n", map ({' <td>' . $_ . '</td>'} @{$row})) . "\n" . ' </tr>' . "\n";
}
$args{content} .= ' </tbody>';
}
my $attributes = _html_attribute('', $self->{config}->{'table-html-attribute'}, \%args);
$attributes = _data_attribute($attributes, $self->{config}->{'table-data-attribute'}, \%args);
my $table = ' <table data-role="table"' . $attributes . '>' . "\n" . $args{content} . "\n" . ' </table>' . "\n";
return $table;
}
sub popup {
my ($self, %args) = @_;
$args{content} ||= ' <p>Popup Content</p>';
$args{role} = 'popup';
my $attributes = _html_attribute('', $self->{config}->{'popup-html-attribute'}, \%args);
$attributes = _data_attribute($attributes, $self->{config}->{'popup-data-attribute'}, \%args);
my $popup = ' <div' . $attributes . '>' . "\n";
$popup .= $args{content} . "\n";
$popup .= ' </div><!-- /popup -->' . "\n";
return $popup;
}
sub page {
my ($self, %args) = @_;
$args{content} ||= ' <p>Page Content</p>';
$args{role} ||= 'page';
my $attributes = _html_attribute('', $self->{config}->{'page-html-attribute'}, \%args);
$attributes = _data_attribute($attributes, $self->{config}->{'page-data-attribute'}, \%args);
my $page = ' <div' . $attributes . '>' . "\n";
$page .= $self->header(%{$args{header}}) if $args{header};
$page .= $self->panel(%{$args{panel}}) if $args{panel};
$page .= ' <div data-role="content">' . "\n" . $args{content} . "\n" . ' </div><!-- /content -->' . "\n";
$page .= $args{after} if $args{after};
$page .= $self->footer(%{$args{footer}}) if $args{footer};
$page .= ' </div><!-- /page -->' . "\n";
return $page if (exists $args{head} && ! $args{head}) || ! $self->{config}->{'head'};
return "<!DOCTYPE html>\n<html>\n" . $self->head() . " <body>\n" . $page . " </body>\n</html>";
}
sub pages {
my ($self, %args) = @_;
my $pages = '';
foreach my $page (@{$args{pages}}) {
$page->{head} = 0;
$pages .= $self->page(%{$page});
}
$pages ||= '<p>Multiple Pages</p>';
return $pages if (exists $args{head} && ! $args{head}) || ! $self->{config}->{'head'};
return "<!DOCTYPE html>\n<html>\n" . $self->head() . " <body>\n" . $pages . " </body>\n</html>";
}
sub collapsible_set {
my ($self, %args) = @_;
if ($args{collapsibles} && @{$args{collapsibles}}) {
foreach my $collapsible (@{$args{collapsibles}}) {
if ($args{active} && ! exists $collapsible->{active}) {
$collapsible->{active} = $args{active};
}
$args{content} .= $self->collapsible(%{$collapsible});
}
}
else {
$args{content} ||= ' <p>Collapsible Set Content</p>';
}
my $attributes = _html_attribute('', $self->{config}->{'collapsible-set-html-attribute'}, \%args);
$attributes = _data_attribute($attributes, $self->{config}->{'collapsible-set-data-attribute'}, \%args);
my $collapsible_set = ' <div data-role="collapsible-set"' . $attributes . '>' . "\n" . $args{content} . "\n" . ' </div>' . "\n";
return $collapsible_set;
lib/JQuery/Mobile.pm view on Meta::CPAN
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:
my $popup = $jquery_mobile->popup(id => 'popup', content => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit');
Attributes defined in C<popup-html-attribute> and C<popup-data-attribute> can be passed to C<popup()>.
Please note that as of writing (jQuery Mobile 1.2), according to the jQuery Mobile website, popups "must live within the page wrapper (for now)".
=head2 C<page>
C<page()> generates a container divs. It accepts the following parameters:
=over
=item C<role>
C<role> can be either "page" or "dialog", defaulted to "page".
=item C<head>
C<page()> includes HTML C<head> and C<body> wrapper tags by default. Set C<head> to "0" to disable that:
print $jquery_mobile->page(head => 0);
=item C<content>
C<content> accepts text string.
=item C<header>
C<header> includes a header toolbars. C<header> accepts a hashref of parameters that gets passed directly to C<header()>.
=item C<footer>
C<footer> includes a footer toolbars. C<footer> accepts a hashref of parameters that gets passed directly to C<footer()>.
=back
For example:
print $jquery_mobile->page(
header => {
content => $jquery_mobile->button(href => '#', value => 'Home', icon => 'home', iconpos => 'notext') . '<h1>Main Title</h1>',
},
footer => {
position => 'fixed',
content => '<h3>Footer content</h3>'
},
content => 'Lorem ipsum dolor sit amet, consectetur adipisicing elit'
);
=head2 C<pages>
C<pages()> generates a "multi-page" template. It accepts the following parameters:
=over
=item C<head>
C<pages()> includes HTML C<head> and C<body> wrapper tags by default. Set C<head> to "0" to disable that.
=item C<pages>
C<pages> accepts an arrayref of parameters acceptable by C<page()>.
=back
( run in 1.072 second using v1.01-cache-2.11-cpan-364913b4093 )