[Jifty-commit] r5294 - in jifty/trunk: . lib/Jifty lib/Jifty/Manual lib/Jifty/Plugin share/plugins/Jifty/Plugin/I18N/web/static/js share/plugins/Jifty/Plugin/Prototypism share/plugins/Jifty/Plugin/Prototypism/web share/plugins/Jifty/Plugin/Prototypism/web/static share/plugins/Jifty/Plugin/Prototypism/web/static/js share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous share/plugins/Jifty/Plugin/SinglePage share/plugins/Jifty/Plugin/SinglePage/web share/plugins/Jifty/Plugin/SinglePage/web/static share/plugins/Jifty/Plugin/SinglePage/web/static/js share/plugins/Jifty/Plugin/SinglePage/web/static/js/singlepage share/plugins/Jifty/Plugin/SinglePage/web/static/js/singlepage/rsh share/web/static/js share/web/static/js/scriptaculous share/web/templates/__jifty t/TestApp-JiftyJS/etc t/TestApp-JiftyJS/lib/TestApp/JiftyJS t/TestApp-JiftyJS/lib/TestApp/JiftyJS/Action t/TestApp-JiftyJS/lib/TestApp/JiftyJS/Model t/TestApp-JiftyJS/share/web/static/js t/TestApp-JiftyJS/share/web/static/js-test t/TestApp-JiftyJS/t t/TestApp-JiftyJS2 t/TestApp-JiftyJS2/bin t/TestApp-JiftyJS2/doc t/TestApp-JiftyJS2/etc t/TestApp-JiftyJS2/log t/TestApp-JiftyJS2/var t/TestApp-Plugin-OnClick/etc t/TestApp-Plugin-OnClick/t t/TestApp-Plugin-SinglePage/lib/TestApp/Plugin/SinglePage t/TestApp-Plugin-SinglePage/t
Jifty commits
jifty-commit at lists.jifty.org
Wed Apr 9 00:13:37 EDT 2008
Author: hlb
Date: Wed Apr 9 00:12:34 2008
New Revision: 5294
Added:
jifty/trunk/lib/Jifty/Manual/JavaScript.pod
jifty/trunk/lib/Jifty/Manual/jQueryMigrationGuide.pod
jifty/trunk/lib/Jifty/Plugin/Prototypism.pm
jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/
jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/
jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/
jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/
jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/
jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/jifty_compatible.js (contents, props changed)
jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/prototype.js (contents, props changed)
jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/
jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/builder.js (contents, props changed)
jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/controls.js (contents, props changed)
jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/dragdrop.js (contents, props changed)
jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/effects.js (contents, props changed)
jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/scriptaculous.js (contents, props changed)
jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/slider.js (contents, props changed)
jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/unittest.js (contents, props changed)
jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/
jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/web/
jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/web/static/
jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/web/static/js/
jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/web/static/js/singlepage/
jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/web/static/js/singlepage/rsh/
jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/web/static/js/singlepage/rsh/LICENSE.txt
jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/web/static/js/singlepage/rsh/blank.html
jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/web/static/js/singlepage/rsh/rsh.js
jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/web/static/js/singlepage/spa.js
jifty/trunk/share/web/static/js/iautocompleter.js (contents, props changed)
jifty/trunk/share/web/static/js/iutil.js (contents, props changed)
jifty/trunk/share/web/static/js/jifty_interface.js (contents, props changed)
jifty/trunk/t/TestApp-JiftyJS/lib/TestApp/JiftyJS/Action/Play2.pm
jifty/trunk/t/TestApp-JiftyJS/lib/TestApp/JiftyJS/Dispatcher.pm
jifty/trunk/t/TestApp-JiftyJS/lib/TestApp/JiftyJS/Model/Offer.pm
jifty/trunk/t/TestApp-JiftyJS/share/web/static/js/
jifty/trunk/t/TestApp-JiftyJS/share/web/static/js/dict/
jifty/trunk/t/TestApp-JiftyJS/share/web/static/js/dict/en.json
jifty/trunk/t/TestApp-JiftyJS/share/web/static/js/dict/en_us.json
jifty/trunk/t/TestApp-JiftyJS/share/web/static/js/dict/zh_tw.json
jifty/trunk/t/TestApp-JiftyJS/t/00-action-Play2.t
jifty/trunk/t/TestApp-JiftyJS/t/00-model-Offer.t
jifty/trunk/t/TestApp-JiftyJS/t/6-offer-actions.t
jifty/trunk/t/TestApp-JiftyJS/t/7-redirect.t
jifty/trunk/t/TestApp-JiftyJS/t/8-placeholder.t
jifty/trunk/t/TestApp-JiftyJS2/
jifty/trunk/t/TestApp-JiftyJS2/Makefile.PL
jifty/trunk/t/TestApp-JiftyJS2/bin/
jifty/trunk/t/TestApp-JiftyJS2/bin/jifty (contents, props changed)
jifty/trunk/t/TestApp-JiftyJS2/doc/
jifty/trunk/t/TestApp-JiftyJS2/etc/
jifty/trunk/t/TestApp-JiftyJS2/etc/config.yml
jifty/trunk/t/TestApp-JiftyJS2/lib (contents, props changed)
jifty/trunk/t/TestApp-JiftyJS2/log/
jifty/trunk/t/TestApp-JiftyJS2/share (contents, props changed)
jifty/trunk/t/TestApp-JiftyJS2/t (contents, props changed)
jifty/trunk/t/TestApp-JiftyJS2/var/
jifty/trunk/t/TestApp-Plugin-SinglePage/t/history.t
Removed:
jifty/trunk/share/web/static/js/prototype.js
jifty/trunk/share/web/static/js/scriptaculous/
Modified:
jifty/trunk/ (props changed)
jifty/trunk/lib/Jifty/Config.pm
jifty/trunk/lib/Jifty/Plugin/Halo.pm
jifty/trunk/lib/Jifty/Plugin/SinglePage.pm
jifty/trunk/lib/Jifty/Web.pm
jifty/trunk/share/plugins/Jifty/Plugin/I18N/web/static/js/loc.js
jifty/trunk/share/web/static/js/bps_util.js
jifty/trunk/share/web/static/js/calendar.js
jifty/trunk/share/web/static/js/halo.js
jifty/trunk/share/web/static/js/jifty.js
jifty/trunk/share/web/static/js/jifty_subs.js
jifty/trunk/share/web/static/js/jifty_utils.js
jifty/trunk/share/web/static/js/key_bindings.js
jifty/trunk/share/web/templates/__jifty/halo
jifty/trunk/t/TestApp-JiftyJS/etc/config.yml
jifty/trunk/t/TestApp-JiftyJS/lib/TestApp/JiftyJS/Action/Play.pm
jifty/trunk/t/TestApp-JiftyJS/lib/TestApp/JiftyJS/View.pm
jifty/trunk/t/TestApp-JiftyJS/share/web/static/js-test/02.action.html
jifty/trunk/t/TestApp-JiftyJS/t/1-jifty-update.t
jifty/trunk/t/TestApp-JiftyJS/t/2-behaviour.t
jifty/trunk/t/TestApp-JiftyJS/t/4-tangent.t
jifty/trunk/t/TestApp-JiftyJS/t/5-action.t
jifty/trunk/t/TestApp-Plugin-OnClick/etc/config.yml
jifty/trunk/t/TestApp-Plugin-OnClick/t/onclick.t
jifty/trunk/t/TestApp-Plugin-SinglePage/lib/TestApp/Plugin/SinglePage/View.pm
Log:
merge jQuery branch into trunk
Modified: jifty/trunk/lib/Jifty/Config.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Config.pm (original)
+++ jifty/trunk/lib/Jifty/Config.pm Wed Apr 9 00:12:34 2008
@@ -346,7 +346,7 @@
sub initial_config {
my $self = shift;
my $guess = $self->guess(@_);
- $guess->{'framework'}->{'ConfigFileVersion'} = 3;
+ $guess->{'framework'}->{'ConfigFileVersion'} = 4;
# These are the plugins which new apps will get by default
$guess->{'framework'}->{'Plugins'} = [
@@ -396,6 +396,12 @@
);
}
+ if ( $config->{'framework'}->{'ConfigFileVersion'} < 4) {
+ unshift (@{$config->{'framework'}->{'Plugins'}},
+ { Prototypism => {}, }
+ );
+ }
+
return $config;
}
Added: jifty/trunk/lib/Jifty/Manual/JavaScript.pod
==============================================================================
--- (empty file)
+++ jifty/trunk/lib/Jifty/Manual/JavaScript.pod Wed Apr 9 00:12:34 2008
@@ -0,0 +1,224 @@
+
+=head1 NAME
+
+JavaScript programming guide for Jifty
+
+=head1 DESCRIPTION
+
+jQuery took over Prototype and becoming the core of Jifty's Javascript
+development. Besides re-implement core javascript libraries with
+jQuery, some good refactor is also being done.
+
+This document is written to help JavaScript programmers working for
+a Jifty project to understand what's the different before jQuery landed,
+and provide a quick reference for prototypism believers to learn the new wave
+of JavaScript programming in Jifty.
+
+=head1 Migration to jQuery
+
+This section provides a simple guide through jQuery's core functions
+that's used to replace Prototype javascript library.
+
+=head2 Selecting elements with jQuery()
+
+Invokin jQuery function with exactly one string argument will return
+a jQuery object that represents a list of elements. The string is
+a CSS selector. For example:
+
+ jQuery("span.message")
+
+This works very similar to Prototype's $$() function, but with one
+difference. The return value is I<not> an Array, it's a jQuery
+object that acts likes a Enumerable object (but still, not one). If you
+really want a Array, you can do:
+
+ my array_of_message = jQuery("span.message").get()
+
+For most cases, C<jQuery("#" + id).get(0)> can be a replacement pattern
+to C<$(id)>. Selecting elements with C<jQuery()> function always
+returns a jQuery object, but not element it self. There are two notice
+especially for Jifty world.
+
+First of all, Jifty develoeprs should always use C<Jifty.$>. Deep in
+the design of Jifty, there are many kind of elements with C<":">
+character in their id. Sadly it is a feature in jQuery to do more
+powerful seleciton with C<":"> character. For example, this selects
+current mouse-overed elements:
+
+ jQuery(":hover")
+
+C<jifty.js> internally use C<Jifty.$> as the direct replacement to
+C<$()> function defined in the Prototype library.
+
+However, for application developers it's quite safe to use
+C<jQuery("#id")> to select elements they created.
+
+=head2 Document ready with jQuery()
+
+The way to do execute some javascript right after the DOM is ready is
+to bind a handler for C<"ready"> event on the C<document> object:
+
+ jQuery(document).ready(function() {
+ ...
+ });
+
+Since is done quite often, there's a shortcut:
+
+ jQuery(function() {
+ ...
+ });
+
+=head1 METHODS
+
+This section list those public functions under C<Jifty> namespace.
+They are defined in jifty.js.
+
+=over
+
+=item Jifty.$( element_id )
+
+This is a shorthand of document.getElementById function, like the C<$>
+function defined in Prototype library. It is also internally used a
+lot because many form specific element ID does not get along with
+jQuery's selector, which expect the ":" character is used for special
+purpose.
+
+element_id should be a string. If not, element_id itself is returned.
+Therefore, this convention:
+
+ element = Jifty.$(element)
+
+Can work when the variable C<element> is either a string, or a HTML
+element object.
+
+=item Jifty.Effect(element, effect_name, option)
+
+When called, instantly pefrom a js effect on give element. "element" is an
+element object.
+
+The last arg "option" is a hash. Currently it's only used for
+specificing callbacks. There are two possible callbacks, before and
+after. You may specify them like this:
+
+ Jifty.Effect(element, "Fade", { duration: 2.0 }, {
+ before: function() { ... },
+ after: function() { ... }
+ });
+
+The "before" callback is called right before the effect starts.
+The "after" callback is called right after it's started, but not
+necessarily ended.
+
+This function is written to make it possible that a Jifty plugin
+can override default effects with other fancy javascript libraries.
+By default, it delegates all the real work to jQuery's built-in
+effect functions.
+
+=item Jifty.Form.getElements(element)
+
+Given a form element, returns all input fields inside this form. That
+includes INPUT, SELECT, tags. The returned value is an array of HTML
+elements.
+
+=item Jifty.Form.getActions(element)
+
+Given a form element, returns a array of elements that are defined as
+Jifty actions.
+
+=item Jifty.Form.clearPlaceholders(element)
+
+=item Jifty.Form.Element.getMoniker( element )
+
+Given an element, or an element id, return a string representing a
+moniker of this element. It returs null if the given element is
+considered having no moniker at all.
+
+=item Jifty.Form.Element.getAction( element )
+
+Takes an element or an element id. Get the action for this form
+element. The returned value is an Action object.
+
+=item Jifty.Form.Element.getType( element )
+
+Takes an element or an element id, returns the type associated with
+this element. Possible return values are "registraion", "value",
+"fallback", or null if the element does not belongs to any of these
+types.
+
+=item Jifty.Form.Element.getField( element )
+
+Takes an element or an element id, returns the name of it. Returns
+null if the element given does not have a name.
+
+=item Jifty.Form.Element.getValue( element )
+
+Tkaes an element or an element id, returns the element value. If the
+element is a CHECKBOX or a RADIO button but it's un-checked, returns
+null.
+
+=item Jifty.Form.Element.validate( element )
+
+Validates the action this form element is part of.
+
+=item Jifty.Form.Element.disableValidation( element )
+
+Temporarily disable validation.
+
+=item Jifty.Form.Element.enableValidation( element )
+
+Re-enable validation.
+
+=item Jifty.Form.Element.getForm( element )
+
+Look up the form that this element is part of.
+
+This is sometimes more complicated than you'd think because the form
+may not exist anymore, or the element may have been inserted into a
+new form. Hence, we may need to walk the DOM.
+
+Upon the failure of searching, null is returned.
+
+=item Jifty.Form.Element.buttonArguments( element )
+
+Takes an element or an element id that is considered as a "button",
+which can be an <INPUT type="submit"> tag, or a <A> tag, returns the
+arguments on this element.
+
+If none, an empty object is returned.
+
+=item Jifty.Form.Element.buttonActions( element )
+
+Takes an element or an element id that is considered as a "button",
+return array of actions on this element.
+
+If none, an empty array is returned.
+
+=item Jifty.Form.Element.buttonFormElements( element )
+
+Takes an element or an element id that is considered as a "button",
+return an array of form elements that's just constructed based on the
+arguments on this element.
+
+If none, an empty array is returned.
+
+=item Jifty.Form.Element.clickDefaultButton( element )
+
+Click the first button that will submit the action associated with the
+form element.
+
+=item Jifty.Form.Element.handleEnter( event )
+
+Trap "Enter" key, and prevent it from doing any browser default
+behaviours.
+
+=back
+
+=head1 REFERENCE
+
+L<http://www.slideshare.net/simon/jquery-in-15-minutes/>
+L<http://www.slideshare.net/simon/learning-jquery-in-30-minutes/>
+L<http://www.slideshare.net/remy.sharp/prototype-jquery-going-from-one-to-the-other/>
+L<http://docs.jquery.com/>
+
+=cut
+
Added: jifty/trunk/lib/Jifty/Manual/jQueryMigrationGuide.pod
==============================================================================
--- (empty file)
+++ jifty/trunk/lib/Jifty/Manual/jQueryMigrationGuide.pod Wed Apr 9 00:12:34 2008
@@ -0,0 +1,136 @@
+=head1 NAME
+
+jQueryMigrationGuide - How to migrate your code to use jQuery.
+
+=head1 Migrate your jifty app to jquery
+
+Application developers may start the migration by modifying
+F<config.yml>, set the C<ConfigFileVersion> to 4. If you did not write
+any custom javascript code for your app, then its done. Everything
+should just work.
+
+If you did wrote some javascript code before, but you did not use any
+of those funcitons defined in F<jifty*.js>, F<prototype.js> or
+F<scriptaculous.js>, then you're still good to go.
+
+Otherwise, your code might need to be modified a little bit. Since
+both F<prototype.js> and F<scriptaculous.js> are removed by default,
+one trivial choice is to simply bring them back. That is as easy as
+adding a B<Prototypism> plugin to your Jifty application.
+
+If you dislike the whole Prototypism like us, you can choose to
+re-write your code with jQuery. In the section L</"From Prototype
+to jQuery"> below, we provide some known pattern that can be applied to
+rewrite prototypism code with jQuery, or with just normal javascript.
+
+If you hack Jifty internal, please make sure you've read the following
+L</"Jifty API"> section and L<Jifty::Manual::JavaScript> to catch the
+Javascript API update since the removal of C<prototype.js>.
+
+Although we've removed C<prototype.js>, we still prefer to use
+non-conflict mode of jQuery at this moment. That is, C<$> function is
+now undefined, but not an alias of jQuery. This is to ensure that it's
+not conflicting with Prototypism at all conditions. If you'd like
+to use C<$> function, create that alias in your C<app.js> like this:
+
+ $ = jQuery;
+
+However, instead of making a global alias, it's always recommended to
+always make this alias with a closure to really localize it.
+
+ (function($) {
+ // $ is an alias to jQuery to the end of this closure
+
+ $(".message").show();
+ })(jQuery);
+
+=head1 Jifty API
+
+Jifty javascript libraries embraced a major re-architect after jQuery
+landed. Especially those internal functions to process form elements.
+
+The Prototype-based old way is to extend Form object and the
+Form.Element object. Since the removal of Prototype library, it is
+dangerous to name those functions under Form. Because loading
+Prototype library can destroy those Jifty functions.
+
+The new jQuery-fashioned way is to always extend internal functions
+under Jifty object. C<Form becomes C<Jifty.Form>, C<Form.Element> becomes
+C<Jifty.Form.Element>, and so on. The detail list of these defined
+functions are given in L<Jifty::Manual::Javascript>. Most of
+those functions are internal functions that you probably should not
+use them directly.
+
+=head1 From Prototype to jQuery
+
+If you've ever written javascript code on your Jifty applications, and
+you'd like to remove PrototypeJS library, here are some dummy rules to
+re-write prototypejs-based javascript code with jQuery.
+
+=head3 Array iteration
+
+From:
+
+ A.each( function( $_ ) { ... } )
+
+To:
+
+ jQuery.each(A, function(index, value ) {
+ // "this" is an alias to current value.
+ })
+
+=head2 Hash key iteration
+
+From:
+
+ H = new Hash({...});
+
+ H.each(function( pair ) {
+ // pair.key is the key
+ // pair.value is the value
+ });
+
+jQuery.each is designed to work on both C<Array> and C<Object> in the
+same way. So there's not much difference.
+
+To:
+
+ // H can be any kind of "Object"
+
+ jQuery.each(H, function(key, value) {
+ // "this" is an alias to current value.
+ })
+
+=head2 Object extend
+
+From:
+
+ obj.extend({ ... }}
+
+To:
+
+ jQuery.extend( obj, { ... } )
+
+=head2 JSON
+
+jQuery does not build-in with JSON stringify function, but since it
+neither altered the native Array, nor defined its own Hash, it's
+prefered and fine to just use C<JSON.stringify> from C<json.js>.
+
+From:
+
+ // obj need to be one of those objects defined in C<prototype.js>
+ obj.toJSON();
+
+To:
+
+ JSON.stringify( obj )
+
+=head2 Effects
+
+jQuery has a small set of default effects built-in to its core. They
+have different naming then those defined in C<scriptaculous.js>. The
+internal way to do effect is via the C<Jifty.Effect> method. Please
+see the detail usage in L<Jifty::Manual::JavaScript>.
+
+=cut
Modified: jifty/trunk/lib/Jifty/Plugin/Halo.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Plugin/Halo.pm (original)
+++ jifty/trunk/lib/Jifty/Plugin/Halo.pm Wed Apr 9 00:12:34 2008
@@ -198,7 +198,7 @@
my $expanded = Jifty->web->serial;
my $yaml = Jifty->web->escape(Jifty::YAML::Dump($value));
#$out .= qq{<a href="#" onclick="Element.toggle('$expanded'); return false">$ref</a><div id="$expanded" style="display: none; position: absolute; left: 200px; border: 1px solid black; background: #ccc; padding: 1em; padding-top: 0; width: 300px; height: 500px; overflow: auto"><pre>$yaml</pre></div>};
- $out .= qq{<a href="#" onclick="Element.toggle('$expanded'); return false">$ref</a><div id="$expanded" class="halo-argument" style="display: none"><pre>$yaml</pre></div>};
+ $out .= qq{<a href="#" onclick="jQuery(Jifty.$('$expanded')).toggle()); return false">$ref</a><div id="$expanded" class="halo-argument" style="display: none"><pre>$yaml</pre></div>};
}
elsif (defined $value) {
$out .= Jifty->web->escape($value);
Added: jifty/trunk/lib/Jifty/Plugin/Prototypism.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/lib/Jifty/Plugin/Prototypism.pm Wed Apr 9 00:12:34 2008
@@ -0,0 +1,42 @@
+use strict;
+use warnings;
+
+package Jifty::Plugin::Prototypism;
+use base 'Jifty::Plugin';
+
+=head1 NAME
+
+Jifty::Plugin::Prototypism
+
+=head1 SYNOPSIS
+
+# In your jifty config.yml under the framework section:
+
+ Plugins:
+ - Prototypism
+ cdn: 'http://yourcdn.for.static.prefix/'
+
+=cut
+
+__PACKAGE__->mk_accessors(qw(cdn));
+
+sub init {
+ my $self = shift;
+ return if $self->_pre_init;
+
+ my %opt = @_;
+ $self->cdn( $opt{ cdn } || '' );
+ my @js = qw(
+ prototype
+ scriptaculous/builder
+ scriptaculous/effects
+ scriptaculous/controls
+ );
+
+ push @js, 'jifty_compatible' if Jifty->config->framework('ConfigFileVersion') < 4;
+ Jifty->web->add_javascript( "prototypism/$_.js" ) for @js;
+
+}
+
+1;
+
Modified: jifty/trunk/lib/Jifty/Plugin/SinglePage.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Plugin/SinglePage.pm (original)
+++ jifty/trunk/lib/Jifty/Plugin/SinglePage.pm Wed Apr 9 00:12:34 2008
@@ -20,6 +20,11 @@
=cut
+Jifty->web->add_javascript(
+ 'singlepage/rsh/rsh.js',
+ 'singlepage/spa.js'
+);
+
sub init {
my $self = shift;
return if $self->_pre_init;
@@ -61,12 +66,13 @@
$self->_push_onclick($args, {
region => $self->region_name,
replace_with => $url,
+ beforeclick => qq{SPA.historyChange('$url', { 'continuation':{}, 'actions':{}, 'fragments':[{'mode':'Replace','args':@{[ Jifty::JSON::objToJson($args->{parameters})]},'region':'__page','path':'$url'}],'action_arguments':{}}, true);},
args => { %{$args->{parameters}}} });
}
elsif (exists $args->{submit} && !$args->{onclick}) {
if ($args->{_form} && $args->{_form}{submit_to}) {
my $to = $args->{_form}{submit_to};
- $self->_push_onclick($args, { beforeclick => qq{return _sp_submit_form(this, event, "$to");} });
+ $self->_push_onclick($args, { beforeclick => qq{return SPA._sp_submit_form(this, event, "$to");} });
}
else {
$self->_push_onclick($args, { refresh_self => 1, submit => $args->{submit} });
Modified: jifty/trunk/lib/Jifty/Web.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Web.pm (original)
+++ jifty/trunk/lib/Jifty/Web.pm Wed Apr 9 00:12:34 2008
@@ -38,13 +38,12 @@
jsan/Upgrade/Array/push.js
jsan/DOM/Events.js
json.js
- prototype.js
jquery-1.2.1.js
+ iutil.js
+ iautocompleter.js
+ jifty_interface.js
jquery_noconflict.js
behaviour.js
- scriptaculous/builder.js
- scriptaculous/effects.js
- scriptaculous/controls.js
formatDate.js
template_declare.js
jifty.js
@@ -58,7 +57,6 @@
key_bindings.js
context_menu.js
bps_util.js
- rico.js
yui/yahoo.js
yui/dom.js
yui/event.js
Modified: jifty/trunk/share/plugins/Jifty/Plugin/I18N/web/static/js/loc.js
==============================================================================
--- jifty/trunk/share/plugins/Jifty/Plugin/I18N/web/static/js/loc.js (original)
+++ jifty/trunk/share/plugins/Jifty/Plugin/I18N/web/static/js/loc.js Wed Apr 9 00:12:34 2008
@@ -1,4 +1,4 @@
-Localization = Object.extend(new Object(), {
+Localization = {
init: function(params) {
this.lang = params.lang || 'en';
if (params["dict_path"]) {
@@ -10,22 +10,21 @@
this.dict = this.load_dict(lang);
},
load_dict: function(lang) {
- var d;
- new Ajax.Request(
- this.dict_path + "/" + lang + ".json",
- {
- method: 'get',
- asynchronous: false,
- onComplete: function(t, obj) {
- eval("d = " + t.responseText || "{}");
- }
+ var d = {};
+ jQuery.ajax({
+ url: this.dict_path + "/" + lang + ".json",
+ type: 'get',
+ asynchronous: fasle,
+ success: function(dict) {
+ eval("d = " + dict || "{}");
}
- );
+ });
+
return d;
},
loc: function(str) {
var dict = this.dict;
- if (dict[str]) {
+ if (dict[str] != null) {
return dict[str];
}
return str;
@@ -58,8 +57,10 @@
if (minutes < 2880) return _('about one day');
else return (Math.round(minutes / 1440) + _(' days'))
}
-
-});
+};
Localization.dict = {};
-_ = Localization.loc.bind(Localization);
+
+window._ = function() {
+ return Localization.loc.apply(Localization, arguments);
+}
Added: jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/jifty_compatible.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/jifty_compatible.js Wed Apr 9 00:12:34 2008
@@ -0,0 +1,33 @@
+// Compatible notice for old school
+
+(function($) {
+ $.each(
+ Jifty.Form,
+ function(k, v) {
+ if ( $.isFunction(v) && Form[k] == null ) {
+ Form[k] = function() {
+ alert("Form." + k +
+ " is going to be depcreated. Please use Jifty.Form." + k +
+ " instead.");
+ v.apply(Jifty.Form, arguments);
+ }
+ }
+ }
+ );
+
+ $.each(
+ Jifty.Form.Element,
+ function(k, v) {
+ if ( $.isFunction(v) && Form.Element[k] == null ) {
+ Form.Element[k] = function() {
+ alert("Form.Element" + k +
+ " is going to be depcreated. Please use Jifty.Form.Element" + k +
+ " instead.");
+ v.apply(Jifty.Form.Element, arguments);
+ }
+ }
+ }
+ );
+
+})(jQuery);
+
Added: jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/prototype.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/prototype.js Wed Apr 9 00:12:34 2008
@@ -0,0 +1,4204 @@
+/* Prototype JavaScript framework, version 1.6.0
+ * (c) 2005-2007 Sam Stephenson
+ *
+ * Prototype is freely distributable under the terms of an MIT-style license.
+ * For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+ *--------------------------------------------------------------------------*/
+
+var Prototype = {
+ Version: '1.6.0',
+
+ Browser: {
+ IE: !!(window.attachEvent && !window.opera),
+ Opera: !!window.opera,
+ WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+ Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
+ MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
+ },
+
+ BrowserFeatures: {
+ XPath: !!document.evaluate,
+ ElementExtensions: !!window.HTMLElement,
+ SpecificElementExtensions:
+ document.createElement('div').__proto__ &&
+ document.createElement('div').__proto__ !==
+ document.createElement('form').__proto__
+ },
+
+ ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
+ JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
+
+ emptyFunction: function() { },
+ K: function(x) { return x }
+};
+
+if (Prototype.Browser.MobileSafari)
+ Prototype.BrowserFeatures.SpecificElementExtensions = false;
+
+if (Prototype.Browser.WebKit)
+ Prototype.BrowserFeatures.XPath = false;
+
+/* Based on Alex Arnell's inheritance implementation. */
+var Class = {
+ create: function() {
+ var parent = null, properties = $A(arguments);
+ if (Object.isFunction(properties[0]))
+ parent = properties.shift();
+
+ function klass() {
+ this.initialize.apply(this, arguments);
+ }
+
+ Object.extend(klass, Class.Methods);
+ klass.superclass = parent;
+ klass.subclasses = [];
+
+ if (parent) {
+ var subclass = function() { };
+ subclass.prototype = parent.prototype;
+ klass.prototype = new subclass;
+ parent.subclasses.push(klass);
+ }
+
+ for (var i = 0; i < properties.length; i++)
+ klass.addMethods(properties[i]);
+
+ if (!klass.prototype.initialize)
+ klass.prototype.initialize = Prototype.emptyFunction;
+
+ klass.prototype.constructor = klass;
+
+ return klass;
+ }
+};
+
+Class.Methods = {
+ addMethods: function(source) {
+ var ancestor = this.superclass && this.superclass.prototype;
+ var properties = Object.keys(source);
+
+ if (!Object.keys({ toString: true }).length)
+ properties.push("toString", "valueOf");
+
+ for (var i = 0, length = properties.length; i < length; i++) {
+ var property = properties[i], value = source[property];
+ if (ancestor && Object.isFunction(value) &&
+ value.argumentNames().first() == "$super") {
+ var method = value, value = Object.extend((function(m) {
+ return function() { return ancestor[m].apply(this, arguments) };
+ })(property).wrap(method), {
+ valueOf: function() { return method },
+ toString: function() { return method.toString() }
+ });
+ }
+ this.prototype[property] = value;
+ }
+
+ return this;
+ }
+};
+
+var Abstract = { };
+
+Object.extend = function(destination, source) {
+ for (var property in source)
+ destination[property] = source[property];
+ return destination;
+};
+
+Object.extend(Object, {
+ inspect: function(object) {
+ try {
+ if (object === undefined) return 'undefined';
+ if (object === null) return 'null';
+ return object.inspect ? object.inspect() : object.toString();
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+ },
+
+ toJSON: function(object) {
+ var type = typeof object;
+ switch (type) {
+ case 'undefined':
+ case 'function':
+ case 'unknown': return;
+ case 'boolean': return object.toString();
+ }
+
+ if (object === null) return 'null';
+ if (object.toJSON) return object.toJSON();
+ if (Object.isElement(object)) return;
+
+ var results = [];
+ for (var property in object) {
+ var value = Object.toJSON(object[property]);
+ if (value !== undefined)
+ results.push(property.toJSON() + ': ' + value);
+ }
+
+ return '{' + results.join(', ') + '}';
+ },
+
+ toQueryString: function(object) {
+ return $H(object).toQueryString();
+ },
+
+ toHTML: function(object) {
+ return object && object.toHTML ? object.toHTML() : String.interpret(object);
+ },
+
+ keys: function(object) {
+ var keys = [];
+ for (var property in object)
+ keys.push(property);
+ return keys;
+ },
+
+ values: function(object) {
+ var values = [];
+ for (var property in object)
+ values.push(object[property]);
+ return values;
+ },
+
+ clone: function(object) {
+ return Object.extend({ }, object);
+ },
+
+ isElement: function(object) {
+ return object && object.nodeType == 1;
+ },
+
+ isArray: function(object) {
+ return object && object.constructor === Array;
+ },
+
+ isHash: function(object) {
+ return object instanceof Hash;
+ },
+
+ isFunction: function(object) {
+ return typeof object == "function";
+ },
+
+ isString: function(object) {
+ return typeof object == "string";
+ },
+
+ isNumber: function(object) {
+ return typeof object == "number";
+ },
+
+ isUndefined: function(object) {
+ return typeof object == "undefined";
+ }
+});
+
+Object.extend(Function.prototype, {
+ argumentNames: function() {
+ var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
+ return names.length == 1 && !names[0] ? [] : names;
+ },
+
+ bind: function() {
+ if (arguments.length < 2 && arguments[0] === undefined) return this;
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function() {
+ return __method.apply(object, args.concat($A(arguments)));
+ }
+ },
+
+ bindAsEventListener: function() {
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function(event) {
+ return __method.apply(object, [event || window.event].concat(args));
+ }
+ },
+
+ curry: function() {
+ if (!arguments.length) return this;
+ var __method = this, args = $A(arguments);
+ return function() {
+ return __method.apply(this, args.concat($A(arguments)));
+ }
+ },
+
+ delay: function() {
+ var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
+ return window.setTimeout(function() {
+ return __method.apply(__method, args);
+ }, timeout);
+ },
+
+ wrap: function(wrapper) {
+ var __method = this;
+ return function() {
+ return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
+ }
+ },
+
+ methodize: function() {
+ if (this._methodized) return this._methodized;
+ var __method = this;
+ return this._methodized = function() {
+ return __method.apply(null, [this].concat($A(arguments)));
+ };
+ }
+});
+
+Function.prototype.defer = Function.prototype.delay.curry(0.01);
+
+Date.prototype.toJSON = function() {
+ return '"' + this.getUTCFullYear() + '-' +
+ (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
+ this.getUTCDate().toPaddedString(2) + 'T' +
+ this.getUTCHours().toPaddedString(2) + ':' +
+ this.getUTCMinutes().toPaddedString(2) + ':' +
+ this.getUTCSeconds().toPaddedString(2) + 'Z"';
+};
+
+var Try = {
+ these: function() {
+ var returnValue;
+
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) { }
+ }
+
+ return returnValue;
+ }
+};
+
+RegExp.prototype.match = RegExp.prototype.test;
+
+RegExp.escape = function(str) {
+ return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+};
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create({
+ initialize: function(callback, frequency) {
+ this.callback = callback;
+ this.frequency = frequency;
+ this.currentlyExecuting = false;
+
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ execute: function() {
+ this.callback(this);
+ },
+
+ stop: function() {
+ if (!this.timer) return;
+ clearInterval(this.timer);
+ this.timer = null;
+ },
+
+ onTimerEvent: function() {
+ if (!this.currentlyExecuting) {
+ try {
+ this.currentlyExecuting = true;
+ this.execute();
+ } finally {
+ this.currentlyExecuting = false;
+ }
+ }
+ }
+});
+Object.extend(String, {
+ interpret: function(value) {
+ return value == null ? '' : String(value);
+ },
+ specialChar: {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '\\': '\\\\'
+ }
+});
+
+Object.extend(String.prototype, {
+ gsub: function(pattern, replacement) {
+ var result = '', source = this, match;
+ replacement = arguments.callee.prepareReplacement(replacement);
+
+ while (source.length > 0) {
+ if (match = source.match(pattern)) {
+ result += source.slice(0, match.index);
+ result += String.interpret(replacement(match));
+ source = source.slice(match.index + match[0].length);
+ } else {
+ result += source, source = '';
+ }
+ }
+ return result;
+ },
+
+ sub: function(pattern, replacement, count) {
+ replacement = this.gsub.prepareReplacement(replacement);
+ count = count === undefined ? 1 : count;
+
+ return this.gsub(pattern, function(match) {
+ if (--count < 0) return match[0];
+ return replacement(match);
+ });
+ },
+
+ scan: function(pattern, iterator) {
+ this.gsub(pattern, iterator);
+ return String(this);
+ },
+
+ truncate: function(length, truncation) {
+ length = length || 30;
+ truncation = truncation === undefined ? '...' : truncation;
+ return this.length > length ?
+ this.slice(0, length - truncation.length) + truncation : String(this);
+ },
+
+ strip: function() {
+ return this.replace(/^\s+/, '').replace(/\s+$/, '');
+ },
+
+ stripTags: function() {
+ return this.replace(/<\/?[^>]+>/gi, '');
+ },
+
+ stripScripts: function() {
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+ },
+
+ extractScripts: function() {
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+ var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+ return (this.match(matchAll) || []).map(function(scriptTag) {
+ return (scriptTag.match(matchOne) || ['', ''])[1];
+ });
+ },
+
+ evalScripts: function() {
+ return this.extractScripts().map(function(script) { return eval(script) });
+ },
+
+ escapeHTML: function() {
+ var self = arguments.callee;
+ self.text.data = this;
+ return self.div.innerHTML;
+ },
+
+ unescapeHTML: function() {
+ var div = new Element('div');
+ div.innerHTML = this.stripTags();
+ return div.childNodes[0] ? (div.childNodes.length > 1 ?
+ $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+ div.childNodes[0].nodeValue) : '';
+ },
+
+ toQueryParams: function(separator) {
+ var match = this.strip().match(/([^?#]*)(#.*)?$/);
+ if (!match) return { };
+
+ return match[1].split(separator || '&').inject({ }, function(hash, pair) {
+ if ((pair = pair.split('='))[0]) {
+ var key = decodeURIComponent(pair.shift());
+ var value = pair.length > 1 ? pair.join('=') : pair[0];
+ if (value != undefined) value = decodeURIComponent(value);
+
+ if (key in hash) {
+ if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
+ hash[key].push(value);
+ }
+ else hash[key] = value;
+ }
+ return hash;
+ });
+ },
+
+ toArray: function() {
+ return this.split('');
+ },
+
+ succ: function() {
+ return this.slice(0, this.length - 1) +
+ String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+ },
+
+ times: function(count) {
+ return count < 1 ? '' : new Array(count + 1).join(this);
+ },
+
+ camelize: function() {
+ var parts = this.split('-'), len = parts.length;
+ if (len == 1) return parts[0];
+
+ var camelized = this.charAt(0) == '-'
+ ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+ : parts[0];
+
+ for (var i = 1; i < len; i++)
+ camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+ return camelized;
+ },
+
+ capitalize: function() {
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+ },
+
+ underscore: function() {
+ return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+ },
+
+ dasherize: function() {
+ return this.gsub(/_/,'-');
+ },
+
+ inspect: function(useDoubleQuotes) {
+ var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+ var character = String.specialChar[match[0]];
+ return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+ });
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+ },
+
+ toJSON: function() {
+ return this.inspect(true);
+ },
+
+ unfilterJSON: function(filter) {
+ return this.sub(filter || Prototype.JSONFilter, '#{1}');
+ },
+
+ isJSON: function() {
+ var str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
+ return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
+ },
+
+ evalJSON: function(sanitize) {
+ var json = this.unfilterJSON();
+ try {
+ if (!sanitize || json.isJSON()) return eval('(' + json + ')');
+ } catch (e) { }
+ throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+ },
+
+ include: function(pattern) {
+ return this.indexOf(pattern) > -1;
+ },
+
+ startsWith: function(pattern) {
+ return this.indexOf(pattern) === 0;
+ },
+
+ endsWith: function(pattern) {
+ var d = this.length - pattern.length;
+ return d >= 0 && this.lastIndexOf(pattern) === d;
+ },
+
+ empty: function() {
+ return this == '';
+ },
+
+ blank: function() {
+ return /^\s*$/.test(this);
+ },
+
+ interpolate: function(object, pattern) {
+ return new Template(this, pattern).evaluate(object);
+ }
+});
+
+if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
+ escapeHTML: function() {
+ return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
+ },
+ unescapeHTML: function() {
+ return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
+ }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+ if (Object.isFunction(replacement)) return replacement;
+ var template = new Template(replacement);
+ return function(match) { return template.evaluate(match) };
+};
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+ div: document.createElement('div'),
+ text: document.createTextNode('')
+});
+
+with (String.prototype.escapeHTML) div.appendChild(text);
+
+var Template = Class.create({
+ initialize: function(template, pattern) {
+ this.template = template.toString();
+ this.pattern = pattern || Template.Pattern;
+ },
+
+ evaluate: function(object) {
+ if (Object.isFunction(object.toTemplateReplacements))
+ object = object.toTemplateReplacements();
+
+ return this.template.gsub(this.pattern, function(match) {
+ if (object == null) return '';
+
+ var before = match[1] || '';
+ if (before == '\\') return match[2];
+
+ var ctx = object, expr = match[3];
+ var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/, match = pattern.exec(expr);
+ if (match == null) return before;
+
+ while (match != null) {
+ var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
+ ctx = ctx[comp];
+ if (null == ctx || '' == match[3]) break;
+ expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
+ match = pattern.exec(expr);
+ }
+
+ return before + String.interpret(ctx);
+ }.bind(this));
+ }
+});
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+
+var $break = { };
+
+var Enumerable = {
+ each: function(iterator, context) {
+ var index = 0;
+ iterator = iterator.bind(context);
+ try {
+ this._each(function(value) {
+ iterator(value, index++);
+ });
+ } catch (e) {
+ if (e != $break) throw e;
+ }
+ return this;
+ },
+
+ eachSlice: function(number, iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var index = -number, slices = [], array = this.toArray();
+ while ((index += number) < array.length)
+ slices.push(array.slice(index, index+number));
+ return slices.collect(iterator, context);
+ },
+
+ all: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result = true;
+ this.each(function(value, index) {
+ result = result && !!iterator(value, index);
+ if (!result) throw $break;
+ });
+ return result;
+ },
+
+ any: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result = false;
+ this.each(function(value, index) {
+ if (result = !!iterator(value, index))
+ throw $break;
+ });
+ return result;
+ },
+
+ collect: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var results = [];
+ this.each(function(value, index) {
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ detect: function(iterator, context) {
+ iterator = iterator.bind(context);
+ var result;
+ this.each(function(value, index) {
+ if (iterator(value, index)) {
+ result = value;
+ throw $break;
+ }
+ });
+ return result;
+ },
+
+ findAll: function(iterator, context) {
+ iterator = iterator.bind(context);
+ var results = [];
+ this.each(function(value, index) {
+ if (iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ grep: function(filter, iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var results = [];
+
+ if (Object.isString(filter))
+ filter = new RegExp(filter);
+
+ this.each(function(value, index) {
+ if (filter.match(value))
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ include: function(object) {
+ if (Object.isFunction(this.indexOf))
+ if (this.indexOf(object) != -1) return true;
+
+ var found = false;
+ this.each(function(value) {
+ if (value == object) {
+ found = true;
+ throw $break;
+ }
+ });
+ return found;
+ },
+
+ inGroupsOf: function(number, fillWith) {
+ fillWith = fillWith === undefined ? null : fillWith;
+ return this.eachSlice(number, function(slice) {
+ while(slice.length < number) slice.push(fillWith);
+ return slice;
+ });
+ },
+
+ inject: function(memo, iterator, context) {
+ iterator = iterator.bind(context);
+ this.each(function(value, index) {
+ memo = iterator(memo, value, index);
+ });
+ return memo;
+ },
+
+ invoke: function(method) {
+ var args = $A(arguments).slice(1);
+ return this.map(function(value) {
+ return value[method].apply(value, args);
+ });
+ },
+
+ max: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result;
+ this.each(function(value, index) {
+ value = iterator(value, index);
+ if (result == undefined || value >= result)
+ result = value;
+ });
+ return result;
+ },
+
+ min: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result;
+ this.each(function(value, index) {
+ value = iterator(value, index);
+ if (result == undefined || value < result)
+ result = value;
+ });
+ return result;
+ },
+
+ partition: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var trues = [], falses = [];
+ this.each(function(value, index) {
+ (iterator(value, index) ?
+ trues : falses).push(value);
+ });
+ return [trues, falses];
+ },
+
+ pluck: function(property) {
+ var results = [];
+ this.each(function(value) {
+ results.push(value[property]);
+ });
+ return results;
+ },
+
+ reject: function(iterator, context) {
+ iterator = iterator.bind(context);
+ var results = [];
+ this.each(function(value, index) {
+ if (!iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ sortBy: function(iterator, context) {
+ iterator = iterator.bind(context);
+ return this.map(function(value, index) {
+ return {value: value, criteria: iterator(value, index)};
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }).pluck('value');
+ },
+
+ toArray: function() {
+ return this.map();
+ },
+
+ zip: function() {
+ var iterator = Prototype.K, args = $A(arguments);
+ if (Object.isFunction(args.last()))
+ iterator = args.pop();
+
+ var collections = [this].concat(args).map($A);
+ return this.map(function(value, index) {
+ return iterator(collections.pluck(index));
+ });
+ },
+
+ size: function() {
+ return this.toArray().length;
+ },
+
+ inspect: function() {
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
+ }
+};
+
+Object.extend(Enumerable, {
+ map: Enumerable.collect,
+ find: Enumerable.detect,
+ select: Enumerable.findAll,
+ filter: Enumerable.findAll,
+ member: Enumerable.include,
+ entries: Enumerable.toArray,
+ every: Enumerable.all,
+ some: Enumerable.any
+});
+function $A(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) return iterable.toArray();
+ var length = iterable.length, results = new Array(length);
+ while (length--) results[length] = iterable[length];
+ return results;
+}
+
+if (Prototype.Browser.WebKit) {
+ function $A(iterable) {
+ if (!iterable) return [];
+ if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
+ iterable.toArray) return iterable.toArray();
+ var length = iterable.length, results = new Array(length);
+ while (length--) results[length] = iterable[length];
+ return results;
+ }
+}
+
+Array.from = $A;
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+ _each: function(iterator) {
+ for (var i = 0, length = this.length; i < length; i++)
+ iterator(this[i]);
+ },
+
+ clear: function() {
+ this.length = 0;
+ return this;
+ },
+
+ first: function() {
+ return this[0];
+ },
+
+ last: function() {
+ return this[this.length - 1];
+ },
+
+ compact: function() {
+ return this.select(function(value) {
+ return value != null;
+ });
+ },
+
+ flatten: function() {
+ return this.inject([], function(array, value) {
+ return array.concat(Object.isArray(value) ?
+ value.flatten() : [value]);
+ });
+ },
+
+ without: function() {
+ var values = $A(arguments);
+ return this.select(function(value) {
+ return !values.include(value);
+ });
+ },
+
+ reverse: function(inline) {
+ return (inline !== false ? this : this.toArray())._reverse();
+ },
+
+ reduce: function() {
+ return this.length > 1 ? this : this[0];
+ },
+
+ uniq: function(sorted) {
+ return this.inject([], function(array, value, index) {
+ if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+ array.push(value);
+ return array;
+ });
+ },
+
+ intersect: function(array) {
+ return this.uniq().findAll(function(item) {
+ return array.detect(function(value) { return item === value });
+ });
+ },
+
+ clone: function() {
+ return [].concat(this);
+ },
+
+ size: function() {
+ return this.length;
+ },
+
+ inspect: function() {
+ return '[' + this.map(Object.inspect).join(', ') + ']';
+ },
+
+ toJSON: function() {
+ var results = [];
+ this.each(function(object) {
+ var value = Object.toJSON(object);
+ if (value !== undefined) results.push(value);
+ });
+ return '[' + results.join(', ') + ']';
+ }
+});
+
+// use native browser JS 1.6 implementation if available
+if (Object.isFunction(Array.prototype.forEach))
+ Array.prototype._each = Array.prototype.forEach;
+
+if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
+ i || (i = 0);
+ var length = this.length;
+ if (i < 0) i = length + i;
+ for (; i < length; i++)
+ if (this[i] === item) return i;
+ return -1;
+};
+
+if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
+ i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
+ var n = this.slice(0, i).reverse().indexOf(item);
+ return (n < 0) ? n : i - n - 1;
+};
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+ if (!Object.isString(string)) return [];
+ string = string.strip();
+ return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+ Array.prototype.concat = function() {
+ var array = [];
+ for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ if (Object.isArray(arguments[i])) {
+ for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+ array.push(arguments[i][j]);
+ } else {
+ array.push(arguments[i]);
+ }
+ }
+ return array;
+ };
+}
+Object.extend(Number.prototype, {
+ toColorPart: function() {
+ return this.toPaddedString(2, 16);
+ },
+
+ succ: function() {
+ return this + 1;
+ },
+
+ times: function(iterator) {
+ $R(0, this, true).each(iterator);
+ return this;
+ },
+
+ toPaddedString: function(length, radix) {
+ var string = this.toString(radix || 10);
+ return '0'.times(length - string.length) + string;
+ },
+
+ toJSON: function() {
+ return isFinite(this) ? this.toString() : 'null';
+ }
+});
+
+$w('abs round ceil floor').each(function(method){
+ Number.prototype[method] = Math[method].methodize();
+});
+function $H(object) {
+ return new Hash(object);
+};
+
+var Hash = Class.create(Enumerable, (function() {
+ if (function() {
+ var i = 0, Test = function(value) { this.key = value };
+ Test.prototype.key = 'foo';
+ for (var property in new Test('bar')) i++;
+ return i > 1;
+ }()) {
+ function each(iterator) {
+ var cache = [];
+ for (var key in this._object) {
+ var value = this._object[key];
+ if (cache.include(key)) continue;
+ cache.push(key);
+ var pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+ }
+ } else {
+ function each(iterator) {
+ for (var key in this._object) {
+ var value = this._object[key], pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+ }
+ }
+
+ function toQueryPair(key, value) {
+ if (Object.isUndefined(value)) return key;
+ return key + '=' + encodeURIComponent(String.interpret(value));
+ }
+
+ return {
+ initialize: function(object) {
+ this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
+ },
+
+ _each: each,
+
+ set: function(key, value) {
+ return this._object[key] = value;
+ },
+
+ get: function(key) {
+ return this._object[key];
+ },
+
+ unset: function(key) {
+ var value = this._object[key];
+ delete this._object[key];
+ return value;
+ },
+
+ toObject: function() {
+ return Object.clone(this._object);
+ },
+
+ keys: function() {
+ return this.pluck('key');
+ },
+
+ values: function() {
+ return this.pluck('value');
+ },
+
+ index: function(value) {
+ var match = this.detect(function(pair) {
+ return pair.value === value;
+ });
+ return match && match.key;
+ },
+
+ merge: function(object) {
+ return this.clone().update(object);
+ },
+
+ update: function(object) {
+ return new Hash(object).inject(this, function(result, pair) {
+ result.set(pair.key, pair.value);
+ return result;
+ });
+ },
+
+ toQueryString: function() {
+ return this.map(function(pair) {
+ var key = encodeURIComponent(pair.key), values = pair.value;
+
+ if (values && typeof values == 'object') {
+ if (Object.isArray(values))
+ return values.map(toQueryPair.curry(key)).join('&');
+ }
+ return toQueryPair(key, values);
+ }).join('&');
+ },
+
+ inspect: function() {
+ return '#<Hash:{' + this.map(function(pair) {
+ return pair.map(Object.inspect).join(': ');
+ }).join(', ') + '}>';
+ },
+
+ toJSON: function() {
+ return Object.toJSON(this.toObject());
+ },
+
+ clone: function() {
+ return new Hash(this);
+ }
+ }
+})());
+
+Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
+Hash.from = $H;
+var ObjectRange = Class.create(Enumerable, {
+ initialize: function(start, end, exclusive) {
+ this.start = start;
+ this.end = end;
+ this.exclusive = exclusive;
+ },
+
+ _each: function(iterator) {
+ var value = this.start;
+ while (this.include(value)) {
+ iterator(value);
+ value = value.succ();
+ }
+ },
+
+ include: function(value) {
+ if (value < this.start)
+ return false;
+ if (this.exclusive)
+ return value < this.end;
+ return value <= this.end;
+ }
+});
+
+var $R = function(start, end, exclusive) {
+ return new ObjectRange(start, end, exclusive);
+};
+
+var Ajax = {
+ getTransport: function() {
+ return Try.these(
+ function() {return new XMLHttpRequest()},
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+ ) || false;
+ },
+
+ activeRequestCount: 0
+};
+
+Ajax.Responders = {
+ responders: [],
+
+ _each: function(iterator) {
+ this.responders._each(iterator);
+ },
+
+ register: function(responder) {
+ if (!this.include(responder))
+ this.responders.push(responder);
+ },
+
+ unregister: function(responder) {
+ this.responders = this.responders.without(responder);
+ },
+
+ dispatch: function(callback, request, transport, json) {
+ this.each(function(responder) {
+ if (Object.isFunction(responder[callback])) {
+ try {
+ responder[callback].apply(responder, [request, transport, json]);
+ } catch (e) { }
+ }
+ });
+ }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+ onCreate: function() { Ajax.activeRequestCount++ },
+ onComplete: function() { Ajax.activeRequestCount-- }
+});
+
+Ajax.Base = Class.create({
+ initialize: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ contentType: 'application/x-www-form-urlencoded',
+ encoding: 'UTF-8',
+ parameters: '',
+ evalJSON: true,
+ evalJS: true
+ };
+ Object.extend(this.options, options || { });
+
+ this.options.method = this.options.method.toLowerCase();
+ if (Object.isString(this.options.parameters))
+ this.options.parameters = this.options.parameters.toQueryParams();
+ }
+});
+
+Ajax.Request = Class.create(Ajax.Base, {
+ _complete: false,
+
+ initialize: function($super, url, options) {
+ $super(options);
+ this.transport = Ajax.getTransport();
+ this.request(url);
+ },
+
+ request: function(url) {
+ this.url = url;
+ this.method = this.options.method;
+ var params = Object.clone(this.options.parameters);
+
+ if (!['get', 'post'].include(this.method)) {
+ // simulate other verbs over post
+ params['_method'] = this.method;
+ this.method = 'post';
+ }
+
+ this.parameters = params;
+
+ if (params = Object.toQueryString(params)) {
+ // when GET, append parameters to URL
+ if (this.method == 'get')
+ this.url += (this.url.include('?') ? '&' : '?') + params;
+ else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+ params += '&_=';
+ }
+
+ try {
+ var response = new Ajax.Response(this);
+ if (this.options.onCreate) this.options.onCreate(response);
+ Ajax.Responders.dispatch('onCreate', this, response);
+
+ this.transport.open(this.method.toUpperCase(), this.url,
+ this.options.asynchronous);
+
+ if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
+
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ this.setRequestHeaders();
+
+ this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+ this.transport.send(this.body);
+
+ /* Force Firefox to handle ready state 4 for synchronous requests */
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
+ this.onStateChange();
+
+ }
+ catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState > 1 && !((readyState == 4) && this._complete))
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ setRequestHeaders: function() {
+ var headers = {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'X-Prototype-Version': Prototype.Version,
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+ };
+
+ if (this.method == 'post') {
+ headers['Content-Type'] = this.options.contentType +
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+ /* Force "Connection: close" for older Mozilla browsers to work
+ * around a bug where XMLHttpRequest sends an incorrect
+ * Content-length header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType &&
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) {
+ headers['Connection'] = 'close';
+ }
+ }
+
+ // user-defined headers
+ if (typeof this.options.requestHeaders == 'object') {
+ var extras = this.options.requestHeaders;
+
+ if (Object.isFunction(extras.push))
+ for (var i = 0, length = extras.length; i < length; i += 2) {
+ headers[extras[i]] = extras[i+1];
+ }
+ else
+ $H(extras).each(function(pair) {
+ headers[pair.key] = pair.value
+ });
+ }
+
+ for (var name in headers) {
+ if ( typeof headers[name] == "string" )
+ this.transport.setRequestHeader(name, headers[name]);
+ }
+
+ },
+
+ success: function() {
+ var status = this.getStatus();
+ return !status || (status >= 200 && status < 300);
+ },
+
+ getStatus: function() {
+ try {
+ return this.transport.status || 0;
+ } catch (e) { return 0 }
+ },
+
+ respondToReadyState: function(readyState) {
+ var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
+
+ if (state == 'Complete') {
+ try {
+ this._complete = true;
+ (this.options['on' + response.status]
+ || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(response, response.headerJSON);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ var contentType = response.getHeader('Content-type');
+ if (this.options.evalJS == 'force'
+ || (this.options.evalJS && contentType
+ && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
+ this.evalResponse();
+ }
+
+ try {
+ (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
+ Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if (state == 'Complete') {
+ // avoid memory leak in MSIE: clean up
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ }
+ },
+
+ getHeader: function(name) {
+ try {
+ return this.transport.getResponseHeader(name);
+ } catch (e) { return null }
+ },
+
+ evalResponse: function() {
+ try {
+ return eval((this.transport.responseText || '').unfilterJSON());
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ dispatchException: function(exception) {
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
+ Ajax.Responders.dispatch('onException', this, exception);
+ }
+});
+
+Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Response = Class.create({
+ initialize: function(request){
+ this.request = request;
+ var transport = this.transport = request.transport,
+ readyState = this.readyState = transport.readyState;
+
+ if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
+ this.status = this.getStatus();
+ this.statusText = this.getStatusText();
+ this.responseText = String.interpret(transport.responseText);
+ this.headerJSON = this._getHeaderJSON();
+ }
+
+ if(readyState == 4) {
+ var xml = transport.responseXML;
+ this.responseXML = xml === undefined ? null : xml;
+ this.responseJSON = this._getResponseJSON();
+ }
+ },
+
+ status: 0,
+ statusText: '',
+
+ getStatus: Ajax.Request.prototype.getStatus,
+
+ getStatusText: function() {
+ try {
+ return this.transport.statusText || '';
+ } catch (e) { return '' }
+ },
+
+ getHeader: Ajax.Request.prototype.getHeader,
+
+ getAllHeaders: function() {
+ try {
+ return this.getAllResponseHeaders();
+ } catch (e) { return null }
+ },
+
+ getResponseHeader: function(name) {
+ return this.transport.getResponseHeader(name);
+ },
+
+ getAllResponseHeaders: function() {
+ return this.transport.getAllResponseHeaders();
+ },
+
+ _getHeaderJSON: function() {
+ var json = this.getHeader('X-JSON');
+ if (!json) return null;
+ json = decodeURIComponent(escape(json));
+ try {
+ return json.evalJSON(this.request.options.sanitizeJSON);
+ } catch (e) {
+ this.request.dispatchException(e);
+ }
+ },
+
+ _getResponseJSON: function() {
+ var options = this.request.options;
+ if (!options.evalJSON || (options.evalJSON != 'force' &&
+ !(this.getHeader('Content-type') || '').include('application/json')))
+ return null;
+ try {
+ return this.transport.responseText.evalJSON(options.sanitizeJSON);
+ } catch (e) {
+ this.request.dispatchException(e);
+ }
+ }
+});
+
+Ajax.Updater = Class.create(Ajax.Request, {
+ initialize: function($super, container, url, options) {
+ this.container = {
+ success: (container.success || container),
+ failure: (container.failure || (container.success ? null : container))
+ };
+
+ options = options || { };
+ var onComplete = options.onComplete;
+ options.onComplete = (function(response, param) {
+ this.updateContent(response.responseText);
+ if (Object.isFunction(onComplete)) onComplete(response, param);
+ }).bind(this);
+
+ $super(url, options);
+ },
+
+ updateContent: function(responseText) {
+ var receiver = this.container[this.success() ? 'success' : 'failure'],
+ options = this.options;
+
+ if (!options.evalScripts) responseText = responseText.stripScripts();
+
+ if (receiver = $(receiver)) {
+ if (options.insertion) {
+ if (Object.isString(options.insertion)) {
+ var insertion = { }; insertion[options.insertion] = responseText;
+ receiver.insert(insertion);
+ }
+ else options.insertion(receiver, responseText);
+ }
+ else receiver.update(responseText);
+ }
+
+ if (this.success()) {
+ if (this.onComplete) this.onComplete.bind(this).defer();
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
+ initialize: function($super, container, url, options) {
+ $super(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = (this.options.frequency || 2);
+ this.decay = (this.options.decay || 1);
+
+ this.updater = { };
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function() {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function() {
+ this.updater.options.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function(response) {
+ if (this.options.decay) {
+ this.decay = (response.responseText == this.lastText ?
+ this.decay * this.options.decay : 1);
+
+ this.lastText = response.responseText;
+ }
+ this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
+ },
+
+ onTimerEvent: function() {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
+ }
+});
+function $(element) {
+ if (arguments.length > 1) {
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+ elements.push($(arguments[i]));
+ return elements;
+ }
+ if (Object.isString(element))
+ element = document.getElementById(element);
+ return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+ document._getElementsByXPath = function(expression, parentElement) {
+ var results = [];
+ var query = document.evaluate(expression, $(parentElement) || document,
+ null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+ for (var i = 0, length = query.snapshotLength; i < length; i++)
+ results.push(Element.extend(query.snapshotItem(i)));
+ return results;
+ };
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Node) var Node = { };
+
+if (!Node.ELEMENT_NODE) {
+ // DOM level 2 ECMAScript Language Binding
+ Object.extend(Node, {
+ ELEMENT_NODE: 1,
+ ATTRIBUTE_NODE: 2,
+ TEXT_NODE: 3,
+ CDATA_SECTION_NODE: 4,
+ ENTITY_REFERENCE_NODE: 5,
+ ENTITY_NODE: 6,
+ PROCESSING_INSTRUCTION_NODE: 7,
+ COMMENT_NODE: 8,
+ DOCUMENT_NODE: 9,
+ DOCUMENT_TYPE_NODE: 10,
+ DOCUMENT_FRAGMENT_NODE: 11,
+ NOTATION_NODE: 12
+ });
+}
+
+(function() {
+ var element = this.Element;
+ this.Element = function(tagName, attributes) {
+ attributes = attributes || { };
+ tagName = tagName.toLowerCase();
+ var cache = Element.cache;
+ if (Prototype.Browser.IE && attributes.name) {
+ tagName = '<' + tagName + ' name="' + attributes.name + '">';
+ delete attributes.name;
+ return Element.writeAttribute(document.createElement(tagName), attributes);
+ }
+ if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
+ return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
+ };
+ Object.extend(this.Element, element || { });
+}).call(window);
+
+Element.cache = { };
+
+Element.Methods = {
+ visible: function(element) {
+ return $(element).style.display != 'none';
+ },
+
+ toggle: function(element) {
+ element = $(element);
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
+ return element;
+ },
+
+ hide: function(element) {
+ $(element).style.display = 'none';
+ return element;
+ },
+
+ show: function(element) {
+ $(element).style.display = '';
+ return element;
+ },
+
+ remove: function(element) {
+ element = $(element);
+ element.parentNode.removeChild(element);
+ return element;
+ },
+
+ update: function(element, content) {
+ element = $(element);
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) return element.update().insert(content);
+ content = Object.toHTML(content);
+ element.innerHTML = content.stripScripts();
+ content.evalScripts.bind(content).defer();
+ return element;
+ },
+
+ replace: function(element, content) {
+ element = $(element);
+ if (content && content.toElement) content = content.toElement();
+ else if (!Object.isElement(content)) {
+ content = Object.toHTML(content);
+ var range = element.ownerDocument.createRange();
+ range.selectNode(element);
+ content.evalScripts.bind(content).defer();
+ content = range.createContextualFragment(content.stripScripts());
+ }
+ element.parentNode.replaceChild(content, element);
+ return element;
+ },
+
+ insert: function(element, insertions) {
+ element = $(element);
+
+ if (Object.isString(insertions) || Object.isNumber(insertions) ||
+ Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
+ insertions = {bottom:insertions};
+
+ var content, t, range;
+
+ for (position in insertions) {
+ if ( position == 'extend' ) continue;
+ content = insertions[position];
+ position = position.toLowerCase();
+ t = Element._insertionTranslations[position];
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) {
+ t.insert(element, content);
+ continue;
+ }
+
+ content = Object.toHTML(content);
+
+ range = element.ownerDocument.createRange();
+ t.initializeRange(element, range);
+ t.insert(element, range.createContextualFragment(content.stripScripts()));
+
+ content.evalScripts.bind(content).defer();
+ }
+
+ return element;
+ },
+
+ wrap: function(element, wrapper, attributes) {
+ element = $(element);
+ if (Object.isElement(wrapper))
+ $(wrapper).writeAttribute(attributes || { });
+ else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
+ else wrapper = new Element('div', wrapper);
+ if (element.parentNode)
+ element.parentNode.replaceChild(wrapper, element);
+ wrapper.appendChild(element);
+ return wrapper;
+ },
+
+ inspect: function(element) {
+ element = $(element);
+ var result = '<' + element.tagName.toLowerCase();
+ $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+ var property = pair.first(), attribute = pair.last();
+ var value = (element[property] || '').toString();
+ if (value) result += ' ' + attribute + '=' + value.inspect(true);
+ });
+ return result + '>';
+ },
+
+ recursivelyCollect: function(element, property) {
+ element = $(element);
+ var elements = [];
+ while (element = element[property])
+ if (element.nodeType == 1)
+ elements.push(Element.extend(element));
+ return elements;
+ },
+
+ ancestors: function(element) {
+ return $(element).recursivelyCollect('parentNode');
+ },
+
+ descendants: function(element) {
+ return $A($(element).getElementsByTagName('*')).each(Element.extend);
+ },
+
+ firstDescendant: function(element) {
+ element = $(element).firstChild;
+ while (element && element.nodeType != 1) element = element.nextSibling;
+ return $(element);
+ },
+
+ immediateDescendants: function(element) {
+ if (!(element = $(element).firstChild)) return [];
+ while (element && element.nodeType != 1) element = element.nextSibling;
+ if (element) return [element].concat($(element).nextSiblings());
+ return [];
+ },
+
+ previousSiblings: function(element) {
+ return $(element).recursivelyCollect('previousSibling');
+ },
+
+ nextSiblings: function(element) {
+ return $(element).recursivelyCollect('nextSibling');
+ },
+
+ siblings: function(element) {
+ element = $(element);
+ return element.previousSiblings().reverse().concat(element.nextSiblings());
+ },
+
+ match: function(element, selector) {
+ if (Object.isString(selector))
+ selector = new Selector(selector);
+ return selector.match($(element));
+ },
+
+ up: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(element.parentNode);
+ var ancestors = element.ancestors();
+ return expression ? Selector.findElement(ancestors, expression, index) :
+ ancestors[index || 0];
+ },
+
+ down: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return element.firstDescendant();
+ var descendants = element.descendants();
+ return expression ? Selector.findElement(descendants, expression, index) :
+ descendants[index || 0];
+ },
+
+ previous: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+ var previousSiblings = element.previousSiblings();
+ return expression ? Selector.findElement(previousSiblings, expression, index) :
+ previousSiblings[index || 0];
+ },
+
+ next: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
+ var nextSiblings = element.nextSiblings();
+ return expression ? Selector.findElement(nextSiblings, expression, index) :
+ nextSiblings[index || 0];
+ },
+
+ select: function() {
+ var args = $A(arguments), element = $(args.shift());
+ return Selector.findChildElements(element, args);
+ },
+
+ adjacent: function() {
+ var args = $A(arguments), element = $(args.shift());
+ return Selector.findChildElements(element.parentNode, args).without(element);
+ },
+
+ identify: function(element) {
+ element = $(element);
+ var id = element.readAttribute('id'), self = arguments.callee;
+ if (id) return id;
+ do { id = 'anonymous_element_' + self.counter++ } while ($(id));
+ element.writeAttribute('id', id);
+ return id;
+ },
+
+ readAttribute: function(element, name) {
+ element = $(element);
+ if (Prototype.Browser.IE) {
+ var t = Element._attributeTranslations.read;
+ if (t.values[name]) return t.values[name](element, name);
+ if (t.names[name]) name = t.names[name];
+ if (name.include(':')) {
+ return (!element.attributes || !element.attributes[name]) ? null :
+ element.attributes[name].value;
+ }
+ }
+ return element.getAttribute(name);
+ },
+
+ writeAttribute: function(element, name, value) {
+ element = $(element);
+ var attributes = { }, t = Element._attributeTranslations.write;
+
+ if (typeof name == 'object') attributes = name;
+ else attributes[name] = value === undefined ? true : value;
+
+ for (var attr in attributes) {
+ var name = t.names[attr] || attr, value = attributes[attr];
+ if (t.values[attr]) name = t.values[attr](element, value);
+ if (value === false || value === null)
+ element.removeAttribute(name);
+ else if (value === true)
+ element.setAttribute(name, name);
+ else element.setAttribute(name, value);
+ }
+ return element;
+ },
+
+ getHeight: function(element) {
+ return $(element).getDimensions().height;
+ },
+
+ getWidth: function(element) {
+ return $(element).getDimensions().width;
+ },
+
+ classNames: function(element) {
+ return new Element.ClassNames(element);
+ },
+
+ hasClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ var elementClassName = element.className;
+ return (elementClassName.length > 0 && (elementClassName == className ||
+ new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
+ },
+
+ addClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ if (!element.hasClassName(className))
+ element.className += (element.className ? ' ' : '') + className;
+ return element;
+ },
+
+ removeClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ element.className = element.className.replace(
+ new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
+ return element;
+ },
+
+ toggleClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return element[element.hasClassName(className) ?
+ 'removeClassName' : 'addClassName'](className);
+ },
+
+ // removes whitespace-only text node children
+ cleanWhitespace: function(element) {
+ element = $(element);
+ var node = element.firstChild;
+ while (node) {
+ var nextNode = node.nextSibling;
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+ element.removeChild(node);
+ node = nextNode;
+ }
+ return element;
+ },
+
+ empty: function(element) {
+ return $(element).innerHTML.blank();
+ },
+
+ descendantOf: function(element, ancestor) {
+ element = $(element), ancestor = $(ancestor);
+
+ if (element.compareDocumentPosition)
+ return (element.compareDocumentPosition(ancestor) & 8) === 8;
+
+ if (element.sourceIndex && !Prototype.Browser.Opera) {
+ var e = element.sourceIndex, a = ancestor.sourceIndex,
+ nextAncestor = ancestor.nextSibling;
+ if (!nextAncestor) {
+ do { ancestor = ancestor.parentNode; }
+ while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
+ }
+ if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex);
+ }
+
+ while (element = element.parentNode)
+ if (element == ancestor) return true;
+ return false;
+ },
+
+ scrollTo: function(element) {
+ element = $(element);
+ var pos = element.cumulativeOffset();
+ window.scrollTo(pos[0], pos[1]);
+ return element;
+ },
+
+ getStyle: function(element, style) {
+ element = $(element);
+ style = style == 'float' ? 'cssFloat' : style.camelize();
+ var value = element.style[style];
+ if (!value) {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css[style] : null;
+ }
+ if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+ return value == 'auto' ? null : value;
+ },
+
+ getOpacity: function(element) {
+ return $(element).getStyle('opacity');
+ },
+
+ setStyle: function(element, styles) {
+ element = $(element);
+ var elementStyle = element.style, match;
+ if (Object.isString(styles)) {
+ element.style.cssText += ';' + styles;
+ return styles.include('opacity') ?
+ element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
+ }
+ for (var property in styles)
+ if (property == 'opacity') element.setOpacity(styles[property]);
+ else
+ elementStyle[(property == 'float' || property == 'cssFloat') ?
+ (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
+ property] = styles[property];
+
+ return element;
+ },
+
+ setOpacity: function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1 || value === '') ? '' :
+ (value < 0.00001) ? 0 : value;
+ return element;
+ },
+
+ getDimensions: function(element) {
+ element = $(element);
+ var display = $(element).getStyle('display');
+ if (display != 'none' && display != null) // Safari bug
+ return {width: element.offsetWidth, height: element.offsetHeight};
+
+ // All *Width and *Height properties give 0 on elements with display none,
+ // so enable the element temporarily
+ var els = element.style;
+ var originalVisibility = els.visibility;
+ var originalPosition = els.position;
+ var originalDisplay = els.display;
+ els.visibility = 'hidden';
+ els.position = 'absolute';
+ els.display = 'block';
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ els.display = originalDisplay;
+ els.position = originalPosition;
+ els.visibility = originalVisibility;
+ return {width: originalWidth, height: originalHeight};
+ },
+
+ makePositioned: function(element) {
+ element = $(element);
+ var pos = Element.getStyle(element, 'position');
+ if (pos == 'static' || !pos) {
+ element._madePositioned = true;
+ element.style.position = 'relative';
+ // Opera returns the offset relative to the positioning context, when an
+ // element is position relative but top and left have not been defined
+ if (window.opera) {
+ element.style.top = 0;
+ element.style.left = 0;
+ }
+ }
+ return element;
+ },
+
+ undoPositioned: function(element) {
+ element = $(element);
+ if (element._madePositioned) {
+ element._madePositioned = undefined;
+ element.style.position =
+ element.style.top =
+ element.style.left =
+ element.style.bottom =
+ element.style.right = '';
+ }
+ return element;
+ },
+
+ makeClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return element;
+ element._overflow = Element.getStyle(element, 'overflow') || 'auto';
+ if (element._overflow !== 'hidden')
+ element.style.overflow = 'hidden';
+ return element;
+ },
+
+ undoClipping: function(element) {
+ element = $(element);
+ if (!element._overflow) return element;
+ element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+ element._overflow = null;
+ return element;
+ },
+
+ cumulativeOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ positionedOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ if (element.tagName == 'BODY') break;
+ var p = Element.getStyle(element, 'position');
+ if (p == 'relative' || p == 'absolute') break;
+ }
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ absolutize: function(element) {
+ element = $(element);
+ if (element.getStyle('position') == 'absolute') return;
+ // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+ var offsets = element.positionedOffset();
+ var top = offsets[1];
+ var left = offsets[0];
+ var width = element.clientWidth;
+ var height = element.clientHeight;
+
+ element._originalLeft = left - parseFloat(element.style.left || 0);
+ element._originalTop = top - parseFloat(element.style.top || 0);
+ element._originalWidth = element.style.width;
+ element._originalHeight = element.style.height;
+
+ element.style.position = 'absolute';
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.width = width + 'px';
+ element.style.height = height + 'px';
+ return element;
+ },
+
+ relativize: function(element) {
+ element = $(element);
+ if (element.getStyle('position') == 'relative') return;
+ // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+ element.style.position = 'relative';
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.height = element._originalHeight;
+ element.style.width = element._originalWidth;
+ return element;
+ },
+
+ cumulativeScrollOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ getOffsetParent: function(element) {
+ if (element.offsetParent) return $(element.offsetParent);
+ if (element == document.body) return $(element);
+
+ while ((element = element.parentNode) && element != document.body)
+ if (Element.getStyle(element, 'position') != 'static')
+ return $(element);
+
+ return $(document.body);
+ },
+
+ viewportOffset: function(forElement) {
+ var valueT = 0, valueL = 0;
+
+ var element = forElement;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+
+ // Safari fix
+ if (element.offsetParent == document.body &&
+ Element.getStyle(element, 'position') == 'absolute') break;
+
+ } while (element = element.offsetParent);
+
+ element = forElement;
+ do {
+ if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
+ }
+ } while (element = element.parentNode);
+
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ clonePosition: function(element, source) {
+ var options = Object.extend({
+ setLeft: true,
+ setTop: true,
+ setWidth: true,
+ setHeight: true,
+ offsetTop: 0,
+ offsetLeft: 0
+ }, arguments[2] || { });
+
+ // find page position of source
+ source = $(source);
+ var p = source.viewportOffset();
+
+ // find coordinate system to use
+ element = $(element);
+ var delta = [0, 0];
+ var parent = null;
+ // delta [0,0] will do fine with position: fixed elements,
+ // position:absolute needs offsetParent deltas
+ if (Element.getStyle(element, 'position') == 'absolute') {
+ parent = element.getOffsetParent();
+ delta = parent.viewportOffset();
+ }
+
+ // correct by body offsets (fixes Safari)
+ if (parent == document.body) {
+ delta[0] -= document.body.offsetLeft;
+ delta[1] -= document.body.offsetTop;
+ }
+
+ // set position
+ if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
+ if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
+ if (options.setWidth) element.style.width = source.offsetWidth + 'px';
+ if (options.setHeight) element.style.height = source.offsetHeight + 'px';
+ return element;
+ }
+};
+
+Element.Methods.identify.counter = 1;
+
+Object.extend(Element.Methods, {
+ getElementsBySelector: Element.Methods.select,
+ childElements: Element.Methods.immediateDescendants
+});
+
+Element._attributeTranslations = {
+ write: {
+ names: {
+ className: 'class',
+ htmlFor: 'for'
+ },
+ values: { }
+ }
+};
+
+
+if (!document.createRange || Prototype.Browser.Opera) {
+ Element.Methods.insert = function(element, insertions) {
+ element = $(element);
+
+ if (Object.isString(insertions) || Object.isNumber(insertions) ||
+ Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
+ insertions = { bottom: insertions };
+
+ var t = Element._insertionTranslations, content, position, pos, tagName;
+
+ for (position in insertions) {
+ content = insertions[position];
+ position = position.toLowerCase();
+ pos = t[position];
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) {
+ pos.insert(element, content);
+ continue;
+ }
+
+ content = Object.toHTML(content);
+ tagName = ((position == 'before' || position == 'after')
+ ? element.parentNode : element).tagName.toUpperCase();
+
+ if (t.tags[tagName]) {
+ var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+ if (position == 'top' || position == 'after') fragments.reverse();
+ fragments.each(pos.insert.curry(element));
+ }
+ // Sartak: pos.adjacency may be undefined. IE6 gets very unhappy if you
+ // try to pass undef to insertAdjacentHTML
+ else if (pos.adjacency) {
+ element.insertAdjacentHTML(pos.adjacency, content.stripScripts());
+ }
+
+ content.evalScripts.bind(content).defer();
+ }
+
+ return element;
+ };
+}
+
+if (Prototype.Browser.Opera) {
+ Element.Methods._getStyle = Element.Methods.getStyle;
+ Element.Methods.getStyle = function(element, style) {
+ switch(style) {
+ case 'left':
+ case 'top':
+ case 'right':
+ case 'bottom':
+ if (Element._getStyle(element, 'position') == 'static') return null;
+ default: return Element._getStyle(element, style);
+ }
+ };
+ Element.Methods._readAttribute = Element.Methods.readAttribute;
+ Element.Methods.readAttribute = function(element, attribute) {
+ if (attribute == 'title') return element.title;
+ return Element._readAttribute(element, attribute);
+ };
+}
+
+else if (Prototype.Browser.IE) {
+ $w('positionedOffset getOffsetParent viewportOffset').each(function(method) {
+ Element.Methods[method] = Element.Methods[method].wrap(
+ function(proceed, element) {
+ element = $(element);
+ var position = element.getStyle('position');
+ if (position != 'static') return proceed(element);
+ element.setStyle({ position: 'relative' });
+ var value = proceed(element);
+ element.setStyle({ position: position });
+ return value;
+ }
+ );
+ });
+
+ Element.Methods.getStyle = function(element, style) {
+ element = $(element);
+ style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+ var value = element.style[style];
+ if (!value && element.currentStyle) value = element.currentStyle[style];
+
+ if (style == 'opacity') {
+ if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+ if (value[1]) return parseFloat(value[1]) / 100;
+ return 1.0;
+ }
+
+ if (value == 'auto') {
+ if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+ return element['offset' + style.capitalize()] + 'px';
+ return null;
+ }
+ return value;
+ };
+
+ Element.Methods.setOpacity = function(element, value) {
+ function stripAlpha(filter){
+ return filter.replace(/alpha\([^\)]*\)/gi,'');
+ }
+ element = $(element);
+ var currentStyle = element.currentStyle;
+ if ((currentStyle && !currentStyle.hasLayout) ||
+ (!currentStyle && element.style.zoom == 'normal'))
+ element.style.zoom = 1;
+
+ var filter = element.getStyle('filter'), style = element.style;
+ if (value == 1 || value === '') {
+ (filter = stripAlpha(filter)) ?
+ style.filter = filter : style.removeAttribute('filter');
+ return element;
+ } else if (value < 0.00001) value = 0;
+ style.filter = stripAlpha(filter) +
+ 'alpha(opacity=' + (value * 100) + ')';
+ return element;
+ };
+
+ Element._attributeTranslations = {
+ read: {
+ names: {
+ 'class': 'className',
+ 'for': 'htmlFor'
+ },
+ values: {
+ _getAttr: function(element, attribute) {
+ return element.getAttribute(attribute, 2);
+ },
+ _getAttrNode: function(element, attribute) {
+ var node = element.getAttributeNode(attribute);
+ return node ? node.value : "";
+ },
+ _getEv: function(element, attribute) {
+ var attribute = element.getAttribute(attribute);
+ return attribute ? attribute.toString().slice(23, -2) : null;
+ },
+ _flag: function(element, attribute) {
+ return $(element).hasAttribute(attribute) ? attribute : null;
+ },
+ style: function(element) {
+ return element.style.cssText.toLowerCase();
+ },
+ title: function(element) {
+ return element.title;
+ }
+ }
+ }
+ };
+
+ Element._attributeTranslations.write = {
+ names: Object.clone(Element._attributeTranslations.read.names),
+ values: {
+ checked: function(element, value) {
+ element.checked = !!value;
+ },
+
+ style: function(element, value) {
+ element.style.cssText = value ? value : '';
+ }
+ }
+ };
+
+ Element._attributeTranslations.has = {};
+
+ $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
+ 'encType maxLength readOnly longDesc').each(function(attr) {
+ Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
+ Element._attributeTranslations.has[attr.toLowerCase()] = attr;
+ });
+
+ (function(v) {
+ Object.extend(v, {
+ href: v._getAttr,
+ src: v._getAttr,
+ type: v._getAttr,
+ action: v._getAttrNode,
+ disabled: v._flag,
+ checked: v._flag,
+ readonly: v._flag,
+ multiple: v._flag,
+ onload: v._getEv,
+ onunload: v._getEv,
+ onclick: v._getEv,
+ ondblclick: v._getEv,
+ onmousedown: v._getEv,
+ onmouseup: v._getEv,
+ onmouseover: v._getEv,
+ onmousemove: v._getEv,
+ onmouseout: v._getEv,
+ onfocus: v._getEv,
+ onblur: v._getEv,
+ onkeypress: v._getEv,
+ onkeydown: v._getEv,
+ onkeyup: v._getEv,
+ onsubmit: v._getEv,
+ onreset: v._getEv,
+ onselect: v._getEv,
+ onchange: v._getEv
+ });
+ })(Element._attributeTranslations.read.values);
+}
+
+else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1) ? 0.999999 :
+ (value === '') ? '' : (value < 0.00001) ? 0 : value;
+ return element;
+ };
+}
+
+else if (Prototype.Browser.WebKit) {
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1 || value === '') ? '' :
+ (value < 0.00001) ? 0 : value;
+
+ if (value == 1)
+ if(element.tagName == 'IMG' && element.width) {
+ element.width++; element.width--;
+ } else try {
+ var n = document.createTextNode(' ');
+ element.appendChild(n);
+ element.removeChild(n);
+ } catch (e) { }
+
+ return element;
+ };
+
+ // Safari returns margins on body which is incorrect if the child is absolutely
+ // positioned. For performance reasons, redefine Position.cumulativeOffset for
+ // KHTML/WebKit only.
+ Element.Methods.cumulativeOffset = function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (element.offsetParent == document.body)
+ if (Element.getStyle(element, 'position') == 'absolute') break;
+
+ element = element.offsetParent;
+ } while (element);
+
+ return Element._returnOffset(valueL, valueT);
+ };
+}
+
+if (Prototype.Browser.IE || Prototype.Browser.Opera) {
+ // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
+ Element.Methods.update = function(element, content) {
+ element = $(element);
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) return element.update().insert(content);
+
+ content = Object.toHTML(content);
+ var tagName = element.tagName.toUpperCase();
+
+ if (tagName in Element._insertionTranslations.tags) {
+ $A(element.childNodes).each(function(node) { element.removeChild(node) });
+ Element._getContentFromAnonymousElement(tagName, content.stripScripts())
+ .each(function(node) { element.appendChild(node) });
+ }
+ else element.innerHTML = content.stripScripts();
+
+ content.evalScripts.bind(content).defer();
+ return element;
+ };
+}
+
+if (document.createElement('div').outerHTML) {
+ Element.Methods.replace = function(element, content) {
+ element = $(element);
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) {
+ element.parentNode.replaceChild(content, element);
+ return element;
+ }
+
+ content = Object.toHTML(content);
+ var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
+
+ if (Element._insertionTranslations.tags[tagName]) {
+ var nextSibling = element.next();
+ var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+ parent.removeChild(element);
+ if (nextSibling)
+ fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
+ else
+ fragments.each(function(node) { parent.appendChild(node) });
+ }
+ else element.outerHTML = content.stripScripts();
+
+ content.evalScripts.bind(content).defer();
+ return element;
+ };
+}
+
+Element._returnOffset = function(l, t) {
+ var result = [l, t];
+ result.left = l;
+ result.top = t;
+ return result;
+};
+
+Element._getContentFromAnonymousElement = function(tagName, html) {
+ var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
+ div.innerHTML = t[0] + html + t[1];
+ t[2].times(function() { div = div.firstChild });
+ return $A(div.childNodes);
+};
+
+Element._insertionTranslations = {
+ before: {
+ adjacency: 'beforeBegin',
+ insert: function(element, node) {
+ element.parentNode.insertBefore(node, element);
+ },
+ initializeRange: function(element, range) {
+ range.setStartBefore(element);
+ }
+ },
+ top: {
+ adjacency: 'afterBegin',
+ insert: function(element, node) {
+ element.insertBefore(node, element.firstChild);
+ },
+ initializeRange: function(element, range) {
+ range.selectNodeContents(element);
+ range.collapse(true);
+ }
+ },
+ bottom: {
+ adjacency: 'beforeEnd',
+ insert: function(element, node) {
+ element.appendChild(node);
+ }
+ },
+ after: {
+ adjacency: 'afterEnd',
+ insert: function(element, node) {
+ element.parentNode.insertBefore(node, element.nextSibling);
+ },
+ initializeRange: function(element, range) {
+ range.setStartAfter(element);
+ }
+ },
+ tags: {
+ TABLE: ['<table>', '</table>', 1],
+ TBODY: ['<table><tbody>', '</tbody></table>', 2],
+ TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
+ TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
+ SELECT: ['<select>', '</select>', 1]
+ }
+};
+
+(function() {
+ this.bottom.initializeRange = this.top.initializeRange;
+ Object.extend(this.tags, {
+ THEAD: this.tags.TBODY,
+ TFOOT: this.tags.TBODY,
+ TH: this.tags.TD
+ });
+}).call(Element._insertionTranslations);
+
+Element.Methods.Simulated = {
+ hasAttribute: function(element, attribute) {
+ attribute = Element._attributeTranslations.has[attribute] || attribute;
+ var node = $(element).getAttributeNode(attribute);
+ return node && node.specified;
+ }
+};
+
+Element.Methods.ByTag = { };
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+ document.createElement('div').__proto__) {
+ window.HTMLElement = { };
+ window.HTMLElement.prototype = document.createElement('div').__proto__;
+ Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.extend = (function() {
+ if (Prototype.BrowserFeatures.SpecificElementExtensions)
+ return Prototype.K;
+
+ var Methods = { }, ByTag = Element.Methods.ByTag;
+
+ var extend = Object.extend(function(element) {
+ if (!element || element._extendedByPrototype ||
+ element.nodeType != 1 || element == window) return element;
+
+ var methods = Object.clone(Methods),
+ tagName = element.tagName, property, value;
+
+ // extend methods for specific tags
+ if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
+
+ for (property in methods) {
+ value = methods[property];
+ if (Object.isFunction(value) && !(property in element))
+ element[property] = value.methodize();
+ }
+
+ element._extendedByPrototype = Prototype.emptyFunction;
+ return element;
+
+ }, {
+ refresh: function() {
+ // extend methods for all tags (Safari doesn't need this)
+ if (!Prototype.BrowserFeatures.ElementExtensions) {
+ Object.extend(Methods, Element.Methods);
+ Object.extend(Methods, Element.Methods.Simulated);
+ }
+ }
+ });
+
+ extend.refresh();
+ return extend;
+})();
+
+Element.hasAttribute = function(element, attribute) {
+ if (element.hasAttribute) return element.hasAttribute(attribute);
+ return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+ var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+
+ if (!methods) {
+ Object.extend(Form, Form.Methods);
+ Object.extend(Form.Element, Form.Element.Methods);
+ Object.extend(Element.Methods.ByTag, {
+ "FORM": Object.clone(Form.Methods),
+ "INPUT": Object.clone(Form.Element.Methods),
+ "SELECT": Object.clone(Form.Element.Methods),
+ "TEXTAREA": Object.clone(Form.Element.Methods)
+ });
+ }
+
+ if (arguments.length == 2) {
+ var tagName = methods;
+ methods = arguments[1];
+ }
+
+ if (!tagName) Object.extend(Element.Methods, methods || { });
+ else {
+ if (Object.isArray(tagName)) tagName.each(extend);
+ else extend(tagName);
+ }
+
+ function extend(tagName) {
+ tagName = tagName.toUpperCase();
+ if (!Element.Methods.ByTag[tagName])
+ Element.Methods.ByTag[tagName] = { };
+ Object.extend(Element.Methods.ByTag[tagName], methods);
+ }
+
+ function copy(methods, destination, onlyIfAbsent) {
+ onlyIfAbsent = onlyIfAbsent || false;
+ for (var property in methods) {
+ var value = methods[property];
+ // don't copy update, temporarily
+ if (!Object.isFunction(value) || property == 'update') continue;
+ if (!onlyIfAbsent || !(property in destination))
+ destination[property] = value.methodize();
+ }
+ }
+
+ function findDOMClass(tagName) {
+ var klass;
+ var trans = {
+ "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+ "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+ "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+ "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+ "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+ "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+ "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+ "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+ "FrameSet", "IFRAME": "IFrame"
+ };
+ if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName.capitalize() + 'Element';
+ if (window[klass]) return window[klass];
+
+ window[klass] = { };
+ window[klass].prototype = document.createElement(tagName).__proto__;
+ return window[klass];
+ }
+
+ if (F.ElementExtensions) {
+ copy(Element.Methods, HTMLElement.prototype);
+ copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+ }
+
+ if (F.SpecificElementExtensions) {
+ for (var tag in Element.Methods.ByTag) {
+ var klass = findDOMClass(tag);
+ if (Object.isUndefined(klass)) continue;
+ copy(T[tag], klass.prototype);
+ }
+ }
+
+ Object.extend(Element, Element.Methods);
+ delete Element.ByTag;
+
+ if (Element.extend.refresh) Element.extend.refresh();
+ Element.cache = { };
+};
+
+document.viewport = {
+ getDimensions: function() {
+ var dimensions = { };
+ $w('width height').each(function(d) {
+ var D = d.capitalize();
+ dimensions[d] = self['inner' + D] ||
+ (document.documentElement['client' + D] || document.body['client' + D]);
+ });
+ return dimensions;
+ },
+
+ getWidth: function() {
+ return this.getDimensions().width;
+ },
+
+ getHeight: function() {
+ return this.getDimensions().height;
+ },
+
+ getScrollOffsets: function() {
+ return Element._returnOffset(
+ window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
+ window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
+ }
+};
+/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license. Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create({
+ initialize: function(expression) {
+ this.expression = expression.strip();
+ this.compileMatcher();
+ },
+
+ compileMatcher: function() {
+ // Selectors with namespaced attributes can't use the XPath version
+ if (Prototype.BrowserFeatures.XPath && !(/(\[[\w-]*?:|:checked)/).test(this.expression))
+ return this.compileXPathMatcher();
+
+ var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+ c = Selector.criteria, le, p, m;
+
+ if (Selector._cache[e]) {
+ this.matcher = Selector._cache[e];
+ return;
+ }
+
+ this.matcher = ["this.matcher = function(root) {",
+ "var r = root, h = Selector.handlers, c = false, n;"];
+
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
+ new Template(c[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.matcher.push("return h.unique(n);\n}");
+ eval(this.matcher.join('\n'));
+ Selector._cache[this.expression] = this.matcher;
+ },
+
+ compileXPathMatcher: function() {
+ var e = this.expression, ps = Selector.patterns,
+ x = Selector.xpath, le, m;
+
+ if (Selector._cache[e]) {
+ this.xpath = Selector._cache[e]; return;
+ }
+
+ this.matcher = ['.//*'];
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ if (m = e.match(ps[i])) {
+ this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
+ new Template(x[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.xpath = this.matcher.join('');
+ Selector._cache[this.expression] = this.xpath;
+ },
+
+ findElements: function(root) {
+ root = root || document;
+ if (this.xpath) return document._getElementsByXPath(this.xpath, root);
+ return this.matcher(root);
+ },
+
+ match: function(element) {
+ this.tokens = [];
+
+ var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
+ var le, p, m;
+
+ while (e && le !== e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ // use the Selector.assertions methods unless the selector
+ // is too complex.
+ if (as[i]) {
+ this.tokens.push([i, Object.clone(m)]);
+ e = e.replace(m[0], '');
+ } else {
+ // reluctantly do a document-wide search
+ // and look for a match in the array
+ return this.findElements(document).include(element);
+ }
+ }
+ }
+ }
+
+ var match = true, name, matches;
+ for (var i = 0, token; token = this.tokens[i]; i++) {
+ name = token[0], matches = token[1];
+ if (!Selector.assertions[name](element, matches)) {
+ match = false; break;
+ }
+ }
+
+ return match;
+ },
+
+ toString: function() {
+ return this.expression;
+ },
+
+ inspect: function() {
+ return "#<Selector:" + this.expression.inspect() + ">";
+ }
+});
+
+Object.extend(Selector, {
+ _cache: { },
+
+ xpath: {
+ descendant: "//*",
+ child: "/*",
+ adjacent: "/following-sibling::*[1]",
+ laterSibling: '/following-sibling::*',
+ tagName: function(m) {
+ if (m[1] == '*') return '';
+ return "[local-name()='" + m[1].toLowerCase() +
+ "' or local-name()='" + m[1].toUpperCase() + "']";
+ },
+ className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+ id: "[@id='#{1}']",
+ attrPresence: "[@#{1}]",
+ attr: function(m) {
+ m[3] = m[5] || m[6];
+ return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+ },
+ pseudo: function(m) {
+ var h = Selector.xpath.pseudos[m[1]];
+ if (!h) return '';
+ if (Object.isFunction(h)) return h(m);
+ return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+ },
+ operators: {
+ '=': "[@#{1}='#{3}']",
+ '!=': "[@#{1}!='#{3}']",
+ '^=': "[starts-with(@#{1}, '#{3}')]",
+ '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+ '*=': "[contains(@#{1}, '#{3}')]",
+ '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+ '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+ },
+ pseudos: {
+ 'first-child': '[not(preceding-sibling::*)]',
+ 'last-child': '[not(following-sibling::*)]',
+ 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
+ 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
+ 'checked': "[@checked]",
+ 'disabled': "[@disabled]",
+ 'enabled': "[not(@disabled)]",
+ 'not': function(m) {
+ var e = m[6], p = Selector.patterns,
+ x = Selector.xpath, le, m, v;
+
+ var exclusion = [];
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in p) {
+ if (m = e.match(p[i])) {
+ v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
+ exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+ return "[not(" + exclusion.join(" and ") + ")]";
+ },
+ 'nth-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+ },
+ 'nth-last-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+ },
+ 'nth-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("position() ", m);
+ },
+ 'nth-last-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+ },
+ 'first-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+ },
+ 'last-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+ },
+ 'only-of-type': function(m) {
+ var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+ },
+ nth: function(fragment, m) {
+ var mm, formula = m[6], predicate;
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ if (mm = formula.match(/^(\d+)$/)) // digit only
+ return '[' + fragment + "= " + mm[1] + ']';
+ if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+ if (mm[1] == "-") mm[1] = -1;
+ var a = mm[1] ? Number(mm[1]) : 1;
+ var b = mm[2] ? Number(mm[2]) : 0;
+ predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+ "((#{fragment} - #{b}) div #{a} >= 0)]";
+ return new Template(predicate).evaluate({
+ fragment: fragment, a: a, b: b });
+ }
+ }
+ }
+ },
+
+ criteria: {
+ tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
+ className: 'n = h.className(n, r, "#{1}", c); c = false;',
+ id: 'n = h.id(n, r, "#{1}", c); c = false;',
+ attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
+ attr: function(m) {
+ m[3] = (m[5] || m[6]);
+ return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
+ },
+ pseudo: function(m) {
+ if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+ return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+ },
+ descendant: 'c = "descendant";',
+ child: 'c = "child";',
+ adjacent: 'c = "adjacent";',
+ laterSibling: 'c = "laterSibling";'
+ },
+
+ patterns: {
+ // combinators must be listed first
+ // (and descendant needs to be last combinator)
+ laterSibling: /^\s*~\s*/,
+ child: /^\s*>\s*/,
+ adjacent: /^\s*\+\s*/,
+ descendant: /^\s/,
+
+ // selectors follow
+ tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
+ id: /^#([\w\-\*]+)(\b|$)/,
+ className: /^\.([\w\-\*]+)(\b|$)/,
+ pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
+ attrPresence: /^\[([\w]+)\]/,
+ attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
+ },
+
+ // for Selector.match and Element#match
+ assertions: {
+ tagName: function(element, matches) {
+ return matches[1].toUpperCase() == element.tagName.toUpperCase();
+ },
+
+ className: function(element, matches) {
+ return Element.hasClassName(element, matches[1]);
+ },
+
+ id: function(element, matches) {
+ return element.id === matches[1];
+ },
+
+ attrPresence: function(element, matches) {
+ return Element.hasAttribute(element, matches[1]);
+ },
+
+ attr: function(element, matches) {
+ var nodeValue = Element.readAttribute(element, matches[1]);
+ return Selector.operators[matches[2]](nodeValue, matches[3]);
+ }
+ },
+
+ handlers: {
+ // UTILITY FUNCTIONS
+ // joins two collections
+ concat: function(a, b) {
+ for (var i = 0, node; node = b[i]; i++)
+ a.push(node);
+ return a;
+ },
+
+ // marks an array of nodes for counting
+ mark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._counted = true;
+ return nodes;
+ },
+
+ unmark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._counted = undefined;
+ return nodes;
+ },
+
+ // mark each child node with its position (for nth calls)
+ // "ofType" flag indicates whether we're indexing for nth-of-type
+ // rather than nth-child
+ index: function(parentNode, reverse, ofType) {
+ parentNode._counted = true;
+ if (reverse) {
+ for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+ var node = nodes[i];
+ if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+ }
+ } else {
+ for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+ if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
+ }
+ },
+
+ // filters out duplicates and extends all nodes
+ unique: function(nodes) {
+ if (nodes.length == 0) return nodes;
+ var results = [], n;
+ for (var i = 0, l = nodes.length; i < l; i++)
+ if (!(n = nodes[i])._counted) {
+ n._counted = true;
+ results.push(Element.extend(n));
+ }
+ return Selector.handlers.unmark(results);
+ },
+
+ // COMBINATOR FUNCTIONS
+ descendant: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, node.getElementsByTagName('*'));
+ return results;
+ },
+
+ child: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
+ if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+ }
+ return results;
+ },
+
+ adjacent: function(nodes) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ var next = this.nextElementSibling(node);
+ if (next) results.push(next);
+ }
+ return results;
+ },
+
+ laterSibling: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, Element.nextSiblings(node));
+ return results;
+ },
+
+ nextElementSibling: function(node) {
+ while (node = node.nextSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ previousElementSibling: function(node) {
+ while (node = node.previousSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ // TOKEN FUNCTIONS
+ tagName: function(nodes, root, tagName, combinator) {
+ tagName = tagName.toUpperCase();
+ var results = [], h = Selector.handlers;
+ if (nodes) {
+ if (combinator) {
+ // fastlane for ordinary descendant combinators
+ if (combinator == "descendant") {
+ for (var i = 0, node; node = nodes[i]; i++)
+ h.concat(results, node.getElementsByTagName(tagName));
+ return results;
+ } else nodes = this[combinator](nodes);
+ if (tagName == "*") return nodes;
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.tagName.toUpperCase() == tagName) results.push(node);
+ return results;
+ } else return root.getElementsByTagName(tagName);
+ },
+
+ id: function(nodes, root, id, combinator) {
+ var targetNode = $(id), h = Selector.handlers;
+ if (!targetNode) return [];
+ if (!nodes && root == document) return [targetNode];
+ if (nodes) {
+ if (combinator) {
+ if (combinator == 'child') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (targetNode.parentNode == node) return [targetNode];
+ } else if (combinator == 'descendant') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.descendantOf(targetNode, node)) return [targetNode];
+ } else if (combinator == 'adjacent') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Selector.handlers.previousElementSibling(targetNode) == node)
+ return [targetNode];
+ } else nodes = h[combinator](nodes);
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node == targetNode) return [targetNode];
+ return [];
+ }
+ return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+ },
+
+ className: function(nodes, root, className, combinator) {
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ return Selector.handlers.byClassName(nodes, root, className);
+ },
+
+ byClassName: function(nodes, root, className) {
+ if (!nodes) nodes = Selector.handlers.descendant([root]);
+ var needle = ' ' + className + ' ';
+ for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+ nodeClassName = node.className;
+ if (nodeClassName.length == 0) continue;
+ if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+ results.push(node);
+ }
+ return results;
+ },
+
+ attrPresence: function(nodes, root, attr) {
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ var results = [];
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.hasAttribute(node, attr)) results.push(node);
+ return results;
+ },
+
+ attr: function(nodes, root, attr, value, operator) {
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ var handler = Selector.operators[operator], results = [];
+ for (var i = 0, node; node = nodes[i]; i++) {
+ var nodeValue = Element.readAttribute(node, attr);
+ if (nodeValue === null) continue;
+ if (handler(nodeValue, value)) results.push(node);
+ }
+ return results;
+ },
+
+ pseudo: function(nodes, name, value, root, combinator) {
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ return Selector.pseudos[name](nodes, value, root);
+ }
+ },
+
+ pseudos: {
+ 'first-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.previousElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'last-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.nextElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'only-child': function(nodes, value, root) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+ results.push(node);
+ return results;
+ },
+ 'nth-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root);
+ },
+ 'nth-last-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true);
+ },
+ 'nth-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, false, true);
+ },
+ 'nth-last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true, true);
+ },
+ 'first-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, false, true);
+ },
+ 'last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, true, true);
+ },
+ 'only-of-type': function(nodes, formula, root) {
+ var p = Selector.pseudos;
+ return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+ },
+
+ // handles the an+b logic
+ getIndices: function(a, b, total) {
+ if (a == 0) return b > 0 ? [b] : [];
+ return $R(1, total).inject([], function(memo, i) {
+ if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+ return memo;
+ });
+ },
+
+ // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+ nth: function(nodes, formula, root, reverse, ofType) {
+ if (nodes.length == 0) return [];
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ var h = Selector.handlers, results = [], indexed = [], m;
+ h.mark(nodes);
+ for (var i = 0, node; node = nodes[i]; i++) {
+ if (!node.parentNode._counted) {
+ h.index(node.parentNode, reverse, ofType);
+ indexed.push(node.parentNode);
+ }
+ }
+ if (formula.match(/^\d+$/)) { // just a number
+ formula = Number(formula);
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.nodeIndex == formula) results.push(node);
+ } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+ if (m[1] == "-") m[1] = -1;
+ var a = m[1] ? Number(m[1]) : 1;
+ var b = m[2] ? Number(m[2]) : 0;
+ var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+ for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+ for (var j = 0; j < l; j++)
+ if (node.nodeIndex == indices[j]) results.push(node);
+ }
+ }
+ h.unmark(nodes);
+ h.unmark(indexed);
+ return results;
+ },
+
+ 'empty': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ // IE treats comments as element nodes
+ if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
+ results.push(node);
+ }
+ return results;
+ },
+
+ 'not': function(nodes, selector, root) {
+ var h = Selector.handlers, selectorType, m;
+ var exclusions = new Selector(selector).findElements(root);
+ h.mark(exclusions);
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node._counted) results.push(node);
+ h.unmark(exclusions);
+ return results;
+ },
+
+ 'enabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node.disabled) results.push(node);
+ return results;
+ },
+
+ 'disabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.disabled) results.push(node);
+ return results;
+ },
+
+ 'checked': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.checked) results.push(node);
+ return results;
+ }
+ },
+
+ operators: {
+ '=': function(nv, v) { return nv == v; },
+ '!=': function(nv, v) { return nv != v; },
+ '^=': function(nv, v) { return nv.startsWith(v); },
+ '$=': function(nv, v) { return nv.endsWith(v); },
+ '*=': function(nv, v) { return nv.include(v); },
+ '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+ '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
+ },
+
+ matchElements: function(elements, expression) {
+ var matches = new Selector(expression).findElements(), h = Selector.handlers;
+ h.mark(matches);
+ for (var i = 0, results = [], element; element = elements[i]; i++)
+ if (element._counted) results.push(element);
+ h.unmark(matches);
+ return results;
+ },
+
+ findElement: function(elements, expression, index) {
+ if (Object.isNumber(expression)) {
+ index = expression; expression = false;
+ }
+ return Selector.matchElements(elements, expression || '*')[index || 0];
+ },
+
+ findChildElements: function(element, expressions) {
+ var exprs = expressions.join(','), expressions = [];
+ exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+ expressions.push(m[1].strip());
+ });
+ var results = [], h = Selector.handlers;
+ for (var i = 0, l = expressions.length, selector; i < l; i++) {
+ selector = new Selector(expressions[i].strip());
+ h.concat(results, selector.findElements(element));
+ }
+ return (l > 1) ? h.unique(results) : results;
+ }
+});
+
+function $$() {
+ return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+ reset: function(form) {
+ $(form).reset();
+ return form;
+ },
+
+ serializeElements: function(elements, options) {
+ if (typeof options != 'object') options = { hash: !!options };
+ else if (options.hash === undefined) options.hash = true;
+ var key, value, submitted = false, submit = options.submit;
+
+ var data = elements.inject({ }, function(result, element) {
+ if (!element.disabled && element.name) {
+ key = element.name; value = $(element).getValue();
+ if (value != null && (element.type != 'submit' || (!submitted &&
+ submit !== false && (!submit || key == submit) && (submitted = true)))) {
+ if (key in result) {
+ // a key is already present; construct an array of values
+ if (!Object.isArray(result[key])) result[key] = [result[key]];
+ result[key].push(value);
+ }
+ else result[key] = value;
+ }
+ }
+ return result;
+ });
+
+ return options.hash ? data : Object.toQueryString(data);
+ }
+};
+
+Form.Methods = {
+ serialize: function(form, options) {
+ return Form.serializeElements(Form.getElements(form), options);
+ },
+
+ getElements: function(form) {
+ return $A($(form).getElementsByTagName('*')).inject([],
+ function(elements, child) {
+ if (Form.Element.Serializers[child.tagName.toLowerCase()])
+ elements.push(Element.extend(child));
+ return elements;
+ }
+ );
+ },
+
+ getInputs: function(form, typeName, name) {
+ form = $(form);
+ var inputs = form.getElementsByTagName('input');
+
+ if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+ for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+ var input = inputs[i];
+ if ((typeName && input.type != typeName) || (name && input.name != name))
+ continue;
+ matchingInputs.push(Element.extend(input));
+ }
+
+ return matchingInputs;
+ },
+
+ disable: function(form) {
+ form = $(form);
+ Form.getElements(form).invoke('disable');
+ return form;
+ },
+
+ enable: function(form) {
+ form = $(form);
+ Form.getElements(form).invoke('enable');
+ return form;
+ },
+
+ findFirstElement: function(form) {
+ var elements = $(form).getElements().findAll(function(element) {
+ return 'hidden' != element.type && !element.disabled;
+ });
+ var firstByIndex = elements.findAll(function(element) {
+ return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
+ }).sortBy(function(element) { return element.tabIndex }).first();
+
+ return firstByIndex ? firstByIndex : elements.find(function(element) {
+ return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+ });
+ },
+
+ focusFirstElement: function(form) {
+ form = $(form);
+ form.findFirstElement().activate();
+ return form;
+ },
+
+ request: function(form, options) {
+ form = $(form), options = Object.clone(options || { });
+
+ var params = options.parameters, action = form.readAttribute('action') || '';
+ if (action.blank()) action = window.location.href;
+ options.parameters = form.serialize(true);
+
+ if (params) {
+ if (Object.isString(params)) params = params.toQueryParams();
+ Object.extend(options.parameters, params);
+ }
+
+ if (form.hasAttribute('method') && !options.method)
+ options.method = form.method;
+
+ return new Ajax.Request(action, options);
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+ focus: function(element) {
+ $(element).focus();
+ return element;
+ },
+
+ select: function(element) {
+ $(element).select();
+ return element;
+ }
+};
+
+Form.Element.Methods = {
+ serialize: function(element) {
+ element = $(element);
+ if (!element.disabled && element.name) {
+ var value = element.getValue();
+ if (value != undefined) {
+ // XXX: Jifty: this used to be:
+ // var pair = { };
+ // pair[element.name] = value
+ // return Object.toQueryString(pair)
+ // but that included the pair.extend function, which occurred
+ // a lot whenever we validated an action. since we're only encoding
+ // the key (element.name) and value, do what we actually mean
+ return encodeURIComponent(element.name)
+ + '='
+ + encodeURIComponent(value);
+ }
+ }
+ return '';
+ },
+
+ getValue: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ return Form.Element.Serializers[method](element);
+ },
+
+ setValue: function(element, value) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ Form.Element.Serializers[method](element, value);
+ return element;
+ },
+
+ clear: function(element) {
+ $(element).value = '';
+ return element;
+ },
+
+ present: function(element) {
+ return $(element).value != '';
+ },
+
+ activate: function(element) {
+ element = $(element);
+ try {
+ element.focus();
+ if (element.select && (element.tagName.toLowerCase() != 'input' ||
+ !['button', 'reset', 'submit'].include(element.type)))
+ element.select();
+ } catch (e) { }
+ return element;
+ },
+
+ disable: function(element) {
+ element = $(element);
+ element.blur();
+ element.disabled = true;
+ return element;
+ },
+
+ enable: function(element) {
+ element = $(element);
+ element.disabled = false;
+ return element;
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.Methods.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+ input: function(element, value) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ return Form.Element.Serializers.inputSelector(element, value);
+ default:
+ return Form.Element.Serializers.textarea(element, value);
+ }
+ },
+
+ inputSelector: function(element, value) {
+ if (value === undefined) return element.checked ? element.value : null;
+ else element.checked = !!value;
+ },
+
+ textarea: function(element, value) {
+ if (value === undefined) return element.value;
+ else element.value = value;
+ },
+
+ select: function(element, index) {
+ if (index === undefined)
+ return this[element.type == 'select-one' ?
+ 'selectOne' : 'selectMany'](element);
+ else {
+ var opt, value, single = !Object.isArray(index);
+ for (var i = 0, length = element.length; i < length; i++) {
+ opt = element.options[i];
+ value = this.optionValue(opt);
+ if (single) {
+ if (value == index) {
+ opt.selected = true;
+ return;
+ }
+ }
+ else opt.selected = index.include(value);
+ }
+ }
+ },
+
+ selectOne: function(element) {
+ var index = element.selectedIndex;
+ return index >= 0 ? this.optionValue(element.options[index]) : null;
+ },
+
+ selectMany: function(element) {
+ var values, length = element.length;
+ if (!length) return null;
+
+ for (var i = 0, values = []; i < length; i++) {
+ var opt = element.options[i];
+ if (opt.selected) values.push(this.optionValue(opt));
+ }
+ return values;
+ },
+
+ optionValue: function(opt) {
+ // extend element because hasAttribute may not be native
+ return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
+ initialize: function($super, element, frequency, callback) {
+ $super(callback, frequency);
+ this.element = $(element);
+ this.lastValue = this.getValue();
+ },
+
+ execute: function() {
+ var value = this.getValue();
+ if (Object.isString(this.lastValue) && Object.isString(value) ?
+ this.lastValue != value : String(this.lastValue) != String(value)) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ }
+});
+
+Form.Element.Observer = Class.create(Abstract.TimedObserver, {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.Observer = Class.create(Abstract.TimedObserver, {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = Class.create({
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ if (this.element.tagName.toLowerCase() == 'form')
+ this.registerFormCallbacks();
+ else
+ this.registerCallback(this.element);
+ },
+
+ onElementEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ },
+
+ registerFormCallbacks: function() {
+ Form.getElements(this.element).each(this.registerCallback, this);
+ },
+
+ registerCallback: function(element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
+ break;
+ default:
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ break;
+ }
+ }
+ }
+});
+
+Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.EventObserver = Class.create(Abstract.EventObserver, {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+if (!window.Event) var Event = { };
+
+Object.extend(Event, {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+ KEY_HOME: 36,
+ KEY_END: 35,
+ KEY_PAGEUP: 33,
+ KEY_PAGEDOWN: 34,
+ KEY_INSERT: 45,
+
+ cache: { },
+
+ relatedTarget: function(event) {
+ var element;
+ switch(event.type) {
+ case 'mouseover': element = event.fromElement; break;
+ case 'mouseout': element = event.toElement; break;
+ default: return null;
+ }
+ return Element.extend(element);
+ }
+});
+
+Event.Methods = (function() {
+ var isButton;
+
+ if (Prototype.Browser.IE) {
+ var buttonMap = { 0: 1, 1: 4, 2: 2 };
+ isButton = function(event, code) {
+ return event.button == buttonMap[code];
+ };
+
+ } else if (Prototype.Browser.WebKit) {
+ isButton = function(event, code) {
+ switch (code) {
+ case 0: return event.which == 1 && !event.metaKey;
+ case 1: return event.which == 1 && event.metaKey;
+ default: return false;
+ }
+ };
+
+ } else {
+ isButton = function(event, code) {
+ return event.which ? (event.which === code + 1) : (event.button === code);
+ };
+ }
+
+ return {
+ isLeftClick: function(event) { return isButton(event, 0) },
+ isMiddleClick: function(event) { return isButton(event, 1) },
+ isRightClick: function(event) { return isButton(event, 2) },
+
+ element: function(event) {
+ var node = Event.extend(event).target;
+ return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
+ },
+
+ findElement: function(event, expression) {
+ var element = Event.element(event);
+ return element.match(expression) ? element : element.up(expression);
+ },
+
+ pointer: function(event) {
+ return {
+ x: event.pageX || (event.clientX +
+ (document.documentElement.scrollLeft || document.body.scrollLeft)),
+ y: event.pageY || (event.clientY +
+ (document.documentElement.scrollTop || document.body.scrollTop))
+ };
+ },
+
+ pointerX: function(event) { return Event.pointer(event).x },
+ pointerY: function(event) { return Event.pointer(event).y },
+
+ stop: function(event) {
+ Event.extend(event);
+ event.preventDefault();
+ event.stopPropagation();
+ event.stopped = true;
+ }
+ };
+})();
+
+Event.extend = (function() {
+ var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
+ m[name] = Event.Methods[name].methodize();
+ return m;
+ });
+
+ if (Prototype.Browser.IE) {
+ Object.extend(methods, {
+ stopPropagation: function() { this.cancelBubble = true },
+ preventDefault: function() { this.returnValue = false },
+ inspect: function() { return "[object Event]" }
+ });
+
+ return function(event) {
+ if (!event) return false;
+ if (event._extendedByPrototype) return event;
+
+ event._extendedByPrototype = Prototype.emptyFunction;
+ var pointer = Event.pointer(event);
+ Object.extend(event, {
+ target: event.srcElement,
+ relatedTarget: Event.relatedTarget(event),
+ pageX: pointer.x,
+ pageY: pointer.y
+ });
+ return Object.extend(event, methods);
+ };
+
+ } else {
+ Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
+ Object.extend(Event.prototype, methods);
+ return Prototype.K;
+ }
+})();
+
+Object.extend(Event, (function() {
+ var cache = Event.cache;
+
+ function getEventID(element) {
+ if (element._eventID) return element._eventID;
+ arguments.callee.id = arguments.callee.id || 1;
+ return element._eventID = ++arguments.callee.id;
+ }
+
+ function getDOMEventName(eventName) {
+ if (eventName && eventName.include(':')) return "dataavailable";
+ return eventName;
+ }
+
+ function getCacheForID(id) {
+ return cache[id] = cache[id] || { };
+ }
+
+ function getWrappersForEventName(id, eventName) {
+ var c = getCacheForID(id);
+ return c[eventName] = c[eventName] || [];
+ }
+
+ function createWrapper(element, eventName, handler) {
+ var id = getEventID(element);
+ var c = getWrappersForEventName(id, eventName);
+ if (c.pluck("handler").include(handler)) return false;
+
+ var wrapper = function(event) {
+ if (!Event || !Event.extend ||
+ (event.eventName && event.eventName != eventName))
+ return false;
+
+ Event.extend(event);
+ handler.call(element, event)
+ };
+
+ wrapper.handler = handler;
+ c.push(wrapper);
+ return wrapper;
+ }
+
+ function findWrapper(id, eventName, handler) {
+ var c = getWrappersForEventName(id, eventName);
+ return c.find(function(wrapper) { return wrapper.handler == handler });
+ }
+
+ function destroyWrapper(id, eventName, handler) {
+ var c = getCacheForID(id);
+ if (!c[eventName]) return false;
+ c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
+ }
+
+ function destroyCache() {
+ for (var id in cache)
+ for (var eventName in cache[id])
+ cache[id][eventName] = null;
+ }
+
+ if (window.attachEvent) {
+ window.attachEvent("onunload", destroyCache);
+ }
+
+ return {
+ observe: function(element, eventName, handler) {
+ element = $(element);
+ var name = getDOMEventName(eventName);
+
+ var wrapper = createWrapper(element, eventName, handler);
+ if (!wrapper) return element;
+
+ if (element.addEventListener) {
+ element.addEventListener(name, wrapper, false);
+ } else {
+ element.attachEvent("on" + name, wrapper);
+ }
+
+ return element;
+ },
+
+ stopObserving: function(element, eventName, handler) {
+ element = $(element);
+ var id = getEventID(element), name = getDOMEventName(eventName);
+
+ if (!handler && eventName) {
+ getWrappersForEventName(id, eventName).each(function(wrapper) {
+ element.stopObserving(eventName, wrapper.handler);
+ });
+ return element;
+
+ } else if (!eventName) {
+ Object.keys(getCacheForID(id)).each(function(eventName) {
+ element.stopObserving(eventName);
+ });
+ return element;
+ }
+
+ var wrapper = findWrapper(id, eventName, handler);
+ if (!wrapper) return element;
+
+ if (element.removeEventListener) {
+ element.removeEventListener(name, wrapper, false);
+ } else {
+ element.detachEvent("on" + name, wrapper);
+ }
+
+ destroyWrapper(id, eventName, handler);
+
+ return element;
+ },
+
+ fire: function(element, eventName, memo) {
+ element = $(element);
+ if (element == document && document.createEvent && !element.dispatchEvent)
+ element = document.documentElement;
+
+ if (document.createEvent) {
+ var event = document.createEvent("HTMLEvents");
+ event.initEvent("dataavailable", true, true);
+ } else {
+ var event = document.createEventObject();
+ event.eventType = "ondataavailable";
+ }
+
+ event.eventName = eventName;
+ event.memo = memo || { };
+
+ if (document.createEvent) {
+ element.dispatchEvent(event);
+ } else {
+ element.fireEvent(event.eventType, event);
+ }
+
+ return event;
+ }
+ };
+})());
+
+Object.extend(Event, Event.Methods);
+
+Element.addMethods({
+ fire: Event.fire,
+ observe: Event.observe,
+ stopObserving: Event.stopObserving
+});
+
+Object.extend(document, {
+ fire: Element.Methods.fire.methodize(),
+ observe: Element.Methods.observe.methodize(),
+ stopObserving: Element.Methods.stopObserving.methodize()
+});
+
+(function() {
+ /* Support for the DOMContentLoaded event is based on work by Dan Webb,
+ Matthias Miller, Dean Edwards and John Resig. */
+
+ var timer, fired = false;
+
+ function fireContentLoadedEvent() {
+ if (fired) return;
+ if (timer) window.clearInterval(timer);
+ document.fire("dom:loaded");
+ fired = true;
+ }
+
+ if (document.addEventListener) {
+ if (Prototype.Browser.WebKit) {
+ timer = window.setInterval(function() {
+ if (/loaded|complete/.test(document.readyState))
+ fireContentLoadedEvent();
+ }, 0);
+
+ Event.observe(window, "load", fireContentLoadedEvent);
+
+ } else {
+ document.addEventListener("DOMContentLoaded",
+ fireContentLoadedEvent, false);
+ }
+
+ } else {
+ document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
+ $("__onDOMContentLoaded").onreadystatechange = function() {
+ if (this.readyState == "complete") {
+ this.onreadystatechange = null;
+ fireContentLoadedEvent();
+ }
+ };
+ }
+})();
+/*------------------------------- DEPRECATED -------------------------------*/
+
+Hash.toQueryString = Object.toQueryString;
+
+var Toggle = { display: Element.toggle };
+
+Element.Methods.childOf = Element.Methods.descendantOf;
+
+var Insertion = {
+ Before: function(element, content) {
+ return Element.insert(element, {before:content});
+ },
+
+ Top: function(element, content) {
+ return Element.insert(element, {top:content});
+ },
+
+ Bottom: function(element, content) {
+ return Element.insert(element, {bottom:content});
+ },
+
+ After: function(element, content) {
+ return Element.insert(element, {after:content});
+ }
+};
+
+var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
+
+// This should be moved to script.aculo.us; notice the deprecated methods
+// further below, that map to the newer Element methods.
+var Position = {
+ // set to true if needed, warning: firefox performance problems
+ // NOT neeeded for page scrolling, only if draggable contained in
+ // scrollable elements
+ includeScrollOffsets: false,
+
+ // must be called before calling withinIncludingScrolloffset, every time the
+ // page is scrolled
+ prepare: function() {
+ this.deltaX = window.pageXOffset
+ || document.documentElement.scrollLeft
+ || document.body.scrollLeft
+ || 0;
+ this.deltaY = window.pageYOffset
+ || document.documentElement.scrollTop
+ || document.body.scrollTop
+ || 0;
+ },
+
+ // caches x/y coordinate pair to use with overlap
+ within: function(element, x, y) {
+ if (this.includeScrollOffsets)
+ return this.withinIncludingScrolloffsets(element, x, y);
+ this.xcomp = x;
+ this.ycomp = y;
+ this.offset = Element.cumulativeOffset(element);
+
+ return (y >= this.offset[1] &&
+ y < this.offset[1] + element.offsetHeight &&
+ x >= this.offset[0] &&
+ x < this.offset[0] + element.offsetWidth);
+ },
+
+ withinIncludingScrolloffsets: function(element, x, y) {
+ var offsetcache = Element.cumulativeScrollOffset(element);
+
+ this.xcomp = x + offsetcache[0] - this.deltaX;
+ this.ycomp = y + offsetcache[1] - this.deltaY;
+ this.offset = Element.cumulativeOffset(element);
+
+ return (this.ycomp >= this.offset[1] &&
+ this.ycomp < this.offset[1] + element.offsetHeight &&
+ this.xcomp >= this.offset[0] &&
+ this.xcomp < this.offset[0] + element.offsetWidth);
+ },
+
+ // within must be called directly before
+ overlap: function(mode, element) {
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+ element.offsetHeight;
+ if (mode == 'horizontal')
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+ element.offsetWidth;
+ },
+
+ // Deprecation layer -- use newer Element methods now (1.5.2).
+
+ cumulativeOffset: Element.Methods.cumulativeOffset,
+
+ positionedOffset: Element.Methods.positionedOffset,
+
+ absolutize: function(element) {
+ Position.prepare();
+ return Element.absolutize(element);
+ },
+
+ relativize: function(element) {
+ Position.prepare();
+ return Element.relativize(element);
+ },
+
+ realOffset: Element.Methods.cumulativeScrollOffset,
+
+ offsetParent: Element.Methods.getOffsetParent,
+
+ page: Element.Methods.viewportOffset,
+
+ clone: function(source, target, options) {
+ options = options || { };
+ return Element.clonePosition(target, source, options);
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
+ function iter(name) {
+ return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
+ }
+
+ instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
+ function(element, className) {
+ className = className.toString().strip();
+ var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
+ return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
+ } : function(element, className) {
+ className = className.toString().strip();
+ var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
+ if (!classNames && !className) return elements;
+
+ var nodes = $(element).getElementsByTagName('*');
+ className = ' ' + className + ' ';
+
+ for (var i = 0, child, cn; child = nodes[i]; i++) {
+ if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
+ (classNames && classNames.all(function(name) {
+ return !name.toString().blank() && cn.include(' ' + name + ' ');
+ }))))
+ elements.push(Element.extend(child));
+ }
+ return elements;
+ };
+
+ return function(className, parentElement) {
+ return $(parentElement || document.body).getElementsByClassName(className);
+ };
+}(Element.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+ initialize: function(element) {
+ this.element = $(element);
+ },
+
+ _each: function(iterator) {
+ this.element.className.split(/\s+/).select(function(name) {
+ return name.length > 0;
+ })._each(iterator);
+ },
+
+ set: function(className) {
+ this.element.className = className;
+ },
+
+ add: function(classNameToAdd) {
+ if (this.include(classNameToAdd)) return;
+ this.set($A(this).concat(classNameToAdd).join(' '));
+ },
+
+ remove: function(classNameToRemove) {
+ if (!this.include(classNameToRemove)) return;
+ this.set($A(this).without(classNameToRemove).join(' '));
+ },
+
+ toString: function() {
+ return $A(this).join(' ');
+ }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+
+/*--------------------------------------------------------------------------*/
+
+Element.addMethods();
Added: jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/builder.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/builder.js Wed Apr 9 00:12:34 2008
@@ -0,0 +1,101 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// See scriptaculous.js for full license.
+
+var Builder = {
+ NODEMAP: {
+ AREA: 'map',
+ CAPTION: 'table',
+ COL: 'table',
+ COLGROUP: 'table',
+ LEGEND: 'fieldset',
+ OPTGROUP: 'select',
+ OPTION: 'select',
+ PARAM: 'object',
+ TBODY: 'table',
+ TD: 'table',
+ TFOOT: 'table',
+ TH: 'table',
+ THEAD: 'table',
+ TR: 'table'
+ },
+ // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
+ // due to a Firefox bug
+ node: function(elementName) {
+ elementName = elementName.toUpperCase();
+
+ // try innerHTML approach
+ var parentTag = this.NODEMAP[elementName] || 'div';
+ var parentElement = document.createElement(parentTag);
+ try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+ parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
+ } catch(e) {}
+ var element = parentElement.firstChild || null;
+
+ // see if browser added wrapping tags
+ if(element && (element.tagName != elementName))
+ element = element.getElementsByTagName(elementName)[0];
+
+ // fallback to createElement approach
+ if(!element) element = document.createElement(elementName);
+
+ // abort if nothing could be created
+ if(!element) return;
+
+ // attributes (or text)
+ if(arguments[1])
+ if(this._isStringOrNumber(arguments[1]) ||
+ (arguments[1] instanceof Array)) {
+ this._children(element, arguments[1]);
+ } else {
+ var attrs = this._attributes(arguments[1]);
+ if(attrs.length) {
+ try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+ parentElement.innerHTML = "<" +elementName + " " +
+ attrs + "></" + elementName + ">";
+ } catch(e) {}
+ element = parentElement.firstChild || null;
+ // workaround firefox 1.0.X bug
+ if(!element) {
+ element = document.createElement(elementName);
+ for(attr in arguments[1])
+ element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
+ }
+ if(element.tagName != elementName)
+ element = parentElement.getElementsByTagName(elementName)[0];
+ }
+ }
+
+ // text, or array of children
+ if(arguments[2])
+ this._children(element, arguments[2]);
+
+ return element;
+ },
+ _text: function(text) {
+ return document.createTextNode(text);
+ },
+ _attributes: function(attributes) {
+ var attrs = [];
+ for(attribute in attributes)
+ attrs.push((attribute=='className' ? 'class' : attribute) +
+ '="' + attributes[attribute].toString().escapeHTML() + '"');
+ return attrs.join(" ");
+ },
+ _children: function(element, children) {
+ if(typeof children=='object') { // array can hold nodes and text
+ children.flatten().each( function(e) {
+ if(typeof e=='object')
+ element.appendChild(e)
+ else
+ if(Builder._isStringOrNumber(e))
+ element.appendChild(Builder._text(e));
+ });
+ } else
+ if(Builder._isStringOrNumber(children))
+ element.appendChild(Builder._text(children));
+ },
+ _isStringOrNumber: function(param) {
+ return(typeof param=='string' || typeof param=='number');
+ }
+}
\ No newline at end of file
Added: jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/controls.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/controls.js Wed Apr 9 00:12:34 2008
@@ -0,0 +1,776 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
+// (c) 2005 Jon Tirsen (http://www.tirsen.com)
+// Contributors:
+// Richard Livsey
+// Rahul Bhargava
+// Rob Wills
+//
+// See scriptaculous.js for full license.
+
+// Autocompleter.Base handles all the autocompletion functionality
+// that's independent of the data source for autocompletion. This
+// includes drawing the autocompletion menu, observing keyboard
+// and mouse events, and similar.
+//
+// Specific autocompleters need to provide, at the very least,
+// a getUpdatedChoices function that will be invoked every time
+// the text inside the monitored textbox changes. This method
+// should get the text for which to provide autocompletion by
+// invoking this.getToken(), NOT by directly accessing
+// this.element.value. This is to allow incremental tokenized
+// autocompletion. Specific auto-completion logic (AJAX, etc)
+// belongs in getUpdatedChoices.
+//
+// Tokenized incremental autocompletion is enabled automatically
+// when an autocompleter is instantiated with the 'tokens' option
+// in the options parameter, e.g.:
+// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
+// will incrementally autocomplete with a comma as the token.
+// Additionally, ',' in the above example can be replaced with
+// a token array, e.g. { tokens: [',', '\n'] } which
+// enables autocompletion on multiple tokens. This is most
+// useful when one of the tokens is \n (a newline), as it
+// allows smart autocompletion after linebreaks.
+
+var Autocompleter = {}
+Autocompleter.Base = function() {};
+Autocompleter.Base.prototype = {
+ baseInitialize: function(element, update, options) {
+ this.element = $(element);
+ this.update = $(update);
+ this.hasFocus = false;
+ this.changed = false;
+ this.active = false;
+ this.index = 0;
+ this.entryCount = 0;
+
+ if (this.setOptions)
+ this.setOptions(options);
+ else
+ this.options = options || {};
+
+ this.options.paramName = this.options.paramName || this.element.name;
+ this.options.tokens = this.options.tokens || [];
+ this.options.frequency = this.options.frequency || 0.4;
+ this.options.minChars = this.options.minChars || 1;
+ this.options.onShow = this.options.onShow ||
+ function(element, update){
+ if(!update.style.position || update.style.position=='absolute') {
+ update.style.position = 'absolute';
+ Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
+ }
+ Effect.Appear(update,{duration:0.15});
+ };
+ this.options.onHide = this.options.onHide ||
+ function(element, update){ new Effect.Fade(update,{duration:0.15}) };
+
+ if (typeof(this.options.tokens) == 'string')
+ this.options.tokens = new Array(this.options.tokens);
+
+ this.observer = null;
+
+ this.element.setAttribute('autocomplete','off');
+
+ Element.hide(this.update);
+
+ Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
+ Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
+ },
+
+ show: function() {
+ /* Next line added by TRS, 07 July 2006 */
+ if ( this.options.beforeShow ) this.options.beforeShow(this);
+
+ if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
+ if(!this.iefix &&
+ (navigator.appVersion.indexOf('MSIE')>0) &&
+ (navigator.userAgent.indexOf('Opera')<0) &&
+ (Element.getStyle(this.update, 'position')=='absolute')) {
+ new Insertion.After(this.update,
+ '<iframe id="' + this.update.id + '_iefix" '+
+ 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
+ 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+ this.iefix = $(this.update.id+'_iefix');
+ }
+ if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
+ },
+
+ fixIEOverlapping: function() {
+ Position.clone(this.update, this.iefix);
+ this.iefix.style.zIndex = 1;
+ this.update.style.zIndex = 2;
+ Element.show(this.iefix);
+ },
+
+ hide: function() {
+ /* Next line added by TRS, 07 July 2006 */
+ if ( this.options.beforeHide ) this.options.beforeHide(this);
+
+ this.stopIndicator();
+ if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
+ if(this.iefix) Element.hide(this.iefix);
+ },
+
+ startIndicator: function() {
+ if(this.options.indicator) Element.show(this.options.indicator);
+ },
+
+ stopIndicator: function() {
+ if(this.options.indicator) Element.hide(this.options.indicator);
+ },
+
+ onKeyPress: function(event) {
+ if(this.active)
+ switch(event.keyCode) {
+ case Event.KEY_TAB:
+ case Event.KEY_RETURN:
+ this.selectEntry();
+ Event.stop(event);
+ case Event.KEY_ESC:
+ this.hide();
+ this.active = false;
+ Event.stop(event);
+ return;
+ case Event.KEY_LEFT:
+ case Event.KEY_RIGHT:
+ return;
+ case Event.KEY_UP:
+ this.markPrevious();
+ this.render();
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+ return;
+ case Event.KEY_DOWN:
+ this.markNext();
+ this.render();
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
+ return;
+ }
+ else
+ if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN)
+ return;
+
+ this.changed = true;
+ this.hasFocus = true;
+
+ if(this.observer) clearTimeout(this.observer);
+ this.observer =
+ setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
+ },
+
+ onHover: function(event) {
+ var element = Event.findElement(event, 'LI');
+ if(this.index != element.autocompleteIndex)
+ {
+ this.index = element.autocompleteIndex;
+ this.render();
+ }
+ Event.stop(event);
+ },
+
+ onClick: function(event) {
+ var element = Event.findElement(event, 'LI');
+ this.index = element.autocompleteIndex;
+ this.selectEntry();
+ this.hide();
+ },
+
+ onBlur: function(event) {
+ // needed to make click events working
+ setTimeout(this.hide.bind(this), 250);
+ this.hasFocus = false;
+ this.active = false;
+ },
+
+ render: function() {
+ if(this.entryCount > 0) {
+ for (var i = 0; i < this.entryCount; i++)
+ this.index==i ?
+ Element.addClassName(this.getEntry(i),"selected") :
+ Element.removeClassName(this.getEntry(i),"selected");
+
+ if(this.hasFocus) {
+ this.show();
+ this.active = true;
+ }
+ } else {
+ this.active = false;
+ this.hide();
+ }
+ },
+
+ markPrevious: function() {
+ if(this.index > 0) this.index--
+ else this.index = this.entryCount-1;
+ },
+
+ markNext: function() {
+ if(this.index < this.entryCount-1) this.index++
+ else this.index = 0;
+ },
+
+ getEntry: function(index) {
+ return this.update.firstChild.childNodes[index];
+ },
+
+ getCurrentEntry: function() {
+ return this.getEntry(this.index);
+ },
+
+ selectEntry: function() {
+ this.active = false;
+ this.updateElement(this.getCurrentEntry());
+ },
+
+ updateElement: function(selectedElement) {
+ if (this.options.updateElement) {
+ this.options.updateElement(selectedElement);
+ return;
+ }
+ var value = '';
+ if (this.options.select) {
+ var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
+ if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
+ } else
+ value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
+
+ var lastTokenPos = this.findLastToken();
+ if (lastTokenPos != -1) {
+ var newValue = this.element.value.substr(0, lastTokenPos + 1);
+ var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
+ if (whitespace)
+ newValue += whitespace[0];
+ this.element.value = newValue + value;
+ } else {
+ this.element.value = value;
+ }
+ this.element.focus();
+
+ if (this.options.afterUpdateElement)
+ this.options.afterUpdateElement(this.element, selectedElement);
+ },
+
+ updateChoices: function(choices) {
+ if(!this.changed && this.hasFocus) {
+ this.update.innerHTML = choices;
+ Element.cleanWhitespace(this.update);
+ Element.cleanWhitespace(this.update.firstChild);
+
+ if(this.update.firstChild && this.update.firstChild.childNodes) {
+ this.entryCount =
+ this.update.firstChild.childNodes.length;
+ for (var i = 0; i < this.entryCount; i++) {
+ var entry = this.getEntry(i);
+ entry.autocompleteIndex = i;
+ this.addObservers(entry);
+ }
+ } else {
+ this.entryCount = 0;
+ }
+
+ this.stopIndicator();
+
+ this.index = 0;
+ this.render();
+ }
+ },
+
+ addObservers: function(element) {
+ Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
+ Event.observe(element, "click", this.onClick.bindAsEventListener(this));
+ },
+
+ onObserverEvent: function() {
+ this.changed = false;
+ if(this.getToken().length>=this.options.minChars) {
+ this.startIndicator();
+ this.getUpdatedChoices();
+ } else {
+ this.active = false;
+ this.hide();
+ }
+ },
+
+ getToken: function() {
+ var tokenPos = this.findLastToken();
+ if (tokenPos != -1)
+ var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
+ else
+ var ret = this.element.value;
+
+ return /\n/.test(ret) ? '' : ret;
+ },
+
+ findLastToken: function() {
+ var lastTokenPos = -1;
+
+ for (var i=0; i<this.options.tokens.length; i++) {
+ var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
+ if (thisTokenPos > lastTokenPos)
+ lastTokenPos = thisTokenPos;
+ }
+ return lastTokenPos;
+ }
+}
+
+Ajax.Autocompleter = Class.create();
+Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
+ initialize: function(element, update, url, options) {
+ this.baseInitialize(element, update, options);
+ this.options.asynchronous = true;
+ this.options.onComplete = this.onComplete.bind(this);
+ this.options.defaultParams = this.options.parameters || null;
+ this.url = url;
+ },
+
+ getUpdatedChoices: function() {
+ entry = encodeURIComponent(this.options.paramName) + '=' +
+ encodeURIComponent(this.getToken());
+
+ this.options.parameters = this.options.callback ?
+ this.options.callback(this.element, entry) : entry;
+
+ if(this.options.defaultParams)
+ this.options.parameters += '&' + this.options.defaultParams;
+
+ new Ajax.Request(this.url, this.options);
+ },
+
+ onComplete: function(request) {
+ this.updateChoices(request.responseText);
+ }
+
+});
+
+// The local array autocompleter. Used when you'd prefer to
+// inject an array of autocompletion options into the page, rather
+// than sending out Ajax queries, which can be quite slow sometimes.
+//
+// The constructor takes four parameters. The first two are, as usual,
+// the id of the monitored textbox, and id of the autocompletion menu.
+// The third is the array you want to autocomplete from, and the fourth
+// is the options block.
+//
+// Extra local autocompletion options:
+// - choices - How many autocompletion choices to offer
+//
+// - partialSearch - If false, the autocompleter will match entered
+// text only at the beginning of strings in the
+// autocomplete array. Defaults to true, which will
+// match text at the beginning of any *word* in the
+// strings in the autocomplete array. If you want to
+// search anywhere in the string, additionally set
+// the option fullSearch to true (default: off).
+//
+// - fullSsearch - Search anywhere in autocomplete array strings.
+//
+// - partialChars - How many characters to enter before triggering
+// a partial match (unlike minChars, which defines
+// how many characters are required to do any match
+// at all). Defaults to 2.
+//
+// - ignoreCase - Whether to ignore case when autocompleting.
+// Defaults to true.
+//
+// It's possible to pass in a custom function as the 'selector'
+// option, if you prefer to write your own autocompletion logic.
+// In that case, the other options above will not apply unless
+// you support them.
+
+Autocompleter.Local = Class.create();
+Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
+ initialize: function(element, update, array, options) {
+ this.baseInitialize(element, update, options);
+ this.options.array = array;
+ },
+
+ getUpdatedChoices: function() {
+ this.updateChoices(this.options.selector(this));
+ },
+
+ setOptions: function(options) {
+ this.options = Object.extend({
+ choices: 10,
+ partialSearch: true,
+ partialChars: 2,
+ ignoreCase: true,
+ fullSearch: false,
+ selector: function(instance) {
+ var ret = []; // Beginning matches
+ var partial = []; // Inside matches
+ var entry = instance.getToken();
+ var count = 0;
+
+ for (var i = 0; i < instance.options.array.length &&
+ ret.length < instance.options.choices ; i++) {
+
+ var elem = instance.options.array[i];
+ var foundPos = instance.options.ignoreCase ?
+ elem.toLowerCase().indexOf(entry.toLowerCase()) :
+ elem.indexOf(entry);
+
+ while (foundPos != -1) {
+ if (foundPos == 0 && elem.length != entry.length) {
+ ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
+ elem.substr(entry.length) + "</li>");
+ break;
+ } else if (entry.length >= instance.options.partialChars &&
+ instance.options.partialSearch && foundPos != -1) {
+ if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
+ partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
+ elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
+ foundPos + entry.length) + "</li>");
+ break;
+ }
+ }
+
+ foundPos = instance.options.ignoreCase ?
+ elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
+ elem.indexOf(entry, foundPos + 1);
+
+ }
+ }
+ if (partial.length)
+ ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
+ return "<ul>" + ret.join('') + "</ul>";
+ }
+ }, options || {});
+ }
+});
+
+// AJAX in-place editor
+//
+// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
+
+// Use this if you notice weird scrolling problems on some browsers,
+// the DOM might be a bit confused when this gets called so do this
+// waits 1 ms (with setTimeout) until it does the activation
+Field.scrollFreeActivate = function(field) {
+ setTimeout(function() {
+ Field.activate(field);
+ }, 1);
+}
+
+Ajax.InPlaceEditor = Class.create();
+Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
+Ajax.InPlaceEditor.prototype = {
+ initialize: function(element, url, options) {
+ this.url = url;
+ this.element = $(element);
+
+ this.options = Object.extend({
+ okButton: true,
+ okText: "ok",
+ cancelLink: true,
+ cancelText: "cancel",
+ savingText: "Saving...",
+ clickToEditText: "Click to edit",
+ okText: "ok",
+ rows: 1,
+ onComplete: function(transport, element) {
+ new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
+ },
+ onFailure: function(transport) {
+ alert("Error communicating with the server: " + transport.responseText.stripTags());
+ },
+ callback: function(form) {
+ return Form.serialize(form);
+ },
+ handleLineBreaks: true,
+ loadingText: 'Loading...',
+ savingClassName: 'inplaceeditor-saving',
+ loadingClassName: 'inplaceeditor-loading',
+ formClassName: 'inplaceeditor-form',
+ highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
+ highlightendcolor: "#FFFFFF",
+ externalControl: null,
+ submitOnBlur: false,
+ ajaxOptions: {}
+ }, options || {});
+
+ if(!this.options.formId && this.element.id) {
+ this.options.formId = this.element.id + "-inplaceeditor";
+ if ($(this.options.formId)) {
+ // there's already a form with that name, don't specify an id
+ this.options.formId = null;
+ }
+ }
+
+ if (this.options.externalControl) {
+ this.options.externalControl = $(this.options.externalControl);
+ }
+
+ this.originalBackground = Element.getStyle(this.element, 'background-color');
+ if (!this.originalBackground) {
+ this.originalBackground = "transparent";
+ }
+
+ this.element.title = this.options.clickToEditText;
+
+ this.onclickListener = this.enterEditMode.bindAsEventListener(this);
+ this.mouseoverListener = this.enterHover.bindAsEventListener(this);
+ this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
+ Event.observe(this.element, 'click', this.onclickListener);
+ Event.observe(this.element, 'mouseover', this.mouseoverListener);
+ Event.observe(this.element, 'mouseout', this.mouseoutListener);
+ if (this.options.externalControl) {
+ Event.observe(this.options.externalControl, 'click', this.onclickListener);
+ Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
+ Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
+ }
+ },
+ enterEditMode: function(evt) {
+ if (this.saving) return;
+ if (this.editing) return;
+ this.editing = true;
+ this.onEnterEditMode();
+ if (this.options.externalControl) {
+ Element.hide(this.options.externalControl);
+ }
+ Element.hide(this.element);
+ this.createForm();
+ this.element.parentNode.insertBefore(this.form, this.element);
+ Field.scrollFreeActivate(this.editField);
+ // stop the event to avoid a page refresh in Safari
+ if (evt) {
+ Event.stop(evt);
+ }
+ return false;
+ },
+ createForm: function() {
+ this.form = document.createElement("form");
+ this.form.id = this.options.formId;
+ Element.addClassName(this.form, this.options.formClassName)
+ this.form.onsubmit = this.onSubmit.bind(this);
+
+ this.createEditField();
+
+ if (this.options.textarea) {
+ var br = document.createElement("br");
+ this.form.appendChild(br);
+ }
+
+ if (this.options.okButton) {
+ okButton = document.createElement("input");
+ okButton.type = "submit";
+ okButton.value = this.options.okText;
+ this.form.appendChild(okButton);
+ }
+
+ if (this.options.cancelLink) {
+ cancelLink = document.createElement("a");
+ cancelLink.href = "#";
+ cancelLink.appendChild(document.createTextNode(this.options.cancelText));
+ cancelLink.onclick = this.onclickCancel.bind(this);
+ this.form.appendChild(cancelLink);
+ }
+ },
+ hasHTMLLineBreaks: function(string) {
+ if (!this.options.handleLineBreaks) return false;
+ return string.match(/<br/i) || string.match(/<p>/i);
+ },
+ convertHTMLLineBreaks: function(string) {
+ return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
+ },
+ createEditField: function() {
+ var text;
+ if(this.options.loadTextURL) {
+ text = this.options.loadingText;
+ } else {
+ text = this.getText();
+ }
+
+ var obj = this;
+
+ if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
+ this.options.textarea = false;
+ var textField = document.createElement("input");
+ textField.obj = this;
+ textField.type = "text";
+ textField.name = "value";
+ textField.value = text;
+ textField.style.backgroundColor = this.options.highlightcolor;
+ var size = this.options.size || this.options.cols || 0;
+ if (size != 0) textField.size = size;
+ if (this.options.submitOnBlur)
+ textField.onblur = this.onSubmit.bind(this);
+ this.editField = textField;
+ } else {
+ this.options.textarea = true;
+ var textArea = document.createElement("textarea");
+ textArea.obj = this;
+ textArea.name = "value";
+ textArea.value = this.convertHTMLLineBreaks(text);
+ textArea.rows = this.options.rows;
+ textArea.cols = this.options.cols || 40;
+ if (this.options.submitOnBlur)
+ textArea.onblur = this.onSubmit.bind(this);
+ this.editField = textArea;
+ }
+
+ if(this.options.loadTextURL) {
+ this.loadExternalText();
+ }
+ this.form.appendChild(this.editField);
+ },
+ getText: function() {
+ return this.element.innerHTML;
+ },
+ loadExternalText: function() {
+ Element.addClassName(this.form, this.options.loadingClassName);
+ this.editField.disabled = true;
+ new Ajax.Request(
+ this.options.loadTextURL,
+ Object.extend({
+ asynchronous: true,
+ onComplete: this.onLoadedExternalText.bind(this)
+ }, this.options.ajaxOptions)
+ );
+ },
+ onLoadedExternalText: function(transport) {
+ Element.removeClassName(this.form, this.options.loadingClassName);
+ this.editField.disabled = false;
+ this.editField.value = transport.responseText.stripTags();
+ },
+ onclickCancel: function() {
+ this.onComplete();
+ this.leaveEditMode();
+ return false;
+ },
+ onFailure: function(transport) {
+ this.options.onFailure(transport);
+ if (this.oldInnerHTML) {
+ this.element.innerHTML = this.oldInnerHTML;
+ this.oldInnerHTML = null;
+ }
+ return false;
+ },
+ onSubmit: function() {
+ // onLoading resets these so we need to save them away for the Ajax call
+ var form = this.form;
+ var value = this.editField.value;
+
+ // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
+ // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
+ // to be displayed indefinitely
+ this.onLoading();
+
+ new Ajax.Updater(
+ {
+ success: this.element,
+ // don't update on failure (this could be an option)
+ failure: null
+ },
+ this.url,
+ Object.extend({
+ parameters: this.options.callback(form, value),
+ onComplete: this.onComplete.bind(this),
+ onFailure: this.onFailure.bind(this)
+ }, this.options.ajaxOptions)
+ );
+ // stop the event to avoid a page refresh in Safari
+ if (arguments.length > 1) {
+ Event.stop(arguments[0]);
+ }
+ return false;
+ },
+ onLoading: function() {
+ this.saving = true;
+ this.removeForm();
+ this.leaveHover();
+ this.showSaving();
+ },
+ showSaving: function() {
+ this.oldInnerHTML = this.element.innerHTML;
+ this.element.innerHTML = this.options.savingText;
+ Element.addClassName(this.element, this.options.savingClassName);
+ this.element.style.backgroundColor = this.originalBackground;
+ Element.show(this.element);
+ },
+ removeForm: function() {
+ if(this.form) {
+ if (this.form.parentNode) Element.remove(this.form);
+ this.form = null;
+ }
+ },
+ enterHover: function() {
+ if (this.saving) return;
+ this.element.style.backgroundColor = this.options.highlightcolor;
+ if (this.effect) {
+ this.effect.cancel();
+ }
+ Element.addClassName(this.element, this.options.hoverClassName)
+ },
+ leaveHover: function() {
+ if (this.options.backgroundColor) {
+ this.element.style.backgroundColor = this.oldBackground;
+ }
+ Element.removeClassName(this.element, this.options.hoverClassName)
+ if (this.saving) return;
+ this.effect = new Effect.Highlight(this.element, {
+ startcolor: this.options.highlightcolor,
+ endcolor: this.options.highlightendcolor,
+ restorecolor: this.originalBackground
+ });
+ },
+ leaveEditMode: function() {
+ Element.removeClassName(this.element, this.options.savingClassName);
+ this.removeForm();
+ this.leaveHover();
+ this.element.style.backgroundColor = this.originalBackground;
+ Element.show(this.element);
+ if (this.options.externalControl) {
+ Element.show(this.options.externalControl);
+ }
+ this.editing = false;
+ this.saving = false;
+ this.oldInnerHTML = null;
+ this.onLeaveEditMode();
+ },
+ onComplete: function(transport) {
+ this.leaveEditMode();
+ this.options.onComplete.bind(this)(transport, this.element);
+ },
+ onEnterEditMode: function() {},
+ onLeaveEditMode: function() {},
+ dispose: function() {
+ if (this.oldInnerHTML) {
+ this.element.innerHTML = this.oldInnerHTML;
+ }
+ this.leaveEditMode();
+ Event.stopObserving(this.element, 'click', this.onclickListener);
+ Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
+ Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
+ if (this.options.externalControl) {
+ Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
+ Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
+ Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
+ }
+ }
+};
+
+// Delayed observer, like Form.Element.Observer,
+// but waits for delay after last key input
+// Ideal for live-search fields
+
+Form.Element.DelayedObserver = Class.create();
+Form.Element.DelayedObserver.prototype = {
+ initialize: function(element, delay, callback) {
+ this.delay = delay || 0.5;
+ this.element = $(element);
+ this.callback = callback;
+ this.timer = null;
+ this.lastValue = $F(this.element);
+ Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
+ },
+ delayedListener: function(event) {
+ if(this.lastValue == $F(this.element)) return;
+ if(this.timer) clearTimeout(this.timer);
+ this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
+ this.lastValue = $F(this.element);
+ },
+ onTimerEvent: function() {
+ this.timer = null;
+ this.callback(this.element, $F(this.element));
+ }
+};
Added: jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/dragdrop.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/dragdrop.js Wed Apr 9 00:12:34 2008
@@ -0,0 +1,585 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// See scriptaculous.js for full license.
+
+/*--------------------------------------------------------------------------*/
+
+var Droppables = {
+ drops: [],
+
+ remove: function(element) {
+ this.drops = this.drops.reject(function(d) { return d.element==$(element) });
+ },
+
+ add: function(element) {
+ element = $(element);
+ var options = Object.extend({
+ greedy: true,
+ hoverclass: null
+ }, arguments[1] || {});
+
+ // cache containers
+ if(options.containment) {
+ options._containers = [];
+ var containment = options.containment;
+ if((typeof containment == 'object') &&
+ (containment.constructor == Array)) {
+ containment.each( function(c) { options._containers.push($(c)) });
+ } else {
+ options._containers.push($(containment));
+ }
+ }
+
+ if(options.accept) options.accept = [options.accept].flatten();
+
+ Element.makePositioned(element); // fix IE
+ options.element = element;
+
+ this.drops.push(options);
+ },
+
+ isContained: function(element, drop) {
+ var parentNode = element.parentNode;
+ return drop._containers.detect(function(c) { return parentNode == c });
+ },
+
+ isAffected: function(point, element, drop) {
+ return (
+ (drop.element!=element) &&
+ ((!drop._containers) ||
+ this.isContained(element, drop)) &&
+ ((!drop.accept) ||
+ (Element.classNames(element).detect(
+ function(v) { return drop.accept.include(v) } ) )) &&
+ Position.within(drop.element, point[0], point[1]) );
+ },
+
+ deactivate: function(drop) {
+ if(drop.hoverclass)
+ Element.removeClassName(drop.element, drop.hoverclass);
+ this.last_active = null;
+ },
+
+ activate: function(drop) {
+ if(drop.hoverclass)
+ Element.addClassName(drop.element, drop.hoverclass);
+ this.last_active = drop;
+ },
+
+ show: function(point, element) {
+ if(!this.drops.length) return;
+
+ if(this.last_active) this.deactivate(this.last_active);
+ this.drops.each( function(drop) {
+ if(Droppables.isAffected(point, element, drop)) {
+ if(drop.onHover)
+ drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
+ if(drop.greedy) {
+ Droppables.activate(drop);
+ throw $break;
+ }
+ }
+ });
+ },
+
+ fire: function(event, element) {
+ if(!this.last_active) return;
+ Position.prepare();
+
+ if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
+ if (this.last_active.onDrop)
+ this.last_active.onDrop(element, this.last_active.element, event);
+ },
+
+ reset: function() {
+ if(this.last_active)
+ this.deactivate(this.last_active);
+ }
+}
+
+var Draggables = {
+ drags: [],
+ observers: [],
+
+ register: function(draggable) {
+ if(this.drags.length == 0) {
+ this.eventMouseUp = this.endDrag.bindAsEventListener(this);
+ this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
+ this.eventKeypress = this.keyPress.bindAsEventListener(this);
+
+ Event.observe(document, "mouseup", this.eventMouseUp);
+ Event.observe(document, "mousemove", this.eventMouseMove);
+ Event.observe(document, "keypress", this.eventKeypress);
+ }
+ this.drags.push(draggable);
+ },
+
+ unregister: function(draggable) {
+ this.drags = this.drags.reject(function(d) { return d==draggable });
+ if(this.drags.length == 0) {
+ Event.stopObserving(document, "mouseup", this.eventMouseUp);
+ Event.stopObserving(document, "mousemove", this.eventMouseMove);
+ Event.stopObserving(document, "keypress", this.eventKeypress);
+ }
+ },
+
+ activate: function(draggable) {
+ window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
+ this.activeDraggable = draggable;
+ },
+
+ deactivate: function(draggbale) {
+ this.activeDraggable = null;
+ },
+
+ updateDrag: function(event) {
+ if(!this.activeDraggable) return;
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ // Mozilla-based browsers fire successive mousemove events with
+ // the same coordinates, prevent needless redrawing (moz bug?)
+ if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
+ this._lastPointer = pointer;
+ this.activeDraggable.updateDrag(event, pointer);
+ },
+
+ endDrag: function(event) {
+ if(!this.activeDraggable) return;
+ this._lastPointer = null;
+ this.activeDraggable.endDrag(event);
+ this.activeDraggable = null;
+ },
+
+ keyPress: function(event) {
+ if(this.activeDraggable)
+ this.activeDraggable.keyPress(event);
+ },
+
+ addObserver: function(observer) {
+ this.observers.push(observer);
+ this._cacheObserverCallbacks();
+ },
+
+ removeObserver: function(element) { // element instead of observer fixes mem leaks
+ this.observers = this.observers.reject( function(o) { return o.element==element });
+ this._cacheObserverCallbacks();
+ },
+
+ notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
+ if(this[eventName+'Count'] > 0)
+ this.observers.each( function(o) {
+ if(o[eventName]) o[eventName](eventName, draggable, event);
+ });
+ },
+
+ _cacheObserverCallbacks: function() {
+ ['onStart','onEnd','onDrag'].each( function(eventName) {
+ Draggables[eventName+'Count'] = Draggables.observers.select(
+ function(o) { return o[eventName]; }
+ ).length;
+ });
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Draggable = Class.create();
+Draggable.prototype = {
+ initialize: function(element) {
+ var options = Object.extend({
+ handle: false,
+ starteffect: function(element) {
+ new Effect.Opacity(element, {duration:0.2, from:1.0, to:0.7});
+ },
+ reverteffect: function(element, top_offset, left_offset) {
+ var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
+ element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur});
+ },
+ endeffect: function(element) {
+ new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0});
+ },
+ zindex: 1000,
+ revert: false,
+ snap: false // false, or xy or [x,y] or function(x,y){ return [x,y] }
+ }, arguments[1] || {});
+
+ this.element = $(element);
+
+ if(options.handle && (typeof options.handle == 'string'))
+ this.handle = Element.childrenWithClassName(this.element, options.handle)[0];
+ if(!this.handle) this.handle = $(options.handle);
+ if(!this.handle) this.handle = this.element;
+
+ Element.makePositioned(this.element); // fix IE
+
+ this.delta = this.currentDelta();
+ this.options = options;
+ this.dragging = false;
+
+ this.eventMouseDown = this.initDrag.bindAsEventListener(this);
+ Event.observe(this.handle, "mousedown", this.eventMouseDown);
+
+ Draggables.register(this);
+ },
+
+ destroy: function() {
+ Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
+ Draggables.unregister(this);
+ },
+
+ currentDelta: function() {
+ return([
+ parseInt(Element.getStyle(this.element,'left') || '0'),
+ parseInt(Element.getStyle(this.element,'top') || '0')]);
+ },
+
+ initDrag: function(event) {
+ if(Event.isLeftClick(event)) {
+ // abort on form elements, fixes a Firefox issue
+ var src = Event.element(event);
+ if(src.tagName && (
+ src.tagName=='INPUT' ||
+ src.tagName=='SELECT' ||
+ src.tagName=='BUTTON' ||
+ src.tagName=='TEXTAREA')) return;
+
+ if(this.element._revert) {
+ this.element._revert.cancel();
+ this.element._revert = null;
+ }
+
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ var pos = Position.cumulativeOffset(this.element);
+ this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
+
+ Draggables.activate(this);
+ Event.stop(event);
+ }
+ },
+
+ startDrag: function(event) {
+ this.dragging = true;
+
+ if(this.options.zindex) {
+ this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
+ this.element.style.zIndex = this.options.zindex;
+ }
+
+ if(this.options.ghosting) {
+ this._clone = this.element.cloneNode(true);
+ Position.absolutize(this.element);
+ this.element.parentNode.insertBefore(this._clone, this.element);
+ }
+
+ Draggables.notify('onStart', this, event);
+ if(this.options.starteffect) this.options.starteffect(this.element);
+ },
+
+ updateDrag: function(event, pointer) {
+ if(!this.dragging) this.startDrag(event);
+ Position.prepare();
+ Droppables.show(pointer, this.element);
+ Draggables.notify('onDrag', this, event);
+ this.draw(pointer);
+ if(this.options.change) this.options.change(this);
+
+ // fix AppleWebKit rendering
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+ Event.stop(event);
+ },
+
+ finishDrag: function(event, success) {
+ this.dragging = false;
+
+ if(this.options.ghosting) {
+ Position.relativize(this.element);
+ Element.remove(this._clone);
+ this._clone = null;
+ }
+
+ if(success) Droppables.fire(event, this.element);
+ Draggables.notify('onEnd', this, event);
+
+ var revert = this.options.revert;
+ if(revert && typeof revert == 'function') revert = revert(this.element);
+
+ var d = this.currentDelta();
+ if(revert && this.options.reverteffect) {
+ this.options.reverteffect(this.element,
+ d[1]-this.delta[1], d[0]-this.delta[0]);
+ } else {
+ this.delta = d;
+ }
+
+ if(this.options.zindex)
+ this.element.style.zIndex = this.originalZ;
+
+ if(this.options.endeffect)
+ this.options.endeffect(this.element);
+
+ Draggables.deactivate(this);
+ Droppables.reset();
+ },
+
+ keyPress: function(event) {
+ if(!event.keyCode==Event.KEY_ESC) return;
+ this.finishDrag(event, false);
+ Event.stop(event);
+ },
+
+ endDrag: function(event) {
+ if(!this.dragging) return;
+ this.finishDrag(event, true);
+ Event.stop(event);
+ },
+
+ draw: function(point) {
+ var pos = Position.cumulativeOffset(this.element);
+ var d = this.currentDelta();
+ pos[0] -= d[0]; pos[1] -= d[1];
+
+ var p = [0,1].map(function(i){ return (point[i]-pos[i]-this.offset[i]) }.bind(this));
+
+ if(this.options.snap) {
+ if(typeof this.options.snap == 'function') {
+ p = this.options.snap(p[0],p[1]);
+ } else {
+ if(this.options.snap instanceof Array) {
+ p = p.map( function(v, i) {
+ return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
+ } else {
+ p = p.map( function(v) {
+ return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
+ }
+ }}
+
+ var style = this.element.style;
+ if((!this.options.constraint) || (this.options.constraint=='horizontal'))
+ style.left = p[0] + "px";
+ if((!this.options.constraint) || (this.options.constraint=='vertical'))
+ style.top = p[1] + "px";
+ if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var SortableObserver = Class.create();
+SortableObserver.prototype = {
+ initialize: function(element, observer) {
+ this.element = $(element);
+ this.observer = observer;
+ this.lastValue = Sortable.serialize(this.element);
+ },
+
+ onStart: function() {
+ this.lastValue = Sortable.serialize(this.element);
+ },
+
+ onEnd: function() {
+ Sortable.unmark();
+ if(this.lastValue != Sortable.serialize(this.element))
+ this.observer(this.element)
+ }
+}
+
+var Sortable = {
+ sortables: new Array(),
+
+ options: function(element){
+ element = $(element);
+ return this.sortables.detect(function(s) { return s.element == element });
+ },
+
+ destroy: function(element){
+ element = $(element);
+ this.sortables.findAll(function(s) { return s.element == element }).each(function(s){
+ Draggables.removeObserver(s.element);
+ s.droppables.each(function(d){ Droppables.remove(d) });
+ s.draggables.invoke('destroy');
+ });
+ this.sortables = this.sortables.reject(function(s) { return s.element == element });
+ },
+
+ create: function(element) {
+ element = $(element);
+ var options = Object.extend({
+ element: element,
+ tag: 'li', // assumes li children, override with tag: 'tagname'
+ dropOnEmpty: false,
+ tree: false, // fixme: unimplemented
+ overlap: 'vertical', // one of 'vertical', 'horizontal'
+ constraint: 'vertical', // one of 'vertical', 'horizontal', false
+ containment: element, // also takes array of elements (or id's); or false
+ handle: false, // or a CSS class
+ only: false,
+ hoverclass: null,
+ ghosting: false,
+ format: null,
+ onChange: Prototype.emptyFunction,
+ onUpdate: Prototype.emptyFunction
+ }, arguments[1] || {});
+
+ // clear any old sortable with same element
+ this.destroy(element);
+
+ // build options for the draggables
+ var options_for_draggable = {
+ revert: true,
+ ghosting: options.ghosting,
+ constraint: options.constraint,
+ handle: options.handle };
+
+ if(options.starteffect)
+ options_for_draggable.starteffect = options.starteffect;
+
+ if(options.reverteffect)
+ options_for_draggable.reverteffect = options.reverteffect;
+ else
+ if(options.ghosting) options_for_draggable.reverteffect = function(element) {
+ element.style.top = 0;
+ element.style.left = 0;
+ };
+
+ if(options.endeffect)
+ options_for_draggable.endeffect = options.endeffect;
+
+ if(options.zindex)
+ options_for_draggable.zindex = options.zindex;
+
+ // build options for the droppables
+ var options_for_droppable = {
+ overlap: options.overlap,
+ containment: options.containment,
+ hoverclass: options.hoverclass,
+ onHover: Sortable.onHover,
+ greedy: !options.dropOnEmpty
+ }
+
+ // fix for gecko engine
+ Element.cleanWhitespace(element);
+
+ options.draggables = [];
+ options.droppables = [];
+
+ // make it so
+
+ // drop on empty handling
+ if(options.dropOnEmpty) {
+ Droppables.add(element,
+ {containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false});
+ options.droppables.push(element);
+ }
+
+ (this.findElements(element, options) || []).each( function(e) {
+ // handles are per-draggable
+ var handle = options.handle ?
+ Element.childrenWithClassName(e, options.handle)[0] : e;
+ options.draggables.push(
+ new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
+ Droppables.add(e, options_for_droppable);
+ options.droppables.push(e);
+ });
+
+ // keep reference
+ this.sortables.push(options);
+
+ // for onupdate
+ Draggables.addObserver(new SortableObserver(element, options.onUpdate));
+
+ },
+
+ // return all suitable-for-sortable elements in a guaranteed order
+ findElements: function(element, options) {
+ if(!element.hasChildNodes()) return null;
+ var elements = [];
+ $A(element.childNodes).each( function(e) {
+ if(e.tagName && e.tagName.toUpperCase()==options.tag.toUpperCase() &&
+ (!options.only || (Element.hasClassName(e, options.only))))
+ elements.push(e);
+ if(options.tree) {
+ var grandchildren = this.findElements(e, options);
+ if(grandchildren) elements.push(grandchildren);
+ }
+ });
+
+ return (elements.length>0 ? elements.flatten() : null);
+ },
+
+ onHover: function(element, dropon, overlap) {
+ if(overlap>0.5) {
+ Sortable.mark(dropon, 'before');
+ if(dropon.previousSibling != element) {
+ var oldParentNode = element.parentNode;
+ element.style.visibility = "hidden"; // fix gecko rendering
+ dropon.parentNode.insertBefore(element, dropon);
+ if(dropon.parentNode!=oldParentNode)
+ Sortable.options(oldParentNode).onChange(element);
+ Sortable.options(dropon.parentNode).onChange(element);
+ }
+ } else {
+ Sortable.mark(dropon, 'after');
+ var nextElement = dropon.nextSibling || null;
+ if(nextElement != element) {
+ var oldParentNode = element.parentNode;
+ element.style.visibility = "hidden"; // fix gecko rendering
+ dropon.parentNode.insertBefore(element, nextElement);
+ if(dropon.parentNode!=oldParentNode)
+ Sortable.options(oldParentNode).onChange(element);
+ Sortable.options(dropon.parentNode).onChange(element);
+ }
+ }
+ },
+
+ onEmptyHover: function(element, dropon) {
+ if(element.parentNode!=dropon) {
+ var oldParentNode = element.parentNode;
+ dropon.appendChild(element);
+ Sortable.options(oldParentNode).onChange(element);
+ Sortable.options(dropon).onChange(element);
+ }
+ },
+
+ unmark: function() {
+ if(Sortable._marker) Element.hide(Sortable._marker);
+ },
+
+ mark: function(dropon, position) {
+ // mark on ghosting only
+ var sortable = Sortable.options(dropon.parentNode);
+ if(sortable && !sortable.ghosting) return;
+
+ if(!Sortable._marker) {
+ Sortable._marker = $('dropmarker') || document.createElement('DIV');
+ Element.hide(Sortable._marker);
+ Element.addClassName(Sortable._marker, 'dropmarker');
+ Sortable._marker.style.position = 'absolute';
+ document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
+ }
+ var offsets = Position.cumulativeOffset(dropon);
+ Sortable._marker.style.left = offsets[0] + 'px';
+ Sortable._marker.style.top = offsets[1] + 'px';
+
+ if(position=='after')
+ if(sortable.overlap == 'horizontal')
+ Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
+ else
+ Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
+
+ Element.show(Sortable._marker);
+ },
+
+ serialize: function(element) {
+ element = $(element);
+ var sortableOptions = this.options(element);
+ var options = Object.extend({
+ tag: sortableOptions.tag,
+ only: sortableOptions.only,
+ name: element.id,
+ format: sortableOptions.format || /^[^_]*_(.*)$/
+ }, arguments[1] || {});
+ return $(this.findElements(element, options) || []).map( function(item) {
+ return (encodeURIComponent(options.name) + "[]=" +
+ encodeURIComponent(item.id.match(options.format) ? item.id.match(options.format)[1] : ''));
+ }).join("&");
+ }
+}
\ No newline at end of file
Added: jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/effects.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/effects.js Wed Apr 9 00:12:34 2008
@@ -0,0 +1,903 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Contributors:
+// Justin Palmer (http://encytemedia.com/)
+// Mark Pilgrim (http://diveintomark.org/)
+// Martin Bialasinki
+//
+// See scriptaculous.js for full license.
+
+/* ------------- element ext -------------- */
+
+// converts rgb() and #xxx to #xxxxxx format,
+// returns self (or first argument) if not convertable
+String.prototype.parseColor = function() {
+ var color = '#';
+ if(this.slice(0,4) == 'rgb(') {
+ var cols = this.slice(4,this.length-1).split(',');
+ var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
+ } else {
+ if(this.slice(0,1) == '#') {
+ if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
+ if(this.length==7) color = this.toLowerCase();
+ }
+ }
+ return(color.length==7 ? color : (arguments[0] || this));
+}
+
+Element.collectTextNodes = function(element) {
+ return $A($(element).childNodes).collect( function(node) {
+ return (node.nodeType==3 ? node.nodeValue :
+ (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
+ }).flatten().join('');
+}
+
+Element.collectTextNodesIgnoreClass = function(element, className) {
+ return $A($(element).childNodes).collect( function(node) {
+ return (node.nodeType==3 ? node.nodeValue :
+ ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
+ Element.collectTextNodes(node) : ''));
+ }).flatten().join('');
+}
+
+Element.setStyle = function(element, style) {
+ element = $(element);
+ for(k in style) element.style[k.camelize()] = style[k];
+}
+
+Element.setContentZoom = function(element, percent) {
+ Element.setStyle(element, {fontSize: (percent/100) + 'em'});
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+}
+
+Element.getOpacity = function(element){
+ var opacity;
+ if (opacity = Element.getStyle(element, 'opacity'))
+ return parseFloat(opacity);
+ if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))
+ if(opacity[1]) return parseFloat(opacity[1]) / 100;
+ return 1.0;
+}
+
+Element.setOpacity = function(element, value){
+ element= $(element);
+ if (value == 1){
+ Element.setStyle(element, { opacity:
+ (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
+ 0.999999 : null });
+ if(/MSIE/.test(navigator.userAgent))
+ Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
+ } else {
+ if(value < 0.00001) value = 0;
+ Element.setStyle(element, {opacity: value});
+ if(/MSIE/.test(navigator.userAgent))
+ Element.setStyle(element,
+ { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
+ 'alpha(opacity='+value*100+')' });
+ }
+}
+
+Element.getInlineOpacity = function(element){
+ return $(element).style.opacity || '';
+}
+
+Element.childrenWithClassName = function(element, className) {
+ return $A($(element).getElementsByTagName('*')).select(
+ function(c) { return Element.hasClassName(c, className) });
+}
+
+Array.prototype.call = function() {
+ var args = arguments;
+ this.each(function(f){ f.apply(this, args) });
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+ tagifyText: function(element) {
+ var tagifyStyle = 'position:relative';
+ if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
+ element = $(element);
+ $A(element.childNodes).each( function(child) {
+ if(child.nodeType==3) {
+ child.nodeValue.toArray().each( function(character) {
+ element.insertBefore(
+ Builder.node('span',{style: tagifyStyle},
+ character == ' ' ? String.fromCharCode(160) : character),
+ child);
+ });
+ Element.remove(child);
+ }
+ });
+ },
+ multiple: function(element, effect) {
+ var elements;
+ if(((typeof element == 'object') ||
+ (typeof element == 'function')) &&
+ (element.length))
+ elements = element;
+ else
+ elements = $(element).childNodes;
+
+ var options = Object.extend({
+ speed: 0.1,
+ delay: 0.0
+ }, arguments[2] || {});
+ var masterDelay = options.delay;
+
+ $A(elements).each( function(element, index) {
+ new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+ });
+ },
+ PAIRS: {
+ 'slide': ['SlideDown','SlideUp'],
+ 'blind': ['BlindDown','BlindUp'],
+ 'appear': ['Appear','Fade']
+ },
+ toggle: function(element, effect) {
+ element = $(element);
+ effect = (effect || 'appear').toLowerCase();
+ var options = Object.extend({
+ queue: { position:'end', scope:(element.id || 'global') }
+ }, arguments[2] || {});
+ Effect[Element.visible(element) ?
+ Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
+ }
+};
+
+var Effect2 = Effect; // deprecated
+
+/* ------------- transitions ------------- */
+
+Effect.Transitions = {}
+
+Effect.Transitions.linear = function(pos) {
+ return pos;
+}
+Effect.Transitions.sinoidal = function(pos) {
+ return (-Math.cos(pos*Math.PI)/2) + 0.5;
+}
+Effect.Transitions.reverse = function(pos) {
+ return 1-pos;
+}
+Effect.Transitions.flicker = function(pos) {
+ return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+}
+Effect.Transitions.wobble = function(pos) {
+ return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+}
+Effect.Transitions.pulse = function(pos) {
+ return (Math.floor(pos*10) % 2 == 0 ?
+ (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
+}
+Effect.Transitions.none = function(pos) {
+ return 0;
+}
+Effect.Transitions.full = function(pos) {
+ return 1;
+}
+
+/* ------------- core effects ------------- */
+
+Effect.ScopedQueue = Class.create();
+Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
+ initialize: function() {
+ this.effects = [];
+ this.interval = null;
+ },
+ _each: function(iterator) {
+ this.effects._each(iterator);
+ },
+ add: function(effect) {
+ var timestamp = new Date().getTime();
+
+ var position = (typeof effect.options.queue == 'string') ?
+ effect.options.queue : effect.options.queue.position;
+
+ switch(position) {
+ case 'front':
+ // move unstarted effects after this effect
+ this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+ e.startOn += effect.finishOn;
+ e.finishOn += effect.finishOn;
+ });
+ break;
+ case 'end':
+ // start effect after last queued effect has finished
+ timestamp = this.effects.pluck('finishOn').max() || timestamp;
+ break;
+ }
+
+ effect.startOn += timestamp;
+ effect.finishOn += timestamp;
+ this.effects.push(effect);
+ if(!this.interval)
+ this.interval = setInterval(this.loop.bind(this), 40);
+ },
+ remove: function(effect) {
+ this.effects = this.effects.reject(function(e) { return e==effect });
+ if(this.effects.length == 0) {
+ clearInterval(this.interval);
+ this.interval = null;
+ }
+ },
+ loop: function() {
+ var timePos = new Date().getTime();
+ this.effects.invoke('loop', timePos);
+ }
+});
+
+Effect.Queues = {
+ instances: $H(),
+ get: function(queueName) {
+ if(typeof queueName != 'string') return queueName;
+
+ if(!this.instances.get(queueName))
+ this.instances.set(queueName, new Effect.ScopedQueue());
+
+ return this.instances.get(queueName);
+ }
+}
+Effect.Queue = Effect.Queues.get('global');
+
+Effect.DefaultOptions = {
+ transition: Effect.Transitions.sinoidal,
+ duration: 1.0, // seconds
+ fps: 25.0, // max. 25fps due to Effect.Queue implementation
+ sync: false, // true for combining
+ from: 0.0,
+ to: 1.0,
+ delay: 0.0,
+ queue: 'parallel'
+}
+
+Effect.Base = function() {};
+Effect.Base.prototype = {
+ position: null,
+ start: function(options) {
+ this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
+ this.currentFrame = 0;
+ this.state = 'idle';
+ this.startOn = this.options.delay*1000;
+ this.finishOn = this.startOn + (this.options.duration*1000);
+ this.event('beforeStart');
+ if(!this.options.sync)
+ Effect.Queues.get(typeof this.options.queue == 'string' ?
+ 'global' : this.options.queue.scope).add(this);
+ },
+ loop: function(timePos) {
+ if(timePos >= this.startOn) {
+ if(timePos >= this.finishOn) {
+ this.render(1.0);
+ this.cancel();
+ this.event('beforeFinish');
+ if(this.finish) this.finish();
+ this.event('afterFinish');
+ return;
+ }
+ var pos = (timePos - this.startOn) / (this.finishOn - this.startOn);
+ var frame = Math.round(pos * this.options.fps * this.options.duration);
+ if(frame > this.currentFrame) {
+ this.render(pos);
+ this.currentFrame = frame;
+ }
+ }
+ },
+ render: function(pos) {
+ if(this.state == 'idle') {
+ this.state = 'running';
+ this.event('beforeSetup');
+ if(this.setup) this.setup();
+ this.event('afterSetup');
+ }
+ if(this.state == 'running') {
+ if(this.options.transition) pos = this.options.transition(pos);
+ pos *= (this.options.to-this.options.from);
+ pos += this.options.from;
+ this.position = pos;
+ this.event('beforeUpdate');
+ if(this.update) this.update(pos);
+ this.event('afterUpdate');
+ }
+ },
+ cancel: function() {
+ if(!this.options.sync)
+ Effect.Queues.get(typeof this.options.queue == 'string' ?
+ 'global' : this.options.queue.scope).remove(this);
+ this.state = 'finished';
+ },
+ event: function(eventName) {
+ if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+ if(this.options[eventName]) this.options[eventName](this);
+ },
+ inspect: function() {
+ return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
+ }
+}
+
+Effect.Parallel = Class.create();
+Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
+ initialize: function(effects) {
+ this.effects = effects || [];
+ this.start(arguments[1]);
+ },
+ update: function(position) {
+ this.effects.invoke('render', position);
+ },
+ finish: function(position) {
+ this.effects.each( function(effect) {
+ effect.render(1.0);
+ effect.cancel();
+ effect.event('beforeFinish');
+ if(effect.finish) effect.finish(position);
+ effect.event('afterFinish');
+ });
+ }
+});
+
+Effect.Opacity = Class.create();
+Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ // make this work on IE on elements without 'layout'
+ if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
+ Element.setStyle(this.element, {zoom: 1});
+ var options = Object.extend({
+ from: Element.getOpacity(this.element) || 0.0,
+ to: 1.0
+ }, arguments[1] || {});
+ this.start(options);
+ },
+ update: function(position) {
+ Element.setOpacity(this.element, position);
+ }
+});
+
+Effect.Move = Class.create();
+Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ var options = Object.extend({
+ x: 0,
+ y: 0,
+ mode: 'relative'
+ }, arguments[1] || {});
+ this.start(options);
+ },
+ setup: function() {
+ // Bug in Opera: Opera returns the "real" position of a static element or
+ // relative element that does not have top/left explicitly set.
+ // ==> Always set top and left for position relative elements in your stylesheets
+ // (to 0 if you do not need them)
+ Element.makePositioned(this.element);
+ this.originalLeft = parseFloat(Element.getStyle(this.element,'left') || '0');
+ this.originalTop = parseFloat(Element.getStyle(this.element,'top') || '0');
+ if(this.options.mode == 'absolute') {
+ // absolute movement, so we need to calc deltaX and deltaY
+ this.options.x = this.options.x - this.originalLeft;
+ this.options.y = this.options.y - this.originalTop;
+ }
+ },
+ update: function(position) {
+ Element.setStyle(this.element, {
+ left: this.options.x * position + this.originalLeft + 'px',
+ top: this.options.y * position + this.originalTop + 'px'
+ });
+ }
+});
+
+// for backwards compatibility
+Effect.MoveBy = function(element, toTop, toLeft) {
+ return new Effect.Move(element,
+ Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
+};
+
+Effect.Scale = Class.create();
+Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
+ initialize: function(element, percent) {
+ this.element = $(element)
+ var options = Object.extend({
+ scaleX: true,
+ scaleY: true,
+ scaleContent: true,
+ scaleFromCenter: false,
+ scaleMode: 'box', // 'box' or 'contents' or {} with provided values
+ scaleFrom: 100.0,
+ scaleTo: percent
+ }, arguments[2] || {});
+ this.start(options);
+ },
+ setup: function() {
+ this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+ this.elementPositioning = Element.getStyle(this.element,'position');
+
+ this.originalStyle = {};
+ ['top','left','width','height','fontSize'].each( function(k) {
+ this.originalStyle[k] = this.element.style[k];
+ }.bind(this));
+
+ this.originalTop = this.element.offsetTop;
+ this.originalLeft = this.element.offsetLeft;
+
+ var fontSize = Element.getStyle(this.element,'font-size') || '100%';
+ ['em','px','%'].each( function(fontSizeType) {
+ if(fontSize.indexOf(fontSizeType)>0) {
+ this.fontSize = parseFloat(fontSize);
+ this.fontSizeType = fontSizeType;
+ }
+ }.bind(this));
+
+ this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+
+ this.dims = null;
+ if(this.options.scaleMode=='box')
+ this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+ if(/^content/.test(this.options.scaleMode))
+ this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+ if(!this.dims)
+ this.dims = [this.options.scaleMode.originalHeight,
+ this.options.scaleMode.originalWidth];
+ },
+ update: function(position) {
+ var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+ if(this.options.scaleContent && this.fontSize)
+ Element.setStyle(this.element, {fontSize: this.fontSize * currentScale + this.fontSizeType });
+ this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+ },
+ finish: function(position) {
+ if (this.restoreAfterFinish) Element.setStyle(this.element, this.originalStyle);
+ },
+ setDimensions: function(height, width) {
+ var d = {};
+ if(this.options.scaleX) d.width = width + 'px';
+ if(this.options.scaleY) d.height = height + 'px';
+ if(this.options.scaleFromCenter) {
+ var topd = (height - this.dims[0])/2;
+ var leftd = (width - this.dims[1])/2;
+ if(this.elementPositioning == 'absolute') {
+ if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
+ if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+ } else {
+ if(this.options.scaleY) d.top = -topd + 'px';
+ if(this.options.scaleX) d.left = -leftd + 'px';
+ }
+ }
+ Element.setStyle(this.element, d);
+ }
+});
+
+Effect.Highlight = Class.create();
+Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
+ this.start(options);
+ },
+ setup: function() {
+ // Prevent executing on elements not in the layout flow
+ if(Element.getStyle(this.element, 'display')=='none') { this.cancel(); return; }
+ // Disable background image during the effect
+ this.oldStyle = {
+ backgroundImage: Element.getStyle(this.element, 'background-image') };
+ Element.setStyle(this.element, {backgroundImage: 'none'});
+ if(!this.options.endcolor)
+ this.options.endcolor = Element.getStyle(this.element, 'background-color').parseColor('#ffffff');
+ if(!this.options.restorecolor)
+ this.options.restorecolor = Element.getStyle(this.element, 'background-color');
+ // init color calculations
+ this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+ this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+ },
+ update: function(position) {
+ Element.setStyle(this.element,{backgroundColor: $R(0,2).inject('#',function(m,v,i){
+ return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
+ },
+ finish: function() {
+ Element.setStyle(this.element, Object.extend(this.oldStyle, {
+ backgroundColor: this.options.restorecolor
+ }));
+ }
+});
+
+Effect.ScrollTo = Class.create();
+Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
+ initialize: function(element) {
+ this.element = $(element);
+ this.start(arguments[1] || {});
+ },
+ setup: function() {
+ Position.prepare();
+ var offsets = Position.cumulativeOffset(this.element);
+ if(this.options.offset) offsets[1] += this.options.offset;
+ var max = window.innerHeight ?
+ window.height - window.innerHeight :
+ document.body.scrollHeight -
+ (document.documentElement.clientHeight ?
+ document.documentElement.clientHeight : document.body.clientHeight);
+ this.scrollStart = Position.deltaY;
+ this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
+ },
+ update: function(position) {
+ Position.prepare();
+ window.scrollTo(Position.deltaX,
+ this.scrollStart + (position*this.delta));
+ }
+});
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+ var oldOpacity = Element.getInlineOpacity(element);
+ var options = Object.extend({
+ from: Element.getOpacity(element) || 1.0,
+ to: 0.0,
+ afterFinishInternal: function(effect) { with(Element) {
+ if(effect.options.to!=0) return;
+ hide(effect.element);
+ setStyle(effect.element, {opacity: oldOpacity}); }}
+ }, arguments[1] || {});
+ return new Effect.Opacity(element,options);
+}
+
+Effect.Appear = function(element) {
+ var options = Object.extend({
+ from: (Element.getStyle(element, 'display') == 'none' ? 0.0 : Element.getOpacity(element) || 0.0),
+ to: 1.0,
+ beforeSetup: function(effect) { with(Element) {
+ setOpacity(effect.element, effect.options.from);
+ show(effect.element); }}
+ }, arguments[1] || {});
+ return new Effect.Opacity(element,options);
+}
+
+Effect.Puff = function(element) {
+ element = $(element);
+ var oldStyle = { opacity: Element.getInlineOpacity(element), position: Element.getStyle(element, 'position') };
+ return new Effect.Parallel(
+ [ new Effect.Scale(element, 200,
+ { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
+ new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
+ Object.extend({ duration: 1.0,
+ beforeSetupInternal: function(effect) { with(Element) {
+ setStyle(effect.effects[0].element, {position: 'absolute'}); }},
+ afterFinishInternal: function(effect) { with(Element) {
+ hide(effect.effects[0].element);
+ setStyle(effect.effects[0].element, oldStyle); }}
+ }, arguments[1] || {})
+ );
+}
+
+Effect.BlindUp = function(element) {
+ element = $(element);
+ Element.makeClipping(element);
+ return new Effect.Scale(element, 0,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ restoreAfterFinish: true,
+ afterFinishInternal: function(effect) { with(Element) {
+ [hide, undoClipping].call(effect.element); }}
+ }, arguments[1] || {})
+ );
+}
+
+Effect.BlindDown = function(element) {
+ element = $(element);
+ var oldHeight = Element.getStyle(element, 'height');
+ var elementDimensions = Element.getDimensions(element);
+ return new Effect.Scale(element, 100,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ scaleFrom: 0,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+ restoreAfterFinish: true,
+ afterSetup: function(effect) { with(Element) {
+ makeClipping(effect.element);
+ setStyle(effect.element, {height: '0px'});
+ show(effect.element);
+ }},
+ afterFinishInternal: function(effect) { with(Element) {
+ undoClipping(effect.element);
+ setStyle(effect.element, {height: oldHeight});
+ }}
+ }, arguments[1] || {})
+ );
+}
+
+Effect.SwitchOff = function(element) {
+ element = $(element);
+ var oldOpacity = Element.getInlineOpacity(element);
+ return new Effect.Appear(element, {
+ duration: 0.4,
+ from: 0,
+ transition: Effect.Transitions.flicker,
+ afterFinishInternal: function(effect) {
+ new Effect.Scale(effect.element, 1, {
+ duration: 0.3, scaleFromCenter: true,
+ scaleX: false, scaleContent: false, restoreAfterFinish: true,
+ beforeSetup: function(effect) { with(Element) {
+ [makePositioned,makeClipping].call(effect.element);
+ }},
+ afterFinishInternal: function(effect) { with(Element) {
+ [hide,undoClipping,undoPositioned].call(effect.element);
+ setStyle(effect.element, {opacity: oldOpacity});
+ }}
+ })
+ }
+ });
+}
+
+Effect.DropOut = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: Element.getStyle(element, 'top'),
+ left: Element.getStyle(element, 'left'),
+ opacity: Element.getInlineOpacity(element) };
+ return new Effect.Parallel(
+ [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
+ new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+ Object.extend(
+ { duration: 0.5,
+ beforeSetup: function(effect) { with(Element) {
+ makePositioned(effect.effects[0].element); }},
+ afterFinishInternal: function(effect) { with(Element) {
+ [hide, undoPositioned].call(effect.effects[0].element);
+ setStyle(effect.effects[0].element, oldStyle); }}
+ }, arguments[1] || {}));
+}
+
+Effect.Shake = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: Element.getStyle(element, 'top'),
+ left: Element.getStyle(element, 'left') };
+ return new Effect.Move(element,
+ { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { with(Element) {
+ undoPositioned(effect.element);
+ setStyle(effect.element, oldStyle);
+ }}}) }}) }}) }}) }}) }});
+}
+
+Effect.SlideDown = function(element) {
+ element = $(element);
+ Element.cleanWhitespace(element);
+ // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+ var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom');
+ var elementDimensions = Element.getDimensions(element);
+ return new Effect.Scale(element, 100, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ scaleFrom: 0,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+ restoreAfterFinish: true,
+ afterSetup: function(effect) { with(Element) {
+ makePositioned(effect.element);
+ makePositioned(effect.element.firstChild);
+ if(window.opera) setStyle(effect.element, {top: ''});
+ makeClipping(effect.element);
+ setStyle(effect.element, {height: '0px'});
+ show(element); }},
+ afterUpdateInternal: function(effect) { with(Element) {
+ setStyle(effect.element.firstChild, {bottom:
+ (effect.dims[0] - effect.element.clientHeight) + 'px' }); }},
+ afterFinishInternal: function(effect) { with(Element) {
+ undoClipping(effect.element);
+ undoPositioned(effect.element.firstChild);
+ undoPositioned(effect.element);
+ setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }}
+ }, arguments[1] || {})
+ );
+}
+
+Effect.SlideUp = function(element) {
+ element = $(element);
+ Element.cleanWhitespace(element);
+ var oldInnerBottom = Element.getStyle(element.firstChild, 'bottom');
+ return new Effect.Scale(element, 0,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ scaleMode: 'box',
+ scaleFrom: 100,
+ restoreAfterFinish: true,
+ beforeStartInternal: function(effect) { with(Element) {
+ makePositioned(effect.element);
+ makePositioned(effect.element.firstChild);
+ if(window.opera) setStyle(effect.element, {top: ''});
+ makeClipping(effect.element);
+ show(element); }},
+ afterUpdateInternal: function(effect) { with(Element) {
+ setStyle(effect.element.firstChild, {bottom:
+ (effect.dims[0] - effect.element.clientHeight) + 'px' }); }},
+ afterFinishInternal: function(effect) { with(Element) {
+ [hide, undoClipping].call(effect.element);
+ undoPositioned(effect.element.firstChild);
+ undoPositioned(effect.element);
+ setStyle(effect.element.firstChild, {bottom: oldInnerBottom}); }}
+ }, arguments[1] || {})
+ );
+}
+
+// Bug in opera makes the TD containing this element expand for a instance after finish
+Effect.Squish = function(element) {
+ return new Effect.Scale(element, window.opera ? 1 : 0,
+ { restoreAfterFinish: true,
+ beforeSetup: function(effect) { with(Element) {
+ makeClipping(effect.element); }},
+ afterFinishInternal: function(effect) { with(Element) {
+ hide(effect.element);
+ undoClipping(effect.element); }}
+ });
+}
+
+Effect.Grow = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ direction: 'center',
+ moveTransistion: Effect.Transitions.sinoidal,
+ scaleTransition: Effect.Transitions.sinoidal,
+ opacityTransition: Effect.Transitions.full
+ }, arguments[1] || {});
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ height: element.style.height,
+ width: element.style.width,
+ opacity: Element.getInlineOpacity(element) };
+
+ var dims = Element.getDimensions(element);
+ var initialMoveX, initialMoveY;
+ var moveX, moveY;
+
+ switch (options.direction) {
+ case 'top-left':
+ initialMoveX = initialMoveY = moveX = moveY = 0;
+ break;
+ case 'top-right':
+ initialMoveX = dims.width;
+ initialMoveY = moveY = 0;
+ moveX = -dims.width;
+ break;
+ case 'bottom-left':
+ initialMoveX = moveX = 0;
+ initialMoveY = dims.height;
+ moveY = -dims.height;
+ break;
+ case 'bottom-right':
+ initialMoveX = dims.width;
+ initialMoveY = dims.height;
+ moveX = -dims.width;
+ moveY = -dims.height;
+ break;
+ case 'center':
+ initialMoveX = dims.width / 2;
+ initialMoveY = dims.height / 2;
+ moveX = -dims.width / 2;
+ moveY = -dims.height / 2;
+ break;
+ }
+
+ return new Effect.Move(element, {
+ x: initialMoveX,
+ y: initialMoveY,
+ duration: 0.01,
+ beforeSetup: function(effect) { with(Element) {
+ hide(effect.element);
+ makeClipping(effect.element);
+ makePositioned(effect.element);
+ }},
+ afterFinishInternal: function(effect) {
+ new Effect.Parallel(
+ [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+ new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
+ new Effect.Scale(effect.element, 100, {
+ scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
+ sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+ ], Object.extend({
+ beforeSetup: function(effect) { with(Element) {
+ setStyle(effect.effects[0].element, {height: '0px'});
+ show(effect.effects[0].element); }},
+ afterFinishInternal: function(effect) { with(Element) {
+ [undoClipping, undoPositioned].call(effect.effects[0].element);
+ setStyle(effect.effects[0].element, oldStyle); }}
+ }, options)
+ )
+ }
+ });
+}
+
+Effect.Shrink = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ direction: 'center',
+ moveTransistion: Effect.Transitions.sinoidal,
+ scaleTransition: Effect.Transitions.sinoidal,
+ opacityTransition: Effect.Transitions.none
+ }, arguments[1] || {});
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ height: element.style.height,
+ width: element.style.width,
+ opacity: Element.getInlineOpacity(element) };
+
+ var dims = Element.getDimensions(element);
+ var moveX, moveY;
+
+ switch (options.direction) {
+ case 'top-left':
+ moveX = moveY = 0;
+ break;
+ case 'top-right':
+ moveX = dims.width;
+ moveY = 0;
+ break;
+ case 'bottom-left':
+ moveX = 0;
+ moveY = dims.height;
+ break;
+ case 'bottom-right':
+ moveX = dims.width;
+ moveY = dims.height;
+ break;
+ case 'center':
+ moveX = dims.width / 2;
+ moveY = dims.height / 2;
+ break;
+ }
+
+ return new Effect.Parallel(
+ [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+ new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+ new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
+ ], Object.extend({
+ beforeStartInternal: function(effect) { with(Element) {
+ [makePositioned, makeClipping].call(effect.effects[0].element) }},
+ afterFinishInternal: function(effect) { with(Element) {
+ [hide, undoClipping, undoPositioned].call(effect.effects[0].element);
+ setStyle(effect.effects[0].element, oldStyle); }}
+ }, options)
+ );
+}
+
+Effect.Pulsate = function(element) {
+ element = $(element);
+ var options = arguments[1] || {};
+ var oldOpacity = Element.getInlineOpacity(element);
+ var transition = options.transition || Effect.Transitions.sinoidal;
+ var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
+ reverser.bind(transition);
+ return new Effect.Opacity(element,
+ Object.extend(Object.extend({ duration: 3.0, from: 0,
+ afterFinishInternal: function(effect) { Element.setStyle(effect.element, {opacity: oldOpacity}); }
+ }, options), {transition: reverser}));
+}
+
+Effect.Fold = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ width: element.style.width,
+ height: element.style.height };
+ Element.makeClipping(element);
+ return new Effect.Scale(element, 5, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ afterFinishInternal: function(effect) {
+ new Effect.Scale(element, 1, {
+ scaleContent: false,
+ scaleY: false,
+ afterFinishInternal: function(effect) { with(Element) {
+ [hide, undoClipping].call(effect.element);
+ setStyle(effect.element, oldStyle);
+ }} });
+ }}, arguments[1] || {}));
+}
Added: jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/scriptaculous.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/scriptaculous.js Wed Apr 9 00:12:34 2008
@@ -0,0 +1,45 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var Scriptaculous = {
+ Version: '1.5.1',
+ require: function(libraryName) {
+ // inserting via DOM fails in Safari 2.0, so brute force approach
+ document.write('<script type="text/javascript" src="'+libraryName+'"></script>');
+ },
+ load: function() {
+ if((typeof Prototype=='undefined') ||
+ parseFloat(Prototype.Version.split(".")[0] + "." +
+ Prototype.Version.split(".")[1]) < 1.4)
+ throw("script.aculo.us requires the Prototype JavaScript framework >= 1.4.0");
+
+ $A(document.getElementsByTagName("script")).findAll( function(s) {
+ return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
+ }).each( function(s) {
+ var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
+ var includes = s.src.match(/\?.*load=([a-z,]*)/);
+ (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider').split(',').each(
+ function(include) { Scriptaculous.require(path+include+'.js') });
+ });
+ }
+}
+
+Scriptaculous.load();
\ No newline at end of file
Added: jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/slider.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/slider.js Wed Apr 9 00:12:34 2008
@@ -0,0 +1,283 @@
+// Copyright (c) 2005 Marty Haught, Thomas Fuchs
+//
+// See http://script.aculo.us for more info
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+if(!Control) var Control = {};
+Control.Slider = Class.create();
+
+// options:
+// axis: 'vertical', or 'horizontal' (default)
+//
+// callbacks:
+// onChange(value)
+// onSlide(value)
+Control.Slider.prototype = {
+ initialize: function(handle, track, options) {
+ var slider = this;
+
+ if(handle instanceof Array) {
+ this.handles = handle.collect( function(e) { return $(e) });
+ } else {
+ this.handles = [$(handle)];
+ }
+
+ this.track = $(track);
+ this.options = options || {};
+
+ this.axis = this.options.axis || 'horizontal';
+ this.increment = this.options.increment || 1;
+ this.step = parseInt(this.options.step || '1');
+ this.range = this.options.range || $R(0,1);
+
+ this.value = 0; // assure backwards compat
+ this.values = this.handles.map( function() { return 0 });
+ this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
+ this.options.startSpan = $(this.options.startSpan || null);
+ this.options.endSpan = $(this.options.endSpan || null);
+
+ this.restricted = this.options.restricted || false;
+
+ this.maximum = this.options.maximum || this.range.end;
+ this.minimum = this.options.minimum || this.range.start;
+
+ // Will be used to align the handle onto the track, if necessary
+ this.alignX = parseInt(this.options.alignX || '0');
+ this.alignY = parseInt(this.options.alignY || '0');
+
+ this.trackLength = this.maximumOffset() - this.minimumOffset();
+ this.handleLength = this.isVertical() ? this.handles[0].offsetHeight : this.handles[0].offsetWidth;
+
+ this.active = false;
+ this.dragging = false;
+ this.disabled = false;
+
+ if(this.options.disabled) this.setDisabled();
+
+ // Allowed values array
+ this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
+ if(this.allowedValues) {
+ this.minimum = this.allowedValues.min();
+ this.maximum = this.allowedValues.max();
+ }
+
+ this.eventMouseDown = this.startDrag.bindAsEventListener(this);
+ this.eventMouseUp = this.endDrag.bindAsEventListener(this);
+ this.eventMouseMove = this.update.bindAsEventListener(this);
+
+ // Initialize handles in reverse (make sure first handle is active)
+ this.handles.each( function(h,i) {
+ i = slider.handles.length-1-i;
+ slider.setValue(parseFloat(
+ (slider.options.sliderValue instanceof Array ?
+ slider.options.sliderValue[i] : slider.options.sliderValue) ||
+ slider.range.start), i);
+ Element.makePositioned(h); // fix IE
+ Event.observe(h, "mousedown", slider.eventMouseDown);
+ });
+
+ Event.observe(this.track, "mousedown", this.eventMouseDown);
+ Event.observe(document, "mouseup", this.eventMouseUp);
+ Event.observe(document, "mousemove", this.eventMouseMove);
+
+ this.initialized = true;
+ },
+ dispose: function() {
+ var slider = this;
+ Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
+ Event.stopObserving(document, "mouseup", this.eventMouseUp);
+ Event.stopObserving(document, "mousemove", this.eventMouseMove);
+ this.handles.each( function(h) {
+ Event.stopObserving(h, "mousedown", slider.eventMouseDown);
+ });
+ },
+ setDisabled: function(){
+ this.disabled = true;
+ },
+ setEnabled: function(){
+ this.disabled = false;
+ },
+ getNearestValue: function(value){
+ if(this.allowedValues){
+ if(value >= this.allowedValues.max()) return(this.allowedValues.max());
+ if(value <= this.allowedValues.min()) return(this.allowedValues.min());
+
+ var offset = Math.abs(this.allowedValues[0] - value);
+ var newValue = this.allowedValues[0];
+ this.allowedValues.each( function(v) {
+ var currentOffset = Math.abs(v - value);
+ if(currentOffset <= offset){
+ newValue = v;
+ offset = currentOffset;
+ }
+ });
+ return newValue;
+ }
+ if(value > this.range.end) return this.range.end;
+ if(value < this.range.start) return this.range.start;
+ return value;
+ },
+ setValue: function(sliderValue, handleIdx){
+ if(!this.active) {
+ this.activeHandle = this.handles[handleIdx];
+ this.activeHandleIdx = handleIdx;
+ this.updateStyles();
+ }
+ handleIdx = handleIdx || this.activeHandleIdx || 0;
+ if(this.initialized && this.restricted) {
+ if((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
+ sliderValue = this.values[handleIdx-1];
+ if((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
+ sliderValue = this.values[handleIdx+1];
+ }
+ sliderValue = this.getNearestValue(sliderValue);
+ this.values[handleIdx] = sliderValue;
+ this.value = this.values[0]; // assure backwards compat
+
+ this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
+ this.translateToPx(sliderValue);
+
+ this.drawSpans();
+ if(!this.dragging || !this.event) this.updateFinished();
+ },
+ setValueBy: function(delta, handleIdx) {
+ this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,
+ handleIdx || this.activeHandleIdx || 0);
+ },
+ translateToPx: function(value) {
+ return Math.round(
+ ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *
+ (value - this.range.start)) + "px";
+ },
+ translateToValue: function(offset) {
+ return ((offset/(this.trackLength-this.handleLength) *
+ (this.range.end-this.range.start)) + this.range.start);
+ },
+ getRange: function(range) {
+ var v = this.values.sortBy(Prototype.K);
+ range = range || 0;
+ return $R(v[range],v[range+1]);
+ },
+ minimumOffset: function(){
+ return(this.isVertical() ? this.alignY : this.alignX);
+ },
+ maximumOffset: function(){
+ return(this.isVertical() ?
+ this.track.offsetHeight - this.alignY : this.track.offsetWidth - this.alignX);
+ },
+ isVertical: function(){
+ return (this.axis == 'vertical');
+ },
+ drawSpans: function() {
+ var slider = this;
+ if(this.spans)
+ $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
+ if(this.options.startSpan)
+ this.setSpan(this.options.startSpan,
+ $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
+ if(this.options.endSpan)
+ this.setSpan(this.options.endSpan,
+ $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
+ },
+ setSpan: function(span, range) {
+ if(this.isVertical()) {
+ span.style.top = this.translateToPx(range.start);
+ span.style.height = this.translateToPx(range.end - range.start);
+ } else {
+ span.style.left = this.translateToPx(range.start);
+ span.style.width = this.translateToPx(range.end - range.start);
+ }
+ },
+ updateStyles: function() {
+ this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
+ Element.addClassName(this.activeHandle, 'selected');
+ },
+ startDrag: function(event) {
+ if(Event.isLeftClick(event)) {
+ if(!this.disabled){
+ this.active = true;
+
+ var handle = Event.element(event);
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ if(handle==this.track) {
+ var offsets = Position.cumulativeOffset(this.track);
+ this.event = event;
+ this.setValue(this.translateToValue(
+ (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
+ ));
+ var offsets = Position.cumulativeOffset(this.activeHandle);
+ this.offsetX = (pointer[0] - offsets[0]);
+ this.offsetY = (pointer[1] - offsets[1]);
+ } else {
+ // find the handle (prevents issues with Safari)
+ while((this.handles.indexOf(handle) == -1) && handle.parentNode)
+ handle = handle.parentNode;
+
+ this.activeHandle = handle;
+ this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
+ this.updateStyles();
+
+ var offsets = Position.cumulativeOffset(this.activeHandle);
+ this.offsetX = (pointer[0] - offsets[0]);
+ this.offsetY = (pointer[1] - offsets[1]);
+ }
+ }
+ Event.stop(event);
+ }
+ },
+ update: function(event) {
+ if(this.active) {
+ if(!this.dragging) this.dragging = true;
+ this.draw(event);
+ // fix AppleWebKit rendering
+ if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
+ Event.stop(event);
+ }
+ },
+ draw: function(event) {
+ var pointer = [Event.pointerX(event), Event.pointerY(event)];
+ var offsets = Position.cumulativeOffset(this.track);
+ pointer[0] -= this.offsetX + offsets[0];
+ pointer[1] -= this.offsetY + offsets[1];
+ this.event = event;
+ this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
+ if(this.initialized && this.options.onSlide)
+ this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
+ },
+ endDrag: function(event) {
+ if(this.active && this.dragging) {
+ this.finishDrag(event, true);
+ Event.stop(event);
+ }
+ this.active = false;
+ this.dragging = false;
+ },
+ finishDrag: function(event, success) {
+ this.active = false;
+ this.dragging = false;
+ this.updateFinished();
+ },
+ updateFinished: function() {
+ if(this.initialized && this.options.onChange)
+ this.options.onChange(this.values.length>1 ? this.values : this.value, this);
+ this.event = null;
+ }
+}
\ No newline at end of file
Added: jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/unittest.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/plugins/Jifty/Plugin/Prototypism/web/static/js/prototypism/scriptaculous/unittest.js Wed Apr 9 00:12:34 2008
@@ -0,0 +1,363 @@
+// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// (c) 2005 Jon Tirsen (http://www.tirsen.com)
+// (c) 2005 Michael Schuerig (http://www.schuerig.de/michael/)
+//
+// See scriptaculous.js for full license.
+
+// experimental, Firefox-only
+Event.simulateMouse = function(element, eventName) {
+ var options = Object.extend({
+ pointerX: 0,
+ pointerY: 0,
+ buttons: 0
+ }, arguments[2] || {});
+ var oEvent = document.createEvent("MouseEvents");
+ oEvent.initMouseEvent(eventName, true, true, document.defaultView,
+ options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
+ false, false, false, false, 0, $(element));
+
+ if(this.mark) Element.remove(this.mark);
+ this.mark = document.createElement('div');
+ this.mark.appendChild(document.createTextNode(" "));
+ document.body.appendChild(this.mark);
+ this.mark.style.position = 'absolute';
+ this.mark.style.top = options.pointerY + "px";
+ this.mark.style.left = options.pointerX + "px";
+ this.mark.style.width = "5px";
+ this.mark.style.height = "5px;";
+ this.mark.style.borderTop = "1px solid red;"
+ this.mark.style.borderLeft = "1px solid red;"
+
+ if(this.step)
+ alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options));
+
+ $(element).dispatchEvent(oEvent);
+};
+
+// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed "too much", this doesn't work in 1.0.6 or DP2.
+// You need to downgrade to 1.0.4 for now to get this working
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much
+Event.simulateKey = function(element, eventName) {
+ var options = Object.extend({
+ ctrlKey: false,
+ altKey: false,
+ shiftKey: false,
+ metaKey: false,
+ keyCode: 0,
+ charCode: 0
+ }, arguments[2] || {});
+
+ var oEvent = document.createEvent("KeyEvents");
+ oEvent.initKeyEvent(eventName, true, true, window,
+ options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
+ options.keyCode, options.charCode );
+ $(element).dispatchEvent(oEvent);
+};
+
+Event.simulateKeys = function(element, command) {
+ for(var i=0; i<command.length; i++) {
+ Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)});
+ }
+};
+
+var Test = {}
+Test.Unit = {};
+
+// security exception workaround
+Test.Unit.inspect = function(obj) {
+ var info = [];
+
+ if(typeof obj=="string" ||
+ typeof obj=="number") {
+ return obj;
+ } else {
+ for(property in obj)
+ if(typeof obj[property]!="function")
+ info.push(property + ' => ' +
+ (typeof obj[property] == "string" ?
+ '"' + obj[property] + '"' :
+ obj[property]));
+ }
+
+ return ("'" + obj + "' #" + typeof obj +
+ ": {" + info.join(", ") + "}");
+}
+
+Test.Unit.Logger = Class.create();
+Test.Unit.Logger.prototype = {
+ initialize: function(log) {
+ this.log = $(log);
+ if (this.log) {
+ this._createLogTable();
+ }
+ },
+ start: function(testName) {
+ if (!this.log) return;
+ this.testName = testName;
+ this.lastLogLine = document.createElement('tr');
+ this.statusCell = document.createElement('td');
+ this.nameCell = document.createElement('td');
+ this.nameCell.appendChild(document.createTextNode(testName));
+ this.messageCell = document.createElement('td');
+ this.lastLogLine.appendChild(this.statusCell);
+ this.lastLogLine.appendChild(this.nameCell);
+ this.lastLogLine.appendChild(this.messageCell);
+ this.loglines.appendChild(this.lastLogLine);
+ },
+ finish: function(status, summary) {
+ if (!this.log) return;
+ this.lastLogLine.className = status;
+ this.statusCell.innerHTML = status;
+ this.messageCell.innerHTML = this._toHTML(summary);
+ },
+ message: function(message) {
+ if (!this.log) return;
+ this.messageCell.innerHTML = this._toHTML(message);
+ },
+ summary: function(summary) {
+ if (!this.log) return;
+ this.logsummary.innerHTML = this._toHTML(summary);
+ },
+ _createLogTable: function() {
+ this.log.innerHTML =
+ '<div id="logsummary"></div>' +
+ '<table id="logtable">' +
+ '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +
+ '<tbody id="loglines"></tbody>' +
+ '</table>';
+ this.logsummary = $('logsummary')
+ this.loglines = $('loglines');
+ },
+ _toHTML: function(txt) {
+ return txt.escapeHTML().replace(/\n/g,"<br/>");
+ }
+}
+
+Test.Unit.Runner = Class.create();
+Test.Unit.Runner.prototype = {
+ initialize: function(testcases) {
+ this.options = Object.extend({
+ testLog: 'testlog'
+ }, arguments[1] || {});
+ this.options.resultsURL = this.parseResultsURLQueryParameter();
+ if (this.options.testLog) {
+ this.options.testLog = $(this.options.testLog) || null;
+ }
+ if(this.options.tests) {
+ this.tests = [];
+ for(var i = 0; i < this.options.tests.length; i++) {
+ if(/^test/.test(this.options.tests[i])) {
+ this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases["setup"], testcases["teardown"]));
+ }
+ }
+ } else {
+ if (this.options.test) {
+ this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases["setup"], testcases["teardown"])];
+ } else {
+ this.tests = [];
+ for(var testcase in testcases) {
+ if(/^test/.test(testcase)) {
+ this.tests.push(new Test.Unit.Testcase(testcase, testcases[testcase], testcases["setup"], testcases["teardown"]));
+ }
+ }
+ }
+ }
+ this.currentTest = 0;
+ this.logger = new Test.Unit.Logger(this.options.testLog);
+ setTimeout(this.runTests.bind(this), 1000);
+ },
+ parseResultsURLQueryParameter: function() {
+ return window.location.search.parseQuery()["resultsURL"];
+ },
+ // Returns:
+ // "ERROR" if there was an error,
+ // "FAILURE" if there was a failure, or
+ // "SUCCESS" if there was neither
+ getResult: function() {
+ var hasFailure = false;
+ for(var i=0;i<this.tests.length;i++) {
+ if (this.tests[i].errors > 0) {
+ return "ERROR";
+ }
+ if (this.tests[i].failures > 0) {
+ hasFailure = true;
+ }
+ }
+ if (hasFailure) {
+ return "FAILURE";
+ } else {
+ return "SUCCESS";
+ }
+ },
+ postResults: function() {
+ if (this.options.resultsURL) {
+ new Ajax.Request(this.options.resultsURL,
+ { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });
+ }
+ },
+ runTests: function() {
+ var test = this.tests[this.currentTest];
+ if (!test) {
+ // finished!
+ this.postResults();
+ this.logger.summary(this.summary());
+ return;
+ }
+ if(!test.isWaiting) {
+ this.logger.start(test.name);
+ }
+ test.run();
+ if(test.isWaiting) {
+ this.logger.message("Waiting for " + test.timeToWait + "ms");
+ setTimeout(this.runTests.bind(this), test.timeToWait || 1000);
+ } else {
+ this.logger.finish(test.status(), test.summary());
+ this.currentTest++;
+ // tail recursive, hopefully the browser will skip the stackframe
+ this.runTests();
+ }
+ },
+ summary: function() {
+ var assertions = 0;
+ var failures = 0;
+ var errors = 0;
+ var messages = [];
+ for(var i=0;i<this.tests.length;i++) {
+ assertions += this.tests[i].assertions;
+ failures += this.tests[i].failures;
+ errors += this.tests[i].errors;
+ }
+ return (
+ this.tests.length + " tests, " +
+ assertions + " assertions, " +
+ failures + " failures, " +
+ errors + " errors");
+ }
+}
+
+Test.Unit.Assertions = Class.create();
+Test.Unit.Assertions.prototype = {
+ initialize: function() {
+ this.assertions = 0;
+ this.failures = 0;
+ this.errors = 0;
+ this.messages = [];
+ },
+ summary: function() {
+ return (
+ this.assertions + " assertions, " +
+ this.failures + " failures, " +
+ this.errors + " errors" + "\n" +
+ this.messages.join("\n"));
+ },
+ pass: function() {
+ this.assertions++;
+ },
+ fail: function(message) {
+ this.failures++;
+ this.messages.push("Failure: " + message);
+ },
+ error: function(error) {
+ this.errors++;
+ this.messages.push(error.name + ": "+ error.message + "(" + Test.Unit.inspect(error) +")");
+ },
+ status: function() {
+ if (this.failures > 0) return 'failed';
+ if (this.errors > 0) return 'error';
+ return 'passed';
+ },
+ assert: function(expression) {
+ var message = arguments[1] || 'assert: got "' + Test.Unit.inspect(expression) + '"';
+ try { expression ? this.pass() :
+ this.fail(message); }
+ catch(e) { this.error(e); }
+ },
+ assertEqual: function(expected, actual) {
+ var message = arguments[2] || "assertEqual";
+ try { (expected == actual) ? this.pass() :
+ this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
+ '", actual "' + Test.Unit.inspect(actual) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertNotEqual: function(expected, actual) {
+ var message = arguments[2] || "assertNotEqual";
+ try { (expected != actual) ? this.pass() :
+ this.fail(message + ': got "' + Test.Unit.inspect(actual) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertNull: function(obj) {
+ var message = arguments[1] || 'assertNull'
+ try { (obj==null) ? this.pass() :
+ this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
+ catch(e) { this.error(e); }
+ },
+ assertHidden: function(element) {
+ var message = arguments[1] || 'assertHidden';
+ this.assertEqual("none", element.style.display, message);
+ },
+ assertNotNull: function(object) {
+ var message = arguments[1] || 'assertNotNull';
+ this.assert(object != null, message);
+ },
+ assertInstanceOf: function(expected, actual) {
+ var message = arguments[2] || 'assertInstanceOf';
+ try {
+ (actual instanceof expected) ? this.pass() :
+ this.fail(message + ": object was not an instance of the expected type"); }
+ catch(e) { this.error(e); }
+ },
+ assertNotInstanceOf: function(expected, actual) {
+ var message = arguments[2] || 'assertNotInstanceOf';
+ try {
+ !(actual instanceof expected) ? this.pass() :
+ this.fail(message + ": object was an instance of the not expected type"); }
+ catch(e) { this.error(e); }
+ },
+ _isVisible: function(element) {
+ element = $(element);
+ if(!element.parentNode) return true;
+ this.assertNotNull(element);
+ if(element.style && Element.getStyle(element, 'display') == 'none')
+ return false;
+
+ return this._isVisible(element.parentNode);
+ },
+ assertNotVisible: function(element) {
+ this.assert(!this._isVisible(element), Test.Unit.inspect(element) + " was not hidden and didn't have a hidden parent either. " + ("" || arguments[1]));
+ },
+ assertVisible: function(element) {
+ this.assert(this._isVisible(element), Test.Unit.inspect(element) + " was not visible. " + ("" || arguments[1]));
+ }
+}
+
+Test.Unit.Testcase = Class.create();
+Object.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {
+ initialize: function(name, test, setup, teardown) {
+ Test.Unit.Assertions.prototype.initialize.bind(this)();
+ this.name = name;
+ this.test = test || function() {};
+ this.setup = setup || function() {};
+ this.teardown = teardown || function() {};
+ this.isWaiting = false;
+ this.timeToWait = 1000;
+ },
+ wait: function(time, nextPart) {
+ this.isWaiting = true;
+ this.test = nextPart;
+ this.timeToWait = time;
+ },
+ run: function() {
+ try {
+ try {
+ if (!this.isWaiting) this.setup.bind(this)();
+ this.isWaiting = false;
+ this.test.bind(this)();
+ } finally {
+ if(!this.isWaiting) {
+ this.teardown.bind(this)();
+ }
+ }
+ }
+ catch(e) { this.error(e); }
+ }
+});
\ No newline at end of file
Added: jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/web/static/js/singlepage/rsh/LICENSE.txt
==============================================================================
--- (empty file)
+++ jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/web/static/js/singlepage/rsh/LICENSE.txt Wed Apr 9 00:12:34 2008
@@ -0,0 +1,15 @@
+Copyright (c) 2007 Brian Dillard and Brad Neuberg:
+Brian Dillard | Project Lead | bdillard at pathf.com | http://blogs.pathf.com/agileajax/
+Brad Neuberg | Original Project Creator | http://codinginparadise.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Added: jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/web/static/js/singlepage/rsh/blank.html
==============================================================================
--- (empty file)
+++ jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/web/static/js/singlepage/rsh/blank.html Wed Apr 9 00:12:34 2008
@@ -0,0 +1,39 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<!--
+Copyright (c) 2007 Brian Dillard and Brad Neuberg:
+Brian Dillard | Project Lead | bdillard at pathf.com | http://blogs.pathf.com/agileajax/
+Brad Neuberg | Original Project Creator | http://codinginparadise.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+-->
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+
+<script language="JavaScript">
+function pageLoaded() {
+ window.parent.dhtmlHistory.iframeLoaded(window.location);
+ document.getElementById("output").innerHTML = window.location;
+}
+</script>
+
+</head>
+
+<body onload="pageLoaded();" style="width:700px;padding:2px;margin:0;">
+
+ <p>blank.html - Needed for Internet Explorer's hidden iframe</p>
+ <p id="output"></p>
+
+</body>
+</html>
Added: jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/web/static/js/singlepage/rsh/rsh.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/web/static/js/singlepage/rsh/rsh.js Wed Apr 9 00:12:34 2008
@@ -0,0 +1,667 @@
+/*
+Copyright (c) 2007 Brian Dillard and Brad Neuberg:
+Brian Dillard | Project Lead | bdillard at pathf.com | http://blogs.pathf.com/agileajax/
+Brad Neuberg | Original Project Creator | http://codinginparadise.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
+(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/*
+ Modified version. The path of blank.html has been changed to fit Jifty's directory structure and cache mechanism.
+*/
+
+/*
+ dhtmlHistory: An object that provides history, history data, and bookmarking for DHTML and Ajax applications.
+
+ dependencies:
+ * the historyStorage object included in this file.
+
+*/
+window.dhtmlHistory = {
+
+ /*Public: User-agent booleans*/
+ isIE: false,
+ isOpera: false,
+ isSafari: false,
+ isKonquerer: false,
+ isGecko: false,
+ isSupported: false,
+
+ /*Public: Create the DHTML history infrastructure*/
+ create: function(options) {
+
+ /*
+ options - object to store initialization parameters
+ options.debugMode - boolean that causes hidden form fields to be shown for development purposes.
+ options.toJSON - function to override default JSON stringifier
+ options.fromJSON - function to override default JSON parser
+ */
+
+ var that = this;
+
+ /*set user-agent flags*/
+ var UA = navigator.userAgent.toLowerCase();
+ var platform = navigator.platform.toLowerCase();
+ var vendor = navigator.vendor || "";
+ if (vendor === "KDE") {
+ this.isKonqueror = true;
+ this.isSupported = false;
+ } else if (typeof window.opera !== "undefined") {
+ this.isOpera = true;
+ this.isSupported = true;
+ } else if (typeof document.all !== "undefined") {
+ this.isIE = true;
+ this.isSupported = true;
+ } else if (vendor.indexOf("Apple Computer, Inc.") > -1) {
+ this.isSafari = true;
+ this.isSupported = (platform.indexOf("mac") > -1);
+ } else if (UA.indexOf("gecko") != -1) {
+ this.isGecko = true;
+ this.isSupported = true;
+ }
+
+ /*Set up the historyStorage object; pass in init parameters*/
+ window.historyStorage.setup(options);
+
+ /*Execute browser-specific setup methods*/
+ if (this.isSafari) {
+ this.createSafari();
+ } else if (this.isOpera) {
+ this.createOpera();
+ }
+
+ /*Get our initial location*/
+ var initialHash = this.getCurrentLocation();
+
+ /*Save it as our current location*/
+ this.currentLocation = initialHash;
+
+ /*Now that we have a hash, create IE-specific code*/
+ if (this.isIE) {
+ this.createIE(initialHash);
+ }
+
+ /*Add an unload listener for the page; this is needed for FF 1.5+ because this browser caches all dynamic updates to the
+ page, which can break some of our logic related to testing whether this is the first instance a page has loaded or whether
+ it is being pulled from the cache*/
+
+ var unloadHandler = function() {
+ that.firstLoad = null;
+ };
+
+ this.addEventListener(window,'unload',unloadHandler);
+
+ /*Determine if this is our first page load; for IE, we do this in this.iframeLoaded(), which is fired on pageload. We do it
+ there because we have no historyStorage at this point, which only exists after the page is finished loading in IE*/
+ if (this.isIE) {
+ /*The iframe will get loaded on page load, and we want to ignore this fact*/
+ this.ignoreLocationChange = true;
+ } else {
+ if (!historyStorage.hasKey(this.PAGELOADEDSTRING)) {
+ /*This is our first page load, so ignore the location change and add our special history entry*/
+ this.ignoreLocationChange = true;
+ this.firstLoad = true;
+ historyStorage.put(this.PAGELOADEDSTRING, true);
+ } else {
+ /*This isn't our first page load, so indicate that we want to pay attention to this location change*/
+ this.ignoreLocationChange = false;
+ /*For browsers other than IE, fire a history change event; on IE, the event will be thrown automatically when its
+ hidden iframe reloads on page load. Unfortunately, we don't have any listeners yet; indicate that we want to fire
+ an event when a listener is added.*/
+ this.fireOnNewListener = true;
+ }
+ }
+
+ /*Other browsers can use a location handler that checks at regular intervals as their primary mechanism; we use it for IE as
+ well to handle an important edge case; see checkLocation() for details*/
+ var locationHandler = function() {
+ that.checkLocation();
+ };
+ setInterval(locationHandler, 100);
+ },
+
+ /*Public: Initialize our DHTML history. You must call this after the page is finished loading.*/
+ initialize: function() {
+ /*IE needs to be explicitly initialized. IE doesn't autofill form data until the page is finished loading, so we have to wait*/
+ if (this.isIE) {
+ /*If this is the first time this page has loaded*/
+ if (!historyStorage.hasKey(this.PAGELOADEDSTRING)) {
+ /*For IE, we do this in initialize(); for other browsers, we do it in create()*/
+ this.fireOnNewListener = false;
+ this.firstLoad = true;
+ historyStorage.put(this.PAGELOADEDSTRING, true);
+ }
+ /*Else if this is a fake onload event*/
+ else {
+ this.fireOnNewListener = true;
+ this.firstLoad = false;
+ }
+ }
+ },
+
+ /*Public: Adds a history change listener. Note that only one listener is supported at this time.*/
+ addListener: function(listener) {
+ this.listener = listener;
+ /*If the page was just loaded and we should not ignore it, fire an event to our new listener now*/
+ if (this.fireOnNewListener) {
+ this.fireHistoryEvent(this.currentLocation);
+ this.fireOnNewListener = false;
+ }
+ },
+
+ /*Public: Generic utility function for attaching events*/
+ addEventListener: function(o,e,l) {
+ if (o.addEventListener) {
+ o.addEventListener(e,l,false);
+ } else if (o.attachEvent) {
+ o.attachEvent('on'+e,function() {
+ l(window.event);
+ });
+ }
+ },
+
+ /*Public: Add a history point.*/
+ add: function(newLocation, historyData) {
+
+ if (this.isSafari) {
+
+ /*Remove any leading hash symbols on newLocation*/
+ newLocation = this.removeHash(newLocation);
+
+ /*Store the history data into history storage*/
+ historyStorage.put(newLocation, historyData);
+
+ /*Save this as our current location*/
+ this.currentLocation = newLocation;
+
+ /*Change the browser location*/
+ window.location.hash = newLocation;
+
+ /*Save this to the Safari form field*/
+ this.putSafariState(newLocation);
+
+ } else {
+
+ /*Most browsers require that we wait a certain amount of time before changing the location, such
+ as 200 MS; rather than forcing external callers to use window.setTimeout to account for this,
+ we internally handle it by putting requests in a queue.*/
+ var that = this;
+ var addImpl = function() {
+
+ /*Indicate that the current wait time is now less*/
+ if (that.currentWaitTime > 0) {
+ that.currentWaitTime = that.currentWaitTime - that.waitTime;
+ }
+
+ /*Remove any leading hash symbols on newLocation*/
+ newLocation = that.removeHash(newLocation);
+
+ /*IE has a strange bug; if the newLocation is the same as _any_ preexisting id in the
+ document, then the history action gets recorded twice; throw a programmer exception if
+ there is an element with this ID*/
+ if (document.getElementById(newLocation) && that.debugMode) {
+ var e = "Exception: History locations can not have the same value as _any_ IDs that might be in the document,"
+ + " due to a bug in IE; please ask the developer to choose a history location that does not match any HTML"
+ + " IDs in this document. The following ID is already taken and cannot be a location: " + newLocation;
+ throw new Error(e);
+ }
+
+ /*Store the history data into history storage*/
+ historyStorage.put(newLocation, historyData);
+
+ /*Indicate to the browser to ignore this upcomming location change since we're making it programmatically*/
+ that.ignoreLocationChange = true;
+
+ /*Indicate to IE that this is an atomic location change block*/
+ that.ieAtomicLocationChange = true;
+
+ /*Save this as our current location*/
+ that.currentLocation = newLocation;
+
+ /*Change the browser location*/
+ window.location.hash = newLocation;
+
+ /*Change the hidden iframe's location if on IE*/
+ if (that.isIE) {
+ that.iframe.src = "/static/js/singlepage/rsh/blank.html?" + newLocation;
+ }
+
+ /*End of atomic location change block for IE*/
+ that.ieAtomicLocationChange = false;
+ };
+
+ /*Now queue up this add request*/
+ window.setTimeout(addImpl, this.currentWaitTime);
+
+ /*Indicate that the next request will have to wait for awhile*/
+ this.currentWaitTime = this.currentWaitTime + this.waitTime;
+ }
+ },
+
+ /*Public*/
+ isFirstLoad: function() {
+ return this.firstLoad;
+ },
+
+ /*Public*/
+ getVersion: function() {
+ return "0.6";
+ },
+
+ /*Get browser's current hash location; for Safari, read value from a hidden form field*/
+
+ /*Public*/
+ getCurrentLocation: function() {
+ var r = (this.isSafari
+ ? this.getSafariState()
+ : this.getCurrentHash()
+ );
+ return r;
+ },
+
+ /*Public: Manually parse the current url for a hash; tip of the hat to YUI*/
+ getCurrentHash: function() {
+ var r = window.location.href;
+ var i = r.indexOf("#");
+ return (i >= 0
+ ? r.substr(i+1)
+ : ""
+ );
+ },
+
+ /*- - - - - - - - - - - -*/
+
+ /*Private: Constant for our own internal history event called when the page is loaded*/
+ PAGELOADEDSTRING: "DhtmlHistory_pageLoaded",
+
+ /*Private: Our history change listener.*/
+ listener: null,
+
+ /*Private: MS to wait between add requests - will be reset for certain browsers*/
+ waitTime: 200,
+
+ /*Private: MS before an add request can execute*/
+ currentWaitTime: 0,
+
+ /*Private: Our current hash location, without the "#" symbol.*/
+ currentLocation: null,
+
+ /*Private: Hidden iframe used to IE to detect history changes*/
+ iframe: null,
+
+ /*Private: Flags and DOM references used only by Safari*/
+ safariHistoryStartPoint: null,
+ safariStack: null,
+ safariLength: null,
+
+ /*Private: Flag used to keep checkLocation() from doing anything when it discovers location changes we've made ourselves
+ programmatically with the add() method. Basically, add() sets this to true. When checkLocation() discovers it's true,
+ it refrains from firing our listener, then resets the flag to false for next cycle. That way, our listener only gets fired on
+ history change events triggered by the user via back/forward buttons and manual hash changes. This flag also helps us set up
+ IE's special iframe-based method of handling history changes.*/
+ ignoreLocationChange: null,
+
+ /*Private: A flag that indicates that we should fire a history change event when we are ready, i.e. after we are initialized and
+ we have a history change listener. This is needed due to an edge case in browsers other than IE; if you leave a page entirely
+ then return, we must fire this as a history change event. Unfortunately, we have lost all references to listeners from earlier,
+ because JavaScript clears out.*/
+ fireOnNewListener: null,
+
+ /*Private: A variable that indicates whether this is the first time this page has been loaded. If you go to a web page, leave it
+ for another one, and then return, the page's onload listener fires again. We need a way to differentiate between the first page
+ load and subsequent ones. This variable works hand in hand with the pageLoaded variable we store into historyStorage.*/
+ firstLoad: null,
+
+ /*Private: A variable to handle an important edge case in IE. In IE, if a user manually types an address into their browser's
+ location bar, we must intercept this by calling checkLocation() at regular intervals. However, if we are programmatically
+ changing the location bar ourselves using the add() method, we need to ignore these changes in checkLocation(). Unfortunately,
+ these changes take several lines of code to complete, so for the duration of those lines of code, we set this variable to true.
+ That signals to checkLocation() to ignore the change-in-progress. Once we're done with our chunk of location-change code in
+ add(), we set this back to false. We'll do the same thing when capturing user-entered address changes in checkLocation itself.*/
+ ieAtomicLocationChange: null,
+
+ /*Private: Create IE-specific DOM nodes and overrides*/
+ createIE: function(initialHash) {
+ /*write out a hidden iframe for IE and set the amount of time to wait between add() requests*/
+ this.waitTime = 400;/*IE needs longer between history updates*/
+ var styles = (historyStorage.debugMode
+ ? 'width: 800px;height:80px;border:1px solid black;'
+ : historyStorage.hideStyles
+ );
+ var iframeID = "rshHistoryFrame";
+ var iframeHTML = '<iframe frameborder="0" id="' + iframeID + '" style="' + styles + '" src="/static/js/singlepage/rsh/blank.html?' + initialHash + '"></iframe>';
+ document.write(iframeHTML);
+ this.iframe = document.getElementById(iframeID);
+ },
+
+ /*Private: Create Opera-specific DOM nodes and overrides*/
+ createOpera: function() {
+ this.waitTime = 400;/*Opera needs longer between history updates*/
+ var imgHTML = '<img src="javascript:location.href=\'javascript:dhtmlHistory.checkLocation();\';" style="' + historyStorage.hideStyles + '" />';
+ document.write(imgHTML);
+ },
+
+ /*Private: Create Safari-specific DOM nodes and overrides*/
+ createSafari: function() {
+ var formID = "rshSafariForm";
+ var stackID = "rshSafariStack";
+ var lengthID = "rshSafariLength";
+ var formStyles = historyStorage.debugMode ? historyStorage.showStyles : historyStorage.hideStyles;
+ var inputStyles = (historyStorage.debugMode
+ ? 'width:800px;height:20px;border:1px solid black;margin:0;padding:0;'
+ : historyStorage.hideStyles
+ );
+ var safariHTML = '<form id="' + formID + '" style="' + formStyles + '">'
+ + '<input type="text" style="' + inputStyles + '" id="' + stackID + '" value="[]"/>'
+ + '<input type="text" style="' + inputStyles + '" id="' + lengthID + '" value=""/>'
+ + '</form>';
+ document.write(safariHTML);
+ this.safariStack = document.getElementById(stackID);
+ this.safariLength = document.getElementById(lengthID);
+ if (!historyStorage.hasKey(this.PAGELOADEDSTRING)) {
+ this.safariHistoryStartPoint = history.length;
+ this.safariLength.value = this.safariHistoryStartPoint;
+ } else {
+ this.safariHistoryStartPoint = this.safariLength.value;
+ }
+ },
+
+ /*Private: Safari method to read the history stack from a hidden form field*/
+ getSafariStack: function() {
+ var r = this.safariStack.value;
+ return historyStorage.fromJSON(r);
+ },
+
+ /*Private: Safari method to read from the history stack*/
+ getSafariState: function() {
+ var stack = this.getSafariStack();
+ var state = stack[history.length - this.safariHistoryStartPoint - 1];
+ return state;
+ },
+ /*Private: Safari method to write the history stack to a hidden form field*/
+ putSafariState: function(newLocation) {
+ var stack = this.getSafariStack();
+ stack[history.length - this.safariHistoryStartPoint] = newLocation;
+ this.safariStack.value = historyStorage.toJSON(stack);
+ },
+
+ /*Private: Notify the listener of new history changes.*/
+ fireHistoryEvent: function(newHash) {
+ /*extract the value from our history storage for this hash*/
+ var historyData = historyStorage.get(newHash);
+ /*call our listener*/
+ this.listener.call(null, newHash, historyData);
+ },
+
+ /*Private: See if the browser has changed location. This is the primary history mechanism for Firefox. For IE, we use this to
+ handle an important edge case: if a user manually types in a new hash value into their IE location bar and press enter, we want to
+ to intercept this and notify any history listener.*/
+ checkLocation: function() {
+
+ /*Ignore any location changes that we made ourselves for browsers other than IE*/
+ if (!this.isIE && this.ignoreLocationChange) {
+ this.ignoreLocationChange = false;
+ return;
+ }
+
+ /*If we are dealing with IE and we are in the middle of making a location change from an iframe, ignore it*/
+ if (!this.isIE && this.ieAtomicLocationChange) {
+ return;
+ }
+
+ /*Get hash location*/
+ var hash = this.getCurrentLocation();
+
+ /*Do nothing if there's been no change*/
+ if (hash == this.currentLocation) {
+ return;
+ }
+
+ /*In IE, users manually entering locations into the browser; we do this by comparing the browser's location against the
+ iframe's location; if they differ, we are dealing with a manual event and need to place it inside our history, otherwise
+ we can return*/
+ this.ieAtomicLocationChange = true;
+
+ if (this.isIE && this.getIframeHash() != hash) {
+ this.iframe.src = "/static/js/singlepage/rsh/blank.html?" + hash;
+ }
+ else if (this.isIE) {
+ /*the iframe is unchanged*/
+ return;
+ }
+
+ /*Save this new location*/
+ this.currentLocation = hash;
+
+ this.ieAtomicLocationChange = false;
+
+ /*Notify listeners of the change*/
+ this.fireHistoryEvent(hash);
+ },
+
+ /*Private: Get the current location of IE's hidden iframe.*/
+ getIframeHash: function() {
+ var doc = this.iframe.contentWindow.document;
+ var hash = String(doc.location.search);
+ if (hash.length == 1 && hash.charAt(0) == "?") {
+ hash = "";
+ }
+ else if (hash.length >= 2 && hash.charAt(0) == "?") {
+ hash = hash.substring(1);
+ }
+ return hash;
+ },
+
+ /*Private: Remove any leading hash that might be on a location.*/
+ removeHash: function(hashValue) {
+ var r;
+ if (hashValue === null || hashValue === undefined) {
+ r = null;
+ }
+ else if (hashValue === "") {
+ r = "";
+ }
+ else if (hashValue.length == 1 && hashValue.charAt(0) == "#") {
+ r = "";
+ }
+ else if (hashValue.length > 1 && hashValue.charAt(0) == "#") {
+ r = hashValue.substring(1);
+ }
+ else {
+ r = hashValue;
+ }
+ return r;
+ },
+
+ /*Private: For IE, tell when the hidden iframe has finished loading.*/
+ iframeLoaded: function(newLocation) {
+ /*ignore any location changes that we made ourselves*/
+ if (this.ignoreLocationChange) {
+ this.ignoreLocationChange = false;
+ return;
+ }
+
+ /*Get the new location*/
+ var hash = String(newLocation.search);
+ if (hash.length == 1 && hash.charAt(0) == "?") {
+ hash = "";
+ }
+ else if (hash.length >= 2 && hash.charAt(0) == "?") {
+ hash = hash.substring(1);
+ }
+ /*Keep the browser location bar in sync with the iframe hash*/
+ window.location.hash = hash;
+
+ /*Notify listeners of the change*/
+ this.fireHistoryEvent(hash);
+ }
+
+};
+
+/*
+ historyStorage: An object that uses a hidden form to store history state across page loads. The mechanism for doing so relies on
+ the fact that browsers save the text in form data for the life of the browser session, which means the text is still there when
+ the user navigates back to the page. This object can be used independently of the dhtmlHistory object for caching of Ajax
+ session information.
+
+ dependencies:
+ * json2007.js (included in a separate file) or alternate JSON methods passed in through an options bundle.
+*/
+window.historyStorage = {
+
+ /*Public: Set up our historyStorage object for use by dhtmlHistory or other objects*/
+ setup: function(options) {
+
+ /*
+ options - object to store initialization parameters - passed in from dhtmlHistory or directly into historyStorage
+ options.debugMode - boolean that causes hidden form fields to be shown for development purposes.
+ options.toJSON - function to override default JSON stringifier
+ options.fromJSON - function to override default JSON parser
+ */
+
+ /*process init parameters*/
+ if (typeof options !== "undefined") {
+ if (options.debugMode) {
+ this.debugMode = options.debugMode;
+ }
+ if (options.toJSON) {
+ this.toJSON = options.toJSON;
+ }
+ if (options.fromJSON) {
+ this.fromJSON = options.fromJSON;
+ }
+ }
+
+ /*write a hidden form and textarea into the page; we'll stow our history stack here*/
+ var formID = "rshStorageForm";
+ var textareaID = "rshStorageField";
+ var formStyles = this.debugMode ? historyStorage.showStyles : historyStorage.hideStyles;
+ var textareaStyles = (historyStorage.debugMode
+ ? 'width: 800px;height:80px;border:1px solid black;'
+ : historyStorage.hideStyles
+ );
+ var textareaHTML = '<form id="' + formID + '" style="' + formStyles + '">'
+ + '<textarea id="' + textareaID + '" style="' + textareaStyles + '"></textarea>'
+ + '</form>';
+ document.write(textareaHTML);
+ this.storageField = document.getElementById(textareaID);
+ if (typeof window.opera !== "undefined") {
+ this.storageField.focus();/*Opera needs to focus this element before persisting values in it*/
+ }
+ },
+
+ /*Public*/
+ put: function(key, value) {
+ this.assertValidKey(key);
+ /*if we already have a value for this, remove the value before adding the new one*/
+ if (this.hasKey(key)) {
+ this.remove(key);
+ }
+ /*store this new key*/
+ this.storageHash[key] = value;
+ /*save and serialize the hashtable into the form*/
+ this.saveHashTable();
+ },
+
+ /*Public*/
+ get: function(key) {
+ this.assertValidKey(key);
+ /*make sure the hash table has been loaded from the form*/
+ this.loadHashTable();
+ var value = this.storageHash[key];
+ if (value === undefined) {
+ value = null;
+ }
+ return value;
+ },
+
+ /*Public*/
+ remove: function(key) {
+ this.assertValidKey(key);
+ /*make sure the hash table has been loaded from the form*/
+ this.loadHashTable();
+ /*delete the value*/
+ delete this.storageHash[key];
+ /*serialize and save the hash table into the form*/
+ this.saveHashTable();
+ },
+
+ /*Public: Clears out all saved data.*/
+ reset: function() {
+ this.storageField.value = "";
+ this.storageHash = {};
+ },
+
+ /*Public*/
+ hasKey: function(key) {
+ this.assertValidKey(key);
+ /*make sure the hash table has been loaded from the form*/
+ this.loadHashTable();
+ return (typeof this.storageHash[key] !== "undefined");
+ },
+
+ /*Public*/
+ isValidKey: function(key) {
+ return (typeof key === "string");
+ },
+
+ /*Public - CSS strings utilized by both objects to hide or show behind-the-scenes DOM elements*/
+ showStyles: 'border:0;margin:0;padding:0;',
+ hideStyles: 'left:-1000px;top:-1000px;width:1px;height:1px;border:0;position:absolute;',
+
+ /*Public - debug mode flag*/
+ debugMode: false,
+
+ /*- - - - - - - - - - - -*/
+
+ /*Private: Our hash of key name/values.*/
+ storageHash: {},
+
+ /*Private: If true, we have loaded our hash table out of the storage form.*/
+ hashLoaded: false,
+
+ /*Private: DOM reference to our history field*/
+ storageField: null,
+
+ /*Private: Assert that a key is valid; throw an exception if it not.*/
+ assertValidKey: function(key) {
+ var isValid = this.isValidKey(key);
+ if (!isValid && this.debugMode) {
+ throw new Error("Please provide a valid key for window.historyStorage. Invalid key = " + key + ".");
+ }
+ },
+
+ /*Private: Load the hash table up from the form.*/
+ loadHashTable: function() {
+ if (!this.hashLoaded) {
+ var serializedHashTable = this.storageField.value;
+ if (serializedHashTable !== "" && serializedHashTable !== null) {
+ this.storageHash = this.fromJSON(serializedHashTable);
+ this.hashLoaded = true;
+ }
+ }
+ },
+ /*Private: Save the hash table into the form.*/
+ saveHashTable: function() {
+ this.loadHashTable();
+ var serializedHashTable = this.toJSON(this.storageHash);
+ this.storageField.value = serializedHashTable;
+ },
+ /*Private: Bridges for our JSON implementations - both rely on 2007 JSON.org library - can be overridden by options bundle*/
+ toJSON: function(o) {
+ return o.toJSONString();
+ },
+ fromJSON: function(s) {
+ return s.parseJSON();
+ }
+};
Added: jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/web/static/js/singlepage/spa.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/plugins/Jifty/Plugin/SinglePage/web/static/js/singlepage/spa.js Wed Apr 9 00:12:34 2008
@@ -0,0 +1,97 @@
+(function($) {
+ SPA = {
+ initialHash: "spa_start",
+ currentHash: null,
+ currentLocation: null,
+ historyChange: function(newLocation, historyData, first) {
+
+ /* reload if user goes to the first page */
+ if (newLocation == SPA.initialHash) {
+ location.href = location.pathname;
+ }
+
+ if (first) {
+ dhtmlHistory.add(newLocation, historyData);
+ } else {
+ if (historyStorage.hasKey(newLocation)) {
+ Jifty.update(historyStorage.get(newLocation), "");
+ }
+ }
+ },
+ _sp_submit_form: function(elt, event, submit_to) {
+ if(event.ctrlKey||event.metaKey||event.altKey||event.shiftKey) return true;
+
+ var form = Jifty.Form.Element.getForm(elt);
+ var elements = Jifty.Form.getElements(form);
+
+ // Three things need to get merged -- hidden defaults, defaults
+ // from buttons, and form values. Hence, we build up three lists
+ // and then merge them.
+ var hiddens = {};
+ var buttons = {};
+ var inputs = {};
+ for (var i = 0; i < elements.length; i++) {
+ var e = elements[i];
+ var parsed = e.getAttribute("name").match(/^J:V-region-__page\.(.*)/);
+ var extras = Jifty.Form.Element.buttonArguments(e);
+
+ var extras_key_length = 0;
+ $.each(extras, function() { extras_key_length++ });
+
+ if (extras_key_length > 1) {
+ // Button with values
+ $.each(extras, function(k, v) {
+ if (k == 'extend') return;
+ parsed = k.match(/^J:V-region-__page\.(.*)/);
+ if ((parsed != null) && (parsed.length == 2)) {
+ buttons[ parsed[1] ] = v;
+ } else if (v.length > 0) {
+ input[ k ] = v;
+ }
+ });
+ } else if ((parsed != null) && (parsed.length == 2)) {
+ // Hidden default
+ hiddens[ parsed[1] ] = $(e).val();
+ } else if (e.name.length > 0) {
+ // Straight up values
+ inputs[ e.name ] = $(e).val();
+ }
+ }
+
+ var args = $.extend({}, hiddens, buttons, inputs);
+
+ return Jifty.update( {'continuation':{},'actions':null,'fragments':[{'mode':'Replace','args':args,'region':'__page','path': submit_to}]}, elt );
+ }
+ };
+
+ /*
+ * If user paste /#/abc in location bar, or click the reload button,
+ * then we should redirect him to the right page
+ */
+ SPA.currentHash = location.hash;
+ if (SPA.currentHash.length) {
+ if (SPA.currentHash.charAt(0) == '#' && SPA.currentHash.charAt(1) == '/') {
+ SPA.currentLocation = SPA.currentHash.slice(1);
+ location.href = SPA.currentLocation;
+ }
+ }
+
+ $(document).ready(function(){
+ dhtmlHistory.initialize();
+ dhtmlHistory.addListener(SPA.historyChange);
+ if (dhtmlHistory.isFirstLoad()) {
+ dhtmlHistory.add(SPA.initialHash, "");
+ }
+ });
+
+})(jQuery);
+
+
+window.dhtmlHistory.create({
+ toJSON: function(o) {
+ return JSON.stringify(o);
+ }
+ , fromJSON: function(s) {
+ return JSON.parse(s);
+ }
+});
Modified: jifty/trunk/share/web/static/js/bps_util.js
==============================================================================
--- jifty/trunk/share/web/static/js/bps_util.js (original)
+++ jifty/trunk/share/web/static/js/bps_util.js Wed Apr 9 00:12:34 2008
@@ -23,7 +23,7 @@
link.setAttribute("href","#");
link.setAttribute("name",e.getAttribute("name"));
- var form = Form.Element.getForm(e);
+ var form = Jifty.Form.Element.getForm(e);
var onclick = e.getAttribute("onclick");
/* Simple buttons that don't use any JS need us to create an onclick
Modified: jifty/trunk/share/web/static/js/calendar.js
==============================================================================
--- jifty/trunk/share/web/static/js/calendar.js (original)
+++ jifty/trunk/share/web/static/js/calendar.js Wed Apr 9 00:12:34 2008
@@ -4,7 +4,7 @@
Jifty.Calendar = {
registerDateWidget: function(id) {
- var input = $(id);
+ var input = document.getElementById(id);
if ( !input ) return false;
@@ -22,7 +22,7 @@
toggleCalendar: function(ev) {
var calId = "cal_" + ev.target.id;
var wrapId = calId + "_wrap";
- var wrap = $(wrapId);
+ var wrap = document.getElementById(wrapId);
var input = ev.target;
if ( Jifty.Calendar.openCalendar == wrapId ) {
@@ -34,7 +34,7 @@
/* We need to delay Jifty's canonicalization until after we've
selected a value via the calendar */
- Form.Element.disableValidation(input);
+ Jifty.Form.Element.disableValidation(input);
wrap = document.createElement("div");
wrap.setAttribute( "id", wrapId );
@@ -96,7 +96,7 @@
openCalendar: "",
hideOpenCalendar: function() {
- if ( Jifty.Calendar.openCalendar && $( Jifty.Calendar.openCalendar ) ) {
+ if ( Jifty.Calendar.openCalendar && document.getElementById( Jifty.Calendar.openCalendar ) ) {
/* Get the input's ID */
var inputId = Jifty.Calendar.openCalendar;
@@ -105,11 +105,11 @@
Element.remove(Jifty.Calendar.openCalendar);
- var input = $( inputId );
+ var input = document.getElementById( inputId );
/* Reenable canonicalization, and do it */
- Form.Element.enableValidation(input);
- Form.Element.validate(input);
+ Jifty.Form.Element.enableValidation(input);
+ Jifty.Form.Element.validate(input);
Jifty.Calendar.openCalendar = "";
}
Modified: jifty/trunk/share/web/static/js/halo.js
==============================================================================
--- jifty/trunk/share/web/static/js/halo.js (original)
+++ jifty/trunk/share/web/static/js/halo.js Wed Apr 9 00:12:34 2008
@@ -7,45 +7,43 @@
function halo_toggle (id) {
if (halo_shown && (id != halo_shown)) {
- halo_top = $('halo-'+halo_shown+'-menu').style.top;
- halo_left = $('halo-'+halo_shown+'-menu').style.left;
- halo_width = $('halo-'+halo_shown+'-menu').style.width;
- Element.hide('halo-'+halo_shown+'-menu');
- }
- $('halo-'+id+'-menu').style.top = halo_top;
- $('halo-'+id+'-menu').style.left = halo_left;
- $('halo-'+id+'-menu').style.width = halo_width;
- Element.toggle('halo-'+id+'-menu');
+ halo_top = Jifty.$('halo-'+halo_shown+'-menu').style.top;
+ halo_left = Jifty.$('halo-'+halo_shown+'-menu').style.left;
+ halo_width = Jifty.$('halo-'+halo_shown+'-menu').style.width;
+ jQuery('#halo-'+halo_shown+'-menu').hide();
+ }
+
+ jQuery("#halo-"+id+"-menu").css({
+ top: halo_top,
+ left: halo_left,
+ width: halo_width
+ }).toggle();
- Drag.init( $('halo-'+id+'-title'), $('halo-'+id+'-menu') );
- init_resize($('halo-'+id+'-resize'), $('halo-'+id+'-menu') );
+ Drag.init( Jifty.$('halo-'+id+'-title'), Jifty.$('halo-'+id+'-menu') );
+ init_resize(Jifty.$('halo-'+id+'-resize'), Jifty.$('halo-'+id+'-menu') );
- var e = $('halo-'+id);
- if (Element.visible('halo-'+id+'-menu')) {
+ var e = jQuery('#halo-'+id).get(0);
+ if (jQuery('#halo-'+id+'-menu').is(":visible")) {
halo_shown = id;
- Element.setStyle(e, {background: '#ffff80'});
+ jQuery(e).css({ background: '#ffff80' });
} else {
- halo_top = $('halo-'+halo_shown+'-menu').style.top;
- halo_left = $('halo-'+halo_shown+'-menu').style.left;
- halo_width = $('halo-'+halo_shown+'-menu').style.width;
+ halo_top = Jifty.$('halo-'+halo_shown+'-menu').style.top;
+ halo_left = Jifty.$('halo-'+halo_shown+'-menu').style.left;
+ halo_width = Jifty.$('halo-'+halo_shown+'-menu').style.width;
halo_shown = null;
- Element.setStyle(e, {background: 'inherit'});
+ jQuery(e).css({ background: 'inherit' });
}
return false;
}
function halo_over (id) {
- var e = $('halo-'+id);
- if (e) {
- Element.setStyle(e, {background: '#ffff80'});
- }
+ jQuery('#halo-'+id).css({ background: '#ffff80' });
}
function halo_out (id) {
- var e = $('halo-'+id);
- if (e && ! Element.visible('halo-'+id+'-menu')) {
- Element.setStyle(e, {background: 'inherit'});
+ if (! jQuery("#halo-"+id+"-menu").is(":visible")) {
+ jQuery('#halo-'+id).css({ background: 'inherit' });
}
}
@@ -79,50 +77,48 @@
halo_padding = '3px';
}
- $("render_info-draw_halos").innerHTML = halos_drawn ? "Hide halos" : "Draw halos";
+ jQuery("#render_info-draw_halos").text(halos_drawn ? "Hide halos" : "Draw halos");
+
+ jQuery(".halo-header").css({
+ display: halo_header_display
+ });
+
+ jQuery(".halo").css({
+ 'border-width': halo_border_width,
+ 'margin': halo_margin,
+ 'padding': halo_padding
+ })
- YAHOO.util.Dom.getElementsByClassName("halo-header", null, null,
- function (e) {
- e.style.display = halo_header_display;
- }
- );
-
- YAHOO.util.Dom.getElementsByClassName("halo", null, null,
- function (e) {
- e.style.borderWidth = halo_border_width;
- e.style.margin = halo_margin;
- e.style.padding = halo_padding;
- }
- );
}
function render_info_tree() {
- Element.toggle("render_info_tree");
+ jQuery("#render_info_tree").toggle();
}
function halo_render(id, name) {
halo_reset(id);
- $('halo-button-'+name+'-'+id).style.fontWeight = 'bold';
- var e = $('halo-rendered-'+id);
+ jQuery('#halo-button-'+name+'-'+id).css("font-weight", "bold");
+
+ var e = jQuery('#halo-rendered-'+id).get(0);
if (name == 'source') {
e.halo_rendered = e.innerHTML;
- e.innerHTML = '<div class="halo-source">' + e.innerHTML.escapeHTML() + '</div>';
+ jQuery(e).html('<div class="halo-source"></div>').find("div").text(e.halo_rendered);
}
else if (name == 'render') {
/* ignore */
}
else {
e.style.display = 'none';
- $('halo-info-'+id).style.display = 'block';
- $('halo-info-'+name+'-'+id).style.display = 'block';
+ jQuery("#halo-info-"+id).show();
+ jQuery('#halo-info-'+name+'-'+id).show();
}
}
function halo_reset(id) {
/* restore all buttons to nonbold */
- for (var child = $('halo-rendermode-'+id).firstChild;
+ for (var child = jQuery('#halo-rendermode-'+id).firstChild;
child != null;
child = child.nextSibling) {
if (child.style) {
@@ -131,8 +127,9 @@
}
/* hide all the info divs */
- $('halo-info-'+id).style.display = 'none';
- for (var child = $('halo-info-'+id).firstChild;
+ jQuery('#halo-info-'+id).hide();
+
+ for (var child = jQuery('#halo-info-'+id).firstChild;
child != null;
child = child.nextSibling) {
if (child.style) {
@@ -141,7 +138,7 @@
}
/* restore the rendered div */
- var e = $('halo-rendered-'+id);
+ var e = jQuery('#halo-rendered-'+id).get(0);
e.style.display = 'block';
if (e.halo_rendered) {
e.innerHTML = e.halo_rendered;
@@ -150,7 +147,7 @@
}
function remove_link(id, name) {
- var link = $('halo-button-'+name+'-'+id);
+ var link = jQuery('#halo-button-'+name+'-'+id).get(0);
var newlink = document.createElement("span");
newlink.appendChild(link.childNodes[0]);
link.parentNode.replaceChild(newlink, link);
Added: jifty/trunk/share/web/static/js/iautocompleter.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/iautocompleter.js Wed Apr 9 00:12:34 2008
@@ -0,0 +1,536 @@
+/**
+ * Interface Elements for jQuery
+ * Autocompleter
+ *
+ * http://interface.eyecon.ro
+ *
+ * Copyright (c) 2006 Stefan Petre
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ */
+
+/**
+ * Attach AJAX driven autocomplete/sugestion box to text input fields.
+ *
+ *
+ *
+ * @name Autocomplete
+ * @description Attach AJAX driven autocomplete/sugestion box to text input fields.
+ * @param Hash hash A hash of parameters
+ * @option String source the URL to request
+ * @option Integer delay (optional) the delayed time to start the AJAX request
+ * @option Boolean autofill (optional) when true the first sugested value fills the input
+ * @option String helperClass (optional) the CSS class applied to sugestion box
+ * @option String selectClass (optional) the CSS class applied to selected/hovered item
+ * @option Integer minchars (optional) the number of characters needed before starting AJAX request
+ * @option Hash fx (optional) {type:[slide|blind|fade]; duration: integer} the fx type to apply to sugestion box and duration for that fx
+ * @option Function onSelect (optional) A function to be executed whenever an item it is selected
+ * @option Function onShow (optional) A function to be executed whenever the suggection box is displayed
+ * @option Function onHide (optional) A function to be executed whenever the suggection box is hidden
+ * @option Function onHighlight (optional) A function to be executed whenever an item it is highlighted
+ *
+ * @type jQuery
+ * @cat Plugins/Interface
+ * @author Stefan Petre
+ */
+jQuery.iAuto = {
+ helper : null,
+ content : null,
+ iframe: null,
+ timer : null,
+ lastValue: null,
+ currentValue: null,
+ subject: null,
+ selectedItem : null,
+ items: null,
+
+ empty : function()
+ {
+ jQuery.iAuto.content.empty();
+ if (jQuery.iAuto.iframe) {
+ jQuery.iAuto.iframe.hide();
+ }
+ },
+
+ clear : function()
+ {
+ jQuery.iAuto.items = null;
+ jQuery.iAuto.selectedItem = null;
+ jQuery.iAuto.lastValue = jQuery.iAuto.subject.value;
+ if(jQuery.iAuto.helper.css('display') == 'block') {
+ if (jQuery.iAuto.subject.autoCFG.fx) {
+ switch(jQuery.iAuto.subject.autoCFG.fx.type) {
+ case 'fade':
+ jQuery.iAuto.helper.fadeOut(jQuery.iAuto.subject.autoCFG.fx.duration, jQuery.iAuto.empty);
+ break;
+ case 'slide':
+ jQuery.iAuto.helper.SlideOutUp(jQuery.iAuto.subject.autoCFG.fx.duration, jQuery.iAuto.empty);
+ break;
+ case 'blind':
+ jQuery.iAuto.helper.BlindUp(jQuery.iAuto.subject.autoCFG.fx.duration, jQuery.iAuto.empty);
+ break;
+ }
+ } else {
+ jQuery.iAuto.helper.hide();
+ }
+ if (jQuery.iAuto.subject.autoCFG.onHide)
+ jQuery.iAuto.subject.autoCFG.onHide.apply(jQuery.iAuto.subject, [jQuery.iAuto.helper, jQuery.iAuto.iframe]);
+ } else {
+ jQuery.iAuto.empty();
+ }
+ window.clearTimeout(jQuery.iAuto.timer);
+ },
+
+ update : function ()
+ {
+ var subject = jQuery.iAuto.subject;
+ var subjectValue = jQuery.iAuto.getFieldValues(subject);
+ //var selectionStart = jQuery.iAuto.getSelectionStart(subject);
+ if (subject && subjectValue.item != jQuery.iAuto.lastValue && subjectValue.item.length >= subject.autoCFG.minchars) {
+ jQuery.iAuto.lastValue = subjectValue.item;
+ jQuery.iAuto.currentValue = subjectValue.item;
+
+ data = {
+ field: jQuery(subject).attr('name')||'field',
+ value: subjectValue.item
+ };
+
+ jQuery.ajax(
+ {
+ type: 'POST',
+ data: jQuery.param(data),
+ success: function(xml)
+ {
+ subject.autoCFG.lastSuggestion = jQuery('item',xml);
+ size = subject.autoCFG.lastSuggestion.size();
+ if (size > 0) {
+ var toWrite = '';
+ subject.autoCFG.lastSuggestion.each(
+ function(nr)
+ {
+ toWrite += '<li rel="' + jQuery('value', this).text() + '" dir="' + nr + '" style="cursor: default;">' + jQuery('text', this).text() + '</li>';
+ }
+ );
+ if (subject.autoCFG.autofill) {
+ var valueToAdd = jQuery('value', subject.autoCFG.lastSuggestion.get(0)).text();
+ subject.value = subjectValue.pre + valueToAdd + subject.autoCFG.multipleSeparator + subjectValue.post;
+ jQuery.iAuto.selection(
+ subject,
+ subjectValue.item.length != valueToAdd.length ? (subjectValue.pre.length + subjectValue.item.length) : valueToAdd.length,
+ subjectValue.item.length != valueToAdd.length ? (subjectValue.pre.length + valueToAdd.length) : valueToAdd.length
+ );
+ }
+
+ if (size > 0) {
+ jQuery.iAuto.writeItems(subject, toWrite);
+ } else {
+ jQuery.iAuto.clear();
+ }
+ } else {
+ jQuery.iAuto.clear();
+ }
+ },
+ url : subject.autoCFG.source
+ }
+ );
+ }
+ },
+
+ writeItems : function(subject, toWrite)
+ {
+ jQuery.iAuto.content.html(toWrite);
+ jQuery.iAuto.items = jQuery('li', jQuery.iAuto.content.get(0));
+ jQuery.iAuto.items
+ .mouseover(jQuery.iAuto.hoverItem)
+ .bind('click', jQuery.iAuto.clickItem);
+ var position = jQuery.iUtil.getPosition(subject);
+ var size = jQuery.iUtil.getSize(subject);
+ jQuery.iAuto.helper
+ .css('top', position.y + size.hb + 'px')
+ .css('left', position.x + 'px')
+ .addClass(subject.autoCFG.helperClass);
+ if (jQuery.iAuto.iframe) {
+ jQuery.iAuto.iframe
+ .css('display', 'block')
+ .css('top', position.y + size.hb + 'px')
+ .css('left', position.x + 'px')
+ .css('width', jQuery.iAuto.helper.css('width'))
+ .css('height', jQuery.iAuto.helper.css('height'));
+ }
+ jQuery.iAuto.selectedItem = 0;
+ jQuery.iAuto.items.get(0).className = subject.autoCFG.selectClass;
+ jQuery.iAuto.applyOn(subject,subject.autoCFG.lastSuggestion.get(0), 'onHighlight');
+
+ if (jQuery.iAuto.helper.css('display') == 'none') {
+ if (subject.autoCFG.inputWidth) {
+ var borders = jQuery.iUtil.getPadding(subject, true);
+ var paddings = jQuery.iUtil.getBorder(subject, true);
+ jQuery.iAuto.helper.css('width', subject.offsetWidth - (jQuery.boxModel ? (borders.l + borders.r + paddings.l + paddings.r) : 0 ) + 'px');
+ }
+ if (subject.autoCFG.fx) {
+ switch(subject.autoCFG.fx.type) {
+ case 'fade':
+ jQuery.iAuto.helper.fadeIn(subject.autoCFG.fx.duration);
+ break;
+ case 'slide':
+ jQuery.iAuto.helper.SlideInUp(subject.autoCFG.fx.duration);
+ break;
+ case 'blind':
+ jQuery.iAuto.helper.BlindDown(subject.autoCFG.fx.duration);
+ break;
+ }
+ } else {
+ jQuery.iAuto.helper.show();
+ }
+
+ if (jQuery.iAuto.subject.autoCFG.onShow)
+ jQuery.iAuto.subject.autoCFG.onShow.apply(jQuery.iAuto.subject, [jQuery.iAuto.helper, jQuery.iAuto.iframe]);
+ }
+ },
+
+ checkCache : function()
+ {
+ var subject = this;
+ if (subject.autoCFG.lastSuggestion) {
+
+ jQuery.iAuto.lastValue = subject.value;
+ jQuery.iAuto.currentValue = subject.value;
+
+ var toWrite = '';
+ subject.autoCFG.lastSuggestion.each(
+ function(nr)
+ {
+ value = jQuery('value', this).text().toLowerCase();
+ inputValue = subject.value.toLowerCase();
+ if (value.indexOf(inputValue) == 0) {
+ toWrite += '<li rel="' + jQuery('value', this).text() + '" dir="' + nr + '" style="cursor: default;">' + jQuery('text', this).text() + '</li>';
+ }
+ }
+ );
+
+ if (toWrite != '') {
+ jQuery.iAuto.writeItems(subject, toWrite);
+
+ this.autoCFG.inCache = true;
+ return;
+ }
+ }
+ subject.autoCFG.lastSuggestion = null;
+ this.autoCFG.inCache = false;
+ },
+
+ selection : function(field, start, end)
+ {
+ if (field.createTextRange) {
+ var selRange = field.createTextRange();
+ selRange.collapse(true);
+ selRange.moveStart("character", start);
+ selRange.moveEnd("character", - end + start);
+ selRange.select();
+ } else if (field.setSelectionRange) {
+ field.setSelectionRange(start, end);
+ } else {
+ if (field.selectionStart) {
+ field.selectionStart = start;
+ field.selectionEnd = end;
+ }
+ }
+ field.focus();
+ },
+
+ getSelectionStart : function(field)
+ {
+ if (field.selectionStart)
+ return field.selectionStart;
+ else if(field.createTextRange) {
+ var selRange = document.selection.createRange();
+ var selRange2 = selRange.duplicate();
+ return 0 - selRange2.moveStart('character', -100000);
+ //result.end = result.start + range.text.length;
+ /*var selRange = document.selection.createRange();
+ var isCollapsed = selRange.compareEndPoints("StartToEnd", selRange) == 0;
+ if (!isCollapsed)
+ selRange.collapse(true);
+ var bookmark = selRange.getBookmark();
+ return bookmark.charCodeAt(2) - 2;*/
+ }
+ },
+
+ getFieldValues : function(field)
+ {
+ var fieldData = {
+ value: field.value,
+ pre: '',
+ post: '',
+ item: ''
+ };
+
+ if(field.autoCFG.multiple) {
+ var finishedPre = false;
+ var selectionStart = jQuery.iAuto.getSelectionStart(field)||0;
+ var chunks = fieldData.value.split(field.autoCFG.multipleSeparator);
+ for (var i=0; i<chunks.length; i++) {
+ if(
+ (fieldData.pre.length + chunks[i].length >= selectionStart
+ ||
+ selectionStart == 0)
+ &&
+ !finishedPre
+ ) {
+ if (fieldData.pre.length <= selectionStart)
+ fieldData.item = chunks[i];
+ else
+ fieldData.post += chunks[i] + (chunks[i] != '' ? field.autoCFG.multipleSeparator : '');
+ finishedPre = true;
+ } else if (finishedPre){
+ fieldData.post += chunks[i] + (chunks[i] != '' ? field.autoCFG.multipleSeparator : '');
+ }
+ if(!finishedPre) {
+ fieldData.pre += chunks[i] + (chunks.length > 1 ? field.autoCFG.multipleSeparator : '');
+ }
+ }
+ } else {
+ fieldData.item = fieldData.value;
+ }
+ return fieldData;
+ },
+
+ autocomplete : function(e)
+ {
+ window.clearTimeout(jQuery.iAuto.timer);
+ var subject = jQuery.iAuto.getFieldValues(this);
+
+ var pressedKey = e.charCode || e.keyCode || -1;
+ if (/13|27|35|36|38|40|9/.test(pressedKey) && jQuery.iAuto.items) {
+ if (window.event) {
+ window.event.cancelBubble = true;
+ window.event.returnValue = false;
+ } else {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ if (jQuery.iAuto.selectedItem != null)
+ jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0).className = '';
+ else
+ jQuery.iAuto.selectedItem = -1;
+ switch(pressedKey) {
+ //enter
+ case 9:
+ case 13:
+ if (jQuery.iAuto.selectedItem == -1)
+ jQuery.iAuto.selectedItem = 0;
+ var selectedItem = jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0);
+ var valueToAdd = selectedItem.getAttribute('rel');
+ this.value = subject.pre + valueToAdd + this.autoCFG.multipleSeparator + subject.post;
+ jQuery.iAuto.lastValue = subject.item;
+ jQuery.iAuto.selection(
+ this,
+ subject.pre.length + valueToAdd.length + this.autoCFG.multipleSeparator.length,
+ subject.pre.length + valueToAdd.length + this.autoCFG.multipleSeparator.length
+ );
+ jQuery.iAuto.clear();
+ if (this.autoCFG.onSelect) {
+ iteration = parseInt(selectedItem.getAttribute('dir'))||0;
+ jQuery.iAuto.applyOn(this,this.autoCFG.lastSuggestion.get(iteration), 'onSelect');
+ }
+ if (this.scrollIntoView)
+ this.scrollIntoView(false);
+ return pressedKey != 13;
+ break;
+ //escape
+ case 27:
+ this.value = subject.pre + jQuery.iAuto.lastValue + this.autoCFG.multipleSeparator + subject.post;
+ this.autoCFG.lastSuggestion = null;
+ jQuery.iAuto.clear();
+ if (this.scrollIntoView)
+ this.scrollIntoView(false);
+ return false;
+ break;
+ //end
+ case 35:
+ jQuery.iAuto.selectedItem = jQuery.iAuto.items.size() - 1;
+ break;
+ //home
+ case 36:
+ jQuery.iAuto.selectedItem = 0;
+ break;
+ //up
+ case 38:
+ jQuery.iAuto.selectedItem --;
+ if (jQuery.iAuto.selectedItem < 0)
+ jQuery.iAuto.selectedItem = jQuery.iAuto.items.size() - 1;
+ break;
+ case 40:
+ jQuery.iAuto.selectedItem ++;
+ if (jQuery.iAuto.selectedItem == jQuery.iAuto.items.size())
+ jQuery.iAuto.selectedItem = 0;
+ break;
+ }
+ jQuery.iAuto.applyOn(this,this.autoCFG.lastSuggestion.get(jQuery.iAuto.selectedItem||0), 'onHighlight');
+ jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0).className = this.autoCFG.selectClass;
+ if (jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0).scrollIntoView)
+ jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0).scrollIntoView(false);
+ if(this.autoCFG.autofill) {
+ var valToAdd = jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0).getAttribute('rel');
+ this.value = subject.pre + valToAdd + this.autoCFG.multipleSeparator + subject.post;
+ if(jQuery.iAuto.lastValue.length != valToAdd.length)
+ jQuery.iAuto.selection(
+ this,
+ subject.pre.length + jQuery.iAuto.lastValue.length,
+ subject.pre.length + valToAdd.length
+ );
+ }
+ return false;
+ }
+ jQuery.iAuto.checkCache.apply(this);
+
+ if (this.autoCFG.inCache == false) {
+ if (subject.item != jQuery.iAuto.lastValue && subject.item.length >= this.autoCFG.minchars)
+ jQuery.iAuto.timer = window.setTimeout(jQuery.iAuto.update, this.autoCFG.delay);
+ if (jQuery.iAuto.items) {
+ jQuery.iAuto.clear();
+ }
+ }
+ return true;
+ },
+
+ applyOn: function(field, item, type)
+ {
+ if (field.autoCFG[type]) {
+ var data = {};
+ childs = item.getElementsByTagName('*');
+ for(i=0; i<childs.length; i++){
+ data[childs[i].tagName] = childs[i].firstChild.nodeValue;
+ }
+ field.autoCFG[type].apply(field,[data]);
+ }
+ },
+
+ hoverItem : function(e)
+ {
+ if (jQuery.iAuto.items) {
+ if (jQuery.iAuto.selectedItem != null)
+ jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0).className = '';
+ jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0).className = '';
+ jQuery.iAuto.selectedItem = parseInt(this.getAttribute('dir'))||0;
+ jQuery.iAuto.items.get(jQuery.iAuto.selectedItem||0).className = jQuery.iAuto.subject.autoCFG.selectClass;
+ }
+ },
+
+ clickItem : function(event)
+ {
+ window.clearTimeout(jQuery.iAuto.timer);
+
+ event = event || jQuery.event.fix( window.event );
+ event.preventDefault();
+ event.stopPropagation();
+ var subject = jQuery.iAuto.getFieldValues(jQuery.iAuto.subject);
+ var valueToAdd = this.getAttribute('rel');
+ jQuery.iAuto.subject.value = subject.pre + valueToAdd + jQuery.iAuto.subject.autoCFG.multipleSeparator + subject.post;
+ jQuery.iAuto.lastValue = this.getAttribute('rel');
+ jQuery.iAuto.selection(
+ jQuery.iAuto.subject,
+ subject.pre.length + valueToAdd.length + jQuery.iAuto.subject.autoCFG.multipleSeparator.length,
+ subject.pre.length + valueToAdd.length + jQuery.iAuto.subject.autoCFG.multipleSeparator.length
+ );
+ jQuery.iAuto.clear();
+ if (jQuery.iAuto.subject.autoCFG.onSelect) {
+ iteration = parseInt(this.getAttribute('dir'))||0;
+ jQuery.iAuto.applyOn(jQuery.iAuto.subject,jQuery.iAuto.subject.autoCFG.lastSuggestion.get(iteration), 'onSelect');
+ }
+
+ return false;
+ },
+
+ protect : function(e)
+ {
+ pressedKey = e.charCode || e.keyCode || -1;
+ if (/13|27|35|36|38|40/.test(pressedKey) && jQuery.iAuto.items) {
+ if (window.event) {
+ window.event.cancelBubble = true;
+ window.event.returnValue = false;
+ } else {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ return false;
+ }
+ },
+
+ build : function(options)
+ {
+ if (!options.source || !jQuery.iUtil) {
+ return;
+ }
+
+ if (!jQuery.iAuto.helper) {
+ if (jQuery.browser.msie) {
+ jQuery('body', document).append('<iframe style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" id="autocompleteIframe" src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
+ jQuery.iAuto.iframe = jQuery('#autocompleteIframe');
+ }
+ jQuery('body', document).append('<div id="autocompleteHelper" style="position: absolute; top: 0; left: 0; z-index: 30001; display: none;"><ul style="margin: 0;padding: 0; list-style: none; z-index: 30002;"> </ul></div>');
+ jQuery.iAuto.helper = jQuery('#autocompleteHelper');
+ jQuery.iAuto.content = jQuery('ul', jQuery.iAuto.helper);
+ }
+
+ return this.each(
+ function()
+ {
+ if (this.tagName != 'INPUT' && this.getAttribute('type') != 'text' )
+ return;
+ this.autoCFG = {};
+ this.autoCFG.source = options.source;
+ this.autoCFG.minchars = Math.abs(parseInt(options.minchars)||1);
+ this.autoCFG.helperClass = options.helperClass ? options.helperClass : '';
+ this.autoCFG.selectClass = options.selectClass ? options.selectClass : '';
+ this.autoCFG.onSelect = options.onSelect && options.onSelect.constructor == Function ? options.onSelect : null;
+ this.autoCFG.onShow = options.onShow && options.onShow.constructor == Function ? options.onShow : null;
+ this.autoCFG.onHide = options.onHide && options.onHide.constructor == Function ? options.onHide : null;
+ this.autoCFG.onHighlight = options.onHighlight && options.onHighlight.constructor == Function ? options.onHighlight : null;
+ this.autoCFG.inputWidth = options.inputWidth||false;
+ this.autoCFG.multiple = options.multiple||false;
+ this.autoCFG.multipleSeparator = this.autoCFG.multiple ? (options.multipleSeparator||', '):'';
+ this.autoCFG.autofill = options.autofill ? true : false;
+ this.autoCFG.delay = Math.abs(parseInt(options.delay)||1000);
+ if (options.fx && options.fx.constructor == Object) {
+ if (!options.fx.type || !/fade|slide|blind/.test(options.fx.type)) {
+ options.fx.type = 'slide';
+ }
+ if (options.fx.type == 'slide' && !jQuery.fx.slide)
+ return;
+ if (options.fx.type == 'blind' && !jQuery.fx.BlindDirection)
+ return;
+
+ options.fx.duration = Math.abs(parseInt(options.fx.duration)||400);
+ if (options.fx.duration > this.autoCFG.delay) {
+ options.fx.duration = this.autoCFG.delay - 100;
+ }
+ this.autoCFG.fx = options.fx;
+ }
+ this.autoCFG.lastSuggestion = null;
+ this.autoCFG.inCache = false;
+
+ jQuery(this)
+ .attr('autocomplete', 'off')
+ .focus(
+ function()
+ {
+ jQuery.iAuto.subject = this;
+ jQuery.iAuto.lastValue = this.value;
+ }
+ )
+ .keypress(jQuery.iAuto.protect)
+ .keyup(jQuery.iAuto.autocomplete)
+
+ .blur(
+ function()
+ {
+ jQuery.iAuto.timer = window.setTimeout(jQuery.iAuto.clear, 200);
+ }
+ );
+ }
+ );
+ }
+};
+jQuery.fn.Autocomplete = jQuery.iAuto.build;
\ No newline at end of file
Added: jifty/trunk/share/web/static/js/iutil.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/iutil.js Wed Apr 9 00:12:34 2008
@@ -0,0 +1,245 @@
+/**
+ * Interface Elements for jQuery
+ * utility function
+ *
+ * http://interface.eyecon.ro
+ *
+ * Copyright (c) 2006 Stefan Petre
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ *
+ */
+
+jQuery.iUtil = {
+ getPosition : function(e)
+ {
+ var x = 0;
+ var y = 0;
+ var es = e.style;
+ var restoreStyles = false;
+ if (jQuery(e).css('display') == 'none') {
+ var oldVisibility = es.visibility;
+ var oldPosition = es.position;
+ restoreStyles = true;
+ es.visibility = 'hidden';
+ es.display = 'block';
+ es.position = 'absolute';
+ }
+ var el = e;
+ while (el){
+ x += el.offsetLeft + (el.currentStyle && !jQuery.browser.opera ?parseInt(el.currentStyle.borderLeftWidth)||0:0);
+ y += el.offsetTop + (el.currentStyle && !jQuery.browser.opera ?parseInt(el.currentStyle.borderTopWidth)||0:0);
+ el = el.offsetParent;
+ }
+ el = e;
+ while (el && el.tagName && el.tagName.toLowerCase() != 'body')
+ {
+ x -= el.scrollLeft||0;
+ y -= el.scrollTop||0;
+ el = el.parentNode;
+ }
+ if (restoreStyles == true) {
+ es.display = 'none';
+ es.position = oldPosition;
+ es.visibility = oldVisibility;
+ }
+ return {x:x, y:y};
+ },
+ getPositionLite : function(el)
+ {
+ var x = 0, y = 0;
+ while(el) {
+ x += el.offsetLeft || 0;
+ y += el.offsetTop || 0;
+ el = el.offsetParent;
+ }
+ return {x:x, y:y};
+ },
+ getSize : function(e)
+ {
+ var w = jQuery.css(e,'width');
+ var h = jQuery.css(e,'height');
+ var wb = 0;
+ var hb = 0;
+ var es = e.style;
+ if (jQuery(e).css('display') != 'none') {
+ wb = e.offsetWidth;
+ hb = e.offsetHeight;
+ } else {
+ var oldVisibility = es.visibility;
+ var oldPosition = es.position;
+ es.visibility = 'hidden';
+ es.display = 'block';
+ es.position = 'absolute';
+ wb = e.offsetWidth;
+ hb = e.offsetHeight;
+ es.display = 'none';
+ es.position = oldPosition;
+ es.visibility = oldVisibility;
+ }
+ return {w:w, h:h, wb:wb, hb:hb};
+ },
+ getSizeLite : function(el)
+ {
+ return {
+ wb:el.offsetWidth||0,
+ hb:el.offsetHeight||0
+ };
+ },
+ getClient : function(e)
+ {
+ var h, w, de;
+ if (e) {
+ w = e.clientWidth;
+ h = e.clientHeight;
+ } else {
+ de = document.documentElement;
+ w = window.innerWidth || self.innerWidth || (de&&de.clientWidth) || document.body.clientWidth;
+ h = window.innerHeight || self.innerHeight || (de&&de.clientHeight) || document.body.clientHeight;
+ }
+ return {w:w,h:h};
+ },
+ getScroll : function (e)
+ {
+ var t=0, l=0, w=0, h=0, iw=0, ih=0;
+ if (e && e.nodeName.toLowerCase() != 'body') {
+ t = e.scrollTop;
+ l = e.scrollLeft;
+ w = e.scrollWidth;
+ h = e.scrollHeight;
+ iw = 0;
+ ih = 0;
+ } else {
+ if (document.documentElement) {
+ t = document.documentElement.scrollTop;
+ l = document.documentElement.scrollLeft;
+ w = document.documentElement.scrollWidth;
+ h = document.documentElement.scrollHeight;
+ } else if (document.body) {
+ t = document.body.scrollTop;
+ l = document.body.scrollLeft;
+ w = document.body.scrollWidth;
+ h = document.body.scrollHeight;
+ }
+ iw = self.innerWidth||document.documentElement.clientWidth||document.body.clientWidth||0;
+ ih = self.innerHeight||document.documentElement.clientHeight||document.body.clientHeight||0;
+ }
+ return { t: t, l: l, w: w, h: h, iw: iw, ih: ih };
+ },
+ getMargins : function(e, toInteger)
+ {
+ var el = jQuery(e);
+ var t = el.css('marginTop') || '';
+ var r = el.css('marginRight') || '';
+ var b = el.css('marginBottom') || '';
+ var l = el.css('marginLeft') || '';
+ if (toInteger)
+ return {
+ t: parseInt(t)||0,
+ r: parseInt(r)||0,
+ b: parseInt(b)||0,
+ l: parseInt(l)
+ };
+ else
+ return {t: t, r: r, b: b, l: l};
+ },
+ getPadding : function(e, toInteger)
+ {
+ var el = jQuery(e);
+ var t = el.css('paddingTop') || '';
+ var r = el.css('paddingRight') || '';
+ var b = el.css('paddingBottom') || '';
+ var l = el.css('paddingLeft') || '';
+ if (toInteger)
+ return {
+ t: parseInt(t)||0,
+ r: parseInt(r)||0,
+ b: parseInt(b)||0,
+ l: parseInt(l)
+ };
+ else
+ return {t: t, r: r, b: b, l: l};
+ },
+ getBorder : function(e, toInteger)
+ {
+ var el = jQuery(e);
+ var t = el.css('borderTopWidth') || '';
+ var r = el.css('borderRightWidth') || '';
+ var b = el.css('borderBottomWidth') || '';
+ var l = el.css('borderLeftWidth') || '';
+ if (toInteger)
+ return {
+ t: parseInt(t)||0,
+ r: parseInt(r)||0,
+ b: parseInt(b)||0,
+ l: parseInt(l)||0
+ };
+ else
+ return {t: t, r: r, b: b, l: l};
+ },
+ getPointer : function(event)
+ {
+ var x = event.pageX || (event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft)) || 0;
+ var y = event.pageY || (event.clientY + (document.documentElement.scrollTop || document.body.scrollTop)) || 0;
+ return {x:x, y:y};
+ },
+ traverseDOM : function(nodeEl, func)
+ {
+ func(nodeEl);
+ nodeEl = nodeEl.firstChild;
+ while(nodeEl){
+ jQuery.iUtil.traverseDOM(nodeEl, func);
+ nodeEl = nodeEl.nextSibling;
+ }
+ },
+ purgeEvents : function(nodeEl)
+ {
+ jQuery.iUtil.traverseDOM(
+ nodeEl,
+ function(el)
+ {
+ for(var attr in el){
+ if(typeof el[attr] === 'function') {
+ el[attr] = null;
+ }
+ }
+ }
+ );
+ },
+ centerEl : function(el, axis)
+ {
+ var clientScroll = jQuery.iUtil.getScroll();
+ var windowSize = jQuery.iUtil.getSize(el);
+ if (!axis || axis == 'vertically')
+ jQuery(el).css(
+ {
+ top: clientScroll.t + ((Math.max(clientScroll.h,clientScroll.ih) - clientScroll.t - windowSize.hb)/2) + 'px'
+ }
+ );
+ if (!axis || axis == 'horizontally')
+ jQuery(el).css(
+ {
+ left: clientScroll.l + ((Math.max(clientScroll.w,clientScroll.iw) - clientScroll.l - windowSize.wb)/2) + 'px'
+ }
+ );
+ },
+ fixPNG : function (el, emptyGIF) {
+ var images = jQuery('img[@src*="png"]', el||document), png;
+ images.each( function() {
+ png = this.src;
+ this.src = emptyGIF;
+ this.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + png + "')";
+ });
+ }
+};
+
+// Helper function to support older browsers!
+[].indexOf || (Array.prototype.indexOf = function(v, n){
+ n = (n == null) ? 0 : n;
+ var m = this.length;
+ for (var i=n; i<m; i++)
+ if (this[i] == v)
+ return i;
+ return -1;
+});
Modified: jifty/trunk/share/web/static/js/jifty.js
==============================================================================
--- jifty/trunk/share/web/static/js/jifty.js (original)
+++ jifty/trunk/share/web/static/js/jifty.js Wed Apr 9 00:12:34 2008
@@ -1,14 +1,38 @@
/* An empty class so we can create things inside it */
-var Jifty = Class.create();
+var Jifty = {};
-Jifty.Web = Class.create();
-Jifty.Web.current_actions = new Array;
+Jifty.$ = function(id) {
+ if (typeof id == 'string')
+ return document.getElementById(id)
+ return id;
+}
+
+Jifty.hasAjaxTransport = function() {
+ var r = false;
+ jQuery.each(
+ [
+ function() {return new XMLHttpRequest()},
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+ ],
+ function(i, v) {
+ try {
+ r = v();
+ if (r) return false;
+ } catch(e) {}
+ })
+ return r ? true : false;
+}();
+
+Jifty.Web = {};
+Jifty.Web.current_actions = [];
Jifty.Web.new_action = function() {
var args = _get_named_args(arguments);
var a;
- Jifty.Web.current_actions.each(function(x) { if (x.moniker == args.moniker) a = x });
+ jQuery(Jifty.Web.current_actions).each(function(i, v) {
+ if (v.moniker == args.moniker) a = v;
+ });
if (!a) throw "hate";
-
return a;
};
@@ -93,7 +117,11 @@
}
/* Actions */
-var Action = Class.create();
+var Action = function() {
+ this.initialize.apply(this, arguments);
+ return this;
+};
+
Action.prototype = {
// New takes the moniker (a string), and an optional array of form
// elements to additionally take into consideration
@@ -101,20 +129,23 @@
this.moniker = moniker;
// Extra form parameters
- this.extras = $A();
+ this.extras = [];
if (arguments.length > 1) {
this.extras = arguments[1];
}
- this.register = $('J:A-' + this.moniker); // Simple case -- no ordering information
+ this.register = Jifty.$('J:A-' + this.moniker); // Simple case -- no ordering information
if (! this.register) {
+ var elements = [];
// We need to go looking -- this also goes looking through this.extras, from above
- var elements = $A(document.getElementsByTagName('input'));
- for (var i = 0; i < this.extras.length; i++)
- elements.push(this.extras[i]);
- for (var i = 0; i < elements.length; i++) {
- if ((Form.Element.getMoniker(elements[i]) == this.moniker)
- && (Form.Element.getType(elements[i]) == "registration")) {
+
+ var add_to_elements = function(){ elements.push(this) };
+ jQuery('input').each(add_to_elements);
+ jQuery.each(this.extras, add_to_elements);
+
+ for (var i = 0, l = elements.length; i < l; i++) {
+ if ((Jifty.Form.Element.getMoniker(elements[i]) == this.moniker)
+ && (Jifty.Form.Element.getType(elements[i]) == "registration")) {
this.register = elements[i];
break;
}
@@ -122,7 +153,7 @@
}
if (this.register) {
- this.form = Form.Element.getForm(this.register);
+ this.form = Jifty.Form.Element.getForm(this.register);
this.actionClass = this.register.value;
}
},
@@ -130,14 +161,14 @@
// Returns an Array of all fields in this Action
fields: function() {
if(!this.cached_fields) {
- var elements = new Array;
- var possible = Form.getElements(this.form);
+ var elements = [];
+ var possible = Jifty.Form.getElements(this.form);
// Also pull from extra query parameters
- for (var i = 0; i < this.extras.length; i++)
+ for (var i = 0, l = this.extras.length; i < l; i++)
possible.push(this.extras[i]);
- for (var i = 0; i < possible.length; i++) {
- if (Form.Element.getMoniker(possible[i]) == this.moniker)
+ for (var i = 0, l = possible.length; i < l; i++) {
+ if (Jifty.Form.Element.getMoniker(possible[i]) == this.moniker)
elements.push(possible[i]);
}
this.cached_fields = elements;
@@ -147,10 +178,10 @@
buttons: function() {
var elements = new Array();
- var possible = Form.getElements(this.form);
+ var possible = Jifty.Form.getElements(this.form);
for(var i = 0; i < possible.length; i++) {
if(possible[i].nodeName == 'INPUT' && possible[i].getAttribute("type") == 'submit') {
- actions = Form.Element.buttonActions(possible[i]);
+ actions = Jifty.Form.Element.buttonActions(possible[i]);
//If the button has no actions explicitly associated
//with it, it's associated with all the actions in the
//form
@@ -166,7 +197,7 @@
getField: function(name) {
var elements = this.fields();
for (var i = 0; i < elements.length; i++) {
- if (Form.Element.getField(elements[i]) == name)
+ if (Jifty.Form.Element.getField(elements[i]) == name)
return elements[i];
}
return null;
@@ -177,16 +208,17 @@
var fields = this.fields();
var serialized = new Array;
- for (var i = 0; i < fields.length; i++) {
- serialized.push(Form.Element.serialize(fields[i]));
- }
+ jQuery.each(fields, function() {
+ serialized.push( jQuery(this).serialize() )
+ });
+
return serialized.join('&');
},
// Returns true if there is a file upload form as one of our elements
hasUpload: function() {
var fields = this.fields();
- for (var i = 0; i < fields.length; i++) {
+ for (var i = 0, l = fields.length; i < l; i++) {
if ((fields[i].getAttribute("type") == "file") && fields[i].value)
return true;
}
@@ -204,21 +236,22 @@
for (var i = 0; i < fields.length; i++) {
var f = fields[i];
- if ( (Form.Element.getType(f) != "registration")
- && (Form.Element.getValue(f) != null)
+ if ( (Jifty.Form.Element.getType(f) != "registration")
+ && (Jifty.Form.Element.getValue(f) != null)
&& (!Jifty.Placeholder.hasPlaceholder(f)))
{
- if (! a['fields'][Form.Element.getField(f)])
- a['fields'][Form.Element.getField(f)] = {};
- var field = Form.Element.getField(f);
- var type = Form.Element.getType(f);
+ if (! a['fields'][Jifty.Form.Element.getField(f)])
+ a['fields'][Jifty.Form.Element.getField(f)] = {};
+ var field = Jifty.Form.Element.getField(f);
+ var type = Jifty.Form.Element.getType(f);
// XXX: fallback value being an array makes server
// upset, we don't think that should happen anyway
if (type == 'fallback' && a['fields'][field][type])
- continue
+ continue
a['fields'][field][type] = this._mergeValues(a['fields'][field][type],
- Form.Element.getValue(f));
+ Jifty.Form.Element.getValue(f));
+
}
}
@@ -241,80 +274,75 @@
show_wait_message();
var id = this.register.id;
- new Ajax.Request(
- '/__jifty/validator.xml', // Right now, the URL is actually completely irrelevant
- {
- asynchronous: 1,
- method: "get",
- parameters: this.serialize() + "&J:VALIDATE=1",
- onComplete:
- function (request) {
- var response = request.responseXML.documentElement;
- for (var action = response.firstChild; action != null; action = action.nextSibling) {
- if ((action.nodeName == 'validationaction') && (action.getAttribute("id") == id)) {
- for (var field = action.firstChild; field != null; field = field.nextSibling) {
- // Possibilities for field.nodeName: it could be #text (whitespace),
- // or 'blank' (the field was blank, don't mess with the error div), or 'ok'
- // (clear the error and warning div!) or 'error' (fill in the error div, clear
- // the warning div!) or 'warning' (fill in the warning div and clear the error div!)
- if (field.nodeName == 'error' || field.nodeName == 'warning') {
- var err_div = document.getElementById(field.getAttribute("id"));
- if (err_div != null) {
- Element.show(err_div);
- err_div.innerHTML = field.firstChild.data;
- }
- } else if (field.nodeName == 'ok') {
- var err_div = document.getElementById(field.getAttribute("id"));
- if (err_div != null) {
- Element.hide(err_div);
- err_div.innerHTML = '';
- }
- }
+ jQuery.ajax({
+ url: '/__jifty/validator.xml', // Right now, the URL is actually completely irrelevant
+ type: "get",
+ data: this.serialize() + "&J:VALIDATE=1",
+ complete: function (request, status) {
+ var response = request.responseXML.documentElement;
+ for (var action = response.firstChild; action != null; action = action.nextSibling) {
+ if ((action.nodeName == 'validationaction') && (action.getAttribute("id") == id)) {
+ for (var field = action.firstChild; field != null; field = field.nextSibling) {
+ // Possibilities for field.nodeName: it could be #text (whitespace),
+ // or 'blank' (the field was blank, don't mess with the error div), or 'ok'
+ // (clear the error and warning div!) or 'error' (fill in the error div, clear
+ // the warning div!) or 'warning' (fill in the warning div and clear the error div!)
+ if (field.nodeName == 'error' || field.nodeName == 'warning') {
+ var err_div = document.getElementById(field.getAttribute("id"));
+ if (err_div != null) {
+ jQuery(err_div).show().html(field.firstChild.data);
}
- } else if ((action.nodeName == 'canonicalizeaction') && (action.getAttribute("id") == id)) {
- for (var field = action.firstChild; field != null; field = field.nextSibling) {
- // Possibilities for field.nodeName: it could be 'ignored', 'blank' , 'update', or 'info'
- // info is a separate action from the update
- if (field.nodeName == 'canonicalization_note') {
- var note_div= document.getElementById(field.getAttribute("id"));
- if (note_div != null) {
- Element.show(note_div);
- note_div.innerHTML = field.firstChild.data;
- }
- }
-
- if (field.nodeName == 'update') {
- var field_name = field.getAttribute("name");
- for (var form_number = 0 ; form_number < document.forms.length; form_number++) {
- var form_field = document.forms[form_number].elements[field_name];
- if (form_field == null || !form_field.hasClassName('ajaxcanonicalization'))
- continue;
- form_field.value = field.firstChild.data;
- }
- }
+ } else if (field.nodeName == 'ok') {
+ var err_div = document.getElementById(field.getAttribute("id"));
+ if (err_div != null) {
+ jQuery(err_div).hide().html('');
+ }
+ }
+ }
+ } else if ((action.nodeName == 'canonicalizeaction') && (action.getAttribute("id") == id)) {
+ for (var field = action.firstChild; field != null; field = field.nextSibling) {
+ // Possibilities for field.nodeName: it could be 'ignored', 'blank' , 'update', or 'info'
+ // info is a separate action from the update
+ if (field.nodeName == 'canonicalization_note') {
+ var note_div= document.getElementById(field.getAttribute("id"));
+ if (note_div != null) {
+ jQuery(note_div).show().html(field.firstChild.data);
+ }
+ }
+
+ if (field.nodeName == 'update') {
+ var field_name = field.getAttribute("name");
+ for (var form_number = 0 ; form_number < document.forms.length; form_number++) {
+ var form_field = document.forms[form_number].elements[field_name];
+ if (form_field == null || !jQuery(form_field).is('.ajaxcanonicalization'))
+ continue;
+ form_field.value = field.firstChild.data;
}
}
}
- return true;
}
- }
- );
+ }
+ return true;
+ }
+ });
+
hide_wait_message();
return false;
},
submit: function() {
show_wait_message();
- new Ajax.Request(
- '/empty',
- { parameters: this.serialize() }
- );
+ jQuery.ajax({
+ url: '/empty',
+ type: 'post',
+ data: this.serialize()
+ });
hide_wait_message();
},
disable_input_fields: function(disabled_elements) {
var disable = function() {
- var elt = arguments[0];
+ var elt = this;
// Disabling hidden elements seems to make IE sad for some reason
if(elt.type != 'hidden') {
// Triggers https://bugzilla.mozilla.org/show_bug.cgi?id=236791
@@ -323,14 +351,14 @@
disabled_elements.push(elt);
}
};
- this.fields().each(disable);
- this.buttons().each(disable);
+ jQuery.each(this.fields(), disable);
+ jQuery.each(this.buttons(), disable);
},
enable_input_fields: function() {
- var enable = function() { arguments[0].disabled = false; };
- this.fields().each( enable );
- this.buttons().each( enable );
+ var enable = function() { this.disabled = false; };
+ jQuery.each(this.fields(), disable);
+ jQuery.each(this.buttons(), disable);
},
@@ -339,9 +367,9 @@
if (!this.s_a) {
/* XXX: make REST client accessible */
var Todo = new AsynapseRecord('todo');
- this.s_a = $H(Todo.eval_ajax_get('/=/action/'+this.actionClass+'.js'));
+ this.s_a = jQuery.extend({}, Todo.eval_ajax_get('/=/action/'+this.actionClass+'.js'));
}
-
+
return this.s_a
},
argument_names: function() {
@@ -360,7 +388,11 @@
var SERIAL_postfix = Math.ceil(10000*Math.random());
var SERIAL = 0;
-ActionField = Class.create();
+var ActionField = function() {
+ this.initialize.apply(this, arguments);
+ return this;
+};
+
ActionField.prototype = {
initialize: function(name, args, action) {
this.name = name;
@@ -472,58 +504,46 @@
};
/* Forms */
-Jifty.Form = Class.create();
-Object.extend(Jifty.Form, {
+Jifty.Form = {};
+
+jQuery.extend(Jifty.Form, {
getElements: function(element) {
- return Form.getElements(element);
+ return jQuery(":input", element).get();
},
+
// Return an Array of Actions that are in this form
getActions: function (element) {
- var elements = new Array;
- var possible = Form.getElements(element);
+ var elements = [];
+
+ jQuery(":input", element).each(function() {
+ if (Jifty.Form.Element.getType(this) == "registration")
+ elements.push(Jifty.Form.Element.getAction(this));
+ });
- for (var i = 0; i < possible.length; i++) {
- if (Jifty.Form.Element.getType(possible[i]) == "registration")
- elements.push(Jifty.Form.Element.getAction(possible[i]));
- }
-
return elements;
},
clearPlaceholders: function(element) {
- var elements = Form.getElements(element);
+ var elements = Jifty.Form.getElements(element);
for(var i = 0; i < elements.length; i++) {
Jifty.Placeholder.clearPlaceholder(elements[i]);
}
}
});
-Object.extend(Form, {
- // Return an Array of Actions that are in this form
- getActions: function (element) {
- // DEPRECATED: use Jifty.Form.getActions instead
- return Jifty.Form.getActions(element);
- },
-
- clearPlaceholders: function(element) {
- // DEPRECATED: use Jifty.Form.clearPlaceholders instead
- return Jifty.Form.clearPlaceholders(element);
- }
-});
-
+var current_actions = {};
-var current_actions = $H();
+Jifty.Form.Element = {};
/* Fields */
-Jifty.Form.Element = Class.create();
-Object.extend(Jifty.Form.Element, {
+jQuery.extend(Jifty.Form.Element, {
// Get the moniker for this form element
// Takes an element or an element id
getMoniker: function (element) {
- element = $(element);
-
+ element = Jifty.$(element);
+
if (/^J:A(:F)+-[^-]+-.+$/.test(element.name)) {
var bits = element.name.match(/^J:A(?::F)+-[^-]+-(.+)$/);
return bits[1];
@@ -538,16 +558,16 @@
// Get the Action for this form element
// Takes an element or an element id
getAction: function (element) {
- element = $(element);
+ element = Jifty.$(element);
var moniker = Jifty.Form.Element.getMoniker(element);
- if (!current_actions.get(moniker))
- current_actions.set(moniker, new Action(moniker));
- return current_actions.get(moniker);
+ if (!current_actions[moniker])
+ current_actions[moniker] = new Action(moniker);
+ return current_actions[moniker];
},
// Returns the name of the field
getField: function (element) {
- element = $(element);
+ element = Jifty.$(element);
if (/^J:A(:F)+-[^-]+-.+$/.test(element.name)) {
var bits = element.name.match(/^J:A(?::F)+-([^-]+)-.+$/);
@@ -559,8 +579,7 @@
// The type of Jifty form element
getType: function (element) {
- element = $(element);
-
+ element = Jifty.$(element);
if (/^J:A-/.test(element.name)) {
return "registration";
} else if (/^J:A:F-/.test(element.name)) {
@@ -572,22 +591,30 @@
}
},
+ getValue: function(element) {
+ var $el = jQuery(Jifty.$(element));
+ if ( $el.is(":checkbox, :radio") ) {
+ return $el.is(":checked") ? $el.val() : null
+ }
+ return $el.val();
+ },
+
// Validates the action this form element is part of
validate: function (element) {
- if(!Element.hasClassName(element, 'validation_disabled')) {
- Jifty.Form.Element.getAction(element).validate();
- }
+ if ( !jQuery(element).is('.validation_disabled') ) {
+ Jifty.Form.Element.getAction(element).validate();
+ }
},
// Temporarily disable validation
- disableValidation: function(element) {
- Element.addClassName(element, 'validation_disabled');
- },
+ disableValidation: function(element) {
+ jQuery(element).addClass('validation_disabled');
+ },
- //Reenable validation
- enableValidation: function(element) {
- Element.removeClassName(element, 'validation_disabled');
- },
+ //Reenable validation
+ enableValidation: function(element) {
+ jQuery(element).removeClass('validation_disabled');
+ },
// Look up the form that this element is part of -- this is sometimes
@@ -595,7 +622,7 @@
// anymore, or the element may have been inserted into a new form.
// Hence, we may need to walk the DOM.
getForm: function (element) {
- element = $(element);
+ element = Jifty.$(element);
if (!element)
return null;
@@ -610,61 +637,62 @@
if (elt.nodeName == 'FORM') {
element.form = elt;
return elt;
- }
+ }
}
return null;
},
buttonArguments: function(element) {
- element = $(element);
+ element = Jifty.$(element);
if (!element)
- return $H();
+ return {}
if (((element.nodeName != 'INPUT') || (element.getAttribute("type") != "submit"))
&& ((element.nodeName != 'A') || (! element.getAttribute("name"))))
- return $H();
+ return {}
if (element.getAttribute("name").length == 0)
- return $H();
+ return {}
- var extras = $H();
+ var extras = {}
// Split other arguments out, if we're on a button
var pairs = element.getAttribute("name").split("|");
for (var i = 0; i < pairs.length; i++) {
var bits = pairs[i].split('=',2);
- extras.set(bits[0], bits[1]);
+ extras[ bits[0] ] = bits[1];
}
return extras;
},
buttonActions: function(element) {
- element = $(element);
- var actions = Jifty.Form.Element.buttonArguments(element).get('J:ACTIONS');
+ element = Jifty.$(element);
+ var actions = Jifty.Form.Element.buttonArguments(element)['J:ACTIONS'];
if(actions) {
return actions.split(",");
} else {
return new Array();
}
- },
+ },
buttonFormElements: function(element) {
- element = $(element);
+ element = Jifty.$(element);
- var extras = $A();
+ var extras = [];
if (!element)
return extras;
var args = Jifty.Form.Element.buttonArguments(element);
- var keys = args.keys();
- for (var i = 0; i < keys.length; i++) {
+
+ jQuery.each(args, function(k, v) {
var e = document.createElement("input");
e.setAttribute("type", "hidden");
- e.setAttribute("name", keys[i]);
- e.setAttribute("value", args.get(keys[i]));
+ e.setAttribute("name", k);
+ e.setAttribute("value", v);
e['virtualform'] = Jifty.Form.Element.getForm(element);
extras.push(e);
- }
+ });
+
return extras;
},
@@ -699,116 +727,29 @@
});
-Object.extend(Form.Element, {
- // Get the moniker for this form element
- // Takes an element or an element id
- getMoniker: function (element) {
- // DEPRECATED: use Jifty.Form.Element.getMoniker instead
- return Jifty.Form.Element.getMoniker(element);
- },
-
- // Get the Action for this form element
- // Takes an element or an element id
- getAction: function (element) {
- // DEPRECATED: use Jifty.Form.Element.getAction instead
- return Jifty.Form.Element.getAction(element);
- },
-
- // Returns the name of the field
- getField: function (element) {
- // DEPRECATED: use Jifty.Form.Element.getField instead
- return Jifty.Form.Element.getField(element);
- },
-
- // The type of Jifty form element
- getType: function (element) {
- // DEPRECATED: use Jifty.Form.Element.getType instead
- return Jifty.Form.Element.getType(element);
- },
-
- // Validates the action this form element is part of
- validate: function (element) {
- // DEPRECATED: use Jifty.Form.Element.validate instead
- return Jifty.Form.Element.validate(element);
- },
-
- // Temporarily disable validation
- disableValidation: function(element) {
- // DEPRECATED: use Jifty.Form.Element.disableValidation instead
- return Jifty.Form.Element.disableValidation(element);
- },
-
- //Reenable validation
- enableValidation: function(element) {
- // DEPRECATED: use Jifty.Form.Element.enableValidation instead
- return Jifty.Form.Element.enableValidation(element);
- },
-
-
- // Look up the form that this element is part of -- this is sometimes
- // more complicated than you'd think because the form may not exist
- // anymore, or the element may have been inserted into a new form.
- // Hence, we may need to walk the DOM.
- getForm: function (element) {
- // DEPRECATED: use Jifty.Form.Element.getForm instead
- return Jifty.Form.Element.getForm(element);
- },
-
- buttonArguments: function(element) {
- // DEPRECATED: use Jifty.Form.Element.buttonArguments instead
- return Jifty.Form.Element.buttonArguments(element);
- },
-
- buttonActions: function(element) {
- // DEPRECATED: use Jifty.Form.Element.buttonActions instead
- return Jifty.Form.Element.buttonActions(element);
- },
-
- buttonFormElements: function(element) {
- // DEPRECATED: use Jifty.Form.Element.buttonFormElements instead
- return Jifty.Form.Element.buttonFormElements(element);
- },
-
- /* Someday Jifty may have the concept of "default"
- buttons. For now, this clicks the first button that will
- submit the action associated with the form element.
- */
- clickDefaultButton: function(element) {
- // DEPRECATED: use Jifty.Form.Element.clickDefaultButton instead
- return Jifty.Form.Element.clickDefaultButton(element);
- },
-
- handleEnter: function(event) {
- // DEPRECATED: use Jifty.Form.Element.handleEnter instead
- return Jifty.Form.Element.handleEnter(event);
- }
-
-});
-
-JSAN.use("DOM.Events");
-
-
// Form elements should focus if the CSS says so.
-Behaviour.register( { ".focus": function(e) {
- /* Check to see if the element is already focused */
- if ( !Element.hasClassName(e, "focused") ) {
- e.focus();
- Element.addClassName(e, "focused");
+Behaviour.register({
+ ".focus": function(e) {
+ /* Check to see if the element is already focused */
+ if (!jQuery(e).hasClass("focused")) {
+ e.focus();
+ jQuery(e).addClass("focused")
+ }
}
- } });
+});
// Form elements should AJAX validate if the CSS says so
Behaviour.register({
'input.ajaxvalidation, textarea.ajaxvalidation, input.ajaxcanonicalization, textarea.ajaxcanonicalization': function(elt) {
- DOM.Events.addListener(elt, "blur", function () {
- Form.Element.validate(elt);
- });
+ jQuery(elt).bind('blur', function () {
+ Jifty.Form.Element.validate(elt);
+ });
},
'input.date': function(e) {
- if ( !Element.hasClassName( e, 'has_calendar_link' ) ) {
+ if ( !jQuery(e).hasClass('has_calendar_link') ) {
createCalendarLink(e);
- Element.addClassName( e, 'has_calendar_link' );
+ jQuery(e).addClass('has_calendar_link');
}
},
'input.button_as_link': function(e) {
@@ -819,100 +760,101 @@
when the autocomplete is active so we can use it on autocompleted
fields
*/
- if ( !Element.hasClassName( e, "jifty_enter_handler_attached" )
- && !Element.hasClassName( e, "ajaxautocompletes" ) )
+ if ( !jQuery(e).hasClass("jifty_enter_handler_attached" )
+ && !jQuery(e).hasClass("ajaxautocompletes" ) )
{
/* Do not use keydown as the event, it will not work as expected in Safari */
- DOM.Events.addListener( e, "keypress", Form.Element.handleEnter );
- Element.addClassName( e, "jifty_enter_handler_attached" );
+ jQuery(e).bind('keypress', Jifty.Form.Element.handleEnter).addClass("jifty_enter_handler_attached");
}
},
".messages": function(e) {
- if ( !Element.hasClassName( e, "jifty_enter_handler_attached" ) ) {
- e.innerHTML=
- '<a href="#" id="dismiss_'+e.id+'" title="Dismiss" onmousedown="this.onfocus=this.blur;" onmouseup="this.onfocus=window.clientInformation?null:window.undefined" onclick="Effect.Fade(this.parentNode); return false;">Dismiss</a>' + e.innerHTML;
+ if ( !jQuery(e).hasClass('jifty_enter_handler_attached') ) {
+ jQuery(e)
+ .prepend('<a href="#" id="dismiss_'+e.id+'" title="Dismiss" onmousedown="this.onfocus=this.blur;" onmouseup="this.onfocus=window.clientInformation?null:window.undefined" onclick="Jifty.Effect(this.parentNode, \'Fade\'); return false;">Dismiss</a>')
+ .addClass("jifty_enter_handler_attached" );
- Element.addClassName( e, "jifty_enter_handler_attached" );
}
},
'.form_field .error, .form_field .warning, .form_field .canonicalization_note': function(e) {
- if ( e.innerHTML == "" ) Element.hide(e);
+ if ( e.innerHTML == "" ) jQuery(e).hide();
}
});
/* Regions */
// Keep track of the fragments on the page
-var fragments = $H();
-var Region = Class.create();
+Jifty.fragments = {};
+
+// Todo: This "fragments" variable should be localized. External access should be restricted
+// to use "Jifty.fragments" instead.
+var fragments = Jifty.fragments;
+
+var Region = function() {
+ this.initialize.apply(this, arguments);
+ return this;
+};
+
Region.prototype = {
initialize: function(name, args, path, parent, in_form) {
this.name = name;
- this.args = $H(args);
+ this.args = jQuery.extend({}, args);
this.path = path;
- this.parent = parent ? fragments.get(parent) : null;
+ this.parent = parent ? fragments[parent] : null;
this.in_form = in_form;
- if (fragments.get(name)) {
+ if (fragments[name]) {
// If this fragment already existed, we want to wipe out
// whatever evil lies we might have said earlier; do this
// by clearing out everything that looks relevant
- var keys = current_args.keys();
- for (var i = 0; i < keys.length; i++) {
- var k = keys[i];
+ jQuery.each(current_args, function(k, v) {
var parsed = k.match(/^(.*?)\.(.*)/);
if ((parsed != null) && (parsed.length == 3) && (parsed[1] == this.name)) {
- current_args.unset(k);
- }
- }
+ current_args[k] = null;
+ }
+ });
}
- fragments.set(name, this);
+ fragments[name] = this;
},
setPath: function(supplied) {
+ var self = this;
+
// Merge in from current_args
- var keys = current_args.keys();
- for (var i = 0; i < keys.length; i++) {
- var k = keys[i];
- if (k == this.name) {
- this.path = current_args.get(k);
+ jQuery.each(current_args, function(k, v) {
+ if (k == self.name) {
+ self.path = v
}
- }
+ });
// Update with supplied
if (supplied != null) {
this.path = supplied;
}
-
+
// Propagate back to current args
- current_args.set(this.name, this.path);
+ current_args[ this.name ] = this.path;
// Return new value
return this.path;
},
setArgs: function(supplied) {
- supplied = $H(supplied);
+ var self = this;
// Merge in current args
- var keys = current_args.keys();
- for (var i = 0; i < keys.length; i++) {
- var k = keys[i];
+ jQuery.each(current_args, function(k, v) {
var parsed = k.match(/^(.*?)\.(.*)/);
if ((parsed != null) && (parsed.length == 3) && (parsed[1] == this.name)) {
- this.args.set(parsed[2], current_args.get(k));
+ self.args[ parsed[2] ] = v
}
- }
-
+ });
// Merge in any supplied parameters
- this.args = this.args.merge(supplied);
+ jQuery.extend(this.args, supplied);
// Fill supplied parameters into current args
- keys = supplied.keys();
- for (var i = 0; i < keys.length; i++) {
- var k = keys[i];
- current_args.set(this.name+'.'+k, supplied.get(k));
- }
-
+ jQuery.each(supplied, function(k, v) {
+ current_args[ self.name + '.' + k ] = v;
+ });
+
// Return new values
return this.args;
},
@@ -942,7 +884,7 @@
// Keep track of the state variables.
-var current_args = $H();
+var current_args = {};
// Prepare element for use in update()
// - 'fragment' is a hash, see fragments in update()
@@ -951,13 +893,9 @@
var name = f['region'];
// Find where we are going to go
- var element = $('region-' + f['region']);
+ var element = document.getElementById('region-' + f['region']);
if (f['element']) {
- var possible = cssQuery(f['element']);
- if (possible.length == 0)
- element = null;
- else
- element = possible[0];
+ element = jQuery(f['element'])[0];
}
f['element'] = element;
@@ -968,12 +906,12 @@
// If we're removing the element, do it now
// XXX TODO: Effects on this?
if (f['mode'] == "Delete") {
- fragments.set(name, null);
- Element.remove(element);
+ fragments[name] = null;
+ jQuery(element).remove();
return;
}
- f['is_new'] = (fragments.get(name) ? false : true);
+ f['is_new'] = (fragments[name] ? false : true);
// If it's new, we need to create it so we can dump it
if (f['is_new']) {
// Find what region we're inside
@@ -991,18 +929,18 @@
}
// Make the region (for now)
- new Region(name, f['args'], f['path'], f['parent'], f['parent'] ? fragments.get(f['parent']).in_form : null);
- } else if ((f['path'] != null) && f['toggle'] && (f['path'] == fragments.get(name).path)) {
+ new Region(name, f['args'], f['path'], f['parent'], f['parent'] ? fragments[f['parent']].in_form : null);
+ } else if ((f['path'] != null) && f['toggle'] && (f['path'] == fragments[name].path)) {
// If they set the 'toggle' flag, and clicking wouldn't change the path
- Element.update(element, '');
- fragments.get(name).path = null;
+ jQuery(element).empty();
+ fragments[name].path = null;
return;
} else if (f['path'] == null) {
// If they didn't know the path, fill it in now
- f['path'] == fragments.get(name).path;
+ f['path'] == fragments[name].path;
}
- return f;
+ return f;
}
var CACHE = {};
@@ -1027,12 +965,12 @@
textContent = fragment_bit.textContent;
} else if (fragment_bit.firstChild) {
textContent = fragment_bit.firstChild.nodeValue;
- }
+ }
try {
var cache_func = eval(textContent);
CACHE[f['path']] = { 'type': c_type, 'content': cache_func };
}
- catch(e) {
+ catch(e) {
alert(e);
alert(textContent);
}
@@ -1045,63 +983,76 @@
// - f: fragment spec
var apply_fragment_updates = function(fragment, f) {
// We found the right fragment
- var dom_fragment = fragments.get(f['region']);
- var new_dom_args = $H();
+ var dom_fragment = fragments[ f['region'] ];
+ var new_dom_args = {};
var element = f['element'];
- walk_node(fragment,
- { argument: function(fragment_bit) {
- // First, update the fragment's arguments
- // with what the server actually used --
- // this is needed in case there was
- // argument mapping going on
- var textContent = '';
- if (fragment_bit.textContent) {
- textContent = fragment_bit.textContent;
- } else if (fragment_bit.firstChild) {
- textContent = fragment_bit.firstChild.nodeValue;
- }
- new_dom_args.set(fragment_bit.getAttribute("name"), textContent);
- },
- content: function(fragment_bit) {
- var textContent = '';
- if (fragment_bit.textContent) {
- textContent = fragment_bit.textContent;
- } else if (fragment_bit.firstChild) {
- textContent = fragment_bit.firstChild.nodeValue;
- }
-
- // Once we find it, do the insertion
- if (f['mode'] && (f['mode'] != 'Replace')) {
- var insertion = eval('Insertion.'+f['mode']);
- new insertion(element, textContent.stripScripts());
- element = document.getElementById('region-' + f['region']);
- } else {
- Element.update(element, textContent.stripScripts());
+ walk_node(
+ fragment,
+ {
+ argument: function(fragment_bit) {
+ // First, update the fragment's arguments
+ // with what the server actually used --
+ // this is needed in case there was
+ // argument mapping going on
+ var textContent = '';
+ if (fragment_bit.textContent) {
+ textContent = fragment_bit.textContent;
+ } else if (fragment_bit.firstChild) {
+ textContent = fragment_bit.firstChild.nodeValue;
+ }
+ new_dom_args[ fragment_bit.getAttribute("name") ] = textContent;
+ },
+ content: function(fragment_bit) {
+ var textContent = '';
+ if (fragment_bit.textContent) {
+ textContent = fragment_bit.textContent;
+ } else if (fragment_bit.firstChild) {
+ textContent = fragment_bit.firstChild.nodeValue;
+ }
+
+ // Re-arrange all <script> tags to the end of textContent.
+ // This approach easily deal with the uncertain amount of
+ // time we need to wait before the region is ready for running
+ // some javascript.
+
+ var re = new RegExp('<script[^>]*>([\\S\\s]*?)<\/script>', 'img');
+ var scripts = (textContent.match(re) || []).join("");
+ var textContentWithoutScript = textContent.replace(re, '');
+ textContent = textContentWithoutScript + scripts;
+
+ // Once we find it, do the insertion
+ if (f['mode'] && (f['mode'] != 'Replace')) {
+ var method = ({
+ After: 'after',
+ Before: 'before',
+ Bottom: 'append',
+ Top: 'prepend'
+ })[ f['mode'] ];
+
+ (jQuery(element)[method])( textContent );
+ } else {
+ jQuery(element).html( textContent );
+ }
+ Behaviour.apply(element);
}
- // We need to give the browser some "settle" time before
- // we eval scripts in the body
- YAHOO.util.Event.onAvailable(element.id, function() {
- (function() { this.evalScripts() }).bind(textContent)();
- });
- Behaviour.apply(element);
}
- });
+ );
dom_fragment.setArgs(new_dom_args);
// Also, set us up the effect
if (f['effect']) {
- try {
- var effect = eval('Effect.'+f['effect']);
- var effect_args = f['effect_args'] || {};
- if (effect) {
- if (f['is_new'])
- Element.hide($('region-'+f['region']));
- (effect)($('region-'+f['region']), effect_args);
+ Jifty.Effect(
+ Jifty.$('region-'+f['region']),
+ f['effect'],
+ f['effect_args'],
+ {
+ before: function() {
+ if(f['is_new'])
+ jQuery(this).hide();
+ }
}
- } catch ( e ) {
- // Don't be sad if the effect doesn't exist
- }
+ );
}
}
@@ -1114,11 +1065,11 @@
// - 'args' is a hash of arguments to override
// - 'path' is the path of the fragment (if this is a new fragment)
// - 'element' is the CSS selector of the element to update, if 'region' isn't supplied
-// - 'mode' is one of 'Replace', or the name of a Prototype Insertion
+// - 'mode' is one of 'Replace', 'Top', 'Bottom', 'Before', or 'After'
// - 'effect' is the name of a Prototype Effect
Jifty.update = function () {
// loads
- if(!Ajax.getTransport()) return true;
+ if (!Jifty.hasAjaxTransport) return true;
// XXX: prevent default behavior in IE
if(window.event) {
window.event.returnValue = false;
@@ -1128,44 +1079,52 @@
var trigger = arguments[1];
// The YAML/JSON data structure that will be sent
- var request = $H();
+ var request = {};
// Keep track of disabled elements
- var disabled_elements = $A();
+ var disabled_elements = [];
// Set request base path
- request.set('path', '/__jifty/webservices/xml');
+ request.path = '/__jifty/webservices/xml';
// Grab extra arguments (from a button)
- var button_args = Form.Element.buttonFormElements(trigger);
+ var button_args = Jifty.Form.Element.buttonFormElements(trigger);
- var form = Form.Element.getForm(trigger);
+ var form = Jifty.Form.Element.getForm(trigger);
// If the action is null, take all actions
if (named_args['actions'] == null) {
named_args['actions'] = {};
// default to disable fields
if (form)
- Form.getActions(form).map(function(x){
+ Jifty.Form.getActions(form).map(function(x){
named_args['actions'][x.moniker] = 1;
});
}
var optional_fragments;
- if (form && form['J:CALL'])
+ if (form && form['J:CALL'])
optional_fragments = [ prepare_element_for_update({'mode':'Replace','args':{},'region':'__page','path': null}) ];
// Build actions structure
var has_request = 0;
- request.set('actions', $H());
+ request['actions'] = {};
+
for (var moniker in named_args['actions']) {
if (moniker == 'extend')
continue;
var disable = named_args['actions'][moniker];
var a = new Action(moniker, button_args);
- current_actions.set(moniker, a); // XXX: how do i make this bloody singleton?
+ current_actions[moniker] = a;
// Special case for Redirect, allow optional, implicit __page
// from the response to be used.
- if (a.actionClass == 'Jifty::Action::Redirect')
- optional_fragments = [ prepare_element_for_update({'mode':'Replace','args':{},'region':'__page','path': a.fields().last().value}) ];
- a.result = {}; a.result.field_error = {};
+ if (a.actionClass == 'Jifty::Action::Redirect') {
+ (function() {
+ var fields = a.fields();
+ var path = fields[fields.length - 1];
+ optional_fragments = [ prepare_element_for_update({'mode':'Replace','args':{},'region':'__page','path': path}) ];
+ })()
+ }
+ a.result = {};
+ a.result.field_error = {};
+
if (a.register) {
if (a.hasUpload())
return true;
@@ -1183,13 +1142,12 @@
fields[argname] = { value: override[argname] };
}
}
- request.get('actions').set(moniker, param);
+ request['actions'][moniker] = param;
++has_request;
}
-
}
- request.set('fragments', $H());
+ request.fragments = {};
var update_from_cache = new Array;
// Build fragments structure
@@ -1204,7 +1162,7 @@
var content_node = document.createElement('content');
var cached_result;
- Jifty.Web.current_region = fragments.get(f['region']);
+ Jifty.Web.current_region = fragments[ f['region'] ];
try { cached_result = apply_cached_for_action(cached['content'], []) }
catch (e) { alert(e) }
@@ -1224,9 +1182,9 @@
my_fragment.setAttribute('id', f['region']);
update_from_cache.push(function(){
var cached_result;
- Jifty.Web.current_region = fragments.get(f['region']);
+ Jifty.Web.current_region = fragments[ f['region'] ];
try {
- cached_result = apply_cached_for_action(cached['content'], Form.getActions(form));
+ cached_result = apply_cached_for_action(cached['content'], Jifty.Form.getActions(form));
}
catch (e) { alert(e); throw e }
content_node.textContent = cached_result;
@@ -1235,7 +1193,7 @@
continue;
}
else if (cached && cached['type'] == 'crudview') {
- try { // XXX: get model class etc as metadata in cache
+ try { // XXX: get model class etc as metadata in cache
// XXX: kill dup code
var Todo = new AsynapseRecord('todo');
var record = Todo.find(f['args']['id']);
@@ -1251,17 +1209,17 @@
// Update with all new values
var name = f['region'];
- var fragment_request = fragments.get(name).data_structure(f['path'], f['args']);
+ var fragment_request = fragments[ name ].data_structure(f['path'], f['args']);
if (f['is_new'])
// Ask for the wrapper if we are making a new region
fragment_request['wrapper'] = 1;
- if (fragments.get(name).in_form)
+ if (fragments[name].in_form)
fragment_request['in_form'] = 1;
// Push it onto the request stack
- request.get('fragments').set(name, fragment_request);
+ request.fragments[name] = fragment_request;
++has_request;
}
@@ -1274,9 +1232,9 @@
show_wait_message();
// And when we get the result back..
- var onSuccess = function(transport, object) {
+ var onSuccess = function(responseXML, object) {
// Grab the XML response
- var response = transport.responseXML.documentElement;
+ var response = responseXML.documentElement;
// Get action results
walk_node(response,
@@ -1289,7 +1247,7 @@
var text = error.textContent
? error.textContent
: (error.firstChild ? error.firstChild.nodeValue : '');
- var action = current_actions.get(moniker);
+ var action = current_actions[moniker];
action.result.field_error[field.getAttribute("name")] = text;
}
}});
@@ -1308,7 +1266,14 @@
response_fragment = response_fragment.nextSibling) {
var exp_id = response_fragment.getAttribute("id");
- var f = expected_fragments.find(function(f) { return exp_id == f['region'] });
+
+ var f;
+ jQuery.each(expected_fragments, function() {
+ if (exp_id == this['region']) {
+ f = this;
+ return false;
+ }
+ });
if (!f)
continue;
@@ -1318,7 +1283,10 @@
extract_cacheable(response_fragment, f);
}
- update_from_cache.each(function(x) { x() });
+ jQuery.each(update_from_cache, function() {
+ this();
+ });
+ // update_from_cache.each(function(x) { x() });
walk_node(response,
{ result: function(result) {
@@ -1331,8 +1299,9 @@
redirect: function(redirect) {
document.location = redirect.firstChild.firstChild.nodeValue;
}});
- current_actions = $H();
+ current_actions = {}
};
+
var onFailure = function(transport, object) {
hide_wait_message_now();
@@ -1346,48 +1315,43 @@
};
// Build variable structure
- request.set('variables', $H());
- var keys = current_args.keys();
- for (var i = 0; i < keys.length; i++) {
- var k = keys[i];
- request.get('variables').set('region-'+k, current_args.get(k));
- }
+ request.variables = {};
+ jQuery.each(current_args, function(k, v) {
+ request.variables['region-'+k] = v;
+ });
// Build continuation structure
- request.set('continuation', named_args['continuation']);
+ request.continuation = named_args['continuation'];
// Push any state variables which we set into the forms
for (var i = 0; i < document.forms.length; i++) {
var form = document.forms[i];
- var keys = current_args.keys();
- for (var j = 0; j < keys.length; j++) {
- var n = keys[j];
- if (form['J:V-region-'+n]) {
- form['J:V-region-'+n].value = current_args.get(n);
+
+ jQuery.each(current_args, function(k, v) {
+ if (form['J:V-region-'+k]) {
+ form['J:V-region-'+k].value = v;
} else {
var hidden = document.createElement('input');
hidden.setAttribute('type', 'hidden');
- hidden.setAttribute('name', 'J:V-region-'+n);
- hidden.setAttribute('id', 'J:V-region-'+n);
- hidden.setAttribute('value', current_args.get(n));
+ hidden.setAttribute('name', 'J:V-region-'+k);
+ hidden.setAttribute('id', 'J:V-region-'+k);
+ hidden.setAttribute('value', v);
form.appendChild(hidden);
}
- }
+ })
}
- // Set up our options
- var options = { postBody: request.toJSON(), //JSON.stringify(request.toObject),
- onSuccess: onSuccess,
- onException: onFailure,
- onFailure: onFailure,
- onComplete: function(){hide_wait_message()},
- requestHeaders: ['Content-Type', 'text/x-json']
- };
-
// Go!
- new Ajax.Request(document.URL,
- options
- );
+ jQuery.ajax({
+ url: document.URL,
+ type: 'post',
+ dataType: 'xml',
+ data: JSON.stringify(request),
+ contentType: 'text/x-json',
+ error: onFailure,
+ complete: function(){hide_wait_message()},
+ success: onSuccess
+ });
return false;
}
@@ -1404,23 +1368,20 @@
function show_wait_message (){
- if ($('jifty-wait-message'))
- new Effect.Appear('jifty-wait-message', {duration: 0.5});
+ jQuery('#jifty-wait-message').fadeIn(500);
}
function hide_wait_message (){
- if ($('jifty-wait-message'))
- new Effect.Fade('jifty-wait-message', {duration: 0.2});
+ jQuery('#jifty-wait-message').fadeOut(200);
}
function hide_wait_message_now() {
- if ($('jifty-wait-message'))
- Element.hide('jifty-wait-message');
+ jQuery('#jifty-wait-message').hide();
}
function show_action_result() {
- var popup = $('jifty-result-popup');
- if(!popup) return;
+ var $popup = jQuery('#jifty-result-popup');
+ if($popup.size() == 0) return;
var moniker = arguments[0];
var result = arguments[1];
@@ -1448,7 +1409,7 @@
node.setAttribute('id', node_id);
node.className = "popup_notification result-" + status;
node.innerHTML = text;
-
+
var wrap1 = document.createElement("div");
wrap1.className = "dropshadow_wrap1";
var wrap2 = document.createElement("div");
@@ -1459,132 +1420,111 @@
wrap1.appendChild(wrap2);
wrap2.appendChild(wrap3);
wrap3.appendChild(node);
-
- if(popup.hasChildNodes()) {
- popup.insertBefore(wrap1, popup.firstChild);
- } else {
- popup.appendChild(wrap1);
- }
-
+
+ $popup.prepend( wrap1 );
+
setTimeout(function () {
- new Effect.Fade(wrap1, {duration: 3.0});
+ jQuery(wrap1).fadeOut(3000);
}, 3500);
}
-Jifty.Autocompleter = Class.create();
-Object.extend(Object.extend(Jifty.Autocompleter.prototype, Ajax.Autocompleter.prototype), {
- initialize: function(field, div) {
- this.field = $(field);
- this.action = Form.Element.getAction(this.field);
- this.url = '/__jifty/autocomplete.xml';
-
- Event.observe(this.field, "focus", this.onFocus.bindAsEventListener(this));
- this.baseInitialize(this.field, $(div), {
- minChars: "0",
- beforeShow: this.beforeShow,
- beforeHide: this.beforeHide,
- frequency: 0.1,
- onShow: this.onShow,
- onHide: this.onHide,
- afterUpdateElement: this.afterUpdate
- });
+Jifty.Autocompleter = function() {
+ this.initialize.apply(this, arguments);
+ return this;
+};
- if ((document.all)&&(navigator.appVersion.indexOf("MSIE")!=-1)) {
- Event.observe(this.element, "keydown", this.onKeyPress.bindAsEventListener(this));
- }
- },
+jQuery.extend(Jifty.Autocompleter.prototype, {
+ initialize: function(field, div) {
+ this.field = Jifty.$(field);
+ this.action = Jifty.Form.Element.getAction(this.field);
+ this.url = '/__jifty/autocomplete.xml';
+
+ var self = this;
+ jQuery(this.field).bind("focus", function(event) {
+ self.changed = true;
+ self.hasFocus = true;
+ Jifty.current_autocompleter_object = self;
+ });
+
+ jQuery(this.field).Autocomplete({
+ source: this.url,
+ minchars: -1,
+ delay: 100,
+ helperClass: 'autocomplete',
+ selectClass: 'selected'
+ });
+ },
- onShow: function(element, update) {
- if(!update.style.position || update.style.position=='absolute') {
- update.style.position = 'absolute';
- Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
- }
- Element.show( update );
- },
+ beforeShow: function() {
+ /* Prevents the race for canonicalization and updating via autocomplete */
+ if ( this.field.onblur ) {
+ this.element._onblur = this.element.onblur;
+ this.element.onblur = null;
+ }
+ },
- onHide: function(element, update) {
- Element.hide( update );
- },
+ beforeHide: function() {
+ /* Restore onblur and config option */
+ if ( this.element._onblur ) {
+ this.element.onblur = this.element._onblur;
+ this.element._onblur = null;
+ }
+ },
- beforeShow: function(obj) {
- /* Prevents the race for canonicalization and updating
- via autocomplete */
- if ( obj.element.onblur ) {
- obj.element._onblur = obj.element.onblur;
- obj.element.onblur = null;
- }
- },
+ afterUpdate: function(field, selection) {
+ Jifty.Form.Element.validate(field);
+ },
- beforeHide: function(obj) {
- /* Restore onblur and config option */
- if ( obj.element._onblur ) {
- obj.element.onblur = obj.element._onblur;
- obj.element._onblur = null;
+ buildRequest: function() {
+ var request = { path: this.url, actions: {} };
+ var a = {};
+ a['moniker'] = 'autocomplete';
+ a['class'] = 'Jifty::Action::Autocomplete';
+ a['fields'] = {};
+ a['fields']['moniker'] = this.action.moniker;
+ a['fields']['argument'] = Jifty.Form.Element.getField(this.field);
+ request['actions']['autocomplete'] = a;
+ request['actions'][this.action.moniker] = this.action.data_structure();
+ request['actions'][this.action.moniker]['active'] = 0;
+ return request;
}
- },
-
- onFocus: function(event) {
- this.changed = true;
- this.hasFocus = true;
-
- if (this.observer)
- clearTimeout(this.observer);
-
- this.onObserverEvent();
- },
-
- afterUpdate: function(field, selection) {
- Form.Element.validate(field);
- },
-
- getUpdatedChoices: function() {
- var request = { path: this.url, actions: {} };
-
- var a = {}; //$H();
- a['moniker'] = 'autocomplete';
- a['class'] = 'Jifty::Action::Autocomplete';
- a['fields'] = {}; //$H();
- a['fields']['moniker'] = this.action.moniker;
- a['fields']['argument'] = Form.Element.getField(this.field);
- request['actions']['autocomplete'] = a;
- request['actions'][this.action.moniker] = this.action.data_structure();
- request['actions'][this.action.moniker]['active'] = 0;
-
- var options = { postBody: JSON.stringify(request),
- onComplete: this.onComplete.bind(this),
- requestHeaders: ['Content-Type', 'text/x-json']
- };
-
- new Ajax.Request(this.url,
- options
- );
- }
+});
-});
+Jifty.Placeholder = function() {
+ this.initialize.apply(this, arguments);
+ return this;
+};
-Jifty.Placeholder = Class.create();
-Object.extend(Jifty.Placeholder.prototype, {
+jQuery.extend(Jifty.Placeholder.prototype, {
element: null,
text: null,
initialize: function(element, text) {
- this.element = $(element);
+ this.element = Jifty.$(element);
this.text = text;
this.element.placeholderText = this.text;
- Event.observe(element, 'focus', this.onFocus.bind(this));
- Event.observe(element, 'blur', this.onBlur.bind(this));
+ var self = this;
+
+ jQuery( this.element )
+ .bind("focus", function(event) {
+ self.onFocus();
+ })
+ .bind("blur", function(event) {
+ self.onBlur();
+ });
+
this.onBlur();
- var form = Form.Element.getForm(element);
-
+ var form = Jifty.Form.Element.getForm(element);
+
if(form && !form.hasPlaceholders) {
form.hasPlaceholders = true;
- // We can't attach this event via DOM event methods because
+ // We can't attach this event via DOM event methods because
// we need to call form.submit() sometimes and still have a good
// way to call this event handler
- form.onsubmit = function () { Form.clearPlaceholders(form); };
+ form.onsubmit = function () { Jifty.Form.clearPlaceholders(form); };
}
},
@@ -1594,8 +1534,7 @@
as the placeholder text. This does have the effect of making it
impossible to submit a field with the same value as the placeholder. */
if (this.element.value == '' || this.element.value == this.text) {
- Element.addClassName(this.element, 'placeholder');
- this.element.value = this.text;
+ jQuery(this.element).addClass('placeholder').val(this.text);
}
},
@@ -1605,12 +1544,11 @@
});
-Object.extend(Jifty.Placeholder, {
-
+jQuery.extend(Jifty.Placeholder, {
hasPlaceholder: function(elt) {
- return Element.hasClassName(elt, 'placeholder');
+ return jQuery(elt).hasClass('placeholder');
},
-
+
clearPlaceholder: function(elt) {
// If the element's text isn't the same as its placeholder text, then the
// browser screwed up and didn't clear our placeholder. Opera on Mac with
@@ -1620,8 +1558,7 @@
elt.placeholderText = elt.placeholderText.replace(/\r/g, '');
if(Jifty.Placeholder.hasPlaceholder(elt) && elt.value == elt.placeholderText) {
- elt.value = '';
- Element.removeClassName(elt, 'placeholder');
+ jQuery(elt).removeClass('placeholder').val('');
}
}
@@ -1644,48 +1581,170 @@
}
}
-function _sp_submit_form(elt, event, submit_to) {
- var form = Form.Element.getForm(elt);
- var elements = Form.getElements(form);
-
- // Three things need to get merged -- hidden defaults, defaults
- // from buttons, and form values. Hence, we build up three lists
- // and then merge them.
- var hiddens = $H();
- var buttons = $H();
- var inputs = $H();
- for (var i = 0; i < elements.length; i++) {
- var e = elements[i];
- var parsed = e.getAttribute("name").match(/^J:V-region-__page\.(.*)/);
- var extras = Form.Element.buttonArguments(e);
- if (extras.keys().length > 1) {
- // Button with values
- for (var j = 0; j < extras.keys().length; j++) {
- if ( extras.keys()[j] == 'extend' ) continue;
- // Might also have J:V mappings on it
- parsed = extras.keys()[j].match(/^J:V-region-__page\.(.*)/);
- if ((parsed != null) && (parsed.length == 2)) {
- buttons.set(parsed[1], extras.values()[j]);
- } else if (extras.keys()[j].length > 0) {
- inputs.set(extras.keys()[j], extras.values()[j]);
- }
-
+/*
+ * Jifty.Effect Usage:
+ *
+ * Jifty.Effect(element, "Fade", { duration: 2.0 });
+ *
+ * When called, instantly pefrom a js effect on give element.
+ *
+ * The last arg "option" is a hash. Currently it's only used for
+ * specificing callbacks. There are two possible callbacks, before
+ * and after. You may specify them like this:
+ *
+ * Jifty.Effect(element, "Fade", { duration: 2.0 }, {
+ * before: function() { ... },
+ * after: function() { ... }
+ * });
+ *
+ * The "before" callback is called right before the effect starts.
+ * The "after" callback is called right after it's started, but not
+ * necessarily ended.
+ *
+ * This function is written to make it possible that a Jifty plugin
+ * can override default effects with other fancy javascript
+ * libraries. By default, it delegates all the real work to
+ * jQuery's built-in effect functions.
+ *
+ */
+
+Jifty.Effect = function(el, name, args, options) {
+ // Scriptaculous. TODO: This should be overrided by Jifty::Prototype plugins instead of coded in here.
+ if (typeof Effect != 'undefined') {
+ try {
+ var effect = eval('Effect.' + name);
+ var effect_args = args || {};
+ if (effect) {
+ (effect)(el, effect_args);
}
- } else if ((parsed != null) && (parsed.length == 2)) {
- // Hidden default
- hiddens.set(parsed[1], $F(e));
- } else if (e.name.length > 0) {
- // Straight up values
- inputs.set(e.name, $F(e));
- }
+ return effect;
+ } catch ( e ) {}
}
- var args = hiddens.merge(buttons.merge(inputs));
+ if (options == null) options = {};
+ // jQuery built-ins
+ var effect =
+ name == 'Fade' ? 'fadeOut' :
+ name == 'Appear' ? 'fadeIn' :
+ name == 'SlideDown' ? 'slideDown' :
+ name == 'SlideUp' ? 'slideUp' :
+ name;
- /* we want to feed a common object instead of a Hash to Jifty.update */
- var args_object = {};
- args.each( function( pair ) { args_object[pair.key] = pair.value; } );
+ if (jQuery.isFunction( jQuery(el)[ effect ] ) ) {
+ if ( jQuery.isFunction(options["before"]) )
+ options["before"].call( el );
- if(event.ctrlKey||event.metaKey||event.altKey||event.shiftKey) return true;
- return Jifty.update( {'continuation':{},'actions':null,'fragments':[{'mode':'Replace','args':args_object,'region':'__page','path': submit_to}]}, elt );
-}
+ ( jQuery(el)[ effect ] )(args);
+
+ if ( jQuery.isFunction(options["after"]) )
+ options["after"].call( el );
+ }
+};
+
+/*
+ * Provide an alias in Global namespace for backward compatibility.
+ * Also Jifty.Form will still work even if prototype.js is loaded
+ * after jifty.js.
+ */
+
+Form = {};
+
+jQuery.extend(Form, {
+ // Return an Array of Actions that are in this form
+ getActions: function (element) {
+ // DEPRECATED: use Jifty.Form.getActions instead
+ return Jifty.Form.getActions(element);
+ },
+ clearPlaceholders: function(element) {
+ // DEPRECATED: use Jifty.Form.clearPlaceholders instead
+ return Jifty.Form.clearPlaceholders(element);
+ },
+
+ Element: {}
+});
+
+jQuery.extend(Form.Element, {
+ // Get the moniker for this form element
+ // Takes an element or an element id
+ getMoniker: function (element) {
+ // DEPRECATED: use Jifty.Form.Element.getMoniker instead
+ return Jifty.Form.Element.getMoniker(element);
+ },
+
+ // Get the Action for this form element
+ // Takes an element or an element id
+ getAction: function (element) {
+ // DEPRECATED: use Jifty.Form.Element.getAction instead
+ return Jifty.Form.Element.getAction(element);
+ },
+
+ // Returns the name of the field
+ getField: function (element) {
+ // DEPRECATED: use Jifty.Form.Element.getField instead
+ return Jifty.Form.Element.getField(element);
+ },
+
+ // The type of Jifty form element
+ getType: function (element) {
+ // DEPRECATED: use Jifty.Form.Element.getType instead
+ return Jifty.Form.Element.getType(element);
+ },
+
+ // Validates the action this form element is part of
+ validate: function (element) {
+ // DEPRECATED: use Jifty.Form.Element.validate instead
+ return Jifty.Form.Element.validate(element);
+ },
+
+ // Temporarily disable validation
+ disableValidation: function(element) {
+ // DEPRECATED: use Jifty.Form.Element.disableValidation instead
+ return Jifty.Form.Element.disableValidation(element);
+ },
+
+ //Reenable validation
+ enableValidation: function(element) {
+ // DEPRECATED: use Jifty.Form.Element.enableValidation instead
+ return Jifty.Form.Element.enableValidation(element);
+ },
+
+
+ // Look up the form that this element is part of -- this is sometimes
+ // more complicated than you'd think because the form may not exist
+ // anymore, or the element may have been inserted into a new form.
+ // Hence, we may need to walk the DOM.
+ getForm: function (element) {
+ // DEPRECATED: use Jifty.Form.Element.getForm instead
+ return Jifty.Form.Element.getForm(element);
+ },
+
+ buttonArguments: function(element) {
+ // DEPRECATED: use Jifty.Form.Element.buttonArguments instead
+ return Jifty.Form.Element.buttonArguments(element);
+ },
+
+ buttonActions: function(element) {
+ // DEPRECATED: use Jifty.Form.Element.buttonActions instead
+ return Jifty.Form.Element.buttonActions(element);
+ },
+
+ buttonFormElements: function(element) {
+ // DEPRECATED: use Jifty.Form.Element.buttonFormElements instead
+ return Jifty.Form.Element.buttonFormElements(element);
+ },
+
+ /* Someday Jifty may have the concept of "default"
+ buttons. For now, this clicks the first button that will
+ submit the action associated with the form element.
+ */
+ clickDefaultButton: function(element) {
+ // DEPRECATED: use Jifty.Form.Element.clickDefaultButton instead
+ return Jifty.Form.Element.clickDefaultButton(element);
+ },
+
+ handleEnter: function(event) {
+ // DEPRECATED: use Jifty.Form.Element.handleEnter instead
+ return Jifty.Form.Element.handleEnter(event);
+ }
+
+});
Added: jifty/trunk/share/web/static/js/jifty_interface.js
==============================================================================
--- (empty file)
+++ jifty/trunk/share/web/static/js/jifty_interface.js Wed Apr 9 00:12:34 2008
@@ -0,0 +1,60 @@
+/**
+ * This file overrides functions defined in Interface Elements for jQuery (http://interface.eyecon.ro)
+ * so they can work with Jifty. It must be loaded after all Interface scripts, and better before
+ * jquery_noconflict.js
+ */
+
+jQuery.iAuto.update = function() {
+ var subject = jQuery.iAuto.subject;
+ var subjectValue = jQuery.iAuto.getFieldValues(subject);
+ //var selectionStart = jQuery.iAuto.getSelectionStart(subject);
+ if (subject && subjectValue.item != jQuery.iAuto.lastValue && subjectValue.item.length >= subject.autoCFG.minchars) {
+ jQuery.iAuto.lastValue = subjectValue.item;
+ jQuery.iAuto.currentValue = subjectValue.item;
+
+ var request = Jifty.current_autocompleter_object.buildRequest();
+ jQuery.ajax(
+ {
+ type: 'post',
+ data: JSON.stringify(request),
+ success: function(xml)
+ {
+ subject.autoCFG.lastSuggestion = jQuery('li',xml);
+ size = subject.autoCFG.lastSuggestion.size();
+ if (size > 0) {
+ var toWrite = '';
+
+ subject.autoCFG.lastSuggestion.each(
+ function(nr) {
+ var v = jQuery(this).text();
+ toWrite += '<li rel="' + v + '" dir="' + nr + '" style="cursor: default;">' + v + '</li>';
+ }
+ );
+
+ if (subject.autoCFG.autofill) {
+ var valueToAdd = jQuery('value', subject.autoCFG.lastSuggestion.get(0)).text();
+ subject.value = subjectValue.pre + valueToAdd + subject.autoCFG.multipleSeparator + subjectValue.post;
+ jQuery.iAuto.selection(
+ subject,
+ subjectValue.item.length != valueToAdd.length ? (subjectValue.pre.length + subjectValue.item.length) : valueToAdd.length,
+ subjectValue.item.length != valueToAdd.length ? (subjectValue.pre.length + valueToAdd.length) : valueToAdd.length
+ );
+ }
+
+ if (size > 0) {
+ jQuery.iAuto.writeItems(subject, toWrite);
+ } else {
+ jQuery.iAuto.clear();
+ }
+ } else {
+ jQuery.iAuto.clear();
+ }
+ },
+ beforeSend: function(xhr) {
+ xhr.setRequestHeader('Content-Type', 'text/x-json');
+ },
+ url : subject.autoCFG.source
+ }
+ );
+ }
+};
Modified: jifty/trunk/share/web/static/js/jifty_subs.js
==============================================================================
--- jifty/trunk/share/web/static/js/jifty_subs.js (original)
+++ jifty/trunk/share/web/static/js/jifty_subs.js Wed Apr 9 00:12:34 2008
@@ -1,6 +1,6 @@
if (typeof Jifty == "undefined") Jifty = { };
-{
+(function(){
/* onPushHandler is called for each new pushed element.
(currently, this is always a <pushfrag>). This routine takes
@@ -18,12 +18,19 @@
var mode = t.getAttribute('mode');
var rid = t.firstChild.getAttribute('id');
var f = { region: rid, path: '', mode: mode };
+
+ // If SinglePlugin is enabled, region name will be prefixed
+ // "__page-" by the time that region was rendered. Therefore
+ // it should also be prefixed the same here.
+ if(Jifty.fragments["__page"] != null) {
+ f['region'] = "__page-" + f['region']
+ }
+
f = prepare_element_for_update(f);
+ if (f == null) return;
apply_fragment_updates(t.firstChild, f);
};
-
-
/* This function constructs a new Jifty.Subs object and sets
up a callback with HTTP.Push to run our onPushHandler each time
@@ -38,34 +45,30 @@
var window_id = args.window_id; // XXX: not yet
var uri = args.uri;
if (!uri)
- uri = "/=/subs?";
- //var push = new HTTP.Push({ "uri": uri, interval : 100, "onPush" : onPushHandler});
+ uri = "/=/subs?forever=0";
this.start = function() {
//push.start();
+ var self = this;
- new Ajax.PeriodicalUpdater({},'/=/subs?forever=0',
- {
- 'decay': 1, 'frequency': 0,
- 'asynchronous':true,
- 'evalScripts':false,
- 'method': 'get',
- 'onSuccess': onSuccess,
- 'onFailure': onFailure
- });
- };
+ jQuery.ajax({
+ url: uri,
+ type: "get",
+ success: function(responseText) {
+ var container = document.createElement('div');
+ container.innerHTML = responseText;
+ jQuery("pushfrag", container).each(function() {
+ onPushHandler(this);
+ });
+
+ setTimeout(function() {
+ self.start();
+ }, 1000)
+ },
+ error: function() {
+ }
+ });
+ }
}
-
- function onSuccess(req, json) {
- var container = document.createElement('div');
- container.innerHTML = req.responseText;
- var frags = container.getElementsByTagName('pushfrag');
- for(var i = 0 ; i < frags.length; i++) {
- onPushHandler(frags[i]);
- }
- }
- function onFailure(req) { }
-
-}
-
+})();
Modified: jifty/trunk/share/web/static/js/jifty_utils.js
==============================================================================
--- jifty/trunk/share/web/static/js/jifty_utils.js (original)
+++ jifty/trunk/share/web/static/js/jifty_utils.js Wed Apr 9 00:12:34 2008
@@ -137,6 +137,10 @@
if ( diff > 0 )
Jifty.SmoothScroll.scrollTo( scrollTop + diff );
+ },
+
+ stripScripts: function(str) {
+ return str.replace(/<script(.|\s)*?\/script>/g, "");
}
};
Modified: jifty/trunk/share/web/static/js/key_bindings.js
==============================================================================
--- jifty/trunk/share/web/static/js/key_bindings.js (original)
+++ jifty/trunk/share/web/static/js/key_bindings.js Wed Apr 9 00:12:34 2008
@@ -74,7 +74,7 @@
writeLegend: function(e) {
if ( !document.createElement
|| !document.createTextNode
- || Element.hasClassName(e, 'keybindings-written') )
+ || jQuery(e).is('.keybindings-written') )
return;
@@ -109,7 +109,7 @@
e.appendChild( label );
e.appendChild( dl );
- Element.addClassName(e, 'keybindings-written');
+ jQuery(e).addClass('keybindings-written');
/* since we wrote the legend, now obey it */
Jifty.KeyBindings.activate();
Modified: jifty/trunk/share/web/templates/__jifty/halo
==============================================================================
--- jifty/trunk/share/web/templates/__jifty/halo (original)
+++ jifty/trunk/share/web/templates/__jifty/halo Wed Apr 9 00:12:34 2008
@@ -99,7 +99,7 @@
<li><b><% $e->[0] %></b>:
% if ($e->[1]) {
% my $expanded = Jifty->web->serial;
-<a href="#" onclick="Element.toggle('<% $expanded %>'); return false"><% $e->[1] %></a>
+<a href="#" onclick="jQuery(Jifty.$('<% $expanded %>')).toggle(); return false"><% $e->[1] %></a>
<div id="<% $expanded %>" style="display: none; position: absolute; left: 200px; border: 1px solid black; background: #ccc; padding: 1em; padding-top: 0; width: 300px; height: 500px; overflow: auto"><pre><% Jifty::YAML::Dump($e->[2]) %></pre></div>
% } elsif (defined $e->[2]) {
<% $e->[2] %>
Modified: jifty/trunk/t/TestApp-JiftyJS/etc/config.yml
==============================================================================
--- jifty/trunk/t/TestApp-JiftyJS/etc/config.yml (original)
+++ jifty/trunk/t/TestApp-JiftyJS/etc/config.yml Wed Apr 9 00:12:34 2008
@@ -4,7 +4,7 @@
ApplicationClass: TestApp::JiftyJS
ApplicationName: TestApp::JiftyJS
ApplicationUUID: F43CA57E-A4BE-11DC-A07C-465A83BE23AB
- ConfigFileVersion: 2
+ ConfigFileVersion: 4
Database:
CheckSchema: 1
Database: testapp_jiftyjs
Modified: jifty/trunk/t/TestApp-JiftyJS/lib/TestApp/JiftyJS/Action/Play.pm
==============================================================================
--- jifty/trunk/t/TestApp-JiftyJS/lib/TestApp/JiftyJS/Action/Play.pm (original)
+++ jifty/trunk/t/TestApp-JiftyJS/lib/TestApp/JiftyJS/Action/Play.pm Wed Apr 9 00:12:34 2008
@@ -19,6 +19,10 @@
ajax validates,
valid are qw(happy angry normal);
+ param flavor =>
+ autocompleter is \&autocomplete_flavor,
+ type is 'text';
+
param tags =>
type is 'text',
ajax canonicalizes;
@@ -54,5 +58,13 @@
return $v;
}
+sub autocomplete_flavor {
+ my ($self, $value) = @_;
+ return grep {
+ $_ =~ /$value/i;
+ } sort qw( berry vanilla caramel caracara honey miso blueberry strawberry );
+
+}
+
1;
Added: jifty/trunk/t/TestApp-JiftyJS/lib/TestApp/JiftyJS/Action/Play2.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-JiftyJS/lib/TestApp/JiftyJS/Action/Play2.pm Wed Apr 9 00:12:34 2008
@@ -0,0 +1,40 @@
+use strict;
+use warnings;
+
+=head1 NAME
+
+TestApp::JiftyJS::Action::Play2
+
+=cut
+
+package TestApp::JiftyJS::Action::Play2;
+use base qw/TestApp::JiftyJS::Action Jifty::Action/;
+
+use Jifty::Param::Schema;
+use Jifty::Action schema {
+ param bogus => type is 'text';
+};
+
+=head2 take_action
+
+=cut
+
+sub take_action {
+ my $self = shift;
+ # Custom action code
+ $self->report_success if not $self->result->failure;
+ return 1;
+}
+
+=head2 report_success
+
+=cut
+
+sub report_success {
+ my $self = shift;
+ # Your success message here
+ $self->result->message('Success');
+}
+
+1;
+
Added: jifty/trunk/t/TestApp-JiftyJS/lib/TestApp/JiftyJS/Dispatcher.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-JiftyJS/lib/TestApp/JiftyJS/Dispatcher.pm Wed Apr 9 00:12:34 2008
@@ -0,0 +1,14 @@
+
+package TestApp::JiftyJS::Dispatcher;
+use Jifty::Dispatcher -base;
+use strict;
+use warnings;
+
+before '*' => run {
+ Jifty->api->allow("AddTwoNumbers");
+ Jifty->api->allow('Play');
+ Jifty->api->allow('Play2');
+};
+
+1;
+
Added: jifty/trunk/t/TestApp-JiftyJS/lib/TestApp/JiftyJS/Model/Offer.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-JiftyJS/lib/TestApp/JiftyJS/Model/Offer.pm Wed Apr 9 00:12:34 2008
@@ -0,0 +1,22 @@
+use strict;
+use warnings;
+
+package TestApp::JiftyJS::Model::Offer;
+use Jifty::DBI::Schema;
+
+use TestApp::JiftyJS::Record schema {
+ column name =>
+ type is "varchar(255)";
+
+ column is_job =>
+ type is "boolean",
+ label is _("Job Offer ?");
+
+};
+
+sub current_user_can {
+ 1;
+}
+
+1;
+
Modified: jifty/trunk/t/TestApp-JiftyJS/lib/TestApp/JiftyJS/View.pm
==============================================================================
--- jifty/trunk/t/TestApp-JiftyJS/lib/TestApp/JiftyJS/View.pm (original)
+++ jifty/trunk/t/TestApp-JiftyJS/lib/TestApp/JiftyJS/View.pm Wed Apr 9 00:12:34 2008
@@ -23,20 +23,16 @@
);
hyperlink(
- id => 'region3',
- label => "John",
+ id => 'region3', label => "John",
onclick => {
region => 'content',
replace_with => 'hello_world',
- args => {
- name => "John"
- }
+ args => { name => "John" }
}
);
hyperlink(
- id => 'region4',
- label => "Smith",
+ id => 'region4', label => "Smith",
onclick => {
region => 'content',
replace_with => 'hello_world',
@@ -46,11 +42,38 @@
}
);
+ hyperlink(
+ id => 'append-region', label => "Append To Region",
+ onclick => {
+ region => "content",
+ append => "hello_world"
+ }
+ );
+
+ hyperlink(
+ id => 'prepend-region', label => "Prepend To Region",
+ onclick => {
+ region => "content",
+ prepend => "hello_world"
+ }
+ );
+
+ hyperlink(
+ id => 'delete-region',
+ label => "Delete Region",
+ onclick => {
+ delete => "content"
+ }
+ );
+
+ hr {};
+
render_region( name => 'content' );
};
template 'hello_world' => sub {
- p { "Hello, " . ( get('name') || "World" ) }
+ p { (get('with_time') ? "Time: " . time . ". " : "")
+ . "Hello, " . ( get('name') || "World" ) };
};
template 'region1' => sub {
@@ -144,5 +167,120 @@
};
};
+template '/effects' => page {
+ h1 { "Jifty.update() tests with effects" };
+
+ for (qw(Appear SlideDown)) {
+ hyperlink(
+ label => "Append ($_)",
+ onclick => {
+ append => 'hello_world',
+ region => 'content',
+ args => {
+ with_time => 1,
+ name => "Append with effect $_"
+ },
+ effect => $_,
+ effect_args => "slow"
+ }
+ );
+ outs_raw(" ");
+
+ hyperlink(
+ label => "Prepend ($_)",
+ onclick => {
+ prepend => 'hello_world',
+ region => 'content',
+ args => {
+ with_time => 1,
+ name => "Prepend with effect $_"
+ },
+ effect => $_,
+ effect_args => "slow"
+ }
+ );
+
+ outs_raw(" | ");
+ }
+
+
+ hyperlink(
+ label => "Reset",
+ onclick => {
+ region => "content",
+ replace_with => "hello_world"
+ }
+ );
+
+ hr {};
+
+ render_region( name => 'content', path => "hello_world" );
+};
+
+template '/act/play2' => page {
+ my $action = new_action(class => 'Play2', moniker => "play2");
+ form {
+ render_action($action);
+
+ form_next_page( url => "/redirected");
+ form_submit( label => "Submit" );
+ };
+};
+
+template '/act/play3' => page {
+ my $action = new_action(moniker => "play2", class => "Play2");
+ form {
+ $action
+ ->form_field('text',
+ label => "Hi",
+ sticky => 0,
+ placeholder => "foobar click me to enter text")
+ ->render();
+ form_submit( label => "Submit" );
+ };
+};
+
+template '/redirected' => page {
+ p { "Redirected!" }
+};
+
+template '/p/zero' => page {
+ render_region("__page", path => "/p/one");
+};
+
+template '/p/one' => sub {
+ p {
+ outs "FooBar.";
+ hyperlink(
+ label => "Two",
+ onclick => {
+ replace_with => '/p/two'
+ },
+ as_button => 1
+ );
+ }
+};
+
+template '/p/two' => sub {
+ hyperlink(
+ label => "Two",
+ onclick => {
+ refresh_self => 1,
+ },
+ as_button => 1
+ );
+ p { "Lorem Ipsum... " } for 1..100;
+
+ outs_raw(<<E);
+ <script type="text/javascript">
+ jQuery(function() {
+ alert( jQuery("p").size() );
+ });
+ </script>
+E
+
+
+};
+
1;
Modified: jifty/trunk/t/TestApp-JiftyJS/share/web/static/js-test/02.action.html
==============================================================================
--- jifty/trunk/t/TestApp-JiftyJS/share/web/static/js-test/02.action.html (original)
+++ jifty/trunk/t/TestApp-JiftyJS/share/web/static/js-test/02.action.html Wed Apr 9 00:12:34 2008
@@ -4,17 +4,9 @@
<script type="text/javascript" src="/static/js/jsan/JSAN.js" charset="UTF-8"></script>
<script type="text/javascript" src="lib/Test/Builder.js" charset="UTF-8"></script>
<script type="text/javascript" src="lib/Test/More.js" charset="UTF-8"></script>
-
- <script type="text/javascript" src="/static/js/prototype.js" charset="UTF-8"></script>
- <script type="text/javascript" src="/static/js/cssquery/cssQuery.js" charset="UTF-8"></script>
- <script type="text/javascript" src="/static/js/cssquery/cssQuery-level2.js" charset="UTF-8"></script>
- <script type="text/javascript" src="/static/js/cssquery/cssQuery-level3.js" charset="UTF-8"></script>
- <script type="text/javascript" src="/static/js/cssquery/cssQuery-standard.js" charset="UTF-8"></script>
+ <script type="text/javascript" src="/static/js/jquery-1.2.1.js" charset="UTF-8"></script>
+ <script type="text/javascript" src="/static/js/jquery_noconflict.js" charset="UTF-8"></script>
<script type="text/javascript" src="/static/js/behaviour.js" charset="UTF-8"></script>
- <script type="text/javascript" src="/static/js/scriptaculous/builder.js" charset="UTF-8"></script>
- <script type="text/javascript" src="/static/js/scriptaculous/effects.js" charset="UTF-8"></script>
- <script type="text/javascript" src="/static/js/scriptaculous/controls.js" charset="UTF-8"></script>
-
<script type="text/javascript" src="/static/js/jifty.js" charset="UTF-8"></script>
<script type="text/javascript">
</script>
@@ -28,23 +20,24 @@
<div class="hidden"><input type="hidden" value="TestApp::JiftyJS::Action::AddTwoNumbers" id="J:A-run-TestApp::JiftyJS::Action::AddTwoNumbers" name="J:A-run-TestApp::JiftyJS::Action::AddTwoNumbers"/></div>
<div class="form_field argument-first_number">
- <span class="preamble text argument-first_number"/>
+ <span class="preamble text argument-first_number"></span>
<label for="J:A:F-first_number-run-TestApp::JiftyJS::Action::AddTwoNumbers-S1182827" class="label text argument-first_number">first_number</label>
<input type="text" class="widget text argument-first_number jifty_enter_handler_attached" value="" id="J:A:F-first_number-run-TestApp::JiftyJS::Action::AddTwoNumbers-S1182827" name="J:A:F-first_number-run-TestApp::JiftyJS::Action::AddTwoNumbers"/>
- <span class="hints text argument-first_number"/>
- <span id="errors-J:A:F-first_number-run-TestApp::JiftyJS::Action::AddTwoNumbers" class="error text argument-first_number" style="display: none;"/>
- <span id="warnings-J:A:F-first_number-run-TestApp::JiftyJS::Action::AddTwoNumbers" class="warning text argument-first_number" style="display: none;"/>
- <span id="canonicalization_note-J:A:F-first_number-run-TestApp::JiftyJS::Action::AddTwoNumbers" class="canonicalization_note text argument-first_number" style="display: none;"/>
+ <span class="hints text argument-first_number"></span>
+ <span id="errors-J:A:F-first_number-run-TestApp::JiftyJS::Action::AddTwoNumbers" class="error text argument-first_number" style="display: none;"></span>
+ <span id="warnings-J:A:F-first_number-run-TestApp::JiftyJS::Action::AddTwoNumbers" class="warning text argument-first_number" style="display: none;"></span>
+ <span id="canonicalization_note-J:A:F-first_number-run-TestApp::JiftyJS::Action::AddTwoNumbers" class="canonicalization_note text argument-first_number" style="display: none;"></span>
</div>
<div class="form_field argument-second_number">
- <span class="preamble text argument-second_number"/>
+ <span class="preamble text argument-second_number"></span>
<label for="J:A:F-second_number-run-TestApp::JiftyJS::Action::AddTwoNumbers-S1192827" class="label text argument-second_number">second_number</label>
<input type="text" class="widget text argument-second_number jifty_enter_handler_attached" value="" id="J:A:F-second_number-run-TestApp::JiftyJS::Action::AddTwoNumbers-S1192827" name="J:A:F-second_number-run-TestApp::JiftyJS::Action::AddTwoNumbers"/>
- <span class="hints text argument-second_number"/>
- <span id="errors-J:A:F-second_number-run-TestApp::JiftyJS::Action::AddTwoNumbers" class="error text argument-second_number" style="display: none;"/>
- <span id="warnings-J:A:F-second_number-run-TestApp::JiftyJS::Action::AddTwoNumbers" class="warning text argument-second_number" style="display: none;"/>
- <span id="canonicalization_note-J:A:F-second_number-run-TestApp::JiftyJS::Action::AddTwoNumbers" class="canonicalization_note text argument-second_number" style="display: none;"/>
+ <span class="hints text argument-second_number"></span>
+
+ <span id="errors-J:A:F-second_number-run-TestApp::JiftyJS::Action::AddTwoNumbers" class="error text argument-second_number" style="display: none;"></span>
+ <span id="warnings-J:A:F-second_number-run-TestApp::JiftyJS::Action::AddTwoNumbers" class="warning text argument-second_number" style="display: none;"></span>
+ <span id="canonicalization_note-J:A:F-second_number-run-TestApp::JiftyJS::Action::AddTwoNumbers" class="canonicalization_note text argument-second_number" style="display: none;"></span>
</div>
Added: jifty/trunk/t/TestApp-JiftyJS/share/web/static/js/dict/en.json
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-JiftyJS/share/web/static/js/dict/en.json Wed Apr 9 00:12:34 2008
@@ -0,0 +1 @@
+{}
\ No newline at end of file
Added: jifty/trunk/t/TestApp-JiftyJS/share/web/static/js/dict/en_us.json
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-JiftyJS/share/web/static/js/dict/en_us.json Wed Apr 9 00:12:34 2008
@@ -0,0 +1 @@
+{}
\ No newline at end of file
Added: jifty/trunk/t/TestApp-JiftyJS/share/web/static/js/dict/zh_tw.json
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-JiftyJS/share/web/static/js/dict/zh_tw.json Wed Apr 9 00:12:34 2008
@@ -0,0 +1 @@
+{}
\ No newline at end of file
Added: jifty/trunk/t/TestApp-JiftyJS/t/00-action-Play2.t
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-JiftyJS/t/00-action-Play2.t Wed Apr 9 00:12:34 2008
@@ -0,0 +1,17 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+
+=head1 DESCRIPTION
+
+A (very) basic test harness for the Play2 action.
+
+=cut
+
+use lib 't/lib';
+use Jifty::SubTest;
+use Jifty::Test tests => 1;
+
+# Make sure we can load the action
+use_ok('TestApp::JiftyJS::Action::Play2');
+
Added: jifty/trunk/t/TestApp-JiftyJS/t/00-model-Offer.t
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-JiftyJS/t/00-model-Offer.t Wed Apr 9 00:12:34 2008
@@ -0,0 +1,51 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+
+=head1 DESCRIPTION
+
+A basic test harness for the Offer model.
+
+=cut
+
+use lib 't/lib';
+use Jifty::SubTest;
+use Jifty::Test tests => 11;
+
+# Make sure we can load the model
+use_ok('TestApp::JiftyJS::Model::Offer');
+
+# Grab a system user
+my $system_user = TestApp::JiftyJS::CurrentUser->superuser;
+ok($system_user, "Found a system user");
+
+# Try testing a create
+my $o = TestApp::JiftyJS::Model::Offer->new(current_user => $system_user);
+my ($id) = $o->create();
+ok($id, "Offer create returned success");
+ok($o->id, "New Offer has valid id set");
+is($o->id, $id, "Create returned the right id");
+
+# And another
+$o->create();
+ok($o->id, "Offer create returned another value");
+isnt($o->id, $id, "And it is different from the previous one");
+
+# Searches in general
+my $collection = TestApp::JiftyJS::Model::OfferCollection->new(current_user => $system_user);
+$collection->unlimit;
+is($collection->count, 2, "Finds two records");
+
+# Searches in specific
+$collection->limit(column => 'id', value => $o->id);
+is($collection->count, 1, "Finds one record with specific id");
+
+# Delete one of them
+$o->delete;
+$collection->redo_search;
+is($collection->count, 0, "Deleted row is gone");
+
+# And the other one is still there
+$collection->unlimit;
+is($collection->count, 1, "Still one left");
+
Modified: jifty/trunk/t/TestApp-JiftyJS/t/1-jifty-update.t
==============================================================================
--- jifty/trunk/t/TestApp-JiftyJS/t/1-jifty-update.t (original)
+++ jifty/trunk/t/TestApp-JiftyJS/t/1-jifty-update.t Wed Apr 9 00:12:34 2008
@@ -4,7 +4,7 @@
use warnings;
use lib 't/lib';
use Jifty::SubTest;
-use Jifty::Test tests => 18;
+use Jifty::Test tests => 29;
use Jifty::Test::WWW::Selenium;
use utf8;
@@ -28,8 +28,30 @@
$sel->click_ok("region4");
$sel->wait_for_text_present_ok("Hello, Smith");
+
+ $sel->click_ok("append-region");
+ $sel->wait_for_text_present_ok("Hello, World");
+ my $src = $sel->get_html_source();
+
+ like $src, qr{<p>Hello, Smith</p>.+<p>Hello, World</p>}is;
+
+ $sel->click_ok("prepend-region");
+ $sel->pause();
+ $sel->wait_for_text_present_ok("Hello, World");
+
+ $src = $sel->get_html_source();
+
+ like $src, qr{<p>Hello, World</p>.+<p>Hello, Smith</p>.+<p>Hello, World</p>}is;
+
+ $sel->click_ok("delete-region");
+ $sel->pause();
+
+ ok(! $sel->is_element_present("region-content"), "'content' region is deleted." );
+
}
+
+
{
# One click updates 3 regions, and triggers an alert.
@@ -42,6 +64,16 @@
$sel->wait_for_text_present_ok("Hello, Pony");
}
-$sel->stop;
-
+{
+ # Make sure there's 100 <p> element.
+ # For any region update, using Jifty.udpate(), javascript code in there are always executed
+ # after HTML is all done. This is to test how many <p> elements the javascript code
+ # can get. And ithe number should be 100.
+ $sel->open_ok('/p/zero');
+ $sel->click_ok('xpath=//input');
+ $sel->pause();
+ my $msg = $sel->get_alert();
+ is($msg, "100");
+}
+$sel->stop;
Modified: jifty/trunk/t/TestApp-JiftyJS/t/2-behaviour.t
==============================================================================
--- jifty/trunk/t/TestApp-JiftyJS/t/2-behaviour.t (original)
+++ jifty/trunk/t/TestApp-JiftyJS/t/2-behaviour.t Wed Apr 9 00:12:34 2008
@@ -4,7 +4,7 @@
use warnings;
use lib 't/lib';
use Jifty::SubTest;
-use Jifty::Test tests => 4;
+use Jifty::Test qw(no_plan);
use Jifty::Test::WWW::Selenium;
use utf8;
@@ -12,8 +12,16 @@
my $sel = Jifty::Test::WWW::Selenium->rc_ok($server);
my $URL = $server->started_ok;
-$sel->open_ok("/static/js-test/index.html");
+for my $test_file (qw(01.behaviour.html 02.action.html)) {
+ $sel->open_ok("/static/js-test/$test_file");
+ my $html = $sel->get_text("test");
+ $html =~ /(\d+)\.\.(\d+)/;
+
+ for($1..$2) {
+ $sel->wait_for_text_present("exact:ok $_");
+ ok(! $sel->is_text_present("exact:nok $_") );
+ }
+}
-$sel->wait_for_text_present_ok("All tests successful.");
$sel->stop;
Modified: jifty/trunk/t/TestApp-JiftyJS/t/4-tangent.t
==============================================================================
--- jifty/trunk/t/TestApp-JiftyJS/t/4-tangent.t (original)
+++ jifty/trunk/t/TestApp-JiftyJS/t/4-tangent.t Wed Apr 9 00:12:34 2008
@@ -12,6 +12,10 @@
my $sel = Jifty::Test::WWW::Selenium->rc_ok($server);
my $URL = $server->started_ok;
+$sel->open("/");
+$sel->set_speed(1000);
+$sel->pause();
+
{
# /tangent/page1 -- tangent --> /tangent/returner -- return --> /tangent/page1
Modified: jifty/trunk/t/TestApp-JiftyJS/t/5-action.t
==============================================================================
--- jifty/trunk/t/TestApp-JiftyJS/t/5-action.t (original)
+++ jifty/trunk/t/TestApp-JiftyJS/t/5-action.t Wed Apr 9 00:12:34 2008
@@ -4,14 +4,29 @@
use warnings;
use lib 't/lib';
use Jifty::SubTest;
-use Jifty::Test tests => 10;
+use Jifty::Test;
use Jifty::Test::WWW::Selenium;
use utf8;
+BEGIN {
+ if ($ENV{'SELENIUM_RC_BROWSER'} eq '*iexplore') {
+ plan(skip_all => "Temporarily, until the 'Operation Abort' bug is solved.");
+ }
+ else {
+ plan(tests => 10);
+ }
+}
+
my $server = Jifty::Test->make_server;
my $sel = Jifty::Test::WWW::Selenium->rc_ok($server);
my $URL = $server->started_ok;
+$sel->open("/");
+
+if ($ENV{'SELENIUM_RC_BROWSER'} eq '*iexplore') {
+ $sel->set_speed(1000);
+}
+
{
# Test "Play" action's parameter.
@@ -22,22 +37,28 @@
# Tag is ajax canonicalized to lowercase.
- $sel->set_speed(1000);
$sel->click_ok($tags);
$sel->type_ok($tags, "FOO");
$sel->fire_event($tags, "blur");
+ $sel->pause(1000);
+
my $tag_value = $sel->get_value($tags);
is $tag_value, 'foo', "Tags are canonicalized to lower-case";
$sel->type_ok($mood, "FOO");
$sel->fire_event($tags, "blur");
+ $sel->pause(1000);
+
is($sel->get_text('//span[contains(@class, "error text argument-mood")]'),
"That doesn't look like a correct value",
"mood validation error");
$sel->type_ok($mood, "angry");
$sel->fire_event($tags, "blur");
+
+ $sel->pause(1000);
+
is($sel->get_text('//span[contains(@class, "error text argument-mood")]'),
"",
"mood validation ok");
@@ -45,3 +66,4 @@
}
$sel->stop;
+
Added: jifty/trunk/t/TestApp-JiftyJS/t/6-offer-actions.t
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-JiftyJS/t/6-offer-actions.t Wed Apr 9 00:12:34 2008
@@ -0,0 +1,94 @@
+# This test is for testing Jifty.update() javascript function.
+
+use strict;
+use warnings;
+use lib 't/lib';
+use Jifty::SubTest;
+use Jifty::Test;;
+use Jifty::Test::WWW::Selenium;
+use utf8;
+
+
+$/ = undef;
+
+my $data = <DATA>;
+$data =~ s/^#.*$//gm;
+
+my @commands = split /\n\n+/, $data;
+
+plan tests => 2+ at commands;
+
+my $server = Jifty::Test->make_server;
+my $sel = Jifty::Test::WWW::Selenium->rc_ok($server);
+my $URL = $server->started_ok;
+
+$sel->open("/");
+
+for (@commands) {
+ my ($cmd, $arg1, $arg2) = (split(/\n\s*/, $_, 3), "", "");
+
+ $cmd =~ s{^ *}{}g;
+ $cmd =~ s{ *$}{}g;
+ $arg1 =~ s{\s*$}{};
+ $arg2 =~ s{\s*$}{};
+
+ $cmd .= "_ok";
+ $sel->$cmd($arg1, $arg2);
+
+}
+$sel->stop;
+
+__DATA__
+open
+ /__jifty/admin/model/Offer
+
+type
+ xpath=//div[contains(@class, "jifty_admin create item")]//input[@type="text"]
+ Not A Job Offer
+
+click
+ xpath=//div[contains(@class,"submit_button")]//input
+
+pause
+ 1000
+
+wait_for_element_present
+ xpath=//span[contains(@class, "value")][contains(@class, "argument-name")][contains(@class, "text")]
+
+wait_for_text_present
+ Not A Job Offer
+
+wait_for_element_present
+ xpath=//input[@type="checkbox"][contains(@class, "argument-is_job")]
+
+get_text
+ xpath=//span[contains(@class, "text")][contains(@class, "value")][contains(@class, "argument-name")]
+ Not A Job Offer
+
+####
+
+open
+ /__jifty/admin/model/Offer
+
+type
+ xpath=//div[contains(@class, "form_field")][contains(@class,"argument-name")]//input[@type="text"]
+ Offer A Job
+
+check
+ xpath=//input[starts-with(@id, "J:A:F-is_job-auto-")][@type="checkbox"]
+
+
+# Click the "Create" button
+click
+ xpath=//div[@class="submit_button"]/input[@type="submit"][contains(@name,"J:ACTIONS=auto-")]
+
+pause
+ 1000
+
+wait_for_element_present
+ xpath=//input[@type="checkbox"][@checked]
+
+get_text
+ xpath=//span[contains(@class, "text")][contains(@class, "argument-name")][contains(@class, "value")]
+ Offer A Job
+
Added: jifty/trunk/t/TestApp-JiftyJS/t/7-redirect.t
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-JiftyJS/t/7-redirect.t Wed Apr 9 00:12:34 2008
@@ -0,0 +1,50 @@
+# This test is for testing Jifty.update() javascript function.
+
+use strict;
+use warnings;
+use lib 't/lib';
+use Jifty::SubTest;
+use Jifty::Test;;
+use Jifty::Test::WWW::Selenium;
+use utf8;
+
+$/ = undef;
+
+my $data = <DATA>;
+$data =~ s/^#.*$//gm;
+
+my @commands = split /\n\n+/, $data;
+
+plan tests => 2+ at commands;
+
+my $server = Jifty::Test->make_server;
+my $sel = Jifty::Test::WWW::Selenium->rc_ok($server);
+my $URL = $server->started_ok;
+
+for (@commands) {
+ my ($cmd, $arg1, $arg2) = (split(/\n\s*/, $_, 3), "", "");
+ $cmd =~ s{^ *}{}g;
+ $cmd =~ s{ *$}{}g;
+ $arg1 =~ s{\s*$}{};
+ $arg2 =~ s{\s*$}{};
+
+ $cmd .= "_ok";
+ $sel->$cmd($arg1, $arg2);
+
+}
+$sel->stop;
+
+__DATA__
+open
+ /act/play2
+
+type
+ xpath=//input[@type='text']
+ Not A Job Offer
+
+click
+ xpath=//input[@type='submit']
+
+wait_for_text_present
+ Redirected!
+
Added: jifty/trunk/t/TestApp-JiftyJS/t/8-placeholder.t
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-JiftyJS/t/8-placeholder.t Wed Apr 9 00:12:34 2008
@@ -0,0 +1,45 @@
+# Test Action
+
+use strict;
+use warnings;
+use lib 't/lib';
+use Jifty::SubTest;
+use Jifty::Test;
+use Jifty::Test::WWW::Selenium;
+use utf8;
+
+BEGIN {
+ if (($ENV{'SELENIUM_RC_BROWSER'}||'') eq '*iexplore') {
+ plan(skip_all => "Temporarily, until the 'Operation Abort' bug is solved.");
+ }
+ else {
+ plan(tests => 6);
+ }
+}
+
+my $server = Jifty::Test->make_server;
+my $sel = Jifty::Test::WWW::Selenium->rc_ok($server);
+my $URL = $server->started_ok;
+
+$sel->open("/");
+
+$sel->set_speed(1000);
+
+{
+ # Test placeholder
+ $sel->open_ok("/act/play3");
+
+ my $input = 'css=input[name="J:A:F-text-play2"]';
+
+ $sel->is_element_present($input);
+ my $text = $sel->get_value($input);
+
+ is( $text, "foobar click me to enter text", "Initial content in the placeholder." );
+
+ $sel->click_ok($input);
+ $sel->fire_event($input, "focus");
+
+ is( $sel->get_value($input), "", "Placeholder goes empty after clicking on it" );
+}
+
+$sel->stop;
Added: jifty/trunk/t/TestApp-JiftyJS2/Makefile.PL
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-JiftyJS2/Makefile.PL Wed Apr 9 00:12:34 2008
@@ -0,0 +1,7 @@
+use inc::Module::Install;
+
+name 'TestApp::JiftyJS/';
+version '0.01';
+requires 'Jifty' => '0.70824';
+
+WriteAll;
Added: jifty/trunk/t/TestApp-JiftyJS2/bin/jifty
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-JiftyJS2/bin/jifty Wed Apr 9 00:12:34 2008
@@ -0,0 +1,11 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+use File::Basename qw(dirname);
+use UNIVERSAL::require;
+
+use Jifty;
+use Jifty::Script;
+
+local $SIG{INT} = sub { warn "Stopped\n"; exit; };
+Jifty::Script->dispatch();
Added: jifty/trunk/t/TestApp-JiftyJS2/etc/config.yml
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-JiftyJS2/etc/config.yml Wed Apr 9 00:12:34 2008
@@ -0,0 +1,69 @@
+---
+framework:
+ AdminMode: 1
+ ApplicationClass: TestApp::JiftyJS
+ ApplicationName: TestApp::JiftyJS
+ ApplicationUUID: F43CA57E-A4BE-11DC-A07C-465A83BE23AB
+ ConfigFileVersion: 3
+ Database:
+ CheckSchema: 1
+ Database: testapp_jiftyjs
+ Driver: SQLite
+ Host: localhost
+ Password: ''
+ RecordBaseClass: Jifty::DBI::Record::Cachable
+ User: ''
+ Version: 0.0.1
+ DevelMode: 1
+ L10N:
+ PoDir: share/po
+ AllowedLang:
+ - en
+ LogLevel: INFO
+ Mailer: Sendmail
+ MailerArgs: []
+
+ Plugins:
+ -
+ LetMe: {}
+
+ -
+ SkeletonApp: {}
+
+ -
+ REST: {}
+
+ -
+ Halo: {}
+
+ -
+ ErrorTemplates: {}
+
+ -
+ OnlineDocs: {}
+
+ -
+ CompressedCSSandJS: { }
+
+ -
+ AdminUI: {}
+
+ PubSub:
+ Backend: Memcached
+ Enable: ~
+ SkipAccessControl: 0
+ TemplateClass: TestApp::JiftyJS::View
+ Web:
+ BaseURL: http://localhost
+ DataDir: var/mason
+ Globals: []
+
+ MasonConfig:
+ autoflush: 0
+ default_escape_flags: h
+ error_format: text
+ error_mode: fatal
+ Port: 8888
+ ServeStaticFiles: 1
+ StaticRoot: share/web/static
+ TemplateRoot: share/web/templates
Added: jifty/trunk/t/TestApp-JiftyJS2/lib
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-JiftyJS2/lib Wed Apr 9 00:12:34 2008
@@ -0,0 +1 @@
+link ../TestApp-JiftyJS/lib
\ No newline at end of file
Added: jifty/trunk/t/TestApp-JiftyJS2/share
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-JiftyJS2/share Wed Apr 9 00:12:34 2008
@@ -0,0 +1 @@
+link ../TestApp-JiftyJS/share
\ No newline at end of file
Added: jifty/trunk/t/TestApp-JiftyJS2/t
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-JiftyJS2/t Wed Apr 9 00:12:34 2008
@@ -0,0 +1 @@
+link ../TestApp-JiftyJS/t
\ No newline at end of file
Modified: jifty/trunk/t/TestApp-Plugin-OnClick/etc/config.yml
==============================================================================
--- jifty/trunk/t/TestApp-Plugin-OnClick/etc/config.yml (original)
+++ jifty/trunk/t/TestApp-Plugin-OnClick/etc/config.yml Wed Apr 9 00:12:34 2008
@@ -4,7 +4,7 @@
ApplicationClass: TestApp::Plugin::OnClick
ApplicationName: TestApp-Plugin-OnClick
ApplicationUUID: 45E1B0FE-820A-11DC-9905-76B28F38D863
- ConfigFileVersion: 2
+ ConfigFileVersion: 3
Database:
CheckSchema: 1
Database: testapp_plugin_onclick
Modified: jifty/trunk/t/TestApp-Plugin-OnClick/t/onclick.t
==============================================================================
--- jifty/trunk/t/TestApp-Plugin-OnClick/t/onclick.t (original)
+++ jifty/trunk/t/TestApp-Plugin-OnClick/t/onclick.t Wed Apr 9 00:12:34 2008
@@ -27,7 +27,11 @@
'please use Jifty.update instead of update.',
'bare update is deprecated'
);
+
+sleep 2;
+
$html = $sel->get_html_source;
+
like( $html, qr/original content/, 'replace content correctly' );
unlike( $html, qr{args:/content\.html}, 'replaced by javascript' );
Modified: jifty/trunk/t/TestApp-Plugin-SinglePage/lib/TestApp/Plugin/SinglePage/View.pm
==============================================================================
--- jifty/trunk/t/TestApp-Plugin-SinglePage/lib/TestApp/Plugin/SinglePage/View.pm (original)
+++ jifty/trunk/t/TestApp-Plugin-SinglePage/lib/TestApp/Plugin/SinglePage/View.pm Wed Apr 9 00:12:34 2008
@@ -49,5 +49,18 @@
h1 { $foo };
};
+template '/p/history/one' => page {
+ p { "This Is Page One" };
+};
+
+template '/p/history/two' => page {
+ p { "This Is Page Two" };
+};
+
+
+template '/p/history/three' => page {
+ p { "This Is Page Three" };
+};
+
1;
Added: jifty/trunk/t/TestApp-Plugin-SinglePage/t/history.t
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-Plugin-SinglePage/t/history.t Wed Apr 9 00:12:34 2008
@@ -0,0 +1,28 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use Jifty::SubTest;
+use Jifty::Test tests => 10;
+use Jifty::Test::WWW::Selenium;
+use utf8;
+
+my $server = Jifty::Test->make_server;
+my $sel = Jifty::Test::WWW::Selenium->rc_ok( $server );
+my $URL = $server->started_ok;
+diag $URL;
+
+$sel->open_ok("/p/history/one");
+$sel->wait_for_text_present_ok("This Is Page One");
+
+$sel->open_ok("/p/history/two");
+$sel->wait_for_text_present_ok("This Is Page Two");
+
+$sel->open_ok("/p/history/three");
+$sel->wait_for_text_present_ok("This Is Page Three");
+
+$sel->go_back();
+$sel->wait_for_text_present_ok("This Is Page Two");
+
+$sel->go_back();
+$sel->wait_for_text_present_ok("This Is Page One");
+
More information about the Jifty-commit
mailing list