Bivio::UI::HTML::ViewShortcuts
# Copyright (c) 1999-2010 bivio Software, Inc. All rights reserved.
# $Id$
package Bivio::UI::HTML::ViewShortcuts;
use strict;
#TODO: Should be UI.ViewShortcuts or UIHTML, but can't do this right now
use Bivio::Base 'UIHTML';
use Bivio::IO::Trace;
my($_WF) = b_use('UIHTML.WidgetFactory');
my($_V6) = b_use('IO.Config')->if_version(6);
my($_LINK_TARGET) = $_V6 ? undef : '_top';
my($_ATTRS) = {};
our($_TRACE);
my($_AA) = b_use('Action.Acknowledgement');
sub BOP_HTML_CLASSES {
return [qw(
acknowledgement
actions
all want_sep
alphabetical_chooser
amount_cell
ascend
attachment
author
blog
body
bottom
bottom_left
bottom_right
byline
checkbox
checkbox_label
date
desc
descend
empty_list
err_title
even
excerpt
field
field_err
footer
footer_left
footer_middle
footer_right
form_prose
forward
go
header
header_left
header_middle
header_right
heading
help_wiki_add
inline
label
label_err
label_ok
list
list_actions
logo
logo_su
main
main_body
main_bottom
main_left
main_middle
main_right
main_top
menu
msg
msg_compose
msg_sep
nav
next
not_found
num
odd
off
on
paged_list
pager
part
parts
prev
prose
realm
required
rounded_box
rounded_box_body
search
selected
selector
sep
user_settings
simple
standard_submit
su
submit
task_menu
text
text_html
text_plain
textarea
title
tools
top
top_left
top_right
topic
tuple
user_state
value
want_sep
)];
}
sub vs_alphabetical_chooser {
# (proto, string) : UI.Widget
# Generates a list of links which are alphabetically ordered and pass their
# "letter" on to the ListQuery.search.
my($proto, $list_model) = @_;
my($all) = Bivio::Biz::Model->get_instance($list_model)
->LOAD_ALL_SEARCH_STRING;
return $proto->vs_call('String',
$proto->vs_call('Join', [
map({(
$_ =~ /^A/ ? $_ eq $all ? ' | ' : '' : ' ',
$proto->vs_call('Link',
$proto->vs_call('Join', [$_]),
['->format_uri', undef,
[sub {
return {
'ListQuery.search' => $_[1],
'ListQuery.date' => $_[0]->get($_[2])
->get_query->get('date'),
};
},
$_,
"Model.$list_model",
],
],
),
)} 'A'..'Z', $all),
]),
);
}
sub vs_blank_cell {
# (proto) : UI.Widget
# (proto, int) : UI.Widget
# Returns a cell which renders a blank. Makes the code clearer to use.
my($proto, $count) = @_;
return $proto->vs_join(' ' x ($count || 1));
}
sub vs_clear_dot {
# (self, any, any) : Widget.ClearDot
# (self, any, any) : Widget.ClearDot
# B<DEPRECATED. Use L<vs_new|"vs_new">>.
my($proto, $width, $height) = @_;
return $proto->vs_new('ClearDot', {
defined($width) ? (width => $width) : (),
defined($height) ? (height => $height) : (),
});
}
sub vs_clear_dot_as_html {
# (self, int, int) : string
# Returns an html string which loads a ClearDot image in
# width and height.
#
# Don't use in rendering code. Use L<vs_clear_dot|"vs_clear_dot"> instead.
my(undef) = shift;
my($c) = _use('ClearDot');
return $c->as_html(@_);
}
sub vs_correct_table_layout_bug {
# (self) : UI.Widget
# Returns a widget which renders a table layout correction javascript if
# necessary.
my($proto) = @_;
return $proto->vs_call('If',
[['->get_request'], 'Type.UserAgent', '->has_table_layout_bug'],
$proto->vs_call('Script', 'correct_table_layout_bug'));
}
sub vs_descriptive_field {
# (proto, any) : array_ref
# Calls vs_form_field and adds I<description> to the result. I<description>
# is an optional string, widget value, or widget. It is always wrapped
# in a String with font form_field_description.
my($proto, $field) = @_;
my($name, $attrs) = ref($field) ? @$field : $field;
my($label, $input) = $proto->vs_form_field($name, $attrs);
return [
$label->put(cell_class => 'form_field_label'),
$proto->vs_call('Join', [
$input,
[sub {
my($req) = shift->get_request;
my($proto, $name) = @_;
#TODO: Need to create a separate space for field_descriptions so we don't
# default to something that we don't expect.
my($v) = $req->get_nested('Bivio::UI::Facade', 'Text')
->unsafe_get_value($name, 'field_description');
return $v ?
$proto->vs_call(
'String',
$proto->vs_call('Prose', '<br><p class="form_field_description">' . $v . '</p>'),
'form_field_description',
) : '';
}, $proto, $name],
], {
cell_class => 'form_field_input',
}),
];
}
sub vs_director {
# (proto, any, hash_ref, UI.Widget, UI.Widget) : UI.Widget
# B<DEPRECATED. Use L<vs_new|"vs_new">>.
my($proto) = shift;
return $proto->vs_new('Director', @_);
}
sub vs_display {
return _wf(@_);
}
sub vs_edit {
return _wf(@_);
}
sub vs_escape_html {
# (self, array_ref) : array_ref
# Wraps I<value> in L<Bivio::HTML::escape|Bivio::HTML/"escape">,
my(undef, $value) = @_;
return [\&_escape, $value];
}
sub vs_fe {
# (proto, string) : string
# Calls SUPER and escapes.
return Bivio::HTML->escape(shift->SUPER::vs_fe(@_));
}
sub vs_first_focus {
# (proto, any) : UI.Widget
# Returns script widget that focuses on the first field on the page.
# I<control> is optional.
my($proto, $control) = @_;
my($w) = $proto->vs_call('Script', 'first_focus');
return defined($control) ? $proto->vs_call('If', $control, $w) : $w;
}
sub vs_form_field {
# (proto, string) : array
# Creates a new I<HTMLWidget.FormField> and returns the widgets (label, field).
# This is equivalent to:
#
# vs_new('FormField', @_)->get_label_and_field
my($proto) = shift;
return $proto->vs_new('FormField', @_)->get_label_and_field;
}
sub vs_html_attrs_initialize {
my($proto, $widget, $attrs, $source) = @_;
$widget->map_invoke(
'unsafe_initialize_attr',
$attrs || $proto->vs_html_attrs_merge,
undef,
[$source],
);
return;
}
sub vs_html_attrs_merge {
my(undef, $extra) = @_;
return [qw(class id), @{$extra || []}];
}
sub vs_html_attrs_render {
my($proto, $widget, $source, $attrs) = @_;
return join(
'',
map($proto->vs_html_attrs_render_one($widget, $source, $_),
@{$attrs || $proto->vs_html_attrs_merge}),
);
}
sub vs_html_attrs_render_one {
my($proto, $widget, $source, $attr) = @_;
return ''
unless length(my $v = $widget->render_simple_attr($attr, $source));
my($k) = $attr =~ /^[A-Z]/ ? lc($attr) : ($attr =~ /([^_]+)$/)[0];
if ($k =~ /^(?:class|id)$/) {
_trace($k, '=', $v) if $_TRACE && !$_ATTRS->{$k}->{$v}++;
$v =~ s/^b_//
unless $_V6;
}
return ' '
. $k
. '="'
. Bivio::HTML->escape_attr_value($v)
. '"';
}
sub vs_image {
# (proto, any) : Widget.Image
# (proto, any, any, hash_ref) : Widget.Image
# B<DEPRECATED. Use L<vs_new|"vs_new">>.
my($proto, $icon, $alt, $attrs) = @_;
_use('Image');
return Bivio::UI::HTML::Widget::Image->new({
src => $icon,
(defined($alt) || ref($icon) ? (alt => $alt) : (alt_text => $icon)),
$attrs ? %$attrs : (),
});
}
sub vs_join {
# (proto, any, ...) : Widget.Join
# B<DEPRECATED. Use L<vs_new|"vs_new">>.
my($proto, @values) = @_;
my($values) = int(@values) == 1 && ref($values[0]) eq 'ARRAY'
? $values[0] : [@values];
return $proto->vs_new('Join', $values);
}
sub vs_link {
# (proto, string) : Widget.Link
# (proto, any, string) : Widget.Link
# (proto, any, string, string) : Widget.Link
# (proto, any, array_ref) : Widget.Link
# (proto, any, UI.Widget) : Widget.Link
# (proto, any, string) : Widget.Link
# If only I<task> is supplied, it is used for both the label and the href.
# It will also be the control for the link. This is the preferred way
# to create links.
#
# Returns a C<Link> with I<label> and I<widget_value>
#
# If I<label> is not a widget, will wrap in a C<String> widget.
#
# If I<task> is passed, will create a widget value by formatting
# as a stateless uri for the TaskId named by I<task>.
#
# If I<abs_uri> is passed, it must contain a / or : or #.
my($proto, $label, $widget_value, $font) = @_;
_use('Link');
my($control);
if (int(@_) <= 2) {
$control = $label;
$widget_value = $label;
$label = $proto->vs_text($widget_value);
}
unless (UNIVERSAL::isa($label, 'Bivio::UI::Widget')) {
$label = $proto->vs_string($label);
}
else {
#TODO: Does this make sense. I put it it in for backward compatibility [RJN]
# Don't assign the font unless creating a string.
$font = undef;
}
$widget_value = [['->get_request'], '->format_stateless_uri',
Bivio::Agent::TaskId->$widget_value()]
# Use widget value or abs_uri (literal)
unless ref($widget_value) || $widget_value =~ m![/:#]!;
return Bivio::UI::HTML::Widget::Link->new({
href => $widget_value,
value => $label,
$control ? (control => $control) : (),
defined($font) ? (string_font => $font) : (),
});
}
sub vs_link_target_as_html {
my($proto, $widget, $source) = @_;
return ''
unless defined(my $t = $widget->ancestral_get(
'link_target', $_LINK_TARGET));
if ($source) {
my($b);
$widget->unsafe_render_value('link_target', $t, $source, \$b);
$t = $b;
}
return defined($t) && length($t)
? ' target="' . Bivio::HTML->escape($t) . '"' : '';
}
sub vs_mailto_for_user_id {
my($proto, $user_id) = @_;
return $proto->vs_call('MailTo',
map([sub {
my($source, $user_id, $model, $field) = @_;
return $source->use('Model.' . $model)->new($source->req)
->unauth_load_or_die({
realm_id => $user_id,
})->get($field);
}, $user_id, split('\.', $_)],
qw(Email.email RealmOwner.display_name)));
}
sub vs_new {
# (proto, string, any, ...) : UI.Widget
# Returns an instance of I<class> created with I<new_args>. Loads I<class>, if
# not already loaded.
my($proto, $class) = (shift, shift);
return UNIVERSAL::can('Bivio::UI::ViewLanguage', 'view_ok')
&& Bivio::UI::ViewLanguage->view_ok
? $proto->vs_call($class, @_) : (_use($class))[0]->new(@_);
}
sub vs_simple_form {
# (proto, string, array_ref) : UI.Widget
# Creates a Form in a Grid. I<rows> may be a field name, a separator name
# (preceded by a dash), a widget (iwc colspan will be set to 2), or a list of
# button names separated by spaces (preceded by a '*'). If there is no '*'
# list, then StandardSubmit will be appended to the list of fields.
my($proto, $form, $rows, $attr) = @_;
$attr ||= {};
$attr->{pad} = 2;
my($have_submit) = 0;
return $proto->vs_call('Form', $form,
$proto->vs_call('Grid', [
map({
my($x);
if (UNIVERSAL::isa($_, 'Bivio::UI::Widget')) {
$_->get_if_exists_else_put(cell_align => 'left'),
$x = [$_->put(cell_colspan => 2)];
}
elsif ($_ =~ s/^-//) {
$x = [$proto->vs_call(
'String',
$proto->vs_text('separator', $_),
0,
{
cell_colspan => 2,
cell_class => 'separator',
},
)];
}
elsif ($_ =~ s/^\*//) {
$have_submit = 1;
$x = [$proto->vs_call(
'StandardSubmit',
{
cell_colspan => 2,
cell_align => 'center',
cell_class => 'form_submit',
$_ ? (buttons => [split(/\s+/, $_)]) : (),
},
)];
}
elsif (ref($_) eq 'ARRAY' && ref($_->[0])) {
$x = $_;
}
else {
$x = $proto->vs_descriptive_field($_);
}
$x;
} @$rows),
$have_submit ? () : [$proto->vs_call('StandardSubmit', {
cell_colspan => 2,
cell_align => 'center',
cell_class => 'form_submit',
})],
], $attr));
}
sub vs_string {
# (proto, any) : Widget.String
# (proto, any, string, hash_ref) : Widget.String
# B<DEPRECATED. Use L<vs_new|"vs_new">>.
my($proto, $value, $font, $attrs) = @_;
return $proto->vs_new('String', $value, $font, $attrs ? $attrs : ());
}
sub vs_task_link {
# (self, string, string) : Widget.Link
# Returns a link widget for the specified task. Only renders if the current
# user can execute the task.
my($proto, $text, $task) = @_;
return $proto->vs_call('Link', $text, $task, {
control => $task,
});
}
sub vs_ts {
# (proto, any, ...) : Widget.String
# Wraps vs_text() in a String(). All arguments passed to vs_text(),
my($proto) = shift;
return $proto->vs_new('String', $proto->vs_text(@_));
}
sub vs_unknown_label {
my($proto, $model, $field) = @_;
return $proto->vs_text(
ref($model) || $model =~ /::/ ? $model->simple_package_name : $model,
$field,
'unknown_label',
);
}
sub vs_xhtml {
# (proto, any) : boolean
# Returns true if rendering in xhtml.
my(undef, $source) = @_;
return $source->get_request->get_or_default('xhtml', 0);
}
sub _escape {
# (any, string) : string
# Escapes its argument. Must be a scalar, and not undef.
my(undef , $value) = @_;
Bivio::Die->die($value, ': vs_escape_html not passed a string')
if ref($value) || !defined($value);
return Bivio::HTML->escape($value);
}
sub _use {
return map(b_use($_ =~ /\W/ ? $_ : ('HTMLWidget', $_)), @_);
}
sub _wf {
my($proto, $field, $attrs) = @_;
$attrs ||= {};
$attrs->{wf_want_display} = $proto->my_caller =~ /display/ ? 1 : 0;
return $_WF->create($field, $attrs);
}
1;