# This file is Copyright (c) 2000-2007 Eric Andreychek. All rights reserved. # For distribution terms, please see the included LICENSE file. package OpenThought; =head1 NAME OpenThought - An AJAX transport and helper library, making AJAX-based page updates trivial =head1 SYNOPSIS use OpenThought(); use CGI(); my $OT = OpenThought->new(); my $q = CGI->new; # First, put everything you wish to give to the browser into a hash my ($fields, $html, $image); $fields->{'myTextBox'} = "Text Box Data"; $fields->{'myCheckbox'} = "true"; $fields->{'myRadioBtn'} = "RadioBtn2Value"; $fields->{'mySelectList'} = [ [ "text1", "value1" ], [ "text2", "value2" ], [ "text3", "value3" ], ]; $html->{'id_tagname'} = "New HTML Code"; $image->{'image_name'} = "http://example.com/my_image.gif"; # You can also execute JavaScript, just put it into a scalar my $javascript_code = "alert('Howdy!')"; # Then send it to the browser using: $OT->param( $fields ); $OT->param( $html ); $OT->param( $image ); $OT->focus( "myTextBox" ); $OT->javascript( $javascript_code ); print $q->header: print $OT->response(); # Or use the utility method: print $q->header; print $OT->response( param => $fields, param => $html, param => $image, focus => "myTextBox", javascript => $javascript_code, ); # In a seperate HTML file, you might have this (which is where you'd first # point the browser, the HTML then calls the Perl when you click the button or # select list)
=head1 DESCRIPTION OpenThought is a library which implements an API for AJAX communication and updates. You can perform updates to form fields, HTML, call JavaScript functions, and more with a trivial amount of code. OpenThought strives to provide a simple yet powerful and flexible means for creating AJAX applications. The interface is simple -- you just build a hash. Hash keys are mapped to field names or id tags in the HTML. The value your hash keys contain is dynamically inserted into the corresponding field (without reloading the page). ==head1 COMPATABILITY OpenThought is compatible with a wide range of browsers, including Internet Explorer 4+, Netscape 4+, Mozilla/Firefox, Safari, Opera, Konqeueror, and others. It detects the browsers capabilities; if the browser doesn't support new functions such as XMLHttpRequest or XMLHTTP, it falls back to using iframes. =head1 METHODS =cut use strict; use Carp; $OpenThought::VERSION="1.99.17"; $OpenThought::DEBUG ||= 0; use vars qw( $DEBUG ); #/------------------------------------------------------------------------- # function: new # =pod =over 4 =item new() $OT = OpenThought->new(); Creates a new OpenThought object. =item Return Value =over 4 =item $OT OpenThought object. =back =back =cut # The main OpenThought constructor sub new { my ( $pkg, $args ) = @_; $args ||= {}; my $class = ref $pkg || $pkg; my $self = { %{ $args }, _persist => $args->{persist} || 0, }; bless ($self, $class); $self->_init(); return $self; } sub _init { my $self = shift; my @settings = qw( log_enabled log_level require channel_type channel_visible channel_url_replace selectbox_max_width selectbox_trim_string selectbox_single_row_mode selectbox_multi_row_mode checkbox_true_value checkbox_false_value radio_null_selection_value data_mode ); delete $self->{_settings} if exists $self->{_settings}; foreach my $setting ( @settings ) { $self->{_settings}{$setting} = []; } $self->{_response} = []; } # Generate, in the proper order, the serialized params and settings sub output { my $self = shift; my ( $save, $serialized_data, $restore ); $save = $serialized_data = $restore = ""; my @settings; foreach my $setting ( keys %{ $self->{_settings} } ) { # There needs to be at least one passed in next unless scalar @{ $self->{_settings}{$setting} } > 0; $serialized_data .= join '', @{ $self->{_settings}{$setting} }; push @settings, $setting; } # Grab all the response data (params, focus, url, javascript, etc) $serialized_data .= join '', @{ $self->{_response} }; # Save/restore the current settings unless told to make them # persist unless( $self->{settings_persist} ) { if (@settings) { $save .= $self->_settings_save( @settings ); $restore .= $self->_settings_restore( @settings ); } } # Hands JavaScript code to the browser. The browser processes the data # automatically as we hand it over -- as far as the browser is concerned, # it is simply loading a new page now (but in the hidden frame). my $code = $self->_add_tags( "${save}${serialized_data}${restore}"); $DEBUG && carp $code ; return $code; } *auto_param = \¶m; *fields = \¶m; *html = \¶m; *images = \¶m; # The user has html, input fields, or images they want displayed in the browser sub param { my ( $self, $data, $options ) = @_; if (ref $data eq "ARRAY") { $options = $data->[1] || {}; $data = $data->[0] || {}; } $data = $self->_as_javascript( $data ); my $save = ""; my $restore = ""; if ($options) { $save = $self->_settings_save( keys %{ $options } ) . $self->settings($options, 1); $restore = $self->_settings_restore( keys %{ $options } ); } $data = "${save}parent.OpenThought.ServerResponse(${data});${restore}"; push @{ $self->{_response} }, $data; return $self->_add_tags($data); } # Calls the Focus function within the browser, which in turn takes the # cursor and puts it into a particular field sub focus { my ( $self, $field ) = @_; my $data = " parent.OpenThought.Focus('$field');"; push @{ $self->{_response} }, $data; return $self->_add_tags($data); } # Send Javascript code to be interpreted by the browser. This would often be # used to call a user defined Javascript function.. an example application of # this would be to use the dynapi Dynamic HTML API Library to create and # manipulate DHTML objects from the server. sub javascript { my ( $self, $javascript_code ) = @_; # NOTE: it really doesn't work to escape the JS! The developer needs to do # it themselves... my $data = " with (parent.document) { $javascript_code }"; push @{ $self->{_response} }, $data; return $self->_add_tags($data); } # Jump to a new page with this url sub url { my ( $self, $url ) = @_; unless ( $url ) { croak "You're missing the parameter to 'url'."; } my $javascript_code; if ( ref $url eq "ARRAY" ) { if ( $url->[0] ) { unless ( not ref $url->[0] ) { croak "The first element of the arrayref passed into 'url' should be a scalar containing the url."; } $javascript_code = "parent.OpenThought.FetchHtml('$url->[0]'"; } if ( $url->[1] ) { unless ( ref $url->[1] eq "HASH" ) { croak "The second element of the arrayref passed into 'url' is optional, but if supplied, must be a hashref."; } foreach my $param ( keys %{ $url->[1] } ) { if ( defined $url->[1]->{ $param } ) { $javascript_code .= ",'$param=$url->[1]{ $param }'"; } else { $javascript_code .= ",'$param'"; } } } } elsif ( not ref $url ) { $javascript_code = "parent.OpenThought.FetchHtml('$url'"; } else { croak "The 'url' method takes either a scalar containing the url, or an an arrayref containing both the url and a hashref with url parameters."; } $javascript_code .= ");"; push @{ $self->{_response} }, $javascript_code; return $self->_add_tags($javascript_code); } # Alter settings within the existing OpenThought Application sub settings { my ( $self, $settings, $return_only ) = @_; unless ( ref $settings eq "HASH" ){ croak "When you pass in settings, they need to be a hash " . 'reference. Either $OT->settings( \%settings ) or ' . '$OT->response( settings => \%settings ).'; } my $data; foreach my $name ( keys %{ $settings } ) { # Persist is special as well. It defines whether the settings # being sent are to remain for the life of this page, or just for this # current request. if( $name eq "settings_persist" ) { $self->{settings_persist} = $settings->{$name}; } # All other parameters are treated the same here elsif ( $self->{_settings}{$name} ) { my $setting = "parent.OpenThought.config.$name = \"" . # $self->_escape_javascript( $settings->{$name} ) . "\");"; $self->_escape_javascript( $settings->{$name} ) . "\";"; unless ($return_only) { push @{ $self->{_settings}{$name} }, $setting; } $data .= $setting; } else { carp "No such setting [$name]."; } } if ($return_only) { return $data; } else { return $self->_add_tags($data); } } =pod =over 4 =item param() $OT->param( \%data, [ \%settings ] ); Update input-type form field elements (text boxes, radio buttons, checkboxes, select lists, text areas, etc), HTML elements, as well as images an image attributes. This method accepts a hash reference containing keys which map to field names, html id's, and image names. The form element, html id, or image will be dynamically updated to contain the value found within the hash key. B