prototype: Reorganize the source tree.
This commit is contained in:
commit
bb4d189b37
365
CHANGELOG
Normal file
365
CHANGELOG
Normal file
@ -0,0 +1,365 @@
|
||||
*SVN*
|
||||
|
||||
* Add test to ensure Content-type header is set for simulated verbs. [sam]
|
||||
|
||||
* Fix Content-Type header for HTTP methods simulated with POST not defaulting to application/x-www-form-urlencoded. [Thomas Fuchs]
|
||||
|
||||
* Simplify form serialization and add support for fields with the same name as Hash methods. Closes #6649. [Mislav Marohnić]
|
||||
|
||||
* Fix attribute selectors for IE. Closes #5170. [Tobie Langel, Andrew Dupont]
|
||||
|
||||
* A slew of dom.js improvements. Closes #4217, #6589, #7001. [Tobie]
|
||||
- Fix Element.getDimensions() for hidden elements, make Element.getHeight() use .getDimensions()
|
||||
- Add Element.getWidth()
|
||||
- Make Element.replace() call .toString() on the html argument (alike .update())
|
||||
- Fix an issue with Element.get/setStyle() and Safari with 'float'
|
||||
- Add a bunch of missing unit tests
|
||||
|
||||
* Fix an issue with Element.setStyle({opacity:''}) setting the opacity to 0 instead of removing the set inline opacity style. [Thomas Fuchs]
|
||||
|
||||
* Ensure Ajax.Request's evalResponse is called before onComplete so that onComplete can reference any elements created during the response. Closes #6727. [jonathan]
|
||||
|
||||
* Ensure the WEBrick test runner sets the correct Content-Type for tests and fixtures. [sam]
|
||||
|
||||
* Form.serialize once again works with non-form elements. This is a temporary change to prevent the Rails link_to_remote regression described in #6898. Prototype 1.5.1 will introduce an API for working with collections of arbitrary form elements. References #6887. Closes #6898. [sam]
|
||||
|
||||
* Make selectors match forms that have an element with name="id" correctly, fixes #5759 [mislav]
|
||||
|
||||
* Remove support for HTTP authorization in Ajax calls introduced with #6366. Closes #6638 [jmecham]
|
||||
|
||||
* Add Enumerable.size() to count elements in an enumerable and the corresponding Array.size() method, fixes #6710 [ZenCocoon]
|
||||
|
||||
* Add String.succ() method to allow for String ranges, fixes #6037 [Cory Hudson, mislav]
|
||||
Examples:
|
||||
'abcd'.succ(); -> 'abce'
|
||||
$R('a','d').map(function(char){ return char; }); -> ['a','b','c','d']
|
||||
|
||||
* Make Element.scrollTo() correctly consider offsets of parent DOM nodes, fixes #6625 [ohader, savetheclocktower]
|
||||
|
||||
* Fix Enumerable.inGroupsOf() to correctly work with fill values that evaluate to false, fixes #6782 [hawk]
|
||||
|
||||
* Remove/cleanup redundant $() calls in dom.js, fixes #6817 [Tobie]
|
||||
|
||||
* Don't cache files in automatic unit tests, fixes #6218 [voidlock]
|
||||
|
||||
* Add $w() to easily create arrays from strings like Ruby's %w, fixes #5682 [glazedginger, brendon.aaron]
|
||||
|
||||
* Add Element.toggleClassName() to toggle CSS classes on elements, fixes #6759 [Tobie]
|
||||
|
||||
* Make Form.getInputs always return an array for consistency, fixes #6475 [Justin Gehtland, savetheclocktower]
|
||||
|
||||
* Make TimedObserver work correctly for SELECT MULTIPLE elements, fixes #6593 [clemos, tdd]
|
||||
|
||||
* Fix Template.prototype.evaluate to correctly interpret 0 and false values, add String.interpret() for safely interpreting and converting values to strings, fixes #6675 [hawk]
|
||||
|
||||
* Make Element.getStyle() work with camelCased argument, fixes #6686 [Tobie]
|
||||
|
||||
* Fix a redundant check in Array.prototype.compact, fixes #4739 [wlodarcz, mislav]
|
||||
|
||||
* Fix $() to correctly pass back the results of document.getElementById(), notably returning "null" on elements not found, fixes #6582 [adsmart]
|
||||
|
||||
* Change/add assertNull, assertUndefined, assertNullOrUndefined and not-* variants in unittest.js, fixes #6582 [adsmart]
|
||||
|
||||
* Cleanup String.prototype.camelize, fix an issue with String.prototype.underscore, fixes #4714, #6685 [Tobie, Thomas Fuchs]
|
||||
|
||||
* Add String.prototype.capitalize, which returns a string with the first character in upper case, fixes #6666 [Tobie]
|
||||
|
||||
* Make Element.getStyle() and Element.setStyle() handle the CSS 'opacity' property transparently in IE, fixes #6093 [brandon.aaron, Tobie]
|
||||
|
||||
* Fix handling of CSS 'float' property for Element.getStyle() and Element.setStyle(), fixes #4160 [Thomas Fuchs, ericf]
|
||||
|
||||
* Fix that onComplete would be called twice with synchronous Ajax requests on Safari (provides units tests for #5756) [Thomas Fuchs]
|
||||
|
||||
* Fix Form.Field.activate to not select text on buttons in IE, fixes #2653 [sutch, mislav, Thomas Fuchs]
|
||||
|
||||
* Fix String.unescapeHTML() on Firefox for strings that are longer than 2048 bytes, fixes #5789 [Paul Moers, Thomas Fuchs]
|
||||
|
||||
* Redefine Array.prototype.concat for Opera, as the native implemenation doesn't work correctly [Thomas Fuchs]
|
||||
|
||||
* Add unit tests for Function.prototype.bind() [Thomas Fuchs]
|
||||
|
||||
* Add String.prototype.underscore and String.prototype.dasherize [Thomas Fuchs]
|
||||
Examples:
|
||||
'Hello_World'.dasherize() -> 'Hello-World'
|
||||
'borderBottomWidth'.underscore() -> 'border_bottom_width'
|
||||
'borderBottomWidth'.underscore().dasherize() -> 'border-bottom-width'
|
||||
|
||||
*1.5.0_rc2* (November 11, 2006)
|
||||
|
||||
* Ensure that existing DOM properties take precedence over extended element methods in all browsers. Closes #5115. [Sean Kleinjung, sam]
|
||||
|
||||
* Add Element.Methods.readAttribute as a simple wrapper around getAttribute (which isn't a "real" function and doesn't have .apply or .call in Safari and IE). Useful in conjunction with Enumerable.invoke for extracting the values of a custom attribute from a collection of elements. [sam]
|
||||
Example:
|
||||
<div id="widgets">
|
||||
<div class="widget" widget_id="7">...</div>
|
||||
<div class="widget" widget_id="8">...</div>
|
||||
<div class="widget" widget_id="9">...</div>
|
||||
</div>
|
||||
|
||||
$$('div.widget').invoke('readAttribute', 'widget_id')
|
||||
// ["7", "8", "9"]
|
||||
|
||||
* Add Element.Methods.immediateDescendants, like $A($(element).childNodes) but without text nodes. [sam]
|
||||
|
||||
* More consistency. Closes #6573. [Bob Silva]
|
||||
|
||||
* String.prototype.toQueryParams and Hash.prototype.toQueryString now properly serialize arrays as multiple values. Closes #4436. [mislav, altblue, L`OcuS]
|
||||
|
||||
* Fix Form.serialize for options with empty values. Closes #5033. [tdd, Thomas Fuchs, sam]
|
||||
|
||||
* Add Element.Methods.Simulated for simulating HTMLElement methods in lesser browsers. Add hasAttribute as the first simulated method. [tdd, Thomas Fuchs, sam]
|
||||
|
||||
* Add a "retry with throw" button for test error messages. [sam]
|
||||
|
||||
* rake test now runs test/unit/*.html by default. Additionally, you can specify individual tests to run with the TESTS environment variable, and you can restrict the tests to particular browsers using the BROWSERS environment variable. [sam]
|
||||
|
||||
Examples:
|
||||
% BROWSERS=safari,firefox rake test
|
||||
% TESTS=dom rake test
|
||||
|
||||
* Element.hasClassName now bypasses the Element.ClassNames API for performance. [sam]
|
||||
|
||||
* Pick some low-hanging performance and DRYness fruit. [sam]
|
||||
- Inline length property accesses in for loops
|
||||
- Enumerable-ize for loops where it makes sense
|
||||
- Make better use of Element.Methods and Form.Methods/Form.Element.Methods
|
||||
|
||||
* A slew of Ajax improvements. Closes #6366. [mislav, sam]
|
||||
|
||||
Public-facing changes include:
|
||||
- HTTP method can be specified in either lowercase or uppercase, and uppercase is always used when opening the XHR connection
|
||||
- Added 'encoding' option (for POST) with a default of 'UTF-8'
|
||||
- Ajax.Request now recognizes all the JavaScript MIME types we're aware of
|
||||
- PUT body support with the 'postBody' option
|
||||
- HTTP authentication support with the 'username' and 'password' options
|
||||
- Query parameters can be passed as a string or as a hash
|
||||
- Fixed both String.toQueryParams and Hash.toQueryString when handling empty values
|
||||
- Request headers can now be specified as a hash with the 'requestHeaders' option
|
||||
|
||||
* Improve performance of the common case where $ is called with a single argument. Closes #6347. [sam, rvermillion, mislav]
|
||||
|
||||
* Fix Object.inspect to correctly distinguish between null and undefined, fixes #5941 [tdd, mislav]
|
||||
|
||||
* Don't serialize disabled form elements, fixes #4586 [tdd]
|
||||
|
||||
* Make HTML element classes extension mechanism for Safari not throw errors on WebKit beta versions [Thomas Fuchs]
|
||||
|
||||
* Add support for using Element.update() with no or a non-string parameter [Thomas Fuchs]
|
||||
|
||||
Example:
|
||||
$('empty_me').update() -> clears the element
|
||||
$('easy_as').update(123) -> set element content to '123'
|
||||
|
||||
* Add unit tests for hashes, fixes #6344 [Tobie Langel]
|
||||
|
||||
* Add clone() method to arrays, fixes #6338 [Tobie Langel]
|
||||
|
||||
* Backing out of [5194] (Element.clear) because of issues with IE on certain elements, #6051
|
||||
|
||||
* Add Element.clear for easily emptying out elements, fixes #6051 [brandon.aaron@gmail.com]
|
||||
|
||||
* Enumerable.each now returns the enumerable to allow for method chaining, fixes #6250 [eventualbuddha]
|
||||
|
||||
* Make makeClipping and undoClipping always return their element to stay chainable
|
||||
|
||||
* Fix an issue with certain Element chain calls not correctly extending elements with Prototype element methods on IE [Thomas Fuchs]
|
||||
|
||||
* Add Enumerable.eachSlice and Enumerable.inGroupsOf, fixes #6046 [rails@tddsworld.com, Thomas Fuchs]
|
||||
|
||||
Example:
|
||||
[1,2,3,4,5].inGroupsOf(3) -> [[1,2,3],[4,5,null]]
|
||||
[1,2,3].inGroupsOf(4,'x') -> [[1,2,3,'x']]
|
||||
|
||||
* Complete unit tests for array.js and string.js [Thomas Fuchs]
|
||||
|
||||
* Performance improvements for document.getElementsByClassName, including querying with XPath in supported browsers. [Andrew Dupont]
|
||||
|
||||
* Make Form.getElements() return elements in the correct order, fix broken Form.serialize return, fixes #4249, #6172 [lars@pinds.com, Thomas Fuchs, john]
|
||||
|
||||
* Add various DOM unit tests, fixes #6176, #6177 [tdd]
|
||||
|
||||
* Split Form.serialize into Form.serialize and Form.serializeElements. The latter can be used stand-alone to serialize an array of elements you pass in, instead of the entire form [DHH]
|
||||
|
||||
* Form.Element.disable() and .enable() will now work correctly, fixes #6034 [dresselm@businesslogic.com]
|
||||
|
||||
* Fix IE and Safari issues with Position.positionedOffset, add position.html unit tests, fixes #5621 [renggli@iam.unibe.ch]
|
||||
|
||||
* Fix an issue with Element.undoClipping and IE [Thomas Fuchs]
|
||||
|
||||
* Element.cleanWhitespace now correctly removes consecutive empty text nodes, fixes #3209 [livier.duroselle@gmail.com]
|
||||
|
||||
* Element.extend now does not try to extend text nodes, fixes #4642 [siegfried.puchbauer@gmail.com]
|
||||
|
||||
*1.5.0_rc1* (September 4, 2006)
|
||||
|
||||
* bindAsEventListener now passes along any provided arguments after the event argument. Closes #5508. [todd.fisher@revolution.com]
|
||||
|
||||
* Fix makeClipping and undoClipping with local overflow style values other than visible and hidden, fixes #3672 [Thomas Fuchs]
|
||||
|
||||
* Add Element.up, Element.down, Element.previous, and Element.next for easily traversing the DOM. (Inspired by Thomas Fuchs' original implementation of Element.up: http://pastie.caboo.se/7702) [sam]
|
||||
|
||||
Examples:
|
||||
<div id="sidebar"> -> $('nav').up() or $('menu').up('div')
|
||||
<ul id="nav"> -> $('sidebar').down() or $('sidebar').down('ul') or $('menu').previous()
|
||||
<li>...</li> -> $('sidebar').down(1) or $('sidebar').down('li')
|
||||
<li>...</li> -> $('sidebar').down(2) or $('sidebar').down('li', 2) or $('sidebar').down('li').next('li')
|
||||
<li class="selected">...</li> -> $('sidebar').down('li.selected')
|
||||
</ul>
|
||||
<ul id="menu"> -> $('sidebar').down('ul').next()
|
||||
...
|
||||
|
||||
* Refactor $$ and Element.getElementsBySelector into Selector.findChildElements. [sam]
|
||||
|
||||
* Add Element.match, which takes a single CSS selector expression and returns true if it matches the element. [sam]
|
||||
|
||||
* Add Element.ancestors, Element.descendants, Element.previousSiblings, Element.nextSiblings, and Element.siblings. [sam]
|
||||
|
||||
* Add Element.inspect for better DOM debugging. [sam]
|
||||
|
||||
* Add an optional boolean argument to String.prototype.inspect which, when true, makes the string double-quoted instead of single-quoted. [sam]
|
||||
|
||||
* Add the uniq() method to Arrays, which returns a new Array with duplicates removed, fixes #3810 [secondlife]
|
||||
|
||||
* Add stop() method to PeriodicalExecutor, fixes #4801 [Jon Evans]
|
||||
|
||||
* Fix issues with Enumerable.any, ObjectRange.toArray, added unit tests, fixes #4419 [miyamuko, Thomas Fuchs]
|
||||
|
||||
* Fix two instances of unneccesarily redeclared variables, fixes #5229 [Thomas Fuchs]
|
||||
|
||||
* Fix a loop in Element.extend to properly use local variable, fixes #5128 [arrix]
|
||||
|
||||
* Add constants for additional keys in Event, fixes #5411, #5795 [simone_b]
|
||||
|
||||
* Workaround a DOM API bug in Opera in Position.page(), fixes #2407, #5848 [Thomas Fuchs]
|
||||
|
||||
* Remove duplicate definition of Position.clone(), fixes #3765 [Thomas Fuchs]
|
||||
|
||||
* Make destructive Element, Form, and Form.Element methods return their first argument, so that multiple calls can be chained together. [sam]
|
||||
|
||||
ex. $("sidebar").addClassName("selected").show();
|
||||
|
||||
The following methods now return their first argument: Element.toggle, Element.hide, Element.show, Element.remove, Element.update, Element.replace, Element.addClassName, Element.removeClassName, Element.observe, Element.stopObserving, Element.cleanWhitespace, Element.scrollTo, Element.setStyle, Element.makePositioned, Element.undoPositioned, Element.makeClipping, Element.undoClipping, Form.reset, Form.disable, Form.enable, Form.focusFirstElement, Form.Element.focus, Form.Element.select, Form.Element.clear, Form.Element.activate, Form.Element.disable, Form.Element.enable.
|
||||
|
||||
* For consistency, Element.toggle, Element.show, Element.hide, Field.clear, and Field.present no longer take an arbitrary number of arguments. [sam]
|
||||
|
||||
!! BACKWARDS COMPATIBILITY CHANGE !!
|
||||
|
||||
If you have code that looks like this:
|
||||
Element.show('page', 'sidebar', 'content');
|
||||
You need to replace it with code like this:
|
||||
['page', 'sidebar', 'content'].each(Element.show);
|
||||
|
||||
* Mix in Form and Form.Element methods to forms and form field elements with $() and $$(). Closes #4448. [Dan Webb, sam]
|
||||
|
||||
* Add Object.clone [sam]
|
||||
|
||||
* Add Form.Element.disable and Form.Element.enable. Closes #4943. [jan@prima.de]
|
||||
|
||||
* Field is now simply an alias for Form.Element. [sam]
|
||||
|
||||
* Add Element.Methods.getElementsByClassName and Element.Methods.getElementsBySelector. Closes #4669. [Andrew Dupont, DHH, sam]
|
||||
|
||||
* Avoid race condition when stopping an Ajax.PeriodicalUpdater. Closes #4809. [e98cuenc@gmail.com]
|
||||
|
||||
* Improve support for synchronous requests. Closes #5916. [sam, jthrom@gmail.com]
|
||||
|
||||
* Add serialization and observation support for input type=search. Closes #4096. [rpnielsen@gmail.com]
|
||||
|
||||
* Properly decode query components in String.prototype.toQueryParams. Closes #3487. [sam]
|
||||
|
||||
* Add Array.prototype.reduce [sam]:
|
||||
[1, 2].reduce() // [1, 2]
|
||||
[1].reduce() // 1
|
||||
[].reduce() // undefined
|
||||
|
||||
* Add Object.keys and Object.values [sam]
|
||||
|
||||
* Simulate non-GET/POST requests by POSTing with a _method parameter set to the actual verb [DHH]
|
||||
|
||||
* Make Element.update() handle TABLE-related elements with the DOM API because of IE's missing .innerHTML property on them [Thomas Fuchs, thx to Rick Olson]
|
||||
|
||||
* Sync to script.aculo.us unittest.js library as of 2006/08/29 [Thomas Fuchs]
|
||||
|
||||
* Add additional unit tests to test/unit/dom.html for testing Element.update and $().update in various enviroments [Thomas Fuchs]
|
||||
|
||||
* Prevent possible exceptions on unloading the page in IE [Thomas Fuchs]
|
||||
|
||||
*1.5.0_rc0* (April 5, 2006)
|
||||
|
||||
* Modify HTMLElement.prototype and short-circuit Element.extend where possible. Closes #4477. [Thomas Fuchs]
|
||||
|
||||
* Only observe window.onunload in IE for Mozilla bfcache support. Closes #3726. [Mike A. Owens]
|
||||
|
||||
* Don't redefine Array.prototype.shift. Closes #4138. [leeo]
|
||||
|
||||
* Prevent redefining Array.prototype.reverse more than once. Closes #3951. [brettdgibson@gmail.com]
|
||||
|
||||
* Fix Enumerable.min/Enumerable.max to recognize the value 0. Closes #3847, #4190. [rubyonrails@brainsick.com, Martin Bialasinski]
|
||||
|
||||
* Change Ajax.getTransport to prefer XMLHttpRequest in anticipation of IE 7. Closes #3688. [jschrab@malicstower.org, sam]
|
||||
|
||||
* Make Array.prototype.flatten more robust. Closes #3453. [Martin Bialasinski, sam]
|
||||
|
||||
* Fix evalScripts from crashing Firefox if script includes 'var'. Closes #3288, #4165. [rahul@ntag.com, sam]
|
||||
|
||||
* Scope iterators locally. Closes #4589. [sam]
|
||||
|
||||
* Support Insertion.Before/Insertion.After for <tr> elements in IE. Closes #3925. [rails-venkatp@sneakemail.com]
|
||||
|
||||
* Add a contentType option for overriding application/x-www-form-urlencoded in Ajax.Request. Closes #3564. [avif@arc90.com, sam]
|
||||
|
||||
* Surround values in the X-JSON header in parenthesis for better compatibility with Firefox. Closes #4118. [bigsmoke@gmail.com]
|
||||
|
||||
* Fix Form.serialize to properly encode option values in multiple selects in IE. Closes #3291. [rubyonrails@brainsick.com]
|
||||
|
||||
* Cache methods added to DOM elements with Element.extend to prevent memory leaks in IE. Closes #4465. [jaen@laborint.com, sam]
|
||||
|
||||
* 1.5.0_pre1* (March 26, 2006)
|
||||
|
||||
* Add attribute selector support for Selector (and $$). Closes #4368. [devslashnull@gmail.com]
|
||||
Example:
|
||||
$$('form#foo input[type=text]').each(function(input) {
|
||||
input.setStyle({color: 'red'});
|
||||
});
|
||||
|
||||
* Send Accept header containing 'text/javascript, text/html, application/xml, text/xml */*'' to inform Rails that we prefer RJS, but we'll take HTML or XML or whatever if you can't deliver the goods [DHH]
|
||||
|
||||
* Make $$ work in IE. Closes #3715. [rubyonrails@brainsick.com]
|
||||
|
||||
* Add test/browser.html, which provides a simple object browser for the Prototype source (Firefox/Safari only). [sam]
|
||||
|
||||
* Add Element.extend, which mixes Element methods into a single HTML element. This means you can now write $('foo').show() instead of Element.show('foo'). $, $$ and document.getElementsByClassName automatically call Element.extend on any returned elements. [sam]
|
||||
|
||||
* Add Element.replace as a cross-browser implementation of the "outerHTML" property. References #3246. [tom@craz8.com]
|
||||
|
||||
* Fix Enumerable.zip iterator behavior. [Marcin Kaszynski, sam]
|
||||
|
||||
*1.5.0_pre0* (January 18, 2006)
|
||||
|
||||
* Add String.prototype.truncate to complement the Action View truncate helper. [sam]
|
||||
|
||||
* Add String.prototype.gsub, String.prototype.sub, and String.prototype.scan, all of which take a pattern and an iterator (or a pattern and a replacement template string in the case of gsub and sub). [sam]
|
||||
|
||||
* Add a Template class for interpolating named keys from an object in a string. [sam]
|
||||
|
||||
* Add the $$ function for finding DOM elements by simple CSS selector strings. [sam]
|
||||
Example: Find all <img> elements inside <p> elements with class "summary", all inside
|
||||
the <div> with id "page". Hide each matched <img> tag.
|
||||
$$('div#page p.summary img').each(Element.hide)
|
||||
|
||||
* Add a Selector class for matching elements by single CSS selector tokens. [sam]
|
||||
|
||||
* Add Test.Unit.Assertions.assertEnumEqual for comparing Enumerables in tests. [sam]
|
||||
|
||||
* Add Element.childOf(element, ancestor) which returns true when element is a child of ancestor. [sam]
|
||||
|
||||
* Fix escaping in String.prototype.inspect. [sam]
|
||||
|
||||
* Add String.prototype.strip to remove leading and trailing whitespace from a string. [sam]
|
||||
|
||||
* Move Prototype to Rails SVN. [sam]
|
||||
http://dev.rubyonrails.org/svn/rails/spinoffs/prototype/
|
||||
|
||||
* Make the console nicer. [sam]
|
||||
|
||||
* Prune the source tree. [sam]
|
16
LICENSE
Normal file
16
LICENSE
Normal file
@ -0,0 +1,16 @@
|
||||
Copyright (c) 2005 Sam Stephenson
|
||||
|
||||
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 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.
|
55
README
Normal file
55
README
Normal file
@ -0,0 +1,55 @@
|
||||
= Prototype
|
||||
==== An object-oriented JavaScript framework
|
||||
|
||||
Prototype is a JavaScript framework that aims to ease development of dynamic
|
||||
web applications. It offers a familiar class-style OO framework, extensive
|
||||
Ajax support, higher-order programming constructs, and easy DOM manipulation.
|
||||
|
||||
=== Targeted platforms
|
||||
|
||||
Prototype currently targets the following platforms:
|
||||
|
||||
* Microsoft Internet Explorer for Windows, version 6.0 and higher
|
||||
* Mozilla Firefox 1.0/Mozilla 1.7 and higher
|
||||
* Apple Safari 1.2 and higher
|
||||
|
||||
== Using Prototype
|
||||
|
||||
To use Prototype in your application, download the latest release from the
|
||||
Prototype web site (http://prototype.conio.net/) and copy
|
||||
<tt>dist/prototype.js</tt> to a suitable location. Then include it in your HTML
|
||||
like so:
|
||||
|
||||
<script type="text/javascript" src="/path/to/prototype.js"></script>
|
||||
|
||||
=== Building Prototype from source
|
||||
|
||||
<tt>prototype.js</tt> is a composite file generated from many source files in
|
||||
the <tt>src/</tt> directory. To build Prototype, you'll need:
|
||||
|
||||
* a copy of the Prototype source tree, either from a distribution tarball or
|
||||
from the Subversion repository (see below)
|
||||
* Ruby 1.8.2 or higher (http://www.ruby-lang.org/)
|
||||
* Rake -- Ruby Make (http://rake.rubyforge.org/)
|
||||
* RDoc, if your Ruby distribution does not include it
|
||||
|
||||
From the root Prototype directory,
|
||||
|
||||
* <tt>rake dist</tt> will preprocess the Prototype source using ERB and
|
||||
generate the composite <tt>dist/prototype.js</tt>.
|
||||
* <tt>rake package</tt> will create a distribution tarball in the
|
||||
<tt>pkg/</tt> directory.
|
||||
|
||||
== Contributing to Prototype
|
||||
|
||||
Check out the Prototype source with
|
||||
$ svn co http://dev.rubyonrails.org/svn/rails/spinoffs/prototype/
|
||||
|
||||
Modify the files in <tt>src/</tt>, add tests in <tt>test/</tt> if possible,
|
||||
and post a patch to http://dev.rubyonrails.org/newticket (please make sure
|
||||
to pick "Prototype" as the component).
|
||||
|
||||
== Documentation
|
||||
|
||||
Please see the Prototype documentation on the script.aculo.us wiki:
|
||||
http://wiki.script.aculo.us/scriptaculous/show/Prototype
|
59
Rakefile
Normal file
59
Rakefile
Normal file
@ -0,0 +1,59 @@
|
||||
require 'rake'
|
||||
require 'rake/packagetask'
|
||||
|
||||
PROTOTYPE_ROOT = File.expand_path(File.dirname(__FILE__))
|
||||
PROTOTYPE_SRC_DIR = File.join(PROTOTYPE_ROOT, 'src')
|
||||
PROTOTYPE_DIST_DIR = File.join(PROTOTYPE_ROOT, 'dist')
|
||||
PROTOTYPE_PKG_DIR = File.join(PROTOTYPE_ROOT, 'pkg')
|
||||
PROTOTYPE_VERSION = '1.5.0'
|
||||
|
||||
task :default => [:dist, :package, :clean_package_source]
|
||||
|
||||
task :dist do
|
||||
$:.unshift File.join(PROTOTYPE_ROOT, 'lib')
|
||||
require 'protodoc'
|
||||
|
||||
Dir.chdir(PROTOTYPE_SRC_DIR) do
|
||||
File.open(File.join(PROTOTYPE_DIST_DIR, 'prototype.js'), 'w+') do |dist|
|
||||
dist << Protodoc::Preprocessor.new('prototype.js')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Rake::PackageTask.new('prototype', PROTOTYPE_VERSION) do |package|
|
||||
package.need_tar_gz = true
|
||||
package.package_dir = PROTOTYPE_PKG_DIR
|
||||
package.package_files.include(
|
||||
'[A-Z]*',
|
||||
'dist/prototype.js',
|
||||
'lib/**',
|
||||
'src/**',
|
||||
'test/**'
|
||||
)
|
||||
end
|
||||
|
||||
task :test => [:dist, :test_units]
|
||||
|
||||
require 'test/lib/jstest'
|
||||
desc "Runs all the JavaScript unit tests and collects the results"
|
||||
JavaScriptTestTask.new(:test_units) do |t|
|
||||
tests_to_run = ENV['TESTS'] && ENV['TESTS'].split(',')
|
||||
browsers_to_test = ENV['BROWSERS'] && ENV['BROWSERS'].split(',')
|
||||
|
||||
t.mount("/dist")
|
||||
t.mount("/test")
|
||||
|
||||
Dir["test/unit/*.html"].each do |test_file|
|
||||
test_file = "/#{test_file}"
|
||||
test_name = test_file[/.*\/(.+?)\.html/, 1]
|
||||
t.run(test_file) unless tests_to_run && !tests_to_run.include?(test_name)
|
||||
end
|
||||
|
||||
%w( safari firefox ie konqueror ).each do |browser|
|
||||
t.browser(browser.to_sym) unless browsers_to_test && !browsers_to_test.include?(browser)
|
||||
end
|
||||
end
|
||||
|
||||
task :clean_package_source do
|
||||
rm_rf File.join(PROTOTYPE_PKG_DIR, "prototype-#{PROTOTYPE_VERSION}")
|
||||
end
|
36
lib/protodoc.rb
Normal file
36
lib/protodoc.rb
Normal file
@ -0,0 +1,36 @@
|
||||
require 'erb'
|
||||
|
||||
class String
|
||||
def lines
|
||||
split $/
|
||||
end
|
||||
|
||||
def strip_whitespace_at_line_ends
|
||||
lines.map {|line| line.gsub(/\s+$/, '')} * $/
|
||||
end
|
||||
end
|
||||
|
||||
module Protodoc
|
||||
module Environment
|
||||
def include(*filenames)
|
||||
filenames.map {|filename| Preprocessor.new(filename).to_s}.join("\n")
|
||||
end
|
||||
end
|
||||
|
||||
class Preprocessor
|
||||
include Environment
|
||||
|
||||
def initialize(filename)
|
||||
@filename = File.expand_path(filename)
|
||||
@template = ERB.new(IO.read(@filename), nil, '%')
|
||||
end
|
||||
|
||||
def to_s
|
||||
@template.result(binding).strip_whitespace_at_line_ends
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if __FILE__ == $0
|
||||
print Protodoc::Preprocessor.new(ARGV.first)
|
||||
end
|
7
src/HEADER
Normal file
7
src/HEADER
Normal file
@ -0,0 +1,7 @@
|
||||
/* Prototype JavaScript framework, version <%= PROTOTYPE_VERSION %>
|
||||
* (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://prototype.conio.net/
|
||||
*
|
||||
/*--------------------------------------------------------------------------*/
|
313
src/ajax.js
Normal file
313
src/ajax.js
Normal file
@ -0,0 +1,313 @@
|
||||
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 (typeof responder[callback] == 'function') {
|
||||
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 = function() {};
|
||||
Ajax.Base.prototype = {
|
||||
setOptions: function(options) {
|
||||
this.options = {
|
||||
method: 'post',
|
||||
asynchronous: true,
|
||||
contentType: 'application/x-www-form-urlencoded',
|
||||
encoding: 'UTF-8',
|
||||
parameters: ''
|
||||
}
|
||||
Object.extend(this.options, options || {});
|
||||
|
||||
this.options.method = this.options.method.toLowerCase();
|
||||
if (typeof this.options.parameters == 'string')
|
||||
this.options.parameters = this.options.parameters.toQueryParams();
|
||||
}
|
||||
}
|
||||
|
||||
Ajax.Request = Class.create();
|
||||
Ajax.Request.Events =
|
||||
['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
|
||||
|
||||
Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
|
||||
_complete: false,
|
||||
|
||||
initialize: function(url, options) {
|
||||
this.transport = Ajax.getTransport();
|
||||
this.setOptions(options);
|
||||
this.request(url);
|
||||
},
|
||||
|
||||
request: function(url) {
|
||||
this.url = url;
|
||||
this.method = this.options.method;
|
||||
var params = this.options.parameters;
|
||||
|
||||
if (!['get', 'post'].include(this.method)) {
|
||||
// simulate other verbs over post
|
||||
params['_method'] = this.method;
|
||||
this.method = 'post';
|
||||
}
|
||||
|
||||
params = Hash.toQueryString(params);
|
||||
if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='
|
||||
|
||||
// when GET, append parameters to URL
|
||||
if (this.method == 'get' && params)
|
||||
this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
|
||||
|
||||
try {
|
||||
Ajax.Responders.dispatch('onCreate', this, this.transport);
|
||||
|
||||
this.transport.open(this.method.toUpperCase(), this.url,
|
||||
this.options.asynchronous);
|
||||
|
||||
if (this.options.asynchronous)
|
||||
setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
|
||||
|
||||
this.transport.onreadystatechange = this.onStateChange.bind(this);
|
||||
this.setRequestHeaders();
|
||||
|
||||
var body = this.method == 'post' ? (this.options.postBody || params) : null;
|
||||
|
||||
this.transport.send(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 (typeof extras.push == 'function')
|
||||
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)
|
||||
this.transport.setRequestHeader(name, headers[name]);
|
||||
},
|
||||
|
||||
success: function() {
|
||||
return !this.transport.status
|
||||
|| (this.transport.status >= 200 && this.transport.status < 300);
|
||||
},
|
||||
|
||||
respondToReadyState: function(readyState) {
|
||||
var state = Ajax.Request.Events[readyState];
|
||||
var transport = this.transport, json = this.evalJSON();
|
||||
|
||||
if (state == 'Complete') {
|
||||
try {
|
||||
this._complete = true;
|
||||
(this.options['on' + this.transport.status]
|
||||
|| this.options['on' + (this.success() ? 'Success' : 'Failure')]
|
||||
|| Prototype.emptyFunction)(transport, json);
|
||||
} catch (e) {
|
||||
this.dispatchException(e);
|
||||
}
|
||||
|
||||
if ((this.getHeader('Content-type') || 'text/javascript').strip().
|
||||
match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
|
||||
this.evalResponse();
|
||||
}
|
||||
|
||||
try {
|
||||
(this.options['on' + state] || Prototype.emptyFunction)(transport, json);
|
||||
Ajax.Responders.dispatch('on' + state, this, transport, json);
|
||||
} 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 }
|
||||
},
|
||||
|
||||
evalJSON: function() {
|
||||
try {
|
||||
var json = this.getHeader('X-JSON');
|
||||
return json ? eval('(' + json + ')') : null;
|
||||
} catch (e) { return null }
|
||||
},
|
||||
|
||||
evalResponse: function() {
|
||||
try {
|
||||
return eval(this.transport.responseText);
|
||||
} catch (e) {
|
||||
this.dispatchException(e);
|
||||
}
|
||||
},
|
||||
|
||||
dispatchException: function(exception) {
|
||||
(this.options.onException || Prototype.emptyFunction)(this, exception);
|
||||
Ajax.Responders.dispatch('onException', this, exception);
|
||||
}
|
||||
});
|
||||
|
||||
Ajax.Updater = Class.create();
|
||||
|
||||
Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
|
||||
initialize: function(container, url, options) {
|
||||
this.container = {
|
||||
success: (container.success || container),
|
||||
failure: (container.failure || (container.success ? null : container))
|
||||
}
|
||||
|
||||
this.transport = Ajax.getTransport();
|
||||
this.setOptions(options);
|
||||
|
||||
var onComplete = this.options.onComplete || Prototype.emptyFunction;
|
||||
this.options.onComplete = (function(transport, param) {
|
||||
this.updateContent();
|
||||
onComplete(transport, param);
|
||||
}).bind(this);
|
||||
|
||||
this.request(url);
|
||||
},
|
||||
|
||||
updateContent: function() {
|
||||
var receiver = this.container[this.success() ? 'success' : 'failure'];
|
||||
var response = this.transport.responseText;
|
||||
|
||||
if (!this.options.evalScripts) response = response.stripScripts();
|
||||
|
||||
if (receiver = $(receiver)) {
|
||||
if (this.options.insertion)
|
||||
new this.options.insertion(receiver, response);
|
||||
else
|
||||
receiver.update(response);
|
||||
}
|
||||
|
||||
if (this.success()) {
|
||||
if (this.onComplete)
|
||||
setTimeout(this.onComplete.bind(this), 10);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ajax.PeriodicalUpdater = Class.create();
|
||||
Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
|
||||
initialize: function(container, url, options) {
|
||||
this.setOptions(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(request) {
|
||||
if (this.options.decay) {
|
||||
this.decay = (request.responseText == this.lastText ?
|
||||
this.decay * this.options.decay : 1);
|
||||
|
||||
this.lastText = request.responseText;
|
||||
}
|
||||
this.timer = setTimeout(this.onTimerEvent.bind(this),
|
||||
this.decay * this.frequency * 1000);
|
||||
},
|
||||
|
||||
onTimerEvent: function() {
|
||||
this.updater = new Ajax.Updater(this.container, this.url, this.options);
|
||||
}
|
||||
});
|
111
src/array.js
Normal file
111
src/array.js
Normal file
@ -0,0 +1,111 @@
|
||||
var $A = Array.from = function(iterable) {
|
||||
if (!iterable) return [];
|
||||
if (iterable.toArray) {
|
||||
return iterable.toArray();
|
||||
} else {
|
||||
var results = [];
|
||||
for (var i = 0, length = iterable.length; i < length; i++)
|
||||
results.push(iterable[i]);
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
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(value && value.constructor == Array ?
|
||||
value.flatten() : [value]);
|
||||
});
|
||||
},
|
||||
|
||||
without: function() {
|
||||
var values = $A(arguments);
|
||||
return this.select(function(value) {
|
||||
return !values.include(value);
|
||||
});
|
||||
},
|
||||
|
||||
indexOf: function(object) {
|
||||
for (var i = 0, length = this.length; i < length; i++)
|
||||
if (this[i] == object) return i;
|
||||
return -1;
|
||||
},
|
||||
|
||||
reverse: function(inline) {
|
||||
return (inline !== false ? this : this.toArray())._reverse();
|
||||
},
|
||||
|
||||
reduce: function() {
|
||||
return this.length > 1 ? this : this[0];
|
||||
},
|
||||
|
||||
uniq: function() {
|
||||
return this.inject([], function(array, value) {
|
||||
return array.include(value) ? array : array.concat([value]);
|
||||
});
|
||||
},
|
||||
|
||||
clone: function() {
|
||||
return [].concat(this);
|
||||
},
|
||||
|
||||
size: function() {
|
||||
return this.length;
|
||||
},
|
||||
|
||||
inspect: function() {
|
||||
return '[' + this.map(Object.inspect).join(', ') + ']';
|
||||
}
|
||||
});
|
||||
|
||||
Array.prototype.toArray = Array.prototype.clone;
|
||||
|
||||
function $w(string){
|
||||
string = string.strip();
|
||||
return string ? string.split(/\s+/) : [];
|
||||
}
|
||||
|
||||
if(window.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(arguments[i].constructor == Array) {
|
||||
for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
|
||||
array.push(arguments[i][j]);
|
||||
} else {
|
||||
array.push(arguments[i]);
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
128
src/base.js
Normal file
128
src/base.js
Normal file
@ -0,0 +1,128 @@
|
||||
var Class = {
|
||||
create: function() {
|
||||
return function() {
|
||||
this.initialize.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var Abstract = new Object();
|
||||
|
||||
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;
|
||||
}
|
||||
},
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
Function.prototype.bind = function() {
|
||||
var __method = this, args = $A(arguments), object = args.shift();
|
||||
return function() {
|
||||
return __method.apply(object, args.concat($A(arguments)));
|
||||
}
|
||||
}
|
||||
|
||||
Function.prototype.bindAsEventListener = function(object) {
|
||||
var __method = this, args = $A(arguments), object = args.shift();
|
||||
return function(event) {
|
||||
return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
|
||||
}
|
||||
}
|
||||
|
||||
Object.extend(Number.prototype, {
|
||||
toColorPart: function() {
|
||||
var digits = this.toString(16);
|
||||
if (this < 16) return '0' + digits;
|
||||
return digits;
|
||||
},
|
||||
|
||||
succ: function() {
|
||||
return this + 1;
|
||||
},
|
||||
|
||||
times: function(iterator) {
|
||||
$R(0, this, true).each(iterator);
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
var PeriodicalExecuter = Class.create();
|
||||
PeriodicalExecuter.prototype = {
|
||||
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);
|
||||
},
|
||||
|
||||
stop: function() {
|
||||
if (!this.timer) return;
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
},
|
||||
|
||||
onTimerEvent: function() {
|
||||
if (!this.currentlyExecuting) {
|
||||
try {
|
||||
this.currentlyExecuting = true;
|
||||
this.callback(this);
|
||||
} finally {
|
||||
this.currentlyExecuting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
678
src/dom.js
Normal file
678
src/dom.js
Normal file
@ -0,0 +1,678 @@
|
||||
function $(element) {
|
||||
if (arguments.length > 1) {
|
||||
for (var i = 0, elements = [], length = arguments.length; i < length; i++)
|
||||
elements.push($(arguments[i]));
|
||||
return elements;
|
||||
}
|
||||
if (typeof element == 'string')
|
||||
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(query.snapshotItem(i));
|
||||
return results;
|
||||
};
|
||||
}
|
||||
|
||||
document.getElementsByClassName = function(className, parentElement) {
|
||||
if (Prototype.BrowserFeatures.XPath) {
|
||||
var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
|
||||
return document._getElementsByXPath(q, parentElement);
|
||||
} else {
|
||||
var children = ($(parentElement) || document.body).getElementsByTagName('*');
|
||||
var elements = [], child;
|
||||
for (var i = 0, length = children.length; i < length; i++) {
|
||||
child = children[i];
|
||||
if (Element.hasClassName(child, className))
|
||||
elements.push(Element.extend(child));
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
if (!window.Element)
|
||||
var Element = new Object();
|
||||
|
||||
Element.extend = function(element) {
|
||||
if (!element || _nativeExtensions || element.nodeType == 3) return element;
|
||||
|
||||
if (!element._extended && element.tagName && element != window) {
|
||||
var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
|
||||
|
||||
if (element.tagName == 'FORM')
|
||||
Object.extend(methods, Form.Methods);
|
||||
if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
|
||||
Object.extend(methods, Form.Element.Methods);
|
||||
|
||||
Object.extend(methods, Element.Methods.Simulated);
|
||||
|
||||
for (var property in methods) {
|
||||
var value = methods[property];
|
||||
if (typeof value == 'function' && !(property in element))
|
||||
element[property] = cache.findOrStore(value);
|
||||
}
|
||||
}
|
||||
|
||||
element._extended = true;
|
||||
return element;
|
||||
};
|
||||
|
||||
Element.extend.cache = {
|
||||
findOrStore: function(value) {
|
||||
return this[value] = this[value] || function() {
|
||||
return value.apply(null, [this].concat($A(arguments)));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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, html) {
|
||||
html = typeof html == 'undefined' ? '' : html.toString();
|
||||
$(element).innerHTML = html.stripScripts();
|
||||
setTimeout(function() {html.evalScripts()}, 10);
|
||||
return element;
|
||||
},
|
||||
|
||||
replace: function(element, html) {
|
||||
element = $(element);
|
||||
html = typeof html == 'undefined' ? '' : html.toString();
|
||||
if (element.outerHTML) {
|
||||
element.outerHTML = html.stripScripts();
|
||||
} else {
|
||||
var range = element.ownerDocument.createRange();
|
||||
range.selectNodeContents(element);
|
||||
element.parentNode.replaceChild(
|
||||
range.createContextualFragment(html.stripScripts()), element);
|
||||
}
|
||||
setTimeout(function() {html.evalScripts()}, 10);
|
||||
return element;
|
||||
},
|
||||
|
||||
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('*'));
|
||||
},
|
||||
|
||||
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 (typeof selector == 'string')
|
||||
selector = new Selector(selector);
|
||||
return selector.match($(element));
|
||||
},
|
||||
|
||||
up: function(element, expression, index) {
|
||||
return Selector.findElement($(element).ancestors(), expression, index);
|
||||
},
|
||||
|
||||
down: function(element, expression, index) {
|
||||
return Selector.findElement($(element).descendants(), expression, index);
|
||||
},
|
||||
|
||||
previous: function(element, expression, index) {
|
||||
return Selector.findElement($(element).previousSiblings(), expression, index);
|
||||
},
|
||||
|
||||
next: function(element, expression, index) {
|
||||
return Selector.findElement($(element).nextSiblings(), expression, index);
|
||||
},
|
||||
|
||||
getElementsBySelector: function() {
|
||||
var args = $A(arguments), element = $(args.shift());
|
||||
return Selector.findChildElements(element, args);
|
||||
},
|
||||
|
||||
getElementsByClassName: function(element, className) {
|
||||
return document.getElementsByClassName(className, element);
|
||||
},
|
||||
|
||||
readAttribute: function(element, name) {
|
||||
element = $(element);
|
||||
if (document.all && !window.opera) {
|
||||
var t = Element._attributeTranslations;
|
||||
if (t.values[name]) return t.values[name](element, name);
|
||||
if (t.names[name]) name = t.names[name];
|
||||
var attribute = element.attributes[name];
|
||||
if(attribute) return attribute.nodeValue;
|
||||
}
|
||||
return element.getAttribute(name);
|
||||
},
|
||||
|
||||
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;
|
||||
if (elementClassName.length == 0) return false;
|
||||
if (elementClassName == className ||
|
||||
elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
|
||||
addClassName: function(element, className) {
|
||||
if (!(element = $(element))) return;
|
||||
Element.classNames(element).add(className);
|
||||
return element;
|
||||
},
|
||||
|
||||
removeClassName: function(element, className) {
|
||||
if (!(element = $(element))) return;
|
||||
Element.classNames(element).remove(className);
|
||||
return element;
|
||||
},
|
||||
|
||||
toggleClassName: function(element, className) {
|
||||
if (!(element = $(element))) return;
|
||||
Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
|
||||
return element;
|
||||
},
|
||||
|
||||
observe: function() {
|
||||
Event.observe.apply(Event, arguments);
|
||||
return $A(arguments).first();
|
||||
},
|
||||
|
||||
stopObserving: function() {
|
||||
Event.stopObserving.apply(Event, arguments);
|
||||
return $A(arguments).first();
|
||||
},
|
||||
|
||||
// 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.match(/^\s*$/);
|
||||
},
|
||||
|
||||
descendantOf: function(element, ancestor) {
|
||||
element = $(element), ancestor = $(ancestor);
|
||||
while (element = element.parentNode)
|
||||
if (element == ancestor) return true;
|
||||
return false;
|
||||
},
|
||||
|
||||
scrollTo: function(element) {
|
||||
element = $(element);
|
||||
var pos = Position.cumulativeOffset(element);
|
||||
window.scrollTo(pos[0], pos[1]);
|
||||
return element;
|
||||
},
|
||||
|
||||
getStyle: function(element, style) {
|
||||
element = $(element);
|
||||
if (['float','cssFloat'].include(style))
|
||||
style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
|
||||
style = style.camelize();
|
||||
var value = element.style[style];
|
||||
if (!value) {
|
||||
if (document.defaultView && document.defaultView.getComputedStyle) {
|
||||
var css = document.defaultView.getComputedStyle(element, null);
|
||||
value = css ? css[style] : null;
|
||||
} else if (element.currentStyle) {
|
||||
value = element.currentStyle[style];
|
||||
}
|
||||
}
|
||||
|
||||
if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
|
||||
value = element['offset'+style.capitalize()] + 'px';
|
||||
|
||||
if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
|
||||
if (Element.getStyle(element, 'position') == 'static') value = 'auto';
|
||||
if(style == 'opacity') {
|
||||
if(value) return parseFloat(value);
|
||||
if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
|
||||
if(value[1]) return parseFloat(value[1]) / 100;
|
||||
return 1.0;
|
||||
}
|
||||
return value == 'auto' ? null : value;
|
||||
},
|
||||
|
||||
setStyle: function(element, style) {
|
||||
element = $(element);
|
||||
for (var name in style) {
|
||||
var value = style[name];
|
||||
if(name == 'opacity') {
|
||||
if (value == 1) {
|
||||
value = (/Gecko/.test(navigator.userAgent) &&
|
||||
!/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
|
||||
if(/MSIE/.test(navigator.userAgent) && !window.opera)
|
||||
element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
|
||||
} else if(value == '') {
|
||||
if(/MSIE/.test(navigator.userAgent) && !window.opera)
|
||||
element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
|
||||
} else {
|
||||
if(value < 0.00001) value = 0;
|
||||
if(/MSIE/.test(navigator.userAgent) && !window.opera)
|
||||
element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
|
||||
'alpha(opacity='+value*100+')';
|
||||
}
|
||||
} else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
|
||||
element.style[name.camelize()] = 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.style.overflow || 'auto';
|
||||
if ((Element.getStyle(element, 'overflow') || 'visible') != '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;
|
||||
}
|
||||
};
|
||||
|
||||
Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
|
||||
|
||||
Element._attributeTranslations = {};
|
||||
|
||||
Element._attributeTranslations.names = {
|
||||
colspan: "colSpan",
|
||||
rowspan: "rowSpan",
|
||||
valign: "vAlign",
|
||||
datetime: "dateTime",
|
||||
accesskey: "accessKey",
|
||||
tabindex: "tabIndex",
|
||||
enctype: "encType",
|
||||
maxlength: "maxLength",
|
||||
readonly: "readOnly",
|
||||
longdesc: "longDesc"
|
||||
};
|
||||
|
||||
Element._attributeTranslations.values = {
|
||||
_getAttr: function(element, attribute) {
|
||||
return element.getAttribute(attribute, 2);
|
||||
},
|
||||
|
||||
_flag: function(element, attribute) {
|
||||
return $(element).hasAttribute(attribute) ? attribute : null;
|
||||
},
|
||||
|
||||
style: function(element) {
|
||||
return element.style.cssText.toLowerCase();
|
||||
},
|
||||
|
||||
title: function(element) {
|
||||
var node = element.getAttributeNode('title');
|
||||
return node.specified ? node.nodeValue : null;
|
||||
}
|
||||
};
|
||||
|
||||
Object.extend(Element._attributeTranslations.values, {
|
||||
href: Element._attributeTranslations.values._getAttr,
|
||||
src: Element._attributeTranslations.values._getAttr,
|
||||
disabled: Element._attributeTranslations.values._flag,
|
||||
checked: Element._attributeTranslations.values._flag,
|
||||
readonly: Element._attributeTranslations.values._flag,
|
||||
multiple: Element._attributeTranslations.values._flag
|
||||
});
|
||||
|
||||
Element.Methods.Simulated = {
|
||||
hasAttribute: function(element, attribute) {
|
||||
var t = Element._attributeTranslations;
|
||||
attribute = t.names[attribute] || attribute;
|
||||
return $(element).getAttributeNode(attribute).specified;
|
||||
}
|
||||
};
|
||||
|
||||
// IE is missing .innerHTML support for TABLE-related elements
|
||||
if (document.all && !window.opera){
|
||||
Element.Methods.update = function(element, html) {
|
||||
element = $(element);
|
||||
html = typeof html == 'undefined' ? '' : html.toString();
|
||||
var tagName = element.tagName.toUpperCase();
|
||||
if (['THEAD','TBODY','TR','TD'].include(tagName)) {
|
||||
var div = document.createElement('div');
|
||||
switch (tagName) {
|
||||
case 'THEAD':
|
||||
case 'TBODY':
|
||||
div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></table>';
|
||||
depth = 2;
|
||||
break;
|
||||
case 'TR':
|
||||
div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></tbody></table>';
|
||||
depth = 3;
|
||||
break;
|
||||
case 'TD':
|
||||
div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</td></tr></tbody></table>';
|
||||
depth = 4;
|
||||
}
|
||||
$A(element.childNodes).each(function(node){
|
||||
element.removeChild(node)
|
||||
});
|
||||
depth.times(function(){ div = div.firstChild });
|
||||
|
||||
$A(div.childNodes).each(
|
||||
function(node){ element.appendChild(node) });
|
||||
} else {
|
||||
element.innerHTML = html.stripScripts();
|
||||
}
|
||||
setTimeout(function() {html.evalScripts()}, 10);
|
||||
return element;
|
||||
}
|
||||
};
|
||||
|
||||
Object.extend(Element, Element.Methods);
|
||||
|
||||
var _nativeExtensions = false;
|
||||
|
||||
if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
|
||||
['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
|
||||
var className = 'HTML' + tag + 'Element';
|
||||
if(window[className]) return;
|
||||
var klass = window[className] = {};
|
||||
klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
|
||||
});
|
||||
|
||||
Element.addMethods = function(methods) {
|
||||
Object.extend(Element.Methods, methods || {});
|
||||
|
||||
function copy(methods, destination, onlyIfAbsent) {
|
||||
onlyIfAbsent = onlyIfAbsent || false;
|
||||
var cache = Element.extend.cache;
|
||||
for (var property in methods) {
|
||||
var value = methods[property];
|
||||
if (!onlyIfAbsent || !(property in destination))
|
||||
destination[property] = cache.findOrStore(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof HTMLElement != 'undefined') {
|
||||
copy(Element.Methods, HTMLElement.prototype);
|
||||
copy(Element.Methods.Simulated, HTMLElement.prototype, true);
|
||||
copy(Form.Methods, HTMLFormElement.prototype);
|
||||
[HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
|
||||
copy(Form.Element.Methods, klass.prototype);
|
||||
});
|
||||
_nativeExtensions = true;
|
||||
}
|
||||
}
|
||||
|
||||
var Toggle = new Object();
|
||||
Toggle.display = Element.toggle;
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
Abstract.Insertion = function(adjacency) {
|
||||
this.adjacency = adjacency;
|
||||
}
|
||||
|
||||
Abstract.Insertion.prototype = {
|
||||
initialize: function(element, content) {
|
||||
this.element = $(element);
|
||||
this.content = content.stripScripts();
|
||||
|
||||
if (this.adjacency && this.element.insertAdjacentHTML) {
|
||||
try {
|
||||
this.element.insertAdjacentHTML(this.adjacency, this.content);
|
||||
} catch (e) {
|
||||
var tagName = this.element.tagName.toUpperCase();
|
||||
if (['TBODY', 'TR'].include(tagName)) {
|
||||
this.insertContent(this.contentFromAnonymousTable());
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.range = this.element.ownerDocument.createRange();
|
||||
if (this.initializeRange) this.initializeRange();
|
||||
this.insertContent([this.range.createContextualFragment(this.content)]);
|
||||
}
|
||||
|
||||
setTimeout(function() {content.evalScripts()}, 10);
|
||||
},
|
||||
|
||||
contentFromAnonymousTable: function() {
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
|
||||
return $A(div.childNodes[0].childNodes[0].childNodes);
|
||||
}
|
||||
}
|
||||
|
||||
var Insertion = new Object();
|
||||
|
||||
Insertion.Before = Class.create();
|
||||
Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
|
||||
initializeRange: function() {
|
||||
this.range.setStartBefore(this.element);
|
||||
},
|
||||
|
||||
insertContent: function(fragments) {
|
||||
fragments.each((function(fragment) {
|
||||
this.element.parentNode.insertBefore(fragment, this.element);
|
||||
}).bind(this));
|
||||
}
|
||||
});
|
||||
|
||||
Insertion.Top = Class.create();
|
||||
Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
|
||||
initializeRange: function() {
|
||||
this.range.selectNodeContents(this.element);
|
||||
this.range.collapse(true);
|
||||
},
|
||||
|
||||
insertContent: function(fragments) {
|
||||
fragments.reverse(false).each((function(fragment) {
|
||||
this.element.insertBefore(fragment, this.element.firstChild);
|
||||
}).bind(this));
|
||||
}
|
||||
});
|
||||
|
||||
Insertion.Bottom = Class.create();
|
||||
Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
|
||||
initializeRange: function() {
|
||||
this.range.selectNodeContents(this.element);
|
||||
this.range.collapse(this.element);
|
||||
},
|
||||
|
||||
insertContent: function(fragments) {
|
||||
fragments.each((function(fragment) {
|
||||
this.element.appendChild(fragment);
|
||||
}).bind(this));
|
||||
}
|
||||
});
|
||||
|
||||
Insertion.After = Class.create();
|
||||
Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
|
||||
initializeRange: function() {
|
||||
this.range.setStartAfter(this.element);
|
||||
},
|
||||
|
||||
insertContent: function(fragments) {
|
||||
fragments.each((function(fragment) {
|
||||
this.element.parentNode.insertBefore(fragment,
|
||||
this.element.nextSibling);
|
||||
}).bind(this));
|
||||
}
|
||||
});
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
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);
|
202
src/enumerable.js
Normal file
202
src/enumerable.js
Normal file
@ -0,0 +1,202 @@
|
||||
var $break = new Object();
|
||||
var $continue = new Object();
|
||||
|
||||
var Enumerable = {
|
||||
each: function(iterator) {
|
||||
var index = 0;
|
||||
try {
|
||||
this._each(function(value) {
|
||||
try {
|
||||
iterator(value, index++);
|
||||
} catch (e) {
|
||||
if (e != $continue) throw e;
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
if (e != $break) throw e;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
eachSlice: function(number, iterator) {
|
||||
var index = -number, slices = [], array = this.toArray();
|
||||
while ((index += number) < array.length)
|
||||
slices.push(array.slice(index, index+number));
|
||||
return slices.map(iterator);
|
||||
},
|
||||
|
||||
all: function(iterator) {
|
||||
var result = true;
|
||||
this.each(function(value, index) {
|
||||
result = result && !!(iterator || Prototype.K)(value, index);
|
||||
if (!result) throw $break;
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
any: function(iterator) {
|
||||
var result = false;
|
||||
this.each(function(value, index) {
|
||||
if (result = !!(iterator || Prototype.K)(value, index))
|
||||
throw $break;
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
collect: function(iterator) {
|
||||
var results = [];
|
||||
this.each(function(value, index) {
|
||||
results.push((iterator || Prototype.K)(value, index));
|
||||
});
|
||||
return results;
|
||||
},
|
||||
|
||||
detect: function(iterator) {
|
||||
var result;
|
||||
this.each(function(value, index) {
|
||||
if (iterator(value, index)) {
|
||||
result = value;
|
||||
throw $break;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
findAll: function(iterator) {
|
||||
var results = [];
|
||||
this.each(function(value, index) {
|
||||
if (iterator(value, index))
|
||||
results.push(value);
|
||||
});
|
||||
return results;
|
||||
},
|
||||
|
||||
grep: function(pattern, iterator) {
|
||||
var results = [];
|
||||
this.each(function(value, index) {
|
||||
var stringValue = value.toString();
|
||||
if (stringValue.match(pattern))
|
||||
results.push((iterator || Prototype.K)(value, index));
|
||||
})
|
||||
return results;
|
||||
},
|
||||
|
||||
include: function(object) {
|
||||
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) {
|
||||
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) {
|
||||
var result;
|
||||
this.each(function(value, index) {
|
||||
value = (iterator || Prototype.K)(value, index);
|
||||
if (result == undefined || value >= result)
|
||||
result = value;
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
min: function(iterator) {
|
||||
var result;
|
||||
this.each(function(value, index) {
|
||||
value = (iterator || Prototype.K)(value, index);
|
||||
if (result == undefined || value < result)
|
||||
result = value;
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
partition: function(iterator) {
|
||||
var trues = [], falses = [];
|
||||
this.each(function(value, index) {
|
||||
((iterator || Prototype.K)(value, index) ?
|
||||
trues : falses).push(value);
|
||||
});
|
||||
return [trues, falses];
|
||||
},
|
||||
|
||||
pluck: function(property) {
|
||||
var results = [];
|
||||
this.each(function(value, index) {
|
||||
results.push(value[property]);
|
||||
});
|
||||
return results;
|
||||
},
|
||||
|
||||
reject: function(iterator) {
|
||||
var results = [];
|
||||
this.each(function(value, index) {
|
||||
if (!iterator(value, index))
|
||||
results.push(value);
|
||||
});
|
||||
return results;
|
||||
},
|
||||
|
||||
sortBy: function(iterator) {
|
||||
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 (typeof args.last() == 'function')
|
||||
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,
|
||||
member: Enumerable.include,
|
||||
entries: Enumerable.toArray
|
||||
});
|
114
src/event.js
Normal file
114
src/event.js
Normal file
@ -0,0 +1,114 @@
|
||||
if (!window.Event) {
|
||||
var Event = new Object();
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
element: function(event) {
|
||||
return event.target || event.srcElement;
|
||||
},
|
||||
|
||||
isLeftClick: function(event) {
|
||||
return (((event.which) && (event.which == 1)) ||
|
||||
((event.button) && (event.button == 1)));
|
||||
},
|
||||
|
||||
pointerX: function(event) {
|
||||
return event.pageX || (event.clientX +
|
||||
(document.documentElement.scrollLeft || document.body.scrollLeft));
|
||||
},
|
||||
|
||||
pointerY: function(event) {
|
||||
return event.pageY || (event.clientY +
|
||||
(document.documentElement.scrollTop || document.body.scrollTop));
|
||||
},
|
||||
|
||||
stop: function(event) {
|
||||
if (event.preventDefault) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
} else {
|
||||
event.returnValue = false;
|
||||
event.cancelBubble = true;
|
||||
}
|
||||
},
|
||||
|
||||
// find the first node with the given tagName, starting from the
|
||||
// node the event was triggered on; traverses the DOM upwards
|
||||
findElement: function(event, tagName) {
|
||||
var element = Event.element(event);
|
||||
while (element.parentNode && (!element.tagName ||
|
||||
(element.tagName.toUpperCase() != tagName.toUpperCase())))
|
||||
element = element.parentNode;
|
||||
return element;
|
||||
},
|
||||
|
||||
observers: false,
|
||||
|
||||
_observeAndCache: function(element, name, observer, useCapture) {
|
||||
if (!this.observers) this.observers = [];
|
||||
if (element.addEventListener) {
|
||||
this.observers.push([element, name, observer, useCapture]);
|
||||
element.addEventListener(name, observer, useCapture);
|
||||
} else if (element.attachEvent) {
|
||||
this.observers.push([element, name, observer, useCapture]);
|
||||
element.attachEvent('on' + name, observer);
|
||||
}
|
||||
},
|
||||
|
||||
unloadCache: function() {
|
||||
if (!Event.observers) return;
|
||||
for (var i = 0, length = Event.observers.length; i < length; i++) {
|
||||
Event.stopObserving.apply(this, Event.observers[i]);
|
||||
Event.observers[i][0] = null;
|
||||
}
|
||||
Event.observers = false;
|
||||
},
|
||||
|
||||
observe: function(element, name, observer, useCapture) {
|
||||
element = $(element);
|
||||
useCapture = useCapture || false;
|
||||
|
||||
if (name == 'keypress' &&
|
||||
(navigator.appVersion.match(/Konqueror|Safari|KHTML/)
|
||||
|| element.attachEvent))
|
||||
name = 'keydown';
|
||||
|
||||
Event._observeAndCache(element, name, observer, useCapture);
|
||||
},
|
||||
|
||||
stopObserving: function(element, name, observer, useCapture) {
|
||||
element = $(element);
|
||||
useCapture = useCapture || false;
|
||||
|
||||
if (name == 'keypress' &&
|
||||
(navigator.appVersion.match(/Konqueror|Safari|KHTML/)
|
||||
|| element.detachEvent))
|
||||
name = 'keydown';
|
||||
|
||||
if (element.removeEventListener) {
|
||||
element.removeEventListener(name, observer, useCapture);
|
||||
} else if (element.detachEvent) {
|
||||
try {
|
||||
element.detachEvent('on' + name, observer);
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* prevent memory leaks in IE */
|
||||
if (navigator.appVersion.match(/\bMSIE\b/))
|
||||
Event.observe(window, 'unload', Event.unloadCache, false);
|
305
src/form.js
Normal file
305
src/form.js
Normal file
@ -0,0 +1,305 @@
|
||||
var Form = {
|
||||
reset: function(form) {
|
||||
$(form).reset();
|
||||
return form;
|
||||
},
|
||||
|
||||
serializeElements: function(elements, getHash) {
|
||||
var data = elements.inject({}, function(result, element) {
|
||||
if (!element.disabled && element.name) {
|
||||
var key = element.name, value = $(element).getValue();
|
||||
if (value != undefined) {
|
||||
if (result[key]) {
|
||||
if (result[key].constructor != Array) result[key] = [result[key]];
|
||||
result[key].push(value);
|
||||
}
|
||||
else result[key] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
return getHash ? data : Hash.toQueryString(data);
|
||||
}
|
||||
};
|
||||
|
||||
Form.Methods = {
|
||||
serialize: function(form, getHash) {
|
||||
return Form.serializeElements(Form.getElements(form), getHash);
|
||||
},
|
||||
|
||||
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().each(function(element) {
|
||||
element.blur();
|
||||
element.disabled = 'true';
|
||||
});
|
||||
return form;
|
||||
},
|
||||
|
||||
enable: function(form) {
|
||||
form = $(form);
|
||||
form.getElements().each(function(element) {
|
||||
element.disabled = '';
|
||||
});
|
||||
return form;
|
||||
},
|
||||
|
||||
findFirstElement: function(form) {
|
||||
return $(form).getElements().find(function(element) {
|
||||
return element.type != 'hidden' && !element.disabled &&
|
||||
['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
|
||||
});
|
||||
},
|
||||
|
||||
focusFirstElement: function(form) {
|
||||
form = $(form);
|
||||
form.findFirstElement().activate();
|
||||
return form;
|
||||
}
|
||||
}
|
||||
|
||||
Object.extend(Form, Form.Methods);
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
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) {
|
||||
var pair = {};
|
||||
pair[element.name] = value;
|
||||
return Hash.toQueryString(pair);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
getValue: function(element) {
|
||||
element = $(element);
|
||||
var method = element.tagName.toLowerCase();
|
||||
return Form.Element.Serializers[method](element);
|
||||
},
|
||||
|
||||
clear: function(element) {
|
||||
$(element).value = '';
|
||||
return element;
|
||||
},
|
||||
|
||||
present: function(element) {
|
||||
return $(element).value != '';
|
||||
},
|
||||
|
||||
activate: function(element) {
|
||||
element = $(element);
|
||||
element.focus();
|
||||
if (element.select && ( element.tagName.toLowerCase() != 'input' ||
|
||||
!['button', 'reset', 'submit'].include(element.type) ) )
|
||||
element.select();
|
||||
return element;
|
||||
},
|
||||
|
||||
disable: function(element) {
|
||||
element = $(element);
|
||||
element.disabled = true;
|
||||
return element;
|
||||
},
|
||||
|
||||
enable: function(element) {
|
||||
element = $(element);
|
||||
element.blur();
|
||||
element.disabled = false;
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
Object.extend(Form.Element, Form.Element.Methods);
|
||||
var Field = Form.Element;
|
||||
var $F = Form.Element.getValue;
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
Form.Element.Serializers = {
|
||||
input: function(element) {
|
||||
switch (element.type.toLowerCase()) {
|
||||
case 'checkbox':
|
||||
case 'radio':
|
||||
return Form.Element.Serializers.inputSelector(element);
|
||||
default:
|
||||
return Form.Element.Serializers.textarea(element);
|
||||
}
|
||||
},
|
||||
|
||||
inputSelector: function(element) {
|
||||
return element.checked ? element.value : null;
|
||||
},
|
||||
|
||||
textarea: function(element) {
|
||||
return element.value;
|
||||
},
|
||||
|
||||
select: function(element) {
|
||||
return this[element.type == 'select-one' ?
|
||||
'selectOne' : 'selectMany'](element);
|
||||
},
|
||||
|
||||
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 = function() {}
|
||||
Abstract.TimedObserver.prototype = {
|
||||
initialize: function(element, frequency, callback) {
|
||||
this.frequency = frequency;
|
||||
this.element = $(element);
|
||||
this.callback = callback;
|
||||
|
||||
this.lastValue = this.getValue();
|
||||
this.registerCallback();
|
||||
},
|
||||
|
||||
registerCallback: function() {
|
||||
setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
|
||||
},
|
||||
|
||||
onTimerEvent: function() {
|
||||
var value = this.getValue();
|
||||
var changed = ('string' == typeof this.lastValue && 'string' == typeof value
|
||||
? this.lastValue != value : String(this.lastValue) != String(value));
|
||||
if (changed) {
|
||||
this.callback(this.element, value);
|
||||
this.lastValue = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Form.Element.Observer = Class.create();
|
||||
Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
|
||||
getValue: function() {
|
||||
return Form.Element.getValue(this.element);
|
||||
}
|
||||
});
|
||||
|
||||
Form.Observer = Class.create();
|
||||
Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
|
||||
getValue: function() {
|
||||
return Form.serialize(this.element);
|
||||
}
|
||||
});
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
Abstract.EventObserver = function() {}
|
||||
Abstract.EventObserver.prototype = {
|
||||
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.bind(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();
|
||||
Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
|
||||
getValue: function() {
|
||||
return Form.Element.getValue(this.element);
|
||||
}
|
||||
});
|
||||
|
||||
Form.EventObserver = Class.create();
|
||||
Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
|
||||
getValue: function() {
|
||||
return Form.serialize(this.element);
|
||||
}
|
||||
});
|
||||
|
91
src/hash.js
Normal file
91
src/hash.js
Normal file
@ -0,0 +1,91 @@
|
||||
var Hash = function(obj) {
|
||||
Object.extend(this, obj || {});
|
||||
};
|
||||
|
||||
Object.extend(Hash, {
|
||||
toQueryString: function(obj) {
|
||||
var parts = [];
|
||||
|
||||
this.prototype._each.call(obj, function(pair) {
|
||||
if (!pair.key) return;
|
||||
|
||||
if (pair.value && pair.value.constructor == Array) {
|
||||
var values = pair.value.compact();
|
||||
if (values.length < 2) pair.value = values.reduce();
|
||||
else {
|
||||
key = encodeURIComponent(pair.key);
|
||||
values.each(function(value) {
|
||||
value = value != undefined ? encodeURIComponent(value) : '';
|
||||
parts.push(key + '=' + encodeURIComponent(value));
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (pair.value == undefined) pair[1] = '';
|
||||
parts.push(pair.map(encodeURIComponent).join('='));
|
||||
});
|
||||
|
||||
return parts.join('&');
|
||||
}
|
||||
});
|
||||
|
||||
Object.extend(Hash.prototype, Enumerable);
|
||||
Object.extend(Hash.prototype, {
|
||||
_each: function(iterator) {
|
||||
for (var key in this) {
|
||||
var value = this[key];
|
||||
if (value && value == Hash.prototype[key]) continue;
|
||||
|
||||
var pair = [key, value];
|
||||
pair.key = key;
|
||||
pair.value = value;
|
||||
iterator(pair);
|
||||
}
|
||||
},
|
||||
|
||||
keys: function() {
|
||||
return this.pluck('key');
|
||||
},
|
||||
|
||||
values: function() {
|
||||
return this.pluck('value');
|
||||
},
|
||||
|
||||
merge: function(hash) {
|
||||
return $H(hash).inject(this, function(mergedHash, pair) {
|
||||
mergedHash[pair.key] = pair.value;
|
||||
return mergedHash;
|
||||
});
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
var result;
|
||||
for(var i = 0, length = arguments.length; i < length; i++) {
|
||||
var value = this[arguments[i]];
|
||||
if (value !== undefined){
|
||||
if (result === undefined) result = value;
|
||||
else {
|
||||
if (result.constructor != Array) result = [result];
|
||||
result.push(value)
|
||||
}
|
||||
}
|
||||
delete this[arguments[i]];
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
toQueryString: function() {
|
||||
return Hash.toQueryString(this);
|
||||
},
|
||||
|
||||
inspect: function() {
|
||||
return '#<Hash:{' + this.map(function(pair) {
|
||||
return pair.map(Object.inspect).join(': ');
|
||||
}).join(', ') + '}>';
|
||||
}
|
||||
});
|
||||
|
||||
function $H(object) {
|
||||
if (object && object.constructor == Hash) return object;
|
||||
return new Hash(object);
|
||||
};
|
225
src/position.js
Normal file
225
src/position.js
Normal file
@ -0,0 +1,225 @@
|
||||
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;
|
||||
},
|
||||
|
||||
realOffset: function(element) {
|
||||
var valueT = 0, valueL = 0;
|
||||
do {
|
||||
valueT += element.scrollTop || 0;
|
||||
valueL += element.scrollLeft || 0;
|
||||
element = element.parentNode;
|
||||
} while (element);
|
||||
return [valueL, valueT];
|
||||
},
|
||||
|
||||
cumulativeOffset: function(element) {
|
||||
var valueT = 0, valueL = 0;
|
||||
do {
|
||||
valueT += element.offsetTop || 0;
|
||||
valueL += element.offsetLeft || 0;
|
||||
element = element.offsetParent;
|
||||
} while (element);
|
||||
return [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 [valueL, valueT];
|
||||
},
|
||||
|
||||
offsetParent: 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;
|
||||
},
|
||||
|
||||
// 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 = this.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 = this.realOffset(element);
|
||||
|
||||
this.xcomp = x + offsetcache[0] - this.deltaX;
|
||||
this.ycomp = y + offsetcache[1] - this.deltaY;
|
||||
this.offset = this.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;
|
||||
},
|
||||
|
||||
page: 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)
|
||||
if (Element.getStyle(element,'position')=='absolute') break;
|
||||
|
||||
} while (element = element.offsetParent);
|
||||
|
||||
element = forElement;
|
||||
do {
|
||||
if (!window.opera || element.tagName=='BODY') {
|
||||
valueT -= element.scrollTop || 0;
|
||||
valueL -= element.scrollLeft || 0;
|
||||
}
|
||||
} while (element = element.parentNode);
|
||||
|
||||
return [valueL, valueT];
|
||||
},
|
||||
|
||||
clone: function(source, target) {
|
||||
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 = Position.page(source);
|
||||
|
||||
// find coordinate system to use
|
||||
target = $(target);
|
||||
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(target,'position') == 'absolute') {
|
||||
parent = Position.offsetParent(target);
|
||||
delta = Position.page(parent);
|
||||
}
|
||||
|
||||
// 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) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
|
||||
if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
|
||||
if(options.setWidth) target.style.width = source.offsetWidth + 'px';
|
||||
if(options.setHeight) target.style.height = source.offsetHeight + 'px';
|
||||
},
|
||||
|
||||
absolutize: function(element) {
|
||||
element = $(element);
|
||||
if (element.style.position == 'absolute') return;
|
||||
Position.prepare();
|
||||
|
||||
var offsets = Position.positionedOffset(element);
|
||||
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';
|
||||
},
|
||||
|
||||
relativize: function(element) {
|
||||
element = $(element);
|
||||
if (element.style.position == 'relative') return;
|
||||
Position.prepare();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Safari returns margins on body which is incorrect if the child is absolutely
|
||||
// positioned. For performance reasons, redefine Position.cumulativeOffset for
|
||||
// KHTML/WebKit only.
|
||||
if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
|
||||
Position.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 [valueL, valueT];
|
||||
}
|
||||
}
|
||||
|
||||
|
20
src/prototype.js
vendored
Normal file
20
src/prototype.js
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<%= include 'HEADER' %>
|
||||
|
||||
var Prototype = {
|
||||
Version: '<%= PROTOTYPE_VERSION %>',
|
||||
BrowserFeatures: {
|
||||
XPath: !!document.evaluate
|
||||
},
|
||||
|
||||
ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
|
||||
emptyFunction: function() {},
|
||||
K: function(x) { return x }
|
||||
}
|
||||
|
||||
<%= include 'base.js', 'string.js' %>
|
||||
|
||||
<%= include 'enumerable.js', 'array.js', 'hash.js', 'range.js' %>
|
||||
|
||||
<%= include 'ajax.js', 'dom.js', 'selector.js', 'form.js', 'event.js', 'position.js' %>
|
||||
|
||||
Element.addMethods();
|
29
src/range.js
Normal file
29
src/range.js
Normal file
@ -0,0 +1,29 @@
|
||||
ObjectRange = Class.create();
|
||||
Object.extend(ObjectRange.prototype, Enumerable);
|
||||
Object.extend(ObjectRange.prototype, {
|
||||
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);
|
||||
}
|
129
src/selector.js
Normal file
129
src/selector.js
Normal file
@ -0,0 +1,129 @@
|
||||
var Selector = Class.create();
|
||||
Selector.prototype = {
|
||||
initialize: function(expression) {
|
||||
this.params = {classNames: []};
|
||||
this.expression = expression.toString().strip();
|
||||
this.parseExpression();
|
||||
this.compileMatcher();
|
||||
},
|
||||
|
||||
parseExpression: function() {
|
||||
function abort(message) { throw 'Parse error in selector: ' + message; }
|
||||
|
||||
if (this.expression == '') abort('empty expression');
|
||||
|
||||
var params = this.params, expr = this.expression, match, modifier, clause, rest;
|
||||
while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
|
||||
params.attributes = params.attributes || [];
|
||||
params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
|
||||
expr = match[1];
|
||||
}
|
||||
|
||||
if (expr == '*') return this.params.wildcard = true;
|
||||
|
||||
while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
|
||||
modifier = match[1], clause = match[2], rest = match[3];
|
||||
switch (modifier) {
|
||||
case '#': params.id = clause; break;
|
||||
case '.': params.classNames.push(clause); break;
|
||||
case '':
|
||||
case undefined: params.tagName = clause.toUpperCase(); break;
|
||||
default: abort(expr.inspect());
|
||||
}
|
||||
expr = rest;
|
||||
}
|
||||
|
||||
if (expr.length > 0) abort(expr.inspect());
|
||||
},
|
||||
|
||||
buildMatchExpression: function() {
|
||||
var params = this.params, conditions = [], clause;
|
||||
|
||||
if (params.wildcard)
|
||||
conditions.push('true');
|
||||
if (clause = params.id)
|
||||
conditions.push('element.readAttribute("id") == ' + clause.inspect());
|
||||
if (clause = params.tagName)
|
||||
conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
|
||||
if ((clause = params.classNames).length > 0)
|
||||
for (var i = 0, length = clause.length; i < length; i++)
|
||||
conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
|
||||
if (clause = params.attributes) {
|
||||
clause.each(function(attribute) {
|
||||
var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
|
||||
var splitValueBy = function(delimiter) {
|
||||
return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
|
||||
}
|
||||
|
||||
switch (attribute.operator) {
|
||||
case '=': conditions.push(value + ' == ' + attribute.value.inspect()); break;
|
||||
case '~=': conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
|
||||
case '|=': conditions.push(
|
||||
splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
|
||||
); break;
|
||||
case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break;
|
||||
case '':
|
||||
case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
|
||||
default: throw 'Unknown operator ' + attribute.operator + ' in selector';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return conditions.join(' && ');
|
||||
},
|
||||
|
||||
compileMatcher: function() {
|
||||
this.match = new Function('element', 'if (!element.tagName) return false; \
|
||||
element = $(element); \
|
||||
return ' + this.buildMatchExpression());
|
||||
},
|
||||
|
||||
findElements: function(scope) {
|
||||
var element;
|
||||
|
||||
if (element = $(this.params.id))
|
||||
if (this.match(element))
|
||||
if (!scope || Element.childOf(element, scope))
|
||||
return [element];
|
||||
|
||||
scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
|
||||
|
||||
var results = [];
|
||||
for (var i = 0, length = scope.length; i < length; i++)
|
||||
if (this.match(element = scope[i]))
|
||||
results.push(Element.extend(element));
|
||||
|
||||
return results;
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
return this.expression;
|
||||
}
|
||||
}
|
||||
|
||||
Object.extend(Selector, {
|
||||
matchElements: function(elements, expression) {
|
||||
var selector = new Selector(expression);
|
||||
return elements.select(selector.match.bind(selector)).map(Element.extend);
|
||||
},
|
||||
|
||||
findElement: function(elements, expression, index) {
|
||||
if (typeof expression == 'number') index = expression, expression = false;
|
||||
return Selector.matchElements(elements, expression || '*')[index || 0];
|
||||
},
|
||||
|
||||
findChildElements: function(element, expressions) {
|
||||
return expressions.map(function(expression) {
|
||||
return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
|
||||
var selector = new Selector(expr);
|
||||
return results.inject([], function(elements, result) {
|
||||
return elements.concat(selector.findElements(result || element));
|
||||
});
|
||||
});
|
||||
}).flatten();
|
||||
}
|
||||
});
|
||||
|
||||
function $$() {
|
||||
return Selector.findChildElements(document, $A(arguments));
|
||||
}
|
170
src/string.js
Normal file
170
src/string.js
Normal file
@ -0,0 +1,170 @@
|
||||
String.interpret = function(value){
|
||||
return value == null ? '' : String(value);
|
||||
}
|
||||
|
||||
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 this;
|
||||
},
|
||||
|
||||
truncate: function(length, truncation) {
|
||||
length = length || 30;
|
||||
truncation = truncation === undefined ? '...' : truncation;
|
||||
return this.length > length ?
|
||||
this.slice(0, length - truncation.length) + truncation : 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 div = document.createElement('div');
|
||||
var text = document.createTextNode(this);
|
||||
div.appendChild(text);
|
||||
return div.innerHTML;
|
||||
},
|
||||
|
||||
unescapeHTML: function() {
|
||||
var div = document.createElement('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 name = decodeURIComponent(pair[0]);
|
||||
var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
|
||||
|
||||
if (hash[name] !== undefined) {
|
||||
if (hash[name].constructor != Array)
|
||||
hash[name] = [hash[name]];
|
||||
if (value) hash[name].push(value);
|
||||
}
|
||||
else hash[name] = 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);
|
||||
},
|
||||
|
||||
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.replace(/\\/g, '\\\\');
|
||||
if (useDoubleQuotes)
|
||||
return '"' + escapedString.replace(/"/g, '\\"') + '"';
|
||||
else
|
||||
return "'" + escapedString.replace(/'/g, '\\\'') + "'";
|
||||
}
|
||||
});
|
||||
|
||||
String.prototype.gsub.prepareReplacement = function(replacement) {
|
||||
if (typeof replacement == 'function') return replacement;
|
||||
var template = new Template(replacement);
|
||||
return function(match) { return template.evaluate(match) };
|
||||
}
|
||||
|
||||
String.prototype.parseQuery = String.prototype.toQueryParams;
|
||||
|
||||
var Template = Class.create();
|
||||
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
|
||||
Template.prototype = {
|
||||
initialize: function(template, pattern) {
|
||||
this.template = template.toString();
|
||||
this.pattern = pattern || Template.Pattern;
|
||||
},
|
||||
|
||||
evaluate: function(object) {
|
||||
return this.template.gsub(this.pattern, function(match) {
|
||||
var before = match[1];
|
||||
if (before == '\\') return match[2];
|
||||
return before + String.interpret(object[match[3]]);
|
||||
});
|
||||
}
|
||||
}
|
229
test/browser.html
Normal file
229
test/browser.html
Normal file
@ -0,0 +1,229 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||
<title>Prototype object browser</title>
|
||||
<style type="text/css" media="screen">
|
||||
body {
|
||||
font-family: Lucida Grande, Verdana, sans-serif;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.inspector {
|
||||
margin: 1%;
|
||||
float: left;
|
||||
width: 31%;
|
||||
border: 1px solid #ccc;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.inspector h2 {
|
||||
font-size: 13px;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
padding: 5px;
|
||||
background-color: #e6e6e6;
|
||||
border-bottom: 1px solid #999;
|
||||
}
|
||||
|
||||
.inspector ul {
|
||||
height: 200px;
|
||||
overflow: auto;
|
||||
margin: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.inspector li {
|
||||
cursor: pointer;
|
||||
list-style-type: none;
|
||||
padding: 2px 5px 2px 30px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.inspector li.selected {
|
||||
background-color: #888;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.inspector.active li.selected {
|
||||
background-color: #1a76fd;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#path, #value {
|
||||
width: 97%;
|
||||
margin: 1%;
|
||||
}
|
||||
|
||||
#path {
|
||||
margin-bottom: 0;
|
||||
border: 1px solid #ccc;
|
||||
border-bottom: 1px solid #999;
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
#value {
|
||||
margin-top: 0;
|
||||
border: 1px solid #ccc;
|
||||
border-top: none;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#path_content, #value_content {
|
||||
display: block;
|
||||
padding: 15px 30px 15px 30px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script type="text/javascript" src="../dist/prototype.js"></script>
|
||||
<script type="text/javascript">
|
||||
var Browser = Class.create();
|
||||
Browser.prototype = {
|
||||
initialize: function(element, name, value, options) {
|
||||
this.element = $(element);
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.history = [];
|
||||
Object.extend(this, options || {});
|
||||
this.reset();
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.go(this.name, this.value);
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
var children = $A(this.element.childNodes),
|
||||
history = this.history.toArray(),
|
||||
elements = history.slice(-3).pluck('element');
|
||||
|
||||
children.each(function(element) {
|
||||
if (element && !elements.include(element))
|
||||
this.element.removeChild(element);
|
||||
}.bind(this));
|
||||
|
||||
children = $A(this.element.childNodes);
|
||||
|
||||
elements.each(function(element, index) {
|
||||
Element.removeClassName(element, 'active');
|
||||
var child = children[index];
|
||||
if (!child)
|
||||
this.element.appendChild(element);
|
||||
else if (!element.parentNode)
|
||||
this.element.insertBefore(element, child);
|
||||
}.bind(this));
|
||||
|
||||
this.setTitle();
|
||||
this.setValue();
|
||||
},
|
||||
|
||||
setTitle: function() {
|
||||
if (this.titleElement)
|
||||
this.titleElement.innerHTML =
|
||||
this.history.pluck('name').invoke('escapeHTML').join('.');
|
||||
},
|
||||
|
||||
setValue: function() {
|
||||
if (this.valueElement)
|
||||
this.valueElement.innerHTML =
|
||||
this.currentValue().escapeHTML() + ' ';
|
||||
},
|
||||
|
||||
currentValue: function() {
|
||||
try {
|
||||
return Object.inspect(this.current());
|
||||
} catch (e) {
|
||||
return '(Internal Function)';
|
||||
}
|
||||
},
|
||||
|
||||
current: function() {
|
||||
return this.history.last().value;
|
||||
},
|
||||
|
||||
go: function(name, value) {
|
||||
var from = this.history.last();
|
||||
this.history.push(new Inspector(this, name, value));
|
||||
this.refresh();
|
||||
if (from)
|
||||
Element.addClassName(from.element, 'active');
|
||||
}
|
||||
}
|
||||
|
||||
var Inspector = Class.create();
|
||||
Inspector.prototype = {
|
||||
initialize: function(browser, name, value) {
|
||||
this.browser = browser;
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.id = 'inspector_' + new Date().getTime();
|
||||
this.history = this.browser.history.toArray();
|
||||
this.history.push(this);
|
||||
this.createElement();
|
||||
this.populate();
|
||||
},
|
||||
|
||||
properties: function() {
|
||||
var properties = [];
|
||||
for (var property in this.value)
|
||||
properties.push(property);
|
||||
properties.sort();
|
||||
return properties;
|
||||
},
|
||||
|
||||
createElement: function() {
|
||||
var element = document.createElement('div');
|
||||
element.className = 'inspector';
|
||||
element.id = this.id;
|
||||
this.element = element;
|
||||
|
||||
var title = document.createElement('h2');
|
||||
title.innerHTML = this.name.toString().escapeHTML();
|
||||
this.titleElement = title;
|
||||
|
||||
var list = document.createElement('ul');
|
||||
this.listElement = list;
|
||||
|
||||
element.appendChild(title);
|
||||
element.appendChild(list);
|
||||
},
|
||||
|
||||
populate: function() {
|
||||
this.properties().each(function(property) {
|
||||
var li = document.createElement('li');
|
||||
li.innerHTML = property.toString().escapeHTML();
|
||||
li.onclick = this.select.bind(this, li);
|
||||
li._property = property;
|
||||
this.listElement.appendChild(li);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
select: function(element) {
|
||||
this.unselect();
|
||||
Element.addClassName(element, 'selected');
|
||||
this.selectedProperty = element;
|
||||
this.browser.history = this.history.toArray();
|
||||
this.browser.go(element._property, this.value[element._property]);
|
||||
},
|
||||
|
||||
unselect: function() {
|
||||
if (this.selectedProperty)
|
||||
Element.removeClassName(this.selectedProperty, 'selected');
|
||||
this.selectedProperty = null;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="browser_wrapper">
|
||||
<div id="browser"></div>
|
||||
<div style="clear: left"></div>
|
||||
</div>
|
||||
<h1 id="path"><span id="path_content"></span></h1>
|
||||
<pre id="value"><div id="value_content"></div></pre>
|
||||
<script type="text/javascript">
|
||||
new Browser('browser', 'window', window, {titleElement: $('path_content'), valueElement: $('value_content')})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
110
test/console.html
Normal file
110
test/console.html
Normal file
@ -0,0 +1,110 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
||||
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<title>Prototype Console</title>
|
||||
<script src="../dist/prototype.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
Prototype.Console = Class.create();
|
||||
Prototype.Console.prototype = {
|
||||
initialize: function(element, form, input) {
|
||||
this.element = $(element);
|
||||
this.form = $(form);
|
||||
this.input = $(input);
|
||||
this.context = window.eval.bind(window);
|
||||
this.registerCallbacks();
|
||||
document.title = 'Prototype Console ' + Prototype.Version;
|
||||
Field.activate(this.input);
|
||||
},
|
||||
|
||||
registerCallbacks: function() {
|
||||
Event.observe(this.form, 'submit', function(event) {
|
||||
this.eval($F(this.input));
|
||||
this.input.value = '';
|
||||
Field.activate(this.input);
|
||||
Event.stop(event);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
log: function(type, message) {
|
||||
new Insertion.Bottom(this.element,
|
||||
'<tr class="' + type + '"><td>' +
|
||||
message.escapeHTML() + '</td></tr>');
|
||||
Element.scrollTo(this.form);
|
||||
},
|
||||
|
||||
eval: function(expression) {
|
||||
if (expression.match(/^\s*$/)) return;
|
||||
try {
|
||||
this.log('input', expression);
|
||||
window.$_ = this.context.call(window, expression);
|
||||
this.log('output', Object.inspect($_));
|
||||
} catch (e) {
|
||||
this.log('error', e.toString());
|
||||
}
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
this.element.innerHTML = '';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style type="text/css">
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.console {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.console td {
|
||||
padding: 5px;
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.console tr.input td {
|
||||
background-color: #eee;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.console tr.error td,
|
||||
.console tr.output td {
|
||||
color: #333;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.console tr.error td {
|
||||
color: #f00;
|
||||
}
|
||||
|
||||
#input-form {
|
||||
width: 100%;
|
||||
background-color: #f0f5b8;
|
||||
border-top: 1px solid #333;
|
||||
padding: 10px;
|
||||
position: fixed;
|
||||
height: 25px;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table class="console">
|
||||
<tbody id="console">
|
||||
</tbody>
|
||||
</table>
|
||||
<form id="input-form">
|
||||
<input type="text" size="60" id="input" />
|
||||
<input type="submit" value="Evaluate" />
|
||||
</form>
|
||||
<script type="text/javascript">
|
||||
window.console = new Prototype.Console('console', 'input-form', 'input');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
227
test/lib/jstest.rb
Normal file
227
test/lib/jstest.rb
Normal file
@ -0,0 +1,227 @@
|
||||
require 'rake/tasklib'
|
||||
require 'thread'
|
||||
require 'webrick'
|
||||
|
||||
class Browser
|
||||
def supported?; true; end
|
||||
def setup ; end
|
||||
def open(url) ; end
|
||||
def teardown ; end
|
||||
|
||||
def host
|
||||
require 'rbconfig'
|
||||
Config::CONFIG['host']
|
||||
end
|
||||
|
||||
def macos?
|
||||
host.include?('darwin')
|
||||
end
|
||||
|
||||
def windows?
|
||||
host.include?('mswin')
|
||||
end
|
||||
|
||||
def linux?
|
||||
host.include?('linux')
|
||||
end
|
||||
|
||||
def applescript(script)
|
||||
raise "Can't run AppleScript on #{host}" unless macos?
|
||||
system "osascript -e '#{script}' 2>&1 >/dev/null"
|
||||
end
|
||||
end
|
||||
|
||||
class FirefoxBrowser < Browser
|
||||
def initialize(path='c:\Program Files\Mozilla Firefox\firefox.exe')
|
||||
@path = path
|
||||
end
|
||||
|
||||
def visit(url)
|
||||
applescript('tell application "Firefox" to Get URL "' + url + '"') if macos?
|
||||
system("#{@path} #{url}") if windows?
|
||||
system("firefox #{url}") if linux?
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Firefox"
|
||||
end
|
||||
end
|
||||
|
||||
class SafariBrowser < Browser
|
||||
def supported?
|
||||
macos?
|
||||
end
|
||||
|
||||
def setup
|
||||
applescript('tell application "Safari" to make new document')
|
||||
end
|
||||
|
||||
def visit(url)
|
||||
applescript('tell application "Safari" to set URL of front document to "' + url + '"')
|
||||
end
|
||||
|
||||
def teardown
|
||||
#applescript('tell application "Safari" to close front document')
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Safari"
|
||||
end
|
||||
end
|
||||
|
||||
class IEBrowser < Browser
|
||||
def initialize(path='C:\Program Files\Internet Explorer\IEXPLORE.EXE')
|
||||
@path = path
|
||||
end
|
||||
|
||||
def setup
|
||||
if windows?
|
||||
puts %{
|
||||
MAJOR ANNOYANCE on Windows.
|
||||
You have to shut down the Internet Explorer manually after each test
|
||||
for the script to proceed.
|
||||
Any suggestions on fixing this is GREATLY appreaciated!
|
||||
Thank you for your understanding.
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def supported?
|
||||
windows?
|
||||
end
|
||||
|
||||
def visit(url)
|
||||
system("#{@path} #{url}") if windows?
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Internet Explorer"
|
||||
end
|
||||
end
|
||||
|
||||
class KonquerorBrowser < Browser
|
||||
def supported?
|
||||
linux?
|
||||
end
|
||||
|
||||
def visit(url)
|
||||
system("kfmclient openURL #{url}")
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Konqueror"
|
||||
end
|
||||
end
|
||||
|
||||
# shut up, webrick :-)
|
||||
class ::WEBrick::HTTPServer
|
||||
def access_log(config, req, res)
|
||||
# nop
|
||||
end
|
||||
end
|
||||
class ::WEBrick::BasicLog
|
||||
def log(level, data)
|
||||
# nop
|
||||
end
|
||||
end
|
||||
|
||||
class NonCachingFileHandler < WEBrick::HTTPServlet::FileHandler
|
||||
def do_GET(req, res)
|
||||
super
|
||||
|
||||
res['Content-Type'] = case req.path
|
||||
when /\.js$/ then 'text/javascript'
|
||||
when /\.html$/ then 'text/html'
|
||||
when /\.css$/ then 'text/css'
|
||||
else 'text/plain'
|
||||
end
|
||||
|
||||
res['ETag'] = nil
|
||||
res['Last-Modified'] = Time.now + 100**4
|
||||
res['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
|
||||
res['Pragma'] = 'no-cache'
|
||||
res['Expires'] = Time.now - 100**4
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class JavaScriptTestTask < ::Rake::TaskLib
|
||||
|
||||
def initialize(name=:test)
|
||||
@name = name
|
||||
@tests = []
|
||||
@browsers = []
|
||||
|
||||
@queue = Queue.new
|
||||
|
||||
result = []
|
||||
|
||||
@server = WEBrick::HTTPServer.new(:Port => 4711) # TODO: make port configurable
|
||||
@server.mount_proc("/results") do |req, res|
|
||||
@queue.push(req.query['result'])
|
||||
res.body = "OK"
|
||||
end
|
||||
@server.mount_proc("/content-type") do |req, res|
|
||||
res.body = req["content-type"]
|
||||
end
|
||||
|
||||
yield self if block_given?
|
||||
define
|
||||
end
|
||||
|
||||
def define
|
||||
task @name do
|
||||
trap("INT") { @server.shutdown }
|
||||
t = Thread.new { @server.start }
|
||||
|
||||
# run all combinations of browsers and tests
|
||||
@browsers.each do |browser|
|
||||
if browser.supported?
|
||||
browser.setup
|
||||
@tests.each do |test|
|
||||
browser.visit("http://localhost:4711#{test}?resultsURL=http://localhost:4711/results&t=" + ("%.6f" % Time.now.to_f))
|
||||
result = @queue.pop
|
||||
puts "#{test} on #{browser}: #{result}"
|
||||
end
|
||||
browser.teardown
|
||||
else
|
||||
puts "Skipping #{browser}, not supported on this OS"
|
||||
end
|
||||
browser.teardown
|
||||
end
|
||||
|
||||
@server.shutdown
|
||||
t.join
|
||||
end
|
||||
end
|
||||
|
||||
def mount(path, dir=nil)
|
||||
dir = Dir.pwd + path unless dir
|
||||
|
||||
# don't cache anything in our tests
|
||||
@server.mount(path, NonCachingFileHandler, dir)
|
||||
end
|
||||
|
||||
# test should be specified as a url
|
||||
def run(test)
|
||||
@tests<<test
|
||||
end
|
||||
|
||||
def browser(browser)
|
||||
browser =
|
||||
case(browser)
|
||||
when :firefox
|
||||
FirefoxBrowser.new
|
||||
when :safari
|
||||
SafariBrowser.new
|
||||
when :ie
|
||||
IEBrowser.new
|
||||
when :konqueror
|
||||
KonquerorBrowser.new
|
||||
else
|
||||
browser
|
||||
end
|
||||
|
||||
@browsers<<browser
|
||||
end
|
||||
end
|
497
test/lib/unittest.js
Normal file
497
test/lib/unittest.js
Normal file
@ -0,0 +1,497 @@
|
||||
// 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/)
|
||||
//
|
||||
// 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.
|
||||
|
||||
|
||||
// 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 = Object.inspect;
|
||||
|
||||
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());
|
||||
var actionButtons = test.actionButtons();
|
||||
if (actionButtons)
|
||||
$(this.logger.lastLogLine).down('td', 2).appendChild(actionButtons);
|
||||
|
||||
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 = [];
|
||||
this.actions = {};
|
||||
},
|
||||
summary: function() {
|
||||
return (
|
||||
this.assertions + " assertions, " +
|
||||
this.failures + " failures, " +
|
||||
this.errors + " errors" + "\n" +
|
||||
this.messages.join("\n"));
|
||||
},
|
||||
actionButtons: function() {
|
||||
if (!Object.keys(this.actions).any()) return false;
|
||||
var div = $(document.createElement("div"));
|
||||
div.addClassName("action_buttons");
|
||||
|
||||
for (var title in this.actions) {
|
||||
var button = $(document.createElement("input"));
|
||||
button.value = title;
|
||||
button.type = "button";
|
||||
button.observe("click", this.actions[title]);
|
||||
div.appendChild(button);
|
||||
}
|
||||
return div;
|
||||
},
|
||||
pass: function() {
|
||||
this.assertions++;
|
||||
},
|
||||
fail: function(message) {
|
||||
this.failures++;
|
||||
this.messages.push("Failure: " + message);
|
||||
},
|
||||
info: function(message) {
|
||||
this.messages.push("Info: " + message);
|
||||
},
|
||||
error: function(error, test) {
|
||||
this.errors++;
|
||||
this.actions['retry with throw'] = function() { test.run(true) };
|
||||
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); }
|
||||
},
|
||||
assertEnumEqual: function(expected, actual) {
|
||||
var message = arguments[2] || "assertEnumEqual";
|
||||
try { $A(expected).length == $A(actual).length &&
|
||||
expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?
|
||||
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); }
|
||||
},
|
||||
assertIdentical: function(expected, actual) {
|
||||
var message = arguments[2] || "assertIdentical";
|
||||
try { (expected === actual) ? this.pass() :
|
||||
this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
|
||||
'", actual "' + Test.Unit.inspect(actual) + '"'); }
|
||||
catch(e) { this.error(e); }
|
||||
},
|
||||
assertNotIdentical: function(expected, actual) {
|
||||
var message = arguments[2] || "assertNotIdentical";
|
||||
try { !(expected === actual) ? this.pass() :
|
||||
this.fail(message + ': expected "' + Test.Unit.inspect(expected) +
|
||||
'", actual "' + 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); }
|
||||
},
|
||||
assertNotNull: function(obj) {
|
||||
var message = arguments[1] || 'assertNotNull'
|
||||
try { (obj!==null) ? this.pass() :
|
||||
this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
|
||||
catch(e) { this.error(e); }
|
||||
},
|
||||
assertUndefined: function(obj) {
|
||||
var message = arguments[1] || 'assertUndefined'
|
||||
try { (typeof obj=="undefined") ? this.pass() :
|
||||
this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
|
||||
catch(e) { this.error(e); }
|
||||
},
|
||||
assertNotUndefined: function(obj) {
|
||||
var message = arguments[1] || 'assertNotUndefined'
|
||||
try { (typeof obj != "undefined") ? this.pass() :
|
||||
this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
|
||||
catch(e) { this.error(e); }
|
||||
},
|
||||
assertNullOrUndefined: function(obj){
|
||||
var message = arguments[1] || 'assertNullOrUndefined'
|
||||
try { (obj==null) ? this.pass() :
|
||||
this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
|
||||
catch(e) { this.error(e); }
|
||||
},
|
||||
assertNotNullOrUndefined: function(obj){
|
||||
var message = arguments[1] || 'assertNotNullOrUndefined'
|
||||
try { (obj!=null) ? this.pass() :
|
||||
this.fail(message + ': got "' + Test.Unit.inspect(obj) + '"'); }
|
||||
catch(e) { this.error(e); }
|
||||
},
|
||||
assertMatch: function(expected, actual) {
|
||||
var message = arguments[2] || 'assertMatch';
|
||||
var regex = new RegExp(expected);
|
||||
try { (regex.exec(actual)) ? this.pass() :
|
||||
this.fail(message + ' : regex: "' + Test.Unit.inspect(expected) + ' did not match: ' + Test.Unit.inspect(actual) + '"'); }
|
||||
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); }
|
||||
},
|
||||
assertRespondsTo: function(method, obj) {
|
||||
var message = arguments[2] || 'assertRespondsTo';
|
||||
try {
|
||||
(obj[method] && typeof obj[method] == 'function') ? this.pass() :
|
||||
this.fail(message + ": object doesn't respond to [" + method + "]"); }
|
||||
catch(e) { this.error(e); }
|
||||
},
|
||||
assertRaise: function(exceptionName, method) {
|
||||
var message = arguments[2] || 'assertRaise';
|
||||
try {
|
||||
method();
|
||||
this.fail(message + ": exception expected but none was raised"); }
|
||||
catch(e) {
|
||||
(e.name==exceptionName) ? this.pass() : this.error(e);
|
||||
}
|
||||
},
|
||||
assertNothingRaised: function(method) {
|
||||
var message = arguments[1] || 'assertNothingRaised';
|
||||
try {
|
||||
method();
|
||||
this.pass();
|
||||
} catch (e) {
|
||||
this.fail(message + ": " + e.toString());
|
||||
}
|
||||
},
|
||||
_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]));
|
||||
},
|
||||
assertElementsMatch: function() {
|
||||
var expressions = $A(arguments), elements = $A(expressions.shift());
|
||||
if (elements.length != expressions.length) {
|
||||
this.fail('assertElementsMatch: size mismatch: ' + elements.length + ' elements, ' + expressions.length + ' expressions');
|
||||
return false;
|
||||
}
|
||||
elements.zip(expressions).all(function(pair, index) {
|
||||
var element = $(pair.first()), expression = pair.last();
|
||||
if (element.match(expression)) return true;
|
||||
this.fail('assertElementsMatch: (in index ' + index + ') expected ' + expression.inspect() + ' but got ' + element.inspect());
|
||||
}.bind(this)) && this.pass();
|
||||
},
|
||||
assertElementMatches: function(element, expression) {
|
||||
this.assertElementsMatch([element], expression);
|
||||
},
|
||||
benchmark: function(operation, iterations) {
|
||||
var startAt = new Date();
|
||||
(iterations || 1).times(operation);
|
||||
var timeTaken = ((new Date())-startAt);
|
||||
this.info((arguments[2] || 'Operation') + ' finished ' +
|
||||
iterations + ' iterations in ' + (timeTaken/1000)+'s' );
|
||||
return timeTaken;
|
||||
}
|
||||
}
|
||||
|
||||
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(rethrow) {
|
||||
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) {
|
||||
if (rethrow) throw e;
|
||||
this.error(e, this);
|
||||
}
|
||||
}
|
||||
});
|
49
test/test.css
Normal file
49
test/test.css
Normal file
@ -0,0 +1,49 @@
|
||||
body, div, p, h1, h2, h3, ul, ol, span, a, table, td, form, img, li {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
font-size:0.8em;
|
||||
}
|
||||
|
||||
#log {
|
||||
padding-bottom: 1em;
|
||||
border-bottom: 2px solid #000;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
#logsummary {
|
||||
margin-bottom: 1em;
|
||||
padding: 1ex;
|
||||
border: 1px solid #000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#logtable {
|
||||
width:100%;
|
||||
border-collapse: collapse;
|
||||
border: 1px dotted #666;
|
||||
}
|
||||
|
||||
#logtable td, #logtable th {
|
||||
text-align: left;
|
||||
padding: 3px 8px;
|
||||
border: 1px dotted #666;
|
||||
}
|
||||
|
||||
#logtable .passed {
|
||||
background-color: #cfc;
|
||||
}
|
||||
|
||||
#logtable .failed, #logtable .error {
|
||||
background-color: #fcc;
|
||||
}
|
||||
|
||||
#logtable td div.action_buttons {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#logtable td div.action_buttons input {
|
||||
margin: 0 5px;
|
||||
font-size: 10px;
|
||||
}
|
174
test/unit/ajax.html
Normal file
174
test/unit/ajax.html
Normal file
@ -0,0 +1,174 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>Prototype Unit test file</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<script src="../../dist/prototype.js" type="text/javascript"></script>
|
||||
<script src="../lib/unittest.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" href="../test.css" type="text/css" />
|
||||
<style type="text/css" media="screen">
|
||||
/* <![CDATA[ */
|
||||
#testcss1 { font-size:11px; color: #f00; }
|
||||
#testcss2 { font-size:12px; color: #0f0; display: none; }
|
||||
/* ]]> */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Prototype Unit test file</h1>
|
||||
<p>
|
||||
Test of utility functions in ajax.js
|
||||
</p>
|
||||
|
||||
<!-- Log output -->
|
||||
<div id="testlog"> </div>
|
||||
<div id="content"></div>
|
||||
<div id="content2" style="color:red"></div>
|
||||
|
||||
<!-- Tests follow -->
|
||||
<script type="text/javascript" language="javascript" charset="utf-8">
|
||||
// <![CDATA[
|
||||
var responderCounter = 0;
|
||||
|
||||
new Test.Unit.Runner({
|
||||
|
||||
setup: function(){
|
||||
$('content').update('');
|
||||
$('content2').update('');
|
||||
},
|
||||
|
||||
teardown: function(){
|
||||
// hack to cleanup responders
|
||||
Ajax.Responders.responders = [Ajax.Responders.responders[0]];
|
||||
},
|
||||
|
||||
testSynchronousRequest: function() {with(this) {
|
||||
assertEqual("", $("content").innerHTML);
|
||||
|
||||
assertEqual(0, Ajax.activeRequestCount);
|
||||
new Ajax.Request("fixtures/hello.js", {
|
||||
asynchronous: false,
|
||||
method: 'GET',
|
||||
onComplete: function(response) { eval(response.responseText) }
|
||||
});
|
||||
assertEqual(0, Ajax.activeRequestCount);
|
||||
|
||||
var h2 = $("content").firstChild;
|
||||
assertEqual("Hello world!", h2.innerHTML);
|
||||
}},
|
||||
|
||||
testAsynchronousRequest: function() {with(this) {
|
||||
assertEqual("", $("content").innerHTML);
|
||||
|
||||
new Ajax.Request("fixtures/hello.js", {
|
||||
asynchronous: true,
|
||||
method: 'get',
|
||||
onComplete: function(response) { eval(response.responseText) }
|
||||
});
|
||||
wait(1000,function(){
|
||||
var h2 = $("content").firstChild;
|
||||
assertEqual("Hello world!", h2.innerHTML);
|
||||
});
|
||||
}},
|
||||
|
||||
testUpdater: function() {with(this) {
|
||||
assertEqual("", $("content").innerHTML);
|
||||
|
||||
new Ajax.Updater("content", "fixtures/content.html", { method:'get' });
|
||||
|
||||
// lowercase comparison because of MSIE which presents HTML tags in uppercase
|
||||
var sentence = ("Pack my box with <em>five dozen</em> liquor jugs! " +
|
||||
"Oh, how <strong>quickly</strong> daft jumping zebras vex...").toLowerCase();
|
||||
|
||||
wait(1000,function(){
|
||||
assertEqual(sentence, $("content").innerHTML.strip().toLowerCase());
|
||||
|
||||
$('content').update('');
|
||||
assertEqual("", $("content").innerHTML);
|
||||
|
||||
new Ajax.Updater({ success:"content", failure:"content2" },
|
||||
"fixtures/content.html", { method:'get', parameters:{ pet:'monkey' } });
|
||||
|
||||
new Ajax.Updater("", "fixtures/content.html", { method:'get', parameters:"pet=monkey" });
|
||||
|
||||
wait(1000,function(){
|
||||
assertEqual(sentence, $("content").innerHTML.strip().toLowerCase());
|
||||
assertEqual("", $("content2").innerHTML);
|
||||
});
|
||||
});
|
||||
}},
|
||||
|
||||
testResponders: function(){with(this) {
|
||||
// check for internal responder
|
||||
assertEqual(1, Ajax.Responders.responders.length);
|
||||
|
||||
var dummyResponder = {
|
||||
onComplete: function(req){ /* dummy */ }
|
||||
};
|
||||
|
||||
Ajax.Responders.register(dummyResponder);
|
||||
assertEqual(2, Ajax.Responders.responders.length);
|
||||
|
||||
// don't add twice
|
||||
Ajax.Responders.register(dummyResponder);
|
||||
assertEqual(2, Ajax.Responders.responders.length);
|
||||
|
||||
Ajax.Responders.unregister(dummyResponder);
|
||||
assertEqual(1, Ajax.Responders.responders.length);
|
||||
|
||||
var responder = {
|
||||
onCreate: function(req){ responderCounter++ },
|
||||
onLoading: function(req){ responderCounter++ },
|
||||
onComplete: function(req){ responderCounter++ }
|
||||
};
|
||||
Ajax.Responders.register(responder);
|
||||
|
||||
assertEqual(0, responderCounter);
|
||||
assertEqual(0, Ajax.activeRequestCount);
|
||||
new Ajax.Request("fixtures/content.html", { method:'get', parameters:"pet=monkey" });
|
||||
assertEqual(1, responderCounter);
|
||||
assertEqual(1, Ajax.activeRequestCount);
|
||||
|
||||
wait(1000,function(){
|
||||
assertEqual(3, responderCounter);
|
||||
assertEqual(0, Ajax.activeRequestCount);
|
||||
});
|
||||
}},
|
||||
|
||||
testEvalResponseShouldBeCalledBeforeOnComplete: function() {with(this) {
|
||||
assertEqual("", $("content").innerHTML);
|
||||
|
||||
assertEqual(0, Ajax.activeRequestCount);
|
||||
new Ajax.Request("fixtures/hello.js", {
|
||||
asynchronous: false,
|
||||
method: 'GET',
|
||||
onComplete: function(response) { assertNotEqual("", $("content").innerHTML) }
|
||||
});
|
||||
assertEqual(0, Ajax.activeRequestCount);
|
||||
|
||||
var h2 = $("content").firstChild;
|
||||
assertEqual("Hello world!", h2.innerHTML);
|
||||
}},
|
||||
|
||||
testContentTypeSetForSimulatedVerbs: function() {with(this) {
|
||||
var isRunningFromRake = true;
|
||||
|
||||
new Ajax.Request('/content-type', {
|
||||
method: 'put',
|
||||
contentType: 'application/bogus',
|
||||
asynchronous: false,
|
||||
onFailure: function() {
|
||||
isRunningFromRake = false;
|
||||
},
|
||||
onComplete: function(response) {
|
||||
if (isRunningFromRake)
|
||||
assertEqual('application/bogus; charset=UTF-8', response.responseText);
|
||||
}
|
||||
});
|
||||
}}
|
||||
|
||||
}, 'testlog');
|
||||
// ]]>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
152
test/unit/array.html
Normal file
152
test/unit/array.html
Normal file
@ -0,0 +1,152 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>Prototype Unit test file</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<script src="../../dist/prototype.js" type="text/javascript"></script>
|
||||
<script src="../lib/unittest.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" href="../test.css" type="text/css" />
|
||||
<style type="text/css" media="screen">
|
||||
/* <![CDATA[ */
|
||||
#testcss1 { font-size:11px; color: #f00; }
|
||||
#testcss2 { font-size:12px; color: #0f0; display: none; }
|
||||
/* ]]> */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Prototype Unit test file</h1>
|
||||
<p>
|
||||
Test of the Array.prototype extensions
|
||||
</p>
|
||||
|
||||
<!-- Log output -->
|
||||
<div id="testlog"> </div>
|
||||
|
||||
<!-- Tests follow -->
|
||||
<script type="text/javascript" language="javascript" charset="utf-8">
|
||||
// <![CDATA[
|
||||
|
||||
var globalArgsTest = 'nothing to see here';
|
||||
|
||||
new Test.Unit.Runner({
|
||||
|
||||
testToArrayOnArguments: function(){ with(this) {
|
||||
function toArrayOnArguments(){
|
||||
globalArgsTest = $A(arguments);
|
||||
}
|
||||
toArrayOnArguments();
|
||||
assertEnumEqual([], globalArgsTest);
|
||||
toArrayOnArguments('foo');
|
||||
assertEnumEqual(['foo'], globalArgsTest);
|
||||
toArrayOnArguments('foo','bar');
|
||||
assertEnumEqual(['foo','bar'], globalArgsTest);
|
||||
}},
|
||||
|
||||
testClear: function(){ with(this) {
|
||||
assertEnumEqual([], [].clear());
|
||||
assertEnumEqual([], [1].clear());
|
||||
assertEnumEqual([], [1,2].clear());
|
||||
}},
|
||||
|
||||
testClone: function(){ with(this) {
|
||||
assertEnumEqual([], [].clone());
|
||||
assertEnumEqual([1], [1].clone());
|
||||
assertEnumEqual([1,2], [1,2].clone());
|
||||
assertEnumEqual([0,1,2], [0,1,2].clone());
|
||||
var a = [0,1,2];
|
||||
var b = a;
|
||||
assertIdentical(a, b);
|
||||
b = a.clone();
|
||||
assertNotIdentical(a, b);
|
||||
}},
|
||||
|
||||
testFirst: function(){ with(this) {
|
||||
assertUndefined([].first());
|
||||
assertEqual(1, [1].first());
|
||||
assertEqual(1, [1,2].first());
|
||||
}},
|
||||
|
||||
testLast: function(){ with(this) {
|
||||
assertUndefined([].last());
|
||||
assertEqual(1, [1].last());
|
||||
assertEqual(2, [1,2].last());
|
||||
}},
|
||||
|
||||
testCompact: function(){ with(this) {
|
||||
assertEnumEqual([], [].compact());
|
||||
assertEnumEqual([1,2,3], [1,2,3].compact());
|
||||
assertEnumEqual([0,1,2,3], [0,null,1,2,undefined,3].compact());
|
||||
assertEnumEqual([1,2,3], [null,1,2,3,null].compact());
|
||||
}},
|
||||
|
||||
testFlatten: function(){ with(this) {
|
||||
assertEnumEqual([], [].flatten());
|
||||
assertEnumEqual([1,2,3], [1,2,3].flatten());
|
||||
assertEnumEqual([1,2,3], [1,[[[2,3]]]].flatten());
|
||||
assertEnumEqual([1,2,3], [[1],[2],[3]].flatten());
|
||||
assertEnumEqual([1,2,3], [[[[[[[1]]]]]],2,3].flatten());
|
||||
}},
|
||||
|
||||
testIndexOf: function(){ with(this) {
|
||||
assertEqual(-1, [].indexOf(1));
|
||||
assertEqual(-1, [0].indexOf(1));
|
||||
assertEqual(0, [1].indexOf(1));
|
||||
assertEqual(1, [0,1,2].indexOf(1));
|
||||
}},
|
||||
|
||||
testInspect: function(){ with(this) {
|
||||
assertEqual('[]',[].inspect());
|
||||
assertEqual('[1]',[1].inspect());
|
||||
assertEqual('[\'a\']',['a'].inspect());
|
||||
assertEqual('[\'a\', 1]',['a',1].inspect());
|
||||
}},
|
||||
|
||||
testReduce: function(){ with(this) {
|
||||
assertUndefined([].reduce());
|
||||
assertNull([null].reduce());
|
||||
assertEqual(1, [1].reduce());
|
||||
assertEnumEqual([1,2,3], [1,2,3].reduce());
|
||||
assertEnumEqual([1,null,3], [1,null,3].reduce());
|
||||
}},
|
||||
|
||||
testReverse: function(){ with(this) {
|
||||
assertEnumEqual([], [].reverse());
|
||||
assertEnumEqual([1], [1].reverse());
|
||||
assertEnumEqual([2,1], [1,2].reverse());
|
||||
assertEnumEqual([3,2,1], [1,2,3].reverse());
|
||||
}},
|
||||
|
||||
testSize: function(){ with(this) {
|
||||
assertEqual(4, [0, 1, 2, 3].size());
|
||||
assertEqual(0, [].size());
|
||||
}},
|
||||
|
||||
testUniq: function(){ with(this) {
|
||||
assertEnumEqual([1], [1, 1, 1].uniq());
|
||||
assertEnumEqual([1], [1].uniq());
|
||||
assertEnumEqual([], [].uniq());
|
||||
assertEnumEqual([0, 1, 2, 3], [0, 1, 2, 2, 3, 0, 2].uniq());
|
||||
}},
|
||||
|
||||
testWithout: function(){ with(this) {
|
||||
assertEnumEqual([], [].without(0));
|
||||
assertEnumEqual([], [0].without(0));
|
||||
assertEnumEqual([1], [0,1].without(0));
|
||||
assertEnumEqual([1,2], [0,1,2].without(0));
|
||||
}},
|
||||
|
||||
test$w: function(){ with(this) {
|
||||
assertEnumEqual(['a', 'b', 'c', 'd'], $w('a b c d'));
|
||||
assertEnumEqual([], $w(' '));
|
||||
assertEnumEqual(['a'], $w('a'));
|
||||
assertEnumEqual(['a'], $w('a '));
|
||||
assertEnumEqual(['a'], $w(' a'));
|
||||
assertEnumEqual(['a', 'b', 'c', 'd'], $w(' a b\nc\t\nd\n'));
|
||||
}}
|
||||
|
||||
}, 'testlog');
|
||||
// ]]>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
133
test/unit/base.html
Normal file
133
test/unit/base.html
Normal file
@ -0,0 +1,133 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>Prototype Unit test file</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<script src="../../dist/prototype.js" type="text/javascript"></script>
|
||||
<script src="../lib/unittest.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" href="../test.css" type="text/css" />
|
||||
<style type="text/css" media="screen">
|
||||
/* <![CDATA[ */
|
||||
#testcss1 { font-size:11px; color: #f00; }
|
||||
#testcss2 { font-size:12px; color: #0f0; display: none; }
|
||||
/* ]]> */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Prototype Unit test file</h1>
|
||||
<p>
|
||||
Test of utility functions in base.js
|
||||
</p>
|
||||
|
||||
<!-- Log output -->
|
||||
<div id="testlog"> </div>
|
||||
|
||||
<!-- Tests follow -->
|
||||
<script type="text/javascript" language="javascript" charset="utf-8">
|
||||
// <![CDATA[
|
||||
|
||||
|
||||
var peEventCount = 0;
|
||||
// peEventFired will stop the PeriodicalExecuter after 3 callbacks
|
||||
function peEventFired(pe) {
|
||||
if (++peEventCount>2) {
|
||||
pe.stop();
|
||||
}
|
||||
}
|
||||
|
||||
var arg1 = 1;
|
||||
var arg2 = 2;
|
||||
var arg3 = 3;
|
||||
function TestObj(){}
|
||||
TestObj.prototype.assertingEventHandler =
|
||||
function( event, assertEvent, assert1, assert2, assert3, a1, a2, a3 ){
|
||||
assertEvent(event);
|
||||
assert1(a1);
|
||||
assert2(a2);
|
||||
assert3(a3);
|
||||
}
|
||||
|
||||
var globalBindTest = null;
|
||||
|
||||
new Test.Unit.Runner({
|
||||
|
||||
testFunctionBind: function() { with(this) {
|
||||
function methodWithoutArguments(){
|
||||
globalBindTest = this.hi;
|
||||
}
|
||||
function methodWithArguments(){
|
||||
globalBindTest = this.hi + ',' + $A(arguments).join(',');
|
||||
}
|
||||
function methodWithBindArguments(){
|
||||
globalBindTest = this.hi + ',' + $A(arguments).join(',');
|
||||
}
|
||||
function methodWithBindArgumentsAndArguments(){
|
||||
globalBindTest = this.hi + ',' + $A(arguments).join(',');
|
||||
}
|
||||
|
||||
methodWithoutArguments.bind({hi:'without'})();
|
||||
assertEqual('without', globalBindTest);
|
||||
|
||||
methodWithArguments.bind({hi:'with'})('arg1','arg2');
|
||||
assertEqual('with,arg1,arg2', globalBindTest);
|
||||
|
||||
methodWithBindArguments.bind({hi:'withBindArgs'},'arg1','arg2')();
|
||||
assertEqual('withBindArgs,arg1,arg2', globalBindTest);
|
||||
|
||||
methodWithBindArgumentsAndArguments.bind({hi:'withBindArgsAndArgs'},'arg1','arg2')('arg3','arg4');
|
||||
assertEqual('withBindArgsAndArgs,arg1,arg2,arg3,arg4', globalBindTest);
|
||||
}},
|
||||
|
||||
testObjectInspect: function() { with(this) {
|
||||
assertEqual('undefined', Object.inspect());
|
||||
assertEqual('undefined', Object.inspect(undefined));
|
||||
assertEqual('null', Object.inspect(null));
|
||||
assertEqual("'foo\\\\b\\\'ar'", Object.inspect('foo\\b\'ar'));
|
||||
assertEqual('[]', Object.inspect([]));
|
||||
}},
|
||||
|
||||
// sanity check
|
||||
testDoesntExtendObjectPrototype: function() {with(this) {
|
||||
// for-in is supported with objects
|
||||
var iterations = 0, obj = { a: 1, b: 2, c: 3 };
|
||||
for(property in obj) iterations++;
|
||||
assertEqual(3, iterations);
|
||||
|
||||
// for-in is not supported with arrays
|
||||
iterations = 0;
|
||||
var arr = [1,2,3];
|
||||
for(property in arr) iterations++;
|
||||
assert(iterations > 3);
|
||||
}},
|
||||
|
||||
testPeriodicalExecuterStop: function() {with(this) {
|
||||
new PeriodicalExecuter(peEventFired, 0.1);
|
||||
|
||||
wait(1000, function() {
|
||||
assertEqual(3, peEventCount);
|
||||
});
|
||||
}},
|
||||
|
||||
testBindAsEventListener: function() {
|
||||
for( var i = 0; i < 10; ++i ){
|
||||
var div = document.createElement('div');
|
||||
div.setAttribute('id','test-'+i);
|
||||
document.body.appendChild(div);
|
||||
var tobj = new TestObj();
|
||||
var eventTest = {test:true};
|
||||
var call = tobj.assertingEventHandler.bindAsEventListener(tobj,
|
||||
this.assertEqual.bind(this,eventTest),
|
||||
this.assertEqual.bind(this,arg1),
|
||||
this.assertEqual.bind(this,arg2),
|
||||
this.assertEqual.bind(this,arg3), arg1, arg2, arg3 );
|
||||
call(eventTest);
|
||||
}
|
||||
}
|
||||
|
||||
}, 'testlog');
|
||||
|
||||
// ]]>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
809
test/unit/dom.html
Normal file
809
test/unit/dom.html
Normal file
@ -0,0 +1,809 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>Prototype Unit test file</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<script src="../../dist/prototype.js" type="text/javascript"></script>
|
||||
<script src="../lib/unittest.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" href="../test.css" type="text/css" />
|
||||
<style type="text/css" media="screen">
|
||||
/* <![CDATA[ */
|
||||
#testcss1 { font-size:11px; color: #f00; }
|
||||
#testcss2 { font-size:12px; color: #0f0; display: none; }
|
||||
#style_test_1 { cursor: pointer; font-size:12px;}
|
||||
div.style-test { margin-left: 1px }
|
||||
|
||||
#not_floating_style { float: none }
|
||||
#floating_style { float: left }
|
||||
#op2 { opacity:0.5;filter:alpha(opacity=50)progid:DXImageTransform.Microsoft.Blur(strength=10);}
|
||||
|
||||
#scroll_test_1 {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#scroll_test_2 {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
#dimensions-visible,
|
||||
#dimensions-display-none,
|
||||
#dimensions-visible-pos-rel,
|
||||
#dimensions-display-none-pos-rel,
|
||||
#dimensions-visible-pos-abs,
|
||||
#dimensions-display-none-pos-abs {
|
||||
font-size: 10px;
|
||||
height: 10em;
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
#dimensions-visible-pos-abs,
|
||||
#dimensions-display-none-pos-abs {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
}
|
||||
|
||||
#dimensions-visible-pos-rel,
|
||||
#dimensions-display-none-pos-rel {
|
||||
position: relative;
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
}
|
||||
|
||||
#dimensions-display-none, #imensions-display-none-pos-rel, #dimensions-display-none-pos-abs {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#dimensions-table, #dimensions-tbody, #dimensions-tr, #dimensions-td {
|
||||
font-size: 10px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-spacing: 0;
|
||||
height: 10em;
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
/* for scroll test on really big screens */
|
||||
body {
|
||||
height: 40000px;
|
||||
}
|
||||
/* ]]> */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Prototype Unit test file</h1>
|
||||
<p>
|
||||
Test of functions in dom.js
|
||||
</p>
|
||||
|
||||
<!-- Log output -->
|
||||
<div id="scroll_test_1">
|
||||
<p id="scroll_test_2">Scroll test</p>
|
||||
</div>
|
||||
|
||||
<div id="testlog"> </div>
|
||||
|
||||
<div id="test-visible">visible</div>
|
||||
<div id="test-hidden" style="display:none;">hidden</div>
|
||||
<div id="test-toggle-visible">visible</div>
|
||||
<div id="test-toggle-hidden" style="display:none;">hidden</div>
|
||||
<div id="test-hide-visible">visible</div>
|
||||
<div id="test-hide-hidden" style="display:none;">hidden</div>
|
||||
<div id="test-show-visible">visible</div>
|
||||
<div id="test-show-hidden" style="display:none;">hidden</div>
|
||||
<div id="removable-container"><div id="removable"></div></div>
|
||||
|
||||
<div>
|
||||
<table>
|
||||
<tbody id="table">
|
||||
<tr>
|
||||
<td>Data</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="a_cell">First Row</td>
|
||||
</tr>
|
||||
<tr id="second_row">
|
||||
<td>Second Row</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="table-container-to-replace">
|
||||
<table>
|
||||
<tbody id="table-to-replace">
|
||||
<tr>
|
||||
<td>Data</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="a_cell-to-replace">First Row</td>
|
||||
</tr>
|
||||
<tr id="second_row-to-replace">
|
||||
<td>Second Row</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<p class="test">Test paragraph outside of container</p>
|
||||
|
||||
<div id="container">
|
||||
<p class="test" id="intended">Test paragraph 1 inside of container</p>
|
||||
<p class="test">Test paragraph 2 inside of container</p>
|
||||
<p class="test">Test paragraph 3 inside of container</p>
|
||||
<p class="test">Test paragraph 4 inside of container</p>
|
||||
</div>
|
||||
|
||||
<div id="testdiv">to be updated</div>
|
||||
<div id="testdiv-replace-container-1"><div id="testdiv-replace-1"></div></div>
|
||||
<div id="testdiv-replace-container-2"><div id="testdiv-replace-2"></div></div>
|
||||
<div id="testdiv-replace-container-3"><div id="testdiv-replace-3"></div></div>
|
||||
<div id="testdiv-replace-container-4"><div id="testdiv-replace-4"></div></div>
|
||||
<div id="testdiv-replace-container-5"><div id="testdiv-replace-5"></div></div>
|
||||
|
||||
<div id="element_with_visible_overflow" style="overflow:visible">V</div>
|
||||
<div id="element_with_hidden_overflow" style="overflow:hidden">H</div>
|
||||
<div id="element_with_scroll_overflow" style="overflow:scroll">S</div>
|
||||
|
||||
<div id="element_extend_test"> </div>
|
||||
|
||||
<div id="test_whitespace"> <span> </span>
|
||||
|
||||
|
||||
<div><div></div> </div><span> </span>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="nav_tests_isolator">
|
||||
<div id="nav_test_first_sibling"></div>
|
||||
<div></div>
|
||||
<p id="nav_test_p" class="test"></p>
|
||||
<span id="nav_test_prev_sibling"></span>
|
||||
|
||||
<ul id="navigation_test" style="display: none">
|
||||
<li class="first"><em>A</em></li>
|
||||
<li><em class="dim">B</em></li>
|
||||
<li id="navigation_test_c">
|
||||
<em>C</em>
|
||||
<ul>
|
||||
<li><em class="dim">E</em></li>
|
||||
<li id="navigation_test_f"><em>F</em></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="last"><em>D</em></li>
|
||||
</ul>
|
||||
|
||||
<div id="navigation_test_next_sibling">
|
||||
<!-- -->
|
||||
</div>
|
||||
|
||||
<p></p>
|
||||
</div>
|
||||
|
||||
<div id="class_names">
|
||||
<p class="A"></p>
|
||||
<ul class="A B" id="class_names_ul">
|
||||
<li class="C"></li>
|
||||
<li class="A C"></li>
|
||||
</ul>
|
||||
<div class="B C D"></div>
|
||||
</div>
|
||||
|
||||
<div id="style_test_1" style="display:none;"></div>
|
||||
<div id="style_test_2" class="style-test" style="font-size:11px;"></div>
|
||||
|
||||
<div id="style_test_3">blah</div>
|
||||
|
||||
|
||||
|
||||
<div id="custom_attributes">
|
||||
<div foo="1" bar="2"></div>
|
||||
<div foo="2"></div>
|
||||
</div>
|
||||
|
||||
<a id="attributes_with_issues_1" href="test.html" accesskey="L" tabindex="50" title="a link"></a>
|
||||
<a id="attributes_with_issues_2" href="" accesskey="" tabindex="" title=""></a>
|
||||
<a id="attributes_with_issues_3"></a>
|
||||
|
||||
<div id="dom_attribute_precedence">
|
||||
<form>
|
||||
<input type="submit" id="update" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="not_floating_none">NFN</div>
|
||||
<div id="not_floating_inline" style="float:none">NFI</div>
|
||||
<div id="not_floating_style">NFS</div>
|
||||
|
||||
<div id="floating_inline" style="float:left">FI</div>
|
||||
<div id="floating_style">FS</div>
|
||||
<!-- Test Element opacity functions -->
|
||||
<img id="op1" alt="op2" src="fixtures/logo.gif" style="opacity:0.5;filter:alpha(opacity=50)" />
|
||||
<img id="op2" alt="op2" src="fixtures/logo.gif"/>
|
||||
<img id="op3" alt="op3" src="fixtures/logo.gif"/>
|
||||
<img id="op4-ie" alt="op3" src="fixtures/logo.gif" style="filter:alpha(opacity=30)" />
|
||||
<div id="dimensions-visible"></div>
|
||||
<div id="dimensions-display-none"></div>
|
||||
<div id="dimensions-visible-pos-rel"></div>
|
||||
<div id="dimensions-display-none-pos-rel"></div>
|
||||
<div id="dimensions-visible-pos-abs"></div>
|
||||
<div id="dimensions-display-none-pos-abs"></div>
|
||||
<table border="0" cellspacing="0" cellpadding="0" id="dimensions-table">
|
||||
<tbody id="dimensions-tbody">
|
||||
<tr id="dimensions-tr">
|
||||
<td id="dimensions-td">Data</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p id="test-empty"></p>
|
||||
<p id="test-empty-but-contains-whitespace">
|
||||
|
||||
|
||||
</p>
|
||||
<p id="test-full">content</p>
|
||||
<div id="ancestor"><div id="child"><div><div id="great-grand-child"></div></div></div></div>
|
||||
<div id="not-in-the-family"></div>
|
||||
<!-- Tests follow -->
|
||||
<script type="text/javascript" language="javascript" charset="utf-8">
|
||||
// <![CDATA[
|
||||
|
||||
var testVar = 'to be updated';
|
||||
|
||||
new Test.Unit.Runner({
|
||||
|
||||
testDollarFunction: function() { with(this) {
|
||||
assertUndefined($());
|
||||
|
||||
assertNull(document.getElementById('noWayThisIDExists'));
|
||||
assertNull($('noWayThisIDExists'));
|
||||
|
||||
assertIdentical(document.getElementById('testdiv'), $('testdiv'));
|
||||
assertEnumEqual([ $('testdiv'), $('container') ], $('testdiv', 'container'));
|
||||
assertEnumEqual([ $('testdiv'), undefined, $('container') ],
|
||||
$('testdiv', 'noWayThisIDExists', 'container'));
|
||||
var elt = $('testdiv');
|
||||
assertIdentical(elt, $(elt));
|
||||
assertRespondsTo('hide', elt);
|
||||
}},
|
||||
|
||||
testGetElementsByClassName: function() {with(this) {
|
||||
assertElementsMatch(document.getElementsByClassName('A'), 'p.A', 'ul#class_names_ul.A', 'li.A.C');
|
||||
assertElementsMatch(document.getElementsByClassName('A', 'class_names_ul'), 'li.A.C');
|
||||
assertElementsMatch(document.getElementsByClassName('B', 'class_names'), 'ul#class_names_ul.A.B', 'div.B.C.D');
|
||||
assertElementsMatch(document.getElementsByClassName('B', 'class_names_ul'));
|
||||
}},
|
||||
|
||||
testInsertWithTR: function() {with(this) {
|
||||
new Insertion.After('second_row', '<tr id="third_row"><td>Third Row</td></tr>');
|
||||
assert($('second_row').childOf('table'));
|
||||
}},
|
||||
|
||||
testElementVisible: function(){with(this) {
|
||||
assertNotEqual('none', $('test-visible').style.display);
|
||||
assertEqual('none', $('test-hidden').style.display);
|
||||
}},
|
||||
|
||||
testElementToggle: function(){with(this) {
|
||||
$('test-toggle-visible').toggle();
|
||||
assert(!$('test-toggle-visible').visible());
|
||||
$('test-toggle-visible').toggle();
|
||||
assert($('test-toggle-visible').visible());
|
||||
$('test-toggle-hidden').toggle();
|
||||
assert($('test-toggle-hidden').visible());
|
||||
$('test-toggle-hidden').toggle();
|
||||
assert(!$('test-toggle-hidden').visible());
|
||||
}},
|
||||
|
||||
testElementShow: function(){with(this) {
|
||||
$('test-show-visible').show();
|
||||
assert($('test-show-visible').visible());
|
||||
$('test-show-hidden').show();
|
||||
assert($('test-show-hidden').visible());
|
||||
}},
|
||||
|
||||
testElementHide: function(){with(this) {
|
||||
$('test-hide-visible').hide();
|
||||
assert(!$('test-hide-visible').visible());
|
||||
$('test-hide-hidden').hide();
|
||||
assert(!$('test-hide-hidden').visible());
|
||||
}},
|
||||
|
||||
testElementRemove: function(){with(this) {
|
||||
$('removable').remove();
|
||||
assert($('removable-container').empty());
|
||||
}},
|
||||
|
||||
testElementUpdate: function() {with(this) {
|
||||
$('testdiv').update('hello from div!');
|
||||
assertEqual('hello from div!', $('testdiv').innerHTML);
|
||||
|
||||
Element.update('testdiv', 'another hello from div!');
|
||||
assertEqual('another hello from div!', $('testdiv').innerHTML);
|
||||
|
||||
Element.update('testdiv', 123);
|
||||
assertEqual('123', $('testdiv').innerHTML)
|
||||
|
||||
Element.update('testdiv');
|
||||
assertEqual('', $('testdiv').innerHTML)
|
||||
}},
|
||||
|
||||
testElementUpdateWithScript: function() {with(this) {
|
||||
$('testdiv').update('hello from div!<script>testVar="hello!"</'+'script>');
|
||||
assertEqual('hello from div!',$('testdiv').innerHTML);
|
||||
wait(100,function(){
|
||||
assertEqual('hello!',testVar);
|
||||
|
||||
Element.update('testdiv','another hello from div!\n<script>testVar="another hello!"</'+'script>\nhere it goes');
|
||||
|
||||
// note: IE normalizes whitespace (like line breaks) to single spaces, thus the match test
|
||||
assertMatch(/^another hello from div!\s+here it goes$/,$('testdiv').innerHTML);
|
||||
wait(100,function(){
|
||||
assertEqual('another hello!',testVar);
|
||||
});
|
||||
});
|
||||
}},
|
||||
|
||||
testElementUpdateInTableRow: function() {with(this) {
|
||||
$('second_row').update('<td id="i_am_a_td">test</td>');
|
||||
assertEqual('test',$('i_am_a_td').innerHTML);
|
||||
|
||||
Element.update('second_row','<td id="i_am_a_td">another <span>test</span></td>');
|
||||
assertEqual('another <span>test</span>',$('i_am_a_td').innerHTML.toLowerCase());
|
||||
}},
|
||||
|
||||
testElementUpdateInTableCell: function() {with(this) {
|
||||
Element.update('a_cell','another <span>test</span>');
|
||||
assertEqual('another <span>test</span>',$('a_cell').innerHTML.toLowerCase());
|
||||
}},
|
||||
|
||||
testElementUpdateInTable: function() {with(this) {
|
||||
Element.update('table','<tr><td>boo!</td></tr>');
|
||||
assertMatch(/^<tr>\s*<td>boo!<\/td><\/tr>$/,$('table').innerHTML.toLowerCase());
|
||||
}},
|
||||
|
||||
testElementReplace: function() {with(this) {
|
||||
$('testdiv-replace-1').replace('hello from div!');
|
||||
assertEqual('hello from div!', $('testdiv-replace-container-1').innerHTML);
|
||||
|
||||
$('testdiv-replace-2').replace(123);
|
||||
assertEqual('123', $('testdiv-replace-container-2').innerHTML)
|
||||
|
||||
$('testdiv-replace-3').replace();
|
||||
assertEqual('', $('testdiv-replace-container-3').innerHTML)
|
||||
}},
|
||||
|
||||
|
||||
testElementReplaceWithScript: function() {with(this) {
|
||||
$('testdiv-replace-4').replace('hello from div!<script>testVarReplace="hello!"</'+'script>');
|
||||
assertEqual('hello from div!', $('testdiv-replace-container-4').innerHTML);
|
||||
wait(100,function(){
|
||||
assertEqual('hello!',testVarReplace);
|
||||
|
||||
$('testdiv-replace-5').replace('another hello from div!\n<script>testVarReplace="another hello!"</'+'script>\nhere it goes');
|
||||
|
||||
// note: IE normalizes whitespace (like line breaks) to single spaces, thus the match test
|
||||
assertMatch(/^another hello from div!\s+here it goes$/,$('testdiv-replace-container-5').innerHTML);
|
||||
wait(100,function(){
|
||||
assertEqual('another hello!',testVarReplace);
|
||||
});
|
||||
});
|
||||
}},
|
||||
|
||||
testElementSelectorMethod: function() {with(this) {
|
||||
var testSelector = $('container').getElementsBySelector('p.test');
|
||||
assertEqual(testSelector.length, 4);
|
||||
assertEqual(testSelector[0], $('intended'));
|
||||
assertEqual(testSelector[0], $$('#container p.test')[0]);
|
||||
}},
|
||||
|
||||
testElementClassNameMethod: function() {with(this) {
|
||||
var testClassNames = $('container').getElementsByClassName('test');
|
||||
var testSelector = $('container').getElementsBySelector('p.test');
|
||||
assertEqual(testClassNames[0], $('intended'));
|
||||
assertEqual(testClassNames.length, 4);
|
||||
assertEqual(testSelector[3], testClassNames[3]);
|
||||
assertEqual(testClassNames.length, testSelector.length);
|
||||
}},
|
||||
|
||||
testElementAncestors: function() {with(this) {
|
||||
var ancestors = $('navigation_test_f').ancestors();
|
||||
assertElementsMatch(ancestors, 'ul', 'li', 'ul#navigation_test',
|
||||
'div#nav_tests_isolator', 'body', 'html');
|
||||
assertElementsMatch(ancestors.last().ancestors());
|
||||
}},
|
||||
|
||||
testElementDescendants: function() {with(this) {
|
||||
assertElementsMatch($('navigation_test').descendants(),
|
||||
'li', 'em', 'li', 'em.dim', 'li', 'em', 'ul', 'li',
|
||||
'em.dim', 'li#navigation_test_f', 'em', 'li', 'em');
|
||||
assertElementsMatch($('navigation_test_f').descendants(), 'em');
|
||||
}},
|
||||
|
||||
testElementImmediateDescendants: function() {with(this) {
|
||||
assertElementsMatch($('navigation_test').immediateDescendants(),
|
||||
'li.first', 'li', 'li#navigation_test_c', 'li.last');
|
||||
assertNotEqual(0, $('navigation_test_next_sibling').childNodes.length);
|
||||
assertEnumEqual([], $('navigation_test_next_sibling').immediateDescendants());
|
||||
}},
|
||||
|
||||
testElementPreviousSiblings: function() {with(this) {
|
||||
assertElementsMatch($('navigation_test').previousSiblings(),
|
||||
'span#nav_test_prev_sibling', 'p.test', 'div', 'div#nav_test_first_sibling');
|
||||
assertElementsMatch($('navigation_test_f').previousSiblings(), 'li');
|
||||
}},
|
||||
|
||||
testElementNextSiblings: function() {with(this) {
|
||||
assertElementsMatch($('navigation_test').nextSiblings(),
|
||||
'div#navigation_test_next_sibling', 'p');
|
||||
assertElementsMatch($('navigation_test_f').nextSiblings());
|
||||
}},
|
||||
|
||||
testElementSiblings: function() {with(this) {
|
||||
assertElementsMatch($('navigation_test').siblings(),
|
||||
'div#nav_test_first_sibling', 'div', 'p.test',
|
||||
'span#nav_test_prev_sibling', 'div#navigation_test_next_sibling', 'p');
|
||||
}},
|
||||
|
||||
testElementUp: function() {with(this) {
|
||||
var element = $('navigation_test_f');
|
||||
assertElementMatches(element.up(), 'ul');
|
||||
assertElementMatches(element.up(0), 'ul');
|
||||
assertElementMatches(element.up(1), 'li');
|
||||
assertElementMatches(element.up(2), 'ul#navigation_test');
|
||||
assertElementsMatch(element.up('li').siblings(), 'li.first', 'li', 'li.last');
|
||||
assertElementMatches(element.up('ul', 1), 'ul#navigation_test');
|
||||
assertEqual(undefined, element.up('garbage'));
|
||||
assertEqual(undefined, element.up(6));
|
||||
}},
|
||||
|
||||
testElementDown: function() {with(this) {
|
||||
var element = $('navigation_test');
|
||||
assertElementMatches(element.down(), 'li.first');
|
||||
assertElementMatches(element.down(0), 'li.first');
|
||||
assertElementMatches(element.down(1), 'em');
|
||||
assertElementMatches(element.down('li', 5), 'li.last');
|
||||
assertElementMatches(element.down('ul').down('li', 1), 'li#navigation_test_f');
|
||||
}},
|
||||
|
||||
testElementPrevious: function() {with(this) {
|
||||
var element = $('navigation_test').down('li.last');
|
||||
assertElementMatches(element.previous(), 'li#navigation_test_c');
|
||||
assertElementMatches(element.previous(1), 'li');
|
||||
assertElementMatches(element.previous('.first'), 'li.first');
|
||||
assertEqual(undefined, element.previous(3));
|
||||
assertEqual(undefined, $('navigation_test').down().previous());
|
||||
}},
|
||||
|
||||
testElementNext: function() {with(this) {
|
||||
var element = $('navigation_test').down('li.first');
|
||||
assertElementMatches(element.next(), 'li');
|
||||
assertElementMatches(element.next(1), 'li#navigation_test_c');
|
||||
assertElementMatches(element.next(2), 'li.last');
|
||||
assertElementMatches(element.next('.last'), 'li.last');
|
||||
assertEqual(undefined, element.next(3));
|
||||
assertEqual(undefined, element.next(2).next());
|
||||
}},
|
||||
|
||||
testElementInspect: function() {with(this) {
|
||||
assertEqual('<ul id="navigation_test">', $('navigation_test').inspect());
|
||||
assertEqual('<li class="first">', $('navigation_test').down().inspect());
|
||||
assertEqual('<em>', $('navigation_test').down(1).inspect());
|
||||
}},
|
||||
|
||||
testElementMakeClipping: function() {with(this) {
|
||||
var chained = Element.extend(document.createElement('DIV'));
|
||||
assertEqual(chained, chained.makeClipping());
|
||||
assertEqual(chained, chained.makeClipping());
|
||||
assertEqual(chained, chained.makeClipping().makeClipping());
|
||||
|
||||
assertEqual(chained, chained.undoClipping());
|
||||
assertEqual(chained, chained.undoClipping());
|
||||
assertEqual(chained, chained.undoClipping().makeClipping());
|
||||
|
||||
['hidden','visible','scroll'].each( function(overflowValue) {
|
||||
var element = $('element_with_'+overflowValue+'_overflow');
|
||||
|
||||
assertEqual(overflowValue, element.getStyle('overflow'));
|
||||
element.makeClipping();
|
||||
assertEqual('hidden', element.getStyle('overflow'));
|
||||
element.undoClipping();
|
||||
assertEqual(overflowValue, element.getStyle('overflow'));
|
||||
});
|
||||
}},
|
||||
|
||||
testElementExtend: function() {with(this) {
|
||||
var element = $('element_extend_test');
|
||||
assertRespondsTo('show', element);
|
||||
|
||||
var XHTML_TAGS = $w(
|
||||
'a abbr acronym address applet area '+
|
||||
'b bdo big blockquote br button caption '+
|
||||
'cite code col colgroup dd del dfn div dl dt '+
|
||||
'em fieldset form h1 h2 h3 h4 h5 h6 hr '+
|
||||
'i iframe img input ins kbd label legend li '+
|
||||
'map object ol optgroup option p param pre q samp '+
|
||||
'script select small span strong style sub sup '+
|
||||
'table tbody td textarea tfoot th thead tr tt ul var');
|
||||
|
||||
XHTML_TAGS.each(function(tag) {
|
||||
var element = document.createElement(tag);
|
||||
assertEqual(element, Element.extend(element));
|
||||
assertRespondsTo('show', element);
|
||||
});
|
||||
|
||||
[null,'','a','aa'].each(function(content) {
|
||||
var textnode = document.createTextNode(content);
|
||||
assertEqual(textnode, Element.extend(textnode));
|
||||
assert(typeof textnode['show'] == 'undefined');
|
||||
});
|
||||
}},
|
||||
|
||||
testElementCleanWhitespace: function() {with(this) {
|
||||
Element.cleanWhitespace("test_whitespace");
|
||||
assertEqual(3, $("test_whitespace").childNodes.length);
|
||||
|
||||
assertEqual(1, $("test_whitespace").firstChild.nodeType);
|
||||
assertEqual('SPAN', $("test_whitespace").firstChild.tagName);
|
||||
|
||||
assertEqual(1, $("test_whitespace").firstChild.nextSibling.nodeType);
|
||||
assertEqual('DIV', $("test_whitespace").firstChild.nextSibling.tagName);
|
||||
|
||||
assertEqual(1, $("test_whitespace").firstChild.nextSibling.nextSibling.nodeType);
|
||||
assertEqual('SPAN', $("test_whitespace").firstChild.nextSibling.nextSibling.tagName);
|
||||
|
||||
var element = document.createElement('DIV');
|
||||
element.appendChild(document.createTextNode(''));
|
||||
element.appendChild(document.createTextNode(''));
|
||||
assertEqual(2, element.childNodes.length);
|
||||
Element.cleanWhitespace(element);
|
||||
assertEqual(0, element.childNodes.length);
|
||||
}},
|
||||
|
||||
testElementEmpty: function() {with(this) {
|
||||
assert($('test-empty').empty());
|
||||
assert($('test-empty-but-contains-whitespace').empty());
|
||||
assert(!$('test-full').empty());
|
||||
}},
|
||||
|
||||
testDescendantOf: function() {with(this) {
|
||||
assert($('child').descendantOf('ancestor'));
|
||||
assert($('child').descendantOf($('ancestor')));
|
||||
assert($('great-grand-child').descendantOf('ancestor'));
|
||||
assert(!$('great-grand-child').descendantOf('not-in-the-family'));
|
||||
}},
|
||||
|
||||
testChildOf: function() {with(this) {
|
||||
assert($('child').childOf('ancestor'));
|
||||
assert($('child').childOf($('ancestor')));
|
||||
assert($('great-grand-child').childOf('ancestor'));
|
||||
assert(!$('great-grand-child').childOf('not-in-the-family'));
|
||||
assertIdentical(Element.Methods.childOf, Element.Methods.descendantOf);
|
||||
}},
|
||||
|
||||
testElementSetStyle: function() { with(this) {
|
||||
Element.setStyle('style_test_3',{ 'left': '2px' });
|
||||
assertEqual('2px', $('style_test_3').style.left);
|
||||
|
||||
Element.setStyle('style_test_3',{ marginTop: '1px' });
|
||||
assertEqual('1px', $('style_test_3').style.marginTop);
|
||||
|
||||
$('style_test_3').setStyle({ 'margin-top': '2px', left: '-1px' });
|
||||
assertEqual('-1px', $('style_test_3').style.left);
|
||||
assertEqual('2px', $('style_test_3').style.marginTop);
|
||||
|
||||
assertEqual('none', $('style_test_3').getStyle('float'));
|
||||
$('style_test_3').setStyle({ 'float': 'left' });
|
||||
assertEqual('left', $('style_test_3').getStyle('float'));
|
||||
|
||||
$('style_test_3').setStyle({ cssFloat: 'none' });
|
||||
assertEqual('none', $('style_test_3').getStyle('float'));
|
||||
|
||||
assertEqual(1, $('style_test_3').getStyle('opacity'));
|
||||
|
||||
$('style_test_3').setStyle({ opacity: 0.5 });
|
||||
assertEqual(0.5, $('style_test_3').getStyle('opacity'));
|
||||
|
||||
// should remove opacity
|
||||
$('style_test_3').setStyle({ opacity: '' });
|
||||
assertEqual(1, $('style_test_3').getStyle('opacity'));
|
||||
}},
|
||||
|
||||
testElementGetStyle: function() { with(this) {
|
||||
assertEqual("none",
|
||||
$('style_test_1').getStyle('display'));
|
||||
|
||||
// not displayed, so "null" ("auto" is tranlated to "null")
|
||||
assertNull(Element.getStyle('style_test_1','width'));
|
||||
|
||||
$('style_test_1').show();
|
||||
|
||||
// from id rule
|
||||
assertEqual("pointer",
|
||||
Element.getStyle('style_test_1','cursor'));
|
||||
|
||||
assertEqual("block",
|
||||
Element.getStyle('style_test_2','display'));
|
||||
|
||||
// we should always get something for width (if displayed)
|
||||
// firefox and safari automatically send the correct value,
|
||||
// IE is special-cased to do the same
|
||||
assertEqual($('style_test_2').offsetWidth+'px', Element.getStyle('style_test_2','width'));
|
||||
|
||||
assertEqual("static",Element.getStyle('style_test_1','position'));
|
||||
// from style
|
||||
assertEqual("11px",
|
||||
Element.getStyle('style_test_2','font-size'));
|
||||
// from class
|
||||
assertEqual("1px",
|
||||
Element.getStyle('style_test_2','margin-left'));
|
||||
|
||||
['not_floating_none','not_floating_style','not_floating_inline'].each(function(element){
|
||||
assertEqual('none', $(element).getStyle('float'));
|
||||
assertEqual('none', $(element).getStyle('cssFloat'));
|
||||
});
|
||||
|
||||
['floating_style','floating_inline'].each(function(element){
|
||||
assertEqual('left', $(element).getStyle('float'));
|
||||
assertEqual('left', $(element).getStyle('cssFloat'));
|
||||
});
|
||||
|
||||
assertEqual(0.5, $('op1').getStyle('opacity'));
|
||||
assertEqual(0.5, $('op2').getStyle('opacity'));
|
||||
assertEqual(1.0, $('op3').getStyle('opacity'));
|
||||
|
||||
$('op1').setStyle({opacity: '0.3'});
|
||||
$('op2').setStyle({opacity: '0.3'});
|
||||
$('op3').setStyle({opacity: '0.3'});
|
||||
|
||||
assertEqual(0.3, $('op1').getStyle('opacity'));
|
||||
assertEqual(0.3, $('op2').getStyle('opacity'));
|
||||
assertEqual(0.3, $('op3').getStyle('opacity'));
|
||||
|
||||
if(navigator.appVersion.match(/MSIE/)) {
|
||||
assertEqual('alpha(opacity=30)', $('op1').getStyle('filter'));
|
||||
assertEqual('progid:DXImageTransform.Microsoft.Blur(strength=10)alpha(opacity=30)', $('op2').getStyle('filter'));
|
||||
assertEqual('alpha(opacity=30)', $('op3').getStyle('filter'));
|
||||
assertEqual(0.3, $('op4-ie').getStyle('opacity'));
|
||||
}
|
||||
// verify that value is stil found when using camelized
|
||||
// strings (function previously used getPropertyValue()
|
||||
// which expected non-camelized strings)
|
||||
assertEqual("12px", $('style_test_1').getStyle('fontSize'));
|
||||
}},
|
||||
|
||||
testElementReadAttribute: function() {with(this) {
|
||||
assertEqual('test.html' , $('attributes_with_issues_1').readAttribute('href'));
|
||||
assertEqual('L' , $('attributes_with_issues_1').readAttribute('accesskey'));
|
||||
assertEqual('50' , $('attributes_with_issues_1').readAttribute('tabindex'));
|
||||
assertEqual('a link' , $('attributes_with_issues_1').readAttribute('title'));
|
||||
|
||||
['href', 'accesskey', 'accesskey', 'title'].each(function(attr){
|
||||
assertEqual('' , $('attributes_with_issues_2').readAttribute(attr));
|
||||
});
|
||||
|
||||
var elements = $('custom_attributes').immediateDescendants();
|
||||
assertEnumEqual(['1', '2'], elements.invoke('readAttribute', 'foo'));
|
||||
assertEnumEqual(['2', null], elements.invoke('readAttribute', 'bar'));
|
||||
}},
|
||||
|
||||
testElementGetHeight: function() {with(this) {
|
||||
assertIdentical(100, $('dimensions-visible').getHeight());
|
||||
assertIdentical(100, $('dimensions-display-none').getHeight());
|
||||
}},
|
||||
|
||||
testElementGetWidth: function() {with(this) {
|
||||
assertIdentical(200, $('dimensions-visible').getWidth());
|
||||
assertIdentical(200, $('dimensions-display-none').getWidth());
|
||||
}},
|
||||
|
||||
testElementGetDimensions: function() {with(this) {
|
||||
assertIdentical(100, $('dimensions-visible').getDimensions().height);
|
||||
assertIdentical(200, $('dimensions-visible').getDimensions().width);
|
||||
assertIdentical(100, $('dimensions-display-none').getDimensions().height);
|
||||
assertIdentical(200, $('dimensions-display-none').getDimensions().width);
|
||||
|
||||
assertIdentical(100, $('dimensions-visible-pos-rel').getDimensions().height);
|
||||
assertIdentical(200, $('dimensions-visible-pos-rel').getDimensions().width);
|
||||
assertIdentical(100, $('dimensions-display-none-pos-rel').getDimensions().height);
|
||||
assertIdentical(200, $('dimensions-display-none-pos-rel').getDimensions().width);
|
||||
|
||||
assertIdentical(100, $('dimensions-visible-pos-abs').getDimensions().height);
|
||||
assertIdentical(200, $('dimensions-visible-pos-abs').getDimensions().width);
|
||||
assertIdentical(100, $('dimensions-display-none-pos-abs').getDimensions().height);
|
||||
assertIdentical(200, $('dimensions-display-none-pos-abs').getDimensions().width);
|
||||
|
||||
$('dimensions-td').hide();
|
||||
assertIdentical(100, $('dimensions-td').getDimensions().height);
|
||||
assertIdentical(200, $('dimensions-td').getDimensions().width);
|
||||
$('dimensions-td').show();
|
||||
|
||||
$('dimensions-tr').hide();
|
||||
assertIdentical(100, $('dimensions-tr').getDimensions().height);
|
||||
assertIdentical(200, $('dimensions-tr').getDimensions().width);
|
||||
$('dimensions-tr').show();
|
||||
|
||||
$('dimensions-table').hide();
|
||||
assertIdentical(100, $('dimensions-table').getDimensions().height);
|
||||
assertIdentical(200, $('dimensions-table').getDimensions().width);
|
||||
}},
|
||||
|
||||
testDOMAttributesHavePrecedenceOverExtendedElementMethods: function() {with(this) {
|
||||
assertNothingRaised(function() { $('dom_attribute_precedence').down('form') });
|
||||
assertEqual($('dom_attribute_precedence').down('input'), $('dom_attribute_precedence').down('form').update);
|
||||
}},
|
||||
|
||||
testClassNames: function() {with(this) {
|
||||
assertEnumEqual([], $('class_names').classNames());
|
||||
assertEnumEqual(['A'], $('class_names').down().classNames());
|
||||
assertEnumEqual(['A', 'B'], $('class_names_ul').classNames());
|
||||
}},
|
||||
|
||||
testHasClassName: function() {with(this) {
|
||||
assert(!$('class_names').hasClassName('does_not_exist'));
|
||||
assert($('class_names').down().hasClassName('A'));
|
||||
assert(!$('class_names').down().hasClassName('does_not_exist'));
|
||||
assert($('class_names_ul').hasClassName('A'));
|
||||
assert($('class_names_ul').hasClassName('B'));
|
||||
assert(!$('class_names_ul').hasClassName('does_not_exist'));
|
||||
}},
|
||||
|
||||
testAddClassName: function() {with(this) {
|
||||
$('class_names').addClassName('added_className');
|
||||
assertEnumEqual(['added_className'], $('class_names').classNames());
|
||||
|
||||
$('class_names').addClassName('added_className'); // verify that className cannot be added twice.
|
||||
assertEnumEqual(['added_className'], $('class_names').classNames());
|
||||
|
||||
$('class_names').addClassName('another_added_className');
|
||||
assertEnumEqual(['added_className', 'another_added_className'], $('class_names').classNames());
|
||||
}},
|
||||
|
||||
testRemoveClassName: function() {with(this) {
|
||||
$('class_names').removeClassName('added_className');
|
||||
assertEnumEqual(['another_added_className'], $('class_names').classNames());
|
||||
|
||||
$('class_names').removeClassName('added_className'); // verify that removing a non existent className is safe.
|
||||
assertEnumEqual(['another_added_className'], $('class_names').classNames());
|
||||
|
||||
$('class_names').removeClassName('another_added_className');
|
||||
assertEnumEqual([], $('class_names').classNames());
|
||||
}},
|
||||
|
||||
testToggleClassName: function() {with(this) {
|
||||
$('class_names').toggleClassName('toggled_className');
|
||||
assertEnumEqual(['toggled_className'], $('class_names').classNames());
|
||||
|
||||
$('class_names').toggleClassName('toggled_className');
|
||||
assertEnumEqual([], $('class_names').classNames());
|
||||
|
||||
$('class_names_ul').toggleClassName('toggled_className');
|
||||
assertEnumEqual(['A', 'B', 'toggled_className'], $('class_names_ul').classNames());
|
||||
|
||||
$('class_names_ul').toggleClassName('toggled_className');
|
||||
assertEnumEqual(['A', 'B'], $('class_names_ul').classNames());
|
||||
}},
|
||||
|
||||
testElementScrollTo: function() {with(this) {
|
||||
var elem = $('scroll_test_2');
|
||||
Element.scrollTo('scroll_test_2');
|
||||
assertEqual(Position.page(elem)[1], 0);
|
||||
window.scrollTo(0, 0);
|
||||
|
||||
elem.scrollTo();
|
||||
assertEqual(Position.page(elem)[1], 0);
|
||||
window.scrollTo(0, 0);
|
||||
}}
|
||||
|
||||
}, 'testlog');
|
||||
|
||||
// ]]>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
65
test/unit/element_mixins.html
Normal file
65
test/unit/element_mixins.html
Normal file
@ -0,0 +1,65 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>Prototype Unit test file</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<script src="../../dist/prototype.js" type="text/javascript"></script>
|
||||
<script src="../lib/unittest.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" href="../test.css" type="text/css" />
|
||||
<style type="text/css" media="screen">
|
||||
/* <![CDATA[ */
|
||||
#testcss1 { font-size:11px; color: #f00; }
|
||||
#testcss2 { font-size:12px; color: #0f0; display: none; }
|
||||
/* ]]> */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Prototype Unit test file</h1>
|
||||
<p>
|
||||
Test of extra methods mixed in to elements with $() and $$().
|
||||
</p>
|
||||
|
||||
<!-- Log output -->
|
||||
<div id="testlog"> </div>
|
||||
|
||||
<h2>Test Form Elements</h2>
|
||||
<form id="form">
|
||||
<input type="text" id="input" value="4" />
|
||||
<input type="submit" />
|
||||
</form>
|
||||
|
||||
<!-- Tests follow -->
|
||||
<script type="text/javascript" language="javascript" charset="utf-8">
|
||||
// <![CDATA[
|
||||
new Test.Unit.Runner({
|
||||
testInput: function() {with(this) {
|
||||
assert($("input").present != null);
|
||||
assert(typeof $("input").present == 'function');
|
||||
assert($("input").select != null);
|
||||
}},
|
||||
|
||||
testForm: function() {with(this) {
|
||||
assert($("form").reset != null);
|
||||
assert($("form").getInputs().length == 2);
|
||||
}},
|
||||
|
||||
testEvent: function() {with(this) {
|
||||
assert($("form").observe != null)
|
||||
// Can't really test this one with TestUnit...
|
||||
$('form').observe("submit", function(e) {
|
||||
alert("yeah!");
|
||||
Event.stop(e);
|
||||
});
|
||||
}},
|
||||
|
||||
testCollections: function() {with(this) {
|
||||
assert($$("input").all(function(input) {
|
||||
return (input.focus != null);
|
||||
}));
|
||||
}}
|
||||
}, 'testlog');
|
||||
// ]]>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
289
test/unit/enumerable.html
Normal file
289
test/unit/enumerable.html
Normal file
@ -0,0 +1,289 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>Prototype Unit test file</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<script src="../../dist/prototype.js" type="text/javascript"></script>
|
||||
<script src="../lib/unittest.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" href="../test.css" type="text/css" />
|
||||
<style type="text/css" media="screen">
|
||||
/* <![CDATA[ */
|
||||
#testcss1 { font-size:11px; color: #f00; }
|
||||
#testcss2 { font-size:12px; color: #0f0; display: none; }
|
||||
/* ]]> */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Prototype Unit test file</h1>
|
||||
<p>
|
||||
Test of utility functions in enumerable.js
|
||||
</p>
|
||||
|
||||
<!-- Log output -->
|
||||
<div id="testlog"> </div>
|
||||
|
||||
<!-- Tests follow -->
|
||||
<script type="text/javascript" language="javascript" charset="utf-8">
|
||||
// <![CDATA[
|
||||
var Fixtures = {
|
||||
People: [
|
||||
{name: 'Sam Stephenson', nickname: 'sam-'},
|
||||
{name: 'Marcel Molina Jr.', nickname: 'noradio'},
|
||||
{name: 'Scott Barron', nickname: 'htonl'},
|
||||
{name: 'Nicholas Seckar', nickname: 'Ulysses'}
|
||||
],
|
||||
|
||||
Nicknames: $w('sam- noradio htonl Ulysses'),
|
||||
|
||||
Primes: [
|
||||
1, 2, 3, 5, 7, 11, 13, 17, 19, 23,
|
||||
29, 31, 37, 41, 43, 47, 53, 59, 61, 67,
|
||||
71, 73, 79, 83, 89, 97
|
||||
],
|
||||
|
||||
Z: []
|
||||
};
|
||||
|
||||
for (var i = 1; i <= 100; i++)
|
||||
Fixtures.Z.push(i);
|
||||
|
||||
function prime(value) {
|
||||
for (var i = 2; i < value; i++)
|
||||
if (value % i == 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
new Test.Unit.Runner({
|
||||
testEachBreak: function() {with(this) {
|
||||
var result = 0;
|
||||
[1, 2, 3].each(function(value) {
|
||||
if ((result = value) == 2) throw $break;
|
||||
});
|
||||
|
||||
assertEqual(2, result);
|
||||
}},
|
||||
|
||||
testEachContinue: function() {with(this) {
|
||||
var results = [];
|
||||
[1, 2, 3].each(function(value) {
|
||||
if (value == 2) throw $continue;
|
||||
results.push(value);
|
||||
});
|
||||
|
||||
assertEqual('1, 3', results.join(', '));
|
||||
}},
|
||||
|
||||
testEachChaining: function() {with(this) {
|
||||
assertEqual(Fixtures.Primes, Fixtures.Primes.each(Prototype.emptyFunction));
|
||||
assertEqual(3, [1, 2, 3].each(Prototype.emptyFunction).length);
|
||||
}},
|
||||
|
||||
testAny: function() {with(this) {
|
||||
assert(!([].any()));
|
||||
|
||||
assert([true, true, true].any());
|
||||
assert([true, false, false].any());
|
||||
assert(![false, false, false].any());
|
||||
|
||||
assert([1, 2, 3, 4, 5].any(function(value) {
|
||||
return value > 3;
|
||||
}));
|
||||
assert(![1, 2, 3, 4, 5].any(function(value) {
|
||||
return value > 10;
|
||||
}));
|
||||
}},
|
||||
|
||||
testAll: function() {with(this) {
|
||||
assert([].all());
|
||||
|
||||
assert([true, true, true].all());
|
||||
assert(![true, false, false].all());
|
||||
assert(![false, false, false].all());
|
||||
|
||||
assert([1, 2, 3, 4, 5].all(function(value) {
|
||||
return value > 0;
|
||||
}));
|
||||
assert(![1, 2, 3, 4, 5].all(function(value) {
|
||||
return value > 3;
|
||||
}));
|
||||
}},
|
||||
|
||||
testCollect: function() {with(this) {
|
||||
assertEqual(Fixtures.Nicknames.join(', '),
|
||||
Fixtures.People.collect(function(person) {
|
||||
return person.nickname;
|
||||
}).join(", "));
|
||||
|
||||
assertEqual(26, Fixtures.Primes.map().length);
|
||||
}},
|
||||
|
||||
testDetect: function() {with(this) {
|
||||
assertEqual('Marcel Molina Jr.',
|
||||
Fixtures.People.detect(function(person) {
|
||||
return person.nickname.match(/no/);
|
||||
}).name);
|
||||
}},
|
||||
|
||||
testEachSlice: function() {with(this) {
|
||||
assertEnumEqual([], [].eachSlice(2));
|
||||
assertEqual(1, [1].eachSlice(1).length);
|
||||
assertEnumEqual([1], [1].eachSlice(1)[0]);
|
||||
assertEqual(2, [1,2,3].eachSlice(2).length);
|
||||
assertEnumEqual(
|
||||
[3, 2, 1, 11, 7, 5, 19, 17, 13, 31, 29, 23, 43, 41, 37, 59, 53, 47, 71, 67, 61, 83, 79, 73, 97, 89],
|
||||
Fixtures.Primes.eachSlice( 3, function(slice){ return slice.reverse() }).flatten()
|
||||
);
|
||||
}},
|
||||
|
||||
testEachWithIndex: function() {with(this) {
|
||||
var nicknames = [], indexes = [];
|
||||
Fixtures.People.each(function(person, index) {
|
||||
nicknames.push(person.nickname);
|
||||
indexes.push(index);
|
||||
});
|
||||
|
||||
assertEqual(Fixtures.Nicknames.join(', '),
|
||||
nicknames.join(', '));
|
||||
assertEqual('0, 1, 2, 3', indexes.join(', '));
|
||||
}},
|
||||
|
||||
testFindAll: function() {with(this) {
|
||||
assertEqual(Fixtures.Primes.join(', '),
|
||||
Fixtures.Z.findAll(prime).join(', '));
|
||||
}},
|
||||
|
||||
testGrep: function() {with(this) {
|
||||
assertEqual('noradio, htonl',
|
||||
Fixtures.Nicknames.grep(/o/).join(", "));
|
||||
|
||||
assertEqual('NORADIO, HTONL',
|
||||
Fixtures.Nicknames.grep(/o/, function(nickname) {
|
||||
return nickname.toUpperCase();
|
||||
}).join(", "))
|
||||
}},
|
||||
|
||||
testInclude: function() {with(this) {
|
||||
assert(Fixtures.Nicknames.include('sam-'));
|
||||
assert(Fixtures.Nicknames.include('noradio'));
|
||||
assert(Fixtures.Nicknames.include('htonl'));
|
||||
assert(Fixtures.Nicknames.include('Ulysses'));
|
||||
assert(!Fixtures.Nicknames.include('gmosx'));
|
||||
}},
|
||||
|
||||
testInGroupsOf: function() { with(this) {
|
||||
assertEnumEqual([], [].inGroupsOf(3));
|
||||
|
||||
var arr = [1, 2, 3, 4, 5, 6].inGroupsOf(3);
|
||||
assertEqual(2, arr.length);
|
||||
assertEnumEqual([1, 2, 3], arr[0]);
|
||||
assertEnumEqual([4, 5, 6], arr[1]);
|
||||
|
||||
arr = [1, 2, 3, 4, 5, 6].inGroupsOf(4);
|
||||
assertEqual(2, arr.length);
|
||||
assertEnumEqual([1, 2, 3, 4], arr[0]);
|
||||
assertEnumEqual([5, 6, null, null], arr[1]);
|
||||
|
||||
arr = [1, 2, 3].inGroupsOf(4,'x');
|
||||
assertEqual(1, arr.length);
|
||||
assertEnumEqual([1, 2, 3, 'x'], arr[0]);
|
||||
|
||||
assertEnumEqual([1,2,3,'a'], [1,2,3].inGroupsOf(2, 'a').flatten());
|
||||
|
||||
arr = [1, 2, 3].inGroupsOf(5, '');
|
||||
assertEqual(1, arr.length);
|
||||
assertEnumEqual([1, 2, 3, '', ''], arr[0]);
|
||||
|
||||
assertEnumEqual([1,2,3,0], [1,2,3].inGroupsOf(2, 0).flatten());
|
||||
assertEnumEqual([1,2,3,false], [1,2,3].inGroupsOf(2, false).flatten());
|
||||
}},
|
||||
|
||||
testInject: function() {with(this) {
|
||||
assertEqual(1061,
|
||||
Fixtures.Primes.inject(0, function(sum, value) {
|
||||
return sum + value;
|
||||
}));
|
||||
}},
|
||||
|
||||
testInvoke: function() {with(this) {
|
||||
var result = [[2, 1, 3], [6, 5, 4]].invoke('sort');
|
||||
assertEqual(2, result.length);
|
||||
assertEqual('1, 2, 3', result[0].join(', '));
|
||||
assertEqual('4, 5, 6', result[1].join(', '));
|
||||
|
||||
result = result.invoke('invoke', 'toString', 2);
|
||||
assertEqual('1, 10, 11', result[0].join(', '));
|
||||
assertEqual('100, 101, 110', result[1].join(', '));
|
||||
}},
|
||||
|
||||
testMax: function() {with(this) {
|
||||
assertEqual(100, Fixtures.Z.max());
|
||||
assertEqual(97, Fixtures.Primes.max());
|
||||
assertEqual(2, [ -9, -8, -7, -6, -4, -3, -2, 0, -1, 2 ].max());
|
||||
assertEqual('sam-', Fixtures.Nicknames.max()); // ?s > ?U
|
||||
}},
|
||||
|
||||
testMin: function() {with(this) {
|
||||
assertEqual(1, Fixtures.Z.min());
|
||||
assertEqual(0, [ 1, 2, 3, 4, 5, 6, 7, 8, 0, 9 ].min());
|
||||
assertEqual('Ulysses', Fixtures.Nicknames.min()); // ?U < ?h
|
||||
}},
|
||||
|
||||
testPartition: function() {with(this) {
|
||||
var result = Fixtures.People.partition(function(person) {
|
||||
return person.name.length < 15;
|
||||
}).invoke('pluck', 'nickname');
|
||||
|
||||
assertEqual(2, result.length);
|
||||
assertEqual('sam-, htonl', result[0].join(', '));
|
||||
assertEqual('noradio, Ulysses', result[1].join(', '));
|
||||
}},
|
||||
|
||||
testPluck: function() {with(this) {
|
||||
assertEqual(Fixtures.Nicknames.join(', '),
|
||||
Fixtures.People.pluck('nickname').join(', '));
|
||||
}},
|
||||
|
||||
testReject: function() {with(this) {
|
||||
assertEqual(0,
|
||||
Fixtures.Nicknames.reject(Prototype.K).length);
|
||||
|
||||
assertEqual('sam-, noradio, htonl',
|
||||
Fixtures.Nicknames.reject(function(nickname) {
|
||||
return nickname != nickname.toLowerCase();
|
||||
}).join(', '));
|
||||
}},
|
||||
|
||||
testSortBy: function() {with(this) {
|
||||
assertEqual('htonl, noradio, sam-, Ulysses',
|
||||
Fixtures.People.sortBy(function(value) {
|
||||
return value.nickname.toLowerCase();
|
||||
}).pluck('nickname').join(', '));
|
||||
}},
|
||||
|
||||
testToArray: function() {with(this) {
|
||||
var result = Fixtures.People.toArray();
|
||||
assert(result != Fixtures.People); // they're different objects...
|
||||
assertEqual(Fixtures.Nicknames.join(', '),
|
||||
result.pluck('nickname').join(', ')); // but the values are the same
|
||||
}},
|
||||
|
||||
testZip: function() {with(this) {
|
||||
var result = [1, 2, 3].zip([4, 5, 6], [7, 8, 9]);
|
||||
assertEqual('[[1, 4, 7], [2, 5, 8], [3, 6, 9]]', result.inspect());
|
||||
|
||||
result = [1, 2, 3].zip([4, 5, 6], [7, 8, 9], function(array) { return array.reverse() });
|
||||
assertEqual('[[7, 4, 1], [8, 5, 2], [9, 6, 3]]', result.inspect());
|
||||
}},
|
||||
|
||||
testSize: function() {with(this) {
|
||||
assertEqual(4, Fixtures.People.size());
|
||||
assertEqual(4, Fixtures.Nicknames.size());
|
||||
assertEqual(26, Fixtures.Primes.size());
|
||||
assertEqual(0, [].size());
|
||||
}}
|
||||
}, 'testlog');
|
||||
// ]]>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
1
test/unit/fixtures/content.html
Normal file
1
test/unit/fixtures/content.html
Normal file
@ -0,0 +1 @@
|
||||
Pack my box with <em>five dozen</em> liquor jugs! Oh, how <strong>quickly</strong> daft jumping zebras vex...
|
1
test/unit/fixtures/hello.js
Normal file
1
test/unit/fixtures/hello.js
Normal file
@ -0,0 +1 @@
|
||||
$("content").update("<H2>Hello world!</H2>");
|
BIN
test/unit/fixtures/logo.gif
Normal file
BIN
test/unit/fixtures/logo.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
280
test/unit/form.html
Normal file
280
test/unit/form.html
Normal file
@ -0,0 +1,280 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>Prototype Unit test file</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<script src="../../dist/prototype.js" type="text/javascript"></script>
|
||||
<script src="../lib/unittest.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" href="../test.css" type="text/css" />
|
||||
<style type="text/css" media="screen">
|
||||
/* <![CDATA[ */
|
||||
#testcss1 { font-size:11px; color: #f00; }
|
||||
#testcss2 { font-size:12px; color: #0f0; display: none; }
|
||||
/* ]]> */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Prototype Unit test file</h1>
|
||||
<p>
|
||||
Test of utility functions in form.js
|
||||
</p>
|
||||
|
||||
<!-- Log output -->
|
||||
<div id="testlog"> </div>
|
||||
|
||||
<form id="form">
|
||||
<input type="text" name="val1" id="input_enabled" value="4" />
|
||||
<div>This is not a form element</div>
|
||||
<input type="text" name="val2" id="input_disabled" disabled="disabled" value="5" />
|
||||
<input type="submit" />
|
||||
</form>
|
||||
<div id="form_wrapper">
|
||||
<form id="form_selects">
|
||||
<select name="vu">
|
||||
<option value="1" selected="selected">One</option>
|
||||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</select>
|
||||
<select id="multiSel1" name="vm[]" multiple="multiple">
|
||||
<option id="multiSel1_opt1" value="1" selected="selected">One</option>
|
||||
<option id="multiSel1_opt2" value="2">Two</option>
|
||||
<option id="multiSel1_opt3" value="3" selected="selected">Three</option>
|
||||
</select>
|
||||
<select name="nvu">
|
||||
<option selected="selected">One</option>
|
||||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</select>
|
||||
<fieldset id="form_fieldset">
|
||||
<select name="nvm[]" multiple="multiple">
|
||||
<option selected="selected">One</option>
|
||||
<option>Two</option>
|
||||
<option selected="selected">Three</option>
|
||||
</select>
|
||||
<select name="evu">
|
||||
<option value="" selected="selected">One</option>
|
||||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</select>
|
||||
<select name="evm[]" multiple="multiple">
|
||||
<option value="" selected="selected">One</option>
|
||||
<option>Two</option>
|
||||
<option selected="selected">Three</option>
|
||||
</select>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<form id="form_getelements">
|
||||
<select id="tf_selectOne" name="tf_selectOne"><option></option><option>1</option></select>
|
||||
<textarea id="tf_textarea" name="tf_textarea"></textarea>
|
||||
<input type="checkbox" id="tf_checkbox" name="tf_checkbox" value="on" />
|
||||
<select id="tf_selectMany" name="tf_selectMany" multiple="multiple"></select>
|
||||
<input type="text" id="tf_text" name="tf_text" />
|
||||
<div>This is not a form element</div>
|
||||
<input type="radio" id="tf_radio" name="tf_radio" value="on" />
|
||||
<input type="hidden" id="tf_hidden" name="tf_hidden" />
|
||||
<input type="password" id="tf_password" name="tf_password" />
|
||||
</form>
|
||||
|
||||
<form id="form_focus">
|
||||
<input type="text" name="focus_disabled" id="focus_disabled" disabled="disabled"/>
|
||||
<input type="submit" name="focus_submit" id="focus_submit" />
|
||||
<input type="button" name="focus_button" id="focus_button" value="button" />
|
||||
<input type="reset" name="focus_reset" id="focus_reset" />
|
||||
<input type="text" name="focus_text" id="focus_text" value="Hello" />
|
||||
</form>
|
||||
|
||||
<!-- Tests follow -->
|
||||
<script type="text/javascript" language="javascript" charset="utf-8">
|
||||
// <![CDATA[
|
||||
var callbackCounter = 0;
|
||||
var timedCounter = 0;
|
||||
|
||||
new Test.Unit.Runner({
|
||||
|
||||
// Make sure to set defaults in the test forms, as some browsers override this
|
||||
// with previously entered values on page reload
|
||||
setup: function(){ with(this) {
|
||||
$('input_enabled').value = '4';
|
||||
$('input_disabled').value = '5';
|
||||
|
||||
$('tf_selectOne').selectedIndex = 0;
|
||||
$('tf_textarea').value = '';
|
||||
$('tf_text').value = '';
|
||||
$('tf_hidden').value = '';
|
||||
$('tf_password').value = '';
|
||||
$('tf_checkbox').checked = false;
|
||||
$('tf_radio').checked = false;
|
||||
}},
|
||||
|
||||
testFormElementEventObserver: function(){ with(this) {
|
||||
var observer = new Form.Element.EventObserver('input_enabled', function(){
|
||||
callbackCounter++;
|
||||
});
|
||||
|
||||
assertEqual(0, callbackCounter);
|
||||
$('input_enabled').value = 'boo!';
|
||||
observer.onElementEvent(); // can't test the event directly, simulating
|
||||
assertEqual(1, callbackCounter);
|
||||
}},
|
||||
|
||||
testFormElementObserver: function(){ with(this) {
|
||||
// First part: regular field
|
||||
var observer = new Form.Element.Observer('input_enabled', 0.5, function() {
|
||||
++timedCounter;
|
||||
});
|
||||
|
||||
// Test it's unchanged yet
|
||||
assertEqual(0, timedCounter);
|
||||
// Test it doesn't change on first check
|
||||
wait(550, function() {
|
||||
assertEqual(0, timedCounter);
|
||||
// Change, test it doesn't immediately change
|
||||
$('input_enabled').value = 'yowza!';
|
||||
assertEqual(0, timedCounter);
|
||||
// Test it changes on next check, but not again on the next
|
||||
wait(550, function() {
|
||||
assertEqual(1, timedCounter);
|
||||
wait(550, function() {
|
||||
assertEqual(1, timedCounter);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Second part: multiple-select! -- Fails before patch in #6593.
|
||||
[1, 2, 3].each(function(index) {
|
||||
$('multiSel1_opt' + index).selected = (1 == index);
|
||||
});
|
||||
timedCounter = 0;
|
||||
observer = new Form.Element.Observer('multiSel1', 0.5, function() {
|
||||
++timedCounter;
|
||||
});
|
||||
|
||||
// Test it's unchanged yet
|
||||
assertEqual(0, timedCounter);
|
||||
// Test it doesn't change on first check
|
||||
wait(550, function() {
|
||||
assertEqual(0, timedCounter);
|
||||
// Change, test it doesn't immediately change
|
||||
// NOTE: it is important that the 3rd be re-selected, for the
|
||||
// serialize form to obtain the expected value :-)
|
||||
$('multiSel1_opt3').selected = true;
|
||||
assertEqual(0, timedCounter);
|
||||
// Test it changes on next check, but not again on the next
|
||||
wait(550, function() {
|
||||
assertEqual(1, timedCounter);
|
||||
wait(550, function() {
|
||||
assertEqual(1, timedCounter);
|
||||
});
|
||||
});
|
||||
});
|
||||
}},
|
||||
|
||||
testFormElementEnabling: function(){ with(this) {
|
||||
assert($('input_disabled').disabled);
|
||||
$('input_disabled').enable();
|
||||
assert(!$('input_disabled').disabled);
|
||||
$('input_disabled').disable();
|
||||
assert($('input_disabled').disabled);
|
||||
|
||||
assert(!$('input_enabled').disabled);
|
||||
$('input_enabled').disable();
|
||||
assert($('input_enabled').disabled);
|
||||
$('input_enabled').enable();
|
||||
assert(!$('input_enabled').disabled);
|
||||
}},
|
||||
|
||||
// due to the lack of a DOM hasFocus() API method,
|
||||
// we're simulating things here a little bit
|
||||
testFormActivating: function(){ with(this) {
|
||||
// Firefox, IE, and Safari 2+
|
||||
function getSelection(element){
|
||||
try {
|
||||
if (typeof element.selectionStart == 'number') {
|
||||
return element.value.substring(element.selectionStart, element.selectionEnd);
|
||||
} else if (document.selection && document.selection.createRange) {
|
||||
return document.selection.createRange().text;
|
||||
}
|
||||
}
|
||||
catch(e){ return null }
|
||||
}
|
||||
|
||||
// Form.focusFirstElement shouldn't focus disabled elements
|
||||
var element = Form.findFirstElement('form_focus');
|
||||
assertEqual('focus_submit',element.id);
|
||||
|
||||
// Test IE doesn't select text on buttons
|
||||
Form.focusFirstElement('form_focus');
|
||||
if(document.selection) assertEqual('', getSelection(element));
|
||||
|
||||
// Form.Field.activate shouldn't select text on buttons
|
||||
element = $('focus_text');
|
||||
assertEqual('', getSelection(element));
|
||||
|
||||
// Form.Field.activate should select text on text input elements
|
||||
element.activate();
|
||||
assertEqual('Hello', getSelection(element));
|
||||
}},
|
||||
|
||||
testFormGetElements: function() {with(this) {
|
||||
var formElements = $('form_getelements').getElements();
|
||||
assertEqual(8, formElements.length);
|
||||
assertEqual('tf_selectOne', formElements[0].id);
|
||||
assertEqual('tf_textarea', formElements[1].id);
|
||||
assertEqual('tf_checkbox', formElements[2].id);
|
||||
assertEqual('tf_selectMany', formElements[3].id);
|
||||
assertEqual('tf_text', formElements[4].id);
|
||||
assertEqual('tf_radio', formElements[5].id);
|
||||
assertEqual('tf_hidden', formElements[6].id);
|
||||
assertEqual('tf_password', formElements[7].id);
|
||||
}},
|
||||
|
||||
testFormGetInputs: function() {with(this){
|
||||
var form = $('form_getelements'), formInputs = Form.getInputs(form);
|
||||
assertEqual(formInputs.length, 5);
|
||||
assert(formInputs instanceof Array);
|
||||
assert(formInputs.all(function(input) { return (input.tagName == "INPUT"); }));
|
||||
|
||||
var formInputs2 = form.getInputs();
|
||||
assertEqual(formInputs2.length, 5);
|
||||
assert(formInputs2 instanceof Array);
|
||||
assert(formInputs2.all(function(input) { return (input.tagName == "INPUT"); }));
|
||||
}},
|
||||
|
||||
testFormSerialize: function() {with(this){
|
||||
assertEqual('tf_selectOne=&tf_textarea=&tf_text=&tf_hidden=&tf_password=',
|
||||
Form.serialize('form_getelements'));
|
||||
|
||||
$('tf_selectOne').selectedIndex = 1;
|
||||
$('tf_textarea').value = "boo hoo!";
|
||||
$('tf_text').value = "123öäü";
|
||||
$('tf_hidden').value = "moo%hoo&test";
|
||||
$('tf_password').value = 'sekrit code';
|
||||
$('tf_checkbox').checked = true;
|
||||
$('tf_radio').checked = true;
|
||||
|
||||
assertEqual(
|
||||
'tf_selectOne=1&tf_textarea=boo%20hoo!&tf_checkbox=on&tf_text=123%C3%B6%C3%A4%C3%BC&'+
|
||||
'tf_radio=on&tf_hidden=moo%25hoo%26test&tf_password=sekrit%20code',
|
||||
Form.serialize('form_getelements'));
|
||||
|
||||
// Checks that disabled element is not included in serialized form.
|
||||
$('input_enabled').enable();
|
||||
assertEqual('val1=4', Form.serialize('form'));
|
||||
|
||||
// Checks that select-related serializations work just fine
|
||||
assertEqual('vu=1&vm%5B%5D=1&vm%5B%5D=3&nvu=One&nvm%5B%5D=One&nvm%5B%5D=Three&evu=&evm%5B%5D=&evm%5B%5D=Three', Form.serialize('form_selects'));
|
||||
}},
|
||||
|
||||
testFormSerializeWorksWithNonFormElements: function() {with(this) {
|
||||
assertEqual('nvm%5B%5D=One&nvm%5B%5D=Three&evu=&evm%5B%5D=&evm%5B%5D=Three', Form.serialize('form_fieldset'));
|
||||
assertEqual('vu=1&vm%5B%5D=1&vm%5B%5D=3&nvu=One&nvm%5B%5D=One&nvm%5B%5D=Three&evu=&evm%5B%5D=&evm%5B%5D=Three', Form.serialize('form_wrapper'));
|
||||
}}
|
||||
|
||||
}, 'testlog');
|
||||
// ]]>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
119
test/unit/hash.html
Normal file
119
test/unit/hash.html
Normal file
@ -0,0 +1,119 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>Prototype Unit test file</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<script src="../../dist/prototype.js" type="text/javascript"></script>
|
||||
<script src="../lib/unittest.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" href="../test.css" type="text/css" />
|
||||
<style type="text/css" media="screen">
|
||||
/* <![CDATA[ */
|
||||
#testcss1 { font-size:11px; color: #f00; }
|
||||
#testcss2 { font-size:12px; color: #0f0; display: none; }
|
||||
/* ]]> */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Prototype Unit test file</h1>
|
||||
<p>
|
||||
Test of the Hash.prototype extensions
|
||||
</p>
|
||||
|
||||
<!-- Log output -->
|
||||
<div id="testlog"> </div>
|
||||
|
||||
<!-- Tests follow -->
|
||||
<script type="text/javascript" language="javascript" charset="utf-8">
|
||||
// <![CDATA[
|
||||
|
||||
var Fixtures = {
|
||||
one: { a: 'A#' },
|
||||
|
||||
many: {
|
||||
a: 'A',
|
||||
b: 'B',
|
||||
c: 'C',
|
||||
d: 'D#'
|
||||
},
|
||||
|
||||
functions: {
|
||||
alpha: 'foo',
|
||||
beta: function(n) { return n+1; }
|
||||
},
|
||||
|
||||
multiple: { color: $w('r g b') },
|
||||
multiple_nil: { color: ['r', null, 'g', undefined, 0] },
|
||||
multiple_all_nil: { color: [null, undefined] },
|
||||
multiple_empty: { color: [] },
|
||||
|
||||
value_undefined: { a:"b", c:undefined },
|
||||
value_null: { a:"b", c:null },
|
||||
value_zero: { a:"b", c:0 },
|
||||
|
||||
dangerous: {
|
||||
_each: 'E',
|
||||
map: 'M',
|
||||
keys: 'K',
|
||||
values: 'V',
|
||||
collect: 'C',
|
||||
inject: 'I'
|
||||
}
|
||||
};
|
||||
|
||||
new Test.Unit.Runner({
|
||||
|
||||
testKeys: function(){ with(this) {
|
||||
assertEnumEqual([], $H({}).keys())
|
||||
assertEnumEqual(['a'], $H(Fixtures.one).keys())
|
||||
assertEnumEqual($w('a b c d'), $H(Fixtures.many).keys())
|
||||
assertEnumEqual($w('alpha beta'), $H(Fixtures.functions).keys())
|
||||
}},
|
||||
|
||||
testValues: function(){ with(this) {
|
||||
assertEnumEqual([], $H({}).values())
|
||||
assertEnumEqual(['A#'], $H(Fixtures.one).values())
|
||||
assertEnumEqual($w('A B C D#'), $H(Fixtures.many).values())
|
||||
assertEqual('function', typeof $H(Fixtures.functions).values()[1])
|
||||
assertEqual(2, $H(Fixtures.functions).beta(1))
|
||||
}},
|
||||
|
||||
testMerge: function(){ with(this) {
|
||||
assertEqual($H(Fixtures.many).inspect(), $H(Fixtures.many).merge().inspect());
|
||||
assertEqual($H(Fixtures.many).inspect(), $H(Fixtures.many).merge({}).inspect());
|
||||
assertEqual("#<Hash:{'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D#', 'aaa': 'AAA'}>", $H(Fixtures.many).merge({aaa: 'AAA'}).inspect());
|
||||
assertEqual("#<Hash:{'a': 'A#', 'b': 'B', 'c': 'C', 'd': 'D#'}>", $H(Fixtures.many).merge(Fixtures.one).inspect());
|
||||
}},
|
||||
|
||||
testRemove: function(){ with(this) {
|
||||
var hash = $H(Fixtures.many)
|
||||
var values = hash.remove('b', 'c')
|
||||
assertEnumEqual($w('a d'), hash.keys())
|
||||
assertEnumEqual($w('B C'), values)
|
||||
}},
|
||||
|
||||
testToQueryString: function(){ with(this) {
|
||||
assertEqual('', $H({}).toQueryString())
|
||||
assertEqual('a=A%23', $H(Fixtures.one).toQueryString())
|
||||
assertEqual('a=A&b=B&c=C&d=D%23', $H(Fixtures.many).toQueryString())
|
||||
assertEqual("a=b&c=", $H(Fixtures.value_undefined).toQueryString())
|
||||
assertEqual("a=b&c=", $H(Fixtures.value_null).toQueryString())
|
||||
assertEqual("a=b&c=0", $H(Fixtures.value_zero).toQueryString())
|
||||
assertEqual("color=r&color=g&color=b", $H(Fixtures.multiple).toQueryString())
|
||||
assertEqual("color=r&color=g&color=0", $H(Fixtures.multiple_nil).toQueryString())
|
||||
assertEqual("color=", $H(Fixtures.multiple_all_nil).toQueryString())
|
||||
assertEqual("color=", $H(Fixtures.multiple_empty).toQueryString())
|
||||
|
||||
assertEqual("_each=E&map=M&keys=K&values=V&collect=C&inject=I", Hash.toQueryString(Fixtures.dangerous))
|
||||
}},
|
||||
|
||||
testInspect: function(){ with(this) {
|
||||
assertEqual('#<Hash:{}>', $H({}).inspect());
|
||||
assertEqual("#<Hash:{'a': 'A#'}>", $H(Fixtures.one).inspect());
|
||||
assertEqual("#<Hash:{'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D#'}>", $H(Fixtures.many).inspect());
|
||||
}}
|
||||
}, 'testlog');
|
||||
// ]]>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
118
test/unit/position.html
Normal file
118
test/unit/position.html
Normal file
@ -0,0 +1,118 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>Prototype Unit test file</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<script src="../../dist/prototype.js" type="text/javascript"></script>
|
||||
<script src="../lib/unittest.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" href="../test.css" type="text/css" />
|
||||
<style type="text/css" media="screen">
|
||||
/* <![CDATA[ */
|
||||
#testcss1 { font-size:11px; color: #f00; }
|
||||
#testcss2 { font-size:12px; color: #0f0; display: none; }
|
||||
/* ]]> */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="ensure_scrollbars" style="width:10000px; height:10000px; position:absolute" > </div>
|
||||
|
||||
<h1>Prototype Unit test file</h1>
|
||||
<p>
|
||||
Test of functions in position.js
|
||||
</p>
|
||||
|
||||
<!-- Log output -->
|
||||
<div id="testlog"> </div>
|
||||
|
||||
<div id="body_absolute" style="position: absolute; top: 10px; left: 10px">
|
||||
<div id="absolute_absolute" style="position: absolute; top: 10px; left:10px"> </div>
|
||||
<div id="absolute_relative" style="position: relative; top: 10px; left:10px">
|
||||
<div style="height:10px">test<span id="inline">test</span></div>
|
||||
<div id="absolute_relative_undefined"> </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tests follow -->
|
||||
<script type="text/javascript" language="javascript" charset="utf-8">
|
||||
// <![CDATA[
|
||||
|
||||
var testVar = 'to be updated';
|
||||
|
||||
new Test.Unit.Runner({
|
||||
|
||||
setup: function() {
|
||||
scrollTo(0,0);
|
||||
Position.prepare();
|
||||
Position.includeScrollOffsets = false;
|
||||
},
|
||||
|
||||
teardown: function() {
|
||||
scrollTo(0,0);
|
||||
Position.prepare();
|
||||
Position.includeScrollOffsets = false;
|
||||
},
|
||||
|
||||
testPrepare: function() {with(this) {
|
||||
Position.prepare();
|
||||
assertEqual(0, Position.deltaX);
|
||||
assertEqual(0, Position.deltaY);
|
||||
scrollTo(20,30);
|
||||
Position.prepare();
|
||||
assertEqual(20, Position.deltaX);
|
||||
assertEqual(30, Position.deltaY);
|
||||
}},
|
||||
|
||||
testPositionedOffset: function() {with(this) {
|
||||
assertEnumEqual([10,10],
|
||||
Position.positionedOffset($('body_absolute')));
|
||||
assertEnumEqual([10,10],
|
||||
Position.positionedOffset($('absolute_absolute')));
|
||||
assertEnumEqual([10,10],
|
||||
Position.positionedOffset($('absolute_relative')));
|
||||
assertEnumEqual([0,10],
|
||||
Position.positionedOffset($('absolute_relative_undefined')));
|
||||
}},
|
||||
|
||||
testPage: function() {with(this) {
|
||||
assertEnumEqual([10,10],
|
||||
Position.page($('body_absolute')));
|
||||
assertEnumEqual([20,20],
|
||||
Position.page($('absolute_absolute')));
|
||||
assertEnumEqual([20,20],
|
||||
Position.page($('absolute_relative')));
|
||||
assertEnumEqual([20,30],
|
||||
Position.page($('absolute_relative_undefined')));
|
||||
}},
|
||||
|
||||
testOffsetParent: function() {with(this) {
|
||||
assertEqual('body_absolute', Position.offsetParent($('absolute_absolute')).id);
|
||||
assertEqual('body_absolute', Position.offsetParent($('absolute_relative')).id);
|
||||
assertEqual('absolute_relative', Position.offsetParent($('inline')).id);
|
||||
assertEqual('absolute_relative', Position.offsetParent($('absolute_relative_undefined')).id);
|
||||
}},
|
||||
|
||||
testWithin: function() {with(this) {
|
||||
[true, false].each(function(withScrollOffsets) {
|
||||
Position.includeScrollOffsets = withScrollOffsets;
|
||||
assert(!Position.within($('body_absolute'), 9, 9), 'outside left/top');
|
||||
assert(Position.within($('body_absolute'), 10, 10), 'left/top corner');
|
||||
assert(Position.within($('body_absolute'), 10, 19), 'left/bottom corner');
|
||||
assert(!Position.within($('body_absolute'), 10, 20), 'outside bottom');
|
||||
});
|
||||
|
||||
scrollTo(20,30);
|
||||
Position.prepare();
|
||||
Position.includeScrollOffsets = true;
|
||||
assert(!Position.within($('body_absolute'), 9, 9), 'outside left/top');
|
||||
assert(Position.within($('body_absolute'), 10, 10), 'left/top corner');
|
||||
assert(Position.within($('body_absolute'), 10, 19), 'left/bottom corner');
|
||||
assert(!Position.within($('body_absolute'), 10, 20), 'outside bottom');
|
||||
}}
|
||||
|
||||
}, 'testlog');
|
||||
|
||||
// ]]>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
93
test/unit/range.html
Normal file
93
test/unit/range.html
Normal file
@ -0,0 +1,93 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>Prototype Unit test file</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<script src="../../dist/prototype.js" type="text/javascript"></script>
|
||||
<script src="../lib/unittest.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" href="../test.css" type="text/css" />
|
||||
<style type="text/css" media="screen">
|
||||
/* <![CDATA[ */
|
||||
#testcss1 { font-size:11px; color: #f00; }
|
||||
#testcss2 { font-size:12px; color: #0f0; display: none; }
|
||||
/* ]]> */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Prototype Unit test file</h1>
|
||||
<p>
|
||||
Test of utility functions in range.js
|
||||
</p>
|
||||
|
||||
<!-- Log output -->
|
||||
<div id="testlog"> </div>
|
||||
|
||||
<!-- Tests follow -->
|
||||
<script type="text/javascript" language="javascript" charset="utf-8">
|
||||
// <![CDATA[
|
||||
new Test.Unit.Runner({
|
||||
|
||||
testInclude: function() {with(this) {
|
||||
assert(!$R(0, 0, true).include(0));
|
||||
assert($R(0, 0, false).include(0));
|
||||
|
||||
assert($R(0, 5, true).include(0));
|
||||
assert($R(0, 5, true).include(4));
|
||||
assert(!$R(0, 5, true).include(5));
|
||||
|
||||
assert($R(0, 5, false).include(0));
|
||||
assert($R(0, 5, false).include(5));
|
||||
assert(!$R(0, 5, false).include(6));
|
||||
}},
|
||||
|
||||
testEach: function() {with(this) {
|
||||
var results = [];
|
||||
$R(0, 0, true).each(function(value) {
|
||||
results.push(value);
|
||||
});
|
||||
|
||||
assertEnumEqual([], results);
|
||||
|
||||
results = [];
|
||||
$R(0, 3, false).each(function(value) {
|
||||
results.push(value);
|
||||
});
|
||||
|
||||
assertEnumEqual([0, 1, 2, 3], results);
|
||||
}},
|
||||
|
||||
testAny: function() {with(this) {
|
||||
assert(!$R(1, 1, true).any());
|
||||
assert($R(0, 3, false).any(function(value) {
|
||||
return value == 3;
|
||||
}));
|
||||
}},
|
||||
|
||||
testAll: function() {with(this) {
|
||||
assert($R(1, 1, true).all());
|
||||
assert($R(0, 3, false).all(function(value) {
|
||||
return value <= 3;
|
||||
}));
|
||||
}},
|
||||
|
||||
testToArray: function() {with(this) {
|
||||
assertEnumEqual([], $R(0, 0, true).toArray());
|
||||
assertEnumEqual([0], $R(0, 0, false).toArray());
|
||||
assertEnumEqual([0], $R(0, 1, true).toArray());
|
||||
assertEnumEqual([0, 1], $R(0, 1, false).toArray());
|
||||
assertEnumEqual([-3, -2, -1, 0, 1, 2], $R(-3, 3, true).toArray());
|
||||
assertEnumEqual([-3, -2, -1, 0, 1, 2, 3], $R(-3, 3, false).toArray());
|
||||
}},
|
||||
|
||||
testDefaultsToNotExclusive: function() {with(this) {
|
||||
assertEnumEqual(
|
||||
$R(-3,3), $R(-3,3,false)
|
||||
);
|
||||
}}
|
||||
|
||||
}, 'testlog');
|
||||
// ]]>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
157
test/unit/selector.html
Normal file
157
test/unit/selector.html
Normal file
@ -0,0 +1,157 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>Prototype Unit test file</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<script src="../../dist/prototype.js" type="text/javascript"></script>
|
||||
<script src="../lib/unittest.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" href="../test.css" type="text/css" />
|
||||
<style type="text/css" media="screen">
|
||||
/* <![CDATA[ */
|
||||
#testcss1 { font-size:11px; color: #f00; }
|
||||
#testcss2 { font-size:12px; color: #0f0; display: none; }
|
||||
/* ]]> */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Prototype Unit test file</h1>
|
||||
<p>
|
||||
Test of utility functions in selector.js
|
||||
</p>
|
||||
|
||||
<div id="fixtures" style="display: none">
|
||||
<h1 class="title">Some title <span>here</span></h1>
|
||||
<p id="p" class="first summary">
|
||||
<strong id="strong">This</strong> is a short blurb
|
||||
<a id="link_1" class="first internal" href="#">with a link</a> or
|
||||
<a id="link_2" class="internal highlight" href="#"><em id="em">two</em></a>.
|
||||
Or <cite id="with_title" title="hello world!">three</cite>.
|
||||
</p>
|
||||
<ul id="list">
|
||||
<li id="item_1" class="first"><a id="link_3" href="#" class="external"><span id="span">Another link</span></a></li>
|
||||
<li id="item_2">Some text</li>
|
||||
<li id="item_3" xml:lang="es-us" class="">Otra cosa</li>
|
||||
</ul>
|
||||
|
||||
<!-- this form has a field with the name 'id',
|
||||
therefore its ID property won't be 'troubleForm': -->
|
||||
<form id="troubleForm"><input type="hidden" name="id" /></form>
|
||||
</div>
|
||||
|
||||
<!-- Log output -->
|
||||
<div id="testlog"> </div>
|
||||
|
||||
<!-- Tests follow -->
|
||||
<script type="text/javascript" language="javascript" charset="utf-8">
|
||||
// <![CDATA[
|
||||
new Test.Unit.Runner({
|
||||
testSelectorWithTagName: function() {with(this) {
|
||||
assertEnumEqual($A(document.getElementsByTagName('li')), $$('li'));
|
||||
assertEnumEqual([$('strong')], $$('strong'));
|
||||
assertEnumEqual([], $$('nonexistent'));
|
||||
assertEnumEqual($A(document.getElementsByTagName('*')), $$('*'));
|
||||
}},
|
||||
|
||||
testSelectorWithId: function() {with(this) {
|
||||
assertEnumEqual([$('fixtures')], $$('#fixtures'));
|
||||
assertEnumEqual([], $$('#nonexistent'));
|
||||
assertEnumEqual([$('troubleForm')], $$('#troubleForm'));
|
||||
}},
|
||||
|
||||
testSelectorWithClassName: function() {with(this) {
|
||||
assertEnumEqual($('p', 'link_1', 'item_1'), $$('.first'));
|
||||
assertEnumEqual([], $$('.second'));
|
||||
}},
|
||||
|
||||
testSelectorWithTagNameAndId: function() {with(this) {
|
||||
assertEnumEqual([$('strong')], $$('strong#strong'));
|
||||
assertEnumEqual([], $$('p#strong'));
|
||||
}},
|
||||
|
||||
testSelectorWithTagNameAndClassName: function() {with(this) {
|
||||
assertEnumEqual($('link_1', 'link_2'), $$('a.internal'));
|
||||
assertEnumEqual([$('link_2')], $$('a.internal.highlight'));
|
||||
assertEnumEqual([$('link_2')], $$('a.highlight.internal'));
|
||||
assertEnumEqual([], $$('a.highlight.internal.nonexistent'));
|
||||
}},
|
||||
|
||||
testSelectorWithIdAndClassName: function() {with(this) {
|
||||
assertEnumEqual([$('link_2')], $$('#link_2.internal'));
|
||||
assertEnumEqual([$('link_2')], $$('.internal#link_2'));
|
||||
assertEnumEqual([$('link_2')], $$('#link_2.internal.highlight'));
|
||||
assertEnumEqual([], $$('#link_2.internal.nonexistent'));
|
||||
}},
|
||||
|
||||
testSelectorWithTagNameAndIdAndClassName: function() {with(this) {
|
||||
assertEnumEqual([$('link_2')], $$('a#link_2.internal'));
|
||||
assertEnumEqual([$('link_2')], $$('a.internal#link_2'));
|
||||
assertEnumEqual([$('item_1')], $$('li#item_1.first'));
|
||||
assertEnumEqual([], $$('li#item_1.nonexistent'));
|
||||
assertEnumEqual([], $$('li#item_1.first.nonexistent'));
|
||||
}},
|
||||
|
||||
test$$MatchesAncestryWithTokensSeparatedByWhitespace: function() {with(this) {
|
||||
assertEnumEqual($('em', 'span'), $$('#fixtures a *'));
|
||||
assertEnumEqual([$('p')], $$('div#fixtures p'));
|
||||
}},
|
||||
|
||||
test$$CombinesResultsWhenMultipleExpressionsArePassed: function() {with(this) {
|
||||
assertEnumEqual($('link_1', 'link_2', 'item_1', 'item_2', 'item_3'), $$('#p a', ' ul#list li '));
|
||||
}},
|
||||
|
||||
testSelectorWithTagNameAndAttributeExistence: function() {with(this) {
|
||||
assertEnumEqual($$('#fixtures h1'), $$('h1[class]'));
|
||||
assertEnumEqual($$('#fixtures h1'), $$('h1[CLASS]'));
|
||||
assertEnumEqual([$('item_3')], $$('li#item_3[class]'));
|
||||
}},
|
||||
|
||||
testSelectorWithTagNameAndSpecificAttributeValue: function() {with(this) {
|
||||
assertEnumEqual($('link_1', 'link_2', 'link_3'), $$('a[href="#"]'));
|
||||
assertEnumEqual($('link_1', 'link_2', 'link_3'), $$('a[href=#]'));
|
||||
}},
|
||||
|
||||
testSelectorWithTagNameAndWhitespaceTokenizedAttributeValue: function() {with(this) {
|
||||
assertEnumEqual($('link_1', 'link_2'), $$('a[class~="internal"]'));
|
||||
assertEnumEqual($('link_1', 'link_2'), $$('a[class~=internal]'));
|
||||
}},
|
||||
|
||||
testSelectorWithUniversalAndHyphenTokenizedAttributeValue: function() {with(this) {
|
||||
assertEnumEqual([$('item_3')], $$('*[xml:lang|="es"]'));
|
||||
assertEnumEqual([$('item_3')], $$('*[xml:lang|="ES"]'));
|
||||
}},
|
||||
|
||||
testSelectorWithTagNameAndNegatedAttributeValue: function() {with(this) {
|
||||
assertEnumEqual([], $$('a[href!=#]'));
|
||||
}},
|
||||
|
||||
test$$WithNestedAttributeSelectors: function() {with(this) {
|
||||
assertEnumEqual([$('strong')], $$('div[style] p[id] strong'));
|
||||
}},
|
||||
|
||||
testSelectorWithMultipleConditions: function() {with(this) {
|
||||
assertEnumEqual([$('link_3')], $$('a[class~=external][href="#"]'));
|
||||
assertEnumEqual([], $$('a[class~=external][href!="#"]'));
|
||||
}},
|
||||
|
||||
testSelectorMatchElements: function() {with(this) {
|
||||
assertElementsMatch(Selector.matchElements($('list').descendants(), 'li'), '#item_1', '#item_2', '#item_3');
|
||||
assertElementsMatch(Selector.matchElements($('fixtures').descendants(), 'a.internal'), '#link_1', '#link_2');
|
||||
assertEnumEqual([], Selector.matchElements($('fixtures').descendants(), 'p.last'));
|
||||
}},
|
||||
|
||||
testSelectorFindElement: function() {with(this) {
|
||||
assertElementMatches(Selector.findElement($('list').descendants(), 'li'), 'li#item_1.first');
|
||||
assertElementMatches(Selector.findElement($('list').descendants(), 'li', 1), 'li#item_2');
|
||||
assertElementMatches(Selector.findElement($('list').descendants(), 'li#item_3'), 'li');
|
||||
assertEqual(undefined, Selector.findElement($('list').descendants(), 'em'));
|
||||
}},
|
||||
|
||||
testSelectorWithSpaceInAttributeValue: function() {with(this) {
|
||||
assertEnumEqual([$('with_title')], $$('cite[title="hello world!"]'));
|
||||
}}
|
||||
}, 'testlog');
|
||||
// ]]>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
337
test/unit/string.html
Normal file
337
test/unit/string.html
Normal file
@ -0,0 +1,337 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>Prototype Unit test file</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<script src="../../dist/prototype.js" type="text/javascript"></script>
|
||||
<script src="../lib/unittest.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" href="../test.css" type="text/css" />
|
||||
<style type="text/css" media="screen">
|
||||
/* <![CDATA[ */
|
||||
#testcss1 { font-size:11px; color: #f00; }
|
||||
#testcss2 { font-size:12px; color: #0f0; display: none; }
|
||||
/* ]]> */
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Prototype Unit test file</h1>
|
||||
<p>
|
||||
Test of utility functions in string.js
|
||||
</p>
|
||||
|
||||
<!-- Log output -->
|
||||
<div id="testlog"> </div>
|
||||
|
||||
<!-- Tests follow -->
|
||||
<script type="text/javascript" language="javascript" charset="utf-8">
|
||||
// <![CDATA[
|
||||
var evalScriptsCounter = 0,
|
||||
largeTextEscaped = '<span>test</span>',
|
||||
largeTextUnescaped = '<span>test</span>';
|
||||
(2048).times(function(){
|
||||
largeTextEscaped += ' ABC';
|
||||
largeTextUnescaped += ' ABC';
|
||||
});
|
||||
|
||||
new Test.Unit.Runner({
|
||||
testInterpret: function(){with(this) {
|
||||
assertIdentical('true', String.interpret(true));
|
||||
assertIdentical('123', String.interpret(123));
|
||||
assertIdentical('foo bar', String.interpret('foo bar'));
|
||||
assertIdentical(
|
||||
'object string',
|
||||
String.interpret({ toString: function(){ return 'object string' } }));
|
||||
|
||||
assertIdentical('0', String.interpret(0));
|
||||
assertIdentical('false', String.interpret(false));
|
||||
assertIdentical('', String.interpret(undefined));
|
||||
assertIdentical('', String.interpret(null));
|
||||
assertIdentical('', String.interpret(''));
|
||||
}},
|
||||
|
||||
testGsubWithReplacementFunction: function() {with(this) {
|
||||
var source = 'foo boo boz';
|
||||
|
||||
assertEqual('Foo Boo BoZ',
|
||||
source.gsub(/[^o]+/, function(match) {
|
||||
return match[0].toUpperCase()
|
||||
}));
|
||||
assertEqual('f2 b2 b1z',
|
||||
source.gsub(/o+/, function(match) {
|
||||
return match[0].length;
|
||||
}));
|
||||
assertEqual('f0 b0 b1z',
|
||||
source.gsub(/o+/, function(match) {
|
||||
return match[0].length % 2;
|
||||
}));
|
||||
|
||||
}},
|
||||
|
||||
testGsubWithReplacementString: function() {with(this) {
|
||||
var source = 'foo boo boz';
|
||||
|
||||
assertEqual('foobooboz',
|
||||
source.gsub(/\s+/, ''));
|
||||
assertEqual(' z',
|
||||
source.gsub(/(.)(o+)/, ''));
|
||||
}},
|
||||
|
||||
testGsubWithReplacementTemplateString: function() {with(this) {
|
||||
var source = 'foo boo boz';
|
||||
|
||||
assertEqual('-oo-#{1}- -oo-#{1}- -o-#{1}-z',
|
||||
source.gsub(/(.)(o+)/, '-#{2}-\\#{1}-'));
|
||||
assertEqual('-foo-f- -boo-b- -bo-b-z',
|
||||
source.gsub(/(.)(o+)/, '-#{0}-#{1}-'));
|
||||
assertEqual('-oo-f- -oo-b- -o-b-z',
|
||||
source.gsub(/(.)(o+)/, '-#{2}-#{1}-'));
|
||||
assertEqual(' z',
|
||||
source.gsub(/(.)(o+)/, '#{3}'));
|
||||
}},
|
||||
|
||||
testSubWithReplacementFunction: function() {with(this) {
|
||||
var source = 'foo boo boz';
|
||||
|
||||
assertEqual('Foo boo boz',
|
||||
source.sub(/[^o]+/, function(match) {
|
||||
return match[0].toUpperCase()
|
||||
}), 1);
|
||||
assertEqual('Foo Boo boz',
|
||||
source.sub(/[^o]+/, function(match) {
|
||||
return match[0].toUpperCase()
|
||||
}, 2), 2);
|
||||
assertEqual(source,
|
||||
source.sub(/[^o]+/, function(match) {
|
||||
return match[0].toUpperCase()
|
||||
}, 0), 0);
|
||||
assertEqual(source,
|
||||
source.sub(/[^o]+/, function(match) {
|
||||
return match[0].toUpperCase()
|
||||
}, -1), -1);
|
||||
}},
|
||||
|
||||
testSubWithReplacementString: function() {with(this) {
|
||||
var source = 'foo boo boz';
|
||||
|
||||
assertEqual('oo boo boz',
|
||||
source.sub(/[^o]+/, ''));
|
||||
assertEqual('oooo boz',
|
||||
source.sub(/[^o]+/, '', 2));
|
||||
assertEqual('-f-oo boo boz',
|
||||
source.sub(/[^o]+/, '-#{0}-'));
|
||||
assertEqual('-f-oo- b-oo boz',
|
||||
source.sub(/[^o]+/, '-#{0}-', 2));
|
||||
}},
|
||||
|
||||
testScan: function() {with(this) {
|
||||
var source = 'foo boo boz', results = [];
|
||||
source.scan(/[o]+/, function(match) {
|
||||
results.push(match[0].length);
|
||||
});
|
||||
assertEnumEqual([2, 2, 1], results);
|
||||
assertEqual(source, source.scan(/x/, fail));
|
||||
}},
|
||||
|
||||
testToArray: function() {with(this) {
|
||||
assertEnumEqual([],''.toArray());
|
||||
assertEnumEqual(['a'],'a'.toArray());
|
||||
assertEnumEqual(['a','b'],'ab'.toArray());
|
||||
assertEnumEqual(['f','o','o'],'foo'.toArray());
|
||||
}},
|
||||
|
||||
/*
|
||||
Note that camelize() differs from its Rails counterpart,
|
||||
as it is optimized for dealing with JavaScript object
|
||||
properties in conjunction with CSS property names:
|
||||
- Looks for dashes, not underscores
|
||||
- CamelCases first word if there is a front dash
|
||||
*/
|
||||
testCamelize: function() {with(this) {
|
||||
assertEqual('', ''.camelize());
|
||||
assertEqual('', '-'.camelize());
|
||||
assertEqual('foo', 'foo'.camelize());
|
||||
assertEqual('foo_bar', 'foo_bar'.camelize());
|
||||
assertEqual('FooBar', '-foo-bar'.camelize());
|
||||
assertEqual('FooBar', 'FooBar'.camelize());
|
||||
|
||||
assertEqual('fooBar', 'foo-bar'.camelize());
|
||||
assertEqual('borderBottomWidth', 'border-bottom-width'.camelize());
|
||||
|
||||
assertEqual('classNameTest','class-name-test'.camelize());
|
||||
assertEqual('classNameTest','className-test'.camelize());
|
||||
assertEqual('classNameTest','class-nameTest'.camelize());
|
||||
|
||||
/* benchmark(function(){
|
||||
'class-name-test'.camelize();
|
||||
},10000); */
|
||||
}},
|
||||
|
||||
testCapitalize: function() {with(this) {
|
||||
assertEqual('',''.capitalize());
|
||||
assertEqual('Ä','ä'.capitalize());
|
||||
assertEqual('A','A'.capitalize());
|
||||
assertEqual('Hello','hello'.capitalize());
|
||||
assertEqual('Hello','HELLO'.capitalize());
|
||||
assertEqual('Hello','Hello'.capitalize());
|
||||
assertEqual('Hello world','hello WORLD'.capitalize());
|
||||
}},
|
||||
|
||||
testUnderscore: function() {with(this) {
|
||||
assertEqual('', ''.underscore());
|
||||
assertEqual('_', '-'.underscore());
|
||||
assertEqual('foo', 'foo'.underscore());
|
||||
assertEqual('foo', 'Foo'.underscore());
|
||||
assertEqual('foo_bar', 'foo_bar'.underscore());
|
||||
assertEqual('border_bottom', 'borderBottom'.underscore());
|
||||
assertEqual('border_bottom_width', 'borderBottomWidth'.underscore());
|
||||
assertEqual('border_bottom_width', 'border-Bottom-Width'.underscore());
|
||||
}},
|
||||
|
||||
testDasherize: function() {with(this) {
|
||||
assertEqual('', ''.dasherize());
|
||||
assertEqual('foo', 'foo'.dasherize());
|
||||
assertEqual('Foo', 'Foo'.dasherize());
|
||||
assertEqual('foo-bar', 'foo-bar'.dasherize());
|
||||
assertEqual('border-bottom-width', 'border_bottom_width'.dasherize());
|
||||
}},
|
||||
|
||||
testTruncate: function() {with(this) {
|
||||
var source = 'foo boo boz foo boo boz foo boo boz foo boo boz';
|
||||
assertEqual(source, source.truncate(source.length));
|
||||
assertEqual('foo boo boz foo boo boz foo...', source.truncate(0));
|
||||
assertEqual('fo...', source.truncate(5));
|
||||
assertEqual('foo b', source.truncate(5, ''));
|
||||
}},
|
||||
|
||||
testStrip: function() {with(this) {
|
||||
assertEqual('hello world', ' hello world '.strip());
|
||||
assertEqual('hello world', 'hello world'.strip());
|
||||
assertEqual('hello \n world', ' hello \n world '.strip());
|
||||
assertEqual('', ' '.strip());
|
||||
}},
|
||||
|
||||
testStripTags: function() {with(this) {
|
||||
assertEqual('hello world', 'hello world'.stripTags());
|
||||
assertEqual('hello world', 'hello <span>world</span>'.stripTags());
|
||||
assertEqual('hello world', '<a href="#" onclick="moo!">hello</a> world'.stripTags());
|
||||
assertEqual('hello world', 'h<b><em>e</em></b>l<i>l</i>o w<span class="moo" id="x"><b>o</b></span>rld'.stripTags());
|
||||
}},
|
||||
|
||||
testStripScripts: function() {with(this) {
|
||||
assertEqual('foo bar', 'foo bar'.stripScripts());
|
||||
assertEqual('foo bar', ('foo <script>boo();<'+'/script>bar').stripScripts());
|
||||
assertEqual('foo bar', ('foo <script type="text/javascript">boo();\nmoo();<'+'/script>bar').stripScripts());
|
||||
}},
|
||||
|
||||
testExtractScripts: function() {with(this) {
|
||||
assertEnumEqual([], 'foo bar'.extractScripts());
|
||||
assertEnumEqual(['boo();'], ('foo <script>boo();<'+'/script>bar').extractScripts());
|
||||
assertEnumEqual(['boo();','boo();\nmoo();'],
|
||||
('foo <script>boo();<'+'/script><script type="text/javascript">boo();\nmoo();<'+'/script>bar').extractScripts());
|
||||
assertEnumEqual(['boo();','boo();\nmoo();'],
|
||||
('foo <script>boo();<'+'/script>blub\nblub<script type="text/javascript">boo();\nmoo();<'+'/script>bar').extractScripts());
|
||||
}},
|
||||
|
||||
testEvalScripts: function() {with(this) {
|
||||
assertEqual(0, evalScriptsCounter);
|
||||
|
||||
('foo <script>evalScriptsCounter++<'+'/script>bar').evalScripts();
|
||||
assertEqual(1, evalScriptsCounter);
|
||||
|
||||
var stringWithScripts = '';
|
||||
(3).times(function(){ stringWithScripts += 'foo <script>evalScriptsCounter++<'+'/script>bar' });
|
||||
stringWithScripts.evalScripts();
|
||||
assertEqual(4, evalScriptsCounter);
|
||||
}},
|
||||
|
||||
testEscapeHTML: function() {with(this) {
|
||||
assertEqual('foo bar', 'foo bar'.escapeHTML());
|
||||
assertEqual('foo <span>bar</span>', 'foo <span>bar</span>'.escapeHTML());
|
||||
assertEqual('foo ß bar', 'foo ß bar'.escapeHTML());
|
||||
|
||||
assertEqual('a<a href="blah">blub</a>b<span><div></div></span>cdef<strong>!!!!</strong>g',
|
||||
'a<a href="blah">blub</a>b<span><div></div></span>cdef<strong>!!!!</strong>g'.escapeHTML());
|
||||
|
||||
assertEqual(largeTextEscaped, largeTextUnescaped.escapeHTML());
|
||||
}},
|
||||
|
||||
testUnescapeHTML: function() {with(this) {
|
||||
assertEqual('foo bar', 'foo bar'.unescapeHTML());
|
||||
assertEqual('foo <span>bar</span>', 'foo <span>bar</span>'.unescapeHTML());
|
||||
assertEqual('foo ß bar', 'foo ß bar'.unescapeHTML());
|
||||
|
||||
assertEqual('a<a href="blah">blub</a>b<span><div></div></span>cdef<strong>!!!!</strong>g',
|
||||
'a<a href="blah">blub</a>b<span><div></div></span>cdef<strong>!!!!</strong>g'.unescapeHTML());
|
||||
|
||||
assertEqual(largeTextUnescaped, largeTextEscaped.unescapeHTML());
|
||||
}},
|
||||
|
||||
testTemplateEvaluation: function() {with(this) {
|
||||
var source = '<tr><td>#{name}</td><td>#{age}</td></tr>';
|
||||
var person = {name: 'Sam', age: 21};
|
||||
var template = new Template(source);
|
||||
|
||||
assertEqual('<tr><td>Sam</td><td>21</td></tr>',
|
||||
template.evaluate(person));
|
||||
assertEqual('<tr><td></td><td></td></tr>',
|
||||
template.evaluate({}));
|
||||
}},
|
||||
|
||||
testTemplateEvaluationWithFalses: function() {with(this) {
|
||||
var source = '<tr><td>#{zero}</td><td>#{false_}</td><td>#{undef}</td><td>#{null_}</td><td>#{empty}</td></tr>';
|
||||
var falses = {zero:0, false_:false, undef:undefined, null_:null, empty:""};
|
||||
var template = new Template(source);
|
||||
|
||||
assertEqual('<tr><td>0</td><td>false</td><td></td><td></td><td></td></tr>',
|
||||
template.evaluate(falses));
|
||||
}},
|
||||
|
||||
testToQueryParams: function() {with(this) {
|
||||
assertEnumEqual([], Object.keys(''.toQueryParams()));
|
||||
assertEnumEqual([], Object.keys('foo?'.toQueryParams()));
|
||||
|
||||
assertEnumEqual(['a', 'b'], Object.keys('foo?a&b'.toQueryParams()));
|
||||
assertEnumEqual(['a', 'b'], Object.keys('foo?a&b#fragment'.toQueryParams()));
|
||||
assertEnumEqual(['a', 'b'], Object.keys('a;b'.toQueryParams(';')));
|
||||
|
||||
var result = 'a'.toQueryParams();
|
||||
assertEqual(undefined, result['a']);
|
||||
assert(result.hasOwnProperty('a'));
|
||||
|
||||
result = 'a&b=c'.toQueryParams();
|
||||
assertEqual(undefined, result['a']);
|
||||
assert(result.hasOwnProperty('a'));
|
||||
assertEqual('c', result['b']);
|
||||
|
||||
result = 'a%20b=c&d=e%20f&g=h'.toQueryParams();
|
||||
assertEqual('c', result['a b']);
|
||||
assertEqual('e f', result['d']);
|
||||
assertEqual('h', result['g']);
|
||||
|
||||
result = 'color=r&color=g&color=b'.toQueryParams();
|
||||
assertEnumEqual(['r', 'g', 'b'], result['color']);
|
||||
assertEnumEqual(['r', 'b'], 'c=r&c=&c=b'.toQueryParams()['c']);
|
||||
assertEqual('blue', 'c=&c=blue'.toQueryParams()['c']);
|
||||
assertEqual('blue', 'c=blue&c='.toQueryParams()['c']);
|
||||
}},
|
||||
|
||||
testInspect: function() {with(this) {
|
||||
assertEqual('\'\'', ''.inspect());
|
||||
assertEqual('\'test\'', 'test'.inspect());
|
||||
assertEqual('\'test \\\'test\\\' "test"\'', 'test \'test\' "test"'.inspect());
|
||||
}},
|
||||
|
||||
testSucc: function() {with(this) {
|
||||
assertEqual('b', 'a'.succ());
|
||||
assertEqual('B', 'A'.succ());
|
||||
assertEqual('1', '0'.succ());
|
||||
assertEqual('abce', 'abcd'.succ());
|
||||
assertEqual('{', 'z'.succ());
|
||||
assertEqual(':', '9'.succ());
|
||||
}}
|
||||
}, 'testlog');
|
||||
// ]]>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user