Compare commits
No commits in common. "master" and "gh-pages" have entirely different histories.
|
@ -1,5 +0,0 @@
|
|||
.DS_Store
|
||||
pkg
|
||||
test/unit/tmp/*
|
||||
doc
|
||||
tmp
|
|
@ -1,20 +0,0 @@
|
|||
[submodule "vendor/unittest_js"]
|
||||
path = vendor/unittest_js
|
||||
url = git://github.com/tobie/unittest_js.git
|
||||
[submodule "vendor/caja_builder"]
|
||||
path = vendor/caja_builder
|
||||
url = git://github.com/tobie/unittest_js_caja_builder.git
|
||||
[submodule "vendor/pdoc"]
|
||||
path = vendor/pdoc
|
||||
url = git://github.com/tobie/pdoc.git
|
||||
[submodule "vendor/sprockets"]
|
||||
path = vendor/sprockets
|
||||
url = git://github.com/sstephenson/sprockets.git
|
||||
|
||||
|
||||
[submodule "vendor/nwmatcher/repository"]
|
||||
path = vendor/nwmatcher/repository
|
||||
url = git://github.com/dperini/nwmatcher.git
|
||||
[submodule "vendor/sizzle/repository"]
|
||||
path = vendor/sizzle/repository
|
||||
url = git://github.com/jeresig/sizzle.git
|
16
LICENSE
16
LICENSE
|
@ -1,16 +0,0 @@
|
|||
Copyright (c) 2005-2008 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.
|
|
@ -1,63 +0,0 @@
|
|||
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.5 and higher
|
||||
* Apple Safari 2.0.4 and higher
|
||||
* Opera 9.25 and higher
|
||||
* Chrome 1.0 and higher
|
||||
|
||||
Using Prototype
|
||||
---------------
|
||||
|
||||
To use Prototype in your application, download the latest release from the
|
||||
Prototype web site (<http://prototypejs.org/download>) and copy
|
||||
`dist/prototype.js` 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 ###
|
||||
|
||||
`prototype.js` is a composite file generated from many source files in
|
||||
the `src/` directory. To build Prototype, you'll need:
|
||||
|
||||
* a copy of the Prototype source tree, either from a distribution tarball or
|
||||
from the Git 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,
|
||||
|
||||
* `rake dist` will preprocess the Prototype source using Sprockets and
|
||||
generate the composite `dist/prototype.js`.
|
||||
* `rake package` will create a distribution tarball in the
|
||||
`pkg/` directory.
|
||||
|
||||
Contributing to Prototype
|
||||
-------------------------
|
||||
|
||||
Check out the Prototype source with
|
||||
|
||||
$ git clone git://github.com/sstephenson/prototype.git
|
||||
$ cd prototype
|
||||
$ git submodule init
|
||||
$ git submodule update vendor/sprockets vendor/pdoc vendor/unittest_js
|
||||
|
||||
Find out how to contribute: <http://prototypejs.org/contribute>.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Please see the online Prototype API: <http://api.prototypejs.org>.
|
273
Rakefile
273
Rakefile
|
@ -1,273 +0,0 @@
|
|||
require 'rake'
|
||||
require 'rake/packagetask'
|
||||
require 'yaml'
|
||||
|
||||
module PrototypeHelper
|
||||
ROOT_DIR = File.expand_path(File.dirname(__FILE__))
|
||||
SRC_DIR = File.join(ROOT_DIR, 'src')
|
||||
DIST_DIR = File.join(ROOT_DIR, 'dist')
|
||||
DOC_DIR = File.join(ROOT_DIR, 'doc')
|
||||
TEMPLATES_DIR = File.join(ROOT_DIR, 'templates')
|
||||
PKG_DIR = File.join(ROOT_DIR, 'pkg')
|
||||
TEST_DIR = File.join(ROOT_DIR, 'test')
|
||||
TEST_UNIT_DIR = File.join(TEST_DIR, 'unit')
|
||||
TMP_DIR = File.join(TEST_UNIT_DIR, 'tmp')
|
||||
VERSION = YAML.load(IO.read(File.join(SRC_DIR, 'constants.yml')))['PROTOTYPE_VERSION']
|
||||
DEFAULT_SELECTOR_ENGINE = 'sizzle'
|
||||
|
||||
%w[sprockets pdoc unittest_js caja_builder].each do |name|
|
||||
$:.unshift File.join(PrototypeHelper::ROOT_DIR, 'vendor', name, 'lib')
|
||||
end
|
||||
|
||||
def self.has_git?
|
||||
begin
|
||||
`git --version`
|
||||
return true
|
||||
rescue Error => e
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def self.require_git
|
||||
return if has_git?
|
||||
puts "\nPrototype requires Git in order to load its dependencies."
|
||||
puts "\nMake sure you've got Git installed and in your path."
|
||||
puts "\nFor more information, visit:\n\n"
|
||||
puts " http://book.git-scm.com/2_installing_git.html"
|
||||
exit
|
||||
end
|
||||
|
||||
def self.sprocketize(options = {})
|
||||
options = {
|
||||
:destination => File.join(DIST_DIR, options[:source]),
|
||||
:strip_comments => true
|
||||
}.merge(options)
|
||||
|
||||
require_sprockets
|
||||
load_path = [SRC_DIR]
|
||||
|
||||
if selector_path = get_selector_engine(options[:selector_engine])
|
||||
load_path << selector_path
|
||||
end
|
||||
|
||||
secretary = Sprockets::Secretary.new(
|
||||
:root => File.join(ROOT_DIR, options[:path]),
|
||||
:load_path => load_path,
|
||||
:source_files => [options[:source]],
|
||||
:strip_comments => options[:strip_comments]
|
||||
)
|
||||
|
||||
secretary.concatenation.save_to(options[:destination])
|
||||
end
|
||||
|
||||
def self.build_doc_for(file)
|
||||
mkdir_p TMP_DIR
|
||||
temp_path = File.join(TMP_DIR, "prototype.temp.js")
|
||||
sprocketize(
|
||||
:path => 'src',
|
||||
:source => file,
|
||||
:destination => temp_path,
|
||||
:selector_engine => ENV['SELECTOR_ENGINE'] || DEFAULT_SELECTOR_ENGINE,
|
||||
:strip_comments => false
|
||||
)
|
||||
rm_rf DOC_DIR
|
||||
|
||||
PDoc.run({
|
||||
:source_files => [temp_path],
|
||||
:destination => DOC_DIR,
|
||||
:index_page => 'README.markdown',
|
||||
:syntax_highlighter => :pygments,
|
||||
:markdown_parser => :bluecloth
|
||||
})
|
||||
|
||||
rm_rf temp_path
|
||||
end
|
||||
|
||||
def self.require_sprockets
|
||||
require_submodule('Sprockets', 'sprockets')
|
||||
end
|
||||
|
||||
def self.require_pdoc
|
||||
require_submodule('PDoc', 'pdoc')
|
||||
end
|
||||
|
||||
def self.require_unittest_js
|
||||
require_submodule('UnittestJS', 'unittest_js')
|
||||
end
|
||||
|
||||
def self.require_caja_builder
|
||||
require_submodule('CajaBuilder', 'caja_builder')
|
||||
end
|
||||
|
||||
def self.get_selector_engine(name)
|
||||
return if name == DEFAULT_SELECTOR_ENGINE || !name
|
||||
submodule_path = File.join(ROOT_DIR, "vendor", name)
|
||||
return submodule_path if File.exist?(File.join(submodule_path, "repository", ".git"))
|
||||
|
||||
get_submodule('the required selector engine', "#{name}/repository")
|
||||
unless File.exist?(submodule_path)
|
||||
puts "The selector engine you required isn't available at vendor/#{name}.\n\n"
|
||||
exit
|
||||
end
|
||||
end
|
||||
|
||||
def self.get_submodule(name, path)
|
||||
require_git
|
||||
puts "\nYou seem to be missing #{name}. Obtaining it via git...\n\n"
|
||||
|
||||
Kernel.system("git submodule init")
|
||||
return true if Kernel.system("git submodule update vendor/#{path}")
|
||||
# If we got this far, something went wrong.
|
||||
puts "\nLooks like it didn't work. Try it manually:\n\n"
|
||||
puts " $ git submodule init"
|
||||
puts " $ git submodule update vendor/#{path}"
|
||||
false
|
||||
end
|
||||
|
||||
def self.require_submodule(name, path)
|
||||
begin
|
||||
require path
|
||||
rescue LoadError => e
|
||||
# Wait until we notice that a submodule is missing before we bother the
|
||||
# user about installing git. (Maybe they brought all the files over
|
||||
# from a different machine.)
|
||||
missing_file = e.message.sub('no such file to load -- ', '')
|
||||
if missing_file == path
|
||||
# Missing a git submodule.
|
||||
retry if get_submodule(name, path)
|
||||
else
|
||||
# Missing a gem.
|
||||
puts "\nIt looks like #{name} is missing the '#{missing_file}' gem. Just run:\n\n"
|
||||
puts " $ gem install #{missing_file}"
|
||||
puts "\nand you should be all set.\n\n"
|
||||
end
|
||||
exit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
task :default => [:dist, :dist_helper, :package, :clean_package_source]
|
||||
|
||||
desc "Builds the distribution."
|
||||
task :dist do
|
||||
PrototypeHelper.sprocketize(
|
||||
:path => 'src',
|
||||
:source => 'prototype.js',
|
||||
:selector_engine => ENV['SELECTOR_ENGINE'] || PrototypeHelper::DEFAULT_SELECTOR_ENGINE
|
||||
)
|
||||
end
|
||||
|
||||
namespace :doc do
|
||||
desc "Builds the documentation."
|
||||
task :build => [:require] do
|
||||
PrototypeHelper.build_doc_for(ENV['SECTION'] ? "#{ENV['SECTION']}.js" : 'prototype.js')
|
||||
end
|
||||
|
||||
task :require do
|
||||
PrototypeHelper.require_pdoc
|
||||
end
|
||||
end
|
||||
|
||||
task :doc => ['doc:build']
|
||||
|
||||
desc "Builds the updating helper."
|
||||
task :dist_helper do
|
||||
PrototypeHelper.sprocketize(:path => 'ext/update_helper', :source => 'prototype_update_helper.js')
|
||||
end
|
||||
|
||||
Rake::PackageTask.new('prototype', PrototypeHelper::VERSION) do |package|
|
||||
package.need_tar_gz = true
|
||||
package.package_dir = PrototypeHelper::PKG_DIR
|
||||
package.package_files.include(
|
||||
'[A-Z]*',
|
||||
'dist/prototype.js',
|
||||
'lib/**',
|
||||
'src/**',
|
||||
'test/**'
|
||||
)
|
||||
end
|
||||
|
||||
task :clean_package_source do
|
||||
rm_rf File.join(PrototypeHelper::PKG_DIR, "prototype-#{PrototypeHelper::VERSION}")
|
||||
end
|
||||
|
||||
task :test => ['test:build', 'test:run']
|
||||
namespace :test do
|
||||
desc 'Runs all the JavaScript unit tests and collects the results'
|
||||
task :run => [:require] do
|
||||
testcases = ENV['TESTCASES']
|
||||
browsers_to_test = ENV['BROWSERS'] && ENV['BROWSERS'].split(',')
|
||||
tests_to_run = ENV['TESTS'] && ENV['TESTS'].split(',')
|
||||
runner = UnittestJS::WEBrickRunner::Runner.new(:test_dir => PrototypeHelper::TMP_DIR)
|
||||
|
||||
Dir[File.join(PrototypeHelper::TMP_DIR, '*_test.html')].each do |file|
|
||||
file = File.basename(file)
|
||||
test = file.sub('_test.html', '')
|
||||
unless tests_to_run && !tests_to_run.include?(test)
|
||||
runner.add_test(file, testcases)
|
||||
end
|
||||
end
|
||||
|
||||
UnittestJS::Browser::SUPPORTED.each do |browser|
|
||||
unless browsers_to_test && !browsers_to_test.include?(browser)
|
||||
runner.add_browser(browser.to_sym)
|
||||
end
|
||||
end
|
||||
|
||||
trap('INT') { runner.teardown; exit }
|
||||
runner.run
|
||||
end
|
||||
|
||||
task :build => [:clean, :dist] do
|
||||
builder = UnittestJS::Builder::SuiteBuilder.new({
|
||||
:input_dir => PrototypeHelper::TEST_UNIT_DIR,
|
||||
:assets_dir => PrototypeHelper::DIST_DIR
|
||||
})
|
||||
selected_tests = (ENV['TESTS'] || '').split(',')
|
||||
builder.collect(*selected_tests)
|
||||
builder.render
|
||||
end
|
||||
|
||||
task :clean => [:require] do
|
||||
UnittestJS::Builder.empty_dir!(PrototypeHelper::TMP_DIR)
|
||||
end
|
||||
|
||||
task :require do
|
||||
PrototypeHelper.require_unittest_js
|
||||
end
|
||||
end
|
||||
|
||||
task :test_units do
|
||||
puts '"rake test_units" is deprecated. Please use "rake test" instead.'
|
||||
end
|
||||
|
||||
task :build_unit_tests do
|
||||
puts '"rake test_units" is deprecated. Please use "rake test:build" instead.'
|
||||
end
|
||||
|
||||
task :clean_tmp do
|
||||
puts '"rake clean_tmp" is deprecated. Please use "rake test:clean" instead.'
|
||||
end
|
||||
|
||||
namespace :caja do
|
||||
task :test => ['test:build', 'test:run']
|
||||
|
||||
namespace :test do
|
||||
task :run => ['rake:test:run']
|
||||
|
||||
task :build => [:require, 'rake:test:clean', :dist] do
|
||||
builder = UnittestJS::CajaBuilder::SuiteBuilder.new({
|
||||
:input_dir => PrototypeHelper::TEST_UNIT_DIR,
|
||||
:assets_dir => PrototypeHelper::DIST_DIR,
|
||||
:whitelist_dir => File.join(PrototypeHelper::TEST_DIR, 'unit', 'caja_whitelists'),
|
||||
:html_attrib_schema => 'html_attrib.json'
|
||||
})
|
||||
selected_tests = (ENV['TESTS'] || '').split(',')
|
||||
builder.collect(*selected_tests)
|
||||
builder.render
|
||||
end
|
||||
end
|
||||
task :require => ['rake:test:require'] do
|
||||
PrototypeHelper.require_caja_builder
|
||||
end
|
||||
end
|
|
@ -0,0 +1,25 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<title>Prototype JavaScript Framework</title>
|
||||
<link rel="stylesheet" href="stylesheets/reset.css" type="text/css" media="screen" charset="utf-8">
|
||||
<link rel="stylesheet" href="stylesheets/main.css" type="text/css" media="screen" title="no title" charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<div id="wrapper">
|
||||
<div id="header">
|
||||
<h1><a href="/"><img src="images/logo.jpg" alt="Prototype JavaScript Framework" /></a></h1>
|
||||
</div>
|
||||
<div id="navigation">
|
||||
</div>
|
||||
<div id="content">
|
||||
{{ content }}
|
||||
</div>
|
||||
<div id="sidebar">
|
||||
</div>
|
||||
<div id="footer">
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,2 +0,0 @@
|
|||
prototype.js
|
||||
prototype_update_helper.js
|
|
@ -1,339 +0,0 @@
|
|||
<!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="../../dist/prototype_update_helper.js" type="text/javascript"></script>
|
||||
<script src="../../vendor/unittest_js/assets/unittest.js" type="text/javascript" charset="utf-8"></script>
|
||||
<link rel="stylesheet" href="../../vendor/unittest_js/assets/unittest.css" type="text/css" charset="utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>Prototype Unit test file</h1>
|
||||
<p>
|
||||
Test of deprecation.js
|
||||
</p>
|
||||
|
||||
<div id="foo">foo</div>
|
||||
<div id="bar">bar</div>
|
||||
<div id="bla">bla</div>
|
||||
|
||||
<!-- Log output -->
|
||||
<div id="testlog"> </div>
|
||||
|
||||
|
||||
<!-- Tests follow -->
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
var log = {
|
||||
message: [],
|
||||
type: []
|
||||
};
|
||||
prototypeUpdateHelper.notify = prototypeUpdateHelper.notify.wrap(function(proceed, message, type) {
|
||||
if (!proceed(message, type)) return; // UpdateHelper#notify return false if the message is not logged.
|
||||
log.message.push(message);
|
||||
log.type.push(type);
|
||||
});
|
||||
|
||||
Array.prototype.beforeLast = function() {
|
||||
return this[this.length - 2];
|
||||
}
|
||||
|
||||
Test.Unit.Testcase.addMethods({
|
||||
assertInfoNotified: function(expected) {
|
||||
var actualMessage = log.message.beforeLast() || log.message.last();
|
||||
var actualType = log.type.beforeLast() || log.type.last();
|
||||
this.assertEqual(expected, actualMessage, 'assertInfoNotified');
|
||||
this.assertEqual('info', actualType, 'assertInfoNotified');
|
||||
log.message.length = 0;
|
||||
log.type.length = 0;
|
||||
},
|
||||
|
||||
assertErrorNotified: function(expected) {
|
||||
var actualMessage = log.message.beforeLast() || log.message.last();
|
||||
var actualType = log.type.beforeLast() || log.type.last();
|
||||
this.assertEqual(expected, actualMessage, 'assertErrorNotified');
|
||||
this.assertEqual('error', actualType, 'assertErrorNotified');
|
||||
log.message.length = 0;
|
||||
log.type.length = 0;
|
||||
},
|
||||
|
||||
assertWarnNotified: function(expected) {
|
||||
var actualMessage = log.message.beforeLast() || log.message.last();
|
||||
var actualType = log.type.beforeLast() || log.type.last();
|
||||
this.assertEqual(expected, actualMessage, 'assertWarnNotified');
|
||||
this.assertEqual('warn', actualType, 'assertWarnNotified');
|
||||
log.message.length = 0;
|
||||
log.type.length = 0;
|
||||
},
|
||||
|
||||
assertNotNotified: function() {
|
||||
this.assertEmpty(log.message, 'assertNotNotified');
|
||||
this.assertEmpty(log.type, 'assertNotNotified');
|
||||
log.message.length = 0;
|
||||
log.type.length = 0;
|
||||
},
|
||||
|
||||
assertEmpty: function(array, message) {
|
||||
this.assertEqual(0, array.length, message || '');
|
||||
}
|
||||
});
|
||||
|
||||
new Test.Unit.Runner({
|
||||
testGetStack: function() {
|
||||
var stack = prototypeUpdateHelper.getStack();
|
||||
if (stack === '') {
|
||||
this.info('UpdaterHelper#getStack is currently not supported on this browser.')
|
||||
} else {
|
||||
this.assertMatch(/prototype_update_helper\.html:\d+\n$/, prototypeUpdateHelper.getStack());
|
||||
}
|
||||
},
|
||||
|
||||
testDisplay: function() {
|
||||
Toggle.display('foo');
|
||||
this.assertInfoNotified('Toggle.display has been deprecated, please use Element.toggle instead.');
|
||||
|
||||
Element.show('foo', 'bar', 'bla');
|
||||
this.assertErrorNotified('Passing an arbitrary number of elements to Element.show is no longer supported.\n' +
|
||||
'Use [id_1, id_2, ...].each(Element.show) or $(id_1, id_2, ...).invoke("show") instead.');
|
||||
|
||||
$('foo', 'bar', 'bla').each(Element.hide);
|
||||
this.assertNotNotified();
|
||||
|
||||
Element.show('foo');
|
||||
this.assertNotNotified();
|
||||
|
||||
Element.hide('foo', 'bar', 'bla');
|
||||
this.assertErrorNotified('Passing an arbitrary number of elements to Element.hide is no longer supported.\n' +
|
||||
'Use [id_1, id_2, ...].each(Element.hide) or $(id_1, id_2, ...).invoke("hide") instead.');
|
||||
|
||||
Element.toggle('foo', 'bar', 'bla');
|
||||
this.assertErrorNotified('Passing an arbitrary number of elements to Element.toggle is no longer supported.\n' +
|
||||
'Use [id_1, id_2, ...].each(Element.toggle) or $(id_1, id_2, ...).invoke("toggle") instead.');
|
||||
},
|
||||
|
||||
testElementStyle: function() {
|
||||
Element.setStyle('foo', { 'fontSize': '18px' });
|
||||
this.assertNotNotified();
|
||||
|
||||
Element.setStyle('foo', { 'font-size': '18px' });
|
||||
this.assertErrorNotified('Use of uncamelized style-property names is no longer supported.\n' +
|
||||
'Use either camelized style-property names or a regular CSS string instead (see online documentation).')
|
||||
|
||||
Element.setStyle('foo', 'font-size: 18px;');
|
||||
this.assertNotNotified();
|
||||
|
||||
$('foo').setStyle({ 'font-size': '18px' });
|
||||
this.assertErrorNotified('Use of uncamelized style-property names is no longer supported.\n' +
|
||||
'Use either camelized style-property names or a regular CSS string instead (see online documentation).')
|
||||
},
|
||||
|
||||
testClassNames: function() {
|
||||
new Element.ClassNames('foo');
|
||||
this.assertInfoNotified('Element.ClassNames has been deprecated.')
|
||||
|
||||
$('foo').classNames();
|
||||
this.assertInfoNotified('Element#classNames has been deprecated.\n' +
|
||||
'If you need to access CSS class names as an array, try: $w(element.classname).')
|
||||
|
||||
Element.getElementsByClassName('foo', 'className');
|
||||
this.assertInfoNotified('Element#getElementsByClassName has been deprecated, please use Element#select instead.')
|
||||
|
||||
document.getElementsByClassName('className');
|
||||
this.assertInfoNotified('document.getElementsByClassName has been deprecated, please use $$ instead.')
|
||||
},
|
||||
|
||||
testDomSelectors: function() {
|
||||
Element.childOf('foo', 'bar');
|
||||
this.assertInfoNotified('Element#childOf has been deprecated, please use Element#descendantOf instead.');
|
||||
|
||||
$('foo').immediateDescendants();
|
||||
this.assertInfoNotified('Element#immediateDescendants has been deprecated, please use Element#childElements instead.');
|
||||
|
||||
$('foo').getElementsBySelector('a');
|
||||
this.assertInfoNotified('Element#getElementsBySelector has been deprecated, please use Element#select instead.');
|
||||
|
||||
$('foo').select('a');
|
||||
this.assertNotNotified();
|
||||
},
|
||||
|
||||
testField: function() {
|
||||
Field.clear('foo', 'bar', 'bla');
|
||||
this.assertErrorNotified('Passing an arbitrary number of elements to Field.clear is no longer supported.\n' +
|
||||
'Use [id_1, id_2, ...].each(Form.Element.clear) or $(id_1, id_2, ...).invoke("clear") instead.');
|
||||
|
||||
Field.present('foo', 'bar', 'bla');
|
||||
this.assertErrorNotified('Passing an arbitrary number of elements to Field.present is no longer supported.\n' +
|
||||
'Use [id_1, id_2, ...].each(Form.Element.present) or $(id_1, id_2, ...).invoke("present") instead.');
|
||||
},
|
||||
|
||||
testInsertion: function() {
|
||||
Insertion.Before('foo', 'text');
|
||||
this.assertInfoNotified('Insertion.Before has been deprecated, please use Element#insert instead.');
|
||||
|
||||
Insertion.Top('foo', 'text');
|
||||
this.assertInfoNotified('Insertion.Top has been deprecated, please use Element#insert instead.');
|
||||
|
||||
Insertion.Bottom('foo', 'text');
|
||||
this.assertInfoNotified('Insertion.Bottom has been deprecated, please use Element#insert instead.');
|
||||
|
||||
Insertion.After('foo', 'text');
|
||||
this.assertInfoNotified('Insertion.After has been deprecated, please use Element#insert instead.');
|
||||
},
|
||||
|
||||
testPosition: function() {
|
||||
Position.prepare('foo');
|
||||
this.assertInfoNotified('Position.prepare has been deprecated.');
|
||||
|
||||
Position.within('foo');
|
||||
this.assertInfoNotified('Position.within has been deprecated.');
|
||||
|
||||
Position.withinIncludingScrolloffsets('foo');
|
||||
this.assertInfoNotified('Position.withinIncludingScrolloffsets has been deprecated.');
|
||||
|
||||
Position.overlap('foo');
|
||||
this.assertInfoNotified('Position.overlap has been deprecated.');
|
||||
|
||||
Position.cumulativeOffset('foo');
|
||||
this.assertInfoNotified('Position.cumulativeOffset has been deprecated, please use Element#cumulativeOffset instead.');
|
||||
|
||||
Position.positionedOffset('foo');
|
||||
this.assertInfoNotified('Position.positionedOffset has been deprecated, please use Element#positionedOffset instead.');
|
||||
|
||||
Position.absolutize('foo');
|
||||
this.assertInfoNotified('Position.absolutize has been deprecated, please use Element#absolutize instead.');
|
||||
|
||||
Position.relativize('foo');
|
||||
this.assertInfoNotified('Position.relativize has been deprecated, please use Element#relativize instead.');
|
||||
|
||||
Position.realOffset('foo');
|
||||
this.assertInfoNotified('Position.realOffset has been deprecated, please use Element#cumulativeScrollOffset instead.');
|
||||
|
||||
Position.offsetParent('foo');
|
||||
this.assertInfoNotified('Position.offsetParent has been deprecated, please use Element#getOffsetParent instead.');
|
||||
|
||||
Position.page('foo');
|
||||
this.assertInfoNotified('Position.page has been deprecated, please use Element#viewportOffset instead.');
|
||||
|
||||
Position.clone('foo', 'bar');
|
||||
this.assertInfoNotified('Position.clone has been deprecated, please use Element#clonePosition instead.');
|
||||
},
|
||||
|
||||
testEvent: function() {
|
||||
Event.unloadCache();
|
||||
this.assertErrorNotified('Event.unloadCache has been deprecated.')
|
||||
},
|
||||
|
||||
testHash: function() {
|
||||
Hash.toQueryString({});
|
||||
this.assertInfoNotified('Hash.toQueryString has been deprecated.\n' +
|
||||
'Use the instance method Hash#toQueryString or Object.toQueryString instead.');
|
||||
|
||||
Hash.toJSON({});
|
||||
this.assertErrorNotified('Hash.toJSON has been removed.\n' +
|
||||
'Use the instance method Hash#toJSON or Object.toJSON instead.');
|
||||
|
||||
var h = $H({ foo: 2 });
|
||||
|
||||
h.remove('foo');
|
||||
this.assertErrorNotified('Hash#remove is no longer supported, use Hash#unset instead.\n' +
|
||||
'Please note that Hash#unset only accepts one argument.');
|
||||
|
||||
h.merge('foo');
|
||||
this.assertWarnNotified('Hash#merge is no longer destructive and now operates on a clone of the Hash instance.\n' + 'If you need a destructive merge, use Hash#update instead.');
|
||||
|
||||
h['foo'];
|
||||
this.assertErrorNotified('Directly accessing a property of an instance of Hash is no longer supported.\n' +
|
||||
'Please use Hash#get(\'foo\') instead.')
|
||||
|
||||
h.foo = 3;
|
||||
this.assertErrorNotified('Directly setting a property on an instance of Hash is no longer supported.\n' +
|
||||
'Please use Hash#set(\'foo\', 3) instead.')
|
||||
|
||||
h.bar = 'bar';
|
||||
h.toJSON();
|
||||
this.assertErrorNotified('Directly setting a property on an instance of Hash is no longer supported.\n' +
|
||||
'Please use Hash#set(\'bar\', \'bar\') instead.')
|
||||
|
||||
h.bar;
|
||||
this.assertErrorNotified('Directly accessing a property of an instance of Hash is no longer supported.\n' +
|
||||
'Please use Hash#get(\'bar\') instead.')
|
||||
|
||||
h.baz = 'baz';
|
||||
h.bar;
|
||||
this.assertErrorNotified('Directly setting a property on an instance of Hash is no longer supported.\n' +
|
||||
'Please use Hash#set(\'baz\', \'baz\') instead.')
|
||||
|
||||
h.set('toJSON', 'arg'); // make sure hash methods are not overwritten
|
||||
this.assertRespondsTo('toJSON', h)
|
||||
},
|
||||
|
||||
testArray: function() {
|
||||
var a = [0, 1, 2, 3];
|
||||
|
||||
a.reduce(function(){});
|
||||
this.assertErrorNotified('Array#reduce is no longer supported.\n' +
|
||||
'This is due to an infortunate naming collision with Mozilla\'s own implementation of Array#reduce which differs completely from Prototype\'s implementation (it\'s in fact similar to Prototype\'s Array#inject).\n' +
|
||||
'Mozilla\'s Array#reduce is already implemented in Firefox 3 (as part of JavaScript 1.8) and is about to be standardized in EcmaScript 3.1.')
|
||||
},
|
||||
|
||||
testClass: function() {
|
||||
Class.create();
|
||||
this.assertInfoNotified('The class API has been fully revised and now allows for mixins and inheritance.\n' +
|
||||
'You can find more about it here: http://prototypejs.org/learn/class-inheritance');
|
||||
Class.create({});
|
||||
this.assertNotNotified();
|
||||
},
|
||||
|
||||
testSelectorInstanceMethods: function() {
|
||||
var selector = new Selector('div');
|
||||
this.assertWarnNotified('The Selector class has been deprecated. Please use the new Prototype.Selector API instead.');
|
||||
|
||||
selector.findElements(document);
|
||||
this.assertWarnNotified('Selector#findElements has been deprecated. Please use the new Prototype.Selector API instead.');
|
||||
|
||||
selector.match(document.documentElement);
|
||||
this.assertWarnNotified('Selector#match has been deprecated. Please use the new Prototype.Selector API instead.');
|
||||
|
||||
selector.toString();
|
||||
this.assertWarnNotified('Selector#toString has been deprecated. Please use the new Prototype.Selector API instead.');
|
||||
|
||||
selector.inspect();
|
||||
this.assertWarnNotified('Selector#inspect has been deprecated. Please use the new Prototype.Selector API instead.');
|
||||
},
|
||||
|
||||
testSelectorMatchElements: function() {
|
||||
Selector.matchElements([], 'div');
|
||||
this.assertWarnNotified('Selector.matchElements has been deprecated. Please use the new Prototype.Selector API instead.');
|
||||
},
|
||||
|
||||
testSelectorFindElement: function() {
|
||||
Selector.findElement([], 'div');
|
||||
this.assertWarnNotified('Selector.findElement has been deprecated. Please use the new Prototype.Selector API instead.');
|
||||
},
|
||||
|
||||
testSelectorFindChildElements: function() {
|
||||
Selector.findChildElements(document, 'div');
|
||||
this.assertWarnNotified('Selector.findChildElements has been deprecated. Please use the new Prototype.Selector API instead.');
|
||||
},
|
||||
|
||||
testLogDeprecationOption: function() {
|
||||
prototypeUpdateHelper.logLevel = UpdateHelper.Warn;
|
||||
var h = $H({ foo: 2 });
|
||||
|
||||
h.merge({ foo: 3 });
|
||||
this.assertWarnNotified('Hash#merge is no longer destructive and now operates on a clone of the Hash instance.\n' + 'If you need a destructive merge, use Hash#update instead.');
|
||||
|
||||
h.remove('foo');
|
||||
this.assertErrorNotified('Hash#remove is no longer supported, use Hash#unset instead.\n' +
|
||||
'Please note that Hash#unset only accepts one argument.');
|
||||
|
||||
document.getElementsByClassName('className');
|
||||
this.assertNotNotified();
|
||||
prototypeUpdateHelper.logLevel = UpdateHelper.Info;
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,416 +0,0 @@
|
|||
//= require "update_helper"
|
||||
|
||||
/* UpdateHelper for Prototype <%= PROTOTYPE_VERSION %> (c) 2008-2009 Tobie Langel
|
||||
*
|
||||
* UpdateHelper for Prototype is freely distributable under the same
|
||||
* terms as Prototype (MIT-style license).
|
||||
* For details, see the Prototype web site: http://www.prototypejs.org/
|
||||
*
|
||||
* Include this file right below prototype.js. All messages
|
||||
* will be logged to the console.
|
||||
*
|
||||
* Note: You can tune the level of warning by redefining
|
||||
* prototypeUpdateHelper.logLevel with one of the appropriate constansts
|
||||
* (UpdateHelper.Info, UpdateHelper.Warn or UpdateHelper.Error), e.g.:
|
||||
*
|
||||
* prototypeUpdateHelper.logLevel = UpdateHelper.Warn;
|
||||
*
|
||||
* This, for example, will prevent deprecation messages from being logged.
|
||||
*
|
||||
* THIS SCRIPT DOES NOT WORK IN INTERNET EXPLORER
|
||||
*--------------------------------------------------------------------------*/
|
||||
|
||||
var prototypeUpdateHelper = new UpdateHelper([
|
||||
{
|
||||
methodName: 'display',
|
||||
namespace: Toggle,
|
||||
message: 'Toggle.display has been deprecated, please use Element.toggle instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'show',
|
||||
namespace: Element.Methods,
|
||||
message: 'Passing an arbitrary number of elements to Element.show is no longer supported.\n' +
|
||||
'Use [id_1, id_2, ...].each(Element.show) or $(id_1, id_2, ...).invoke("show") instead.',
|
||||
type: 'error',
|
||||
condition: function() { return arguments.length > 1 && !Object.isNumber(arguments[1]) }
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'hide',
|
||||
namespace: Element.Methods,
|
||||
message: 'Passing an arbitrary number of elements to Element.hide is no longer supported.\n' +
|
||||
'Use [id_1, id_2, ...].each(Element.hide) or $(id_1, id_2, ...).invoke("hide") instead.',
|
||||
type: 'error',
|
||||
condition: function() { return arguments.length > 1 && !Object.isNumber(arguments[1]) }
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'toggle',
|
||||
namespace: Element.Methods,
|
||||
message: 'Passing an arbitrary number of elements to Element.toggle is no longer supported.\n' +
|
||||
'Use [id_1, id_2, ...].each(Element.toggle) or $(id_1, id_2, ...).invoke("toggle") instead.',
|
||||
type: 'error',
|
||||
condition: function() { return arguments.length > 1 && !Object.isNumber(arguments[1]) }
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'clear',
|
||||
namespace: Form.Element.Methods,
|
||||
message: 'Passing an arbitrary number of elements to Field.clear is no longer supported.\n' +
|
||||
'Use [id_1, id_2, ...].each(Form.Element.clear) or $(id_1, id_2, ...).invoke("clear") instead.',
|
||||
type: 'error',
|
||||
condition: function() { return arguments.length > 1 && !Object.isNumber(arguments[1]) }
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'present',
|
||||
namespace: Form.Element.Methods,
|
||||
message: 'Passing an arbitrary number of elements to Field.present is no longer supported.\n' +
|
||||
'Use [id_1, id_2, ...].each(Form.Element.present) or $(id_1, id_2, ...).invoke("present") instead.',
|
||||
type: 'error',
|
||||
condition: function() { return arguments.length > 1 && !Object.isNumber(arguments[1]) }
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'childOf',
|
||||
namespace: Element.Methods,
|
||||
message: 'Element#childOf has been deprecated, please use Element#descendantOf instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'Before',
|
||||
namespace: Insertion,
|
||||
message: 'Insertion.Before has been deprecated, please use Element#insert instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'Top',
|
||||
namespace: Insertion,
|
||||
message: 'Insertion.Top has been deprecated, please use Element#insert instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'Bottom',
|
||||
namespace: Insertion,
|
||||
message: 'Insertion.Bottom has been deprecated, please use Element#insert instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'After',
|
||||
namespace: Insertion,
|
||||
message: 'Insertion.After has been deprecated, please use Element#insert instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'prepare',
|
||||
namespace: Position,
|
||||
message: 'Position.prepare has been deprecated.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'within',
|
||||
namespace: Position,
|
||||
message: 'Position.within has been deprecated.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'withinIncludingScrolloffsets',
|
||||
namespace: Position,
|
||||
message: 'Position.withinIncludingScrolloffsets has been deprecated.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'overlap',
|
||||
namespace: Position,
|
||||
message: 'Position.overlap has been deprecated.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'cumulativeOffset',
|
||||
namespace: Position,
|
||||
message: 'Position.cumulativeOffset has been deprecated, please use Element#cumulativeOffset instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'positionedOffset',
|
||||
namespace: Position,
|
||||
message: 'Position.positionedOffset has been deprecated, please use Element#positionedOffset instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'absolutize',
|
||||
namespace: Position,
|
||||
message: 'Position.absolutize has been deprecated, please use Element#absolutize instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'relativize',
|
||||
namespace: Position,
|
||||
message: 'Position.relativize has been deprecated, please use Element#relativize instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'realOffset',
|
||||
namespace: Position,
|
||||
message: 'Position.realOffset has been deprecated, please use Element#cumulativeScrollOffset instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'offsetParent',
|
||||
namespace: Position,
|
||||
message: 'Position.offsetParent has been deprecated, please use Element#getOffsetParent instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'page',
|
||||
namespace: Position,
|
||||
message: 'Position.page has been deprecated, please use Element#viewportOffset instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'clone',
|
||||
namespace: Position,
|
||||
message: 'Position.clone has been deprecated, please use Element#clonePosition instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'initialize',
|
||||
namespace: Element.ClassNames.prototype,
|
||||
message: 'Element.ClassNames has been deprecated.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'classNames',
|
||||
namespace: Element.Methods,
|
||||
message: 'Element#classNames has been deprecated.\n' +
|
||||
'If you need to access CSS class names as an array, try: $w(element.classname).'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'setStyle',
|
||||
namespace: Element.Methods,
|
||||
message: 'Use of uncamelized style-property names is no longer supported.\n' +
|
||||
'Use either camelized style-property names or a regular CSS string instead (see online documentation).',
|
||||
type: 'error',
|
||||
condition: function(element, style) {
|
||||
return !Object.isString(style) && Object.keys(style).join('').include('-');
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'getElementsByClassName',
|
||||
namespace: document,
|
||||
message: 'document.getElementsByClassName has been deprecated, please use $$ instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'getElementsByClassName',
|
||||
namespace: Element.Methods,
|
||||
message: 'Element#getElementsByClassName has been deprecated, please use Element#select instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'immediateDescendants',
|
||||
namespace: Element.Methods,
|
||||
message: 'Element#immediateDescendants has been deprecated, please use Element#childElements instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'getElementsBySelector',
|
||||
namespace: Element.Methods,
|
||||
message: 'Element#getElementsBySelector has been deprecated, please use Element#select instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'toQueryString',
|
||||
namespace: Hash,
|
||||
message: 'Hash.toQueryString has been deprecated.\n' +
|
||||
'Use the instance method Hash#toQueryString or Object.toQueryString instead.'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'toJSON',
|
||||
namespace: Hash,
|
||||
message: 'Hash.toJSON has been removed.\n' +
|
||||
'Use the instance method Hash#toJSON or Object.toJSON instead.',
|
||||
type: 'error'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'remove',
|
||||
namespace: Hash.prototype,
|
||||
message: 'Hash#remove is no longer supported, use Hash#unset instead.\n' +
|
||||
'Please note that Hash#unset only accepts one argument.',
|
||||
type: 'error'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'merge',
|
||||
namespace: Hash.prototype,
|
||||
message: 'Hash#merge is no longer destructive and now operates on a clone of the Hash instance.\n' +
|
||||
'If you need a destructive merge, use Hash#update instead.',
|
||||
type: 'warn'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'reduce',
|
||||
namespace: Array.prototype,
|
||||
message: 'Array#reduce is no longer supported.\n' +
|
||||
'This is due to an infortunate naming collision with Mozilla\'s own implementation of Array#reduce which differs completely from Prototype\'s implementation (it\'s in fact similar to Prototype\'s Array#inject).\n' +
|
||||
'Mozilla\'s Array#reduce is already implemented in Firefox 3 (as part of JavaScript 1.8) and is about to be standardized in EcmaScript 3.1.',
|
||||
type: 'error'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'unloadCache',
|
||||
namespace: Event,
|
||||
message: 'Event.unloadCache has been deprecated.',
|
||||
type: 'error'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'create',
|
||||
namespace: Class,
|
||||
message: 'The class API has been fully revised and now allows for mixins and inheritance.\n' +
|
||||
'You can find more about it here: http://prototypejs.org/learn/class-inheritance',
|
||||
condition: function() { return !arguments.length }
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'initialize',
|
||||
namespace: Selector.prototype,
|
||||
message: 'The Selector class has been deprecated. Please use the new Prototype.Selector API instead.',
|
||||
type: 'warn'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'findElements',
|
||||
namespace: Selector.prototype,
|
||||
message: 'Selector#findElements has been deprecated. Please use the new Prototype.Selector API instead.',
|
||||
type: 'warn'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'match',
|
||||
namespace: Selector.prototype,
|
||||
message: 'Selector#match has been deprecated. Please use the new Prototype.Selector API instead.',
|
||||
type: 'warn'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'toString',
|
||||
namespace: Selector.prototype,
|
||||
message: 'Selector#toString has been deprecated. Please use the new Prototype.Selector API instead.',
|
||||
type: 'warn'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'inspect',
|
||||
namespace: Selector.prototype,
|
||||
message: 'Selector#inspect has been deprecated. Please use the new Prototype.Selector API instead.',
|
||||
type: 'warn'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'matchElements',
|
||||
namespace: Selector,
|
||||
message: 'Selector.matchElements has been deprecated. Please use the new Prototype.Selector API instead.',
|
||||
type: 'warn'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'findElement',
|
||||
namespace: Selector,
|
||||
message: 'Selector.findElement has been deprecated. Please use the new Prototype.Selector API instead.',
|
||||
type: 'warn'
|
||||
},
|
||||
|
||||
{
|
||||
methodName: 'findChildElements',
|
||||
namespace: Selector,
|
||||
message: 'Selector.findChildElements has been deprecated. Please use the new Prototype.Selector API instead.',
|
||||
type: 'warn'
|
||||
}
|
||||
]);
|
||||
|
||||
// Special casing for Hash.
|
||||
|
||||
(function() {
|
||||
var __properties = Object.keys(Hash.prototype).concat(['_object', '__properties']);
|
||||
|
||||
var messages = {
|
||||
setting: new Template("Directly setting a property on an instance of Hash is no longer supported.\n" +
|
||||
"Please use Hash#set('#{property}', #{value}) instead."),
|
||||
getting: new Template("Directly accessing a property of an instance of Hash is no longer supported.\n" +
|
||||
"Please use Hash#get('#{property}') instead.")
|
||||
};
|
||||
|
||||
function notify(property, value) {
|
||||
var message = messages[arguments.length == 1 ? 'getting' : 'setting'].evaluate({
|
||||
property: property,
|
||||
value: Object.inspect(value)
|
||||
});
|
||||
prototypeUpdateHelper.notify(message, 'error');
|
||||
}
|
||||
|
||||
function defineSetters(obj, prop) {
|
||||
storeProperties(obj);
|
||||
if (obj.__properties.include(prop)) return;
|
||||
obj.__properties.push(prop);
|
||||
obj.__defineGetter__(prop, function() {
|
||||
checkProperties(this);
|
||||
notify(prop);
|
||||
});
|
||||
obj.__defineSetter__(prop, function(value) {
|
||||
checkProperties(this);
|
||||
notify(prop, value);
|
||||
});
|
||||
}
|
||||
|
||||
function checkProperties(hash) {
|
||||
storeProperties(hash);
|
||||
var current = Object.keys(hash);
|
||||
if (current.length == hash.__properties.length)
|
||||
return;
|
||||
current.each(function(prop) {
|
||||
if (hash.__properties.include(prop)) return;
|
||||
notify(prop, hash[prop]);
|
||||
defineSetters(hash, prop);
|
||||
});
|
||||
}
|
||||
|
||||
function storeProperties(h) {
|
||||
if (typeof h.__properties === 'undefined')
|
||||
h.__properties = __properties.clone();
|
||||
return h;
|
||||
}
|
||||
|
||||
Hash.prototype.set = Hash.prototype.set.wrap(function(proceed, property, value) {
|
||||
defineSetters(this, property);
|
||||
return proceed(property, value);
|
||||
});
|
||||
|
||||
$w('merge update').each(function(name) {
|
||||
Hash.prototype[name] = Hash.prototype[name].wrap(function(proceed, object) {
|
||||
for (var prop in object) defineSetters(this, prop);
|
||||
return proceed(object);
|
||||
});
|
||||
});
|
||||
|
||||
$H(Hash.prototype).each(function(method) {
|
||||
var key = method.key;
|
||||
if (!Object.isFunction(method.value) || key == 'initialize') return;
|
||||
Hash.prototype[key] = Hash.prototype[key].wrap(function(proceed) {
|
||||
checkProperties(this);
|
||||
return proceed.apply(proceed, $A(arguments).splice(1));
|
||||
});
|
||||
});
|
||||
|
||||
Hash.prototype.initialize = Hash.prototype.initialize.wrap(function(proceed, object) {
|
||||
storeProperties(this);
|
||||
for (var prop in object) defineSetters(this, prop);
|
||||
proceed(object);
|
||||
});
|
||||
})();
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/* Update Helper (c) 2008-2009 Tobie Langel
|
||||
*
|
||||
* Requires Prototype >= 1.6.0
|
||||
*
|
||||
* Update Helper is distributable under the same terms as Prototype
|
||||
* (MIT-style license). For details, see the Prototype web site:
|
||||
* http://www.prototypejs.org/
|
||||
*
|
||||
*--------------------------------------------------------------------------*/
|
||||
|
||||
var UpdateHelper = Class.create({
|
||||
logLevel: 0,
|
||||
MessageTemplate: new Template('Update Helper: #{message}\n#{stack}'),
|
||||
Regexp: new RegExp("@" + window.location.protocol + ".*?\\d+\\n", "g"),
|
||||
|
||||
initialize: function(deprecatedMethods) {
|
||||
var notify = function(message, type) {
|
||||
this.notify(message, type);
|
||||
}.bind(this); // Late binding to simplify testing.
|
||||
|
||||
deprecatedMethods.each(function(d) {
|
||||
var condition = d.condition,
|
||||
type = d.type || 'info',
|
||||
message = d.message,
|
||||
namespace = d.namespace,
|
||||
method = d.methodName;
|
||||
|
||||
namespace[method] = (namespace[method] || function() {}).wrap(function(proceed) {
|
||||
var args = $A(arguments).splice(1);
|
||||
if (!condition || condition.apply(this, args)) notify(message, type);
|
||||
return proceed.apply(proceed, args);
|
||||
});
|
||||
});
|
||||
Element.addMethods();
|
||||
},
|
||||
|
||||
notify: function(message, type) {
|
||||
switch(type) {
|
||||
case 'info':
|
||||
if (this.logLevel > UpdateHelper.Info) return false;
|
||||
case 'warn':
|
||||
if (this.logLevel > UpdateHelper.Warn) return false;
|
||||
default:
|
||||
if (this.logLevel > UpdateHelper.Error) return false;
|
||||
}
|
||||
this.log(this.MessageTemplate.evaluate({
|
||||
message: message,
|
||||
stack: this.getStack()
|
||||
}), type);
|
||||
return true;
|
||||
},
|
||||
|
||||
getStack: function() {
|
||||
try {
|
||||
throw new Error("stack");
|
||||
} catch(e) {
|
||||
var match = (e.stack || '').match(this.Regexp);
|
||||
if (match) {
|
||||
return match.reject(function(path) {
|
||||
return (/(prototype|unittest|update_helper)\.js/).test(path);
|
||||
}).join("\n");
|
||||
} else { return ''; }
|
||||
}
|
||||
},
|
||||
|
||||
log: function(message, type) {
|
||||
if (type == 'error') console.error(message);
|
||||
else if (type == 'warn') console.warn(message);
|
||||
else console.log(message);
|
||||
}
|
||||
});
|
||||
|
||||
Object.extend(UpdateHelper, {
|
||||
Info: 0,
|
||||
Warn: 1,
|
||||
Error: 2
|
||||
});
|
||||
|
|
@ -0,0 +1 @@
|
|||
Nothing to see here...
|
116
src/ajax.js
116
src/ajax.js
|
@ -1,116 +0,0 @@
|
|||
/**
|
||||
* == Ajax ==
|
||||
*
|
||||
* Prototype's APIs around the `XmlHttpRequest` object.
|
||||
*
|
||||
* The Prototype framework enables you to deal with Ajax calls in a manner that is
|
||||
* both easy and compatible with all modern browsers.
|
||||
*
|
||||
* Actual requests are made by creating instances of [[Ajax.Request]].
|
||||
*
|
||||
* <h5>Request headers</h5>
|
||||
*
|
||||
* The following headers are sent with all Ajax requests (and can be
|
||||
* overridden with the `requestHeaders` option described below):
|
||||
*
|
||||
* * `X-Requested-With` is set to `XMLHttpRequest`.
|
||||
* * `X-Prototype-Version` is set to Prototype's current version (e.g.,
|
||||
* `1.6.0.3`).
|
||||
* * `Accept` is set to `text/javascript, text/html, application/xml,
|
||||
* text/xml, * / *`
|
||||
* * `Content-type` is automatically determined based on the `contentType`
|
||||
* and `encoding` options.
|
||||
*
|
||||
* <h5>Ajax options</h5>
|
||||
*
|
||||
* All Ajax classes share a common set of _options_ and _callbacks_.
|
||||
* Callbacks are called at various points in the life-cycle of a request, and
|
||||
* always feature the same list of arguments.
|
||||
*
|
||||
* <h5>Common options</h5>
|
||||
*
|
||||
* * `asynchronous` ([[Boolean]]; default `true`): Determines whether
|
||||
* `XMLHttpRequest` is used asynchronously or not. Synchronous usage is
|
||||
* **strongly discouraged** — it halts all script execution for the
|
||||
* duration of the request _and_ blocks the browser UI.
|
||||
* * `contentType` ([[String]]; default `application/x-www-form-urlencoded`):
|
||||
* The `Content-type` header for your request. Change this header if you
|
||||
* want to send data in another format (like XML).
|
||||
* * `encoding` ([[String]]; default `UTF-8`): The encoding for the contents
|
||||
* of your request. It is best left as-is, but should weird encoding issues
|
||||
* arise, you may have to tweak this.
|
||||
* * `method` ([[String]]; default `post`): The HTTP method to use for the
|
||||
* request. The other common possibility is `get`. Abiding by Rails
|
||||
* conventions, Prototype also reacts to other HTTP verbs (such as `put` and
|
||||
* `delete`) by submitting via `post` and adding a extra `_method` parameter
|
||||
* with the originally-requested method.
|
||||
* * `parameters` ([[String]]): The parameters for the request, which will be
|
||||
* encoded into the URL for a `get` method, or into the request body for the
|
||||
* other methods. This can be provided either as a URL-encoded string, a
|
||||
* [[Hash]], or a plain [[Object]].
|
||||
* * `postBody` ([[String]]): Specific contents for the request body on a
|
||||
* `post` method. If it is not provided, the contents of the `parameters`
|
||||
* option will be used instead.
|
||||
* * `requestHeaders` ([[Object]]): A set of key-value pairs, with properties
|
||||
* representing header names.
|
||||
* * `evalJS` ([[Boolean]] | [[String]]; default `true`): Automatically `eval`s
|
||||
* the content of [[Ajax.Response#responseText]] and populates
|
||||
* [[Ajax.Response#responseJSON]] with it if the `Content-type` returned by
|
||||
* the server is set to `application/json`. If the request doesn't obey
|
||||
* same-origin policy, the content is sanitized before evaluation. If you
|
||||
* need to force evalutation, pass `'force'`. To prevent it altogether, pass
|
||||
* `false`.
|
||||
* * `sanitizeJSON` ([[Boolean]]; default is `false` for same-origin requests,
|
||||
* `true` otherwise): Sanitizes the contents of
|
||||
* [[Ajax.Response#responseText]] before evaluating it.
|
||||
*
|
||||
* <h5>Common callbacks</h5>
|
||||
*
|
||||
* When used on individual instances, all callbacks (except `onException`) are
|
||||
* invoked with two parameters: the `XMLHttpRequest` object and the result of
|
||||
* evaluating the `X-JSON` response header, if any (can be `null`).
|
||||
*
|
||||
* For another way of describing their chronological order and which callbacks
|
||||
* are mutually exclusive, see [[Ajax.Request]].
|
||||
*
|
||||
* * `onCreate`: Triggered when the [[Ajax.Request]] object is initialized.
|
||||
* This is _after_ the parameters and the URL have been processed, but
|
||||
* _before_ opening the connection via the XHR object.
|
||||
* * `onUninitialized` (*Not guaranteed*): Invoked just after the XHR object
|
||||
* is created.
|
||||
* * `onLoading` (*Not guaranteed*): Triggered when the underlying XHR object
|
||||
* is being setup, and its connection opened.
|
||||
* * `onLoaded` (*Not guaranteed*): Triggered once the underlying XHR object
|
||||
* is setup, the connection is open, and it is ready to send its actual
|
||||
* request.
|
||||
* * `onInteractive` (*Not guaranteed*): Triggered whenever the requester
|
||||
* receives a part of the response (but not the final part), should it
|
||||
* be sent in several packets.
|
||||
* * `onSuccess`: Invoked when a request completes and its status code is
|
||||
* `undefined` or belongs in the `2xy` family. This is skipped if a
|
||||
* code-specific callback is defined (e.g., `on200`), and happens _before_
|
||||
* `onComplete`.
|
||||
* * `onFailure`: Invoked when a request completes and its status code exists
|
||||
* but _is not_ in the `2xy` family. This is skipped if a code-specific
|
||||
* callback is defined (e.g. `on403`), and happens _before_ `onComplete`.
|
||||
* * `onXYZ` (_with `XYZ` representing any HTTP status code_): Invoked just
|
||||
* after the response is complete _if_ the status code is the exact code
|
||||
* used in the callback name. _Prevents_ execution of `onSuccess` and
|
||||
* `onFailure`. Happens _before_ `onComplete`.
|
||||
* * `onException`: Triggered whenever an XHR error arises. Has a custom
|
||||
* signature: the first argument is the requester (i.e. an [[Ajax.Request]]
|
||||
* instance), and the second is the exception object.
|
||||
* * `onComplete`: Triggered at the _very end_ of a request's life-cycle, after
|
||||
* the request completes, status-specific callbacks are called, and possible
|
||||
* automatic behaviors are processed. Guaranteed to run regardless of what
|
||||
* happened during the request.
|
||||
*
|
||||
**/
|
||||
|
||||
//= require "ajax/ajax"
|
||||
//= require "ajax/responders"
|
||||
//= require "ajax/base"
|
||||
//= require "ajax/request"
|
||||
//= require "ajax/response"
|
||||
//= require "ajax/updater"
|
||||
//= require "ajax/periodical_updater"
|
|
@ -1,21 +0,0 @@
|
|||
/** section: Ajax
|
||||
* Ajax
|
||||
**/
|
||||
|
||||
var Ajax = {
|
||||
getTransport: function() {
|
||||
return Try.these(
|
||||
function() {return new XMLHttpRequest()},
|
||||
function() {return new ActiveXObject('Msxml2.XMLHTTP')},
|
||||
function() {return new ActiveXObject('Microsoft.XMLHTTP')}
|
||||
) || false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Ajax.activeRequestCount -> Number
|
||||
*
|
||||
* Represents the number of active XHR requests triggered through
|
||||
* [[Ajax.Request]], [[Ajax.Updater]], or [[Ajax.PeriodicalUpdater]].
|
||||
**/
|
||||
activeRequestCount: 0
|
||||
};
|
|
@ -1,22 +0,0 @@
|
|||
// Abstract class; does not need documentation.
|
||||
Ajax.Base = Class.create({
|
||||
initialize: function(options) {
|
||||
this.options = {
|
||||
method: 'post',
|
||||
asynchronous: true,
|
||||
contentType: 'application/x-www-form-urlencoded',
|
||||
encoding: 'UTF-8',
|
||||
parameters: '',
|
||||
evalJSON: true,
|
||||
evalJS: true
|
||||
};
|
||||
Object.extend(this.options, options || { });
|
||||
|
||||
this.options.method = this.options.method.toLowerCase();
|
||||
|
||||
if (Object.isString(this.options.parameters))
|
||||
this.options.parameters = this.options.parameters.toQueryParams();
|
||||
else if (Object.isHash(this.options.parameters))
|
||||
this.options.parameters = this.options.parameters.toObject();
|
||||
}
|
||||
});
|
|
@ -1,111 +0,0 @@
|
|||
/** section: Ajax
|
||||
* class Ajax.PeriodicalUpdater
|
||||
*
|
||||
* Periodically performs an Ajax request and updates a container's contents
|
||||
* based on the response text.
|
||||
*
|
||||
* `Ajax.PeriodicalUpdater` behaves like [[Ajax.Updater]], but performs the
|
||||
* update at a prescribed interval, rather than only once. (Note that it is
|
||||
* _not_ a subclass of `Ajax.Updater`; it's a wrapper around it.)
|
||||
*
|
||||
* This class addresses the common need of periodical update, as required by
|
||||
* all sorts of "polling" mechanisms (e.g., an online chatroom or an online
|
||||
* mail client).
|
||||
*
|
||||
* The basic idea is to run a regular [[Ajax.Updater]] at regular intervals,
|
||||
* keeping track of the response text so it can (optionally) react to
|
||||
* receiving the exact same response consecutively.
|
||||
*
|
||||
* <h5>Additional options</h5>
|
||||
*
|
||||
* `Ajax.PeriodicalUpdater` features all the common options and callbacks
|
||||
* described in the [[Ajax section]] — _plus_ those added by `Ajax.Updater`.
|
||||
*
|
||||
* It also provides two new options:
|
||||
*
|
||||
* * `frequency` ([[Number]]; default is `2`): How long, in seconds, to wait
|
||||
* between the end of one request and the beginning of the next.
|
||||
* * `decay` ([[Number]]; default is `1`): The rate at which the `frequency`
|
||||
* grows when the response received is _exactly_ the same as the previous.
|
||||
* The default of `1` means `frequency` will never grow; override the
|
||||
* default if a stale response implies it's worthwhile to poll less often.
|
||||
* If `decay` is set to `2`, for instance, `frequency` will double
|
||||
* (2 seconds, 4 seconds, 8 seconds...) each consecutive time the result
|
||||
* is the same; when the result is different once again, `frequency` will
|
||||
* revert to its original value.
|
||||
*
|
||||
* <h5>Disabling and re-enabling a <code>PeriodicalUpdater</code></h5>
|
||||
*
|
||||
* You can hit the brakes on a running `PeriodicalUpdater` by calling
|
||||
* [[Ajax.PeriodicalUpdater#stop]]. If you wish to re-enable it later, call
|
||||
* [[Ajax.PeriodicalUpdater#start]].
|
||||
*
|
||||
**/
|
||||
|
||||
Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
|
||||
/**
|
||||
* new Ajax.PeriodicalUpdater(container, url[, options])
|
||||
* - container (String | Element): The DOM element whose contents to update
|
||||
* as a result of the Ajax request. Can be a DOM node or a string that
|
||||
* identifies a node's ID.
|
||||
* - url (String): The URL to fetch. When the _same-origin_ policy is in
|
||||
* effect (as it is in most cases), `url` **must** be a relative URL or an
|
||||
* absolute URL that starts with a slash (i.e., it must not begin with
|
||||
* `http`).
|
||||
* - options (Object): Configuration for the request. See the
|
||||
* [[Ajax section]] for more information.
|
||||
*
|
||||
* Creates a new `Ajax.PeriodicalUpdater`.
|
||||
**/
|
||||
initialize: function($super, container, url, options) {
|
||||
$super(options);
|
||||
this.onComplete = this.options.onComplete;
|
||||
|
||||
this.frequency = (this.options.frequency || 2);
|
||||
this.decay = (this.options.decay || 1);
|
||||
|
||||
this.updater = { };
|
||||
this.container = container;
|
||||
this.url = url;
|
||||
|
||||
this.start();
|
||||
},
|
||||
|
||||
/**
|
||||
* Ajax.PeriodicalUpdater#start() -> undefined
|
||||
*
|
||||
* Starts the periodical updater (if it had previously been stopped with
|
||||
* [[Ajax.PeriodicalUpdater#stop]]).
|
||||
**/
|
||||
start: function() {
|
||||
this.options.onComplete = this.updateComplete.bind(this);
|
||||
this.onTimerEvent();
|
||||
},
|
||||
|
||||
/**
|
||||
* Ajax.PeriodicalUpdater#stop() -> undefined
|
||||
*
|
||||
* Stops the periodical updater.
|
||||
*
|
||||
* Also calls the `onComplete` callback, if one has been defined.
|
||||
**/
|
||||
stop: function() {
|
||||
this.updater.options.onComplete = undefined;
|
||||
clearTimeout(this.timer);
|
||||
(this.onComplete || Prototype.emptyFunction).apply(this, arguments);
|
||||
},
|
||||
|
||||
updateComplete: function(response) {
|
||||
if (this.options.decay) {
|
||||
this.decay = (response.responseText == this.lastText ?
|
||||
this.decay * this.options.decay : 1);
|
||||
|
||||
this.lastText = response.responseText;
|
||||
}
|
||||
this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
|
||||
},
|
||||
|
||||
onTimerEvent: function() {
|
||||
this.updater = new Ajax.Updater(this.container, this.url, this.options);
|
||||
}
|
||||
});
|
|
@ -1,271 +0,0 @@
|
|||
/** section: Ajax
|
||||
* class Ajax.Request
|
||||
*
|
||||
* Initiates and processes an Ajax request.
|
||||
*
|
||||
* `Ajax.Request` is a general-purpose class for making HTTP requests.
|
||||
*
|
||||
* <h5>Automatic JavaScript response evaluation</h5>
|
||||
*
|
||||
* If an Ajax request follows the _same-origin policy_ **and** its response
|
||||
* has a JavaScript-related `Content-type`, the content of the `responseText`
|
||||
* property will automatically be passed to `eval`.
|
||||
*
|
||||
* In other words: you don't even need to provide a callback to leverage
|
||||
* pure-JavaScript Ajax responses. This is the convention that drives Rails's
|
||||
* RJS.
|
||||
*
|
||||
* The list of JavaScript-related MIME-types handled by Prototype is:
|
||||
*
|
||||
* * `application/ecmascript`
|
||||
* * `application/javascript`
|
||||
* * `application/x-ecmascript`
|
||||
* * `application/x-javascript`
|
||||
* * `text/ecmascript`
|
||||
* * `text/javascript`
|
||||
* * `text/x-ecmascript`
|
||||
* * `text/x-javascript`
|
||||
*
|
||||
* The MIME-type string is examined in a case-insensitive manner.
|
||||
*
|
||||
* <h5>Methods you may find useful</h5>
|
||||
*
|
||||
* Instances of the `Request` object provide several methods that can come in
|
||||
* handy in your callback functions, especially once the request is complete.
|
||||
*
|
||||
* <h5>Is the response a successful one?</h5>
|
||||
*
|
||||
* The [[Ajax.Request#success]] method examines the XHR object's `status`
|
||||
* property and follows general HTTP guidelines: unknown status is deemed
|
||||
* successful, as is the whole `2xy` status code family. It's a generally
|
||||
* better way of testing your response than the usual
|
||||
* `200 == transport.status`.
|
||||
*
|
||||
* <h5>Getting HTTP response headers</h5>
|
||||
*
|
||||
* While you can obtain response headers from the XHR object using its
|
||||
* `getResponseHeader` method, this makes for verbose code, and several
|
||||
* implementations raise an exception when the header is not found. To make
|
||||
* this easier, you can use the [[Ajax.Response#getHeader]] method, which
|
||||
* delegates to the longer version and returns `null` if an exception occurs:
|
||||
*
|
||||
* new Ajax.Request('/your/url', {
|
||||
* onSuccess: function(response) {
|
||||
* // Note how we brace against null values
|
||||
* if ((response.getHeader('Server') || '').match(/Apache/))
|
||||
* ++gApacheCount;
|
||||
* // Remainder of the code
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* <h5>Evaluating JSON headers</h5>
|
||||
*
|
||||
* Some backends will return JSON not as response text, but in the `X-JSON`
|
||||
* header. In this case, you don't even need to evaluate the returned JSON
|
||||
* yourself, as Prototype automatically does so. It passes the result as the
|
||||
* `headerJSON` property of the [[Ajax.Response]] object. Note that if there
|
||||
* is no such header — or its contents are invalid — `headerJSON` will be set
|
||||
* to `null`.
|
||||
*
|
||||
* new Ajax.Request('/your/url', {
|
||||
* onSuccess: function(transport) {
|
||||
* transport.headerJSON
|
||||
* }
|
||||
* });
|
||||
**/
|
||||
Ajax.Request = Class.create(Ajax.Base, {
|
||||
_complete: false,
|
||||
|
||||
/**
|
||||
* new Ajax.Request(url[, options])
|
||||
* - url (String): The URL to fetch. When the _same-origin_ policy is in
|
||||
* effect (as it is in most cases), `url` **must** be a relative URL or an
|
||||
* absolute URL that starts with a slash (i.e., it must not begin with
|
||||
* `http`).
|
||||
* - options (Object): Configuration for the request. See the
|
||||
* [[Ajax section]] for more information.
|
||||
*
|
||||
* Creates a new `Ajax.Request`.
|
||||
**/
|
||||
initialize: function($super, url, options) {
|
||||
$super(options);
|
||||
this.transport = Ajax.getTransport();
|
||||
this.request(url);
|
||||
},
|
||||
|
||||
request: function(url) {
|
||||
this.url = url;
|
||||
this.method = this.options.method;
|
||||
var params = Object.clone(this.options.parameters);
|
||||
|
||||
if (!['get', 'post'].include(this.method)) {
|
||||
// simulate other verbs over post
|
||||
params['_method'] = this.method;
|
||||
this.method = 'post';
|
||||
}
|
||||
|
||||
this.parameters = params;
|
||||
|
||||
if (params = Object.toQueryString(params)) {
|
||||
// when GET, append parameters to URL
|
||||
if (this.method == 'get')
|
||||
this.url += (this.url.include('?') ? '&' : '?') + params;
|
||||
else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
|
||||
params += '&_=';
|
||||
}
|
||||
|
||||
try {
|
||||
var response = new Ajax.Response(this);
|
||||
if (this.options.onCreate) this.options.onCreate(response);
|
||||
Ajax.Responders.dispatch('onCreate', this, response);
|
||||
|
||||
this.transport.open(this.method.toUpperCase(), this.url,
|
||||
this.options.asynchronous);
|
||||
|
||||
if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
|
||||
|
||||
this.transport.onreadystatechange = this.onStateChange.bind(this);
|
||||
this.setRequestHeaders();
|
||||
|
||||
this.body = this.method == 'post' ? (this.options.postBody || params) : null;
|
||||
this.transport.send(this.body);
|
||||
|
||||
/* Force Firefox to handle ready state 4 for synchronous requests */
|
||||
if (!this.options.asynchronous && this.transport.overrideMimeType)
|
||||
this.onStateChange();
|
||||
|
||||
}
|
||||
catch (e) {
|
||||
this.dispatchException(e);
|
||||
}
|
||||
},
|
||||
|
||||
onStateChange: function() {
|
||||
var readyState = this.transport.readyState;
|
||||
if (readyState > 1 && !((readyState == 4) && this._complete))
|
||||
this.respondToReadyState(this.transport.readyState);
|
||||
},
|
||||
|
||||
setRequestHeaders: function() {
|
||||
var headers = {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'X-Prototype-Version': Prototype.Version,
|
||||
'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
|
||||
};
|
||||
|
||||
if (this.method == 'post') {
|
||||
headers['Content-type'] = this.options.contentType +
|
||||
(this.options.encoding ? '; charset=' + this.options.encoding : '');
|
||||
|
||||
/* Force "Connection: close" for older Mozilla browsers to work
|
||||
* around a bug where XMLHttpRequest sends an incorrect
|
||||
* Content-length header. See Mozilla Bugzilla #246651.
|
||||
*/
|
||||
if (this.transport.overrideMimeType &&
|
||||
(navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
|
||||
headers['Connection'] = 'close';
|
||||
}
|
||||
|
||||
// user-defined headers
|
||||
if (typeof this.options.requestHeaders == 'object') {
|
||||
var extras = this.options.requestHeaders;
|
||||
|
||||
if (Object.isFunction(extras.push))
|
||||
for (var i = 0, length = extras.length; i < length; i += 2)
|
||||
headers[extras[i]] = extras[i+1];
|
||||
else
|
||||
$H(extras).each(function(pair) { headers[pair.key] = pair.value });
|
||||
}
|
||||
|
||||
for (var name in headers)
|
||||
this.transport.setRequestHeader(name, headers[name]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Ajax.Request#success() -> Boolean
|
||||
*
|
||||
* Tests whether the request was successful.
|
||||
**/
|
||||
success: function() {
|
||||
var status = this.getStatus();
|
||||
return !status || (status >= 200 && status < 300);
|
||||
},
|
||||
|
||||
getStatus: function() {
|
||||
try {
|
||||
return this.transport.status || 0;
|
||||
} catch (e) { return 0 }
|
||||
},
|
||||
|
||||
respondToReadyState: function(readyState) {
|
||||
var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
|
||||
|
||||
if (state == 'Complete') {
|
||||
try {
|
||||
this._complete = true;
|
||||
(this.options['on' + response.status]
|
||||
|| this.options['on' + (this.success() ? 'Success' : 'Failure')]
|
||||
|| Prototype.emptyFunction)(response, response.headerJSON);
|
||||
} catch (e) {
|
||||
this.dispatchException(e);
|
||||
}
|
||||
|
||||
var contentType = response.getHeader('Content-type');
|
||||
if (this.options.evalJS == 'force'
|
||||
|| (this.options.evalJS && this.isSameOrigin() && contentType
|
||||
&& contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
|
||||
this.evalResponse();
|
||||
}
|
||||
|
||||
try {
|
||||
(this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
|
||||
Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
|
||||
} catch (e) {
|
||||
this.dispatchException(e);
|
||||
}
|
||||
|
||||
if (state == 'Complete') {
|
||||
// avoid memory leak in MSIE: clean up
|
||||
this.transport.onreadystatechange = Prototype.emptyFunction;
|
||||
}
|
||||
},
|
||||
|
||||
isSameOrigin: function() {
|
||||
var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
|
||||
return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
|
||||
protocol: location.protocol,
|
||||
domain: document.domain,
|
||||
port: location.port ? ':' + location.port : ''
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Ajax.Request#getHeader(name) -> String | null
|
||||
* - name (String): The name of an HTTP header that may have been part of
|
||||
* the response.
|
||||
*
|
||||
* Returns the value of the given response header, or `null` if that header
|
||||
* was not found.
|
||||
**/
|
||||
getHeader: function(name) {
|
||||
try {
|
||||
return this.transport.getResponseHeader(name) || null;
|
||||
} catch (e) { return null; }
|
||||
},
|
||||
|
||||
evalResponse: function() {
|
||||
try {
|
||||
return eval((this.transport.responseText || '').unfilterJSON());
|
||||
} catch (e) {
|
||||
this.dispatchException(e);
|
||||
}
|
||||
},
|
||||
|
||||
dispatchException: function(exception) {
|
||||
(this.options.onException || Prototype.emptyFunction)(this, exception);
|
||||
Ajax.Responders.dispatch('onException', this, exception);
|
||||
}
|
||||
});
|
||||
|
||||
Ajax.Request.Events =
|
||||
['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
|
|
@ -1,121 +0,0 @@
|
|||
/** section: Ajax
|
||||
* Ajax.Responders
|
||||
* includes Enumerable
|
||||
*
|
||||
* A repository of global listeners notified about every step of
|
||||
* Prototype-based Ajax requests.
|
||||
*
|
||||
* Sometimes, you need to provide generic behaviors over all Ajax operations
|
||||
* happening on the page (through [[Ajax.Request]], [[Ajax.Updater]] or
|
||||
* [[Ajax.PeriodicalUpdater]]).
|
||||
*
|
||||
* For instance, you might want to automatically show an indicator when an
|
||||
* Ajax request is ongoing, and hide it when none are. You may well want to
|
||||
* factor out exception handling as well, logging those somewhere on the page
|
||||
* in a custom fashion. The possibilities are myriad.
|
||||
*
|
||||
* To achieve this, Prototype provides `Ajax.Responders`, which lets you
|
||||
* register (and, if you wish, unregister later) _responders_, which are
|
||||
* objects with specially-named methods. These names come from a set of
|
||||
* general callbacks corresponding to different points in time (or outcomes)
|
||||
* of an Ajax request's life cycle.
|
||||
*
|
||||
* For instance, Prototype automatically registers a responder that maintains
|
||||
* a nifty variable: [[Ajax.activeRequestCount]]. This represents, at a given
|
||||
* time, the number of currently active Ajax requests — by monitoring their
|
||||
* `onCreate` and `onComplete` events. The code for this is fairly simple:
|
||||
*
|
||||
* Ajax.Responders.register({
|
||||
* onCreate: function() {
|
||||
* Ajax.activeRequestCount++;
|
||||
* },
|
||||
* onComplete: function() {
|
||||
* Ajax.activeRequestCount--;
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* <h5>Responder callbacks</h5>
|
||||
*
|
||||
* The callbacks for responders are similar to the callbacks described in
|
||||
* the [[Ajax section]], but take a different signature. They're invoked with
|
||||
* three parameters: the requester object (i.e., the corresponding "instance"
|
||||
* of [[Ajax.Request]]), the `XMLHttpRequest` object, and the result of
|
||||
* evaluating the `X-JSON` response header, if any (can be `null`). They also
|
||||
* execute in the context of the responder, bound to the `this` reference.
|
||||
*
|
||||
* * `onCreate`: Triggered whenever a requester object from the `Ajax`
|
||||
* namespace is created, after its parameters are adjusted and before its
|
||||
* XHR connection is opened. This takes *two* arguments: the requester
|
||||
* object and the underlying XHR object.
|
||||
* * `onUninitialized` (*Not guaranteed*): Invoked just after the XHR object
|
||||
* is created.
|
||||
* * `onLoading` (*Not guaranteed*): Triggered when the underlying XHR object
|
||||
* is being setup, and its connection opened.
|
||||
* * `onLoaded` (*Not guaranteed*): Triggered once the underlying XHR object
|
||||
* is setup, the connection is open, and it is ready to send its actual
|
||||
* request.
|
||||
* * `onInteractive` (*Not guaranteed*): Triggered whenever the requester
|
||||
* receives a part of the response (but not the final part), should it
|
||||
* be sent in several packets.
|
||||
* * `onException`: Triggered whenever an XHR error arises. Has a custom
|
||||
* signature: the first argument is the requester (i.e. an [[Ajax.Request]]
|
||||
* instance), and the second is the exception object.
|
||||
* * `onComplete`: Triggered at the _very end_ of a request's life-cycle, after
|
||||
* the request completes, status-specific callbacks are called, and possible
|
||||
* automatic behaviors are processed. Guaranteed to run regardless of what
|
||||
* happened during the request.
|
||||
*
|
||||
**/
|
||||
|
||||
Ajax.Responders = {
|
||||
responders: [],
|
||||
|
||||
_each: function(iterator) {
|
||||
this.responders._each(iterator);
|
||||
},
|
||||
|
||||
/**
|
||||
* Ajax.Responders.register(responder) -> undefined
|
||||
* - responder (Object): A list of functions with keys corresponding to the
|
||||
* names of possible callbacks.
|
||||
*
|
||||
* Add a group of responders to all Ajax requests.
|
||||
**/
|
||||
register: function(responder) {
|
||||
if (!this.include(responder))
|
||||
this.responders.push(responder);
|
||||
},
|
||||
|
||||
/**
|
||||
* Ajax.Responders.unregister(responder) -> undefined
|
||||
* - responder (Object): A list of functions with keys corresponding to the
|
||||
* names of possible callbacks.
|
||||
*
|
||||
* Remove a previously-added group of responders.
|
||||
*
|
||||
* As always, unregistering something requires you to use the very same
|
||||
* object you used at registration. If you plan to use `unregister`, be sure
|
||||
* to assign your responder to a _variable_ before passing it into
|
||||
* [[Ajax.Responders#register]] — don't pass it an object literal.
|
||||
**/
|
||||
unregister: function(responder) {
|
||||
this.responders = this.responders.without(responder);
|
||||
},
|
||||
|
||||
dispatch: function(callback, request, transport, json) {
|
||||
this.each(function(responder) {
|
||||
if (Object.isFunction(responder[callback])) {
|
||||
try {
|
||||
responder[callback].apply(responder, [request, transport, json]);
|
||||
} catch (e) { }
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Object.extend(Ajax.Responders, Enumerable);
|
||||
|
||||
Ajax.Responders.register({
|
||||
onCreate: function() { Ajax.activeRequestCount++ },
|
||||
onComplete: function() { Ajax.activeRequestCount-- }
|
||||
});
|
|
@ -1,170 +0,0 @@
|
|||
/** section: Ajax
|
||||
* class Ajax.Response
|
||||
*
|
||||
* A wrapper class around `XmlHttpRequest` for dealing with HTTP responses
|
||||
* of Ajax requests.
|
||||
*
|
||||
* An instance of `Ajax.Response` is passed as the first argument of all Ajax
|
||||
* requests' callbacks. You _will not_ need to create instances of
|
||||
* `Ajax.Response` yourself.
|
||||
**/
|
||||
|
||||
/**
|
||||
* Ajax.Response#readyState -> Number
|
||||
*
|
||||
* The request's current state.
|
||||
*
|
||||
* `0` corresponds to `"Uninitialized"`, `1` to `"Loading"`, `2` to
|
||||
* `"Loaded"`, `3` to `"Interactive"`, and `4` to `"Complete"`.
|
||||
**/
|
||||
|
||||
/**
|
||||
* Ajax.Response#responseText -> String
|
||||
*
|
||||
* The text body of the response.
|
||||
**/
|
||||
|
||||
/**
|
||||
* Ajax.Response#responseXML -> document | null
|
||||
*
|
||||
* The XML body of the response if the `Content-type` of the request is set
|
||||
* to `application/xml`; `null` otherwise.
|
||||
**/
|
||||
|
||||
/**
|
||||
* Ajax.Response#responseJSON -> Object | Array | null
|
||||
*
|
||||
* The JSON body of the response if the `Content-type` of the request is set
|
||||
* to `application/json`; `null` otherwise.
|
||||
**/
|
||||
|
||||
/**
|
||||
* Ajax.Response#headerJSON -> Object | Array | null
|
||||
*
|
||||
* Auto-evaluated content of the `X-JSON` header if present; `null` otherwise.
|
||||
**/
|
||||
|
||||
/**
|
||||
* Ajax.Response#request -> Ajax.Request | Ajax.Updater
|
||||
*
|
||||
* The request object itself (an instance of [[Ajax.Request]] or
|
||||
* [[Ajax.Updater]]).
|
||||
**/
|
||||
|
||||
/**
|
||||
* Ajax.Response#transport -> XmlHttpRequest
|
||||
*
|
||||
* The native `XmlHttpRequest` object itself.
|
||||
**/
|
||||
|
||||
Ajax.Response = Class.create({
|
||||
// Don't document the constructor; should never be manually instantiated.
|
||||
initialize: function(request){
|
||||
this.request = request;
|
||||
var transport = this.transport = request.transport,
|
||||
readyState = this.readyState = transport.readyState;
|
||||
|
||||
if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
|
||||
this.status = this.getStatus();
|
||||
this.statusText = this.getStatusText();
|
||||
this.responseText = String.interpret(transport.responseText);
|
||||
this.headerJSON = this._getHeaderJSON();
|
||||
}
|
||||
|
||||
if (readyState == 4) {
|
||||
var xml = transport.responseXML;
|
||||
this.responseXML = Object.isUndefined(xml) ? null : xml;
|
||||
this.responseJSON = this._getResponseJSON();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Ajax.Response#status -> Number
|
||||
*
|
||||
* The HTTP status code sent by the server.
|
||||
**/
|
||||
status: 0,
|
||||
|
||||
/**
|
||||
* Ajax.Response#statusText -> String
|
||||
*
|
||||
* The HTTP status text sent by the server.
|
||||
**/
|
||||
statusText: '',
|
||||
|
||||
getStatus: Ajax.Request.prototype.getStatus,
|
||||
|
||||
getStatusText: function() {
|
||||
try {
|
||||
return this.transport.statusText || '';
|
||||
} catch (e) { return '' }
|
||||
},
|
||||
|
||||
/**
|
||||
* Ajax.Response#getHeader(name) -> String | null
|
||||
*
|
||||
* See [[Ajax.Request#getHeader]].
|
||||
**/
|
||||
getHeader: Ajax.Request.prototype.getHeader,
|
||||
|
||||
/**
|
||||
* Ajax.Response#getAllHeaders() -> String | null
|
||||
*
|
||||
* Returns a string containing all headers separated by line breaks. _Does
|
||||
* not__ throw errors if no headers are present the way its native
|
||||
* counterpart does.
|
||||
**/
|
||||
getAllHeaders: function() {
|
||||
try {
|
||||
return this.getAllResponseHeaders();
|
||||
} catch (e) { return null }
|
||||
},
|
||||
|
||||
/**
|
||||
* Ajax.Response#getResponseHeader(name) -> String
|
||||
*
|
||||
* Returns the value of the requested header if present; throws an error
|
||||
* otherwise. This is just a wrapper around the `XmlHttpRequest` method of
|
||||
* the same name.
|
||||
**/
|
||||
getResponseHeader: function(name) {
|
||||
return this.transport.getResponseHeader(name);
|
||||
},
|
||||
|
||||
/**
|
||||
* Ajax.Response#getAllResponseHeaders() -> String
|
||||
*
|
||||
* Returns a string containing all headers separated by line breaks; throws
|
||||
* an error if no headers exist. This is just a wrapper around the
|
||||
* `XmlHttpRequest` method of the same name.
|
||||
**/
|
||||
getAllResponseHeaders: function() {
|
||||
return this.transport.getAllResponseHeaders();
|
||||
},
|
||||
|
||||
_getHeaderJSON: function() {
|
||||
var json = this.getHeader('X-JSON');
|
||||
if (!json) return null;
|
||||
json = decodeURIComponent(escape(json));
|
||||
try {
|
||||
return json.evalJSON(this.request.options.sanitizeJSON ||
|
||||
!this.request.isSameOrigin());
|
||||
} catch (e) {
|
||||
this.request.dispatchException(e);
|
||||
}
|
||||
},
|
||||
|
||||
_getResponseJSON: function() {
|
||||
var options = this.request.options;
|
||||
if (!options.evalJSON || (options.evalJSON != 'force' &&
|
||||
!(this.getHeader('Content-type') || '').include('application/json')) ||
|
||||
this.responseText.blank())
|
||||
return null;
|
||||
try {
|
||||
return this.responseText.evalJSON(options.sanitizeJSON ||
|
||||
!this.request.isSameOrigin());
|
||||
} catch (e) {
|
||||
this.request.dispatchException(e);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,127 +0,0 @@
|
|||
/** section: Ajax
|
||||
* class Ajax.Updater < Ajax.Request
|
||||
*
|
||||
* A class that performs an Ajax request and updates a container's contents
|
||||
* with the contents of the response.
|
||||
*
|
||||
* `Ajax.Updater` is a subclass of [[Ajax.Request]] built for a common
|
||||
* use-case.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* new Ajax.Updater('items', '/items', {
|
||||
* parameters: { text: $F('text') }
|
||||
* });
|
||||
*
|
||||
* This example will make a request to the URL `/items` (with the given
|
||||
* parameters); it will then replace the contents of the element with the ID
|
||||
* of `items` with whatever response it receives.
|
||||
*
|
||||
* <h5>Callbacks</h5>
|
||||
*
|
||||
* `Ajax.Updater` supports all the callbacks listed in the [[Ajax section]].
|
||||
* Note that the `onComplete` callback will be invoked **after** the element
|
||||
* is updated.
|
||||
*
|
||||
* <h5>Additional options</h5>
|
||||
*
|
||||
* `Ajax.Updater` has some options of its own apart from the common options
|
||||
* described in the [[Ajax section]]:
|
||||
*
|
||||
* * `evalScripts` ([[Boolean]]; defaults to `false`): Whether `<script>`
|
||||
* elements in the response text should be evaluated.
|
||||
* * `insertion` ([[String]]): By default, `Element.update` is used, meaning
|
||||
* the contents of the response will replace the entire contents of the
|
||||
* container. You may _instead_ insert the response text without disrupting
|
||||
* existing contents. The `insertion` option takes one of four strings —
|
||||
* `top`, `bottom`, `before`, or `after` — and _inserts_ the contents of the
|
||||
* response in the manner described by [[Element#insert]].
|
||||
*
|
||||
* <h5>More About `evalScripts`</h5>
|
||||
*
|
||||
* If you use `evalScripts: true`, any _inline_ `<script>` block will be evaluated.
|
||||
* This **does not** mean it will be evaluated in the global scope; it won't, and that
|
||||
* has important ramifications for your `var` and `function` statements. Also note
|
||||
* that only inline `<script>` blocks are supported; external scripts are ignored.
|
||||
* See [[String#evalScripts]] for the details.
|
||||
*
|
||||
* <h5>Single container, or success/failure split?</h5>
|
||||
*
|
||||
* The examples above all assume you're going to update the same container
|
||||
* whether your request succeeds or fails. Instead, you may want to update
|
||||
* _only_ for successful requests, or update a _different container_ on failed
|
||||
* requests.
|
||||
*
|
||||
* To achieve this, you can pass an object instead of a DOM element for the
|
||||
* `container` parameter. This object _must_ have a `success` property whose
|
||||
* value identifies the container to be updated on successful requests.
|
||||
*
|
||||
* If you also provide it with a `failure` property, its value will be used as
|
||||
* the container for failed requests.
|
||||
*
|
||||
* In the following code, only successful requests get an update:
|
||||
*
|
||||
* new Ajax.Updater({ success: 'items' }, '/items', {
|
||||
* parameters: { text: $F('text') },
|
||||
* insertion: 'bottom'
|
||||
* });
|
||||
*
|
||||
* This next example assumes failed requests will deliver an error message as
|
||||
* response text — one that should be shown to the user in another area:
|
||||
*
|
||||
* new Ajax.Updater({ success: 'items', failure: 'notice' }, '/items',
|
||||
* parameters: { text: $F('text') },
|
||||
* insertion: 'bottom'
|
||||
* });
|
||||
*
|
||||
**/
|
||||
|
||||
Ajax.Updater = Class.create(Ajax.Request, {
|
||||
/**
|
||||
* new Ajax.Updater(container, url[, options])
|
||||
* - container (String | Element): The DOM element whose contents to update
|
||||
* as a result of the Ajax request. Can be a DOM node or a string that
|
||||
* identifies a node's ID.
|
||||
* - url (String): The URL to fetch. When the _same-origin_ policy is in
|
||||
* effect (as it is in most cases), `url` **must** be a relative URL or an
|
||||
* absolute URL that starts with a slash (i.e., it must not begin with
|
||||
* `http`).
|
||||
* - options (Object): Configuration for the request. See the
|
||||
* [[Ajax section]] for more information.
|
||||
*
|
||||
* Creates a new `Ajax.Updater`.
|
||||
**/
|
||||
initialize: function($super, container, url, options) {
|
||||
this.container = {
|
||||
success: (container.success || container),
|
||||
failure: (container.failure || (container.success ? null : container))
|
||||
};
|
||||
|
||||
options = Object.clone(options);
|
||||
var onComplete = options.onComplete;
|
||||
options.onComplete = (function(response, json) {
|
||||
this.updateContent(response.responseText);
|
||||
if (Object.isFunction(onComplete)) onComplete(response, json);
|
||||
}).bind(this);
|
||||
|
||||
$super(url, options);
|
||||
},
|
||||
|
||||
updateContent: function(responseText) {
|
||||
var receiver = this.container[this.success() ? 'success' : 'failure'],
|
||||
options = this.options;
|
||||
|
||||
if (!options.evalScripts) responseText = responseText.stripScripts();
|
||||
|
||||
if (receiver = $(receiver)) {
|
||||
if (options.insertion) {
|
||||
if (Object.isString(options.insertion)) {
|
||||
var insertion = { }; insertion[options.insertion] = responseText;
|
||||
receiver.insert(insertion);
|
||||
}
|
||||
else options.insertion(receiver, responseText);
|
||||
}
|
||||
else receiver.update(responseText);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1 +0,0 @@
|
|||
PROTOTYPE_VERSION: 1.6.1
|
|
@ -1,278 +0,0 @@
|
|||
/*------------------------------- DEPRECATED -------------------------------*/
|
||||
|
||||
Hash.toQueryString = Object.toQueryString;
|
||||
|
||||
var Toggle = { display: Element.toggle };
|
||||
|
||||
Element.Methods.childOf = Element.Methods.descendantOf;
|
||||
|
||||
var Insertion = {
|
||||
Before: function(element, content) {
|
||||
return Element.insert(element, {before:content});
|
||||
},
|
||||
|
||||
Top: function(element, content) {
|
||||
return Element.insert(element, {top:content});
|
||||
},
|
||||
|
||||
Bottom: function(element, content) {
|
||||
return Element.insert(element, {bottom:content});
|
||||
},
|
||||
|
||||
After: function(element, content) {
|
||||
return Element.insert(element, {after:content});
|
||||
}
|
||||
};
|
||||
|
||||
var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
|
||||
|
||||
// This should be moved to script.aculo.us; notice the deprecated methods
|
||||
// further below, that map to the newer Element methods.
|
||||
var Position = {
|
||||
// set to true if needed, warning: firefox performance problems
|
||||
// NOT neeeded for page scrolling, only if draggable contained in
|
||||
// scrollable elements
|
||||
includeScrollOffsets: false,
|
||||
|
||||
// must be called before calling withinIncludingScrolloffset, every time the
|
||||
// page is scrolled
|
||||
prepare: function() {
|
||||
this.deltaX = window.pageXOffset
|
||||
|| document.documentElement.scrollLeft
|
||||
|| document.body.scrollLeft
|
||||
|| 0;
|
||||
this.deltaY = window.pageYOffset
|
||||
|| document.documentElement.scrollTop
|
||||
|| document.body.scrollTop
|
||||
|| 0;
|
||||
},
|
||||
|
||||
// caches x/y coordinate pair to use with overlap
|
||||
within: function(element, x, y) {
|
||||
if (this.includeScrollOffsets)
|
||||
return this.withinIncludingScrolloffsets(element, x, y);
|
||||
this.xcomp = x;
|
||||
this.ycomp = y;
|
||||
this.offset = Element.cumulativeOffset(element);
|
||||
|
||||
return (y >= this.offset[1] &&
|
||||
y < this.offset[1] + element.offsetHeight &&
|
||||
x >= this.offset[0] &&
|
||||
x < this.offset[0] + element.offsetWidth);
|
||||
},
|
||||
|
||||
withinIncludingScrolloffsets: function(element, x, y) {
|
||||
var offsetcache = Element.cumulativeScrollOffset(element);
|
||||
|
||||
this.xcomp = x + offsetcache[0] - this.deltaX;
|
||||
this.ycomp = y + offsetcache[1] - this.deltaY;
|
||||
this.offset = Element.cumulativeOffset(element);
|
||||
|
||||
return (this.ycomp >= this.offset[1] &&
|
||||
this.ycomp < this.offset[1] + element.offsetHeight &&
|
||||
this.xcomp >= this.offset[0] &&
|
||||
this.xcomp < this.offset[0] + element.offsetWidth);
|
||||
},
|
||||
|
||||
// within must be called directly before
|
||||
overlap: function(mode, element) {
|
||||
if (!mode) return 0;
|
||||
if (mode == 'vertical')
|
||||
return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
|
||||
element.offsetHeight;
|
||||
if (mode == 'horizontal')
|
||||
return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
|
||||
element.offsetWidth;
|
||||
},
|
||||
|
||||
// Deprecation layer -- use newer Element methods now (1.5.2).
|
||||
|
||||
cumulativeOffset: Element.Methods.cumulativeOffset,
|
||||
|
||||
positionedOffset: Element.Methods.positionedOffset,
|
||||
|
||||
absolutize: function(element) {
|
||||
Position.prepare();
|
||||
return Element.absolutize(element);
|
||||
},
|
||||
|
||||
relativize: function(element) {
|
||||
Position.prepare();
|
||||
return Element.relativize(element);
|
||||
},
|
||||
|
||||
realOffset: Element.Methods.cumulativeScrollOffset,
|
||||
|
||||
offsetParent: Element.Methods.getOffsetParent,
|
||||
|
||||
page: Element.Methods.viewportOffset,
|
||||
|
||||
clone: function(source, target, options) {
|
||||
options = options || { };
|
||||
return Element.clonePosition(target, source, options);
|
||||
}
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
|
||||
function iter(name) {
|
||||
return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
|
||||
}
|
||||
|
||||
instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
|
||||
function(element, className) {
|
||||
className = className.toString().strip();
|
||||
var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
|
||||
return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
|
||||
} : function(element, className) {
|
||||
className = className.toString().strip();
|
||||
var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
|
||||
if (!classNames && !className) return elements;
|
||||
|
||||
var nodes = $(element).getElementsByTagName('*');
|
||||
className = ' ' + className + ' ';
|
||||
|
||||
for (var i = 0, child, cn; child = nodes[i]; i++) {
|
||||
if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
|
||||
(classNames && classNames.all(function(name) {
|
||||
return !name.toString().blank() && cn.include(' ' + name + ' ');
|
||||
}))))
|
||||
elements.push(Element.extend(child));
|
||||
}
|
||||
return elements;
|
||||
};
|
||||
|
||||
return function(className, parentElement) {
|
||||
return $(parentElement || document.body).getElementsByClassName(className);
|
||||
};
|
||||
}(Element.Methods);
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
Element.ClassNames = Class.create();
|
||||
Element.ClassNames.prototype = {
|
||||
initialize: function(element) {
|
||||
this.element = $(element);
|
||||
},
|
||||
|
||||
_each: function(iterator) {
|
||||
this.element.className.split(/\s+/).select(function(name) {
|
||||
return name.length > 0;
|
||||
})._each(iterator);
|
||||
},
|
||||
|
||||
set: function(className) {
|
||||
this.element.className = className;
|
||||
},
|
||||
|
||||
add: function(classNameToAdd) {
|
||||
if (this.include(classNameToAdd)) return;
|
||||
this.set($A(this).concat(classNameToAdd).join(' '));
|
||||
},
|
||||
|
||||
remove: function(classNameToRemove) {
|
||||
if (!this.include(classNameToRemove)) return;
|
||||
this.set($A(this).without(classNameToRemove).join(' '));
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
return $A(this).join(' ');
|
||||
}
|
||||
};
|
||||
|
||||
Object.extend(Element.ClassNames.prototype, Enumerable);
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
/** deprecated, section: DOM
|
||||
* class Selector
|
||||
*
|
||||
* A class that queries the document for elements that match a given CSS
|
||||
* selector.
|
||||
**/
|
||||
(function() {
|
||||
window.Selector = Class.create({
|
||||
/** deprecated
|
||||
* new Selector(expression)
|
||||
* - expression (String): A CSS selector.
|
||||
*
|
||||
* Creates a `Selector` with the given CSS selector.
|
||||
**/
|
||||
initialize: function(expression) {
|
||||
this.expression = expression.strip();
|
||||
},
|
||||
|
||||
/** deprecated
|
||||
* Selector#findElements(root) -> [Element...]
|
||||
* - root (Element | document): A "scope" to search within. All results will
|
||||
* be descendants of this node.
|
||||
*
|
||||
* Searches the document for elements that match the instance's CSS
|
||||
* selector.
|
||||
**/
|
||||
findElements: function(rootElement) {
|
||||
return Prototype.Selector.select(this.expression, rootElement);
|
||||
},
|
||||
|
||||
/** deprecated
|
||||
* Selector#match(element) -> Boolean
|
||||
*
|
||||
* Tests whether a `element` matches the instance's CSS selector.
|
||||
**/
|
||||
match: function(element) {
|
||||
return Prototype.Selector.match(element, this.expression);
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
return this.expression;
|
||||
},
|
||||
|
||||
inspect: function() {
|
||||
return "#<Selector: " + this.expression + ">";
|
||||
}
|
||||
});
|
||||
|
||||
Object.extend(Selector, {
|
||||
/** deprecated
|
||||
* Selector.matchElements(elements, expression) -> [Element...]
|
||||
*
|
||||
* Filters the given collection of elements with `expression`.
|
||||
*
|
||||
* The only nodes returned will be those that match the given CSS selector.
|
||||
**/
|
||||
matchElements: Prototype.Selector.filter,
|
||||
|
||||
/** deprecated
|
||||
* Selector.findElement(elements, expression[, index = 0]) -> Element
|
||||
* Selector.findElement(elements[, index = 0]) -> Element
|
||||
*
|
||||
* Returns the `index`th element in the collection that matches
|
||||
* `expression`.
|
||||
*
|
||||
* Returns the `index`th element overall if `expression` is not given.
|
||||
**/
|
||||
findElement: function(elements, expression, index) {
|
||||
index = index || 0;
|
||||
var matchIndex = 0, element;
|
||||
// Match each element individually, since Sizzle.matches does not preserve order
|
||||
for (var i = 0, length = elements.length; i < length; i++) {
|
||||
element = elements[i];
|
||||
if (Prototype.Selector.match(element, expression) && index === matchIndex++) {
|
||||
return Element.extend(element);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/** deprecated
|
||||
* Selector.findChildElements(element, expressions) -> [Element...]
|
||||
*
|
||||
* Searches beneath `element` for any elements that match the selector
|
||||
* (or selectors) specified in `expressions`.
|
||||
**/
|
||||
findChildElements: function(element, expressions) {
|
||||
var selector = expressions.toArray().join(', ');
|
||||
return Prototype.Selector.select(selector, element || document);
|
||||
}
|
||||
});
|
||||
})();
|
35
src/dom.js
35
src/dom.js
|
@ -1,35 +0,0 @@
|
|||
/**
|
||||
* == DOM ==
|
||||
* Extensions to DOM elements, plus other utilities for DOM traversal
|
||||
* and modification.
|
||||
*
|
||||
* Prototype's DOM extensions represent a large portion of where you'll spend
|
||||
* your time. Prototype adds many convenience methods to elements returned by
|
||||
* the [[$]] function. For instance, you can write
|
||||
*
|
||||
* $('comments').addClassName('active').show();
|
||||
*
|
||||
* to get the element with the ID of `comments`, add a class name to it, and
|
||||
* show it (if it was previously hidden).
|
||||
*
|
||||
* In other words, Prototype adds "instance" methods to DOM nodes. This is
|
||||
* made possible by direct extension of the backing DOM objects (in browsers
|
||||
* that support it) and by manual extension of individual nodes (in browsers
|
||||
* that do not).
|
||||
*
|
||||
**/
|
||||
|
||||
/** section: DOM
|
||||
* Prototype
|
||||
*
|
||||
* The Prototype namespace.
|
||||
*
|
||||
**/
|
||||
|
||||
//= require "dom/dom"
|
||||
//= require <selector_engine>
|
||||
//= require "dom/selector"
|
||||
//= require "dom/form"
|
||||
//= require "dom/event"
|
||||
|
||||
Element.addMethods();
|
2454
src/dom/dom.js
2454
src/dom/dom.js
File diff suppressed because it is too large
Load Diff
824
src/dom/event.js
824
src/dom/event.js
|
@ -1,824 +0,0 @@
|
|||
(function() {
|
||||
|
||||
/** section: DOM
|
||||
* class Event
|
||||
*
|
||||
* The namespace for Prototype's event system.
|
||||
*
|
||||
* <h5>Events: a fine mess</h5>
|
||||
*
|
||||
* Event management is one of the really sore spots of cross-browser
|
||||
* scripting.
|
||||
*
|
||||
* True, the prevalent issue is: everybody does it the W3C way, and MSIE
|
||||
* does it another way altogether. But there are quite a few subtler,
|
||||
* sneakier issues here and there waiting to bite your ankle — such as the
|
||||
* `keypress`/`keydown` issue with KHTML-based browsers (Konqueror and
|
||||
* Safari). Also, MSIE has a tendency to leak memory when it comes to
|
||||
* discarding event handlers.
|
||||
*
|
||||
* <h5>Prototype to the rescue</h5>
|
||||
*
|
||||
* Of course, Prototype smooths it over so well you'll forget these
|
||||
* troubles even exist. Enter the `Event` namespace. It is replete with
|
||||
* methods that help to normalize the information reported by events across
|
||||
* browsers.
|
||||
*
|
||||
* `Event` also provides a standardized list of key codes you can use with
|
||||
* keyboard-related events, including `KEY_BACKSPACE`, `KEY_TAB`,
|
||||
* `KEY_RETURN`, `KEY_ESC`, `KEY_LEFT`, `KEY_UP`, `KEY_RIGHT`, `KEY_DOWN`,
|
||||
* `KEY_DELETE`, `KEY_HOME`, `KEY_END`, `KEY_PAGEUP`, `KEY_PAGEDOWN` and
|
||||
* `KEY_INSERT`.
|
||||
*
|
||||
* The functions you're most likely to use a lot are [[Event.observe]],
|
||||
* [[Event.element]] and [[Event.stop]]. If your web app uses custom events,
|
||||
* you'll also get a lot of mileage out of [[Event.fire]].
|
||||
**/
|
||||
var Event = {
|
||||
KEY_BACKSPACE: 8,
|
||||
KEY_TAB: 9,
|
||||
KEY_RETURN: 13,
|
||||
KEY_ESC: 27,
|
||||
KEY_LEFT: 37,
|
||||
KEY_UP: 38,
|
||||
KEY_RIGHT: 39,
|
||||
KEY_DOWN: 40,
|
||||
KEY_DELETE: 46,
|
||||
KEY_HOME: 36,
|
||||
KEY_END: 35,
|
||||
KEY_PAGEUP: 33,
|
||||
KEY_PAGEDOWN: 34,
|
||||
KEY_INSERT: 45,
|
||||
|
||||
cache: {}
|
||||
};
|
||||
|
||||
var docEl = document.documentElement;
|
||||
var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
|
||||
&& 'onmouseleave' in docEl;
|
||||
|
||||
var _isButton;
|
||||
if (Prototype.Browser.IE) {
|
||||
// IE doesn't map left/right/middle the same way.
|
||||
var buttonMap = { 0: 1, 1: 4, 2: 2 };
|
||||
_isButton = function(event, code) {
|
||||
return event.button === buttonMap[code];
|
||||
};
|
||||
} else if (Prototype.Browser.WebKit) {
|
||||
// In Safari we have to account for when the user holds down
|
||||
// the "meta" key.
|
||||
_isButton = function(event, code) {
|
||||
switch (code) {
|
||||
case 0: return event.which == 1 && !event.metaKey;
|
||||
case 1: return event.which == 1 && event.metaKey;
|
||||
default: return false;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
_isButton = function(event, code) {
|
||||
return event.which ? (event.which === code + 1) : (event.button === code);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Event.isLeftClick(@event) -> Boolean
|
||||
*
|
||||
* Determines whether a button-related mouse event involved the left
|
||||
* mouse button.
|
||||
*
|
||||
* Keep in mind that the "left" mouse button is actually the "primary" mouse
|
||||
* button. When a mouse is in left-handed mode, the browser will report
|
||||
* clicks of the _right_ button as "left-clicks."
|
||||
**/
|
||||
function isLeftClick(event) { return _isButton(event, 0) }
|
||||
|
||||
/**
|
||||
* Event.isMiddleClick(@event) -> Boolean
|
||||
*
|
||||
* Determines whether a button-related mouse event involved the middle
|
||||
* mouse button.
|
||||
**/
|
||||
function isMiddleClick(event) { return _isButton(event, 1) }
|
||||
|
||||
/**
|
||||
* Event.isRightClick(@event) -> Boolean
|
||||
*
|
||||
* Determines whether a button-related mouse event involved the right
|
||||
* mouse button.
|
||||
*
|
||||
* Keep in mind that the "left" mouse button is actually the "secondary"
|
||||
* mouse button. When a mouse is in left-handed mode, the browser will
|
||||
* report clicks of the _left_ button as "left-clicks."
|
||||
**/
|
||||
function isRightClick(event) { return _isButton(event, 2) }
|
||||
|
||||
/** deprecated
|
||||
* Event.element(@event) -> Element
|
||||
*
|
||||
* Returns the DOM element on which the event occurred. This method
|
||||
* is deprecated, use [[Event.findElement findElement]] instead.
|
||||
**/
|
||||
function element(event) {
|
||||
event = Event.extend(event);
|
||||
|
||||
var node = event.target, type = event.type,
|
||||
currentTarget = event.currentTarget;
|
||||
|
||||
if (currentTarget && currentTarget.tagName) {
|
||||
// Firefox screws up the "click" event when moving between radio buttons
|
||||
// via arrow keys. It also screws up the "load" and "error" events on images,
|
||||
// reporting the document as the target instead of the original image.
|
||||
if (type === 'load' || type === 'error' ||
|
||||
(type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
|
||||
&& currentTarget.type === 'radio'))
|
||||
node = currentTarget;
|
||||
}
|
||||
|
||||
// Fix a Safari bug where a text node gets passed as the target of an
|
||||
// anchor click rather than the anchor itself.
|
||||
if (node.nodeType == Node.TEXT_NODE)
|
||||
node = node.parentNode;
|
||||
|
||||
return Element.extend(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Event.findElement(@event, expression) -> Element
|
||||
*
|
||||
* Returns the first DOM element that matches a given CSS selector —
|
||||
* starting with the element on which the event occurred, then moving up
|
||||
* its ancestor chain.
|
||||
**/
|
||||
function findElement(event, expression) {
|
||||
var element = Event.element(event);
|
||||
if (!expression) return element;
|
||||
while (element) {
|
||||
if (Prototype.Selector.match(element, expression)) {
|
||||
return Element.extend(element);
|
||||
}
|
||||
element = element.parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event.pointer(@event) -> Object
|
||||
*
|
||||
* Returns the absolute position of the pointer for a mouse event.
|
||||
*
|
||||
* Returns an object in the form `{ x: Number, y: Number}`.
|
||||
*
|
||||
* Note that this position is absolute on the _page_, not on the
|
||||
* _viewport_.
|
||||
**/
|
||||
function pointer(event) {
|
||||
return { x: pointerX(event), y: pointerY(event) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Event.pointerX(@event) -> Number
|
||||
*
|
||||
* Returns the absolute horizontal position of the pointer for a mouse
|
||||
* event.
|
||||
*
|
||||
* Note that this position is absolute on the _page_, not on the
|
||||
* _viewport_.
|
||||
**/
|
||||
function pointerX(event) {
|
||||
var docElement = document.documentElement,
|
||||
body = document.body || { scrollLeft: 0 };
|
||||
|
||||
return event.pageX || (event.clientX +
|
||||
(docElement.scrollLeft || body.scrollLeft) -
|
||||
(docElement.clientLeft || 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Event.pointerY(@event) -> Number
|
||||
*
|
||||
* Returns the absolute vertical position of the pointer for a mouse
|
||||
* event.
|
||||
*
|
||||
* Note that this position is absolute on the _page_, not on the
|
||||
* _viewport_.
|
||||
**/
|
||||
function pointerY(event) {
|
||||
var docElement = document.documentElement,
|
||||
body = document.body || { scrollTop: 0 };
|
||||
|
||||
return event.pageY || (event.clientY +
|
||||
(docElement.scrollTop || body.scrollTop) -
|
||||
(docElement.clientTop || 0));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Event.stop(@event) -> undefined
|
||||
*
|
||||
* Stops the event's propagation and prevents its eventual default action
|
||||
* from being triggered.
|
||||
*
|
||||
* Stopping an event also sets a `stopped` property on that event for
|
||||
* future inspection.
|
||||
**/
|
||||
function stop(event) {
|
||||
Event.extend(event);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// Set a "stopped" property so that a custom event can be inspected
|
||||
// after the fact to determine whether or not it was stopped.
|
||||
event.stopped = true;
|
||||
}
|
||||
|
||||
Event.Methods = {
|
||||
isLeftClick: isLeftClick,
|
||||
isMiddleClick: isMiddleClick,
|
||||
isRightClick: isRightClick,
|
||||
|
||||
element: element,
|
||||
findElement: findElement,
|
||||
|
||||
pointer: pointer,
|
||||
pointerX: pointerX,
|
||||
pointerY: pointerY,
|
||||
|
||||
stop: stop
|
||||
};
|
||||
|
||||
|
||||
// Compile the list of methods that get extended onto Events.
|
||||
var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
|
||||
m[name] = Event.Methods[name].methodize();
|
||||
return m;
|
||||
});
|
||||
|
||||
if (Prototype.Browser.IE) {
|
||||
function _relatedTarget(event) {
|
||||
var element;
|
||||
switch (event.type) {
|
||||
case 'mouseover': element = event.fromElement; break;
|
||||
case 'mouseout': element = event.toElement; break;
|
||||
default: return null;
|
||||
}
|
||||
return Element.extend(element);
|
||||
}
|
||||
|
||||
Object.extend(methods, {
|
||||
stopPropagation: function() { this.cancelBubble = true },
|
||||
preventDefault: function() { this.returnValue = false },
|
||||
inspect: function() { return '[object Event]' }
|
||||
});
|
||||
|
||||
// IE's method for extending events.
|
||||
Event.extend = function(event, element) {
|
||||
if (!event) return false;
|
||||
if (event._extendedByPrototype) return event;
|
||||
|
||||
event._extendedByPrototype = Prototype.emptyFunction;
|
||||
var pointer = Event.pointer(event);
|
||||
|
||||
// The optional `element` argument gives us a fallback value for the
|
||||
// `target` property in case IE doesn't give us through `srcElement`.
|
||||
Object.extend(event, {
|
||||
target: event.srcElement || element,
|
||||
relatedTarget: _relatedTarget(event),
|
||||
pageX: pointer.x,
|
||||
pageY: pointer.y
|
||||
});
|
||||
|
||||
return Object.extend(event, methods);
|
||||
};
|
||||
} else {
|
||||
Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
|
||||
Object.extend(Event.prototype, methods);
|
||||
Event.extend = Prototype.K;
|
||||
}
|
||||
|
||||
function _createResponder(element, eventName, handler) {
|
||||
// We don't set a default on the call to Element#retrieve so that we can
|
||||
// handle the element's "virgin" state.
|
||||
var registry = Element.retrieve(element, 'prototype_event_registry');
|
||||
|
||||
if (Object.isUndefined(registry)) {
|
||||
// First time we've handled this element. Put it into the cache.
|
||||
CACHE.push(element);
|
||||
registry = Element.retrieve(element, 'prototype_event_registry', $H());
|
||||
}
|
||||
|
||||
var respondersForEvent = registry.get(eventName);
|
||||
if (Object.isUndefined(respondersForEvent)) {
|
||||
respondersForEvent = [];
|
||||
registry.set(eventName, respondersForEvent);
|
||||
}
|
||||
|
||||
// Work around the issue that permits a handler to be attached more than
|
||||
// once to the same element & event type.
|
||||
if (respondersForEvent.pluck('handler').include(handler)) return false;
|
||||
|
||||
var responder;
|
||||
if (eventName.include(":")) {
|
||||
// Custom event.
|
||||
responder = function(event) {
|
||||
// If it's not a custom event, ignore it.
|
||||
if (Object.isUndefined(event.eventName))
|
||||
return false;
|
||||
|
||||
// If it's a custom event, but not the _correct_ custom event, ignore it.
|
||||
if (event.eventName !== eventName)
|
||||
return false;
|
||||
|
||||
Event.extend(event, element);
|
||||
handler.call(element, event);
|
||||
};
|
||||
} else {
|
||||
// Non-custom event.
|
||||
if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
|
||||
(eventName === "mouseenter" || eventName === "mouseleave")) {
|
||||
// If we're dealing with mouseenter or mouseleave in a non-IE browser,
|
||||
// we create a custom responder that mimics their behavior within
|
||||
// mouseover and mouseout.
|
||||
if (eventName === "mouseenter" || eventName === "mouseleave") {
|
||||
responder = function(event) {
|
||||
Event.extend(event, element);
|
||||
|
||||
var parent = event.relatedTarget;
|
||||
while (parent && parent !== element) {
|
||||
try { parent = parent.parentNode; }
|
||||
catch(e) { parent = element; }
|
||||
}
|
||||
|
||||
if (parent === element) return;
|
||||
|
||||
handler.call(element, event);
|
||||
};
|
||||
}
|
||||
} else {
|
||||
responder = function(event) {
|
||||
Event.extend(event, element);
|
||||
handler.call(element, event);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
responder.handler = handler;
|
||||
respondersForEvent.push(responder);
|
||||
return responder;
|
||||
}
|
||||
|
||||
function _destroyCache() {
|
||||
for (var i = 0, length = CACHE.length; i < length; i++) {
|
||||
Event.stopObserving(CACHE[i]);
|
||||
CACHE[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
var CACHE = [];
|
||||
|
||||
// Internet Explorer needs to remove event handlers on page unload
|
||||
// in order to avoid memory leaks.
|
||||
if (Prototype.Browser.IE)
|
||||
window.attachEvent('onunload', _destroyCache);
|
||||
|
||||
// Safari needs a dummy event handler on page unload so that it won't
|
||||
// use its bfcache. Safari <= 3.1 has an issue with restoring the "document"
|
||||
// object when page is returned to via the back button using its bfcache.
|
||||
if (Prototype.Browser.WebKit)
|
||||
window.addEventListener('unload', Prototype.emptyFunction, false);
|
||||
|
||||
|
||||
var _getDOMEventName = Prototype.K,
|
||||
translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
|
||||
|
||||
if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
|
||||
_getDOMEventName = function(eventName) {
|
||||
return (translations[eventName] || eventName);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Event.observe(element, eventName, handler) -> Element
|
||||
* - element (Element | String): The DOM element to observe, or its ID.
|
||||
* - eventName (String): The name of the event, in all lower case, without the "on"
|
||||
* prefix — e.g., "click" (not "onclick").
|
||||
* - handler (Function): The function to call when the event occurs.
|
||||
*
|
||||
* Registers an event handler on a DOM element. Aliased as [[Element#observe]].
|
||||
*
|
||||
* `Event.observe` smooths out a variety of differences between browsers and provides
|
||||
* some handy additional features as well. Key features in brief:
|
||||
* * Several handlers can be registered for the same event on the same element.
|
||||
* * Prototype figures out whether to use `addEventListener` (W3C standard) or
|
||||
* `attachEvent` (MSIE); you don't have to worry about it.
|
||||
* * The handler is passed an _extended_ [[Event]] object (even on MSIE).
|
||||
* * The handler's context (`this` value) is set to the extended element being observed
|
||||
* (even if the event actually occurred on a descendent element and bubbled up).
|
||||
* * Prototype handles cleaning up the handler when leaving the page (important for MSIE memory
|
||||
* leak prevention).
|
||||
* * `observe` makes it possible to stop observing the event easily via [[Event.stopObserving]].
|
||||
* * Adds support for `mouseenter` / `mouseleave` in all browsers.
|
||||
*
|
||||
* Although you can use `Event.observe` directly and there are times when that's the most
|
||||
* convenient or direct way, it's more common to use its alias [[Element#observe]]. These two
|
||||
* statements have the same effect:
|
||||
*
|
||||
* Event.observe('foo', 'click', myHandler);
|
||||
* $('foo').observe('click', myHandler);
|
||||
*
|
||||
* The examples in this documentation use the [[Element#observe]] form.
|
||||
*
|
||||
* <h5>The Handler</h5>
|
||||
*
|
||||
* Signature:
|
||||
*
|
||||
* function handler(event) {
|
||||
* // `this` = the element being observed
|
||||
* }
|
||||
*
|
||||
* So for example, this will turn the background of the element 'foo' blue when it's clicked:
|
||||
*
|
||||
* $('foo').observe('click', function(event) {
|
||||
* this.setStyle({backgroundColor: 'blue'});
|
||||
* });
|
||||
*
|
||||
* Note that we used `this` to refer to the element, and that we received the `event` object
|
||||
* as a parameter (even on MSIE).
|
||||
*
|
||||
* <h5>It's All About Timing</h5>
|
||||
*
|
||||
* One of the most common errors trying to observe events is trying to do it before the element
|
||||
* exists in the DOM. Don't try to observe elements until after the
|
||||
* [[document.observe dom:loaded]] event or `window` `load` event has been fired.
|
||||
*
|
||||
* <h5>Preventing the Default Event Action and Bubbling</h5>
|
||||
*
|
||||
* If we want to stop the event (e.g., prevent its default action and stop it bubbling), we can
|
||||
* do so with the extended event object's [[Event#stop]] method:
|
||||
*
|
||||
* $('foo').observe('click', function(event) {
|
||||
* event.stop();
|
||||
* });
|
||||
*
|
||||
* <h5>Finding the Element Where the Event Occurred</h5>
|
||||
*
|
||||
* Since most events bubble from descendant elements up through the hierarchy until they're
|
||||
* handled, we can observe an event on a container rather than individual elements within the
|
||||
* container. This is sometimes called "event delegation". It's particularly handy for tables:
|
||||
*
|
||||
* <table id='records'>
|
||||
* <thead>
|
||||
* <tr><th colspan='2'>No record clicked</th></tr>
|
||||
* </thead>
|
||||
* <tbody>
|
||||
* <tr data-recnum='1'><td>1</td><td>First record</td></tr>
|
||||
* <tr data-recnum='2'><td>2</td><td>Second record</td></tr>
|
||||
* <tr data-recnum='3'><td>3</td><td>Third record</td></tr>
|
||||
* </tbody>
|
||||
* </table>
|
||||
*
|
||||
* Instead of observing each cell or row, we can simply observe the table:
|
||||
*
|
||||
* $('records').observe('click', function(event) {
|
||||
* var clickedRow;
|
||||
* clickedRow = event.findElement('tr');
|
||||
* if (clickedRow) {
|
||||
* this.down('th').update("You clicked record #" + clickedRow.readAttribute("data-recnum"));
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* When any row in the table is clicked, we update the table's first header cell saying which
|
||||
* record was clicked. [[Event#findElement]] finds the row that was clicked, and `this` refers
|
||||
* to the table we were observing.
|
||||
*
|
||||
* <h5>Stopping Observing the Event</h5>
|
||||
*
|
||||
* If we don't need to observe the event anymore, we can stop observing it with
|
||||
* [[Event.stopObserving]] (aka [[Element#stopObserving]]).
|
||||
*
|
||||
* <h5>Using an Instance Method as a Handler</h5>
|
||||
*
|
||||
* If we want to use an instance method as a handler, we will probably want to use
|
||||
* [[Function#bind]] to set the handler's context; otherwise, the context will be lost and
|
||||
* `this` won't mean what we expect it to mean within the handler function. E.g.:
|
||||
*
|
||||
* var MyClass = Class.create({
|
||||
* initialize: function(name, element) {
|
||||
* this.name = name;
|
||||
* element = $(element);
|
||||
* if (element) {
|
||||
* element.observe(this.handleClick.bind(this));
|
||||
* }
|
||||
* },
|
||||
* handleClick: function(event) {
|
||||
* alert("My name is " + this.name);
|
||||
* },
|
||||
* });
|
||||
*
|
||||
* Without the `bind`, when `handleClick` was triggered by the event, `this` wouldn't
|
||||
* refer to the instance and so the alert wouldn't show the name. Because we used `bind`, it
|
||||
* works correctly. See [[Function#bind]] for
|
||||
* details. There's also [[Function#bindAsEventListener]], which is handy for certain very
|
||||
* specific situations. (Normally, `bind` is all you need.)
|
||||
*
|
||||
* <h5>Side Notes</h5>
|
||||
*
|
||||
* Although Prototype smooths out most of the differences between browsers, the fundamental
|
||||
* behavior of a browser implementation isn't changed. For example, the timing of the `change`
|
||||
* or `blur` events varies a bit from browser to browser.
|
||||
*
|
||||
* <h5>Changes in 1.6.x</h5>
|
||||
*
|
||||
* Prior to Prototype 1.6, `observe` supported a fourth argument (`useCapture`), a boolean that
|
||||
* indicated whether to use the browser's capturing phase or its bubbling phase. Since MSIE does
|
||||
* not support the capturing phase, we removed this argument from 1.6, lest it give users the
|
||||
* false impression that they can use the capturing phase in all browsers.
|
||||
*
|
||||
* 1.6 also introduced setting the `this` context to the element being observed, automatically
|
||||
* extending the [[Event]] object, and the [[Event#findElement]] method.
|
||||
*
|
||||
**/
|
||||
function observe(element, eventName, handler) {
|
||||
element = $(element);
|
||||
|
||||
var responder = _createResponder(element, eventName, handler);
|
||||
|
||||
if (!responder) return element;
|
||||
|
||||
if (eventName.include(':')) {
|
||||
// Custom event.
|
||||
if (element.addEventListener)
|
||||
element.addEventListener("dataavailable", responder, false);
|
||||
else {
|
||||
// We observe two IE-proprietarty events: one for custom events that
|
||||
// bubble and one for custom events that do not bubble.
|
||||
element.attachEvent("ondataavailable", responder);
|
||||
element.attachEvent("onfilterchange", responder);
|
||||
}
|
||||
} else {
|
||||
var actualEventName = _getDOMEventName(eventName);
|
||||
|
||||
// Ordinary event.
|
||||
if (element.addEventListener)
|
||||
element.addEventListener(actualEventName, responder, false);
|
||||
else
|
||||
element.attachEvent("on" + actualEventName, responder);
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event.stopObserving(element[, eventName[, handler]]) -> Element
|
||||
* - element (Element | String): The element to stop observing, or its ID.
|
||||
* - eventName (String): _(Optional)_ The name of the event to stop observing, in all lower case,
|
||||
* without the "on" — e.g., "click" (not "onclick").
|
||||
* - handler (Function): _(Optional)_ The handler to remove; must be the _exact same_ reference
|
||||
* that was passed to [[Event.observe]] (see below.).
|
||||
*
|
||||
* Unregisters one or more event handlers.
|
||||
*
|
||||
* If `handler` is omitted, unregisters all event handlers on `element`
|
||||
* for that `eventName`. If `eventName` is also omitted, unregisters _all_
|
||||
* event handlers on `element`. (In each case, only affects handlers registered via Prototype.)
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* Assuming:
|
||||
*
|
||||
* $('foo').observe('click', myHandler);
|
||||
*
|
||||
* ...we can stop observing using that handler like so:
|
||||
*
|
||||
* $('foo').stopObserving('click', myHandler);
|
||||
*
|
||||
* If we want to remove _all_ 'click' handlers from 'foo', we leave off the handler argument:
|
||||
*
|
||||
* $('foo').stopObserving('click');
|
||||
*
|
||||
* If we want to remove _all_ handlers for _all_ events from 'foo' (perhaps we're about to remove
|
||||
* it from the DOM), we simply omit both the handler and the event name:
|
||||
*
|
||||
* $('foo').stopObserving();
|
||||
*
|
||||
* <h5>A Common Error</h5>
|
||||
*
|
||||
* When using instance methods as observers, it's common to use [[Function#bind]] on them, e.g.:
|
||||
*
|
||||
* $('foo').observe('click', this.handlerMethod.bind(this));
|
||||
*
|
||||
* If you do that, __this will not work__ to unregister the handler:
|
||||
*
|
||||
* $('foo').stopObserving('click', this.handlerMethod.bind(this)); // <== WRONG
|
||||
*
|
||||
* [[Function#bind]] returns a _new_ function every time it's called, and so if you don't retain
|
||||
* the reference you used when observing, you can't unhook that function specifically. (You can
|
||||
* still unhook __all__ handlers for an event, or all handlers on the element entirely.)
|
||||
*
|
||||
* To do this, you need to keep a reference to the bound function:
|
||||
*
|
||||
* this.boundHandlerMethod = this.handlerMethod.bind(this);
|
||||
* $('foo').observe('click', this.boundHandlerMethod);
|
||||
*
|
||||
* ...and then to remove:
|
||||
*
|
||||
* $('foo').stopObserving('click', this.boundHandlerMethod); // <== Right
|
||||
*
|
||||
**/
|
||||
function stopObserving(element, eventName, handler) {
|
||||
element = $(element);
|
||||
|
||||
var registry = Element.retrieve(element, 'prototype_event_registry');
|
||||
if (!registry) return element;
|
||||
|
||||
if (!eventName) {
|
||||
// We stop observing all events.
|
||||
// e.g.: $(element).stopObserving();
|
||||
registry.each( function(pair) {
|
||||
var eventName = pair.key;
|
||||
stopObserving(element, eventName);
|
||||
});
|
||||
return element;
|
||||
}
|
||||
|
||||
var responders = registry.get(eventName);
|
||||
if (!responders) return element;
|
||||
|
||||
if (!handler) {
|
||||
// We stop observing all handlers for the given eventName.
|
||||
// e.g.: $(element).stopObserving('click');
|
||||
responders.each(function(r) {
|
||||
stopObserving(element, eventName, r.handler);
|
||||
});
|
||||
return element;
|
||||
}
|
||||
|
||||
var responder = responders.find( function(r) { return r.handler === handler; });
|
||||
if (!responder) return element;
|
||||
|
||||
if (eventName.include(':')) {
|
||||
// Custom event.
|
||||
if (element.removeEventListener)
|
||||
element.removeEventListener("dataavailable", responder, false);
|
||||
else {
|
||||
element.detachEvent("ondataavailable", responder);
|
||||
element.detachEvent("onfilterchange", responder);
|
||||
}
|
||||
} else {
|
||||
// Ordinary event.
|
||||
var actualEventName = _getDOMEventName(eventName);
|
||||
if (element.removeEventListener)
|
||||
element.removeEventListener(actualEventName, responder, false);
|
||||
else
|
||||
element.detachEvent('on' + actualEventName, responder);
|
||||
}
|
||||
|
||||
registry.set(eventName, responders.without(responder));
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event.fire(element, eventName[, memo[, bubble = true]]) -> Event
|
||||
* - memo (?): Metadata for the event. Will be accessible through the
|
||||
* event's `memo` property.
|
||||
* - bubble (Boolean): Whether the event will bubble.
|
||||
*
|
||||
* Fires a custom event of name `eventName` with `element` as its target.
|
||||
*
|
||||
* Custom events must include a colon (`:`) in their names.
|
||||
**/
|
||||
function fire(element, eventName, memo, bubble) {
|
||||
element = $(element);
|
||||
|
||||
if (Object.isUndefined(bubble))
|
||||
bubble = true;
|
||||
|
||||
if (element == document && document.createEvent && !element.dispatchEvent)
|
||||
element = document.documentElement;
|
||||
|
||||
var event;
|
||||
if (document.createEvent) {
|
||||
event = document.createEvent('HTMLEvents');
|
||||
event.initEvent('dataavailable', true, true);
|
||||
} else {
|
||||
event = document.createEventObject();
|
||||
event.eventType = bubble ? 'ondataavailable' : 'onfilterchange';
|
||||
}
|
||||
|
||||
event.eventName = eventName;
|
||||
event.memo = memo || { };
|
||||
|
||||
if (document.createEvent)
|
||||
element.dispatchEvent(event);
|
||||
else
|
||||
element.fireEvent(event.eventType, event);
|
||||
|
||||
return Event.extend(event);
|
||||
}
|
||||
|
||||
|
||||
Object.extend(Event, Event.Methods);
|
||||
|
||||
Object.extend(Event, {
|
||||
fire: fire,
|
||||
observe: observe,
|
||||
stopObserving: stopObserving
|
||||
});
|
||||
|
||||
Element.addMethods({
|
||||
/**
|
||||
* Element.fire(@element, eventName[, memo[, bubble = true]]) -> Event
|
||||
* See [[Event.fire]].
|
||||
**/
|
||||
fire: fire,
|
||||
|
||||
/**
|
||||
* Element.observe(@element, eventName, handler) -> Element
|
||||
* See [[Event.observe]].
|
||||
**/
|
||||
observe: observe,
|
||||
|
||||
/**
|
||||
* Element.stopObserving(@element[, eventName[, handler]]) -> Element
|
||||
* See [[Event.stopObserving]].
|
||||
**/
|
||||
stopObserving: stopObserving
|
||||
});
|
||||
|
||||
/** section: DOM
|
||||
* document
|
||||
*
|
||||
* Prototype extends the built-in `document` object with several convenience
|
||||
* methods related to events.
|
||||
**/
|
||||
Object.extend(document, {
|
||||
/**
|
||||
* document.fire(eventName[, memo[, bubble = true]]) -> Event
|
||||
* See [[Event.fire]].
|
||||
**/
|
||||
fire: fire.methodize(),
|
||||
|
||||
/**
|
||||
* document.observe(eventName, handler) -> Element
|
||||
* See [[Event.observe]].
|
||||
**/
|
||||
observe: observe.methodize(),
|
||||
|
||||
/**
|
||||
* document.stopObserving([eventName[, handler]]) -> Element
|
||||
* See [[Event.stopObserving]].
|
||||
**/
|
||||
stopObserving: stopObserving.methodize(),
|
||||
|
||||
/**
|
||||
* document.loaded -> Boolean
|
||||
*
|
||||
* Whether the full DOM tree is ready for manipulation.
|
||||
**/
|
||||
loaded: false
|
||||
});
|
||||
|
||||
// Export to the global scope.
|
||||
if (window.Event) Object.extend(window.Event, Event);
|
||||
else window.Event = Event;
|
||||
})();
|
||||
|
||||
(function() {
|
||||
/* Support for the DOMContentLoaded event is based on work by Dan Webb,
|
||||
Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
|
||||
|
||||
var timer;
|
||||
|
||||
function fireContentLoadedEvent() {
|
||||
if (document.loaded) return;
|
||||
if (timer) window.clearTimeout(timer);
|
||||
document.loaded = true;
|
||||
document.fire('dom:loaded');
|
||||
}
|
||||
|
||||
function checkReadyState() {
|
||||
if (document.readyState === 'complete') {
|
||||
document.stopObserving('readystatechange', checkReadyState);
|
||||
fireContentLoadedEvent();
|
||||
}
|
||||
}
|
||||
|
||||
function pollDoScroll() {
|
||||
try { document.documentElement.doScroll('left'); }
|
||||
catch(e) {
|
||||
timer = pollDoScroll.defer();
|
||||
return;
|
||||
}
|
||||
fireContentLoadedEvent();
|
||||
}
|
||||
|
||||
if (document.addEventListener) {
|
||||
document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
|
||||
} else {
|
||||
document.observe('readystatechange', checkReadyState);
|
||||
if (window == top)
|
||||
timer = pollDoScroll.defer();
|
||||
}
|
||||
|
||||
// Worst-case fallback
|
||||
Event.observe(window, 'load', fireContentLoadedEvent);
|
||||
})();
|
638
src/dom/form.js
638
src/dom/form.js
|
@ -1,638 +0,0 @@
|
|||
/** section: DOM
|
||||
* Form
|
||||
*
|
||||
* Utilities for dealing with forms in the DOM.
|
||||
*
|
||||
* `Form` is a namespace for all things form-related, packed with form
|
||||
* manipulation and serialization goodness. While it holds methods dealing
|
||||
* with forms as a whole, its submodule [[Form.Element]] deals with specific
|
||||
* form controls.
|
||||
*
|
||||
* Many of these methods are also available directly on `form` elements.
|
||||
**/
|
||||
|
||||
var Form = {
|
||||
/**
|
||||
* Form.reset(form) -> Element
|
||||
*
|
||||
* Resets a form to its default values.
|
||||
**/
|
||||
reset: function(form) {
|
||||
form = $(form);
|
||||
form.reset();
|
||||
return form;
|
||||
},
|
||||
|
||||
/**
|
||||
* Form.serializeElements(elements[, options]) -> String | Object
|
||||
* - elements (Array): A collection of elements to include in the
|
||||
* serialization.
|
||||
* - options (Object): _(Optional)_ A set of options that affect the return
|
||||
* value of the method.
|
||||
*
|
||||
* Serialize an array of form elements to an object or string suitable
|
||||
* for [[Ajax]] requests.
|
||||
*
|
||||
* As per the HTML spec, disabled fields are not included.
|
||||
*
|
||||
* If multiple elements have the same name and we're returning an object,
|
||||
* the value for that key in the object will be an array of the field values
|
||||
* in the order they appeared on the array of elements.
|
||||
*
|
||||
* <h5>The Options</h5>
|
||||
*
|
||||
* The options allow you to control two things: What kind of return
|
||||
* value you get (an object or a string), and whether and which `submit`
|
||||
* fields are included in that object or string.
|
||||
*
|
||||
* If you do not supply an `options` object _at all_, the options
|
||||
* `{ hash: false }` are used.
|
||||
*
|
||||
* If you supply an `options` object, it may have the following options:
|
||||
* - `hash` ([[Boolean]]): `true` to return a plain object with keys and values
|
||||
* (not a [[Hash]]; see below), `false` to return a String in query string
|
||||
* format. If you supply an `options` object with no `hash` member, `hash`
|
||||
* defaults to `true`. Note that this is __not__ the same as leaving off the
|
||||
* `options` object entirely (see above).
|
||||
* - `submit` ([[Boolean]] | [[String]]): In essence: If you omit this option the
|
||||
* first submit button in the form is included; if you supply `false`,
|
||||
* no submit buttons are included; if you supply the name of a submit
|
||||
* button, the first button with that name is included. Note that the `false`
|
||||
* value __must__ really be `false`, not _falsey_; falsey-but-not-false is
|
||||
* like omitting the option.
|
||||
*
|
||||
* _(Deprecated)_ If you pass in a [[Boolean]] instead of an object for `options`, it
|
||||
* is used as the `hash` option and all other options are defaulted.
|
||||
*
|
||||
* <h5>A <em>hash</em>, not a Hash</h5>
|
||||
*
|
||||
* If you opt to receive an object, it is a plain JavaScript object with keys
|
||||
* and values, __not__ a [[Hash]]. All JavaScript objects are hashes in the lower-case
|
||||
* sense of the word, which is why the option has that somewhat-confusing name.
|
||||
**/
|
||||
serializeElements: function(elements, options) {
|
||||
// An earlier version accepted a boolean second parameter (hash) where
|
||||
// the default if omitted was false; respect that, but if they pass in an
|
||||
// options object (e.g., the new signature) but don't specify the hash option,
|
||||
// default true, as that's the new preferred approach.
|
||||
if (typeof options != 'object') options = { hash: !!options };
|
||||
else if (Object.isUndefined(options.hash)) options.hash = true;
|
||||
var key, value, submitted = false, submit = options.submit;
|
||||
|
||||
var data = elements.inject({ }, function(result, element) {
|
||||
if (!element.disabled && element.name) {
|
||||
key = element.name; value = $(element).getValue();
|
||||
if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
|
||||
submit !== false && (!submit || key == submit) && (submitted = true)))) {
|
||||
if (key in result) {
|
||||
// a key is already present; construct an array of values
|
||||
if (!Object.isArray(result[key])) result[key] = [result[key]];
|
||||
result[key].push(value);
|
||||
}
|
||||
else result[key] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
return options.hash ? data : Object.toQueryString(data);
|
||||
}
|
||||
};
|
||||
|
||||
Form.Methods = {
|
||||
/**
|
||||
* Form.serialize(@form[, options]) -> String | Object
|
||||
* - options (Object): A list of options that affect the return value
|
||||
* of the method.
|
||||
*
|
||||
* Serialize form data to an object or string suitable for Ajax requests.
|
||||
*
|
||||
* See [[Form.serializeElements]] for details on the options.
|
||||
**/
|
||||
serialize: function(form, options) {
|
||||
return Form.serializeElements(Form.getElements(form), options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Form.getElements(@form) -> [Element...]
|
||||
*
|
||||
* Returns a collection of all controls within a form.
|
||||
**/
|
||||
getElements: function(form) {
|
||||
var elements = $(form).getElementsByTagName('*'),
|
||||
element,
|
||||
arr = [ ],
|
||||
serializers = Form.Element.Serializers;
|
||||
// `length` is not used to prevent interference with
|
||||
// length-named elements shadowing `length` of a nodelist
|
||||
for (var i = 0; element = elements[i]; i++) {
|
||||
arr.push(element);
|
||||
}
|
||||
return arr.inject([], function(elements, child) {
|
||||
if (serializers[child.tagName.toLowerCase()])
|
||||
elements.push(Element.extend(child));
|
||||
return elements;
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Form.getInputs(@form [, type [, name]]) -> [Element...]
|
||||
* - type (String): A value for the `type` attribute against which to
|
||||
* filter.
|
||||
* - name (String): A value for the `name` attribute against which to
|
||||
* filter.
|
||||
*
|
||||
* Returns a collection of all `INPUT` elements in a form.
|
||||
*
|
||||
* Use optional `type` and `name` arguments to restrict the search on
|
||||
* these attributes.
|
||||
**/
|
||||
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;
|
||||
},
|
||||
|
||||
/**
|
||||
* Form.disable(@form) -> Element
|
||||
*
|
||||
* Disables the form as a whole. Form controls will be visible but
|
||||
* uneditable.
|
||||
**/
|
||||
disable: function(form) {
|
||||
form = $(form);
|
||||
Form.getElements(form).invoke('disable');
|
||||
return form;
|
||||
},
|
||||
|
||||
/**
|
||||
* Form.enable(@form) -> Element
|
||||
*
|
||||
* Enables a fully- or partially-disabled form.
|
||||
**/
|
||||
enable: function(form) {
|
||||
form = $(form);
|
||||
Form.getElements(form).invoke('enable');
|
||||
return form;
|
||||
},
|
||||
|
||||
/**
|
||||
* Form.findFirstElement(@form) -> Element
|
||||
*
|
||||
* Finds the first non-hidden, non-disabled control within the form.
|
||||
**/
|
||||
findFirstElement: function(form) {
|
||||
var elements = $(form).getElements().findAll(function(element) {
|
||||
return 'hidden' != element.type && !element.disabled;
|
||||
});
|
||||
var firstByIndex = elements.findAll(function(element) {
|
||||
return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
|
||||
}).sortBy(function(element) { return element.tabIndex }).first();
|
||||
|
||||
return firstByIndex ? firstByIndex : elements.find(function(element) {
|
||||
return /^(?:input|select|textarea)$/i.test(element.tagName);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Form.focusFirstElement(@form) -> Element
|
||||
*
|
||||
* Gives keyboard focus to the first element of the form. Returns the form.
|
||||
**/
|
||||
focusFirstElement: function(form) {
|
||||
form = $(form);
|
||||
form.findFirstElement().activate();
|
||||
return form;
|
||||
},
|
||||
|
||||
/**
|
||||
* Form.request(@form[, options]) -> Ajax.Request
|
||||
* - options (Object): Options to pass along to the `Ajax.Request`
|
||||
* constructor.
|
||||
*
|
||||
* A convenience method for serializing and submitting the form via an
|
||||
* [[Ajax.Request]] to the URL of the form's `action` attribute.
|
||||
*
|
||||
* The `options` parameter is passed to the `Ajax.Request` instance,
|
||||
* allowing one to override the HTTP method and/or specify additional
|
||||
* parameters and callbacks.
|
||||
**/
|
||||
request: function(form, options) {
|
||||
form = $(form), options = Object.clone(options || { });
|
||||
|
||||
var params = options.parameters, action = form.readAttribute('action') || '';
|
||||
if (action.blank()) action = window.location.href;
|
||||
options.parameters = form.serialize(true);
|
||||
|
||||
if (params) {
|
||||
if (Object.isString(params)) params = params.toQueryParams();
|
||||
Object.extend(options.parameters, params);
|
||||
}
|
||||
|
||||
if (form.hasAttribute('method') && !options.method)
|
||||
options.method = form.method;
|
||||
|
||||
return new Ajax.Request(action, options);
|
||||
}
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Form.Element
|
||||
*
|
||||
* Utilities for dealing with form controls in the DOM.
|
||||
*
|
||||
* This is a collection of methods that assist in dealing with form controls.
|
||||
* They provide ways to focus, serialize, disable/enable or extract current
|
||||
* value from a specific control.
|
||||
*
|
||||
* Note that nearly all these methods are available directly on `input`,
|
||||
* `select`, and `textarea` elements. Therefore, these are equivalent:
|
||||
*
|
||||
* Form.Element.activate('myfield');
|
||||
* $('myfield').activate();
|
||||
*
|
||||
* Naturally, you should always prefer the shortest form suitable in a
|
||||
* situation. Most of these methods also return the element itself (as
|
||||
* indicated by the return type) for chainability.
|
||||
**/
|
||||
|
||||
Form.Element = {
|
||||
/**
|
||||
* Form.Element.focus(element) -> Element
|
||||
*
|
||||
* Gives keyboard focus to an element. Returns the element.
|
||||
**/
|
||||
focus: function(element) {
|
||||
$(element).focus();
|
||||
return element;
|
||||
},
|
||||
|
||||
/**
|
||||
* Form.Element.select(element) -> Element
|
||||
*
|
||||
* Selects the current text in a text input. Returns the element.
|
||||
**/
|
||||
select: function(element) {
|
||||
$(element).select();
|
||||
return element;
|
||||
}
|
||||
};
|
||||
|
||||
Form.Element.Methods = {
|
||||
|
||||
/**
|
||||
* Form.Element.serialize(@element) -> String
|
||||
*
|
||||
* Returns a URL-encoded string representation of a form control in the
|
||||
* `name=value` format.
|
||||
**/
|
||||
serialize: function(element) {
|
||||
element = $(element);
|
||||
if (!element.disabled && element.name) {
|
||||
var value = element.getValue();
|
||||
if (value != undefined) {
|
||||
var pair = { };
|
||||
pair[element.name] = value;
|
||||
return Object.toQueryString(pair);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
/** alias of: $F
|
||||
* Form.Element.getValue(@element) -> String | Array
|
||||
*
|
||||
* Returns the current value of a form control.
|
||||
*
|
||||
* A string is returned for most controls; only multiple `select` boxes
|
||||
* return an array of values.
|
||||
*
|
||||
* The global shortcut for this method is [[$F]].
|
||||
**/
|
||||
getValue: function(element) {
|
||||
element = $(element);
|
||||
var method = element.tagName.toLowerCase();
|
||||
return Form.Element.Serializers[method](element);
|
||||
},
|
||||
|
||||
/**
|
||||
* Form.Element.setValue(@element, value) -> Element
|
||||
*
|
||||
* Sets `value` to be the value of the form control. Returns the element.
|
||||
**/
|
||||
setValue: function(element, value) {
|
||||
element = $(element);
|
||||
var method = element.tagName.toLowerCase();
|
||||
Form.Element.Serializers[method](element, value);
|
||||
return element;
|
||||
},
|
||||
|
||||
/**
|
||||
* Form.Element.clear(@element) -> Element
|
||||
*
|
||||
* Clears the contents of a text input. Returns the element.
|
||||
**/
|
||||
clear: function(element) {
|
||||
$(element).value = '';
|
||||
return element;
|
||||
},
|
||||
|
||||
/**
|
||||
* Form.Element.present(@element) -> Element
|
||||
*
|
||||
* Returns `true` if a text input has contents, `false` otherwise.
|
||||
**/
|
||||
present: function(element) {
|
||||
return $(element).value != '';
|
||||
},
|
||||
|
||||
/**
|
||||
* Form.Element.activate(@element) -> Element
|
||||
*
|
||||
* Gives focus to a form control and selects its contents if it is a text
|
||||
* input.
|
||||
**/
|
||||
activate: function(element) {
|
||||
element = $(element);
|
||||
try {
|
||||
element.focus();
|
||||
if (element.select && (element.tagName.toLowerCase() != 'input' ||
|
||||
!(/^(?:button|reset|submit)$/i.test(element.type))))
|
||||
element.select();
|
||||
} catch (e) { }
|
||||
return element;
|
||||
},
|
||||
|
||||
/**
|
||||
* Form.Element.disable(@element) -> Element
|
||||
*
|
||||
* Disables a form control, effectively preventing its value from changing
|
||||
* until it is enabled again.
|
||||
**/
|
||||
disable: function(element) {
|
||||
element = $(element);
|
||||
element.disabled = true;
|
||||
return element;
|
||||
},
|
||||
|
||||
/**
|
||||
* Form.Element.enable(@element) -> Element
|
||||
*
|
||||
* Enables a previously disabled form control.
|
||||
**/
|
||||
enable: function(element) {
|
||||
element = $(element);
|
||||
element.disabled = false;
|
||||
return element;
|
||||
}
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
var Field = Form.Element;
|
||||
|
||||
/** section: DOM, related to: Form
|
||||
* $F(element) -> String | Array
|
||||
**/
|
||||
var $F = Form.Element.Methods.getValue;
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
Form.Element.Serializers = {
|
||||
input: function(element, value) {
|
||||
switch (element.type.toLowerCase()) {
|
||||
case 'checkbox':
|
||||
case 'radio':
|
||||
return Form.Element.Serializers.inputSelector(element, value);
|
||||
default:
|
||||
return Form.Element.Serializers.textarea(element, value);
|
||||
}
|
||||
},
|
||||
|
||||
inputSelector: function(element, value) {
|
||||
if (Object.isUndefined(value)) return element.checked ? element.value : null;
|
||||
else element.checked = !!value;
|
||||
},
|
||||
|
||||
textarea: function(element, value) {
|
||||
if (Object.isUndefined(value)) return element.value;
|
||||
else element.value = value;
|
||||
},
|
||||
|
||||
select: function(element, value) {
|
||||
if (Object.isUndefined(value))
|
||||
return this[element.type == 'select-one' ?
|
||||
'selectOne' : 'selectMany'](element);
|
||||
else {
|
||||
var opt, currentValue, single = !Object.isArray(value);
|
||||
for (var i = 0, length = element.length; i < length; i++) {
|
||||
opt = element.options[i];
|
||||
currentValue = this.optionValue(opt);
|
||||
if (single) {
|
||||
if (currentValue == value) {
|
||||
opt.selected = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else opt.selected = value.include(currentValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
/** section: DOM
|
||||
* Abstract
|
||||
**/
|
||||
|
||||
/**
|
||||
* class Abstract.TimedObserver
|
||||
*
|
||||
* An abstract DOM element observer class, subclasses of which can be used to periodically
|
||||
* check a value and trigger a callback when the value has changed.
|
||||
*
|
||||
* A `TimedObserver` object will try to check a value using the `getValue()`
|
||||
* instance method which must be defined by the subclass. There are two out-of-the-box subclasses:
|
||||
* [[Form.Observer]], which serializes a form and triggers when the result has changed; and
|
||||
* [[Form.Element.Observer]], which triggers when the value of a given form field changes.
|
||||
*
|
||||
* <h5>Creating Your Own TimedObserver Implementations</h5>
|
||||
*
|
||||
* It's easy to create your own `TimedObserver` implementations: Simply subclass `TimedObserver`
|
||||
* and provide the `getValue()` method. For example, this is the complete source code for
|
||||
* [[Form.Element.Observer]]:
|
||||
*
|
||||
* Form.Element.Observer = Class.create(Abstract.TimedObserver, {
|
||||
* getValue: function() {
|
||||
* return Form.Element.getValue(this.element);
|
||||
* }
|
||||
* });
|
||||
**/
|
||||
Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
|
||||
/**
|
||||
* new Abstract.TimedObserver(element, frequency, callback)
|
||||
* - element (String | Element): The DOM element to watch. Can be an element instance or an ID.
|
||||
* - frequency (Number): The frequency, in seconds — e.g., 0.33 to check for changes every
|
||||
* third of a second.
|
||||
* - callback (Function): The callback to trigger when the value changes.
|
||||
*
|
||||
* Initializes an `Abstract.TimedObserver`; used by subclasses.
|
||||
**/
|
||||
initialize: function($super, element, frequency, callback) {
|
||||
$super(callback, frequency);
|
||||
this.element = $(element);
|
||||
this.lastValue = this.getValue();
|
||||
},
|
||||
|
||||
execute: function() {
|
||||
var value = this.getValue();
|
||||
if (Object.isString(this.lastValue) && Object.isString(value) ?
|
||||
this.lastValue != value : String(this.lastValue) != String(value)) {
|
||||
this.callback(this.element, value);
|
||||
this.lastValue = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* class Form.Element.Observer < Abstract.TimedObserver
|
||||
*
|
||||
* An [[Abstract.TimedObserver]] subclass that watches for changes to a form field's value.
|
||||
* This triggers the callback when the form field's value (according to
|
||||
* [[Form.Element#getValue]]) changes. (Note that when the value actually changes can vary from
|
||||
* browser to browser, particularly with `select` boxes.)
|
||||
**/
|
||||
Form.Element.Observer = Class.create(Abstract.TimedObserver, {
|
||||
/**
|
||||
* new Form.Element.Observer(element, frequency, callback)
|
||||
* - element (String | Element): The form element to watch. Can be an element instance or an ID.
|
||||
* - frequency (Number): The frequency, in seconds — e.g., 0.33 to check for changes every
|
||||
* third of a second.
|
||||
* - callback (Function): The callback to trigger when the value changes.
|
||||
*
|
||||
* Creates a Form.Element.Observer.
|
||||
**/
|
||||
getValue: function() {
|
||||
return Form.Element.getValue(this.element);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* class Form.Observer < Abstract.TimedObserver
|
||||
*
|
||||
* An [[Abstract.TimedObserver]] subclass that watches for changes to a form.
|
||||
* The callback is triggered when the form changes — e.g., when any of its fields' values
|
||||
* changes, when fields are added/removed, etc.; anything that affects the serialized
|
||||
* form of the form (see [[Form#serialize]]).
|
||||
**/
|
||||
Form.Observer = Class.create(Abstract.TimedObserver, {
|
||||
/**
|
||||
* new Form.Observer(element, frequency, callback)
|
||||
* - element (String | Element): The element of the form to watch. Can be an element
|
||||
* instance or an ID.
|
||||
* - frequency (Number): The frequency, in seconds -- e.g., 0.33 to check for changes every
|
||||
* third of a second.
|
||||
* - callback (Function): The callback to trigger when the form changes.
|
||||
*
|
||||
* Creates a Form.Observer.
|
||||
**/
|
||||
getValue: function() {
|
||||
return Form.serialize(this.element);
|
||||
}
|
||||
});
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* class Abstract.EventObserver
|
||||
**/
|
||||
Abstract.EventObserver = Class.create({
|
||||
initialize: function(element, callback) {
|
||||
this.element = $(element);
|
||||
this.callback = callback;
|
||||
|
||||
this.lastValue = this.getValue();
|
||||
if (this.element.tagName.toLowerCase() == 'form')
|
||||
this.registerFormCallbacks();
|
||||
else
|
||||
this.registerCallback(this.element);
|
||||
},
|
||||
|
||||
onElementEvent: function() {
|
||||
var value = this.getValue();
|
||||
if (this.lastValue != value) {
|
||||
this.callback(this.element, value);
|
||||
this.lastValue = value;
|
||||
}
|
||||
},
|
||||
|
||||
registerFormCallbacks: function() {
|
||||
Form.getElements(this.element).each(this.registerCallback, this);
|
||||
},
|
||||
|
||||
registerCallback: function(element) {
|
||||
if (element.type) {
|
||||
switch (element.type.toLowerCase()) {
|
||||
case 'checkbox':
|
||||
case 'radio':
|
||||
Event.observe(element, 'click', this.onElementEvent.bind(this));
|
||||
break;
|
||||
default:
|
||||
Event.observe(element, 'change', this.onElementEvent.bind(this));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* class Form.Element.EventObserver < Abstract.EventObserver
|
||||
**/
|
||||
Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
|
||||
getValue: function() {
|
||||
return Form.Element.getValue(this.element);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* class Form.EventObserver < Abstract.EventObserver
|
||||
**/
|
||||
Form.EventObserver = Class.create(Abstract.EventObserver, {
|
||||
getValue: function() {
|
||||
return Form.serialize(this.element);
|
||||
}
|
||||
});
|
|
@ -1,66 +0,0 @@
|
|||
/** section: DOM, related to: Prototype.Selector
|
||||
* $$(expression...) -> [Element...]
|
||||
*
|
||||
* Returns all elements in the document that match the provided CSS selectors.
|
||||
**/
|
||||
|
||||
window.$$ = function() {
|
||||
var expression = $A(arguments).join(', ');
|
||||
return Prototype.Selector.select(expression, document);
|
||||
};
|
||||
|
||||
/**
|
||||
* Prototype.Selector
|
||||
*
|
||||
* A namespace that acts as a wrapper around
|
||||
* the choosen selector engine (Sizzle by default).
|
||||
*
|
||||
**/
|
||||
|
||||
// Implementation provided by selector engine.
|
||||
|
||||
/**
|
||||
* Prototype.Selector.select(expression[, root = document]) -> [Element...]
|
||||
* - expression (String): A CSS selector.
|
||||
* - root (Element | document): A "scope" to search within. All results will
|
||||
* be descendants of this node.
|
||||
*
|
||||
* Searches `root` for elements that match the provided CSS selector and returns an
|
||||
* array of extended [[Element]] objects.
|
||||
**/
|
||||
|
||||
// Implementation provided by selector engine.
|
||||
|
||||
/**
|
||||
* Prototype.Selector.match(element, expression) -> Boolean
|
||||
* - element (Element): a DOM element.
|
||||
* - expression (String): A CSS selector.
|
||||
*
|
||||
* Tests whether `element` matches the CSS selector.
|
||||
**/
|
||||
|
||||
// Implementation provided by selector engine.
|
||||
|
||||
/**
|
||||
* Prototype.Selector.find(elements, expression[, index = 0]) -> Element
|
||||
* - elements (Enumerable): a collection of DOM elements.
|
||||
* - expression (String): A CSS selector.
|
||||
* - index: Numeric index of the match to return, defaults to 0.
|
||||
*
|
||||
* Filters the given collection of elements with `expression` and returns the
|
||||
* first matching element (or the `index`th matching element if `index` is
|
||||
* specified).
|
||||
**/
|
||||
if (!Prototype.Selector.find) {
|
||||
Prototype.Selector.find = function(elements, expression, index) {
|
||||
if (Object.isUndefined(index)) index = 0;
|
||||
var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
if (match(elements[i], expression) && index == matchIndex++) {
|
||||
return Element.extend(elements[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
48
src/lang.js
48
src/lang.js
|
@ -1,48 +0,0 @@
|
|||
/**
|
||||
* == Language ==
|
||||
* Additions to JavaScript's "standard library" and extensions to
|
||||
* built-in JavaScript objects.
|
||||
**/
|
||||
|
||||
var Abstract = { };
|
||||
|
||||
/** section: Language
|
||||
* Try
|
||||
**/
|
||||
|
||||
/**
|
||||
* Try.these(function...) -> ?
|
||||
* - function (Function): A function that may throw an exception.
|
||||
*
|
||||
* Accepts an arbitrary number of functions and returns the result of the
|
||||
* first one that doesn't throw an error.
|
||||
**/
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
//= require "lang/class"
|
||||
//= require "lang/object"
|
||||
//= require "lang/function"
|
||||
//= require "lang/date"
|
||||
//= require "lang/regexp"
|
||||
//= require "lang/periodical_executer"
|
||||
//= require "lang/string"
|
||||
//= require "lang/template"
|
||||
//= require "lang/enumerable"
|
||||
//= require "lang/array"
|
||||
//= require "lang/hash"
|
||||
//= require "lang/number"
|
||||
//= require "lang/range"
|
|
@ -1,449 +0,0 @@
|
|||
/** section: Language, related to: Array
|
||||
* $A(iterable) -> Array
|
||||
*
|
||||
* Accepts an array-like collection (anything with numeric indices) and returns
|
||||
* its equivalent as an actual Array object.
|
||||
* This method is a convenience alias of [[Array.from]], but is the preferred way
|
||||
* of casting to an Array.
|
||||
**/
|
||||
function $A(iterable) {
|
||||
if (!iterable) return [];
|
||||
// Safari <2.0.4 crashes when accessing property of a node list with property accessor.
|
||||
// It nevertheless works fine with `in` operator, which is why we use it here
|
||||
if ('toArray' in Object(iterable)) return iterable.toArray();
|
||||
var length = iterable.length || 0, results = new Array(length);
|
||||
while (length--) results[length] = iterable[length];
|
||||
return results;
|
||||
}
|
||||
|
||||
/** section: Language, related to: Array
|
||||
* $w(string) -> Array
|
||||
* - string (String): A string with zero or more spaces.
|
||||
*
|
||||
* Splits a string into an array, treating all whitespace as delimiters.
|
||||
*
|
||||
* Equivalent to Ruby's `%w{foo bar}` or Perl's `qw(foo bar)`.
|
||||
**/
|
||||
function $w(string) {
|
||||
if (!Object.isString(string)) return [];
|
||||
string = string.strip();
|
||||
return string ? string.split(/\s+/) : [];
|
||||
}
|
||||
|
||||
/** alias of: $A
|
||||
* Array.from(iterable) -> Array
|
||||
**/
|
||||
Array.from = $A;
|
||||
|
||||
/** section: Language
|
||||
* class Array
|
||||
* includes Enumerable
|
||||
*
|
||||
* Prototype extends all native JavaScript arrays with quite a few powerful
|
||||
* methods.
|
||||
*
|
||||
* This is done in two ways:
|
||||
*
|
||||
* * It mixes in the [[Enumerable]] module, which brings in a ton of methods.
|
||||
* * It adds quite a few extra methods, which are documented in this section.
|
||||
*
|
||||
* With Prototype, arrays become much, much more than the trivial objects we
|
||||
* used to manipulate, limiting ourselves to using their `length` property and
|
||||
* their `[]` indexing operator. They become very powerful objects that
|
||||
* greatly simplify the code for 99% of the common use cases involving them.
|
||||
*
|
||||
* <h5>Why you should stop using for...in to iterate</h5>
|
||||
*
|
||||
* Many JavaScript authors have been misled into using the `for...in` JavaScript
|
||||
* construct to loop over array elements. This kind of code just won't work
|
||||
* with Prototype.
|
||||
*
|
||||
* The ECMA 262 standard, which defines ECMAScript 3rd edition, supposedly
|
||||
* implemented by all major browsers including MSIE, defines ten methods
|
||||
* on Array (§15.4.4), including nice methods like `concat`, `join`, `pop`, and
|
||||
* `push`.
|
||||
*
|
||||
* This same standard explicitly defines that the `for...in` construct (§12.6.4)
|
||||
* exists to enumerate the properties of the object appearing on the right side
|
||||
* of the `in` keyword. Only properties specifically marked as _non-enumerable_
|
||||
* are ignored by such a loop. By default, the `prototype` and `length`
|
||||
* properties are so marked, which prevents you from enumerating over array
|
||||
* methods when using for...in. This comfort led developers to use `for...in` as a
|
||||
* shortcut for indexing loops, when it is not its actual purpose.
|
||||
*
|
||||
* However, Prototype has no way to mark the methods it adds to
|
||||
* `Array.prototype` as non-enumerable. Therefore, using `for...in` on arrays
|
||||
* when using Prototype will enumerate all extended methods as well, such as
|
||||
* those coming from the [[Enumerable]] module, and those Prototype puts in the
|
||||
* Array namespace (listed further below).
|
||||
*
|
||||
* <h5>What you should use instead</h5>
|
||||
*
|
||||
* You can revert to vanilla loops:
|
||||
*
|
||||
* for (var index = 0; index < myArray.length; ++index) {
|
||||
* var item = myArray[index];
|
||||
* // Your code working on item here...
|
||||
* }
|
||||
*
|
||||
* Or you can use iterators, such as [[Array#each]]:
|
||||
*
|
||||
* myArray.each(function(item) {
|
||||
* // Your code working on item here...
|
||||
* });
|
||||
*
|
||||
*
|
||||
* The inability to use `for...in` on arrays is not much of a burden: as you'll
|
||||
* see, most of what you used to loop over arrays for can be concisely done
|
||||
* using the new methods provided by Array or the mixed-in [[Enumerable]]
|
||||
* module. So manual loops should be fairly rare.
|
||||
*
|
||||
*
|
||||
* <h5>A note on performance</h5>
|
||||
*
|
||||
* Should you have a very large array, using iterators with lexical closures
|
||||
* (anonymous functions that you pass to the iterators and that get invoked at
|
||||
* every loop iteration) in methods like [[Array#each]] — _or_ relying on
|
||||
* repetitive array construction (such as uniq), may yield unsatisfactory
|
||||
* performance. In such cases, you're better off writing manual indexing loops,
|
||||
* but take care then to cache the length property and use the prefix `++`
|
||||
* operator:
|
||||
*
|
||||
* // Custom loop with cached length property: maximum full-loop
|
||||
* // performance on very large arrays!
|
||||
* for (var index = 0, len = myArray.length; index < len; ++index) {
|
||||
* var item = myArray[index];
|
||||
* // Your code working on item here...
|
||||
* }
|
||||
*
|
||||
**/
|
||||
|
||||
(function() {
|
||||
var arrayProto = Array.prototype,
|
||||
slice = arrayProto.slice,
|
||||
_each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
|
||||
|
||||
function each(iterator) {
|
||||
for (var i = 0, length = this.length; i < length; i++)
|
||||
iterator(this[i]);
|
||||
}
|
||||
if (!_each) _each = each;
|
||||
|
||||
/**
|
||||
* Array#clear() -> Array
|
||||
*
|
||||
* Clears the array (makes it empty) and returns the array reference.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* var guys = ['Sam', 'Justin', 'Andrew', 'Dan'];
|
||||
* guys.clear();
|
||||
* // -> []
|
||||
* guys
|
||||
* // -> []
|
||||
**/
|
||||
function clear() {
|
||||
this.length = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Array#first() -> ?
|
||||
*
|
||||
* Returns the array's first item (e.g., `array[0]`).
|
||||
**/
|
||||
function first() {
|
||||
return this[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Array#last() -> ?
|
||||
*
|
||||
* Returns the array's last item (e.g., `array[array.length - 1]`).
|
||||
**/
|
||||
function last() {
|
||||
return this[this.length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Array#compact() -> Array
|
||||
*
|
||||
* Returns a **copy** of the array without any `null` or `undefined` values.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* var orig = [undefined, 'A', undefined, 'B', null, 'C'];
|
||||
* var copy = orig.compact();
|
||||
* // orig -> [undefined, 'A', undefined, 'B', null, 'C'];
|
||||
* // copy -> ['A', 'B', 'C'];
|
||||
**/
|
||||
function compact() {
|
||||
return this.select(function(value) {
|
||||
return value != null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Array#flatten() -> Array
|
||||
*
|
||||
* Returns a flattened (one-dimensional) copy of the array, leaving
|
||||
* the original array unchanged.
|
||||
*
|
||||
* Nested arrays are recursively injected inline. This can prove very
|
||||
* useful when handling the results of a recursive collection algorithm,
|
||||
* for instance.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* var a = ['frank', ['bob', 'lisa'], ['jill', ['tom', 'sally']]];
|
||||
* var b = a.flatten();
|
||||
* // a -> ['frank', ['bob', 'lisa'], ['jill', ['tom', 'sally']]]
|
||||
* // b -> ['frank', 'bob', 'lisa', 'jill', 'tom', 'sally']
|
||||
**/
|
||||
function flatten() {
|
||||
return this.inject([], function(array, value) {
|
||||
if (Object.isArray(value))
|
||||
return array.concat(value.flatten());
|
||||
array.push(value);
|
||||
return array;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Array#without(value[, value...]) -> Array
|
||||
* - value (?): A value to exclude.
|
||||
*
|
||||
* Produces a new version of the array that does not contain any of the
|
||||
* specified values, leaving the original array unchanged.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* [3, 5, 6].without(3)
|
||||
* // -> [5, 6]
|
||||
*
|
||||
* [3, 5, 6, 20].without(20, 6)
|
||||
* // -> [3, 5]
|
||||
**/
|
||||
function without() {
|
||||
var values = slice.call(arguments, 0);
|
||||
return this.select(function(value) {
|
||||
return !values.include(value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Array#reverse([inline = true]) -> Array
|
||||
* - inline (Boolean): Whether to modify the array in place. Defaults to `true`.
|
||||
* Clones the original array when `false`.
|
||||
*
|
||||
* Reverses the array's contents, optionally cloning it first.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* // Making a copy
|
||||
* var nums = [3, 5, 6, 1, 20];
|
||||
* var rev = nums.reverse(false);
|
||||
* // nums -> [3, 5, 6, 1, 20]
|
||||
* // rev -> [20, 1, 6, 5, 3]
|
||||
*
|
||||
* // Working inline
|
||||
* var nums = [3, 5, 6, 1, 20];
|
||||
* nums.reverse();
|
||||
* // nums -> [20, 1, 6, 5, 3]
|
||||
**/
|
||||
function reverse(inline) {
|
||||
return (inline === false ? this.toArray() : this)._reverse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Array#uniq([sorted = false]) -> Array
|
||||
* - sorted (Boolean): Whether the array has already been sorted. If `true`,
|
||||
* a less-costly algorithm will be used.
|
||||
*
|
||||
* Produces a duplicate-free version of an array. If no duplicates are
|
||||
* found, the original array is returned.
|
||||
*
|
||||
* On large arrays when `sorted` is `false`, this method has a potentially
|
||||
* large performance cost.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* [1, 3, 2, 1].uniq();
|
||||
* // -> [1, 2, 3]
|
||||
*
|
||||
* ['A', 'a'].uniq();
|
||||
* // -> ['A', 'a'] (because String comparison is case-sensitive)
|
||||
**/
|
||||
function uniq(sorted) {
|
||||
return this.inject([], function(array, value, index) {
|
||||
if (0 == index || (sorted ? array.last() != value : !array.include(value)))
|
||||
array.push(value);
|
||||
return array;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Array#intersect(array) -> Array
|
||||
* - array (Array): A collection of values.
|
||||
*
|
||||
* Returns an array containing every item that is shared between the two
|
||||
* given arrays.
|
||||
**/
|
||||
function intersect(array) {
|
||||
return this.uniq().findAll(function(item) {
|
||||
return array.detect(function(value) { return item === value });
|
||||
});
|
||||
}
|
||||
|
||||
/** alias of: Array#clone
|
||||
* Array#toArray() -> Array
|
||||
**/
|
||||
|
||||
/**
|
||||
* Array#clone() -> Array
|
||||
*
|
||||
* Returns a duplicate of the array, leaving the original array intact.
|
||||
**/
|
||||
function clone() {
|
||||
return slice.call(this, 0);
|
||||
}
|
||||
|
||||
/** related to: Enumerable#size
|
||||
* Array#size() -> Number
|
||||
*
|
||||
* Returns the size of the array (e.g., `array.length`).
|
||||
*
|
||||
* This is just a local optimization of the mixed-in [[Enumerable#size]]
|
||||
* which avoids array cloning and uses the array's native length property.
|
||||
**/
|
||||
function size() {
|
||||
return this.length;
|
||||
}
|
||||
|
||||
/** related to: Object.inspect
|
||||
* Array#inspect() -> String
|
||||
*
|
||||
* Returns the debug-oriented string representation of an array.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* ['Apples', {good: 'yes', bad: 'no'}, 3, 34].inspect()
|
||||
* // -> "['Apples', [object Object], 3, 34]"
|
||||
**/
|
||||
function inspect() {
|
||||
return '[' + this.map(Object.inspect).join(', ') + ']';
|
||||
}
|
||||
|
||||
/** related to: Object.toJSON
|
||||
* Array#toJSON() -> String
|
||||
*
|
||||
* Returns a JSON string representation of the array.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* ['a', {b: null}].toJSON();
|
||||
* //-> '["a", {"b": null}]'
|
||||
**/
|
||||
function toJSON() {
|
||||
var results = [];
|
||||
this.each(function(object) {
|
||||
var value = Object.toJSON(object);
|
||||
if (!Object.isUndefined(value)) results.push(value);
|
||||
});
|
||||
return '[' + results.join(', ') + ']';
|
||||
}
|
||||
|
||||
/**
|
||||
* Array#indexOf(item[, offset = 0]) -> Number
|
||||
* - item (?): A value that may or may not be in the array.
|
||||
* - offset (Number): The number of initial items to skip before beginning
|
||||
* the search.
|
||||
*
|
||||
* Returns the index of the first occurrence of `item` within the array,
|
||||
* or `-1` if `item` doesn't exist in the array. `Array#indexOf` compares
|
||||
* items using *strict equality* (`===`).
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* [3, 5, 6, 1, 20].indexOf(1)
|
||||
* // -> 3
|
||||
*
|
||||
* [3, 5, 6, 1, 20].indexOf(90)
|
||||
* // -> -1 (not found)
|
||||
*
|
||||
* ['1', '2', '3'].indexOf(1);
|
||||
* // -> -1 (not found, 1 !== '1')
|
||||
**/
|
||||
function indexOf(item, i) {
|
||||
i || (i = 0);
|
||||
var length = this.length;
|
||||
if (i < 0) i = length + i;
|
||||
for (; i < length; i++)
|
||||
if (this[i] === item) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** related to: Array#indexOf
|
||||
* Array#lastIndexOf(item[, offset]) -> Number
|
||||
* - item (?): A value that may or may not be in the array.
|
||||
* - offset (Number): The number of items at the end to skip before beginning
|
||||
* the search.
|
||||
*
|
||||
* Returns the position of the last occurrence of `item` within the array — or
|
||||
* `-1` if `item` doesn't exist in the array.
|
||||
**/
|
||||
function lastIndexOf(item, i) {
|
||||
i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
|
||||
var n = this.slice(0, i).reverse().indexOf(item);
|
||||
return (n < 0) ? n : i - n - 1;
|
||||
}
|
||||
|
||||
// Replaces a built-in function. No PDoc needed.
|
||||
function concat() {
|
||||
var array = slice.call(this, 0), item;
|
||||
for (var i = 0, length = arguments.length; i < length; i++) {
|
||||
item = arguments[i];
|
||||
if (Object.isArray(item) && !('callee' in item)) {
|
||||
for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
|
||||
array.push(item[j]);
|
||||
} else {
|
||||
array.push(item);
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
Object.extend(arrayProto, Enumerable);
|
||||
|
||||
if (!arrayProto._reverse)
|
||||
arrayProto._reverse = arrayProto.reverse;
|
||||
|
||||
Object.extend(arrayProto, {
|
||||
_each: _each,
|
||||
clear: clear,
|
||||
first: first,
|
||||
last: last,
|
||||
compact: compact,
|
||||
flatten: flatten,
|
||||
without: without,
|
||||
reverse: reverse,
|
||||
uniq: uniq,
|
||||
intersect: intersect,
|
||||
clone: clone,
|
||||
toArray: clone,
|
||||
size: size,
|
||||
inspect: inspect,
|
||||
toJSON: toJSON
|
||||
});
|
||||
|
||||
// fix for opera
|
||||
var CONCAT_ARGUMENTS_BUGGY = (function() {
|
||||
return [].concat(arguments)[0][0] !== 1;
|
||||
})(1,2)
|
||||
|
||||
if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
|
||||
|
||||
// use native browser JS 1.6 implementation if available
|
||||
if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
|
||||
if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
|
||||
})();
|
|
@ -1,186 +0,0 @@
|
|||
/* Based on Alex Arnell's inheritance implementation. */
|
||||
|
||||
/** section: Language
|
||||
* Class
|
||||
*
|
||||
* Manages Prototype's class-based OOP system.
|
||||
*
|
||||
* Refer to Prototype's web site for a [tutorial on classes and
|
||||
* inheritance](http://prototypejs.org/learn/class-inheritance).
|
||||
**/
|
||||
var Class = (function() {
|
||||
|
||||
// Some versions of JScript fail to enumerate over properties, names of which
|
||||
// correspond to non-enumerable properties in the prototype chain
|
||||
var IS_DONTENUM_BUGGY = (function(){
|
||||
for (var p in { toString: 1 }) {
|
||||
// check actual property name, so that it works with augmented Object.prototype
|
||||
if (p === 'toString') return false;
|
||||
}
|
||||
return true;
|
||||
})();
|
||||
|
||||
/**
|
||||
* Class.create([superclass][, methods...]) -> Class
|
||||
* - superclass (Class): The optional superclass to inherit methods from.
|
||||
* - methods (Object): An object whose properties will be "mixed-in" to the
|
||||
* new class. Any number of mixins can be added; later mixins take
|
||||
* precedence.
|
||||
*
|
||||
* `Class.create` creates a class and returns a constructor function for
|
||||
* instances of the class. Calling the constructor function (typically as
|
||||
* part of a `new` statement) will invoke the class's `initialize` method.
|
||||
*
|
||||
* `Class.create` accepts two kinds of arguments. If the first argument is
|
||||
* a `Class`, it's used as the new class's superclass, and all its methods
|
||||
* are inherited. Otherwise, any arguments passed are treated as objects,
|
||||
* and their methods are copied over ("mixed in") as instance methods of the
|
||||
* new class. In cases of method name overlap, later arguments take
|
||||
* precedence over earlier arguments.
|
||||
*
|
||||
* If a subclass overrides an instance method declared in a superclass, the
|
||||
* subclass's method can still access the original method. To do so, declare
|
||||
* the subclass's method as normal, but insert `$super` as the first
|
||||
* argument. This makes `$super` available as a method for use within the
|
||||
* function.
|
||||
*
|
||||
* To extend a class after it has been defined, use [[Class#addMethods]].
|
||||
*
|
||||
* For details, see the
|
||||
* [inheritance tutorial](http://prototypejs.org/learn/class-inheritance)
|
||||
* on the Prototype website.
|
||||
**/
|
||||
function subclass() {};
|
||||
function create() {
|
||||
var parent = null, properties = $A(arguments);
|
||||
if (Object.isFunction(properties[0]))
|
||||
parent = properties.shift();
|
||||
|
||||
function klass() {
|
||||
this.initialize.apply(this, arguments);
|
||||
}
|
||||
|
||||
Object.extend(klass, Class.Methods);
|
||||
klass.superclass = parent;
|
||||
klass.subclasses = [];
|
||||
|
||||
if (parent) {
|
||||
subclass.prototype = parent.prototype;
|
||||
klass.prototype = new subclass;
|
||||
parent.subclasses.push(klass);
|
||||
}
|
||||
|
||||
for (var i = 0, length = properties.length; i < length; i++)
|
||||
klass.addMethods(properties[i]);
|
||||
|
||||
if (!klass.prototype.initialize)
|
||||
klass.prototype.initialize = Prototype.emptyFunction;
|
||||
|
||||
klass.prototype.constructor = klass;
|
||||
return klass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class#addMethods(methods) -> Class
|
||||
* - methods (Object): The methods to add to the class.
|
||||
*
|
||||
* Adds methods to an existing class.
|
||||
*
|
||||
* `Class#addMethods` is a method available on classes that have been
|
||||
* defined with `Class.create`. It can be used to add new instance methods
|
||||
* to that class, or overwrite existing methods, after the class has been
|
||||
* defined.
|
||||
*
|
||||
* New methods propagate down the inheritance chain. If the class has
|
||||
* subclasses, those subclasses will receive the new methods — even in
|
||||
* the context of `$super` calls. The new methods also propagate to instances
|
||||
* of the class and of all its subclasses, even those that have already been
|
||||
* instantiated.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* var Animal = Class.create({
|
||||
* initialize: function(name, sound) {
|
||||
* this.name = name;
|
||||
* this.sound = sound;
|
||||
* },
|
||||
*
|
||||
* speak: function() {
|
||||
* alert(this.name + " says: " + this.sound + "!");
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* // subclassing Animal
|
||||
* var Snake = Class.create(Animal, {
|
||||
* initialize: function($super, name) {
|
||||
* $super(name, 'hissssssssss');
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* var ringneck = new Snake("Ringneck");
|
||||
* ringneck.speak();
|
||||
*
|
||||
* //-> alerts "Ringneck says: hissssssss!"
|
||||
*
|
||||
* // adding Snake#speak (with a supercall)
|
||||
* Snake.addMethods({
|
||||
* speak: function($super) {
|
||||
* $super();
|
||||
* alert("You should probably run. He looks really mad.");
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* ringneck.speak();
|
||||
* //-> alerts "Ringneck says: hissssssss!"
|
||||
* //-> alerts "You should probably run. He looks really mad."
|
||||
*
|
||||
* // redefining Animal#speak
|
||||
* Animal.addMethods({
|
||||
* speak: function() {
|
||||
* alert(this.name + 'snarls: ' + this.sound + '!');
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* ringneck.speak();
|
||||
* //-> alerts "Ringneck snarls: hissssssss!"
|
||||
* //-> alerts "You should probably run. He looks really mad."
|
||||
**/
|
||||
function addMethods(source) {
|
||||
var ancestor = this.superclass && this.superclass.prototype,
|
||||
properties = Object.keys(source);
|
||||
|
||||
// IE6 doesn't enumerate `toString` and `valueOf` (among other built-in `Object.prototype`) properties,
|
||||
// Force copy if they're not Object.prototype ones.
|
||||
// Do not copy other Object.prototype.* for performance reasons
|
||||
if (IS_DONTENUM_BUGGY) {
|
||||
if (source.toString != Object.prototype.toString)
|
||||
properties.push("toString");
|
||||
if (source.valueOf != Object.prototype.valueOf)
|
||||
properties.push("valueOf");
|
||||
}
|
||||
|
||||
for (var i = 0, length = properties.length; i < length; i++) {
|
||||
var property = properties[i], value = source[property];
|
||||
if (ancestor && Object.isFunction(value) &&
|
||||
value.argumentNames()[0] == "$super") {
|
||||
var method = value;
|
||||
value = (function(m) {
|
||||
return function() { return ancestor[m].apply(this, arguments); };
|
||||
})(property).wrap(method);
|
||||
|
||||
value.valueOf = method.valueOf.bind(method);
|
||||
value.toString = method.toString.bind(method);
|
||||
}
|
||||
this.prototype[property] = value;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
return {
|
||||
create: create,
|
||||
Methods: {
|
||||
addMethods: addMethods
|
||||
}
|
||||
};
|
||||
})();
|
|
@ -1,29 +0,0 @@
|
|||
/** section: Language
|
||||
* class Date
|
||||
*
|
||||
* Extensions to the built-in `Date` object.
|
||||
**/
|
||||
|
||||
/**
|
||||
* Date#toJSON() -> String
|
||||
*
|
||||
* Produces a string representation of the date in ISO 8601 format.
|
||||
* The time zone is always UTC, as denoted by the suffix "Z".
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* var d = new Date(1969, 11, 31, 19);
|
||||
* d.getTimezoneOffset();
|
||||
* //-> -180 (time offest is given in minutes.)
|
||||
* d.toJSON();
|
||||
* //-> '"1969-12-31T16:00:00Z"'
|
||||
**/
|
||||
Date.prototype.toJSON = function() {
|
||||
return '"' + this.getUTCFullYear() + '-' +
|
||||
(this.getUTCMonth() + 1).toPaddedString(2) + '-' +
|
||||
this.getUTCDate().toPaddedString(2) + 'T' +
|
||||
this.getUTCHours().toPaddedString(2) + ':' +
|
||||
this.getUTCMinutes().toPaddedString(2) + ':' +
|
||||
this.getUTCSeconds().toPaddedString(2) + 'Z"';
|
||||
};
|
||||
|
|
@ -1,821 +0,0 @@
|
|||
/** section: Language
|
||||
* mixin Enumerable
|
||||
*
|
||||
* `Enumerable` provides a large set of useful methods for enumerations —
|
||||
* objects that act as collections of values. It is a cornerstone of
|
||||
* Prototype.
|
||||
*
|
||||
* `Enumerable` is a _mixin_: a set of methods intended not for standaone
|
||||
* use, but for incorporation into other objects.
|
||||
*
|
||||
* Prototype mixes `Enumerable` into several classes. The most visible cases
|
||||
* are [[Array]] and [[Hash]], but you'll find it in less obvious spots as
|
||||
* well, such as in [[ObjectRange]] and various DOM- or Ajax-related objects.
|
||||
*
|
||||
* <h5>The <code>context</code> parameter</h5>
|
||||
*
|
||||
* Every method of `Enumerable` that takes an iterator also takes the "context
|
||||
* object" as the next (optional) parameter. The context object is what the
|
||||
* iterator will be _bound_ to — what the keyword `this` will refer to inside
|
||||
* the iterator.
|
||||
*
|
||||
* var myObject = {};
|
||||
*
|
||||
* ['foo', 'bar', 'baz'].each(function(name, index) {
|
||||
* this[name] = index;
|
||||
* }, myObject); // we have specified the context
|
||||
*
|
||||
* myObject;
|
||||
* // -> { foo: 0, bar: 1, baz: 2}
|
||||
*
|
||||
* If there is no `context` argument, the iterator function will execute in
|
||||
* the scope from which the `Enumerable` method itself was called.
|
||||
*
|
||||
* <h5>Mixing <code>Enumerable</code> into your own objects</h5>
|
||||
*
|
||||
* So, let's say you've created your very own collection-like object (say,
|
||||
* some sort of Set, or perhaps something that dynamically fetches data
|
||||
* ranges from the server side, lazy-loading style). You want to be able to
|
||||
* mix `Enumerable` in (and we commend you for it). How do you go about this?
|
||||
*
|
||||
* The Enumerable module basically makes only one requirement on your object:
|
||||
* it must provide a method named `_each` (note the leading underscore) that
|
||||
* will accept a function as its unique argument, and will contain the actual
|
||||
* "raw iteration" algorithm, invoking its argument with each element in turn.
|
||||
*
|
||||
* As detailed in the documentation for [[Enumerable#each]], `Enumerable`
|
||||
* provides all the extra layers (handling iteration short-circuits, passing
|
||||
* numeric indices, etc.). You just need to implement the actual iteration,
|
||||
* as fits your internal structure.
|
||||
*
|
||||
* If you're still confused, just have a look at the Prototype source code for
|
||||
* [[Array]], [[Hash]], or [[ObjectRange]]. They all begin with their own
|
||||
* `_each` method, which should help you grasp the idea.
|
||||
*
|
||||
* Once you're done with this, you just need to mix `Enumerable` in, which
|
||||
* you'll usually do before defining your methods, so as to make sure whatever
|
||||
* overrides you provide for `Enumerable` methods will indeed prevail. In
|
||||
* short, your code will probably end up looking like this:
|
||||
*
|
||||
*
|
||||
* var YourObject = Class.create(Enumerable, {
|
||||
* initialize: function() { // with whatever constructor arguments you need
|
||||
* // Your construction code
|
||||
* },
|
||||
*
|
||||
* _each: function(iterator) {
|
||||
* // Your iteration code, invoking iterator at every turn
|
||||
* },
|
||||
*
|
||||
* // Your other methods here, including Enumerable overrides
|
||||
* });
|
||||
*
|
||||
* Then, obviously, your object can be used like this:
|
||||
*
|
||||
* var obj = new YourObject();
|
||||
* // Populate the collection somehow
|
||||
* obj.pluck('somePropName');
|
||||
* obj.invoke('someMethodName');
|
||||
* obj.size();
|
||||
* // etc.
|
||||
*
|
||||
**/
|
||||
|
||||
var $break = { };
|
||||
|
||||
var Enumerable = (function() {
|
||||
/**
|
||||
* Enumerable#each(iterator[, context]) -> Enumerable
|
||||
* - iterator (Function): A `Function` that expects an item in the
|
||||
* collection as the first argument and a numerical index as the second.
|
||||
* - context (Object): The scope in which to call `iterator`. Affects what
|
||||
* the keyword `this` means inside `iterator`.
|
||||
*
|
||||
* Calls `iterator` for each item in the collection.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* ['one', 'two', 'three'].each(alert);
|
||||
* // Alerts "one", then alerts "two", then alerts "three"
|
||||
*
|
||||
* <h5>Built-In Variants</h5>
|
||||
*
|
||||
* Most of the common use cases for `each` are already available pre-coded
|
||||
* as other methods on `Enumerable`. Whether you want to find the first
|
||||
* matching item in an enumeration, or transform it, or determine whether it
|
||||
* has any (or all) values matching a particular condition, `Enumerable`
|
||||
* has a method to do that for you.
|
||||
**/
|
||||
function each(iterator, context) {
|
||||
var index = 0;
|
||||
try {
|
||||
this._each(function(value) {
|
||||
iterator.call(context, value, index++);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e != $break) throw e;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#eachSlice(number[, iterator = Prototype.K[, context]]) -> Enumerable
|
||||
* - number (Number): The number of items to include in each slice.
|
||||
* - iterator (Function): An optional function to use to transform each
|
||||
* element before it's included in the slice; if this is not provided,
|
||||
* the element itself is included.
|
||||
* - context (Object): An optional object to use as `this` within
|
||||
* calls to the iterator.
|
||||
*
|
||||
* Groups items into chunks of the given size. The final "slice" may have
|
||||
* fewer than `number` items; it won't "pad" the last group with empty
|
||||
* values. For that behavior, use [[Enumerable#inGroupsOf]].
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* var students = [
|
||||
* { name: 'Sunny', age: 20 },
|
||||
* { name: 'Audrey', age: 21 },
|
||||
* { name: 'Matt', age: 20 },
|
||||
* { name: 'Amelie', age: 26 },
|
||||
* { name: 'Will', age: 21 }
|
||||
* ];
|
||||
*
|
||||
* students.eachSlice(3, function(student) {
|
||||
* return student.name;
|
||||
* });
|
||||
* // -> [['Sunny', 'Audrey', 'Matt'], ['Amelie', 'Will']]
|
||||
**/
|
||||
function eachSlice(number, iterator, context) {
|
||||
var index = -number, slices = [], array = this.toArray();
|
||||
if (number < 1) return array;
|
||||
while ((index += number) < array.length)
|
||||
slices.push(array.slice(index, index+number));
|
||||
return slices.collect(iterator, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#all([iterator = Prototype.K[, context]]) -> Boolean
|
||||
* - iterator (Function): An optional function to use to evaluate
|
||||
* each element in the enumeration; the function should return the value to
|
||||
* test. If this is not provided, the element itself is tested.
|
||||
* - context (Object): An optional object to use as `this` within
|
||||
* calls to the iterator.
|
||||
*
|
||||
* Determines whether all the elements are "truthy" (boolean-equivalent to
|
||||
* `true`), either directly or through computation by the provided iterator.
|
||||
* Stops on the first falsy element found (e.g., the first element that
|
||||
* is boolean-equivalent to `false`, such as `undefined`, `0`, or indeed
|
||||
* `false`);
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* [].all();
|
||||
* // -> true (empty arrays have no elements that could be falsy)
|
||||
*
|
||||
* $R(1, 5).all();
|
||||
* // -> true (all values in [1..5] are truthy)
|
||||
*
|
||||
* [0, 1, 2].all();
|
||||
* // -> false (with only one loop cycle: 0 is falsy)
|
||||
*
|
||||
* [9, 10, 15].all(function(n) { return n >= 10; });
|
||||
* // -> false (the iterator returns false on 9)
|
||||
**/
|
||||
function all(iterator, context) {
|
||||
iterator = iterator || Prototype.K;
|
||||
var result = true;
|
||||
this.each(function(value, index) {
|
||||
result = result && !!iterator.call(context, value, index);
|
||||
if (!result) throw $break;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#any([iterator = Prototype.K[, context]]) -> Boolean
|
||||
* - iterator (Function): An optional function to use to evaluate each
|
||||
* element in the enumeration; the function should return the value to
|
||||
* test. If this is not provided, the element itself is tested.
|
||||
* - context (Object): An optional object to use as `this` within
|
||||
* calls to the iterator.
|
||||
*
|
||||
* Determines whether at least one element is truthy (boolean-equivalent to
|
||||
* `true`), either directly or through computation by the provided iterator.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* [].any();
|
||||
* // -> false (empty arrays have no elements that could be truthy)
|
||||
*
|
||||
* $R(0, 2).any();
|
||||
* // -> true (on the second loop, 1 is truthy)
|
||||
*
|
||||
* [2, 4, 6, 8, 10].any(function(n) { return n > 5; });
|
||||
* // -> true (the iterator will return true on 6)
|
||||
**/
|
||||
function any(iterator, context) {
|
||||
iterator = iterator || Prototype.K;
|
||||
var result = false;
|
||||
this.each(function(value, index) {
|
||||
if (result = !!iterator.call(context, value, index))
|
||||
throw $break;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#collect([iterator = Prototype.K[, context]]) -> Array
|
||||
* - iterator (Function): The iterator function to apply to each element
|
||||
* in the enumeration.
|
||||
* - context (Object): An optional object to use as `this` within
|
||||
* calls to the iterator.
|
||||
*
|
||||
* Returns the result of applying `iterator` to each element. If no
|
||||
* `iterator` is provided, the elements are simply copied to the
|
||||
* returned array.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* ['Hitch', "Hiker's", 'Guide', 'to', 'the', 'Galaxy'].collect(function(s) {
|
||||
* return s.charAt(0).toUpperCase();
|
||||
* });
|
||||
* // -> ['H', 'H', 'G', 'T', 'T', 'G']
|
||||
*
|
||||
* $R(1,5).collect(function(n) {
|
||||
* return n * n;
|
||||
* });
|
||||
* // -> [1, 4, 9, 16, 25]
|
||||
**/
|
||||
function collect(iterator, context) {
|
||||
iterator = iterator || Prototype.K;
|
||||
var results = [];
|
||||
this.each(function(value, index) {
|
||||
results.push(iterator.call(context, value, index));
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#detect(iterator[, context]) -> firstElement | undefined
|
||||
* - iterator (Function): The iterator function to apply to each element
|
||||
* in the enumeration.
|
||||
* - context (Object): An optional object to use as `this` within
|
||||
* calls to the iterator.
|
||||
*
|
||||
* Returns the first element for which the iterator returns a truthy value.
|
||||
* Aliased by the [[Enumerable#find]] method.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* [1, 7, -2, -4, 5].detect(function(n) { return n < 0; });
|
||||
* // -> -2
|
||||
**/
|
||||
function detect(iterator, context) {
|
||||
var result;
|
||||
this.each(function(value, index) {
|
||||
if (iterator.call(context, value, index)) {
|
||||
result = value;
|
||||
throw $break;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#findAll(iterator[, context]) -> Array
|
||||
* - iterator (Function): An iterator function to use to test the elements.
|
||||
* - context (Object): An optional object to use as `this` within
|
||||
* calls to the iterator.
|
||||
*
|
||||
* Returns all the elements for which the iterator returned a truthy value.
|
||||
* For the opposite operation, see [[Enumerable#reject]].
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* [1, 'two', 3, 'four', 5].findAll(Object.isString);
|
||||
* // -> ['two', 'four']
|
||||
**/
|
||||
function findAll(iterator, context) {
|
||||
var results = [];
|
||||
this.each(function(value, index) {
|
||||
if (iterator.call(context, value, index))
|
||||
results.push(value);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#grep(filter[, iterator = Prototype.K[, context]]) -> Array
|
||||
* - filter (RegExp | String | Object): The filter to apply to elements. This
|
||||
* can be a `RegExp` instance, a regular expression [[String]], or any
|
||||
* object with a `match` function.
|
||||
* - iterator (Function): An optional function to apply to selected elements
|
||||
* before including them in the result.
|
||||
* - context (Object): An optional object to use as `this` within
|
||||
* calls to the iterator.
|
||||
*
|
||||
* Returns an array containing all of the elements for which the given
|
||||
* filter returns `true` (or a truthy value). If an iterator is provided,
|
||||
* it is used to produce the returned value for each selected element; this
|
||||
* is done *after* the element has been selected by the filter.
|
||||
*
|
||||
* If the given filter is a [[String]], it is converted into a `RegExp`
|
||||
* object. To select elements, each element is passed into the filter's
|
||||
* `match` function, which should return a truthy value to select the element
|
||||
* or a falsy value not to. Note that the `RegExp` `match` function will
|
||||
* convert elements to Strings to perform matching.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* // Get all strings containing a repeated letter
|
||||
* ['hello', 'world', 'this', 'is', 'cool'].grep(/(.)\1/);
|
||||
* // -> ['hello', 'cool']
|
||||
*
|
||||
* // Get all numbers ending with 0 or 5 and subtract 1 from them
|
||||
* $R(1, 30).grep(/[05]$/, function(n) { return n - 1; });
|
||||
* // -> [4, 9, 14, 19, 24, 29]
|
||||
**/
|
||||
function grep(filter, iterator, context) {
|
||||
iterator = iterator || Prototype.K;
|
||||
var results = [];
|
||||
|
||||
if (Object.isString(filter))
|
||||
filter = new RegExp(RegExp.escape(filter));
|
||||
|
||||
this.each(function(value, index) {
|
||||
if (filter.match(value))
|
||||
results.push(iterator.call(context, value, index));
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#include(object) -> Boolean
|
||||
* - object (?): The object to look for.
|
||||
*
|
||||
* Determines whether a given object is in the enumerable or not,
|
||||
* based on the `==` comparison operator (equality with implicit type
|
||||
* conversion).
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* $R(1, 15).include(10);
|
||||
* // -> true
|
||||
*
|
||||
* ['hello', 'world'].include('HELLO');
|
||||
* // -> false ('hello' != 'HELLO')
|
||||
*
|
||||
* [1, 2, '3', '4', '5'].include(3);
|
||||
* // -> true ('3' == 3)
|
||||
**/
|
||||
function include(object) {
|
||||
if (Object.isFunction(this.indexOf))
|
||||
if (this.indexOf(object) != -1) return true;
|
||||
|
||||
var found = false;
|
||||
this.each(function(value) {
|
||||
if (value == object) {
|
||||
found = true;
|
||||
throw $break;
|
||||
}
|
||||
});
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#inGroupsOf(number[, fillWith = null]) -> [group...]
|
||||
* - number (Number): The number of items to include in each group.
|
||||
* - fillWith (Object): An optional filler to use if the last group needs
|
||||
* any; defaults to `null`.
|
||||
*
|
||||
* Like [[Enumerable#eachSlice]], but pads out the last chunk with the
|
||||
* specified value if necessary and doesn't support the `iterator` function.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* var students = [
|
||||
* { name: 'Sunny', age: 20 },
|
||||
* { name: 'Audrey', age: 21 },
|
||||
* { name: 'Matt', age: 20 },
|
||||
* { name: 'Amelie', age: 26 },
|
||||
* { name: 'Will', age: 21 }
|
||||
* ];
|
||||
*
|
||||
* students.inGroupsOf(2, { name: '', age: 0 });
|
||||
* // -> [
|
||||
* // [{ name: 'Sunny', age: 20 }, { name: 'Audrey', age: 21 }],
|
||||
* // [{ name: 'Matt', age: 20 }, { name: 'Amelie', age: 26 }],
|
||||
* // [{ name: 'Will', age: 21 }, { name: '', age: 0 }]
|
||||
* // ]
|
||||
**/
|
||||
function inGroupsOf(number, fillWith) {
|
||||
fillWith = Object.isUndefined(fillWith) ? null : fillWith;
|
||||
return this.eachSlice(number, function(slice) {
|
||||
while(slice.length < number) slice.push(fillWith);
|
||||
return slice;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#inject(accumulator, iterator[, context]) -> accumulatedValue
|
||||
* - accumulator (?): The initial value to which the `iterator` adds.
|
||||
* - iterator (Function): An iterator function used to build the accumulated
|
||||
* result.
|
||||
* - context (Object): An optional object to use as `this` within
|
||||
* calls to the iterator.
|
||||
*
|
||||
* Incrementally builds a result value based on the successive results
|
||||
* of the iterator. This can be used for array construction, numerical
|
||||
* sums/averages, etc.
|
||||
*
|
||||
* The `iterator` function is called once for each element in the
|
||||
* enumeration, receiving the current value of the accumulator as its first
|
||||
* argument, the element as its second argument, and the element's index as
|
||||
* its third. It returns the new value for the accumulator.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* $R(1,10).inject(0, function(acc, n) { return acc + n; });
|
||||
* // -> 55 (sum of 1 to 10)
|
||||
*
|
||||
* ['a', 'b', 'c', 'd', 'e'].inject([], function(string, value, index) {
|
||||
* if (index % 2 === 0) { // even numbers
|
||||
* string += value;
|
||||
* }
|
||||
* return string;
|
||||
* });
|
||||
* // -> 'ace'
|
||||
**/
|
||||
function inject(memo, iterator, context) {
|
||||
this.each(function(value, index) {
|
||||
memo = iterator.call(context, memo, value, index);
|
||||
});
|
||||
return memo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#invoke(methodName[, arg...]) -> Array
|
||||
* - methodName (String): The name of the method to invoke.
|
||||
* - args (?): Optional arguments to pass to the method.
|
||||
*
|
||||
* Invokes the same method, with the same arguments, for all items in a
|
||||
* collection. Returns an array of the results of the method calls.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* ['hello', 'world'].invoke('toUpperCase');
|
||||
* // -> ['HELLO', 'WORLD']
|
||||
*
|
||||
* ['hello', 'world'].invoke('substring', 0, 3);
|
||||
* // -> ['hel', 'wor']
|
||||
*
|
||||
* $$('input').invoke('stopObserving', 'change');
|
||||
* // -> Stops observing the 'change' event on all input elements,
|
||||
* // returns an array of the element references.
|
||||
**/
|
||||
function invoke(method) {
|
||||
var args = $A(arguments).slice(1);
|
||||
return this.map(function(value) {
|
||||
return value[method].apply(value, args);
|
||||
});
|
||||
}
|
||||
|
||||
/** related to: Enumerable#min
|
||||
* Enumerable#max([iterator = Prototype.K[, context]]) -> maxValue
|
||||
* - iterator (Function): An optional function to use to evaluate each
|
||||
* element in the enumeration; the function should return the value to
|
||||
* test. If this is not provided, the element itself is tested.
|
||||
* - context (Object): An optional object to use as `this` within
|
||||
* calls to the iterator.
|
||||
*
|
||||
* Returns the maximum element (or element-based `iterator` result), or
|
||||
* `undefined` if the enumeration is empty. Elements are either compared
|
||||
* directly, or by first calling `iterator` and comparing returned values.
|
||||
* If multiple "max" elements (or results) are equivalent, the one closest
|
||||
* to the end of the enumeration is returned.
|
||||
*
|
||||
* If provided, `iterator` is called with two arguments: The element being
|
||||
* evaluated, and its index in the enumeration; it should return the value
|
||||
* `max` should consider (and potentially return).
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* ['c', 'b', 'a'].max();
|
||||
* // -> 'c'
|
||||
*
|
||||
* [1, 3, '3', 2].max();
|
||||
* // -> '3' (because both 3 and '3' are "max", and '3' was later)
|
||||
*
|
||||
* ['zero', 'one', 'two'].max(function(item) { return item.length; });
|
||||
* // -> 4
|
||||
**/
|
||||
function max(iterator, context) {
|
||||
iterator = iterator || Prototype.K;
|
||||
var result;
|
||||
this.each(function(value, index) {
|
||||
value = iterator.call(context, value, index);
|
||||
if (result == null || value >= result)
|
||||
result = value;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/** related to: Enumerable#max
|
||||
* Enumerable#min([iterator = Prototype.K[, context]]) -> minValue
|
||||
* - iterator (Function): An optional function to use to evaluate each
|
||||
* element in the enumeration; the function should return the value to
|
||||
* test. If this is not provided, the element itself is tested.
|
||||
* - context (Object): An optional object to use as `this` within
|
||||
* calls to the iterator.
|
||||
*
|
||||
* Returns the minimum element (or element-based `iterator` result), or
|
||||
* `undefined` if the enumeration is empty. Elements are either compared
|
||||
* directly, or by first calling `iterator` and comparing returned values.
|
||||
* If multiple "min" elements (or results) are equivalent, the one closest
|
||||
* to the *beginning* of the enumeration is returned.
|
||||
*
|
||||
* If provided, `iterator` is called with two arguments: The element being
|
||||
* evaluated, and its index in the enumeration; it should return the value
|
||||
* `min` should consider (and potentially return).
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* ['c', 'b', 'a'].min();
|
||||
* // -> 'a'
|
||||
*
|
||||
* [3, 1, '1', 2].min();
|
||||
* // -> 1 (because both 1 and '1' are "min", and 1 was earlier)
|
||||
*
|
||||
* ['un', 'deux', 'trois'].min(function(item) { return item.length; });
|
||||
* // -> 2
|
||||
**/
|
||||
function min(iterator, context) {
|
||||
iterator = iterator || Prototype.K;
|
||||
var result;
|
||||
this.each(function(value, index) {
|
||||
value = iterator.call(context, value, index);
|
||||
if (result == null || value < result)
|
||||
result = value;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#partition([iterator = Prototype.K[, context]]) -> [TrueArray, FalseArray]
|
||||
* - iterator (Function): An optional function to use to evaluate each
|
||||
* element in the enumeration; the function should return the value to
|
||||
* test. If this is not provided, the element itself is tested.
|
||||
* - context (Object): An optional object to use as `this` within
|
||||
* calls to the iterator.
|
||||
*
|
||||
* Partitions the elements in two groups: those regarded as true, and those
|
||||
* considered false. By default, regular JavaScript boolean equivalence
|
||||
* (e.g., truthiness vs. falsiness) is used, but an iterator can be provided
|
||||
* that computes a boolean representation of the elements.
|
||||
*
|
||||
* Using `partition` is more efficient than using [[Enumerable#findAll]] and
|
||||
* then using [[Enumerable#reject]] because the enumeration is only processed
|
||||
* once.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* ['hello', null, 42, false, true, , 17].partition();
|
||||
* // -> [['hello', 42, true, 17], [null, false, undefined]]
|
||||
*
|
||||
* $R(1, 10).partition(function(n) {
|
||||
* return 0 == n % 2;
|
||||
* });
|
||||
* // -> [[2, 4, 6, 8, 10], [1, 3, 5, 7, 9]]
|
||||
**/
|
||||
function partition(iterator, context) {
|
||||
iterator = iterator || Prototype.K;
|
||||
var trues = [], falses = [];
|
||||
this.each(function(value, index) {
|
||||
(iterator.call(context, value, index) ?
|
||||
trues : falses).push(value);
|
||||
});
|
||||
return [trues, falses];
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#pluck(property) -> Array
|
||||
* - property (String): The name of the property to fetch.
|
||||
*
|
||||
* Pre-baked implementation for a common use-case of [[Enumerable#collect]]
|
||||
* and [[Enumerable#each]]: fetching the same property for all of the
|
||||
* elements. Returns an array of the property values.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* ['hello', 'world', 'this', 'is', 'nice'].pluck('length');
|
||||
* // -> [5, 5, 4, 2, 4]
|
||||
**/
|
||||
function pluck(property) {
|
||||
var results = [];
|
||||
this.each(function(value) {
|
||||
results.push(value[property]);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#reject(iterator[, context]) -> Array
|
||||
* - iterator (Function): An iterator function to use to test the elements.
|
||||
* - context (Object): An optional object to use as `this` within
|
||||
* calls to the iterator.
|
||||
*
|
||||
* Returns all the elements for which the iterator returns a falsy value.
|
||||
* For the opposite operation, see [[Enumerable#findAll]].
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* [1, "two", 3, "four", 5].reject(Object.isString);
|
||||
* // -> [1, 3, 5]
|
||||
**/
|
||||
function reject(iterator, context) {
|
||||
var results = [];
|
||||
this.each(function(value, index) {
|
||||
if (!iterator.call(context, value, index))
|
||||
results.push(value);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#sortBy(iterator[, context]) -> Array
|
||||
* - iterator (Function): The function to use to compute the criterion for
|
||||
* each element in the enumeration.
|
||||
* - context (Object): An optional object to use as `this` within
|
||||
* calls to the iterator.
|
||||
*
|
||||
* Creates a custom-sorted array of the elements based on the criteria
|
||||
* computed, for each element, by the iterator. Computed criteria must have
|
||||
* well-defined ordering semantics (i.e. the `<` operator must exist between
|
||||
* any two criteria).
|
||||
*
|
||||
* `sortBy` does not guarantee a *stable* sort; adjacent equivalent elements
|
||||
* may be swapped.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* ['hello', 'world', 'this', 'is', 'nice'].sortBy(function(s) {
|
||||
* return s.length;
|
||||
* });
|
||||
* // -> ['is', 'nice', 'this', 'world', 'hello']
|
||||
**/
|
||||
function sortBy(iterator, context) {
|
||||
return this.map(function(value, index) {
|
||||
return {
|
||||
value: value,
|
||||
criteria: iterator.call(context, value, index)
|
||||
};
|
||||
}).sort(function(left, right) {
|
||||
var a = left.criteria, b = right.criteria;
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
}).pluck('value');
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#toArray() -> Array
|
||||
*
|
||||
* Returns an Array containing the elements of the enumeration.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* $R(1, 5).toArray();
|
||||
* // -> [1, 2, 3, 4, 5]
|
||||
*
|
||||
* $H({ name: 'Sunny', age: 20 }).toArray();
|
||||
* // -> [['name', 'Sunny'], ['age', 20]]
|
||||
**/
|
||||
function toArray() {
|
||||
return this.map();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#zip(sequence...[, iterator = Prototype.K]) -> Array
|
||||
* - sequence (Object): A sequence to zip with this enumerable (there can
|
||||
* be several of these if desired).
|
||||
* - iterator (Function): Optional function to use to transform the tuples
|
||||
* once generated; this is always the last argument provided.
|
||||
*
|
||||
* Zips together (think of the zipper on a pair of trousers) 2+ sequences,
|
||||
* returning a new array of tuples. Each tuple is an array containing one
|
||||
* value per original sequence. Tuples can be transformed to something else
|
||||
* by applying the optional `iterator` on them.
|
||||
*
|
||||
* If supplied, `iterator` is called with each tuple as its only argument
|
||||
* and should return the value to use in place of that tuple.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* var firstNames = ['Jane', 'Nitin', 'Guy'];
|
||||
* var lastNames = ['Doe', 'Patel', 'Forcier'];
|
||||
* var ages = [23, 41, 17];
|
||||
*
|
||||
* firstNames.zip(lastNames);
|
||||
* // -> [['Jane', 'Doe'], ['Nitin', 'Patel'], ['Guy', 'Forcier']]
|
||||
*
|
||||
* firstNames.zip(lastNames, ages);
|
||||
* // -> [['Jane', 'Doe', 23], ['Nitin', 'Patel', 41], ['Guy', 'Forcier', 17]]
|
||||
*
|
||||
* firstNames.zip(lastNames, ages, function(tuple) {
|
||||
* return tuple[0] + ' ' + tuple[1] + ' is ' + tuple[2];
|
||||
* });
|
||||
* // -> ['Jane Doe is 23', 'Nitin Patel is 41', 'Guy Forcier is 17']
|
||||
**/
|
||||
function zip() {
|
||||
var iterator = Prototype.K, args = $A(arguments);
|
||||
if (Object.isFunction(args.last()))
|
||||
iterator = args.pop();
|
||||
|
||||
var collections = [this].concat(args).map($A);
|
||||
return this.map(function(value, index) {
|
||||
return iterator(collections.pluck(index));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#size() -> Number
|
||||
*
|
||||
* Returns the size of the enumeration.
|
||||
**/
|
||||
function size() {
|
||||
return this.toArray().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerable#inspect() -> String
|
||||
*
|
||||
* Returns the debug-oriented string representation of the object.
|
||||
**/
|
||||
function inspect() {
|
||||
return '#<Enumerable:' + this.toArray().inspect() + '>';
|
||||
}
|
||||
|
||||
/** alias of: Enumerable#collect
|
||||
* Enumerable#map([iterator = Prototype.K[, context]]) -> Array
|
||||
**/
|
||||
|
||||
/** alias of: Enumerable#any
|
||||
* Enumerable#some([iterator = Prototype.K[, context]]) -> Boolean
|
||||
**/
|
||||
|
||||
/** alias of: Enumerable#all
|
||||
* Enumerable#every([iterator = Prototype.K[, context]]) -> Boolean
|
||||
**/
|
||||
|
||||
/** alias of: Enumerable#findAll
|
||||
* Enumerable#select(iterator[, context]) -> Array
|
||||
**/
|
||||
|
||||
/** alias of: Enumerable#findAll
|
||||
* Enumerable#filter(iterator[, context]) -> Array
|
||||
**/
|
||||
|
||||
/** alias of: Enumerable#include
|
||||
* Enumerable#member(object) -> Boolean
|
||||
**/
|
||||
|
||||
/** alias of: Enumerable#toArray
|
||||
* Enumerable#entries() -> Array
|
||||
**/
|
||||
|
||||
/** alias of: Enumerable#detect
|
||||
* Enumerable#find(iterator[, context]) -> firstElement | undefined
|
||||
**/
|
||||
|
||||
return {
|
||||
each: each,
|
||||
eachSlice: eachSlice,
|
||||
all: all,
|
||||
every: all,
|
||||
any: any,
|
||||
some: any,
|
||||
collect: collect,
|
||||
map: collect,
|
||||
detect: detect,
|
||||
findAll: findAll,
|
||||
select: findAll,
|
||||
filter: findAll,
|
||||
grep: grep,
|
||||
include: include,
|
||||
member: include,
|
||||
inGroupsOf: inGroupsOf,
|
||||
inject: inject,
|
||||
invoke: invoke,
|
||||
max: max,
|
||||
min: min,
|
||||
partition: partition,
|
||||
pluck: pluck,
|
||||
reject: reject,
|
||||
sortBy: sortBy,
|
||||
toArray: toArray,
|
||||
entries: toArray,
|
||||
zip: zip,
|
||||
size: size,
|
||||
inspect: inspect,
|
||||
find: detect
|
||||
};
|
||||
})();
|
|
@ -1,391 +0,0 @@
|
|||
/** section: Language
|
||||
* class Function
|
||||
*
|
||||
* Extensions to the built-in `Function` object.
|
||||
**/
|
||||
Object.extend(Function.prototype, (function() {
|
||||
var slice = Array.prototype.slice;
|
||||
|
||||
function update(array, args) {
|
||||
var arrayLength = array.length, length = args.length;
|
||||
while (length--) array[arrayLength + length] = args[length];
|
||||
return array;
|
||||
}
|
||||
|
||||
function merge(array, args) {
|
||||
array = slice.call(array, 0);
|
||||
return update(array, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function#argumentNames() -> Array
|
||||
*
|
||||
* Reads the argument names as stated in the function definition and returns
|
||||
* the values as an array of strings (or an empty array if the function is
|
||||
* defined without parameters).
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* function fn(foo, bar) {
|
||||
* return foo + bar;
|
||||
* }
|
||||
* fn.argumentNames();
|
||||
* //-> ['foo', 'bar']
|
||||
*
|
||||
* Prototype.emptyFunction.argumentNames();
|
||||
* //-> []
|
||||
**/
|
||||
function argumentNames() {
|
||||
var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
|
||||
.replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
|
||||
.replace(/\s+/g, '').split(',');
|
||||
return names.length == 1 && !names[0] ? [] : names;
|
||||
}
|
||||
|
||||
/** related to: Function#bindAsEventListener
|
||||
* Function#bind(context[, args...]) -> Function
|
||||
* - context (Object): The object to bind to.
|
||||
* - args (?): Optional additional arguments to curry for the function.
|
||||
*
|
||||
* Binds this function to the given `context` by wrapping it in another
|
||||
* function and returning the wrapper. Whenever the resulting "bound"
|
||||
* function is called, it will call the original ensuring that `this` is set
|
||||
* to `context`. Also optionally curries arguments for the function.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* A typical use of `Function#bind` is to ensure that a callback (event
|
||||
* handler, etc.) that is an object method gets called with the correct
|
||||
* object as its context (`this` value):
|
||||
*
|
||||
* var AlertOnClick = Class.create({
|
||||
* initialize: function(msg) {
|
||||
* this.msg = msg;
|
||||
* },
|
||||
* handleClick: function(event) {
|
||||
* event.stop();
|
||||
* alert(this.msg);
|
||||
* }
|
||||
* });
|
||||
* var myalert = new AlertOnClick("Clicked!");
|
||||
* $('foo').observe('click', myalert.handleClick); // <= WRONG
|
||||
* // -> If 'foo' is clicked, the alert will be blank; "this" is wrong
|
||||
* $('bar').observe('click', myalert.handleClick.bind(myalert)); // <= RIGHT
|
||||
* // -> If 'bar' is clicked, the alert will be "Clicked!"
|
||||
*
|
||||
* `bind` can also *curry* (burn in) arguments for the function if you
|
||||
* provide them after the `context` argument:
|
||||
*
|
||||
* var Averager = Class.create({
|
||||
* initialize: function() {
|
||||
* this.count = 0;
|
||||
* this.total = 0;
|
||||
* },
|
||||
* add: function(addend) {
|
||||
* ++this.count;
|
||||
* this.total += addend;
|
||||
* },
|
||||
* getAverage: function() {
|
||||
* return this.count == 0 ? NaN : this.total / this.count;
|
||||
* }
|
||||
* });
|
||||
* var a = new Averager();
|
||||
* var b = new Averager();
|
||||
* var aAdd5 = a.add.bind(a, 5); // Bind to a, curry 5
|
||||
* var aAdd10 = a.add.bind(a, 10); // Bind to a, curry 10
|
||||
* var bAdd20 = b.add.bind(b, 20); // Bind to b, curry 20
|
||||
* aAdd5();
|
||||
* aAdd10();
|
||||
* bAdd20();
|
||||
* bAdd20();
|
||||
* alert(a.getAverage());
|
||||
* // -> Alerts "7.5" (average of [5, 10])
|
||||
* alert(b.getAverage());
|
||||
* // -> Alerts "20" (average of [20, 20])
|
||||
*
|
||||
* (To curry without binding, see [[Function#curry]].)
|
||||
**/
|
||||
function bind(context) {
|
||||
if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
|
||||
var __method = this, args = slice.call(arguments, 1);
|
||||
return function() {
|
||||
var a = merge(args, arguments);
|
||||
return __method.apply(context, a);
|
||||
}
|
||||
}
|
||||
|
||||
/** related to: Function#bind
|
||||
* Function#bindAsEventListener(context[, args...]) -> Function
|
||||
* - context (Object): The object to bind to.
|
||||
* - args (?): Optional arguments to curry after the event argument.
|
||||
*
|
||||
* An event-specific variant of [[Function#bind]] which ensures the function
|
||||
* will recieve the current event object as the first argument when
|
||||
* executing.
|
||||
*
|
||||
* It is not necessary to use `bindAsEventListener` for all bound event
|
||||
* handlers; [[Function#bind]] works well for the vast majority of cases.
|
||||
* `bindAsEventListener` is only needed when:
|
||||
*
|
||||
* - Using old-style DOM0 handlers rather than handlers hooked up via
|
||||
* [[Event.observe]], because `bindAsEventListener` gets the event object
|
||||
* from the right place (even on MSIE). (If you're using `Event.observe`,
|
||||
* that's already handled.)
|
||||
* - You want to bind an event handler and curry additional arguments but
|
||||
* have those arguments appear after, rather than before, the event object.
|
||||
* This mostly happens if the number of arguments will vary, and so you
|
||||
* want to know the event object is the first argument.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* var ContentUpdater = Class.create({
|
||||
* initialize: function(initialData) {
|
||||
* this.data = Object.extend({}, initialData);
|
||||
* },
|
||||
* // On an event, update the content in the elements whose
|
||||
* // IDs are passed as arguments from our data
|
||||
* updateTheseHandler: function(event) {
|
||||
* var argIndex, id, element;
|
||||
* event.stop();
|
||||
* for (argIndex = 1; argIndex < arguments.length; ++argIndex) {
|
||||
* id = arguments[argIndex];
|
||||
* element = $(id);
|
||||
* if (element) {
|
||||
* element.update(String(this.data[id]).escapeHTML());
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
* var cu = new ContentUpdater({
|
||||
* dispName: 'Joe Bloggs',
|
||||
* dispTitle: 'Manager <provisional>',
|
||||
* dispAge: 47
|
||||
* });
|
||||
* // Using bindAsEventListener because of the variable arg lists:
|
||||
* $('btnUpdateName').observe('click',
|
||||
* cu.updateTheseHandler.bindAsEventListener(cu, 'dispName')
|
||||
* );
|
||||
* $('btnUpdateAll').observe('click',
|
||||
* cu.updateTheseHandler.bindAsEventListener(cu, 'dispName', 'dispTitle', 'dispAge')
|
||||
* );
|
||||
**/
|
||||
function bindAsEventListener(context) {
|
||||
var __method = this, args = slice.call(arguments, 1);
|
||||
return function(event) {
|
||||
var a = update([event || window.event], args);
|
||||
return __method.apply(context, a);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function#curry(args...) -> Function
|
||||
* - args (?): The arguments to curry.
|
||||
*
|
||||
* *Curries* (burns in) arguments to a function, returning a new function
|
||||
* that when called with call the original passing in the curried arguments
|
||||
* (along with any new ones):
|
||||
*
|
||||
* function showArguments() {
|
||||
* alert($A(arguments).join(', '));
|
||||
* }
|
||||
* showArguments(1, 2,, 3);
|
||||
* // -> alerts "1, 2, 3"
|
||||
*
|
||||
* var f = showArguments.curry(1, 2, 3);
|
||||
* f('a', 'b');
|
||||
* // -> alerts "1, 2, 3, a, b"
|
||||
*
|
||||
* `Function#curry` works just like [[Function#bind]] without the initial
|
||||
* context argument. Use `bind` if you need to curry arguments _and_ set
|
||||
* context at the same time.
|
||||
*
|
||||
* The name "curry" comes from [mathematics](http://en.wikipedia.org/wiki/Currying).
|
||||
**/
|
||||
function curry() {
|
||||
if (!arguments.length) return this;
|
||||
var __method = this, args = slice.call(arguments, 0);
|
||||
return function() {
|
||||
var a = merge(args, arguments);
|
||||
return __method.apply(this, a);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function#delay(timeout[, args...]) -> Number
|
||||
* - timeout (Number): The time, in seconds, to wait before calling the
|
||||
* function.
|
||||
* - args (?): Optional arguments to pass to the function when calling it.
|
||||
*
|
||||
* Schedules the function to run after the specified amount of time, passing
|
||||
* any arguments given.
|
||||
*
|
||||
* Behaves much like `window.setTimeout`, but the timeout is in seconds
|
||||
* rather than milliseconds. Returns an integer ID that can be used to
|
||||
* clear the timeout with `window.clearTimeout` before it runs.
|
||||
*
|
||||
* To schedule a function to run as soon as the interpreter is idle, use
|
||||
* [[Function#defer]].
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* function showMsg(msg) {
|
||||
* alert(msg);
|
||||
* }
|
||||
* showMsg.delay(0.1, "Hi there!");
|
||||
* // -> Waits a 10th of a second, then alerts "Hi there!"
|
||||
**/
|
||||
function delay(timeout) {
|
||||
var __method = this, args = slice.call(arguments, 1);
|
||||
timeout = timeout * 1000;
|
||||
return window.setTimeout(function() {
|
||||
return __method.apply(__method, args);
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function#defer(args...) -> Number
|
||||
* - args (?): Optional arguments to pass into the function.
|
||||
*
|
||||
* Schedules the function to run as soon as the interpreter is idle.
|
||||
*
|
||||
* A "deferred" function will not run immediately; rather, it will run as soon
|
||||
* as the interpreter's call stack is empty.
|
||||
*
|
||||
* Behaves much like `window.setTimeout` with a delay set to `0`. Returns an
|
||||
* ID that can be used to clear the timeout with `window.clearTimeout` before
|
||||
* it runs.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* function showMsg(msg) {
|
||||
* alert(msg);
|
||||
* }
|
||||
*
|
||||
* showMsg("One");
|
||||
* showMsg.defer("Two");
|
||||
* showMsg("Three");
|
||||
* // Alerts "One", then "Three", then (after a brief pause) "Two"
|
||||
* // Note that "Three" happens before "Two"
|
||||
**/
|
||||
function defer() {
|
||||
var args = update([0.01], arguments);
|
||||
return this.delay.apply(this, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function#wrap(wrapper) -> Function
|
||||
* - wrapper (Function): The function to use as a wrapper.
|
||||
*
|
||||
* Returns a function "wrapped" around the original function.
|
||||
*
|
||||
* `Function#wrap` distills the essence of aspect-oriented programming into
|
||||
* a single method, letting you easily build on existing functions by
|
||||
* specifying before and after behavior, transforming the return value, or
|
||||
* even preventing the original function from being called.
|
||||
*
|
||||
* The wraper function is called with this signature:
|
||||
*
|
||||
* function wrapper(callOriginal[, args...])
|
||||
*
|
||||
* ...where `callOriginal` is a function that can be used to call the
|
||||
* original (wrapped) function (or not, as appropriate). (`callOriginal` is
|
||||
* not a direct reference to the original function, there's a layer of
|
||||
* indirection in-between that sets up the proper context \[`this` value\] for
|
||||
* it.)
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* // Wrap String#capitalize so it accepts an additional argument
|
||||
* String.prototype.capitalize = String.prototype.capitalize.wrap(
|
||||
* function(callOriginal, eachWord) {
|
||||
* if (eachWord && this.include(" ")) {
|
||||
* // capitalize each word in the string
|
||||
* return this.split(" ").invoke("capitalize").join(" ");
|
||||
* } else {
|
||||
* // proceed using the original function
|
||||
* return callOriginal();
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* "hello world".capitalize();
|
||||
* // -> "Hello world" (only the 'H' is capitalized)
|
||||
* "hello world".capitalize(true);
|
||||
* // -> "Hello World" (both 'H' and 'W' are capitalized)
|
||||
**/
|
||||
function wrap(wrapper) {
|
||||
var __method = this;
|
||||
return function() {
|
||||
var a = update([__method.bind(this)], arguments);
|
||||
return wrapper.apply(this, a);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function#methodize() -> Function
|
||||
*
|
||||
* Wraps the function inside another function that, when called, pushes
|
||||
* `this` to the original function as the first argument (with any further
|
||||
* arguments following it).
|
||||
*
|
||||
* The `methodize` method transforms the original function that has an
|
||||
* explicit first argument to a function that passes `this` (the current
|
||||
* context) as an implicit first argument at call time. It is useful when we
|
||||
* want to transform a function that takes an object to a method of that
|
||||
* object or its prototype, shortening its signature by one argument.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* // A function that sets a name on a target object
|
||||
* function setName(target, name) {
|
||||
* target.name = name;
|
||||
* }
|
||||
*
|
||||
* // Use it
|
||||
* obj = {};
|
||||
* setName(obj, 'Fred');
|
||||
* obj.name;
|
||||
* // -> "Fred"
|
||||
*
|
||||
* // Make it a method of the object
|
||||
* obj.setName = setName.methodize();
|
||||
*
|
||||
* // Use the method instead
|
||||
* obj.setName('Barney');
|
||||
* obj.name;
|
||||
* // -> "Barney"
|
||||
*
|
||||
* The example above is quite simplistic. It's more useful to copy methodized
|
||||
* functions to object prototypes so that new methods are immediately shared
|
||||
* among instances. In the Prototype library, `methodize` is used in various
|
||||
* places such as the DOM module, so that (for instance) you can hide an
|
||||
* element either by calling the static version of `Element.hide` and passing in
|
||||
* an element reference or ID, like so:
|
||||
*
|
||||
* Element.hide('myElement');
|
||||
*
|
||||
* ...or if you already have an element reference, just calling the
|
||||
* methodized form instead:
|
||||
*
|
||||
* myElement.hide();
|
||||
**/
|
||||
function methodize() {
|
||||
if (this._methodized) return this._methodized;
|
||||
var __method = this;
|
||||
return this._methodized = function() {
|
||||
var a = update([this], arguments);
|
||||
return __method.apply(null, a);
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
argumentNames: argumentNames,
|
||||
bind: bind,
|
||||
bindAsEventListener: bindAsEventListener,
|
||||
curry: curry,
|
||||
delay: delay,
|
||||
defer: defer,
|
||||
wrap: wrap,
|
||||
methodize: methodize
|
||||
}
|
||||
})());
|
||||
|
379
src/lang/hash.js
379
src/lang/hash.js
|
@ -1,379 +0,0 @@
|
|||
/** section: Language, related to: Hash
|
||||
* $H([object]) -> Hash
|
||||
*
|
||||
* Creates a `Hash`. This is purely a convenience wrapper around the Hash
|
||||
* constructor, it does not do anything other than pass any argument it's
|
||||
* given into the Hash constructor and return the result.
|
||||
**/
|
||||
function $H(object) {
|
||||
return new Hash(object);
|
||||
};
|
||||
|
||||
/** section: Language
|
||||
* class Hash
|
||||
* includes Enumerable
|
||||
*
|
||||
* A set of key/value pairs.
|
||||
*
|
||||
* `Hash` can be thought of as an associative array, binding unique keys to
|
||||
* values (which are not necessarily unique), though it can not guarantee
|
||||
* consistent order its elements when iterating. Because of the nature of
|
||||
* JavaScript, every object is in fact a hash; but `Hash` adds a number of
|
||||
* methods that let you enumerate keys and values, iterate over key/value
|
||||
* pairs, merge two hashes together, and much more.
|
||||
*
|
||||
* <h5>Creating a hash</h5>
|
||||
*
|
||||
* You can create a Hash either via `new Hash()` or the convenience alias
|
||||
* `$H()`; there is **no** difference between them. In either case, you may
|
||||
* optionally pass in an object to seed the `Hash`. If you pass in a `Hash`,
|
||||
* it will be cloned.
|
||||
*
|
||||
**/
|
||||
var Hash = Class.create(Enumerable, (function() {
|
||||
/**
|
||||
* new Hash([object])
|
||||
*
|
||||
* Creates a new `Hash`. If `object` is given, the new hash will be populated
|
||||
* with all the object's properties. See [[$H]].
|
||||
**/
|
||||
function initialize(object) {
|
||||
this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
|
||||
}
|
||||
|
||||
// Docs for #each even though technically it's implemented by Enumerable
|
||||
/**
|
||||
* Hash#each(iterator[, context]) -> Hash
|
||||
* - iterator (Function): A function that expects each item in the `Hash`
|
||||
* as the first argument and a numerical index as the second.
|
||||
* - context (Object): The scope in which to call `iterator`. Determines what
|
||||
* `this` means inside `iterator`.
|
||||
*
|
||||
* Iterates over the name/value pairs in the hash.
|
||||
*
|
||||
* This is actually just the [[Enumerable#each #each]] method from the
|
||||
* mixed-in [[Enumerable]] module. It is documented here to describe the
|
||||
* structure of the elements passed to the iterator and the order of
|
||||
* iteration.
|
||||
*
|
||||
* The iterator's first argument (the "item") is an object with two
|
||||
* properties:
|
||||
*
|
||||
* - `key`: the key name as a `String`
|
||||
* - `value`: the corresponding value (which may be `undefined`)
|
||||
*
|
||||
* The order of iteration is implementation-dependent, as it relies on
|
||||
* the order of the native `for..in` loop. Although most modern
|
||||
* implementations exhibit *ordered* behavior, this is not standardized and
|
||||
* may not always be the case, and so cannot be relied upon.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* var h = $H({version: 1.6, author: 'The Core Team'});
|
||||
*
|
||||
* h.each(function(pair) {
|
||||
* alert(pair.key + ' = "' + pair.value + '"');
|
||||
* });
|
||||
* // Alerts 'version = "1.6"' and 'author = "The Core Team"'
|
||||
* // -or-
|
||||
* // Alerts 'author = "The Core Team"' and 'version = "1.6"'
|
||||
**/
|
||||
|
||||
// Our _internal_ each
|
||||
function _each(iterator) {
|
||||
for (var key in this._object) {
|
||||
var value = this._object[key], pair = [key, value];
|
||||
pair.key = key;
|
||||
pair.value = value;
|
||||
iterator(pair);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash#set(key, value) -> value
|
||||
* - key (String): The key to use for this value.
|
||||
* - value (?): The value to use for this key.
|
||||
*
|
||||
* Stores `value` in the hash using the key `key` and returns `value`.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* var h = $H();
|
||||
* h.keys();
|
||||
* // -> [] (initially empty)
|
||||
* h.set('a', 'apple');
|
||||
* // -> "apple"
|
||||
* h.keys();
|
||||
* // -> ["a"] (has the new entry)
|
||||
* h.get('a');
|
||||
* // -> "apple"
|
||||
**/
|
||||
function set(key, value) {
|
||||
return this._object[key] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash#get(key) -> value
|
||||
*
|
||||
* Returns the stored value for the given `key`.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* var h = new Hash({a: 'apple', b: 'banana', c: 'coconut'});
|
||||
* h.get('a');
|
||||
* // -> 'apple'
|
||||
**/
|
||||
function get(key) {
|
||||
// simulating poorly supported hasOwnProperty
|
||||
if (this._object[key] !== Object.prototype[key])
|
||||
return this._object[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash#unset(key) -> value
|
||||
*
|
||||
* Deletes the stored pair for the given `key` from the hash and returns its
|
||||
* value.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* var h = new Hash({a: 'apple', b: 'banana', c: 'coconut'});
|
||||
* h.keys();
|
||||
* // -> ["a", "b", "c"]
|
||||
* h.unset('a');
|
||||
* // -> 'apple'
|
||||
* h.keys();
|
||||
* // -> ["b", "c"] ("a" is no longer in the hash)
|
||||
**/
|
||||
function unset(key) {
|
||||
var value = this._object[key];
|
||||
delete this._object[key];
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash#toObject() -> Object
|
||||
*
|
||||
* Returns a cloned, vanilla object whose properties (and property values)
|
||||
* match the keys (and values) from the hash.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* var h = new Hash({ a: 'apple', b: 'banana', c: 'coconut' });
|
||||
* var obj = h.toObject();
|
||||
* obj.a;
|
||||
* // -> "apple"
|
||||
**/
|
||||
function toObject() {
|
||||
return Object.clone(this._object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash#keys() -> [String...]
|
||||
*
|
||||
* Provides an Array containing the keys for items stored in the hash.
|
||||
*
|
||||
* The order of the keys is not guaranteed.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* var h = $H({one: "uno", two: "due", three: "tre"});
|
||||
* h.keys();
|
||||
* // -> ["one", "three", "two"] (these may be in any order)
|
||||
**/
|
||||
function keys() {
|
||||
return this.pluck('key');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash#values() -> Array
|
||||
*
|
||||
* Collects the values of the hash and returns them in an array.
|
||||
*
|
||||
* The order of the values is not guaranteed.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* var h = $H({one: "uno", two: "due", three: "tre"});
|
||||
* h.values();
|
||||
* // -> ["uno", "tre", "due"] (these may be in any order)
|
||||
**/
|
||||
function values() {
|
||||
return this.pluck('value');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash#index(value) -> String
|
||||
*
|
||||
* Returns the first key in the hash whose value matches `value`.
|
||||
* Returns `false` if there is no such key.
|
||||
**/
|
||||
function index(value) {
|
||||
var match = this.detect(function(pair) {
|
||||
return pair.value === value;
|
||||
});
|
||||
return match && match.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash#merge(object) -> Hash
|
||||
* - object (Object | Hash): The object to merge with this hash to produce
|
||||
* the resulting hash.
|
||||
*
|
||||
* Returns a new `Hash` instance with `object`'s key/value pairs merged in;
|
||||
* this hash remains unchanged.
|
||||
*
|
||||
* To modify the original hash in place, use [[Hash#update]].
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* var h = $H({one: "uno", two: "due"});
|
||||
* var h2 = h.merge({three: "tre"});
|
||||
* h.keys();
|
||||
* // -> ["one", "two"] (unchanged)
|
||||
* h2.keys();
|
||||
* // -> ["one", "two", "three"] (has merged contents)
|
||||
**/
|
||||
function merge(object) {
|
||||
return this.clone().update(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash#update(object) -> Hash
|
||||
* - object (Object | Hash): The object to merge with this hash to produce
|
||||
* the resulting hash.
|
||||
*
|
||||
* Updates a hash *in place* with the key/value pairs of `object`, returns
|
||||
* the hash.
|
||||
*
|
||||
* `update` modifies the hash. To get a new hash instead, use
|
||||
* [[Hash#merge]].
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* var h = $H({one: "uno", two: "due"});
|
||||
* h.update({three: "tre"});
|
||||
* // -> h (a reference to the original hash)
|
||||
* h.keys();
|
||||
* // -> ["one", "two", "three"] (has merged contents)
|
||||
**/
|
||||
function update(object) {
|
||||
return new Hash(object).inject(this, function(result, pair) {
|
||||
result.set(pair.key, pair.value);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
// Private. No PDoc necessary.
|
||||
function toQueryPair(key, value) {
|
||||
if (Object.isUndefined(value)) return key;
|
||||
return key + '=' + encodeURIComponent(String.interpret(value));
|
||||
}
|
||||
|
||||
/** related to: String#toQueryParams
|
||||
* Hash#toQueryString() -> String
|
||||
*
|
||||
* Returns a URL-encoded string containing the hash's contents as query
|
||||
* parameters according to the following rules:
|
||||
*
|
||||
* - An undefined value results a parameter with no value portion at all
|
||||
* (simply the key name, no equal sign).
|
||||
* - A null value results a parameter with a blank value (the key followed
|
||||
* by an equal sign and nothing else).
|
||||
* - A boolean value results a parameter with the value "true" or "false".
|
||||
* - An Array value results in a parameter for each array element, in
|
||||
* array order, each using the same key.
|
||||
* - All keys and values are URI-encoded using JavaScript's native
|
||||
* `encodeURIComponent` function.
|
||||
*
|
||||
* The order of pairs in the string is not guaranteed, other than the order
|
||||
* of array values described above.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* $H({action: 'ship',
|
||||
* order_id: 123,
|
||||
* fees: ['f1', 'f2']
|
||||
* }).toQueryString();
|
||||
* // -> "action=ship&order_id=123&fees=f1&fees=f2"
|
||||
*
|
||||
* $H({comment: '',
|
||||
* 'key with spaces': true,
|
||||
* related_order: undefined,
|
||||
* contents: null,
|
||||
* 'label': 'a demo'
|
||||
* }).toQueryString();
|
||||
* // -> "comment=&key%20with%20spaces=true&related_order&contents=&label=a%20demo"
|
||||
*
|
||||
* // an empty hash is an empty query string:
|
||||
* $H().toQueryString();
|
||||
* // -> ""
|
||||
**/
|
||||
function toQueryString() {
|
||||
return this.inject([], function(results, pair) {
|
||||
var key = encodeURIComponent(pair.key), values = pair.value;
|
||||
|
||||
if (values && typeof values == 'object') {
|
||||
if (Object.isArray(values))
|
||||
return results.concat(values.map(toQueryPair.curry(key)));
|
||||
} else results.push(toQueryPair(key, values));
|
||||
return results;
|
||||
}).join('&');
|
||||
}
|
||||
|
||||
/** related to: Object.inspect
|
||||
* Hash#inspect() -> String
|
||||
*
|
||||
* Returns the debug-oriented string representation of the Hash.
|
||||
**/
|
||||
function inspect() {
|
||||
return '#<Hash:{' + this.map(function(pair) {
|
||||
return pair.map(Object.inspect).join(': ');
|
||||
}).join(', ') + '}>';
|
||||
}
|
||||
|
||||
/** related to: Object.toJSON
|
||||
* Hash#toJSON() -> String
|
||||
*
|
||||
* Returns a JSON string containing the keys and values in this hash.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* var h = $H({'a': 'apple', 'b': 23, 'c': false});
|
||||
* h.toJSON();
|
||||
* // -> {"a": "apple", "b": 23, "c": false}
|
||||
**/
|
||||
function toJSON() {
|
||||
return Object.toJSON(this.toObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash#clone() -> Hash
|
||||
*
|
||||
* Returns a clone of this Hash.
|
||||
**/
|
||||
function clone() {
|
||||
return new Hash(this);
|
||||
}
|
||||
|
||||
return {
|
||||
initialize: initialize,
|
||||
_each: _each,
|
||||
set: set,
|
||||
get: get,
|
||||
unset: unset,
|
||||
toObject: toObject,
|
||||
toTemplateReplacements: toObject,
|
||||
keys: keys,
|
||||
values: values,
|
||||
index: index,
|
||||
merge: merge,
|
||||
update: update,
|
||||
toQueryString: toQueryString,
|
||||
inspect: inspect,
|
||||
toJSON: toJSON,
|
||||
clone: clone
|
||||
};
|
||||
})());
|
||||
|
||||
Hash.from = $H;
|
|
@ -1,168 +0,0 @@
|
|||
/** section: Language
|
||||
* class Number
|
||||
*
|
||||
* Extensions to the built-in `Number` object.
|
||||
*
|
||||
* Prototype extends native JavaScript numbers in order to provide:
|
||||
*
|
||||
* * [[ObjectRange]] compatibility, through [[Number#succ]].
|
||||
* * Numerical loops with [[Number#times]].
|
||||
* * Simple utility methods such as [[Number#toColorPart]] and
|
||||
* [[Number#toPaddedString]].
|
||||
* * Instance-method aliases of many functions in the `Math` namespace.
|
||||
*
|
||||
**/
|
||||
Object.extend(Number.prototype, (function() {
|
||||
/**
|
||||
* Number#toColorPart() -> String
|
||||
*
|
||||
* Produces a 2-digit hexadecimal representation of the number
|
||||
* (which is therefore assumed to be in the \[0..255\] range, inclusive).
|
||||
* Useful for composing CSS color strings.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* 10.toColorPart()
|
||||
* // -> "0a"
|
||||
**/
|
||||
function toColorPart() {
|
||||
return this.toPaddedString(2, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Number#succ() -> Number
|
||||
*
|
||||
* Returns the successor of the current Number, as defined by current + 1.
|
||||
* Used to make numbers compatible with ObjectRange.
|
||||
**/
|
||||
function succ() {
|
||||
return this + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number#times(iterator[,context]) -> Number
|
||||
* - iterator (Function): An iterator function to call.
|
||||
* - context (Object): An optional context (`this` value) to use when
|
||||
* calling `iterator`.
|
||||
*
|
||||
* Calls `iterator` the specified number of times, passing in a number as
|
||||
* the first parameter. The number will be 0 on first call, 1 on second
|
||||
* call, etc. `times` returns the number instance it was called on.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* (3).times(alert);
|
||||
* // -> Alerts "0", then "1", then "2"; returns 3
|
||||
*
|
||||
* var obj = {count: 0, total: 0};
|
||||
* function add(addend) {
|
||||
* ++this.count;
|
||||
* this.total += addend;
|
||||
* }
|
||||
* (4).times(add, obj);
|
||||
* // -> 4
|
||||
* obj.count;
|
||||
* // -> 4
|
||||
* obj.total;
|
||||
* // -> 6 (e.g., 0 + 1 + 2 + 3)
|
||||
**/
|
||||
function times(iterator, context) {
|
||||
$R(0, this, true).each(iterator, context);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number#toPaddedString(length[, radix]) -> String
|
||||
* - length (Number): The minimum length for the resulting string.
|
||||
* - radix (Number): An optional radix for the string representation,
|
||||
* defaults to 10 (decimal).
|
||||
*
|
||||
* Returns a string representation of the number padded with leading 0s so
|
||||
* that the string's length is at least equal to `length`. Takes an optional
|
||||
* `radix` argument which specifies the base to use for conversion.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* (13).toPaddedString(4);
|
||||
* // -> "0013"
|
||||
*
|
||||
* (13).toPaddedString(2);
|
||||
* // -> "13"
|
||||
*
|
||||
* (13).toPaddedString(1);
|
||||
* // -> "13"
|
||||
*
|
||||
* (13).toPaddedString(4, 16)
|
||||
* // -> "000d"
|
||||
*
|
||||
* (13).toPaddedString(4, 2);
|
||||
* // -> "1101"
|
||||
**/
|
||||
function toPaddedString(length, radix) {
|
||||
var string = this.toString(radix || 10);
|
||||
return '0'.times(length - string.length) + string;
|
||||
}
|
||||
|
||||
/** related to: Object.toJSON
|
||||
* Number#toJSON() -> String
|
||||
*
|
||||
* Returns a JSON string representation of the number.
|
||||
**/
|
||||
function toJSON() {
|
||||
return isFinite(this) ? this.toString() : 'null';
|
||||
}
|
||||
|
||||
/**
|
||||
* Number#abs() -> Number
|
||||
*
|
||||
* Returns the absolute value of the number. Convenience method that simply
|
||||
* calls `Math.abs` on this instance and returns the result.
|
||||
**/
|
||||
function abs() {
|
||||
return Math.abs(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Number#round() -> Number
|
||||
*
|
||||
* Rounds the number to the nearest integer. Convenience method that simply
|
||||
* calls `Math.round` on this instance and returns the result.
|
||||
**/
|
||||
function round() {
|
||||
return Math.round(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Number#ceil() -> Number
|
||||
*
|
||||
* Returns the smallest integer greater than or equal to the number.
|
||||
* Convenience method that simply calls `Math.ceil` on this instance and
|
||||
* returns the result.
|
||||
**/
|
||||
function ceil() {
|
||||
return Math.ceil(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Number#floor() -> Number
|
||||
*
|
||||
* Returns the largest integer less than or equal to the number.
|
||||
* Convenience method that simply calls `Math.floor` on this instance and
|
||||
* returns the result.
|
||||
**/
|
||||
function floor() {
|
||||
return Math.floor(this);
|
||||
}
|
||||
|
||||
return {
|
||||
toColorPart: toColorPart,
|
||||
succ: succ,
|
||||
times: times,
|
||||
toPaddedString: toPaddedString,
|
||||
toJSON: toJSON,
|
||||
abs: abs,
|
||||
round: round,
|
||||
ceil: ceil,
|
||||
floor: floor
|
||||
};
|
||||
})());
|
|
@ -1,296 +0,0 @@
|
|||
/** section: Language
|
||||
* class Object
|
||||
*
|
||||
* Extensions to the built-in `Object` object.
|
||||
*
|
||||
* Because it is dangerous and invasive to augment `Object.prototype` (i.e.,
|
||||
* add instance methods to objects), all these methods are static methods that
|
||||
* take an `Object` as their first parameter.
|
||||
*
|
||||
**/
|
||||
(function() {
|
||||
|
||||
var _toString = Object.prototype.toString;
|
||||
|
||||
/**
|
||||
* Object.extend(destination, source) -> Object
|
||||
* - destination (Object): The object to receive the new properties.
|
||||
* - source (Object): The object whose properties will be duplicated.
|
||||
*
|
||||
* Copies all properties from the source to the destination object. Returns
|
||||
* the destination object.
|
||||
**/
|
||||
function extend(destination, source) {
|
||||
for (var property in source)
|
||||
destination[property] = source[property];
|
||||
return destination;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object.inspect(object) -> String
|
||||
* - object (Object): The item to be inspected.
|
||||
*
|
||||
* Returns the debug-oriented string representation of the object.
|
||||
*
|
||||
* `undefined` and `null` are represented as such.
|
||||
*
|
||||
* Other types are checked for a `inspect` method. If there is one, it is
|
||||
* used; otherwise, it reverts to the `toString` method.
|
||||
*
|
||||
* Prototype provides `inspect` methods for many types, both built-in and
|
||||
* library-defined — among them `String`, `Array`, `Enumerable` and `Hash`.
|
||||
* These attempt to provide useful string representations (from a
|
||||
* developer's standpoint) for their respective types.
|
||||
**/
|
||||
function inspect(object) {
|
||||
try {
|
||||
if (isUndefined(object)) return 'undefined';
|
||||
if (object === null) return 'null';
|
||||
return object.inspect ? object.inspect() : String(object);
|
||||
} catch (e) {
|
||||
if (e instanceof RangeError) return '...';
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Object.toJSON(object) -> String
|
||||
* - object (Object): The object to be serialized.
|
||||
*
|
||||
* Returns a JSON string.
|
||||
*
|
||||
* `undefined` and `function` types have no JSON representation. `boolean`
|
||||
* and `null` are coerced to strings.
|
||||
*
|
||||
* For other types, `Object.toJSON` looks for a `toJSON` method on `object`.
|
||||
* If there is one, it is used; otherwise the object is treated like a
|
||||
* generic `Object`.
|
||||
**/
|
||||
function toJSON(object) {
|
||||
var type = typeof object;
|
||||
switch (type) {
|
||||
case 'undefined':
|
||||
case 'function':
|
||||
case 'unknown': return;
|
||||
case 'boolean': return object.toString();
|
||||
}
|
||||
|
||||
if (object === null) return 'null';
|
||||
if (object.toJSON) return object.toJSON();
|
||||
if (isElement(object)) return;
|
||||
|
||||
var results = [];
|
||||
for (var property in object) {
|
||||
var value = toJSON(object[property]);
|
||||
if (!isUndefined(value))
|
||||
results.push(property.toJSON() + ': ' + value);
|
||||
}
|
||||
|
||||
return '{' + results.join(', ') + '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Object.toQueryString(object) -> String
|
||||
* object (Object): The object whose property/value pairs will be converted.
|
||||
*
|
||||
* Turns an object into its URL-encoded query string representation.
|
||||
*
|
||||
* This is a form of serialization, and is mostly useful to provide complex
|
||||
* parameter sets for stuff such as objects in the Ajax namespace (e.g.
|
||||
* [[Ajax.Request]]).
|
||||
*
|
||||
* Undefined-value pairs will be serialized as if empty-valued. Array-valued
|
||||
* pairs will get serialized with one name/value pair per array element. All
|
||||
* values get URI-encoded using JavaScript's native `encodeURIComponent`
|
||||
* function.
|
||||
*
|
||||
* The order of pairs in the serialized form is not guaranteed (and mostly
|
||||
* irrelevant anyway) — except for array-based parts, which are serialized
|
||||
* in array order.
|
||||
**/
|
||||
function toQueryString(object) {
|
||||
return $H(object).toQueryString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Object.toHTML(object) -> String
|
||||
* - object (Object): The object to convert to HTML.
|
||||
*
|
||||
* Converts the object to its HTML representation.
|
||||
*
|
||||
* Returns the return value of `object`'s `toHTML` method if it exists; else
|
||||
* runs `object` through [[String.interpret]].
|
||||
**/
|
||||
function toHTML(object) {
|
||||
return object && object.toHTML ? object.toHTML() : String.interpret(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Object.keys(object) -> Array
|
||||
* - object (Object): The object to pull keys from.
|
||||
*
|
||||
* Returns an array of the object's property names.
|
||||
*
|
||||
* Note that the order of the resulting array is browser-dependent — it
|
||||
* relies on the `for…in` loop, for which the ECMAScript spec does not
|
||||
* prescribe an enumeration order. Sort the resulting array if you wish to
|
||||
* normalize the order of the object keys.
|
||||
**/
|
||||
function keys(object) {
|
||||
var results = [];
|
||||
for (var property in object)
|
||||
results.push(property);
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object.values(object) -> Array
|
||||
* - object (Object): The object to pull values from.
|
||||
*
|
||||
* Returns an array of the object's values.
|
||||
*
|
||||
* Note that the order of the resulting array is browser-dependent — it
|
||||
* relies on the `for…in` loop, for which the ECMAScript spec does not
|
||||
* prescribe an enumeration order.
|
||||
*
|
||||
* Also, remember that while property _names_ are unique, property _values_
|
||||
* have no such constraint.
|
||||
**/
|
||||
function values(object) {
|
||||
var results = [];
|
||||
for (var property in object)
|
||||
results.push(object[property]);
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object.clone(object) -> Object
|
||||
* - object (Object): The object to clone.
|
||||
*
|
||||
* Creates and returns a shallow duplicate of the passed object by copying
|
||||
* all of the original's key/value pairs onto an empty object.
|
||||
*
|
||||
* Do note that this is a _shallow_ copy, not a _deep_ copy. Nested objects
|
||||
* will retain their references.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* var original = {name: 'primaryColors', values: ['red', 'green', 'blue']};
|
||||
* var copy = Object.clone(original);
|
||||
* original.name;
|
||||
* // -> "primaryColors"
|
||||
* original.values[0];
|
||||
* // -> "red"
|
||||
* copy.name;
|
||||
* // -> "primaryColors"
|
||||
* copy.name = "secondaryColors";
|
||||
* original.name;
|
||||
* // -> "primaryColors"
|
||||
* copy.name;
|
||||
* // -> "secondaryColors"
|
||||
* copy.values[0] = 'magenta';
|
||||
* copy.values[1] = 'cyan';
|
||||
* copy.values[2] = 'yellow';
|
||||
* original.values[0];
|
||||
* // -> "magenta" (it was a shallow copy, so they shared the array)
|
||||
**/
|
||||
function clone(object) {
|
||||
return extend({ }, object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Object.isElement(object) -> Boolean
|
||||
* - object (Object): The object to test.
|
||||
*
|
||||
* Returns `true` if `object` is a DOM node of type 1; `false` otherwise.
|
||||
**/
|
||||
function isElement(object) {
|
||||
return !!(object && object.nodeType == 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Object.isArray(object) -> Boolean
|
||||
* - object (Object): The object to test.
|
||||
*
|
||||
* Returns `true` if `object` is an array; false otherwise.
|
||||
**/
|
||||
function isArray(object) {
|
||||
return _toString.call(object) == "[object Array]";
|
||||
}
|
||||
|
||||
var hasNativeIsArray = (typeof Array.isArray == 'function')
|
||||
&& Array.isArray([]) && !Array.isArray({});
|
||||
|
||||
if (hasNativeIsArray) {
|
||||
isArray = Array.isArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object.isHash(object) -> Boolean
|
||||
* - object (Object): The object to test.
|
||||
*
|
||||
* Returns `true` if `object` is an instance of the [[Hash]] class; `false`
|
||||
* otherwise.
|
||||
**/
|
||||
function isHash(object) {
|
||||
return object instanceof Hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Object.isFunction(object) -> Boolean
|
||||
* - object (Object): The object to test.
|
||||
*
|
||||
* Returns `true` if `object` is of type `function`; `false` otherwise.
|
||||
**/
|
||||
function isFunction(object) {
|
||||
return typeof object === "function";
|
||||
}
|
||||
|
||||
/**
|
||||
* Object.isString(object) -> Boolean
|
||||
* - object (Object): The object to test.
|
||||
*
|
||||
* Returns `true` if `object` is of type `string`; `false` otherwise.
|
||||
**/
|
||||
function isString(object) {
|
||||
return _toString.call(object) == "[object String]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Object.isNumber(object) -> Boolean
|
||||
* - object (Object): The object to test.
|
||||
*
|
||||
* Returns `true` if `object` is of type `number`; `false` otherwise.
|
||||
**/
|
||||
function isNumber(object) {
|
||||
return _toString.call(object) == "[object Number]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Object.isUndefined(object) -> Boolean
|
||||
* - object (Object): The object to test.
|
||||
*
|
||||
* Returns `true` if `object` is of type `string`; `false` otherwise.
|
||||
**/
|
||||
function isUndefined(object) {
|
||||
return typeof object === "undefined";
|
||||
}
|
||||
|
||||
extend(Object, {
|
||||
extend: extend,
|
||||
inspect: inspect,
|
||||
toJSON: toJSON,
|
||||
toQueryString: toQueryString,
|
||||
toHTML: toHTML,
|
||||
keys: keys,
|
||||
values: values,
|
||||
clone: clone,
|
||||
isElement: isElement,
|
||||
isArray: isArray,
|
||||
isHash: isHash,
|
||||
isFunction: isFunction,
|
||||
isString: isString,
|
||||
isNumber: isNumber,
|
||||
isUndefined: isUndefined
|
||||
});
|
||||
})();
|
|
@ -1,67 +0,0 @@
|
|||
/** section: Language
|
||||
* class PeriodicalExecuter
|
||||
*
|
||||
* A class that oversees the calling of a particular function periodically.
|
||||
*
|
||||
* `PeriodicalExecuter` shields you from multiple parallel executions of the
|
||||
* `callback` function, should it take longer than the given interval to
|
||||
* execute.
|
||||
*
|
||||
* This is especially useful if you use one to interact with the user at
|
||||
* given intervals (e.g. use a prompt or confirm call): this will avoid
|
||||
* multiple message boxes all waiting to be actioned.
|
||||
**/
|
||||
var PeriodicalExecuter = Class.create({
|
||||
/**
|
||||
* new PeriodicalExecuter(callback, frequency)
|
||||
* - callback (Function): the function to be executed at each interval.
|
||||
* - frequency (Number): the amount of time, in sections, to wait in between
|
||||
* callbacks.
|
||||
*
|
||||
* Creates an `PeriodicalExecuter`.
|
||||
**/
|
||||
initialize: function(callback, frequency) {
|
||||
this.callback = callback;
|
||||
this.frequency = frequency;
|
||||
this.currentlyExecuting = false;
|
||||
|
||||
this.registerCallback();
|
||||
},
|
||||
|
||||
registerCallback: function() {
|
||||
this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
|
||||
},
|
||||
|
||||
execute: function() {
|
||||
this.callback(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* PeriodicalExecuter#stop() -> undefined
|
||||
*
|
||||
* Stops the periodical executer (there will be no further triggers).
|
||||
**/
|
||||
stop: function() {
|
||||
if (!this.timer) return;
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
},
|
||||
|
||||
onTimerEvent: function() {
|
||||
if (!this.currentlyExecuting) {
|
||||
// IE doesn't support `finally` statements unless all errors are caught.
|
||||
// We mimic the behaviour of `finally` statements by duplicating code
|
||||
// that would belong in it. First at the bottom of the `try` statement
|
||||
// (for errorless cases). Secondly, inside a `catch` statement which
|
||||
// rethrows any caught errors.
|
||||
try {
|
||||
this.currentlyExecuting = true;
|
||||
this.execute();
|
||||
this.currentlyExecuting = false;
|
||||
} catch(e) {
|
||||
this.currentlyExecuting = false;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,76 +0,0 @@
|
|||
/** section: Language
|
||||
* class ObjectRange
|
||||
* includes Enumerable
|
||||
*
|
||||
* A succession of values.
|
||||
*
|
||||
* An `ObjectRange` can model a range of any value that implements a `succ`
|
||||
* method (which links that value to its "successor").
|
||||
*
|
||||
* Prototype provides such a method for [[Number]] and [[String]], but you
|
||||
* are (of course) welcome to implement useful semantics in your own objects,
|
||||
* in order to enable ranges based on them.
|
||||
*
|
||||
* `ObjectRange` mixes in [[Enumerable]], which makes ranges very versatile.
|
||||
* It takes care, however, to override the default code for `include`, to
|
||||
* achieve better efficiency.
|
||||
*
|
||||
* While `ObjectRange` does provide a constructor, the preferred way to obtain
|
||||
* a range is to use the [[$R]] utility function, which is strictly equivalent
|
||||
* (only way more concise to use).
|
||||
**/
|
||||
|
||||
/** section: Language
|
||||
* $R(start, end[, exclusive = false]) -> ObjectRange
|
||||
*
|
||||
* Creates a new ObjectRange object.
|
||||
* This method is a convenience wrapper around the [[ObjectRange]] constructor,
|
||||
* but $R is the preferred alias.
|
||||
**/
|
||||
function $R(start, end, exclusive) {
|
||||
return new ObjectRange(start, end, exclusive);
|
||||
}
|
||||
|
||||
var ObjectRange = Class.create(Enumerable, (function() {
|
||||
/**
|
||||
* new ObjectRange(start, end[, exclusive = false])
|
||||
*
|
||||
* Creates a new `ObjectRange`.
|
||||
*
|
||||
* The `exclusive` argument specifies whether `end` itself is a part of the
|
||||
* range.
|
||||
**/
|
||||
function initialize(start, end, exclusive) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.exclusive = exclusive;
|
||||
}
|
||||
|
||||
function _each(iterator) {
|
||||
var value = this.start;
|
||||
while (this.include(value)) {
|
||||
iterator(value);
|
||||
value = value.succ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ObjectRange#include(value) -> Boolean
|
||||
*
|
||||
* Determines whether the value is included in the range.
|
||||
**/
|
||||
function include(value) {
|
||||
if (value < this.start)
|
||||
return false;
|
||||
if (this.exclusive)
|
||||
return value < this.end;
|
||||
return value <= this.end;
|
||||
}
|
||||
|
||||
return {
|
||||
initialize: initialize,
|
||||
_each: _each,
|
||||
include: include
|
||||
};
|
||||
})());
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
/** section: Language
|
||||
* class RegExp
|
||||
*
|
||||
* Extensions to the built-in `RegExp` object.
|
||||
**/
|
||||
|
||||
/** alias of: RegExp#test
|
||||
* RegExp#match(str) -> Boolean
|
||||
*
|
||||
* Return true if string matches the regular expression, false otherwise.
|
||||
**/
|
||||
RegExp.prototype.match = RegExp.prototype.test;
|
||||
|
||||
/**
|
||||
* RegExp.escape(str) -> String
|
||||
* - str (String): A string intended to be used in a `RegExp` constructor.
|
||||
*
|
||||
* Escapes any characters in the string that have special meaning in a
|
||||
* regular expression.
|
||||
*
|
||||
* Use before passing a string into the `RegExp` constructor.
|
||||
**/
|
||||
RegExp.escape = function(str) {
|
||||
return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
|
||||
};
|
|
@ -1,525 +0,0 @@
|
|||
/** section: Language
|
||||
* class String
|
||||
*
|
||||
* Extensions to the built-in `String` class.
|
||||
*
|
||||
* Prototype enhances the `String` object with a series of useful methods for
|
||||
* ranging from the trivial to the complex. Tired of stripping trailing
|
||||
* whitespace? Try [[String#strip]]. Want to replace `replace`? Have a look at
|
||||
* [[String#sub]] and [[String#gsub]]. Need to parse a query string? We have
|
||||
* [[String#toQueryParams]].
|
||||
**/
|
||||
Object.extend(String, {
|
||||
/**
|
||||
* String.interpret(value) -> String
|
||||
*
|
||||
* Coerces `value` into a string. Returns an empty string for `null`.
|
||||
**/
|
||||
interpret: function(value) {
|
||||
return value == null ? '' : String(value);
|
||||
},
|
||||
specialChar: {
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'\\': '\\\\'
|
||||
}
|
||||
});
|
||||
|
||||
Object.extend(String.prototype, (function() {
|
||||
|
||||
function prepareReplacement(replacement) {
|
||||
if (Object.isFunction(replacement)) return replacement;
|
||||
var template = new Template(replacement);
|
||||
return function(match) { return template.evaluate(match) };
|
||||
}
|
||||
|
||||
/**
|
||||
* String#gsub(pattern, replacement) -> String
|
||||
*
|
||||
* Returns the string with every occurence of a given pattern replaced by either
|
||||
* a regular string, the returned value of a function or a [[Template]] string.
|
||||
* The pattern can be a string or a regular expression.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* ""hello".gsub(/([aeiou])/, '<#{1}>');
|
||||
* // => "h<e>ll<o>"
|
||||
**/
|
||||
function gsub(pattern, replacement) {
|
||||
var result = '', source = this, match;
|
||||
replacement = prepareReplacement(replacement);
|
||||
|
||||
if (Object.isString(pattern))
|
||||
pattern = RegExp.escape(pattern);
|
||||
|
||||
if (!(pattern.length || pattern.source)) {
|
||||
replacement = replacement('');
|
||||
return replacement + source.split('').join(replacement) + 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* String#sub(pattern, replacement[, count = 1]) -> String
|
||||
*
|
||||
* Returns a string with the first count occurrences of pattern replaced by either
|
||||
* a regular string, the returned value of a function or a [[Template]] string.
|
||||
* The pattern can be a string or a regular expression.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* "20091201".sub(/^(\d{4})(\d{2})(\d{2})$/, "#{1}-#{2}-#{3}");
|
||||
* // => "2009-12-01"
|
||||
**/
|
||||
function sub(pattern, replacement, count) {
|
||||
replacement = prepareReplacement(replacement);
|
||||
count = Object.isUndefined(count) ? 1 : count;
|
||||
|
||||
return this.gsub(pattern, function(match) {
|
||||
if (--count < 0) return match[0];
|
||||
return replacement(match);
|
||||
});
|
||||
}
|
||||
|
||||
/** related to: String#gsub
|
||||
* String#scan(pattern, iterator) -> String
|
||||
*
|
||||
* Allows iterating over every occurrence of the given pattern (which can be a
|
||||
* string or a regular expression).
|
||||
* Returns the original string.
|
||||
**/
|
||||
function scan(pattern, iterator) {
|
||||
this.gsub(pattern, iterator);
|
||||
return String(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* String#truncate([length = 30[, suffix = '...']]) -> String
|
||||
*
|
||||
* Truncates a string to given `length` and appends `suffix` to it (indicating
|
||||
* that it is only an excerpt).
|
||||
**/
|
||||
function truncate(length, truncation) {
|
||||
length = length || 30;
|
||||
truncation = Object.isUndefined(truncation) ? '...' : truncation;
|
||||
return this.length > length ?
|
||||
this.slice(0, length - truncation.length) + truncation : String(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* String#strip() -> String
|
||||
*
|
||||
* Strips all leading and trailing whitespace from a string.
|
||||
**/
|
||||
function strip() {
|
||||
return this.replace(/^\s+/, '').replace(/\s+$/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* String#stripTags() -> String
|
||||
*
|
||||
* Strips a string of any HTML tags.
|
||||
*
|
||||
* Note that `stripTags` will only strip HTML 4.01 tags — like `div`,
|
||||
* `span`, and `abbr`. It _will not_ strip namespace-prefixed tags such
|
||||
* as `h:table` or `xsl:template`.
|
||||
*
|
||||
* <h5>Caveat User</h5>
|
||||
*
|
||||
* Note that the processing `stripTags` does is good enough for most purposes, but
|
||||
* you cannot rely on it for security purposes. If you're processing end-user-supplied
|
||||
* content, `stripTags` is _not_ sufficiently robust to ensure that the content
|
||||
* is completely devoid of HTML tags in the case of a user intentionally trying to circumvent
|
||||
* tag restrictions. But then, you'll be running them through [[String#escapeHTML]] anyway,
|
||||
* won't you?
|
||||
**/
|
||||
function stripTags() {
|
||||
return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* String#stripScripts() -> String
|
||||
*
|
||||
* Strips a string of things that look like an HTML script blocks.
|
||||
*
|
||||
* <h5>Example</h5>
|
||||
*
|
||||
* "<p>This is a test.<script>alert("Look, a test!");</script>End of test</p>".stripScripts();
|
||||
* // => "<p>This is a test.End of test</p>"
|
||||
*
|
||||
* <h5>Caveat User</h5>
|
||||
*
|
||||
* Note that the processing `stripScripts` does is good enough for most purposes,
|
||||
* but you cannot rely on it for security purposes. If you're processing end-user-supplied
|
||||
* content, `stripScripts` is probably not sufficiently robust to prevent hack attacks.
|
||||
**/
|
||||
function stripScripts() {
|
||||
return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
|
||||
}
|
||||
|
||||
/**
|
||||
* String#extractScripts() -> Array
|
||||
*
|
||||
* Extracts the content of any script blocks present in the string and
|
||||
* returns them as an array of strings.
|
||||
**/
|
||||
function extractScripts() {
|
||||
var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),
|
||||
matchOne = new RegExp(Prototype.ScriptFragment, 'im');
|
||||
return (this.match(matchAll) || []).map(function(scriptTag) {
|
||||
return (scriptTag.match(matchOne) || ['', ''])[1];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* String#evalScripts() -> Array
|
||||
*
|
||||
* Evaluates the content of any inline `<script>` block present in the string.
|
||||
* Returns an array containing the value returned by each script.
|
||||
* `<script>` blocks referencing external files will be treated as though
|
||||
* they were empty (the result for that position in the array will be `undefined`);
|
||||
* external files are _not_ loaded and processed by `evalScripts`.
|
||||
*
|
||||
* <h5>About `evalScripts`, `var`s, and defining functions</h5>
|
||||
*
|
||||
* `evalScripts` evaluates script blocks, but this **does not** mean they are
|
||||
* evaluated in the global scope. They aren't, they're evaluated in the scope of
|
||||
* the `evalScripts` method. This has important ramifications for your scripts:
|
||||
*
|
||||
* * Anything in your script declared with the `var` keyword will be
|
||||
* discarded momentarily after evaluation, and will be invisible to any
|
||||
* other scope.
|
||||
* * If any `<script>` blocks _define functions_, they will need to be assigned to
|
||||
* properties of the `window` object.
|
||||
*
|
||||
* For example, this won't work:
|
||||
*
|
||||
* // This kind of script won't work if processed by evalScripts:
|
||||
* function coolFunc() {
|
||||
* // Amazing stuff!
|
||||
* }
|
||||
*
|
||||
* Instead, use the following syntax:
|
||||
*
|
||||
* // This kind of script WILL work if processed by evalScripts:
|
||||
* window.coolFunc = function() {
|
||||
* // Amazing stuff!
|
||||
* }
|
||||
*
|
||||
* (You can leave off the `window.` part of that, but it's bad form.)
|
||||
**/
|
||||
function evalScripts() {
|
||||
return this.extractScripts().map(function(script) { return eval(script) });
|
||||
}
|
||||
|
||||
/**
|
||||
* String#escapeHTML() -> String
|
||||
*
|
||||
* Converts HTML special characters to their entity equivalents.
|
||||
**/
|
||||
function escapeHTML() {
|
||||
return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
||||
}
|
||||
|
||||
/** related to: String#escapeHTML
|
||||
* String#unescapeHTML() -> String
|
||||
*
|
||||
* Strips tags and converts the entity forms of special HTML characters
|
||||
* to their normal form.
|
||||
**/
|
||||
function unescapeHTML() {
|
||||
// Warning: In 1.7 String#unescapeHTML will no longer call String#stripTags.
|
||||
return this.stripTags().replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&');
|
||||
}
|
||||
|
||||
/**
|
||||
* String#parseQuery([separator = '&']) -> Object
|
||||
**/
|
||||
|
||||
/** alias of: String#parseQuery, related to: Hash#toQueryString
|
||||
* String#toQueryParams([separator = '&']) -> Object
|
||||
*
|
||||
* Parses a URI-like query string and returns an object composed of
|
||||
* parameter/value pairs.
|
||||
**/
|
||||
function toQueryParams(separator) {
|
||||
var match = this.strip().match(/([^?#]*)(#.*)?$/);
|
||||
if (!match) return { };
|
||||
|
||||
return match[1].split(separator || '&').inject({ }, function(hash, pair) {
|
||||
if ((pair = pair.split('='))[0]) {
|
||||
var key = decodeURIComponent(pair.shift()),
|
||||
value = pair.length > 1 ? pair.join('=') : pair[0];
|
||||
|
||||
if (value != undefined) value = decodeURIComponent(value);
|
||||
|
||||
if (key in hash) {
|
||||
if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
|
||||
hash[key].push(value);
|
||||
}
|
||||
else hash[key] = value;
|
||||
}
|
||||
return hash;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* String#toArray() -> Array
|
||||
*
|
||||
* Splits the string character-by-character and returns an array with
|
||||
* the result.
|
||||
**/
|
||||
function toArray() {
|
||||
return this.split('');
|
||||
}
|
||||
|
||||
/**
|
||||
* String#succ() -> String
|
||||
*
|
||||
* Used internally by ObjectRange.
|
||||
* Converts the last character of the string to the following character in
|
||||
* the Unicode alphabet.
|
||||
**/
|
||||
function succ() {
|
||||
return this.slice(0, this.length - 1) +
|
||||
String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* String#times(count) -> String
|
||||
*
|
||||
* Concatenates the string `count` times.
|
||||
**/
|
||||
function times(count) {
|
||||
return count < 1 ? '' : new Array(count + 1).join(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* String#camelize() -> String
|
||||
*
|
||||
* Converts a string separated by dashes into a camelCase equivalent.
|
||||
* For instance, 'foo-bar' would be converted to 'fooBar'.
|
||||
*
|
||||
* <h5>Examples</h5>
|
||||
*
|
||||
* 'background-color'.camelize();
|
||||
* // -> 'backgroundColor'
|
||||
*
|
||||
* '-moz-binding'.camelize();
|
||||
* // -> 'MozBinding'
|
||||
**/
|
||||
function camelize() {
|
||||
return this.replace(/-+(.)?/g, function(match, chr) {
|
||||
return chr ? chr.toUpperCase() : '';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* String#capitalize() -> String
|
||||
*
|
||||
* Capitalizes the first letter of a string and downcases all the others.
|
||||
**/
|
||||
function capitalize() {
|
||||
return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* String#underscore() -> String
|
||||
*
|
||||
* Converts a camelized string into a series of words separated by an
|
||||
* underscore (`_`).
|
||||
**/
|
||||
function underscore() {
|
||||
return this.replace(/::/g, '/')
|
||||
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
|
||||
.replace(/([a-z\d])([A-Z])/g, '$1_$2')
|
||||
.replace(/-/g, '_')
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* String#dasherize() -> String
|
||||
*
|
||||
* Replaces every instance of the underscore character ("_") by a dash ("-").
|
||||
**/
|
||||
function dasherize() {
|
||||
return this.replace(/_/g, '-');
|
||||
}
|
||||
|
||||
/** related to: Object.inspect
|
||||
* String#inspect([useDoubleQuotes = false]) -> String
|
||||
*
|
||||
* Returns a debug-oriented version of the string (i.e. wrapped in single or
|
||||
* double quotes, with backslashes and quotes escaped).
|
||||
**/
|
||||
function inspect(useDoubleQuotes) {
|
||||
var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
|
||||
if (character in String.specialChar) {
|
||||
return String.specialChar[character];
|
||||
}
|
||||
return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
|
||||
});
|
||||
if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
|
||||
return "'" + escapedString.replace(/'/g, '\\\'') + "'";
|
||||
}
|
||||
|
||||
/** related to: Object.toJSON
|
||||
* String#toJSON() -> String
|
||||
*
|
||||
* Returns a JSON string.
|
||||
**/
|
||||
function toJSON() {
|
||||
return this.inspect(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* String#unfilterJSON([filter = Prototype.JSONFilter]) -> String
|
||||
*
|
||||
* Strips comment delimiters around Ajax JSON or JavaScript responses.
|
||||
* This security method is called internally.
|
||||
**/
|
||||
function unfilterJSON(filter) {
|
||||
return this.replace(filter || Prototype.JSONFilter, '$1');
|
||||
}
|
||||
|
||||
/**
|
||||
* String#isJSON() -> Boolean
|
||||
*
|
||||
* Check if the string is valid JSON by the use of regular expressions.
|
||||
* This security method is called internally.
|
||||
**/
|
||||
function isJSON() {
|
||||
var str = this;
|
||||
if (str.blank()) return false;
|
||||
str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
|
||||
return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* String#evalJSON([sanitize = false]) -> object
|
||||
*
|
||||
* Evaluates the JSON in the string and returns the resulting object.
|
||||
*
|
||||
* If the optional `sanitize` parameter is set to `true`, the string is
|
||||
* checked for possible malicious attempts; if one is detected, `eval`
|
||||
* is _not called_.
|
||||
**/
|
||||
function evalJSON(sanitize) {
|
||||
var json = this.unfilterJSON();
|
||||
try {
|
||||
if (!sanitize || json.isJSON()) return eval('(' + json + ')');
|
||||
} catch (e) { }
|
||||
throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
|
||||
}
|
||||
|
||||
/**
|
||||
* String#include(substring) -> Boolean
|
||||
*
|
||||
* Checks if the string contains `substring`.
|
||||
**/
|
||||
function include(pattern) {
|
||||
return this.indexOf(pattern) > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* String#startsWith(substring) -> Boolean
|
||||
*
|
||||
* Checks if the string starts with `substring`.
|
||||
**/
|
||||
function startsWith(pattern) {
|
||||
// We use `lastIndexOf` instead of `indexOf` to avoid tying execution
|
||||
// time to string length when string doesn't start with pattern.
|
||||
return this.lastIndexOf(pattern, 0) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* String#endsWith(substring) -> Boolean
|
||||
*
|
||||
* Checks if the string ends with `substring`.
|
||||
**/
|
||||
function endsWith(pattern) {
|
||||
var d = this.length - pattern.length;
|
||||
// We use `indexOf` instead of `lastIndexOf` to avoid tying execution
|
||||
// time to string length when string doesn't end with pattern.
|
||||
return d >= 0 && this.indexOf(pattern, d) === d;
|
||||
}
|
||||
|
||||
/**
|
||||
* String#empty() -> Boolean
|
||||
*
|
||||
* Checks if the string is empty.
|
||||
**/
|
||||
function empty() {
|
||||
return this == '';
|
||||
}
|
||||
|
||||
/**
|
||||
* String#blank() -> Boolean
|
||||
*
|
||||
* Check if the string is "blank" — either empty (length of `0`) or containing
|
||||
* only whitespace.
|
||||
**/
|
||||
function blank() {
|
||||
return /^\s*$/.test(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* String#interpolate(object[, pattern]) -> String
|
||||
*
|
||||
* Treats the string as a [[Template]] and fills it with `object`'s
|
||||
* properties.
|
||||
**/
|
||||
function interpolate(object, pattern) {
|
||||
return new Template(this, pattern).evaluate(object);
|
||||
}
|
||||
|
||||
return {
|
||||
gsub: gsub,
|
||||
sub: sub,
|
||||
scan: scan,
|
||||
truncate: truncate,
|
||||
// Firefox 3.5+ supports String.prototype.trim
|
||||
// (`trim` is ~ 5x faster than `strip` in FF3.5)
|
||||
strip: String.prototype.trim || strip,
|
||||
stripTags: stripTags,
|
||||
stripScripts: stripScripts,
|
||||
extractScripts: extractScripts,
|
||||
evalScripts: evalScripts,
|
||||
escapeHTML: escapeHTML,
|
||||
unescapeHTML: unescapeHTML,
|
||||
toQueryParams: toQueryParams,
|
||||
parseQuery: toQueryParams,
|
||||
toArray: toArray,
|
||||
succ: succ,
|
||||
times: times,
|
||||
camelize: camelize,
|
||||
capitalize: capitalize,
|
||||
underscore: underscore,
|
||||
dasherize: dasherize,
|
||||
inspect: inspect,
|
||||
toJSON: toJSON,
|
||||
unfilterJSON: unfilterJSON,
|
||||
isJSON: isJSON,
|
||||
evalJSON: evalJSON,
|
||||
include: include,
|
||||
startsWith: startsWith,
|
||||
endsWith: endsWith,
|
||||
empty: empty,
|
||||
blank: blank,
|
||||
interpolate: interpolate
|
||||
};
|
||||
})());
|
||||
|
|
@ -1,154 +0,0 @@
|
|||
/** section: Language
|
||||
* class Template
|
||||
*
|
||||
* A class for sophisticated string interpolation.
|
||||
*
|
||||
* Any time you have a group of similar objects and you need to produce
|
||||
* formatted output for these objects, maybe inside a loop, you typically
|
||||
* resort to concatenating string literals with the object's fields:
|
||||
*
|
||||
* "The TV show " + title + " was created by " + author + ".";
|
||||
*
|
||||
* There's nothing wrong with this approach, except that it is hard to
|
||||
* visualize the output immediately just by glancing at the concatenation
|
||||
* expression. The `Template` class provides a much nicer and clearer way of
|
||||
* achieving this formatting.
|
||||
*
|
||||
* <h5>Straightforward templates</h5>
|
||||
*
|
||||
* The `Template` class uses a basic formatting syntax, similar to what is
|
||||
* used in Ruby. The templates are created from strings that have embedded
|
||||
* symbols in the form (e.g., `#{fieldName}`) that will be replaced by
|
||||
* actual values when the template is applied (evaluated) to an object.
|
||||
*
|
||||
* // the template (our formatting expression)
|
||||
* var myTemplate = new Template(
|
||||
* 'The TV show #{title} was created by #{author}.');
|
||||
*
|
||||
* // our data to be formatted by the template
|
||||
* var show = {
|
||||
* title: 'The Simpsons',
|
||||
* author: 'Matt Groening',
|
||||
* network: 'FOX'
|
||||
* };
|
||||
*
|
||||
* // let's format our data
|
||||
* myTemplate.evaluate(show);
|
||||
* // -> "The TV show The Simpsons was created by Matt Groening."
|
||||
*
|
||||
* <h5>Templates are meant to be reused</h5>
|
||||
*
|
||||
* As the example illustrates, `Template` objects are not tied to specific
|
||||
* data. The data is bound to the template only during the evaluation of the
|
||||
* template, without affecting the template itself. The next example shows the
|
||||
* same template being used with a handful of distinct objects.
|
||||
*
|
||||
* // creating a few similar objects
|
||||
* var conversion1 = { from: 'meters', to: 'feet', factor: 3.28 };
|
||||
* var conversion2 = { from: 'kilojoules', to: 'BTUs', factor: 0.9478 };
|
||||
* var conversion3 = { from: 'megabytes', to: 'gigabytes', factor: 1024 };
|
||||
*
|
||||
* // the template
|
||||
* var templ = new Template(
|
||||
* 'Multiply by #{factor} to convert from #{from} to #{to}.');
|
||||
*
|
||||
* // let's format each object
|
||||
* [conversion1, conversion2, conversion3].each( function(conv){
|
||||
* templ.evaluate(conv);
|
||||
* });
|
||||
* // -> Multiply by 3.28 to convert from meters to feet.
|
||||
* // -> Multiply by 0.9478 to convert from kilojoules to BTUs.
|
||||
* // -> Multiply by 1024 to convert from megabytes to gigabytes.
|
||||
*
|
||||
* <h5>Escape sequence</h5>
|
||||
*
|
||||
* There's always the chance that one day you'll need to have a literal in your
|
||||
* template that looks like a symbol, but is not supposed to be replaced. For
|
||||
* these situations there's an escape character: the backslash (<code>\\</code>).
|
||||
*
|
||||
* // NOTE: you're seeing two backslashes here because the backslash
|
||||
* // is also an escape character in JavaScript strings, so a literal
|
||||
* // backslash is represented by two backslashes.
|
||||
* var t = new Template(
|
||||
* 'in #{lang} we also use the \\#{variable} syntax for templates.');
|
||||
* var data = { lang:'Ruby', variable: '(not used)' };
|
||||
* t.evaluate(data);
|
||||
* // -> in Ruby we also use the #{variable} syntax for templates.
|
||||
*
|
||||
* <h5>Custom syntaxes</h5>
|
||||
*
|
||||
* The default syntax of the template strings will probably be enough for most
|
||||
* scenarios. In the rare occasion where the default Ruby-like syntax is
|
||||
* inadequate, there's a provision for customization. `Template`'s
|
||||
* constructor accepts an optional second argument that is a regular expression
|
||||
* object to match the replaceable symbols in the template string. Let's put
|
||||
* together a template that uses a syntax similar to the now ubiquitous `{{ }}`
|
||||
* constructs:
|
||||
*
|
||||
* // matches symbols like '{{ field }}'
|
||||
* var syntax = /(^|.|\r|\n)(\{{\s*(\w+)\s*}})/;
|
||||
*
|
||||
* var t = new Template(
|
||||
* '<div>Name: <b>{{ name }}</b>, Age: <b>{{ age }}</b></div>',
|
||||
* syntax);
|
||||
* t.evaluate( {name: 'John Smith', age: 26} );
|
||||
* // -> <div>Name: <b>John Smith</b>, Age: <b>26</b></div>
|
||||
*
|
||||
* There are important constraints to any custom syntax. Any syntax must
|
||||
* provide at least three groupings in the regular expression. The first
|
||||
* grouping is to capture what comes before the symbol, to detect the backslash
|
||||
* escape character (no, you cannot use a different character). The second
|
||||
* grouping captures the entire symbol and will be completely replaced upon
|
||||
* evaluation. Lastly, the third required grouping captures the name of the
|
||||
* field inside the symbol.
|
||||
*
|
||||
**/
|
||||
var Template = Class.create({
|
||||
/**
|
||||
* new Template(template[, pattern = Template.Pattern])
|
||||
*
|
||||
* Creates a Template object.
|
||||
*
|
||||
* The optional `pattern` argument expects a `RegExp` that defines a custom
|
||||
* syntax for the replaceable symbols in `template`.
|
||||
**/
|
||||
initialize: function(template, pattern) {
|
||||
this.template = template.toString();
|
||||
this.pattern = pattern || Template.Pattern;
|
||||
},
|
||||
|
||||
/**
|
||||
* Template#evaluate(object) -> String
|
||||
*
|
||||
* Applies the template to `object`'s data, producing a formatted string
|
||||
* with symbols replaced by `object`'s corresponding properties.
|
||||
**/
|
||||
evaluate: function(object) {
|
||||
if (object && Object.isFunction(object.toTemplateReplacements))
|
||||
object = object.toTemplateReplacements();
|
||||
|
||||
return this.template.gsub(this.pattern, function(match) {
|
||||
if (object == null) return (match[1] + '');
|
||||
|
||||
var before = match[1] || '';
|
||||
if (before == '\\') return match[2];
|
||||
|
||||
var ctx = object, expr = match[3],
|
||||
pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
|
||||
|
||||
match = pattern.exec(expr);
|
||||
if (match == null) return before;
|
||||
|
||||
while (match != null) {
|
||||
var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
|
||||
ctx = ctx[comp];
|
||||
if (null == ctx || '' == match[3]) break;
|
||||
expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
|
||||
match = pattern.exec(expr);
|
||||
}
|
||||
|
||||
return before + String.interpret(ctx);
|
||||
});
|
||||
}
|
||||
});
|
||||
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
|
|
@ -1,66 +0,0 @@
|
|||
/* Prototype JavaScript framework, version <%= PROTOTYPE_VERSION %>
|
||||
* (c) 2005-2009 Sam Stephenson
|
||||
*
|
||||
* Prototype is freely distributable under the terms of an MIT-style license.
|
||||
* For details, see the Prototype web site: http://www.prototypejs.org/
|
||||
*
|
||||
*--------------------------------------------------------------------------*/
|
||||
|
||||
var Prototype = {
|
||||
Version: '<%= PROTOTYPE_VERSION %>',
|
||||
|
||||
Browser: (function(){
|
||||
var ua = navigator.userAgent;
|
||||
// Opera (at least) 8.x+ has "Opera" as a [[Class]] of `window.opera`
|
||||
// This is a safer inference than plain boolean type conversion of `window.opera`
|
||||
var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
|
||||
return {
|
||||
IE: !!window.attachEvent && !isOpera,
|
||||
Opera: isOpera,
|
||||
WebKit: ua.indexOf('AppleWebKit/') > -1,
|
||||
Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
|
||||
MobileSafari: /Apple.*Mobile.*Safari/.test(ua)
|
||||
}
|
||||
})(),
|
||||
|
||||
BrowserFeatures: {
|
||||
XPath: !!document.evaluate,
|
||||
SelectorsAPI: !!document.querySelector,
|
||||
ElementExtensions: (function() {
|
||||
var constructor = window.Element || window.HTMLElement;
|
||||
return !!(constructor && constructor.prototype);
|
||||
})(),
|
||||
SpecificElementExtensions: (function() {
|
||||
// First, try the named class
|
||||
if (typeof window.HTMLDivElement !== 'undefined')
|
||||
return true;
|
||||
|
||||
var div = document.createElement('div'),
|
||||
form = document.createElement('form'),
|
||||
isSupported = false;
|
||||
|
||||
if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
|
||||
isSupported = true;
|
||||
}
|
||||
|
||||
div = form = null;
|
||||
|
||||
return isSupported;
|
||||
})()
|
||||
},
|
||||
|
||||
ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
|
||||
JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
|
||||
|
||||
emptyFunction: function() { },
|
||||
K: function(x) { return x }
|
||||
};
|
||||
|
||||
if (Prototype.Browser.MobileSafari)
|
||||
Prototype.BrowserFeatures.SpecificElementExtensions = false;
|
||||
|
||||
//= require "lang"
|
||||
//= require "ajax"
|
||||
//= require "dom"
|
||||
|
||||
//= require "deprecated"
|
|
@ -1,29 +0,0 @@
|
|||
Prototype._original_property = window.Sizzle;
|
||||
//= require "sizzle"
|
||||
|
||||
Prototype.Selector = (function(engine) {
|
||||
function extend(elements) {
|
||||
for (var i = 0, length = elements.length; i < length; i++) {
|
||||
Element.extend(elements[i]);
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
function select(selector, scope) {
|
||||
return extend(engine(selector, scope || document));
|
||||
}
|
||||
|
||||
function match(element, selector) {
|
||||
return engine.matches(selector, [element]).length == 1;
|
||||
}
|
||||
|
||||
return {
|
||||
engine: engine,
|
||||
select: select,
|
||||
match: match
|
||||
};
|
||||
})(Sizzle);
|
||||
|
||||
// Restore globals.
|
||||
window.Sizzle = Prototype._original_property;
|
||||
delete Prototype._original_property;
|
1015
src/sizzle.js
1015
src/sizzle.js
File diff suppressed because it is too large
Load Diff
|
@ -1,229 +0,0 @@
|
|||
<!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>
|
|
@ -1,110 +0,0 @@
|
|||
<!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>
|
|
@ -1,267 +0,0 @@
|
|||
<!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 functional test file</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<script src="../../dist/prototype.js" type="text/javascript"></script>
|
||||
|
||||
<style type="text/css" media="screen">
|
||||
/* <![CDATA[ */
|
||||
body { margin:1em 2em; padding:0; font-size:0.8em }
|
||||
hr { width:31.2em; margin:1em 0; text-align:left }
|
||||
p { width:30em; margin:0.5em 0; padding:0.3em 0.6em; color:#222; background:#eee; border:1px solid silver; }
|
||||
.subtest { margin-top:-0.5em }
|
||||
.passed { color:green; border-color:olive }
|
||||
.failed { color:firebrick; border-color:firebrick }
|
||||
.button { padding:0.2em 0.4em; background:#ccc; border:1px solid #aaa }
|
||||
#log { position:absolute; left:35em; top:5em; width:20em; font-size:13px !important }
|
||||
h2 { font:normal 1.1em Verdana,Arial,sans-serif; font-style:italic; color:gray; margin-top:-1.2em }
|
||||
h2 *, h2 a:visited { color:#444 }
|
||||
h2 a:hover { color:blue }
|
||||
a:visited { color:blue }
|
||||
a:hover { color:red }
|
||||
/* ]]> */
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
Element.addMethods({
|
||||
passed: function(el, message) {
|
||||
el = $(el);
|
||||
el.className = 'passed';
|
||||
(el.down('span') || el).update(message || 'Test passed!');
|
||||
},
|
||||
|
||||
failed: function(el, message) {
|
||||
el = $(el);
|
||||
el.className = 'failed';
|
||||
(el.down('span') || el).update(message || 'Test failed');
|
||||
}
|
||||
});
|
||||
|
||||
function log(obj) {
|
||||
var line, all = [];
|
||||
for (prop in obj) {
|
||||
if (typeof obj[prop] == 'function' || /^[A-Z]|[XY]$/.test(prop)) continue;
|
||||
line = prop + ": " + Object.inspect(obj[prop]);
|
||||
all.push(line.escapeHTML());
|
||||
}
|
||||
$('log').update(all.join('<br />'));
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Prototype functional tests for the Event module</h1>
|
||||
|
||||
<div id="log">log empty</div>
|
||||
|
||||
<p id="basic">A basic event test - <strong>click here</strong></p>
|
||||
<p id="basic_remove" class="subtest"><strong>click</strong> to stop observing the first test</p>
|
||||
|
||||
<p id="inline_test" onclick="Event.stop(event); $(this).passed();"><strong>click</strong> to ensure generic Event methods work on inline handlers</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
var basic_callback = function(e){
|
||||
$('basic').passed();
|
||||
if ($('basic_remove')) $('basic_remove').show()
|
||||
else $('basic').failed()
|
||||
log(e);
|
||||
}
|
||||
$('basic').observe('click', basic_callback)
|
||||
$('basic_remove').observe('click', function(e){
|
||||
el = $('basic')
|
||||
el.passed('This test should now be inactive (try clicking)')
|
||||
el.stopObserving('click')
|
||||
$('basic_remove').remove()
|
||||
log(e);
|
||||
}).hide()
|
||||
</script>
|
||||
|
||||
<p id="basic2"><strong>Scope</strong> test - scope of the handler should be this element</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('basic2').observe('click', function(e) {
|
||||
if(this === window) $('basic2').failed('Window scope! (needs scope correction)');
|
||||
else this.passed();
|
||||
log(e);
|
||||
});
|
||||
</script>
|
||||
|
||||
<p id="basic3"><strong>Event object</strong> test - should be present as a first argument</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('basic3').observe('click', function(evt) {
|
||||
el = $('basic3');
|
||||
if (typeof evt != 'object') this.failed('Expected event object for first argument');
|
||||
else this.passed('Good first argument');
|
||||
log(evt);
|
||||
});
|
||||
</script>
|
||||
|
||||
<p><a href="#wrong" id="hijack">Hijack link test</a> (preventDefault)</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('hijack').observe('click', function(e){
|
||||
el = $(this.parentNode);
|
||||
log(e); // this makes it fail?!?
|
||||
e.preventDefault();
|
||||
|
||||
setTimeout(function() {
|
||||
if (window.location.hash == '#wrong') el.failed('Hijack failed (<a href="' +
|
||||
window.location.toString().replace(/#.+$/, '') + '">remove the fragment</a>)')
|
||||
else el.passed();
|
||||
}, 50)
|
||||
})
|
||||
</script>
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
<p>Mouse click:
|
||||
<span class="button" id="left">left</span> <span class="button" id="middle">middle</span> <span class="button" id="right">right</span></p>
|
||||
|
||||
<script type="text/javascript">
|
||||
$w('left middle right').each(function(button) {
|
||||
Event.observe(button, 'mousedown', function(e) {
|
||||
if (Event['is' + this.id.capitalize() + 'Click'](e)) this.passed('Squeak!')
|
||||
else this.failed('OH NO!');
|
||||
log(e);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<p id="context">Context menu event (tries to prevent default)</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('context').observe('contextmenu', function(e){
|
||||
this.passed();
|
||||
Event.stop(e);
|
||||
log(e);
|
||||
})
|
||||
</script>
|
||||
|
||||
<p id="target">Event.element() test</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('target').observe('click', function(e) {
|
||||
if (e.element() == this && e.target == this) this.passed();
|
||||
else this.failed();
|
||||
log(e);
|
||||
});
|
||||
</script>
|
||||
|
||||
<p id="currentTarget"><span>Event.currentTarget test</span></p>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('currentTarget').observe('click', function(e){
|
||||
if(e.currentTarget !== this) this.failed();
|
||||
else this.passed();
|
||||
log(e);
|
||||
})
|
||||
</script>
|
||||
|
||||
<p id="findElement"><span>Event.findElement() test</span></p>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('findElement').observe('click', function(e){
|
||||
if(e.findElement('p') == this && e.findElement('body') == document.body &&
|
||||
e.findElement('foo') == null) this.passed();
|
||||
else this.failed();
|
||||
log(e);
|
||||
})
|
||||
</script>
|
||||
|
||||
<div id="container"><p id="stop"><strong>Stop propagation</strong> test (bubbling)</p></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('stop').observe('click', function(e){
|
||||
e.stop();
|
||||
this.passed();
|
||||
log(e);
|
||||
})
|
||||
$('container').observe('click', function(e){
|
||||
$('stop').failed();
|
||||
log(e);
|
||||
})
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<p id="keyup_log"><strong>Keyup</strong> test - focus on the textarea and type</p>
|
||||
<textarea id="keyup" class="subtest"></textarea>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('keyup').observe('keyup', function(e){
|
||||
el = $('keyup_log');
|
||||
el.passed('Key captured: the length is ' + $('keyup').value.length);
|
||||
log(e);
|
||||
})
|
||||
</script>
|
||||
|
||||
<p id="bind"><code>bindAsEventListener()</code> test</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('bind').observe('click', function(e, str, arr){
|
||||
el = $('bind')
|
||||
try {
|
||||
if (arguments.length != 3) throw arguments.length + ' arguments: ' + $A(arguments).inspect()
|
||||
if (str != 'foo') throw 'wrong string: ' + str
|
||||
if (arr.constructor != Array) throw '3rd parameter is not an array'
|
||||
el.passed();
|
||||
}
|
||||
catch (err) { el.failed(err.toString()) }
|
||||
log(e);
|
||||
}.bindAsEventListener(document.body, 'foo', [1,2,3]))
|
||||
</script>
|
||||
|
||||
<p id="obj_inspect"><code>Object.inspect(event)</code> test</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('obj_inspect').observe('click', function(e){
|
||||
el = $('obj_inspect')
|
||||
try { el.passed(Object.inspect(e)) }
|
||||
catch (err) { el.failed('Failed! Error thrown') }
|
||||
log(e);
|
||||
})
|
||||
</script>
|
||||
|
||||
<p id="mouseenter"><code>mouseenter</code> test</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('mouseenter').observe('click', function() {
|
||||
$('mouseenter').insert("<br />carefully mouse over the word 'mouseenter', then outside of the box entirely");
|
||||
$('mouseenter').observe('mouseenter', function(event) {
|
||||
this.failed();
|
||||
$('mouseenter').stopObserving();
|
||||
});
|
||||
$('mouseenter').observe('mouseleave', function(event) {
|
||||
if ($(event.relatedTarget).descendantOf($('mouseenter'))) {
|
||||
$('mouseenter').failed();
|
||||
} else $('mouseenter').passed();
|
||||
|
||||
$('mouseenter').stopObserving();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<p id="addunload">Add unload events</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
$('addunload').observe('click', function(e){
|
||||
if (this._done) return;
|
||||
|
||||
window.onunload = function(){ alert('inline unload fired!') }
|
||||
Event.observe(window, 'unload', function(event){
|
||||
if (!event.target) {
|
||||
alert('FAILURE: event.target should not be null!');
|
||||
}
|
||||
alert('observed unload fired!')
|
||||
});
|
||||
|
||||
this.update('Registered two unload events, one inline ("onunload") and one regular - try to refresh, both should fire')
|
||||
this._done = true
|
||||
log(e);
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,379 +0,0 @@
|
|||
var extendDefault = function(options) {
|
||||
return Object.extend({
|
||||
asynchronous: false,
|
||||
method: 'get',
|
||||
onException: function(e) { throw e }
|
||||
}, options);
|
||||
};
|
||||
|
||||
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() {
|
||||
this.assertEqual("", $("content").innerHTML);
|
||||
|
||||
this.assertEqual(0, Ajax.activeRequestCount);
|
||||
new Ajax.Request("../fixtures/hello.js", {
|
||||
asynchronous: false,
|
||||
method: 'GET',
|
||||
evalJS: 'force'
|
||||
});
|
||||
this.assertEqual(0, Ajax.activeRequestCount);
|
||||
|
||||
var h2 = $("content").firstChild;
|
||||
this.assertEqual("Hello world!", h2.innerHTML);
|
||||
},
|
||||
|
||||
testAsynchronousRequest: function() {
|
||||
this.assertEqual("", $("content").innerHTML);
|
||||
|
||||
new Ajax.Request("../fixtures/hello.js", {
|
||||
asynchronous: true,
|
||||
method: 'get',
|
||||
evalJS: 'force'
|
||||
});
|
||||
this.wait(1000, function() {
|
||||
var h2 = $("content").firstChild;
|
||||
this.assertEqual("Hello world!", h2.innerHTML);
|
||||
});
|
||||
},
|
||||
|
||||
testUpdater: function() {
|
||||
this.assertEqual("", $("content").innerHTML);
|
||||
|
||||
new Ajax.Updater("content", "../fixtures/content.html", { method:'get' });
|
||||
|
||||
this.wait(1000, function() {
|
||||
this.assertEqual(sentence, $("content").innerHTML.strip().toLowerCase());
|
||||
|
||||
$('content').update('');
|
||||
this.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" });
|
||||
|
||||
this.wait(1000, function() {
|
||||
this.assertEqual(sentence, $("content").innerHTML.strip().toLowerCase());
|
||||
this.assertEqual("", $("content2").innerHTML);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testUpdaterWithInsertion: function() {
|
||||
$('content').update();
|
||||
new Ajax.Updater("content", "../fixtures/content.html", { method:'get', insertion: Insertion.Top });
|
||||
this.wait(1000, function() {
|
||||
this.assertEqual(sentence, $("content").innerHTML.strip().toLowerCase());
|
||||
$('content').update();
|
||||
new Ajax.Updater("content", "../fixtures/content.html", { method:'get', insertion: 'bottom' });
|
||||
this.wait(1000, function() {
|
||||
this.assertEqual(sentence, $("content").innerHTML.strip().toLowerCase());
|
||||
|
||||
$('content').update();
|
||||
new Ajax.Updater("content", "../fixtures/content.html", { method:'get', insertion: 'after' });
|
||||
this.wait(1000, function() {
|
||||
this.assertEqual('five dozen', $("content").next().innerHTML.strip().toLowerCase());
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testUpdaterOptions: function() {
|
||||
var options = {
|
||||
method: 'get',
|
||||
asynchronous: false,
|
||||
evalJS: 'force',
|
||||
onComplete: Prototype.emptyFunction
|
||||
}
|
||||
var request = new Ajax.Updater("content", "../fixtures/hello.js", options);
|
||||
request.options.onComplete = Prototype.emptyFunction;
|
||||
this.assertIdentical(Prototype.emptyFunction, options.onComplete);
|
||||
},
|
||||
|
||||
testResponders: function(){
|
||||
// check for internal responder
|
||||
this.assertEqual(1, Ajax.Responders.responders.length);
|
||||
|
||||
var dummyResponder = {
|
||||
onComplete: Prototype.emptyFunction
|
||||
};
|
||||
|
||||
Ajax.Responders.register(dummyResponder);
|
||||
this.assertEqual(2, Ajax.Responders.responders.length);
|
||||
|
||||
// don't add twice
|
||||
Ajax.Responders.register(dummyResponder);
|
||||
this.assertEqual(2, Ajax.Responders.responders.length);
|
||||
|
||||
Ajax.Responders.unregister(dummyResponder);
|
||||
this.assertEqual(1, Ajax.Responders.responders.length);
|
||||
|
||||
var responder = {
|
||||
onCreate: function(req){ responderCounter++ },
|
||||
onLoading: function(req){ responderCounter++ },
|
||||
onComplete: function(req){ responderCounter++ }
|
||||
};
|
||||
Ajax.Responders.register(responder);
|
||||
|
||||
this.assertEqual(0, responderCounter);
|
||||
this.assertEqual(0, Ajax.activeRequestCount);
|
||||
new Ajax.Request("../fixtures/content.html", { method:'get', parameters:"pet=monkey" });
|
||||
this.assertEqual(1, responderCounter);
|
||||
this.assertEqual(1, Ajax.activeRequestCount);
|
||||
|
||||
this.wait(1000,function() {
|
||||
this.assertEqual(3, responderCounter);
|
||||
this.assertEqual(0, Ajax.activeRequestCount);
|
||||
});
|
||||
},
|
||||
|
||||
testEvalResponseShouldBeCalledBeforeOnComplete: function() {
|
||||
if (this.isRunningFromRake) {
|
||||
this.assertEqual("", $("content").innerHTML);
|
||||
|
||||
this.assertEqual(0, Ajax.activeRequestCount);
|
||||
new Ajax.Request("../fixtures/hello.js", extendDefault({
|
||||
onComplete: function(response) { this.assertNotEqual("", $("content").innerHTML) }.bind(this)
|
||||
}));
|
||||
this.assertEqual(0, Ajax.activeRequestCount);
|
||||
|
||||
var h2 = $("content").firstChild;
|
||||
this.assertEqual("Hello world!", h2.innerHTML);
|
||||
} else {
|
||||
this.info(message);
|
||||
}
|
||||
},
|
||||
|
||||
testContentTypeSetForSimulatedVerbs: function() {
|
||||
if (this.isRunningFromRake) {
|
||||
new Ajax.Request('/inspect', extendDefault({
|
||||
method: 'put',
|
||||
contentType: 'application/bogus',
|
||||
onComplete: function(response) {
|
||||
this.assertEqual('application/bogus; charset=UTF-8', response.responseJSON.headers['content-type']);
|
||||
}.bind(this)
|
||||
}));
|
||||
} else {
|
||||
this.info(message);
|
||||
}
|
||||
},
|
||||
|
||||
testOnCreateCallback: function() {
|
||||
new Ajax.Request("../fixtures/content.html", extendDefault({
|
||||
onCreate: function(transport) { this.assertEqual(0, transport.readyState) }.bind(this),
|
||||
onComplete: function(transport) { this.assertNotEqual(0, transport.readyState) }.bind(this)
|
||||
}));
|
||||
},
|
||||
|
||||
testEvalJS: function() {
|
||||
if (this.isRunningFromRake) {
|
||||
|
||||
$('content').update();
|
||||
new Ajax.Request("/response", extendDefault({
|
||||
parameters: Fixtures.js,
|
||||
onComplete: function(transport) {
|
||||
var h2 = $("content").firstChild;
|
||||
this.assertEqual("Hello world!", h2.innerHTML);
|
||||
}.bind(this)
|
||||
}));
|
||||
|
||||
$('content').update();
|
||||
new Ajax.Request("/response", extendDefault({
|
||||
evalJS: false,
|
||||
parameters: Fixtures.js,
|
||||
onComplete: function(transport) {
|
||||
this.assertEqual("", $("content").innerHTML);
|
||||
}.bind(this)
|
||||
}));
|
||||
} else {
|
||||
this.info(message);
|
||||
}
|
||||
|
||||
$('content').update();
|
||||
new Ajax.Request("../fixtures/hello.js", extendDefault({
|
||||
evalJS: 'force',
|
||||
onComplete: function(transport) {
|
||||
var h2 = $("content").firstChild;
|
||||
this.assertEqual("Hello world!", h2.innerHTML);
|
||||
}.bind(this)
|
||||
}));
|
||||
},
|
||||
|
||||
testCallbacks: function() {
|
||||
var options = extendDefault({
|
||||
onCreate: function(transport) { this.assertInstanceOf(Ajax.Response, transport) }.bind(this)
|
||||
});
|
||||
|
||||
Ajax.Request.Events.each(function(state){
|
||||
options['on' + state] = options.onCreate;
|
||||
});
|
||||
|
||||
new Ajax.Request("../fixtures/content.html", options);
|
||||
},
|
||||
|
||||
testResponseText: function() {
|
||||
new Ajax.Request("../fixtures/empty.html", extendDefault({
|
||||
onComplete: function(transport) { this.assertEqual('', transport.responseText) }.bind(this)
|
||||
}));
|
||||
|
||||
new Ajax.Request("../fixtures/content.html", extendDefault({
|
||||
onComplete: function(transport) { this.assertEqual(sentence, transport.responseText.toLowerCase()) }.bind(this)
|
||||
}));
|
||||
},
|
||||
|
||||
testResponseXML: function() {
|
||||
if (this.isRunningFromRake) {
|
||||
new Ajax.Request("/response", extendDefault({
|
||||
parameters: Fixtures.xml,
|
||||
onComplete: function(transport) {
|
||||
this.assertEqual('foo', transport.responseXML.getElementsByTagName('name')[0].getAttribute('attr'))
|
||||
}.bind(this)
|
||||
}));
|
||||
} else {
|
||||
this.info(message);
|
||||
}
|
||||
},
|
||||
|
||||
testResponseJSON: function() {
|
||||
if (this.isRunningFromRake) {
|
||||
new Ajax.Request("/response", extendDefault({
|
||||
parameters: Fixtures.json,
|
||||
onComplete: function(transport) { this.assertEqual(123, transport.responseJSON.test) }.bind(this)
|
||||
}));
|
||||
|
||||
new Ajax.Request("/response", extendDefault({
|
||||
parameters: {
|
||||
'Content-Length': 0,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
onComplete: function(transport) { this.assertNull(transport.responseJSON) }.bind(this)
|
||||
}));
|
||||
|
||||
new Ajax.Request("/response", extendDefault({
|
||||
evalJSON: false,
|
||||
parameters: Fixtures.json,
|
||||
onComplete: function(transport) { this.assertNull(transport.responseJSON) }.bind(this)
|
||||
}));
|
||||
|
||||
new Ajax.Request("/response", extendDefault({
|
||||
parameters: Fixtures.jsonWithoutContentType,
|
||||
onComplete: function(transport) { this.assertNull(transport.responseJSON) }.bind(this)
|
||||
}));
|
||||
|
||||
new Ajax.Request("/response", extendDefault({
|
||||
sanitizeJSON: true,
|
||||
parameters: Fixtures.invalidJson,
|
||||
onException: function(request, error) {
|
||||
this.assert(error.message.include('Badly formed JSON string'));
|
||||
this.assertInstanceOf(Ajax.Request, request);
|
||||
}.bind(this)
|
||||
}));
|
||||
} else {
|
||||
this.info(message);
|
||||
}
|
||||
|
||||
new Ajax.Request("../fixtures/data.json", extendDefault({
|
||||
evalJSON: 'force',
|
||||
onComplete: function(transport) { this.assertEqual(123, transport.responseJSON.test) }.bind(this)
|
||||
}));
|
||||
},
|
||||
|
||||
testHeaderJSON: function() {
|
||||
if (this.isRunningFromRake) {
|
||||
new Ajax.Request("/response", extendDefault({
|
||||
parameters: Fixtures.headerJson,
|
||||
onComplete: function(transport, json) {
|
||||
this.assertEqual('hello #éà', transport.headerJSON.test);
|
||||
this.assertEqual('hello #éà', json.test);
|
||||
}.bind(this)
|
||||
}));
|
||||
|
||||
new Ajax.Request("/response", extendDefault({
|
||||
onComplete: function(transport, json) {
|
||||
this.assertNull(transport.headerJSON)
|
||||
this.assertNull(json)
|
||||
}.bind(this)
|
||||
}));
|
||||
} else {
|
||||
this.info(message);
|
||||
}
|
||||
},
|
||||
|
||||
testGetHeader: function() {
|
||||
if (this.isRunningFromRake) {
|
||||
new Ajax.Request("/response", extendDefault({
|
||||
parameters: { 'X-TEST': 'some value' },
|
||||
onComplete: function(transport) {
|
||||
this.assertEqual('some value', transport.getHeader('X-Test'));
|
||||
this.assertNull(transport.getHeader('X-Inexistant'));
|
||||
}.bind(this)
|
||||
}));
|
||||
} else {
|
||||
this.info(message);
|
||||
}
|
||||
},
|
||||
|
||||
testParametersCanBeHash: function() {
|
||||
if (this.isRunningFromRake) {
|
||||
new Ajax.Request("/response", extendDefault({
|
||||
parameters: $H({ "one": "two", "three": "four" }),
|
||||
onComplete: function(transport) {
|
||||
this.assertEqual("two", transport.getHeader("one"));
|
||||
this.assertEqual("four", transport.getHeader("three"));
|
||||
this.assertNull(transport.getHeader("toObject"));
|
||||
}.bind(this)
|
||||
}));
|
||||
} else {
|
||||
this.info(message);
|
||||
}
|
||||
},
|
||||
|
||||
testIsSameOriginMethod: function() {
|
||||
var isSameOrigin = Ajax.Request.prototype.isSameOrigin;
|
||||
this.assert(isSameOrigin.call({ url: '/foo/bar.html' }), '/foo/bar.html');
|
||||
this.assert(isSameOrigin.call({ url: window.location.toString() }), window.location);
|
||||
this.assert(!isSameOrigin.call({ url: 'http://example.com' }), 'http://example.com');
|
||||
|
||||
if (this.isRunningFromRake) {
|
||||
Ajax.Request.prototype.isSameOrigin = function() {
|
||||
return false
|
||||
};
|
||||
|
||||
$("content").update('same origin policy');
|
||||
new Ajax.Request("/response", extendDefault({
|
||||
parameters: Fixtures.js,
|
||||
onComplete: function(transport) {
|
||||
this.assertEqual("same origin policy", $("content").innerHTML);
|
||||
}.bind(this)
|
||||
}));
|
||||
|
||||
new Ajax.Request("/response", extendDefault({
|
||||
parameters: Fixtures.invalidJson,
|
||||
onException: function(request, error) {
|
||||
this.assert(error.message.include('Badly formed JSON string'));
|
||||
}.bind(this)
|
||||
}));
|
||||
|
||||
new Ajax.Request("/response", extendDefault({
|
||||
parameters: { 'X-JSON': '{});window.attacked = true;({}' },
|
||||
onException: function(request, error) {
|
||||
this.assert(error.message.include('Badly formed JSON string'));
|
||||
}.bind(this)
|
||||
}));
|
||||
|
||||
Ajax.Request.prototype.isSameOrigin = isSameOrigin;
|
||||
} else {
|
||||
this.info(message);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,196 +0,0 @@
|
|||
var globalArgsTest = 'nothing to see here';
|
||||
|
||||
new Test.Unit.Runner({
|
||||
test$A: function(){
|
||||
this.assertEnumEqual([], $A({}));
|
||||
},
|
||||
|
||||
testToArrayOnArguments: function(){
|
||||
function toArrayOnArguments(){
|
||||
globalArgsTest = $A(arguments);
|
||||
}
|
||||
toArrayOnArguments();
|
||||
this.assertEnumEqual([], globalArgsTest);
|
||||
toArrayOnArguments('foo');
|
||||
this.assertEnumEqual(['foo'], globalArgsTest);
|
||||
toArrayOnArguments('foo','bar');
|
||||
this.assertEnumEqual(['foo','bar'], globalArgsTest);
|
||||
},
|
||||
|
||||
testToArrayOnNodeList: function(){
|
||||
// direct HTML
|
||||
this.assertEqual(3, $A($('test_node').childNodes).length);
|
||||
|
||||
// DOM
|
||||
var element = document.createElement('div');
|
||||
element.appendChild(document.createTextNode('22'));
|
||||
(2).times(function(){ element.appendChild(document.createElement('span')) });
|
||||
this.assertEqual(3, $A(element.childNodes).length);
|
||||
|
||||
// HTML String
|
||||
element = document.createElement('div');
|
||||
$(element).update('22<span></span><span></span');
|
||||
this.assertEqual(3, $A(element.childNodes).length);
|
||||
},
|
||||
|
||||
testToArrayOnPrimitive: function() {
|
||||
this.assertEnumEqual(['a', 'b', 'c'], $A('abc'));
|
||||
this.assertEnumEqual([], $A(''));
|
||||
this.assertEnumEqual([], $A(null));
|
||||
this.assertEnumEqual([], $A(undefined));
|
||||
this.assertEnumEqual([], $A());
|
||||
this.assertEnumEqual([], $A(5));
|
||||
this.assertEnumEqual([], $A(true));
|
||||
},
|
||||
|
||||
testClear: function(){
|
||||
this.assertEnumEqual([], [].clear());
|
||||
this.assertEnumEqual([], [1].clear());
|
||||
this.assertEnumEqual([], [1,2].clear());
|
||||
},
|
||||
|
||||
testClone: function(){
|
||||
this.assertEnumEqual([], [].clone());
|
||||
this.assertEnumEqual([1], [1].clone());
|
||||
this.assertEnumEqual([1,2], [1,2].clone());
|
||||
this.assertEnumEqual([0,1,2], [0,1,2].clone());
|
||||
var a = [0,1,2];
|
||||
var b = a;
|
||||
this.assertIdentical(a, b);
|
||||
b = a.clone();
|
||||
this.assertNotIdentical(a, b);
|
||||
},
|
||||
|
||||
testFirst: function(){
|
||||
this.assertUndefined([].first());
|
||||
this.assertEqual(1, [1].first());
|
||||
this.assertEqual(1, [1,2].first());
|
||||
},
|
||||
|
||||
testLast: function(){
|
||||
this.assertUndefined([].last());
|
||||
this.assertEqual(1, [1].last());
|
||||
this.assertEqual(2, [1,2].last());
|
||||
},
|
||||
|
||||
testCompact: function(){
|
||||
this.assertEnumEqual([], [].compact());
|
||||
this.assertEnumEqual([1,2,3], [1,2,3].compact());
|
||||
this.assertEnumEqual([0,1,2,3], [0,null,1,2,undefined,3].compact());
|
||||
this.assertEnumEqual([1,2,3], [null,1,2,3,null].compact());
|
||||
},
|
||||
|
||||
testFlatten: function(){
|
||||
this.assertEnumEqual([], [].flatten());
|
||||
this.assertEnumEqual([1,2,3], [1,2,3].flatten());
|
||||
this.assertEnumEqual([1,2,3], [1,[[[2,3]]]].flatten());
|
||||
this.assertEnumEqual([1,2,3], [[1],[2],[3]].flatten());
|
||||
this.assertEnumEqual([1,2,3], [[[[[[[1]]]]]],2,3].flatten());
|
||||
},
|
||||
|
||||
testIndexOf: function(){
|
||||
this.assertEqual(-1, [].indexOf(1));
|
||||
this.assertEqual(-1, [0].indexOf(1));
|
||||
this.assertEqual(0, [1].indexOf(1));
|
||||
this.assertEqual(1, [0,1,2].indexOf(1));
|
||||
this.assertEqual(0, [1,2,1].indexOf(1));
|
||||
this.assertEqual(2, [1,2,1].indexOf(1, -1));
|
||||
this.assertEqual(1, [undefined,null].indexOf(null));
|
||||
},
|
||||
|
||||
testLastIndexOf: function(){
|
||||
this.assertEqual(-1,[].lastIndexOf(1));
|
||||
this.assertEqual(-1, [0].lastIndexOf(1));
|
||||
this.assertEqual(0, [1].lastIndexOf(1));
|
||||
this.assertEqual(2, [0,2,4,6].lastIndexOf(4));
|
||||
this.assertEqual(3, [4,4,2,4,6].lastIndexOf(4));
|
||||
this.assertEqual(3, [0,2,4,6].lastIndexOf(6,3));
|
||||
this.assertEqual(-1, [0,2,4,6].lastIndexOf(6,2));
|
||||
this.assertEqual(0, [6,2,4,6].lastIndexOf(6,2));
|
||||
|
||||
var fixture = [1,2,3,4,3];
|
||||
this.assertEqual(4, fixture.lastIndexOf(3));
|
||||
this.assertEnumEqual([1,2,3,4,3],fixture);
|
||||
|
||||
//tests from http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:lastIndexOf
|
||||
var array = [2, 5, 9, 2];
|
||||
this.assertEqual(3,array.lastIndexOf(2));
|
||||
this.assertEqual(-1,array.lastIndexOf(7));
|
||||
this.assertEqual(3,array.lastIndexOf(2,3));
|
||||
this.assertEqual(0,array.lastIndexOf(2,2));
|
||||
this.assertEqual(0,array.lastIndexOf(2,-2));
|
||||
this.assertEqual(3,array.lastIndexOf(2,-1));
|
||||
},
|
||||
|
||||
testInspect: function(){
|
||||
this.assertEqual('[]',[].inspect());
|
||||
this.assertEqual('[1]',[1].inspect());
|
||||
this.assertEqual('[\'a\']',['a'].inspect());
|
||||
this.assertEqual('[\'a\', 1]',['a',1].inspect());
|
||||
},
|
||||
|
||||
testIntersect: function(){
|
||||
this.assertEnumEqual([1,3], [1,1,3,5].intersect([1,2,3]));
|
||||
this.assertEnumEqual([1], [1,1].intersect([1,1]));
|
||||
this.assertEnumEqual([], [1,1,3,5].intersect([4]));
|
||||
this.assertEnumEqual([], [1].intersect(['1']));
|
||||
|
||||
this.assertEnumEqual(
|
||||
['B','C','D'],
|
||||
$R('A','Z').toArray().intersect($R('B','D').toArray())
|
||||
);
|
||||
},
|
||||
|
||||
testToJSON: function(){
|
||||
this.assertEqual('[]', [].toJSON());
|
||||
this.assertEqual('[\"a\"]', ['a'].toJSON());
|
||||
this.assertEqual('[\"a\", 1]', ['a', 1].toJSON());
|
||||
this.assertEqual('[\"a\", {\"b\": null}]', ['a', {'b': null}].toJSON());
|
||||
},
|
||||
|
||||
testReverse: function(){
|
||||
this.assertEnumEqual([], [].reverse());
|
||||
this.assertEnumEqual([1], [1].reverse());
|
||||
this.assertEnumEqual([2,1], [1,2].reverse());
|
||||
this.assertEnumEqual([3,2,1], [1,2,3].reverse());
|
||||
},
|
||||
|
||||
testSize: function(){
|
||||
this.assertEqual(4, [0, 1, 2, 3].size());
|
||||
this.assertEqual(0, [].size());
|
||||
},
|
||||
|
||||
testUniq: function(){
|
||||
this.assertEnumEqual([1], [1, 1, 1].uniq());
|
||||
this.assertEnumEqual([1], [1].uniq());
|
||||
this.assertEnumEqual([], [].uniq());
|
||||
this.assertEnumEqual([0, 1, 2, 3], [0, 1, 2, 2, 3, 0, 2].uniq());
|
||||
this.assertEnumEqual([0, 1, 2, 3], [0, 0, 1, 1, 2, 3, 3, 3].uniq(true));
|
||||
},
|
||||
|
||||
testWithout: function(){
|
||||
this.assertEnumEqual([], [].without(0));
|
||||
this.assertEnumEqual([], [0].without(0));
|
||||
this.assertEnumEqual([1], [0,1].without(0));
|
||||
this.assertEnumEqual([1,2], [0,1,2].without(0));
|
||||
},
|
||||
|
||||
test$w: function(){
|
||||
this.assertEnumEqual(['a', 'b', 'c', 'd'], $w('a b c d'));
|
||||
this.assertEnumEqual([], $w(' '));
|
||||
this.assertEnumEqual([], $w(''));
|
||||
this.assertEnumEqual([], $w(null));
|
||||
this.assertEnumEqual([], $w(undefined));
|
||||
this.assertEnumEqual([], $w());
|
||||
this.assertEnumEqual([], $w(10));
|
||||
this.assertEnumEqual(['a'], $w('a'));
|
||||
this.assertEnumEqual(['a'], $w('a '));
|
||||
this.assertEnumEqual(['a'], $w(' a'));
|
||||
this.assertEnumEqual(['a', 'b', 'c', 'd'], $w(' a b\nc\t\nd\n'));
|
||||
},
|
||||
|
||||
testConcat: function(){
|
||||
var args = (function() { return [].concat(arguments) })(1, 2);
|
||||
this.assertIdentical(1, args[0][0]);
|
||||
}
|
||||
});
|
|
@ -1,43 +0,0 @@
|
|||
new Test.Unit.Runner({
|
||||
testBrowserDetection: function() {
|
||||
var results = $H(Prototype.Browser).map(function(engine){
|
||||
return engine;
|
||||
}).partition(function(engine){
|
||||
return engine[1] === true
|
||||
});
|
||||
var trues = results[0], falses = results[1];
|
||||
|
||||
this.info('User agent string is: ' + navigator.userAgent);
|
||||
|
||||
this.assert(trues.size() == 0 || trues.size() == 1,
|
||||
'There should be only one or no browser detected.');
|
||||
|
||||
// we should have definite trues or falses here
|
||||
trues.each(function(result) {
|
||||
this.assert(result[1] === true);
|
||||
}, this);
|
||||
falses.each(function(result) {
|
||||
this.assert(result[1] === false);
|
||||
}, this);
|
||||
|
||||
if(navigator.userAgent.indexOf('AppleWebKit/') > -1) {
|
||||
this.info('Running on WebKit');
|
||||
this.assert(Prototype.Browser.WebKit);
|
||||
}
|
||||
|
||||
if(Object.prototype.toString.call(window.opera) === '[object Opera]') {
|
||||
this.info('Running on Opera');
|
||||
this.assert(Prototype.Browser.Opera);
|
||||
}
|
||||
|
||||
if(!!(window.attachEvent && !window.opera)) {
|
||||
this.info('Running on IE');
|
||||
this.assert(Prototype.Browser.IE);
|
||||
}
|
||||
|
||||
if(navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1) {
|
||||
this.info('Running on Gecko');
|
||||
this.assert(Prototype.Browser.Gecko);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,53 +0,0 @@
|
|||
{
|
||||
"description": "Extends the default HTML attribute whiteList for Prototype testing purposes.",
|
||||
|
||||
"inherits": [
|
||||
{
|
||||
"src": "resource:///html4-attributes.json"
|
||||
}
|
||||
],
|
||||
|
||||
"allowed": [
|
||||
"FORM:ACCEPT-CHARSET",
|
||||
"DIV:FOO",
|
||||
"DIV:BAR",
|
||||
"A:ACCESSKEY"
|
||||
],
|
||||
|
||||
"types": [
|
||||
{
|
||||
"key": "A:ACCESSKEY",
|
||||
"description": "Allow the A:ACCESSKEY accessibility key character",
|
||||
"pattern": ".?",
|
||||
"optional": true
|
||||
},
|
||||
|
||||
{
|
||||
"key": "INPUT:TYPE",
|
||||
"description": "Overwrite INPUT:TYPE rule to allow for DATE value.",
|
||||
"pattern": "TEXT|PASSWORD|CHECKBOX|RADIO|SUBMIT|RESET|FILE|HIDDEN|IMAGE|BUTTON|DATE",
|
||||
"optional": true
|
||||
},
|
||||
|
||||
{
|
||||
"key": "A:TABINDEX",
|
||||
"description": "Overwrite A:TABINDEX to allow for an empty value",
|
||||
"pattern": ".*",
|
||||
"optional": true
|
||||
},
|
||||
|
||||
{
|
||||
"key": "DIV:FOO",
|
||||
"description": "Custom BAR attribute.",
|
||||
"pattern": ".*",
|
||||
"optional": true
|
||||
},
|
||||
|
||||
{
|
||||
"key": "DIV:BAR",
|
||||
"description": "Custom FOO attribute",
|
||||
"pattern": ".*",
|
||||
"optional": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
new Test.Unit.Runner({
|
||||
testClassCreate: function() {
|
||||
this.assert(Object.isFunction(Animal), 'Animal is not a constructor');
|
||||
this.assertEnumEqual([Cat, Mouse, Dog, Ox], Animal.subclasses);
|
||||
Animal.subclasses.each(function(subclass) {
|
||||
this.assertEqual(Animal, subclass.superclass);
|
||||
}, this);
|
||||
|
||||
var Bird = Class.create(Animal);
|
||||
this.assertEqual(Bird, Animal.subclasses.last());
|
||||
// for..in loop (for some reason) doesn't iterate over the constructor property in top-level classes
|
||||
this.assertEnumEqual(Object.keys(new Animal).sort(), Object.keys(new Bird).without('constructor').sort());
|
||||
},
|
||||
|
||||
testClassInstantiation: function() {
|
||||
var pet = new Animal("Nibbles");
|
||||
this.assertEqual("Nibbles", pet.name, "property not initialized");
|
||||
this.assertEqual('Nibbles: Hi!', pet.say('Hi!'));
|
||||
this.assertEqual(Animal, pet.constructor, "bad constructor reference");
|
||||
this.assertUndefined(pet.superclass);
|
||||
|
||||
var Empty = Class.create();
|
||||
this.assert('object', typeof new Empty);
|
||||
},
|
||||
|
||||
testInheritance: function() {
|
||||
var tom = new Cat('Tom');
|
||||
this.assertEqual(Cat, tom.constructor, "bad constructor reference");
|
||||
this.assertEqual(Animal, tom.constructor.superclass, 'bad superclass reference');
|
||||
this.assertEqual('Tom', tom.name);
|
||||
this.assertEqual('Tom: meow', tom.say('meow'));
|
||||
this.assertEqual('Tom: Yuk! I only eat mice.', tom.eat(new Animal));
|
||||
},
|
||||
|
||||
testSuperclassMethodCall: function() {
|
||||
var tom = new Cat('Tom');
|
||||
this.assertEqual('Tom: Yum!', tom.eat(new Mouse));
|
||||
|
||||
// augment the constructor and test
|
||||
var Dodo = Class.create(Animal, {
|
||||
initialize: function($super, name) {
|
||||
$super(name);
|
||||
this.extinct = true;
|
||||
},
|
||||
|
||||
say: function($super, message) {
|
||||
return $super(message) + " honk honk";
|
||||
}
|
||||
});
|
||||
|
||||
var gonzo = new Dodo('Gonzo');
|
||||
this.assertEqual('Gonzo', gonzo.name);
|
||||
this.assert(gonzo.extinct, 'Dodo birds should be extinct');
|
||||
this.assertEqual("Gonzo: hello honk honk", gonzo.say("hello"));
|
||||
},
|
||||
|
||||
testClassAddMethods: function() {
|
||||
var tom = new Cat('Tom');
|
||||
var jerry = new Mouse('Jerry');
|
||||
|
||||
Animal.addMethods({
|
||||
sleep: function() {
|
||||
return this.say('ZZZ');
|
||||
}
|
||||
});
|
||||
|
||||
Mouse.addMethods({
|
||||
sleep: function($super) {
|
||||
return $super() + " ... no, can't sleep! Gotta steal cheese!";
|
||||
},
|
||||
escape: function(cat) {
|
||||
return this.say('(from a mousehole) Take that, ' + cat.name + '!');
|
||||
}
|
||||
});
|
||||
|
||||
this.assertEqual('Tom: ZZZ', tom.sleep(), "added instance method not available to subclass");
|
||||
this.assertEqual("Jerry: ZZZ ... no, can't sleep! Gotta steal cheese!", jerry.sleep());
|
||||
this.assertEqual("Jerry: (from a mousehole) Take that, Tom!", jerry.escape(tom));
|
||||
// insure that a method has not propagated *up* the prototype chain:
|
||||
this.assertUndefined(tom.escape);
|
||||
this.assertUndefined(new Animal().escape);
|
||||
|
||||
Animal.addMethods({
|
||||
sleep: function() {
|
||||
return this.say('zZzZ');
|
||||
}
|
||||
});
|
||||
|
||||
this.assertEqual("Jerry: zZzZ ... no, can't sleep! Gotta steal cheese!", jerry.sleep());
|
||||
},
|
||||
|
||||
testBaseClassWithMixin: function() {
|
||||
var grass = new Plant('grass', 3);
|
||||
this.assertRespondsTo('getValue', grass);
|
||||
this.assertEqual('#<Plant: grass>', grass.inspect());
|
||||
},
|
||||
|
||||
testSubclassWithMixin: function() {
|
||||
var snoopy = new Dog('Snoopy', 12, 'male');
|
||||
this.assertRespondsTo('reproduce', snoopy);
|
||||
},
|
||||
|
||||
testSubclassWithMixins: function() {
|
||||
var cow = new Ox('cow', 400, 'female');
|
||||
this.assertEqual('#<Ox: cow>', cow.inspect());
|
||||
this.assertRespondsTo('reproduce', cow);
|
||||
this.assertRespondsTo('getValue', cow);
|
||||
},
|
||||
|
||||
testClassWithToStringAndValueOfMethods: function() {
|
||||
var Foo = Class.create({
|
||||
toString: function() { return "toString" },
|
||||
valueOf: function() { return "valueOf" }
|
||||
});
|
||||
|
||||
var Bar = Class.create(Foo, {
|
||||
valueOf: function() { return "myValueOf" }
|
||||
});
|
||||
|
||||
var Parent = Class.create({
|
||||
m1: function(){ return 'm1' },
|
||||
m2: function(){ return 'm2' }
|
||||
});
|
||||
var Child = Class.create(Parent, {
|
||||
m1: function($super) { return 'm1 child' },
|
||||
m2: function($super) { return 'm2 child' }
|
||||
});
|
||||
|
||||
this.assert(new Child().m1.toString().indexOf('m1 child') > -1);
|
||||
|
||||
this.assertEqual("toString", new Foo().toString());
|
||||
this.assertEqual("valueOf", new Foo().valueOf());
|
||||
this.assertEqual("toString", new Bar().toString());
|
||||
this.assertEqual("myValueOf", new Bar().valueOf());
|
||||
}
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
new Test.Unit.Runner({
|
||||
testDateToJSON: function() {
|
||||
this.assertEqual('\"1970-01-01T00:00:00Z\"', new Date(Date.UTC(1970, 0, 1)).toJSON());
|
||||
}
|
||||
});
|
File diff suppressed because it is too large
Load Diff
|
@ -1,32 +0,0 @@
|
|||
new Test.Unit.Runner({
|
||||
testInput: function() {
|
||||
this.assert($("input").present != null);
|
||||
this.assert(typeof $("input").present == 'function');
|
||||
this.assert($("input").select != null);
|
||||
this.assertRespondsTo('present', Form.Element);
|
||||
this.assertRespondsTo('present', Form.Element.Methods);
|
||||
this.assertRespondsTo('coffee', $('input'));
|
||||
this.assertIdentical(Prototype.K, Form.Element.coffee);
|
||||
this.assertIdentical(Prototype.K, Form.Element.Methods.coffee);
|
||||
},
|
||||
|
||||
testForm: function() {
|
||||
this.assert($("form").reset != null);
|
||||
this.assert($("form").getInputs().length == 2);
|
||||
},
|
||||
|
||||
testEvent: function() {
|
||||
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() {
|
||||
this.assert($$("input").all(function(input) {
|
||||
return (input.focus != null);
|
||||
}));
|
||||
}
|
||||
});
|
|
@ -1,271 +0,0 @@
|
|||
function prime(value) {
|
||||
for (var i = 2; i < value; i++)
|
||||
if (value % i == 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
new Test.Unit.Runner({
|
||||
testEachBreak: function() {
|
||||
var result = 0;
|
||||
Fixtures.Basic.each(function(value) {
|
||||
if ((result = value) == 2) throw $break;
|
||||
});
|
||||
|
||||
this.assertEqual(2, result);
|
||||
},
|
||||
|
||||
testEachReturnActsAsContinue: function() {
|
||||
var results = [];
|
||||
Fixtures.Basic.each(function(value) {
|
||||
if (value == 2) return;
|
||||
results.push(value);
|
||||
});
|
||||
|
||||
this.assertEqual('1, 3', results.join(', '));
|
||||
},
|
||||
|
||||
testEachChaining: function() {
|
||||
this.assertEqual(Fixtures.Primes, Fixtures.Primes.each(Prototype.emptyFunction));
|
||||
this.assertEqual(3, Fixtures.Basic.each(Prototype.emptyFunction).length);
|
||||
},
|
||||
|
||||
testEnumContext: function() {
|
||||
var results = [];
|
||||
Fixtures.Basic.each(function(value) {
|
||||
results.push(value * this.i);
|
||||
}, { i: 2 });
|
||||
|
||||
this.assertEqual('2 4 6', results.join(' '));
|
||||
|
||||
this.assert(Fixtures.Basic.all(function(value){
|
||||
return value >= this.min && value <= this.max;
|
||||
}, { min: 1, max: 3 }));
|
||||
this.assert(!Fixtures.Basic.all(function(value){
|
||||
return value >= this.min && value <= this.max;
|
||||
}));
|
||||
this.assert(Fixtures.Basic.any(function(value){
|
||||
return value == this.target_value;
|
||||
}, { target_value: 2 }));
|
||||
},
|
||||
|
||||
testAny: function() {
|
||||
this.assert(!([].any()));
|
||||
|
||||
this.assert([true, true, true].any());
|
||||
this.assert([true, false, false].any());
|
||||
this.assert(![false, false, false].any());
|
||||
|
||||
this.assert(Fixtures.Basic.any(function(value) {
|
||||
return value > 2;
|
||||
}));
|
||||
this.assert(!Fixtures.Basic.any(function(value) {
|
||||
return value > 5;
|
||||
}));
|
||||
},
|
||||
|
||||
testAll: function() {
|
||||
this.assert([].all());
|
||||
|
||||
this.assert([true, true, true].all());
|
||||
this.assert(![true, false, false].all());
|
||||
this.assert(![false, false, false].all());
|
||||
|
||||
this.assert(Fixtures.Basic.all(function(value) {
|
||||
return value > 0;
|
||||
}));
|
||||
this.assert(!Fixtures.Basic.all(function(value) {
|
||||
return value > 1;
|
||||
}));
|
||||
},
|
||||
|
||||
testCollect: function() {
|
||||
this.assertEqual(Fixtures.Nicknames.join(', '),
|
||||
Fixtures.People.collect(function(person) {
|
||||
return person.nickname;
|
||||
}).join(", "));
|
||||
|
||||
this.assertEqual(26, Fixtures.Primes.map().length);
|
||||
},
|
||||
|
||||
testDetect: function() {
|
||||
this.assertEqual('Marcel Molina Jr.',
|
||||
Fixtures.People.detect(function(person) {
|
||||
return person.nickname.match(/no/);
|
||||
}).name);
|
||||
},
|
||||
|
||||
testEachSlice: function() {
|
||||
this.assertEnumEqual([], [].eachSlice(2));
|
||||
this.assertEqual(1, [1].eachSlice(1).length);
|
||||
this.assertEnumEqual([1], [1].eachSlice(1)[0]);
|
||||
this.assertEqual(2, Fixtures.Basic.eachSlice(2).length);
|
||||
this.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()
|
||||
);
|
||||
this.assertEnumEqual(Fixtures.Basic, Fixtures.Basic.eachSlice(-10));
|
||||
this.assertEnumEqual(Fixtures.Basic, Fixtures.Basic.eachSlice(0));
|
||||
this.assertNotIdentical(Fixtures.Basic, Fixtures.Basic.eachSlice(0));
|
||||
},
|
||||
|
||||
testEachWithIndex: function() {
|
||||
var nicknames = [], indexes = [];
|
||||
Fixtures.People.each(function(person, index) {
|
||||
nicknames.push(person.nickname);
|
||||
indexes.push(index);
|
||||
});
|
||||
|
||||
this.assertEqual(Fixtures.Nicknames.join(', '),
|
||||
nicknames.join(', '));
|
||||
this.assertEqual('0, 1, 2, 3', indexes.join(', '));
|
||||
},
|
||||
|
||||
testFindAll: function() {
|
||||
this.assertEqual(Fixtures.Primes.join(', '),
|
||||
Fixtures.Z.findAll(prime).join(', '));
|
||||
},
|
||||
|
||||
testGrep: function() {
|
||||
this.assertEqual('noradio, htonl',
|
||||
Fixtures.Nicknames.grep(/o/).join(", "));
|
||||
|
||||
this.assertEqual('NORADIO, HTONL',
|
||||
Fixtures.Nicknames.grep(/o/, function(nickname) {
|
||||
return nickname.toUpperCase();
|
||||
}).join(", "))
|
||||
|
||||
this.assertEnumEqual($('grepHeader', 'grepCell'),
|
||||
$('grepTable', 'grepTBody', 'grepRow', 'grepHeader', 'grepCell').grep(new Selector('.cell')));
|
||||
|
||||
// troublesome characters
|
||||
this.assertEnumEqual(['?a', 'c?'], ['?a','b','c?'].grep('?'));
|
||||
this.assertEnumEqual(['*a', 'c*'], ['*a','b','c*'].grep('*'));
|
||||
this.assertEnumEqual(['+a', 'c+'], ['+a','b','c+'].grep('+'));
|
||||
this.assertEnumEqual(['{1}a', 'c{1}'], ['{1}a','b','c{1}'].grep('{1}'));
|
||||
this.assertEnumEqual(['(a', 'c('], ['(a','b','c('].grep('('));
|
||||
this.assertEnumEqual(['|a', 'c|'], ['|a','b','c|'].grep('|'));
|
||||
},
|
||||
|
||||
testInclude: function() {
|
||||
this.assert(Fixtures.Nicknames.include('sam-'));
|
||||
this.assert(Fixtures.Nicknames.include('noradio'));
|
||||
this.assert(!Fixtures.Nicknames.include('gmosx'));
|
||||
this.assert(Fixtures.Basic.include(2));
|
||||
this.assert(Fixtures.Basic.include('2'));
|
||||
this.assert(!Fixtures.Basic.include('4'));
|
||||
},
|
||||
|
||||
testInGroupsOf: function() {
|
||||
this.assertEnumEqual([], [].inGroupsOf(3));
|
||||
|
||||
var arr = [1, 2, 3, 4, 5, 6].inGroupsOf(3);
|
||||
this.assertEqual(2, arr.length);
|
||||
this.assertEnumEqual([1, 2, 3], arr[0]);
|
||||
this.assertEnumEqual([4, 5, 6], arr[1]);
|
||||
|
||||
arr = [1, 2, 3, 4, 5, 6].inGroupsOf(4);
|
||||
this.assertEqual(2, arr.length);
|
||||
this.assertEnumEqual([1, 2, 3, 4], arr[0]);
|
||||
this.assertEnumEqual([5, 6, null, null], arr[1]);
|
||||
|
||||
var basic = Fixtures.Basic
|
||||
|
||||
arr = basic.inGroupsOf(4,'x');
|
||||
this.assertEqual(1, arr.length);
|
||||
this.assertEnumEqual([1, 2, 3, 'x'], arr[0]);
|
||||
|
||||
this.assertEnumEqual([1,2,3,'a'], basic.inGroupsOf(2, 'a').flatten());
|
||||
|
||||
arr = basic.inGroupsOf(5, '');
|
||||
this.assertEqual(1, arr.length);
|
||||
this.assertEnumEqual([1, 2, 3, '', ''], arr[0]);
|
||||
|
||||
this.assertEnumEqual([1,2,3,0], basic.inGroupsOf(2, 0).flatten());
|
||||
this.assertEnumEqual([1,2,3,false], basic.inGroupsOf(2, false).flatten());
|
||||
},
|
||||
|
||||
testInject: function() {
|
||||
this.assertEqual(1061,
|
||||
Fixtures.Primes.inject(0, function(sum, value) {
|
||||
return sum + value;
|
||||
}));
|
||||
},
|
||||
|
||||
testInvoke: function() {
|
||||
var result = [[2, 1, 3], [6, 5, 4]].invoke('sort');
|
||||
this.assertEqual(2, result.length);
|
||||
this.assertEqual('1, 2, 3', result[0].join(', '));
|
||||
this.assertEqual('4, 5, 6', result[1].join(', '));
|
||||
|
||||
result = result.invoke('invoke', 'toString', 2);
|
||||
this.assertEqual('1, 10, 11', result[0].join(', '));
|
||||
this.assertEqual('100, 101, 110', result[1].join(', '));
|
||||
},
|
||||
|
||||
testMax: function() {
|
||||
this.assertEqual(100, Fixtures.Z.max());
|
||||
this.assertEqual(97, Fixtures.Primes.max());
|
||||
this.assertEqual(2, [ -9, -8, -7, -6, -4, -3, -2, 0, -1, 2 ].max());
|
||||
this.assertEqual('sam-', Fixtures.Nicknames.max()); // ?s > ?U
|
||||
},
|
||||
|
||||
testMin: function() {
|
||||
this.assertEqual(1, Fixtures.Z.min());
|
||||
this.assertEqual(0, [ 1, 2, 3, 4, 5, 6, 7, 8, 0, 9 ].min());
|
||||
this.assertEqual('Ulysses', Fixtures.Nicknames.min()); // ?U < ?h
|
||||
},
|
||||
|
||||
testPartition: function() {
|
||||
var result = Fixtures.People.partition(function(person) {
|
||||
return person.name.length < 15;
|
||||
}).invoke('pluck', 'nickname');
|
||||
|
||||
this.assertEqual(2, result.length);
|
||||
this.assertEqual('sam-, htonl', result[0].join(', '));
|
||||
this.assertEqual('noradio, Ulysses', result[1].join(', '));
|
||||
},
|
||||
|
||||
testPluck: function() {
|
||||
this.assertEqual(Fixtures.Nicknames.join(', '),
|
||||
Fixtures.People.pluck('nickname').join(', '));
|
||||
},
|
||||
|
||||
testReject: function() {
|
||||
this.assertEqual(0,
|
||||
Fixtures.Nicknames.reject(Prototype.K).length);
|
||||
|
||||
this.assertEqual('sam-, noradio, htonl',
|
||||
Fixtures.Nicknames.reject(function(nickname) {
|
||||
return nickname != nickname.toLowerCase();
|
||||
}).join(', '));
|
||||
},
|
||||
|
||||
testSortBy: function() {
|
||||
this.assertEqual('htonl, noradio, sam-, Ulysses',
|
||||
Fixtures.People.sortBy(function(value) {
|
||||
return value.nickname.toLowerCase();
|
||||
}).pluck('nickname').join(', '));
|
||||
},
|
||||
|
||||
testToArray: function() {
|
||||
var result = Fixtures.People.toArray();
|
||||
this.assert(result != Fixtures.People); // they're different objects...
|
||||
this.assertEqual(Fixtures.Nicknames.join(', '),
|
||||
result.pluck('nickname').join(', ')); // but the values are the same
|
||||
},
|
||||
|
||||
testZip: function() {
|
||||
var result = [1, 2, 3].zip([4, 5, 6], [7, 8, 9]);
|
||||
this.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() });
|
||||
this.assertEqual('[[7, 4, 1], [8, 5, 2], [9, 6, 3]]', result.inspect());
|
||||
},
|
||||
|
||||
testSize: function() {
|
||||
this.assertEqual(4, Fixtures.People.size());
|
||||
this.assertEqual(4, Fixtures.Nicknames.size());
|
||||
this.assertEqual(26, Fixtures.Primes.size());
|
||||
this.assertEqual(0, [].size());
|
||||
}
|
||||
});
|
|
@ -1,255 +0,0 @@
|
|||
var documentLoaded = document.loaded;
|
||||
|
||||
new Test.Unit.Runner({
|
||||
|
||||
// test firing an event and observing it on the element it's fired from
|
||||
testCustomEventFiring: function() {
|
||||
var span = $("span"), fired = false, observer = function(event) {
|
||||
this.assertEqual(span, event.element());
|
||||
this.assertEqual(1, event.memo.index);
|
||||
fired = true;
|
||||
}.bind(this);
|
||||
|
||||
span.observe("test:somethingHappened", observer);
|
||||
span.fire("test:somethingHappened", { index: 1 });
|
||||
this.assert(fired);
|
||||
|
||||
fired = false;
|
||||
span.fire("test:somethingElseHappened");
|
||||
this.assert(!fired);
|
||||
|
||||
span.stopObserving("test:somethingHappened", observer);
|
||||
span.fire("test:somethingHappened");
|
||||
this.assert(!fired);
|
||||
},
|
||||
|
||||
// test firing an event and observing it on a containing element
|
||||
testCustomEventBubbling: function() {
|
||||
var span = $("span"), outer = $("outer"), fired = false, observer = function(event) {
|
||||
this.assertEqual(span, event.element());
|
||||
fired = true;
|
||||
}.bind(this);
|
||||
|
||||
outer.observe("test:somethingHappened", observer);
|
||||
span.fire("test:somethingHappened");
|
||||
this.assert(fired);
|
||||
|
||||
fired = false;
|
||||
span.fire("test:somethingElseHappened");
|
||||
this.assert(!fired);
|
||||
|
||||
outer.stopObserving("test:somethingHappened", observer);
|
||||
span.fire("test:somethingHappened");
|
||||
this.assert(!fired);
|
||||
},
|
||||
|
||||
testCustomEventCanceling: function() {
|
||||
var span = $("span"), outer = $("outer"), inner = $("inner");
|
||||
var fired = false, stopped = false;
|
||||
|
||||
function outerObserver(event) {
|
||||
fired = span == event.element();
|
||||
}
|
||||
|
||||
function innerObserver(event) {
|
||||
event.stop();
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
inner.observe("test:somethingHappened", innerObserver);
|
||||
outer.observe("test:somethingHappened", outerObserver);
|
||||
span.fire("test:somethingHappened");
|
||||
this.assert(stopped);
|
||||
this.assert(!fired);
|
||||
|
||||
fired = stopped = false;
|
||||
inner.stopObserving("test:somethingHappened", innerObserver);
|
||||
span.fire("test:somethingHappened");
|
||||
this.assert(!stopped);
|
||||
this.assert(fired);
|
||||
|
||||
outer.stopObserving("test:somethingHappened", outerObserver);
|
||||
},
|
||||
|
||||
testEventObjectIsExtended: function() {
|
||||
var span = $("span"), event, observedEvent, observer = function(e) { observedEvent = e };
|
||||
span.observe("test:somethingHappened", observer);
|
||||
event = span.fire("test:somethingHappened");
|
||||
this.assertEqual(event, observedEvent);
|
||||
this.assertEqual(Event.Methods.stop.methodize(), event.stop);
|
||||
span.stopObserving("test:somethingHappened", observer);
|
||||
|
||||
event = span.fire("test:somethingHappenedButNoOneIsListening");
|
||||
this.assertEqual(Event.Methods.stop.methodize(), event.stop);
|
||||
},
|
||||
|
||||
testEventObserversAreBoundToTheObservedElement: function() {
|
||||
var span = $("span"), target, observer = function() { target = this };
|
||||
|
||||
span.observe("test:somethingHappened", observer);
|
||||
span.fire("test:somethingHappened");
|
||||
span.stopObserving("test:somethingHappened", observer);
|
||||
this.assertEqual(span, target);
|
||||
target = null;
|
||||
|
||||
var outer = $("outer");
|
||||
outer.observe("test:somethingHappened", observer);
|
||||
span.fire("test:somethingHappened");
|
||||
outer.stopObserving("test:somethingHappened", observer);
|
||||
this.assertEqual(outer, target);
|
||||
},
|
||||
|
||||
testMultipleCustomEventObserversWithTheSameHandler: function() {
|
||||
var span = $("span"), count = 0, observer = function() { count++ };
|
||||
|
||||
span.observe("test:somethingHappened", observer);
|
||||
span.observe("test:somethingElseHappened", observer);
|
||||
span.fire("test:somethingHappened");
|
||||
this.assertEqual(1, count);
|
||||
span.fire("test:somethingElseHappened");
|
||||
this.assertEqual(2, count);
|
||||
},
|
||||
|
||||
testMultipleEventHandlersCanBeAddedAndRemovedFromAnElement: function() {
|
||||
var span = $("span"), count1 = 0, count2 = 0;
|
||||
var observer1 = function() { count1++ };
|
||||
var observer2 = function() { count2++ };
|
||||
|
||||
span.observe("test:somethingHappened", observer1);
|
||||
span.observe("test:somethingHappened", observer2);
|
||||
span.fire("test:somethingHappened");
|
||||
this.assertEqual(1, count1);
|
||||
this.assertEqual(1, count2);
|
||||
|
||||
span.stopObserving("test:somethingHappened", observer1);
|
||||
span.stopObserving("test:somethingHappened", observer2);
|
||||
span.fire("test:somethingHappened");
|
||||
this.assertEqual(1, count1);
|
||||
this.assertEqual(1, count2);
|
||||
},
|
||||
|
||||
testStopObservingWithoutArguments: function() {
|
||||
var span = $("span"), count = 0, observer = function() { count++ };
|
||||
|
||||
span.observe("test:somethingHappened", observer);
|
||||
span.observe("test:somethingElseHappened", observer);
|
||||
span.stopObserving();
|
||||
span.fire("test:somethingHappened");
|
||||
this.assertEqual(0, count);
|
||||
span.fire("test:somethingElseHappened");
|
||||
this.assertEqual(0, count);
|
||||
},
|
||||
|
||||
testStopObservingWithoutHandlerArgument: function() {
|
||||
var span = $("span"), count = 0, observer = function() { count++ };
|
||||
|
||||
span.observe("test:somethingHappened", observer);
|
||||
span.observe("test:somethingElseHappened", observer);
|
||||
span.stopObserving("test:somethingHappened");
|
||||
span.fire("test:somethingHappened");
|
||||
this.assertEqual(0, count);
|
||||
span.fire("test:somethingElseHappened");
|
||||
this.assertEqual(1, count);
|
||||
span.stopObserving("test:somethingElseHappened");
|
||||
span.fire("test:somethingElseHappened");
|
||||
this.assertEqual(1, count);
|
||||
},
|
||||
|
||||
testStopObservingRemovesHandlerFromCache: function() {
|
||||
var span = $("span"), observer = Prototype.emptyFunction, eventID;
|
||||
|
||||
span.observe("test:somethingHappened", observer);
|
||||
|
||||
var registry = span.getStorage().get('prototype_event_registry');
|
||||
|
||||
this.assert(registry);
|
||||
this.assert(Object.isArray(registry.get('test:somethingHappened')));
|
||||
this.assertEqual(1, registry.get('test:somethingHappened').length);
|
||||
|
||||
span.stopObserving("test:somethingHappened", observer);
|
||||
|
||||
registry = span.getStorage().get('prototype_event_registry');
|
||||
|
||||
this.assert(registry);
|
||||
this.assert(Object.isArray(registry.get('test:somethingHappened')));
|
||||
this.assertEqual(0, registry.get('test:somethingHappened').length);
|
||||
},
|
||||
|
||||
testObserveAndStopObservingAreChainable: function() {
|
||||
var span = $("span"), observer = Prototype.emptyFunction;
|
||||
|
||||
this.assertEqual(span, span.observe("test:somethingHappened", observer));
|
||||
this.assertEqual(span, span.stopObserving("test:somethingHappened", observer));
|
||||
|
||||
span.observe("test:somethingHappened", observer);
|
||||
this.assertEqual(span, span.stopObserving("test:somethingHappened"));
|
||||
|
||||
this.assertEqual(span, span.stopObserving("test:somethingOtherHappened", observer));
|
||||
|
||||
span.observe("test:somethingHappened", observer);
|
||||
this.assertEqual(span, span.stopObserving());
|
||||
this.assertEqual(span, span.stopObserving()); // assert it again, after there are no observers
|
||||
|
||||
span.observe("test:somethingHappened", observer);
|
||||
this.assertEqual(span, span.observe("test:somethingHappened", observer)); // try to reuse the same observer
|
||||
span.stopObserving();
|
||||
},
|
||||
|
||||
testDocumentLoaded: function() {
|
||||
this.assert(!documentLoaded);
|
||||
this.assert(document.loaded);
|
||||
},
|
||||
|
||||
testDocumentContentLoadedEventFiresBeforeWindowLoad: function() {
|
||||
this.assert(eventResults.contentLoaded, "contentLoaded");
|
||||
this.assert(eventResults.contentLoaded.endOfDocument, "contentLoaded.endOfDocument");
|
||||
this.assert(!eventResults.contentLoaded.windowLoad, "!contentLoaded.windowLoad");
|
||||
this.assert(eventResults.windowLoad, "windowLoad");
|
||||
this.assert(eventResults.windowLoad.endOfDocument, "windowLoad.endOfDocument");
|
||||
this.assert(eventResults.windowLoad.contentLoaded, "windowLoad.contentLoaded");
|
||||
},
|
||||
|
||||
testEventStopped: function() {
|
||||
var span = $("span"), event;
|
||||
|
||||
span.observe("test:somethingHappened", Prototype.emptyFunction);
|
||||
event = span.fire("test:somethingHappened");
|
||||
this.assert(!event.stopped, "event.stopped should be false with an empty observer");
|
||||
span.stopObserving("test:somethingHappened");
|
||||
|
||||
span.observe("test:somethingHappened", function(e) { e.stop() });
|
||||
event = span.fire("test:somethingHappened");
|
||||
this.assert(event.stopped, "event.stopped should be true for an observer that calls stop");
|
||||
span.stopObserving("test:somethingHappened");
|
||||
},
|
||||
|
||||
testEventFindElement: function() {
|
||||
var span = $("span"), event;
|
||||
event = span.fire("test:somethingHappened");
|
||||
this.assertElementMatches(event.findElement(), 'span#span');
|
||||
this.assertElementMatches(event.findElement('span'), 'span#span');
|
||||
this.assertElementMatches(event.findElement('p'), 'p#inner');
|
||||
this.assertEqual(null, event.findElement('div.does_not_exist'));
|
||||
this.assertElementMatches(event.findElement('.does_not_exist, span'), 'span#span');
|
||||
},
|
||||
|
||||
testEventIDDuplication: function() {
|
||||
$('container').down().observe("test:somethingHappened", Prototype.emptyFunction);
|
||||
$('container').innerHTML += $('container').innerHTML;
|
||||
this.assertUndefined($('container').down(1)._prototypeEventID);
|
||||
}
|
||||
});
|
||||
|
||||
document.observe("dom:loaded", function(event) {
|
||||
eventResults.contentLoaded = {
|
||||
endOfDocument: eventResults.endOfDocument,
|
||||
windowLoad: eventResults.windowLoad
|
||||
};
|
||||
});
|
||||
|
||||
Event.observe(window, "load", function(event) {
|
||||
eventResults.windowLoad = {
|
||||
endOfDocument: eventResults.endOfDocument,
|
||||
contentLoaded: eventResults.contentLoaded
|
||||
};
|
||||
});
|
|
@ -1,2 +0,0 @@
|
|||
<div id="content"></div>
|
||||
<div id="content2" style="color:red"></div>
|
|
@ -1,42 +0,0 @@
|
|||
var Fixtures = {
|
||||
js: {
|
||||
responseBody: '$("content").update("<H2>Hello world!</H2>");',
|
||||
'Content-Type': ' text/javascript '
|
||||
},
|
||||
|
||||
html: {
|
||||
responseBody: "Pack my box with <em>five dozen</em> liquor jugs! " +
|
||||
"Oh, how <strong>quickly</strong> daft jumping zebras vex..."
|
||||
},
|
||||
|
||||
xml: {
|
||||
responseBody: '<?xml version="1.0" encoding="UTF-8" ?><name attr="foo">bar</name>',
|
||||
'Content-Type': 'application/xml'
|
||||
},
|
||||
|
||||
json: {
|
||||
responseBody: '{\n\r"test": 123}',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
|
||||
jsonWithoutContentType: {
|
||||
responseBody: '{"test": 123}'
|
||||
},
|
||||
|
||||
invalidJson: {
|
||||
responseBody: '{});window.attacked = true;({}',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
|
||||
headerJson: {
|
||||
'X-JSON': '{"test": "hello #éà"}'
|
||||
}
|
||||
};
|
||||
|
||||
var responderCounter = 0;
|
||||
|
||||
// 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();
|
||||
|
||||
var message = 'You must be running your tests from rake to test this feature.';
|
|
@ -1 +0,0 @@
|
|||
<div id="test_node">22<span id="span_1"></span><span id="span_2"></span></div>
|
|
@ -1,83 +0,0 @@
|
|||
// base class
|
||||
var Animal = Class.create({
|
||||
initialize: function(name) {
|
||||
this.name = name;
|
||||
},
|
||||
name: "",
|
||||
eat: function() {
|
||||
return this.say("Yum!");
|
||||
},
|
||||
say: function(message) {
|
||||
return this.name + ": " + message;
|
||||
}
|
||||
});
|
||||
|
||||
// subclass that augments a method
|
||||
var Cat = Class.create(Animal, {
|
||||
eat: function($super, food) {
|
||||
if (food instanceof Mouse) return $super();
|
||||
else return this.say("Yuk! I only eat mice.");
|
||||
}
|
||||
});
|
||||
|
||||
// empty subclass
|
||||
var Mouse = Class.create(Animal, {});
|
||||
|
||||
//mixins
|
||||
var Sellable = {
|
||||
getValue: function(pricePerKilo) {
|
||||
return this.weight * pricePerKilo;
|
||||
},
|
||||
|
||||
inspect: function() {
|
||||
return '#<Sellable: #{weight}kg>'.interpolate(this);
|
||||
}
|
||||
};
|
||||
|
||||
var Reproduceable = {
|
||||
reproduce: function(partner) {
|
||||
if (partner.constructor != this.constructor || partner.sex == this.sex)
|
||||
return null;
|
||||
var weight = this.weight / 10, sex = Math.random(1).round() ? 'male' : 'female';
|
||||
return new this.constructor('baby', weight, sex);
|
||||
}
|
||||
};
|
||||
|
||||
// base class with mixin
|
||||
var Plant = Class.create(Sellable, {
|
||||
initialize: function(name, weight) {
|
||||
this.name = name;
|
||||
this.weight = weight;
|
||||
},
|
||||
|
||||
inspect: function() {
|
||||
return '#<Plant: #{name}>'.interpolate(this);
|
||||
}
|
||||
});
|
||||
|
||||
// subclass with mixin
|
||||
var Dog = Class.create(Animal, Reproduceable, {
|
||||
initialize: function($super, name, weight, sex) {
|
||||
this.weight = weight;
|
||||
this.sex = sex;
|
||||
$super(name);
|
||||
}
|
||||
});
|
||||
|
||||
// subclass with mixins
|
||||
var Ox = Class.create(Animal, Sellable, Reproduceable, {
|
||||
initialize: function($super, name, weight, sex) {
|
||||
this.weight = weight;
|
||||
this.sex = sex;
|
||||
$super(name);
|
||||
},
|
||||
|
||||
eat: function(food) {
|
||||
if (food instanceof Plant)
|
||||
this.weight += food.weight;
|
||||
},
|
||||
|
||||
inspect: function() {
|
||||
return '#<Ox: #{name}>'.interpolate(this);
|
||||
}
|
||||
});
|
|
@ -1 +0,0 @@
|
|||
Pack my box with <em>five dozen</em> liquor jugs! Oh, how <strong>quickly</strong> daft jumping zebras vex...
|
|
@ -1 +0,0 @@
|
|||
{test: 123}
|
|
@ -1,84 +0,0 @@
|
|||
#style_test_1 { cursor: pointer; font-size:12px;}
|
||||
div.style-test { margin-left: 1px }
|
||||
|
||||
#style_test_dimensions_container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 500px;
|
||||
width: 20px;
|
||||
height: 30px;
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
border: 3px solid red;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
#notInlineAbsoluted { position: absolute; }
|
||||
|
||||
#elementToViewportDimensions {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
/* for scroll test on really big screens */
|
||||
body {
|
||||
height: 40000px;
|
||||
}
|
|
@ -1,278 +0,0 @@
|
|||
<div id="scroll_test_1">
|
||||
<p id="scroll_test_2">Scroll test</p>
|
||||
</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="testdiv-replace-container-element"><div id="testdiv-replace-element"></div></div>
|
||||
<div id="testdiv-replace-container-toelement"><div id="testdiv-replace-toelement"></div></div>
|
||||
<div id="testdiv-replace-container-tohtml"><div id="testdiv-replace-tohtml"></div></div>
|
||||
<div id="testtable-replace-container"><table id="testtable-replace"></table></div>
|
||||
<table id="testrow-replace-container"><tr id="testrow-replace"></tr></table>
|
||||
<select id="testoption-replace-container"><option id="testoption-replace"></option><option>stays</option></select>
|
||||
<div id="testform-replace-container"><p>some text</p><form id="testform-replace"><input id="testinput-replace" type="text" /></form><p>some text</p></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="element_reextend_test"><div id="discard_1"></div></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">
|
||||
<!-- comment node to screw things up -->
|
||||
<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>
|
||||
<li class="1"></li>
|
||||
</ul>
|
||||
<div class="B C D"></div>
|
||||
<div id="unextended"></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>
|
||||
<span id="style_test_4">blah</span>
|
||||
<span id="style_test_5">blah</span>
|
||||
|
||||
<div id="style_test_dimensions_container">
|
||||
<div id="style_test_dimensions" style="background:#ddd;padding:1px;margin:1px;border:1px solid #00f"><div style="height:5px;background:#eee;width:5px;padding:2px;margin:2px;border:2px solid #0f0"> </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="test_csstext_1">test_csstext_1</div>
|
||||
<div id="test_csstext_2">test_csstext_2</div>
|
||||
<div id="test_csstext_3" style="border: 1px solid red">test_csstext_3</div>
|
||||
<div id="test_csstext_4" style="font-size: 20px">test_csstext_4</div>
|
||||
<div id="test_csstext_5">test_csstext_5</div>
|
||||
|
||||
<div id="custom_attributes">
|
||||
<div foo="1" bar="2"></div>
|
||||
<div foo="2"></div>
|
||||
</div>
|
||||
|
||||
<div id="cloned_element_attributes_issue" foo="original"></div>
|
||||
<a id="attributes_with_issues_1" href="test.html" accesskey="L" tabindex="50" title="a link" onclick="alert('hello world');"></a>
|
||||
<a id="attributes_with_issues_2" href="" accesskey="" tabindex="" title=""></a>
|
||||
<a id="attributes_with_issues_3"></a>
|
||||
<form id="attributes_with_issues_form" method="post" action="blah">
|
||||
<input type="checkbox" id="attributes_with_issues_checked" name="a" checked="checked"/>
|
||||
<input type="checkbox" id="attributes_with_issues_disabled" name="b" checked="checked" disabled="disabled"/>
|
||||
<input type="text" id="attributes_with_issues_readonly" name="c" readonly="readonly" value="blech"/>
|
||||
<input type="date" id="attributes_with_issues_type" value="blech" />
|
||||
<select id="attributes_with_issues_multiple" name="e" multiple="multiple">
|
||||
<option>blech</option>
|
||||
<option>blah</option>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<!-- writeAttributes -->
|
||||
<p id="write_attribute_para"></p>
|
||||
<a id="write_attribute_link" href="test.html"></a>
|
||||
<form action="/dev/null" id="write_attribute_form" method="get" accept-charset="utf-8">
|
||||
<label id="write_attribute_label"></label>
|
||||
<input type="checkbox" name="write_attribute_checkbox" value="" id="write_attribute_checkbox">
|
||||
<input type="checkbox" checked="checked" name="write_attribute_checked_checkbox" value="" id="write_attribute_checked_checkbox">
|
||||
<input type="text" name="write_attribute_input" value="" id="write_attribute_input">
|
||||
<select id="write_attribute_select">
|
||||
<option>Cat</option>
|
||||
<option>Dog</option>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<table id="write_attribute_table" cellpadding="6" cellspacing="4">
|
||||
<tr><td id="write_attribute_td">A</td><td>B</td></tr>
|
||||
<tr><td>C</td></tr>
|
||||
<tr><td>D</td><td>E</td><td>F</td></tr>
|
||||
</table>
|
||||
|
||||
<div id="dom_attribute_precedence">
|
||||
<form action="blech" method="post">
|
||||
<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>
|
||||
|
||||
<div id="dimensions-nester" style="width: 500px;">
|
||||
<div id="dimensions-nestee" style="display: none">This is a nested DIV. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</div>
|
||||
</div>
|
||||
|
||||
|
||||
<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 id="grand-child">
|
||||
<div id="great-grand-child"></div>
|
||||
</div></div><!-- intentional formatting; don't change this line -->
|
||||
<div id="sibling"><div id="grand-sibling"></div></div>
|
||||
</div>
|
||||
<div id="not-in-the-family"></div>
|
||||
|
||||
<div id="insertions-container"><div id="insertions-main"><p>some content.</p></div></div>
|
||||
<div id="insertions-node-container"><div id="insertions-node-main"><p>some content.</p></div></div>
|
||||
<div id="element-insertions-container"><div id="element-insertions-main"><p>some content.</p></div></div>
|
||||
<div id="element-insertions-multiple-container"><div id="element-insertions-multiple-main"><p>some content.</p></div></div>
|
||||
<table id="table_for_insertions"></table>
|
||||
<table id="table_for_row_insertions"><tr id="row_1"></tr></table>
|
||||
<form method="post" action="blah">
|
||||
<select id="select_for_update" name="select_for_update">
|
||||
<option>option 1</option>
|
||||
<option>option 2</option>
|
||||
</select>
|
||||
<select id="select_for_insert_bottom" name="select_for_insert_bottom">
|
||||
<option>option 1</option>
|
||||
<option>option 2</option>
|
||||
</select>
|
||||
<select id="select_for_insert_top" name="select_for_insert_top">
|
||||
<option>option 1</option>
|
||||
<option>option 2</option>
|
||||
</select>
|
||||
</form>
|
||||
<div id="wrap-container"><p id="wrap"></p></div>
|
||||
|
||||
<!-- Positioning methods bench -->
|
||||
<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;font-size:2px">test<span id="inline">test</span></div>
|
||||
<div id="absolute_relative_undefined">XYZ</div>
|
||||
</div>
|
||||
<div id="absolute_fixed" style="position: fixed; top: 10px; left: 10px">
|
||||
<span id="absolute_fixed_absolute" style="position: absolute; top: 10px; left: 10px">foo</span>
|
||||
<span id="absolute_fixed_undefined" style="display:block">bar</span>
|
||||
</div></div>
|
||||
<div id="notInlineAbsoluted"></div>
|
||||
<div id="inlineAbsoluted" style="position: absolute"></div>
|
||||
|
||||
<div id="unextended"></div>
|
||||
<div id="identification">
|
||||
<div id="predefined_id"></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div id="anonymous_element_3"></div>
|
||||
</div>
|
||||
|
||||
<div id='elementToViewportDimensions' style='display: none'></div>
|
||||
<div id="auto_dimensions" style="height:auto"></div>
|
|
@ -1,17 +0,0 @@
|
|||
var testVar = 'to be updated', testVar2 = '', documentViewportProperties;
|
||||
|
||||
Element.addMethods({
|
||||
hashBrowns: function(element) { return 'hash browns'; }
|
||||
});
|
||||
|
||||
Element.addMethods("LI", {
|
||||
pancakes: function(element) { return "pancakes"; }
|
||||
});
|
||||
|
||||
Element.addMethods("DIV", {
|
||||
waffles: function(element) { return "waffles"; }
|
||||
});
|
||||
|
||||
Element.addMethods($w("li div"), {
|
||||
orangeJuice: function(element) { return "orange juice"; }
|
||||
});
|
|
@ -1,4 +0,0 @@
|
|||
<form id="form">
|
||||
<input type="text" id="input" value="4" />
|
||||
<input type="submit" />
|
||||
</form>
|
|
@ -1,2 +0,0 @@
|
|||
Form.Element.Methods.coffee = Prototype.K;
|
||||
Element.addMethods();
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
<table id="grepTable">
|
||||
<tbody id="grepTBody">
|
||||
<tr id="grepRow">
|
||||
<th id="grepHeader" class="cell"></th>
|
||||
<td id="grepCell" class="cell"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
|
@ -1,23 +0,0 @@
|
|||
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'),
|
||||
|
||||
Basic: [1, 2, 3],
|
||||
|
||||
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);
|
|
@ -1,4 +0,0 @@
|
|||
<div id="outer" style="display: none">
|
||||
<p id="inner">One two three <span id="span">four</span></p>
|
||||
</div>
|
||||
<div id="container"><div></div></div>
|
|
@ -1,108 +0,0 @@
|
|||
<form id="form" method="get" action="fixtures/empty.js">
|
||||
<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" name="first_submit" value="Commit it!" />
|
||||
<input type="submit" name="second_submit" value="Delete it!" />
|
||||
<input type="text" name="action" value="blah" />
|
||||
</form>
|
||||
|
||||
<form id="bigform" method="get" action="fixtures/empty.js">
|
||||
<div id="inputs">
|
||||
<input type="text" name="dummy" id="dummy_disabled" disabled="disabled"/>
|
||||
<input type="submit" name="commit" id="submit" />
|
||||
<input type="button" name="clicky" value="click me" />
|
||||
<input type="reset" name="revert" />
|
||||
<input type="text" name="greeting" id="focus_text" value="Hello" />
|
||||
</div>
|
||||
|
||||
<!-- some edge cases in serialization -->
|
||||
<div id="value_checks">
|
||||
<input name="twin" type="text" value="" />
|
||||
<input name="twin" type="text" value="siamese" />
|
||||
<!-- Rails checkbox hack with hidden input: -->
|
||||
<input name="checky" type="checkbox" id="checkbox_hack" value="1" />
|
||||
<input name="checky" type="hidden" value="0" />
|
||||
</div>
|
||||
|
||||
<!-- all variations of SELECT controls -->
|
||||
<div id="selects_wrapper">
|
||||
<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="selects_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>
|
||||
</div>
|
||||
|
||||
<div id="various">
|
||||
<select name="tf_selectOne"><option selected="selected"></option><option>1</option></select>
|
||||
<textarea name="tf_textarea"></textarea>
|
||||
<input type="checkbox" name="tf_checkbox" value="on" />
|
||||
<select name="tf_selectMany" multiple="multiple"></select>
|
||||
<input type="text" name="tf_text" />
|
||||
<div>This is not a form element</div>
|
||||
<input type="radio" name="tf_radio" value="on" />
|
||||
<input type="hidden" name="tf_hidden" />
|
||||
<input type="password" name="tf_password" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form id="form_focus_hidden" style="display: none">
|
||||
<input type="text" />
|
||||
</form>
|
||||
|
||||
<form id="form_with_file_input">
|
||||
<input type="file" name="file_name" value="foo" />
|
||||
</form>
|
||||
|
||||
<!-- tabindexed forms -->
|
||||
<div id="tabindex">
|
||||
<form id="ffe">
|
||||
<p><input type="text" disabled="disabled" id="ffe_disabled" /></p>
|
||||
<input type="hidden" id="ffe_hidden" />
|
||||
<input type="checkbox" id="ffe_checkbox" />
|
||||
</form>
|
||||
|
||||
<form id="ffe_ti">
|
||||
<p><input type="text" disabled="disabled" id="ffe_ti_disabled" /></p>
|
||||
<input type="hidden" id="ffe_ti_hidden" />
|
||||
<input type="checkbox" id="ffe_ti_checkbox" />
|
||||
<input type="submit" id="ffe_ti_submit" tabindex="1" />
|
||||
</form>
|
||||
|
||||
<form id="ffe_ti2">
|
||||
<p><input type="text" disabled="disabled" id="ffe_ti2_disabled" /></p>
|
||||
<input type="hidden" id="ffe_ti2_hidden" />
|
||||
<input type="checkbox" id="ffe_ti2_checkbox" tabindex="0" />
|
||||
<input type="submit" id="ffe_ti2_submit" tabindex="1" />
|
||||
</form>
|
||||
|
||||
</div>
|
|
@ -1,13 +0,0 @@
|
|||
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;
|
|
@ -1,25 +0,0 @@
|
|||
var Fixtures = {
|
||||
one: { a: 'A#' },
|
||||
|
||||
many: {
|
||||
a: 'A',
|
||||
b: 'B',
|
||||
c: 'C',
|
||||
d: 'D#'
|
||||
},
|
||||
|
||||
functions: {
|
||||
quad: function(n) { return n*n },
|
||||
plus: function(n) { return n+n }
|
||||
},
|
||||
|
||||
multiple: { color: $w('r g b') },
|
||||
multiple_nil: { color: ['r', null, 'g', undefined, 0] },
|
||||
multiple_all_nil: { color: [null, undefined] },
|
||||
multiple_empty: { color: [] },
|
||||
multiple_special: { 'stuff[]': $w('$ a ;') },
|
||||
|
||||
value_undefined: { a:"b", c:undefined },
|
||||
value_null: { a:"b", c:null },
|
||||
value_zero: { a:"b", c:0 }
|
||||
};
|
|
@ -1 +0,0 @@
|
|||
$("content").update("<H2>Hello world!</H2>");
|
Binary file not shown.
Before Width: | Height: | Size: 3.4 KiB |
|
@ -1,6 +0,0 @@
|
|||
<div id="test"></div>
|
||||
<ul id="list">
|
||||
<li></li>
|
||||
<li></li>
|
||||
<li></li>
|
||||
</ul>
|
|
@ -1,7 +0,0 @@
|
|||
var Person = function(name){
|
||||
this.name = name;
|
||||
};
|
||||
|
||||
Person.prototype.toJSON = function() {
|
||||
return '-' + this.name;
|
||||
};
|
|
@ -1,9 +0,0 @@
|
|||
<div id="ensure_scrollbars" style="width:10000px; height:10000px; position:absolute" > </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;font-size:2px">test<span id="inline">test</span></div>
|
||||
<div id="absolute_relative_undefined"> </div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,88 +0,0 @@
|
|||
<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" rel="external nofollow" href="#">with a <em id="em2">link</em></a> or
|
||||
<a id="link_2" class="internal highlight" href="#"><em id="em">two</em></a>.
|
||||
Or <cite id="with_title" title="hello world!">a citation</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" id="hidden" />
|
||||
<input type="text" name="disabled_text_field" id="disabled_text_field" disabled="disabled" />
|
||||
<input type="text" name="enabled_text_field" id="enabled_text_field" />
|
||||
<input type="checkbox" name="checkboxes" id="checked_box" checked="checked" value="Checked" />
|
||||
<input type="checkbox" name="checkboxes" id="unchecked_box" value="Unchecked"/>
|
||||
<input type="radio" name="radiobuttons" id="checked_radio" checked="checked" value="Checked" />
|
||||
<input type="radio" name="radiobuttons" id="unchecked_radio" value="Unchecked" />
|
||||
</form>
|
||||
|
||||
<form id="troubleForm2">
|
||||
<input type="checkbox" name="brackets[5][]" id="chk_1" checked="checked" value="1" />
|
||||
<input type="checkbox" name="brackets[5][]" id="chk_2" value="2" />
|
||||
</form>
|
||||
|
||||
<div id="level1">
|
||||
<span id="level2_1">
|
||||
<span id="level3_1"></span>
|
||||
<!-- This comment should be ignored by the adjacent selector -->
|
||||
<span id="level3_2"></span>
|
||||
</span>
|
||||
<span id="level2_2">
|
||||
<em id="level_only_child">
|
||||
</em>
|
||||
</span>
|
||||
<div id="level2_3"></div>
|
||||
</div> <!-- #level1 -->
|
||||
|
||||
<div id="dupContainer">
|
||||
<span id="dupL1" class="span_foo span_bar">
|
||||
<span id="dupL2">
|
||||
<span id="dupL3">
|
||||
<span id="dupL4">
|
||||
<span id="dupL5"></span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div> <!-- #dupContainer -->
|
||||
<div id="dupContainer.withdot:active">
|
||||
<span id="dupL1_dotcolon" class="span_foo span_bar">
|
||||
<span id="dupL2_dotcolon">
|
||||
<span id="dupL3_dotcolon">
|
||||
<span id="dupL4_dotcolon">
|
||||
<span id="dupL5_dotcolon"></span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div> <!-- #dupContainer.withdot:active -->
|
||||
|
||||
<div id="grandfather"> grandfather
|
||||
<div id="father" class="brothers men"> father
|
||||
<div id="son"> son </div>
|
||||
</div>
|
||||
<div id="uncle" class="brothers men"> uncle </div>
|
||||
</div>
|
||||
|
||||
<form id="commaParent" title="commas,are,good">
|
||||
<input type="hidden" id="commaChild" name="foo" value="#commaOne,#commaTwo" />
|
||||
<input type="hidden" id="commaTwo" name="foo2" value="oops" />
|
||||
</form>
|
||||
<div id="counted_container"><div class="is_counted"></div></div>
|
||||
|
||||
<div foo-bar="baz" id="attr_with_dash">blah</div>
|
||||
|
||||
<div id="container_1" class="container">
|
||||
<div id="container_2" class="container">
|
||||
<span id="target_1"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,4 +0,0 @@
|
|||
<div id="test_div_parent" class="test_class">
|
||||
<div id="test_div_child" class="test_class">
|
||||
</div>
|
||||
</div>
|
|
@ -1,8 +0,0 @@
|
|||
var attackTarget;
|
||||
var evalScriptsCounter = 0,
|
||||
largeTextEscaped = '<span>test</span>',
|
||||
largeTextUnescaped = '<span>test</span>';
|
||||
(2048).times(function(){
|
||||
largeTextEscaped += ' ABC';
|
||||
largeTextUnescaped += ' ABC';
|
||||
});
|
|
@ -1,18 +0,0 @@
|
|||
<div id="testlog_2"> </div>
|
||||
|
||||
<!-- Test elements follow -->
|
||||
<div id="test_1" class="a bbbbbbbbbbbb cccccccccc dddd"> </div>
|
||||
|
||||
<div id="test_2"> <span> </span>
|
||||
|
||||
|
||||
|
||||
<div><div></div> </div><span> </span>
|
||||
</div>
|
||||
|
||||
<ul id="tlist"><li id="tlist_1">x1</li><li id="tlist_2">x2</li></ul>
|
||||
<ul id="tlist2"><li class="a" id="tlist2_1">x1</li><li id="tlist2_2">x2</li></ul>
|
||||
|
||||
<div id="testmoveby" style="background-color:#333;width:100px;">XXXX</div>
|
||||
|
||||
<div id="testcss1">testcss1<span id="testcss1_span" style="display:none;">blah</span></div><div id="testcss2">testcss1</div>
|
|
@ -1,382 +0,0 @@
|
|||
// sweet sweet additional assertions
|
||||
Object.extend(Test.Unit.Testcase.prototype, {
|
||||
assertEnabled: function() {
|
||||
for (var i = 0, element; element = arguments[i]; i++) {
|
||||
this.assert(!$(element).disabled, Test.Unit.inspect(element) + ' was disabled');
|
||||
}
|
||||
},
|
||||
assertDisabled: function() {
|
||||
for (var i = 0, element; element = arguments[i]; i++) {
|
||||
this.assert($(element).disabled, Test.Unit.inspect(element) + ' was enabled');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
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(){
|
||||
$$('form').each(function(f){ f.reset() });
|
||||
// hidden value does not reset (for some reason)
|
||||
$('bigform')['tf_hidden'].value = '';
|
||||
},
|
||||
|
||||
testDollarF: function(){
|
||||
this.assertEqual("4", $F("input_enabled"));
|
||||
},
|
||||
|
||||
testFormReset: function() {
|
||||
this.assert(!Object.isUndefined(Form.reset('form').reset));
|
||||
},
|
||||
|
||||
testFormElementEventObserver: function(){
|
||||
var callbackCounter = 0;
|
||||
var observer = new Form.Element.EventObserver('input_enabled', function(){
|
||||
callbackCounter++;
|
||||
});
|
||||
|
||||
this.assertEqual(0, callbackCounter);
|
||||
$('input_enabled').value = 'boo!';
|
||||
observer.onElementEvent(); // can't test the event directly, simulating
|
||||
this.assertEqual(1, callbackCounter);
|
||||
},
|
||||
|
||||
testFormElementObserver: function(){
|
||||
var timedCounter = 0;
|
||||
// First part: regular field
|
||||
var observer = new Form.Element.Observer('input_enabled', 0.5, function() {
|
||||
++timedCounter;
|
||||
});
|
||||
|
||||
// Test it's unchanged yet
|
||||
this.assertEqual(0, timedCounter);
|
||||
// Test it doesn't change on first check
|
||||
this.wait(550, function() {
|
||||
this.assertEqual(0, timedCounter);
|
||||
// Change, test it doesn't immediately change
|
||||
$('input_enabled').value = 'yowza!';
|
||||
this.assertEqual(0, timedCounter);
|
||||
// Test it changes on next check, but not again on the next
|
||||
this.wait(550, function() {
|
||||
this.assertEqual(1, timedCounter);
|
||||
this.wait(550, function() {
|
||||
this.assertEqual(1, timedCounter);
|
||||
observer.stop();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Second part: multiple-select
|
||||
[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
|
||||
this.assertEqual(0, timedCounter);
|
||||
// Test it doesn't change on first check
|
||||
this.wait(550, function() {
|
||||
this.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;
|
||||
this.assertEqual(0, timedCounter);
|
||||
// Test it changes on next check, but not again on the next
|
||||
this.wait(550, function() {
|
||||
this.assertEqual(1, timedCounter);
|
||||
this.wait(550, function() {
|
||||
this.assertEqual(1, timedCounter);
|
||||
observer.stop();
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testFormObserver: function(){
|
||||
var timedCounter = 0;
|
||||
// should work the same way was Form.Element.Observer
|
||||
var observer = new Form.Observer('form', 0.5, function(form, value) {
|
||||
++timedCounter;
|
||||
});
|
||||
|
||||
// Test it's unchanged yet
|
||||
this.assertEqual(0, timedCounter);
|
||||
// Test it doesn't change on first check
|
||||
this.wait(550, function() {
|
||||
this.assertEqual(0, timedCounter);
|
||||
// Change, test it doesn't immediately change
|
||||
$('input_enabled').value = 'yowza!';
|
||||
this.assertEqual(0, timedCounter);
|
||||
// Test it changes on next check, but not again on the next
|
||||
this.wait(550, function() {
|
||||
this.assertEqual(1, timedCounter);
|
||||
this.wait(550, function() {
|
||||
this.assertEqual(1, timedCounter);
|
||||
observer.stop();
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testFormEnabling: function(){
|
||||
var form = $('bigform')
|
||||
var input1 = $('dummy_disabled');
|
||||
var input2 = $('focus_text');
|
||||
|
||||
this.assertDisabled(input1);
|
||||
this.assertEnabled(input2);
|
||||
|
||||
form.disable();
|
||||
this.assertDisabled(input1, input2);
|
||||
form.enable();
|
||||
this.assertEnabled(input1, input2);
|
||||
input1.disable();
|
||||
this.assertDisabled(input1);
|
||||
|
||||
// non-form elements:
|
||||
var fieldset = $('selects_fieldset');
|
||||
var fields = fieldset.immediateDescendants();
|
||||
fields.each(function(select) { this.assertEnabled(select) }, this);
|
||||
|
||||
Form.disable(fieldset)
|
||||
fields.each(function(select) { this.assertDisabled(select) }, this);
|
||||
|
||||
Form.enable(fieldset)
|
||||
fields.each(function(select) { this.assertEnabled(select) }, this);
|
||||
},
|
||||
|
||||
testFormElementEnabling: function(){
|
||||
var field = $('input_disabled');
|
||||
field.enable();
|
||||
this.assertEnabled(field);
|
||||
field.disable();
|
||||
this.assertDisabled(field);
|
||||
|
||||
var field = $('input_enabled');
|
||||
this.assertEnabled(field);
|
||||
field.disable();
|
||||
this.assertDisabled(field);
|
||||
field.enable();
|
||||
this.assertEnabled(field);
|
||||
},
|
||||
|
||||
// due to the lack of a DOM hasFocus() API method,
|
||||
// we're simulating things here a little bit
|
||||
testFormActivating: function(){
|
||||
// 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('bigform');
|
||||
this.assertEqual('submit', element.id);
|
||||
|
||||
// Test IE doesn't select text on buttons
|
||||
Form.focusFirstElement('bigform');
|
||||
if(document.selection) this.assertEqual('', getSelection(element));
|
||||
|
||||
// Form.Element.activate shouldn't select text on buttons
|
||||
element = $('focus_text');
|
||||
this.assertEqual('', getSelection(element));
|
||||
|
||||
// Form.Element.activate should select text on text input elements
|
||||
element.activate();
|
||||
this.assertEqual('Hello', getSelection(element));
|
||||
|
||||
// Form.Element.activate shouldn't raise an exception when the form or field is hidden
|
||||
this.assertNothingRaised(function() {
|
||||
$('form_focus_hidden').focusFirstElement();
|
||||
});
|
||||
},
|
||||
|
||||
testFormGetElements: function() {
|
||||
var elements = Form.getElements('various'),
|
||||
names = $w('tf_selectOne tf_textarea tf_checkbox tf_selectMany tf_text tf_radio tf_hidden tf_password');
|
||||
this.assertEnumEqual(names, elements.pluck('name'))
|
||||
},
|
||||
|
||||
testFormGetInputs: function() {
|
||||
var form = $('form');
|
||||
[form.getInputs(), Form.getInputs(form)].each(function(inputs){
|
||||
this.assertEqual(inputs.length, 5);
|
||||
this.assert(inputs instanceof Array);
|
||||
this.assert(inputs.all(function(input) { return (input.tagName == "INPUT"); }));
|
||||
}, this);
|
||||
},
|
||||
|
||||
testFormFindFirstElement: function() {
|
||||
this.assertEqual($('ffe_checkbox'), $('ffe').findFirstElement());
|
||||
this.assertEqual($('ffe_ti_submit'), $('ffe_ti').findFirstElement());
|
||||
this.assertEqual($('ffe_ti2_checkbox'), $('ffe_ti2').findFirstElement());
|
||||
},
|
||||
|
||||
testFormSerialize: function() {
|
||||
// form is initially empty
|
||||
var form = $('bigform');
|
||||
var expected = { tf_selectOne:'', tf_textarea:'', tf_text:'', tf_hidden:'', tf_password:'' };
|
||||
this.assertHashEqual(expected, Form.serialize('various', true));
|
||||
|
||||
// set up some stuff
|
||||
form['tf_selectOne'].selectedIndex = 1;
|
||||
form['tf_textarea'].value = "boo hoo!";
|
||||
form['tf_text'].value = "123öäü";
|
||||
form['tf_hidden'].value = "moo%hoo&test";
|
||||
form['tf_password'].value = 'sekrit code';
|
||||
form['tf_checkbox'].checked = true;
|
||||
form['tf_radio'].checked = true;
|
||||
var expected = { tf_selectOne:1, tf_textarea:"boo hoo!", tf_text:"123öäü",
|
||||
tf_hidden:"moo%hoo&test", tf_password:'sekrit code', tf_checkbox:'on', tf_radio:'on' }
|
||||
|
||||
// return params
|
||||
this.assertHashEqual(expected, Form.serialize('various', true));
|
||||
// return string
|
||||
this.assertEnumEqual(Object.toQueryString(expected).split('&').sort(),
|
||||
Form.serialize('various').split('&').sort());
|
||||
this.assertEqual('string', typeof $('form').serialize({ hash:false }));
|
||||
|
||||
// Checks that disabled element is not included in serialized form.
|
||||
$('input_enabled').enable();
|
||||
this.assertHashEqual({ val1:4, action:'blah', first_submit:'Commit it!' },
|
||||
$('form').serialize(true));
|
||||
|
||||
// should not eat empty values for duplicate names
|
||||
$('checkbox_hack').checked = false;
|
||||
var data = Form.serialize('value_checks', true);
|
||||
this.assertEnumEqual(['', 'siamese'], data['twin']);
|
||||
this.assertEqual('0', data['checky']);
|
||||
|
||||
$('checkbox_hack').checked = true;
|
||||
this.assertEnumEqual($w('1 0'), Form.serialize('value_checks', true)['checky']);
|
||||
|
||||
// all kinds of SELECT controls
|
||||
var params = Form.serialize('selects_fieldset', true);
|
||||
var expected = { 'nvm[]':['One', 'Three'], evu:'', 'evm[]':['', 'Three'] };
|
||||
this.assertHashEqual(expected, params);
|
||||
params = Form.serialize('selects_wrapper', true);
|
||||
this.assertHashEqual(Object.extend(expected, { vu:1, 'vm[]':[1, 3], nvu:'One' }), params);
|
||||
|
||||
// explicit submit button
|
||||
this.assertHashEqual({ val1:4, action:'blah', second_submit:'Delete it!' },
|
||||
$('form').serialize({ submit: 'second_submit' }));
|
||||
this.assertHashEqual({ val1:4, action:'blah' },
|
||||
$('form').serialize({ submit: false }));
|
||||
this.assertHashEqual({ val1:4, action:'blah' },
|
||||
$('form').serialize({ submit: 'inexistent' }));
|
||||
|
||||
// file input should not be serialized
|
||||
this.assertEqual('', $('form_with_file_input').serialize());
|
||||
},
|
||||
|
||||
testFormMethodsOnExtendedElements: function() {
|
||||
var form = $('form');
|
||||
this.assertEqual(Form.serialize('form'), form.serialize());
|
||||
this.assertEqual(Form.Element.serialize('input_enabled'), $('input_enabled').serialize());
|
||||
this.assertNotEqual(form.serialize, $('input_enabled').serialize);
|
||||
|
||||
Element.addMethods('INPUT', { anInputMethod: function(input) { return 'input' } });
|
||||
Element.addMethods('SELECT', { aSelectMethod: function(select) { return 'select' } });
|
||||
|
||||
form = $('bigform');
|
||||
var input = form['tf_text'], select = form['tf_selectOne'];
|
||||
input._extendedByPrototype = select._extendedByPrototype = void 0;
|
||||
|
||||
this.assert($(input).anInputMethod);
|
||||
this.assert(!input.aSelectMethod);
|
||||
this.assertEqual('input', input.anInputMethod());
|
||||
|
||||
this.assert($(select).aSelectMethod);
|
||||
this.assert(!select.anInputMethod);
|
||||
this.assertEqual('select', select.aSelectMethod());
|
||||
},
|
||||
|
||||
testFormRequest: function() {
|
||||
var request = $("form").request();
|
||||
this.assert($("form").hasAttribute("method"));
|
||||
this.assert(request.url.include("fixtures/empty.js?val1=4"));
|
||||
this.assertEqual("get", request.method);
|
||||
|
||||
request = $("form").request({ method: "put", parameters: {val2: "hello"} });
|
||||
this.assert(request.url.endsWith("fixtures/empty.js"));
|
||||
this.assertEqual(4, request.options.parameters['val1']);
|
||||
this.assertEqual('hello', request.options.parameters['val2']);
|
||||
this.assertEqual("post", request.method);
|
||||
this.assertEqual("put", request.parameters['_method']);
|
||||
|
||||
// with empty action attribute
|
||||
request = $("ffe").request({ method: 'post' });
|
||||
this.assert(request.url.include("/tmp/form_test.html"),
|
||||
'wrong default action for form element with empty action attribute');
|
||||
},
|
||||
|
||||
testFormElementMethodsChaining: function(){
|
||||
var methods = $w('clear activate disable enable'),
|
||||
formElements = $('form').getElements();
|
||||
methods.each(function(method){
|
||||
formElements.each(function(element){
|
||||
var returned = element[method]();
|
||||
this.assertIdentical(element, returned);
|
||||
}, this);
|
||||
}, this);
|
||||
},
|
||||
|
||||
testSetValue: function(){
|
||||
// text input
|
||||
var input = $('input_enabled'), oldValue = input.getValue();
|
||||
this.assertEqual(input, input.setValue('foo'), 'setValue chaining is broken');
|
||||
this.assertEqual('foo', input.getValue(), 'value improperly set');
|
||||
input.setValue(oldValue);
|
||||
this.assertEqual(oldValue, input.getValue(), 'value improperly restored to original');
|
||||
|
||||
// checkbox
|
||||
input = $('checkbox_hack');
|
||||
input.setValue(false);
|
||||
this.assertEqual(null, input.getValue(), 'checkbox should be unchecked');
|
||||
input.setValue(true);
|
||||
this.assertEqual("1", input.getValue(), 'checkbox should be checked');
|
||||
// selectbox
|
||||
input = $('bigform')['vu'];
|
||||
input.setValue('3');
|
||||
this.assertEqual('3', input.getValue(), 'single select option improperly set');
|
||||
input.setValue('1');
|
||||
this.assertEqual('1', input.getValue());
|
||||
// multiple select
|
||||
input = $('bigform')['vm[]'];
|
||||
input.setValue(['2', '3']);
|
||||
this.assertEnumEqual(['2', '3'], input.getValue(),
|
||||
'multiple select options improperly set');
|
||||
input.setValue(['1', '3']);
|
||||
this.assertEnumEqual(['1', '3'], input.getValue());
|
||||
},
|
||||
|
||||
testSerializeFormTroublesomeNames: function() {
|
||||
var el = new Element('form', {
|
||||
action: '/'
|
||||
});
|
||||
var input = new Element('input', {
|
||||
type: 'text',
|
||||
name: 'length',
|
||||
value: 'foo'
|
||||
});
|
||||
var input2 = new Element('input', {
|
||||
type: 'text',
|
||||
name: 'bar',
|
||||
value: 'baz'
|
||||
});
|
||||
el.appendChild(input);
|
||||
el.appendChild(input2);
|
||||
this.assertHashEqual({ length: 'foo', bar: 'baz' }, el.serialize(true));
|
||||
}
|
||||
});
|
|
@ -1,133 +0,0 @@
|
|||
new Test.Unit.Runner({
|
||||
testFunctionArgumentNames: function() {
|
||||
this.assertEnumEqual([], (function() {}).argumentNames());
|
||||
this.assertEnumEqual(["one"], (function(one) {}).argumentNames());
|
||||
this.assertEnumEqual(["one", "two", "three"], (function(one, two, three) {}).argumentNames());
|
||||
this.assertEnumEqual(["one", "two", "three"], (function( one , two
|
||||
, three ) {}).argumentNames());
|
||||
this.assertEqual("$super", (function($super) {}).argumentNames().first());
|
||||
|
||||
function named1() {};
|
||||
this.assertEnumEqual([], named1.argumentNames());
|
||||
function named2(one) {};
|
||||
this.assertEnumEqual(["one"], named2.argumentNames());
|
||||
function named3(one, two, three) {};
|
||||
this.assertEnumEqual(["one", "two", "three"], named3.argumentNames());
|
||||
function named4(one,
|
||||
two,
|
||||
|
||||
three) {}
|
||||
this.assertEnumEqual($w('one two three'), named4.argumentNames());
|
||||
function named5(/*foo*/ foo, /* bar */ bar, /*****/ baz) {}
|
||||
this.assertEnumEqual($w("foo bar baz"), named5.argumentNames());
|
||||
function named6(
|
||||
/*foo*/ foo,
|
||||
/**/bar,
|
||||
/* baz */ /* baz */ baz,
|
||||
// Skip a line just to screw with the regex...
|
||||
/* thud */ thud) {}
|
||||
this.assertEnumEqual($w("foo bar baz thud"), named6.argumentNames());
|
||||
},
|
||||
|
||||
testFunctionBind: function() {
|
||||
function methodWithoutArguments() { return this.hi };
|
||||
function methodWithArguments() { return this.hi + ',' + $A(arguments).join(',') };
|
||||
var func = Prototype.emptyFunction;
|
||||
|
||||
this.assertIdentical(func, func.bind());
|
||||
this.assertIdentical(func, func.bind(undefined));
|
||||
this.assertNotIdentical(func, func.bind(null));
|
||||
|
||||
this.assertEqual('without', methodWithoutArguments.bind({ hi: 'without' })());
|
||||
this.assertEqual('with,arg1,arg2', methodWithArguments.bind({ hi: 'with' })('arg1','arg2'));
|
||||
this.assertEqual('withBindArgs,arg1,arg2',
|
||||
methodWithArguments.bind({ hi: 'withBindArgs' }, 'arg1', 'arg2')());
|
||||
this.assertEqual('withBindArgsAndArgs,arg1,arg2,arg3,arg4',
|
||||
methodWithArguments.bind({ hi: 'withBindArgsAndArgs' }, 'arg1', 'arg2')('arg3', 'arg4'));
|
||||
},
|
||||
|
||||
testFunctionCurry: function() {
|
||||
var split = function(delimiter, string) { return string.split(delimiter); };
|
||||
var splitOnColons = split.curry(":");
|
||||
this.assertNotIdentical(split, splitOnColons);
|
||||
this.assertEnumEqual(split(":", "0:1:2:3:4:5"), splitOnColons("0:1:2:3:4:5"));
|
||||
this.assertIdentical(split, split.curry());
|
||||
},
|
||||
|
||||
testFunctionDelay: function() {
|
||||
window.delayed = undefined;
|
||||
var delayedFunction = function() { window.delayed = true; };
|
||||
var delayedFunctionWithArgs = function() { window.delayedWithArgs = $A(arguments).join(' '); };
|
||||
delayedFunction.delay(0.8);
|
||||
delayedFunctionWithArgs.delay(0.8, 'hello', 'world');
|
||||
this.assertUndefined(window.delayed);
|
||||
this.wait(1000, function() {
|
||||
this.assert(window.delayed);
|
||||
this.assertEqual('hello world', window.delayedWithArgs);
|
||||
});
|
||||
},
|
||||
|
||||
testFunctionWrap: function() {
|
||||
function sayHello(){
|
||||
return 'hello world';
|
||||
};
|
||||
|
||||
this.assertEqual('HELLO WORLD', sayHello.wrap(function(proceed) {
|
||||
return proceed().toUpperCase();
|
||||
})());
|
||||
|
||||
var temp = String.prototype.capitalize;
|
||||
String.prototype.capitalize = String.prototype.capitalize.wrap(function(proceed, eachWord) {
|
||||
if(eachWord && this.include(' ')) return this.split(' ').map(function(str){
|
||||
return str.capitalize();
|
||||
}).join(' ');
|
||||
return proceed();
|
||||
});
|
||||
this.assertEqual('Hello world', 'hello world'.capitalize());
|
||||
this.assertEqual('Hello World', 'hello world'.capitalize(true));
|
||||
this.assertEqual('Hello', 'hello'.capitalize());
|
||||
String.prototype.capitalize = temp;
|
||||
},
|
||||
|
||||
testFunctionDefer: function() {
|
||||
window.deferred = undefined;
|
||||
var deferredFunction = function() { window.deferred = true; };
|
||||
deferredFunction.defer();
|
||||
this.assertUndefined(window.deferred);
|
||||
this.wait(50, function() {
|
||||
this.assert(window.deferred);
|
||||
|
||||
window.deferredValue = 0;
|
||||
var deferredFunction2 = function(arg) { window.deferredValue = arg; };
|
||||
deferredFunction2.defer('test');
|
||||
this.wait(50, function() {
|
||||
this.assertEqual('test', window.deferredValue);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
testFunctionMethodize: function() {
|
||||
var Foo = { bar: function(baz) { return baz } };
|
||||
var baz = { quux: Foo.bar.methodize() };
|
||||
|
||||
this.assertEqual(Foo.bar.methodize(), baz.quux);
|
||||
this.assertEqual(baz, Foo.bar(baz));
|
||||
this.assertEqual(baz, baz.quux());
|
||||
},
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,178 +0,0 @@
|
|||
new Test.Unit.Runner({
|
||||
testSet: function() {
|
||||
var h = $H({a: 'A'})
|
||||
|
||||
this.assertEqual('B', h.set('b', 'B'));
|
||||
this.assertHashEqual({a: 'A', b: 'B'}, h);
|
||||
|
||||
this.assertUndefined(h.set('c'));
|
||||
this.assertHashEqual({a: 'A', b: 'B', c: undefined}, h);
|
||||
},
|
||||
|
||||
testGet: function() {
|
||||
var h = $H({a: 'A'});
|
||||
this.assertEqual('A', h.get('a'));
|
||||
this.assertUndefined(h.a);
|
||||
this.assertUndefined($H({}).get('a'));
|
||||
|
||||
this.assertUndefined($H({}).get('toString'));
|
||||
this.assertUndefined($H({}).get('constructor'));
|
||||
},
|
||||
|
||||
testUnset: function() {
|
||||
var hash = $H(Fixtures.many);
|
||||
this.assertEqual('B', hash.unset('b'));
|
||||
this.assertHashEqual({a:'A', c: 'C', d:'D#'}, hash);
|
||||
this.assertUndefined(hash.unset('z'));
|
||||
this.assertHashEqual({a:'A', c: 'C', d:'D#'}, hash);
|
||||
// not equivalent to Hash#remove
|
||||
this.assertEqual('A', hash.unset('a', 'c'));
|
||||
this.assertHashEqual({c: 'C', d:'D#'}, hash);
|
||||
},
|
||||
|
||||
testToObject: function() {
|
||||
var hash = $H(Fixtures.many), object = hash.toObject();
|
||||
this.assertInstanceOf(Object, object);
|
||||
this.assertHashEqual(Fixtures.many, object);
|
||||
this.assertNotIdentical(Fixtures.many, object);
|
||||
hash.set('foo', 'bar');
|
||||
this.assertHashNotEqual(object, hash.toObject());
|
||||
},
|
||||
|
||||
testConstruct: function() {
|
||||
var object = Object.clone(Fixtures.one);
|
||||
var h = new Hash(object), h2 = $H(object);
|
||||
this.assertInstanceOf(Hash, h);
|
||||
this.assertInstanceOf(Hash, h2);
|
||||
|
||||
this.assertHashEqual({}, new Hash());
|
||||
this.assertHashEqual(object, h);
|
||||
this.assertHashEqual(object, h2);
|
||||
|
||||
h.set('foo', 'bar');
|
||||
this.assertHashNotEqual(object, h);
|
||||
|
||||
var clone = $H(h);
|
||||
this.assertInstanceOf(Hash, clone);
|
||||
this.assertHashEqual(h, clone);
|
||||
h.set('foo', 'foo');
|
||||
this.assertHashNotEqual(h, clone);
|
||||
this.assertIdentical($H, Hash.from);
|
||||
},
|
||||
|
||||
testKeys: function() {
|
||||
this.assertEnumEqual([], $H({}).keys());
|
||||
this.assertEnumEqual(['a'], $H(Fixtures.one).keys());
|
||||
this.assertEnumEqual($w('a b c d'), $H(Fixtures.many).keys().sort());
|
||||
this.assertEnumEqual($w('plus quad'), $H(Fixtures.functions).keys().sort());
|
||||
},
|
||||
|
||||
testValues: function() {
|
||||
this.assertEnumEqual([], $H({}).values());
|
||||
this.assertEnumEqual(['A#'], $H(Fixtures.one).values());
|
||||
this.assertEnumEqual($w('A B C D#'), $H(Fixtures.many).values().sort());
|
||||
this.assertEnumEqual($w('function function'),
|
||||
$H(Fixtures.functions).values().map(function(i){ return typeof i }));
|
||||
this.assertEqual(9, $H(Fixtures.functions).get('quad')(3));
|
||||
this.assertEqual(6, $H(Fixtures.functions).get('plus')(3));
|
||||
},
|
||||
|
||||
testIndex: function() {
|
||||
this.assertUndefined($H().index('foo'));
|
||||
|
||||
this.assert('a', $H(Fixtures.one).index('A#'));
|
||||
this.assert('a', $H(Fixtures.many).index('A'));
|
||||
this.assertUndefined($H(Fixtures.many).index('Z'))
|
||||
|
||||
var hash = $H({a:1,b:'2',c:1});
|
||||
this.assert(['a','c'].include(hash.index(1)));
|
||||
this.assertUndefined(hash.index('1'));
|
||||
},
|
||||
|
||||
testMerge: function() {
|
||||
var h = $H(Fixtures.many);
|
||||
this.assertNotIdentical(h, h.merge());
|
||||
this.assertNotIdentical(h, h.merge({}));
|
||||
this.assertInstanceOf(Hash, h.merge());
|
||||
this.assertInstanceOf(Hash, h.merge({}));
|
||||
this.assertHashEqual(h, h.merge());
|
||||
this.assertHashEqual(h, h.merge({}));
|
||||
this.assertHashEqual(h, h.merge($H()));
|
||||
this.assertHashEqual({a:'A', b:'B', c:'C', d:'D#', aaa:'AAA' }, h.merge({aaa: 'AAA'}));
|
||||
this.assertHashEqual({a:'A#', b:'B', c:'C', d:'D#' }, h.merge(Fixtures.one));
|
||||
},
|
||||
|
||||
testUpdate: function() {
|
||||
var h = $H(Fixtures.many);
|
||||
this.assertIdentical(h, h.update());
|
||||
this.assertIdentical(h, h.update({}));
|
||||
this.assertHashEqual(h, h.update());
|
||||
this.assertHashEqual(h, h.update({}));
|
||||
this.assertHashEqual(h, h.update($H()));
|
||||
this.assertHashEqual({a:'A', b:'B', c:'C', d:'D#', aaa:'AAA' }, h.update({aaa: 'AAA'}));
|
||||
this.assertHashEqual({a:'A#', b:'B', c:'C', d:'D#', aaa:'AAA' }, h.update(Fixtures.one));
|
||||
},
|
||||
|
||||
testToQueryString: function() {
|
||||
this.assertEqual('', $H({}).toQueryString());
|
||||
this.assertEqual('a%23=A', $H({'a#': 'A'}).toQueryString());
|
||||
this.assertEqual('a=A%23', $H(Fixtures.one).toQueryString());
|
||||
this.assertEqual('a=A&b=B&c=C&d=D%23', $H(Fixtures.many).toQueryString());
|
||||
this.assertEqual("a=b&c", $H(Fixtures.value_undefined).toQueryString());
|
||||
this.assertEqual("a=b&c", $H("a=b&c".toQueryParams()).toQueryString());
|
||||
this.assertEqual("a=b&c=", $H(Fixtures.value_null).toQueryString());
|
||||
this.assertEqual("a=b&c=0", $H(Fixtures.value_zero).toQueryString());
|
||||
this.assertEqual("color=r&color=g&color=b", $H(Fixtures.multiple).toQueryString());
|
||||
this.assertEqual("color=r&color=&color=g&color&color=0", $H(Fixtures.multiple_nil).toQueryString());
|
||||
this.assertEqual("color=&color", $H(Fixtures.multiple_all_nil).toQueryString());
|
||||
this.assertEqual("", $H(Fixtures.multiple_empty).toQueryString());
|
||||
this.assertEqual("", $H({foo: {}, bar: {}}).toQueryString());
|
||||
this.assertEqual("stuff%5B%5D=%24&stuff%5B%5D=a&stuff%5B%5D=%3B", $H(Fixtures.multiple_special).toQueryString());
|
||||
this.assertHashEqual(Fixtures.multiple_special, $H(Fixtures.multiple_special).toQueryString().toQueryParams());
|
||||
this.assertIdentical(Object.toQueryString, Hash.toQueryString);
|
||||
},
|
||||
|
||||
testInspect: function() {
|
||||
this.assertEqual('#<Hash:{}>', $H({}).inspect());
|
||||
this.assertEqual("#<Hash:{'a': 'A#'}>", $H(Fixtures.one).inspect());
|
||||
this.assertEqual("#<Hash:{'a': 'A', 'b': 'B', 'c': 'C', 'd': 'D#'}>", $H(Fixtures.many).inspect());
|
||||
},
|
||||
|
||||
testClone: function() {
|
||||
var h = $H(Fixtures.many);
|
||||
this.assertHashEqual(h, h.clone());
|
||||
this.assertInstanceOf(Hash, h.clone());
|
||||
this.assertNotIdentical(h, h.clone());
|
||||
},
|
||||
|
||||
testToJSON: function() {
|
||||
this.assertEqual('{\"b\": [false, true], \"c\": {\"a\": \"hello!\"}}',
|
||||
$H({'b': [undefined, false, true, undefined], c: {a: 'hello!'}}).toJSON());
|
||||
},
|
||||
|
||||
testAbilityToContainAnyKey: function() {
|
||||
var h = $H({ _each: 'E', map: 'M', keys: 'K', pluck: 'P', unset: 'U' });
|
||||
this.assertEnumEqual($w('_each keys map pluck unset'), h.keys().sort());
|
||||
this.assertEqual('U', h.unset('unset'));
|
||||
this.assertHashEqual({ _each: 'E', map: 'M', keys: 'K', pluck: 'P' }, h);
|
||||
},
|
||||
|
||||
testHashToTemplateReplacements: function() {
|
||||
var template = new Template("#{a} #{b}"), hash = $H({ a: "hello", b: "world" });
|
||||
this.assertEqual("hello world", template.evaluate(hash.toObject()));
|
||||
this.assertEqual("hello world", template.evaluate(hash));
|
||||
this.assertEqual("hello", "#{a}".interpolate(hash));
|
||||
},
|
||||
|
||||
testPreventIterationOverShadowedProperties: function() {
|
||||
// redundant now that object is systematically cloned.
|
||||
var FooMaker = function(value) {
|
||||
this.key = value;
|
||||
};
|
||||
FooMaker.prototype.key = 'foo';
|
||||
var foo = new FooMaker('bar');
|
||||
this.assertEqual("key=bar", new Hash(foo).toQueryString());
|
||||
this.assertEqual("key=bar", new Hash(new Hash(foo)).toQueryString());
|
||||
}
|
||||
|
||||
});
|
|
@ -1,44 +0,0 @@
|
|||
new Test.Unit.Runner({
|
||||
|
||||
testNumberMathMethods: function() {
|
||||
this.assertEqual(1, (0.9).round());
|
||||
this.assertEqual(-2, (-1.9).floor());
|
||||
this.assertEqual(-1, (-1.9).ceil());
|
||||
|
||||
$w('abs floor round ceil').each(function(method) {
|
||||
this.assertEqual(Math[method](Math.PI), Math.PI[method]());
|
||||
}, this);
|
||||
},
|
||||
|
||||
testNumberToColorPart: function() {
|
||||
this.assertEqual('00', (0).toColorPart());
|
||||
this.assertEqual('0a', (10).toColorPart());
|
||||
this.assertEqual('ff', (255).toColorPart());
|
||||
},
|
||||
|
||||
testNumberToPaddedString: function() {
|
||||
this.assertEqual('00', (0).toPaddedString(2, 16));
|
||||
this.assertEqual('0a', (10).toPaddedString(2, 16));
|
||||
this.assertEqual('ff', (255).toPaddedString(2, 16));
|
||||
this.assertEqual('000', (0).toPaddedString(3));
|
||||
this.assertEqual('010', (10).toPaddedString(3));
|
||||
this.assertEqual('100', (100).toPaddedString(3));
|
||||
this.assertEqual('1000', (1000).toPaddedString(3));
|
||||
},
|
||||
|
||||
testNumberToJSON: function() {
|
||||
this.assertEqual('null', Number.NaN.toJSON());
|
||||
this.assertEqual('0', (0).toJSON());
|
||||
this.assertEqual('-293', (-293).toJSON());
|
||||
},
|
||||
|
||||
testNumberTimes: function() {
|
||||
var results = [];
|
||||
(5).times(function(i) { results.push(i) });
|
||||
this.assertEnumEqual($R(0, 4), results);
|
||||
|
||||
results = [];
|
||||
(5).times(function(i) { results.push(i * this.i) }, { i: 2 });
|
||||
this.assertEnumEqual([0, 2, 4, 6, 8], results);
|
||||
}
|
||||
});
|
|
@ -1,180 +0,0 @@
|
|||
new Test.Unit.Runner({
|
||||
testObjectExtend: function() {
|
||||
var object = {foo: 'foo', bar: [1, 2, 3]};
|
||||
this.assertIdentical(object, Object.extend(object));
|
||||
this.assertHashEqual({foo: 'foo', bar: [1, 2, 3]}, object);
|
||||
this.assertIdentical(object, Object.extend(object, {bla: 123}));
|
||||
this.assertHashEqual({foo: 'foo', bar: [1, 2, 3], bla: 123}, object);
|
||||
this.assertHashEqual({foo: 'foo', bar: [1, 2, 3], bla: null},
|
||||
Object.extend(object, {bla: null}));
|
||||
},
|
||||
|
||||
testObjectToQueryString: function() {
|
||||
this.assertEqual('a=A&b=B&c=C&d=D%23', Object.toQueryString({a: 'A', b: 'B', c: 'C', d: 'D#'}));
|
||||
},
|
||||
|
||||
testObjectClone: function() {
|
||||
var object = {foo: 'foo', bar: [1, 2, 3]};
|
||||
this.assertNotIdentical(object, Object.clone(object));
|
||||
this.assertHashEqual(object, Object.clone(object));
|
||||
this.assertHashEqual({}, Object.clone());
|
||||
var clone = Object.clone(object);
|
||||
delete clone.bar;
|
||||
this.assertHashEqual({foo: 'foo'}, clone,
|
||||
"Optimizing Object.clone perf using prototyping doesn't allow properties to be deleted.");
|
||||
},
|
||||
|
||||
testObjectInspect: function() {
|
||||
this.assertEqual('undefined', Object.inspect());
|
||||
this.assertEqual('undefined', Object.inspect(undefined));
|
||||
this.assertEqual('null', Object.inspect(null));
|
||||
this.assertEqual("'foo\\\\b\\\'ar'", Object.inspect('foo\\b\'ar'));
|
||||
this.assertEqual('[]', Object.inspect([]));
|
||||
this.assertNothingRaised(function() { Object.inspect(window.Node) });
|
||||
},
|
||||
|
||||
testObjectToJSON: function() {
|
||||
this.assertUndefined(Object.toJSON(undefined));
|
||||
this.assertUndefined(Object.toJSON(Prototype.K));
|
||||
this.assertEqual('\"\"', Object.toJSON(''));
|
||||
this.assertEqual('[]', Object.toJSON([]));
|
||||
this.assertEqual('[\"a\"]', Object.toJSON(['a']));
|
||||
this.assertEqual('[\"a\", 1]', Object.toJSON(['a', 1]));
|
||||
this.assertEqual('[\"a\", {\"b\": null}]', Object.toJSON(['a', {'b': null}]));
|
||||
this.assertEqual('{\"a\": \"hello!\"}', Object.toJSON({a: 'hello!'}));
|
||||
this.assertEqual('{}', Object.toJSON({}));
|
||||
this.assertEqual('{}', Object.toJSON({a: undefined, b: undefined, c: Prototype.K}));
|
||||
this.assertEqual('{\"b\": [false, true], \"c\": {\"a\": \"hello!\"}}',
|
||||
Object.toJSON({'b': [undefined, false, true, undefined], c: {a: 'hello!'}}));
|
||||
this.assertEqual('{\"b\": [false, true], \"c\": {\"a\": \"hello!\"}}',
|
||||
Object.toJSON($H({'b': [undefined, false, true, undefined], c: {a: 'hello!'}})));
|
||||
this.assertEqual('true', Object.toJSON(true));
|
||||
this.assertEqual('false', Object.toJSON(false));
|
||||
this.assertEqual('null', Object.toJSON(null));
|
||||
var sam = new Person('sam');
|
||||
this.assertEqual('-sam', Object.toJSON(sam));
|
||||
this.assertEqual('-sam', sam.toJSON());
|
||||
var element = $('test');
|
||||
this.assertUndefined(Object.toJSON(element));
|
||||
element.toJSON = function(){return 'I\'m a div with id test'};
|
||||
this.assertEqual('I\'m a div with id test', Object.toJSON(element));
|
||||
},
|
||||
|
||||
testObjectToHTML: function() {
|
||||
this.assertIdentical('', Object.toHTML());
|
||||
this.assertIdentical('', Object.toHTML(''));
|
||||
this.assertIdentical('', Object.toHTML(null));
|
||||
this.assertIdentical('0', Object.toHTML(0));
|
||||
this.assertIdentical('123', Object.toHTML(123));
|
||||
this.assertEqual('hello world', Object.toHTML('hello world'));
|
||||
this.assertEqual('hello world', Object.toHTML({toHTML: function() { return 'hello world' }}));
|
||||
},
|
||||
|
||||
testObjectIsArray: function() {
|
||||
this.assert(Object.isArray([]));
|
||||
this.assert(Object.isArray([0]));
|
||||
this.assert(Object.isArray([0, 1]));
|
||||
this.assert(!Object.isArray({}));
|
||||
this.assert(!Object.isArray($('list').childNodes));
|
||||
this.assert(!Object.isArray());
|
||||
this.assert(!Object.isArray(''));
|
||||
this.assert(!Object.isArray('foo'));
|
||||
this.assert(!Object.isArray(0));
|
||||
this.assert(!Object.isArray(1));
|
||||
this.assert(!Object.isArray(null));
|
||||
this.assert(!Object.isArray(true));
|
||||
this.assert(!Object.isArray(false));
|
||||
this.assert(!Object.isArray(undefined));
|
||||
},
|
||||
|
||||
testObjectIsHash: function() {
|
||||
this.assert(Object.isHash($H()));
|
||||
this.assert(Object.isHash(new Hash()));
|
||||
this.assert(!Object.isHash({}));
|
||||
this.assert(!Object.isHash(null));
|
||||
this.assert(!Object.isHash());
|
||||
this.assert(!Object.isHash(''));
|
||||
this.assert(!Object.isHash(2));
|
||||
this.assert(!Object.isHash(false));
|
||||
this.assert(!Object.isHash(true));
|
||||
this.assert(!Object.isHash([]));
|
||||
},
|
||||
|
||||
testObjectIsElement: function() {
|
||||
this.assert(Object.isElement(document.createElement('div')));
|
||||
this.assert(Object.isElement(new Element('div')));
|
||||
this.assert(Object.isElement($('testlog')));
|
||||
this.assert(!Object.isElement(document.createTextNode('bla')));
|
||||
|
||||
// falsy variables should not mess up return value type
|
||||
this.assertIdentical(false, Object.isElement(0));
|
||||
this.assertIdentical(false, Object.isElement(''));
|
||||
this.assertIdentical(false, Object.isElement(NaN));
|
||||
this.assertIdentical(false, Object.isElement(null));
|
||||
this.assertIdentical(false, Object.isElement(undefined));
|
||||
},
|
||||
|
||||
testObjectIsFunction: function() {
|
||||
this.assert(Object.isFunction(function() { }));
|
||||
this.assert(Object.isFunction(Class.create()));
|
||||
this.assert(!Object.isFunction("a string"));
|
||||
this.assert(!Object.isFunction($("testlog")));
|
||||
this.assert(!Object.isFunction([]));
|
||||
this.assert(!Object.isFunction({}));
|
||||
this.assert(!Object.isFunction(0));
|
||||
this.assert(!Object.isFunction(false));
|
||||
this.assert(!Object.isFunction(undefined));
|
||||
},
|
||||
|
||||
testObjectIsString: function() {
|
||||
this.assert(!Object.isString(function() { }));
|
||||
this.assert(Object.isString("a string"));
|
||||
this.assert(Object.isString(new String("a string")));
|
||||
this.assert(!Object.isString(0));
|
||||
this.assert(!Object.isString([]));
|
||||
this.assert(!Object.isString({}));
|
||||
this.assert(!Object.isString(false));
|
||||
this.assert(!Object.isString(undefined));
|
||||
this.assert(!Object.isString(document), 'host objects should return false rather than throw exceptions');
|
||||
},
|
||||
|
||||
testObjectIsNumber: function() {
|
||||
this.assert(Object.isNumber(0));
|
||||
this.assert(Object.isNumber(1.0));
|
||||
this.assert(Object.isNumber(new Number(0)));
|
||||
this.assert(Object.isNumber(new Number(1.0)));
|
||||
this.assert(!Object.isNumber(function() { }));
|
||||
this.assert(!Object.isNumber({ test: function() { return 3 } }));
|
||||
this.assert(!Object.isNumber("a string"));
|
||||
this.assert(!Object.isNumber([]));
|
||||
this.assert(!Object.isNumber({}));
|
||||
this.assert(!Object.isNumber(false));
|
||||
this.assert(!Object.isNumber(undefined));
|
||||
this.assert(!Object.isNumber(document), 'host objects should return false rather than throw exceptions');
|
||||
},
|
||||
|
||||
testObjectIsUndefined: function() {
|
||||
this.assert(Object.isUndefined(undefined));
|
||||
this.assert(!Object.isUndefined(null));
|
||||
this.assert(!Object.isUndefined(false));
|
||||
this.assert(!Object.isUndefined(0));
|
||||
this.assert(!Object.isUndefined(""));
|
||||
this.assert(!Object.isUndefined(function() { }));
|
||||
this.assert(!Object.isUndefined([]));
|
||||
this.assert(!Object.isUndefined({}));
|
||||
},
|
||||
|
||||
// sanity check
|
||||
testDoesntExtendObjectPrototype: function() {
|
||||
// for-in is supported with objects
|
||||
var iterations = 0, obj = { a: 1, b: 2, c: 3 };
|
||||
for(property in obj) iterations++;
|
||||
this.assertEqual(3, iterations);
|
||||
|
||||
// for-in is not supported with arrays
|
||||
iterations = 0;
|
||||
var arr = [1,2,3];
|
||||
for(property in arr) iterations++;
|
||||
this.assert(iterations > 3);
|
||||
}
|
||||
});
|
|
@ -1,35 +0,0 @@
|
|||
new Test.Unit.Runner({
|
||||
testPeriodicalExecuterStop: function() {
|
||||
var peEventCount = 0;
|
||||
function peEventFired(pe) {
|
||||
if (++peEventCount > 2) pe.stop();
|
||||
}
|
||||
|
||||
// peEventFired will stop the PeriodicalExecuter after 3 callbacks
|
||||
new PeriodicalExecuter(peEventFired, 0.05);
|
||||
|
||||
this.wait(600, function() {
|
||||
this.assertEqual(3, peEventCount);
|
||||
});
|
||||
},
|
||||
|
||||
testOnTimerEventMethod: function() {
|
||||
var testcase = this,
|
||||
pe = {
|
||||
onTimerEvent: PeriodicalExecuter.prototype.onTimerEvent,
|
||||
execute: function() {
|
||||
testcase.assert(pe.currentlyExecuting);
|
||||
}
|
||||
};
|
||||
|
||||
pe.onTimerEvent();
|
||||
this.assert(!pe.currentlyExecuting);
|
||||
|
||||
pe.execute = function() {
|
||||
testcase.assert(pe.currentlyExecuting);
|
||||
throw new Error()
|
||||
}
|
||||
this.assertRaise('Error', pe.onTimerEvent.bind(pe));
|
||||
this.assert(!pe.currentlyExecuting);
|
||||
}
|
||||
});
|
|
@ -1,44 +0,0 @@
|
|||
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() {
|
||||
Position.prepare();
|
||||
this.assertEqual(0, Position.deltaX);
|
||||
this.assertEqual(0, Position.deltaY);
|
||||
scrollTo(20,30);
|
||||
Position.prepare();
|
||||
this.assertEqual(20, Position.deltaX);
|
||||
this.assertEqual(30, Position.deltaY);
|
||||
},
|
||||
|
||||
testWithin: function() {
|
||||
[true, false].each(function(withScrollOffsets) {
|
||||
Position.includeScrollOffsets = withScrollOffsets;
|
||||
this.assert(!Position.within($('body_absolute'), 9, 9), 'outside left/top');
|
||||
this.assert(Position.within($('body_absolute'), 10, 10), 'left/top corner');
|
||||
this.assert(Position.within($('body_absolute'), 10, 19), 'left/bottom corner');
|
||||
this.assert(!Position.within($('body_absolute'), 10, 20), 'outside bottom');
|
||||
}, this);
|
||||
|
||||
scrollTo(20,30);
|
||||
Position.prepare();
|
||||
Position.includeScrollOffsets = true;
|
||||
this.assert(!Position.within($('body_absolute'), 9, 9), 'outside left/top');
|
||||
this.assert(Position.within($('body_absolute'), 10, 10), 'left/top corner');
|
||||
this.assert(Position.within($('body_absolute'), 10, 19), 'left/bottom corner');
|
||||
this.assert(!Position.within($('body_absolute'), 10, 20), 'outside bottom');
|
||||
}
|
||||
});
|
|
@ -1,43 +0,0 @@
|
|||
new Test.Unit.Runner({
|
||||
testBrowserDetection: function() {
|
||||
var results = $H(Prototype.Browser).map(function(engine){
|
||||
return engine;
|
||||
}).partition(function(engine){
|
||||
return engine[1] === true
|
||||
});
|
||||
var trues = results[0], falses = results[1];
|
||||
|
||||
this.info('User agent string is: ' + navigator.userAgent);
|
||||
|
||||
this.assert(trues.size() == 0 || trues.size() == 1,
|
||||
'There should be only one or no browser detected.');
|
||||
|
||||
// we should have definite trues or falses here
|
||||
trues.each(function(result) {
|
||||
this.assert(result[1] === true);
|
||||
}, this);
|
||||
falses.each(function(result) {
|
||||
this.assert(result[1] === false);
|
||||
}, this);
|
||||
|
||||
if(navigator.userAgent.indexOf('AppleWebKit/') > -1) {
|
||||
this.info('Running on WebKit');
|
||||
this.assert(Prototype.Browser.WebKit);
|
||||
}
|
||||
|
||||
if(!!window.opera) {
|
||||
this.info('Running on Opera');
|
||||
this.assert(Prototype.Browser.Opera);
|
||||
}
|
||||
|
||||
if(!!(window.attachEvent && !window.opera)) {
|
||||
this.info('Running on IE');
|
||||
this.assert(Prototype.Browser.IE);
|
||||
}
|
||||
|
||||
if(navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1) {
|
||||
this.info('Running on Gecko');
|
||||
this.assert(Prototype.Browser.Gecko);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,58 +0,0 @@
|
|||
new Test.Unit.Runner({
|
||||
|
||||
testInclude: function() {
|
||||
this.assert(!$R(0, 0, true).include(0));
|
||||
this.assert($R(0, 0, false).include(0));
|
||||
|
||||
this.assert($R(0, 5, true).include(0));
|
||||
this.assert($R(0, 5, true).include(4));
|
||||
this.assert(!$R(0, 5, true).include(5));
|
||||
|
||||
this.assert($R(0, 5, false).include(0));
|
||||
this.assert($R(0, 5, false).include(5));
|
||||
this.assert(!$R(0, 5, false).include(6));
|
||||
},
|
||||
|
||||
testEach: function() {
|
||||
var results = [];
|
||||
$R(0, 0, true).each(function(value) {
|
||||
results.push(value);
|
||||
});
|
||||
|
||||
this.assertEnumEqual([], results);
|
||||
|
||||
results = [];
|
||||
$R(0, 3, false).each(function(value) {
|
||||
results.push(value);
|
||||
});
|
||||
|
||||
this.assertEnumEqual([0, 1, 2, 3], results);
|
||||
},
|
||||
|
||||
testAny: function() {
|
||||
this.assert(!$R(1, 1, true).any());
|
||||
this.assert($R(0, 3, false).any(function(value) {
|
||||
return value == 3;
|
||||
}));
|
||||
},
|
||||
|
||||
testAll: function() {
|
||||
this.assert($R(1, 1, true).all());
|
||||
this.assert($R(0, 3, false).all(function(value) {
|
||||
return value <= 3;
|
||||
}));
|
||||
},
|
||||
|
||||
testToArray: function() {
|
||||
this.assertEnumEqual([], $R(0, 0, true).toArray());
|
||||
this.assertEnumEqual([0], $R(0, 0, false).toArray());
|
||||
this.assertEnumEqual([0], $R(0, 1, true).toArray());
|
||||
this.assertEnumEqual([0, 1], $R(0, 1, false).toArray());
|
||||
this.assertEnumEqual([-3, -2, -1, 0, 1, 2], $R(-3, 3, true).toArray());
|
||||
this.assertEnumEqual([-3, -2, -1, 0, 1, 2, 3], $R(-3, 3, false).toArray());
|
||||
},
|
||||
|
||||
testDefaultsToNotExclusive: function() {
|
||||
this.assertEnumEqual($R(-3,3), $R(-3,3,false));
|
||||
}
|
||||
});
|
|
@ -1,42 +0,0 @@
|
|||
new Test.Unit.Runner({
|
||||
testRegExpEscape: function() {
|
||||
this.assertEqual('word', RegExp.escape('word'));
|
||||
this.assertEqual('\\/slashes\\/', RegExp.escape('/slashes/'));
|
||||
this.assertEqual('\\\\backslashes\\\\', RegExp.escape('\\backslashes\\'));
|
||||
this.assertEqual('\\\\border of word', RegExp.escape('\\border of word'));
|
||||
|
||||
this.assertEqual('\\(\\?\\:non-capturing\\)', RegExp.escape('(?:non-capturing)'));
|
||||
this.assertEqual('non-capturing', new RegExp(RegExp.escape('(?:') + '([^)]+)').exec('(?:non-capturing)')[1]);
|
||||
|
||||
this.assertEqual('\\(\\?\\=positive-lookahead\\)', RegExp.escape('(?=positive-lookahead)'));
|
||||
this.assertEqual('positive-lookahead', new RegExp(RegExp.escape('(?=') + '([^)]+)').exec('(?=positive-lookahead)')[1]);
|
||||
|
||||
this.assertEqual('\\(\\?<\\=positive-lookbehind\\)', RegExp.escape('(?<=positive-lookbehind)'));
|
||||
this.assertEqual('positive-lookbehind', new RegExp(RegExp.escape('(?<=') + '([^)]+)').exec('(?<=positive-lookbehind)')[1]);
|
||||
|
||||
this.assertEqual('\\(\\?\\!negative-lookahead\\)', RegExp.escape('(?!negative-lookahead)'));
|
||||
this.assertEqual('negative-lookahead', new RegExp(RegExp.escape('(?!') + '([^)]+)').exec('(?!negative-lookahead)')[1]);
|
||||
|
||||
this.assertEqual('\\(\\?<\\!negative-lookbehind\\)', RegExp.escape('(?<!negative-lookbehind)'));
|
||||
this.assertEqual('negative-lookbehind', new RegExp(RegExp.escape('(?<!') + '([^)]+)').exec('(?<!negative-lookbehind)')[1]);
|
||||
|
||||
this.assertEqual('\\[\\\\w\\]\\+', RegExp.escape('[\\w]+'));
|
||||
this.assertEqual('character class', new RegExp(RegExp.escape('[') + '([^\\]]+)').exec('[character class]')[1]);
|
||||
|
||||
this.assertEqual('<div>', new RegExp(RegExp.escape('<div>')).exec('<td><div></td>')[0]);
|
||||
|
||||
this.assertEqual('false', RegExp.escape(false));
|
||||
this.assertEqual('undefined', RegExp.escape());
|
||||
this.assertEqual('null', RegExp.escape(null));
|
||||
this.assertEqual('42', RegExp.escape(42));
|
||||
|
||||
this.assertEqual('\\\\n\\\\r\\\\t', RegExp.escape('\\n\\r\\t'));
|
||||
this.assertEqual('\n\r\t', RegExp.escape('\n\r\t'));
|
||||
this.assertEqual('\\{5,2\\}', RegExp.escape('{5,2}'));
|
||||
|
||||
this.assertEqual(
|
||||
'\\/\\(\\[\\.\\*\\+\\?\\^\\=\\!\\:\\$\\{\\}\\(\\)\\|\\[\\\\\\]\\\\\\\/\\\\\\\\\\]\\)\\/g',
|
||||
RegExp.escape('/([.*+?^=!:${}()|[\\]\\/\\\\])/g')
|
||||
);
|
||||
}
|
||||
});
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
<div id="test_div_parent" class="test_class">
|
||||
<div id="test_div_child" class="test_class">
|
||||
</div>
|
||||
</div>
|
||||
*/
|
||||
|
||||
new Test.Unit.Runner({
|
||||
testEngine: function() {
|
||||
this.assert(Prototype.Selector.engine);
|
||||
},
|
||||
|
||||
testSelect: function() {
|
||||
var elements = Prototype.Selector.select('.test_class');
|
||||
|
||||
this.assert(Object.isArray(elements));
|
||||
this.assertEqual(2, elements.length);
|
||||
this.assertEqual('test_div_parent', elements[0].id);
|
||||
this.assertEqual('test_div_child', elements[1].id);
|
||||
},
|
||||
|
||||
testSelectWithContext: function() {
|
||||
var elements = Prototype.Selector.select('.test_class', $('test_div_parent'));
|
||||
|
||||
this.assert(Object.isArray(elements));
|
||||
this.assertEqual(1, elements.length);
|
||||
this.assertEqual('test_div_child', elements[0].id);
|
||||
},
|
||||
|
||||
testSelectWithEmptyResult: function() {
|
||||
var elements = Prototype.Selector.select('.non_existent');
|
||||
|
||||
this.assert(Object.isArray(elements));
|
||||
this.assertEqual(0, elements.length);
|
||||
},
|
||||
|
||||
testMatch: function() {
|
||||
var element = $('test_div_parent');
|
||||
|
||||
this.assertEqual(true, Prototype.Selector.match(element, '.test_class'));
|
||||
this.assertEqual(false, Prototype.Selector.match(element, '.non_existent'));
|
||||
},
|
||||
|
||||
testFind: function() {
|
||||
var elements = document.getElementsByTagName('*'),
|
||||
expression = '.test_class';
|
||||
this.assertEqual('test_div_parent', Prototype.Selector.find(elements, expression).id);
|
||||
this.assertEqual('test_div_child', Prototype.Selector.find(elements, expression, 1).id);
|
||||
}
|
||||
});
|
|
@ -1,414 +0,0 @@
|
|||
var $RunBenchmarks = false;
|
||||
|
||||
function reduce(arr) {
|
||||
return arr.length > 1 ? arr : arr[0];
|
||||
}
|
||||
|
||||
new Test.Unit.Runner({
|
||||
|
||||
testSelectorWithTagName: function() {
|
||||
this.assertEnumEqual($A(document.getElementsByTagName('li')), $$('li'));
|
||||
this.assertEnumEqual([$('strong')], $$('strong'));
|
||||
this.assertEnumEqual([], $$('nonexistent'));
|
||||
|
||||
var allNodes = $A(document.getElementsByTagName('*')).select( function(node) {
|
||||
return node.tagName !== '!';
|
||||
});
|
||||
this.assertEnumEqual(allNodes, $$('*'));
|
||||
},
|
||||
|
||||
testSelectorWithId: function() {
|
||||
this.assertEnumEqual([$('fixtures')], $$('#fixtures'));
|
||||
this.assertEnumEqual([], $$('#nonexistent'));
|
||||
this.assertEnumEqual([$('troubleForm')], $$('#troubleForm'));
|
||||
},
|
||||
|
||||
testSelectorWithClassName: function() {
|
||||
this.assertEnumEqual($('p', 'link_1', 'item_1'), $$('.first'));
|
||||
this.assertEnumEqual([], $$('.second'));
|
||||
},
|
||||
|
||||
testSelectorWithTagNameAndId: function() {
|
||||
this.assertEnumEqual([$('strong')], $$('strong#strong'));
|
||||
this.assertEnumEqual([], $$('p#strong'));
|
||||
},
|
||||
|
||||
testSelectorWithTagNameAndClassName: function() {
|
||||
this.assertEnumEqual($('link_1', 'link_2'), $$('a.internal'));
|
||||
this.assertEnumEqual([$('link_2')], $$('a.internal.highlight'));
|
||||
this.assertEnumEqual([$('link_2')], $$('a.highlight.internal'));
|
||||
this.assertEnumEqual([], $$('a.highlight.internal.nonexistent'));
|
||||
},
|
||||
|
||||
testSelectorWithIdAndClassName: function() {
|
||||
this.assertEnumEqual([$('link_2')], $$('#link_2.internal'));
|
||||
this.assertEnumEqual([$('link_2')], $$('.internal#link_2'));
|
||||
this.assertEnumEqual([$('link_2')], $$('#link_2.internal.highlight'));
|
||||
this.assertEnumEqual([], $$('#link_2.internal.nonexistent'));
|
||||
},
|
||||
|
||||
testSelectorWithTagNameAndIdAndClassName: function() {
|
||||
this.assertEnumEqual([$('link_2')], $$('a#link_2.internal'));
|
||||
this.assertEnumEqual([$('link_2')], $$('a.internal#link_2'));
|
||||
this.assertEnumEqual([$('item_1')], $$('li#item_1.first'));
|
||||
this.assertEnumEqual([], $$('li#item_1.nonexistent'));
|
||||
this.assertEnumEqual([], $$('li#item_1.first.nonexistent'));
|
||||
},
|
||||
|
||||
test$$MatchesAncestryWithTokensSeparatedByWhitespace: function() {
|
||||
this.assertEnumEqual($('em2', 'em', 'span'), $$('#fixtures a *'));
|
||||
this.assertEnumEqual([$('p')], $$('div#fixtures p'));
|
||||
},
|
||||
|
||||
test$$CombinesResultsWhenMultipleExpressionsArePassed: function() {
|
||||
this.assertEnumEqual($('link_1', 'link_2', 'item_1', 'item_2', 'item_3'), $$('#p a', ' ul#list li '));
|
||||
},
|
||||
|
||||
testSelectorWithTagNameAndAttributeExistence: function() {
|
||||
this.assertEnumEqual($$('#fixtures h1'), $$('h1[class]'), 'h1[class]');
|
||||
this.assertEnumEqual($$('#fixtures h1'), $$('h1[CLASS]'), 'h1[CLASS]');
|
||||
this.assertEnumEqual([$('item_3')], $$('li#item_3[class]'), 'li#item_3[class]');
|
||||
},
|
||||
|
||||
testSelectorWithTagNameAndSpecificAttributeValue: function() {
|
||||
this.assertEnumEqual($('link_1', 'link_2', 'link_3'), $$('a[href="#"]'));
|
||||
this.assertEnumEqual($('link_1', 'link_2', 'link_3'), $$('a[href=#]'));
|
||||
},
|
||||
|
||||
testSelectorWithTagNameAndWhitespaceTokenizedAttributeValue: function() {
|
||||
this.assertEnumEqual($('link_1', 'link_2'), $$('a[class~="internal"]'), "a[class~=\"internal\"]");
|
||||
this.assertEnumEqual($('link_1', 'link_2'), $$('a[class~=internal]'), "a[class~=internal]");
|
||||
},
|
||||
|
||||
testSelectorWithAttributeAndNoTagName: function() {
|
||||
this.assertEnumEqual($(document.body).select('a[href]'), $(document.body).select('[href]'));
|
||||
this.assertEnumEqual($$('a[class~="internal"]'), $$('[class~=internal]'));
|
||||
this.assertEnumEqual($$('*[id]'), $$('[id]'));
|
||||
this.assertEnumEqual($('checked_radio', 'unchecked_radio'), $$('[type=radio]'));
|
||||
this.assertEnumEqual($$('*[type=checkbox]'), $$('[type=checkbox]'));
|
||||
this.assertEnumEqual($('with_title', 'commaParent'), $$('[title]'));
|
||||
this.assertEnumEqual($$('#troubleForm *[type=radio]'), $$('#troubleForm [type=radio]'));
|
||||
this.assertEnumEqual($$('#troubleForm *[type]'), $$('#troubleForm [type]'));
|
||||
},
|
||||
|
||||
testSelectorWithAttributeContainingDash: function() {
|
||||
this.assertEnumEqual([$('attr_with_dash')], $$('[foo-bar]'), "attribute with hyphen");
|
||||
},
|
||||
|
||||
testSelectorWithUniversalAndHyphenTokenizedAttributeValue: function() {
|
||||
this.assertEnumEqual([$('item_3')], $$('*[xml:lang|="es"]'));
|
||||
this.assertEnumEqual([$('item_3')], $$('*[xml:lang|="ES"]'));
|
||||
},
|
||||
|
||||
testSelectorWithTagNameAndNegatedAttributeValue: function() {
|
||||
this.assertEnumEqual([], $$('a[href!="#"]'));
|
||||
},
|
||||
|
||||
testSelectorWithBracketAttributeValue: function() {
|
||||
this.assertEnumEqual($('chk_1', 'chk_2'), $$('#troubleForm2 input[name="brackets[5][]"]'));
|
||||
this.assertEnumEqual([$('chk_1')], $$('#troubleForm2 input[name="brackets[5][]"]:checked'));
|
||||
this.assertEnumEqual([$('chk_2')], $$('#troubleForm2 input[name="brackets[5][]"][value=2]'));
|
||||
this.assertEnumEqual([], $$('#troubleForm2 input[name=brackets[5][]]'));
|
||||
},
|
||||
|
||||
test$$WithNestedAttributeSelectors: function() {
|
||||
this.assertEnumEqual([$('strong')], $$('div[style] p[id] strong'), 'div[style] p[id] strong');
|
||||
},
|
||||
|
||||
testSelectorWithMultipleConditions: function() {
|
||||
this.assertEnumEqual([$('link_3')], $$('a[class~=external][href="#"]'),
|
||||
'a[class~=external][href="#"]');
|
||||
this.assertEnumEqual([], $$('a[class~=external][href!="#"]'),
|
||||
'a[class~=external][href!="#"]');
|
||||
},
|
||||
|
||||
testSelectorMatchElements: function() {
|
||||
this.assertElementsMatch(Selector.matchElements($('list').descendants(), 'li'), '#item_1', '#item_2', '#item_3');
|
||||
this.assertElementsMatch(Selector.matchElements($('fixtures').descendants(), 'a.internal'), '#link_1', '#link_2');
|
||||
this.assertEnumEqual([], Selector.matchElements($('fixtures').descendants(), 'p.last'));
|
||||
this.assertElementsMatch(Selector.matchElements($('fixtures').descendants(), '.inexistant, a.internal'), '#link_1', '#link_2');
|
||||
},
|
||||
|
||||
testSelectorFindElement: function() {
|
||||
this.assertElementMatches(Selector.findElement($('list').descendants(), 'li'), 'li#item_1.first');
|
||||
this.assertElementMatches(Selector.findElement($('list').descendants(), 'li', 1), 'li#item_2');
|
||||
this.assertElementMatches(Selector.findElement($('list').descendants(), 'li#item_3'), 'li');
|
||||
this.assertEqual(undefined, Selector.findElement($('list').descendants(), 'em'));
|
||||
},
|
||||
|
||||
testElementMatch: function() {
|
||||
var span = $('dupL1');
|
||||
|
||||
// tests that should pass
|
||||
this.assert(span.match('span'));
|
||||
this.assert(span.match('span#dupL1'));
|
||||
this.assert(span.match('div > span'), 'child combinator');
|
||||
this.assert(span.match('#dupContainer span'), 'descendant combinator');
|
||||
this.assert(span.match('#dupL1'), 'ID only');
|
||||
this.assert(span.match('span.span_foo'), 'class name 1');
|
||||
this.assert(span.match('span.span_bar'), 'class name 2');
|
||||
this.assert(span.match('span:first-child'), 'first-child pseudoclass');
|
||||
|
||||
this.assert(!span.match('span.span_wtf'), 'bogus class name');
|
||||
this.assert(!span.match('#dupL2'), 'different ID');
|
||||
this.assert(!span.match('div'), 'different tag name');
|
||||
this.assert(!span.match('span span'), 'different ancestry');
|
||||
this.assert(!span.match('span > span'), 'different parent');
|
||||
this.assert(!span.match('span:nth-child(5)'), 'different pseudoclass');
|
||||
|
||||
this.assert(!$('link_2').match('a[rel^=external]'));
|
||||
this.assert($('link_1').match('a[rel^=external]'));
|
||||
this.assert($('link_1').match('a[rel^="external"]'));
|
||||
this.assert($('link_1').match("a[rel^='external']"));
|
||||
|
||||
this.assert(span.match({ match: function(element) { return true }}), 'custom selector');
|
||||
this.assert(!span.match({ match: function(element) { return false }}), 'custom selector');
|
||||
},
|
||||
|
||||
testSelectorWithSpaceInAttributeValue: function() {
|
||||
this.assertEnumEqual([$('with_title')], $$('cite[title="hello world!"]'));
|
||||
},
|
||||
|
||||
// AND NOW COME THOSE NEW TESTS AFTER ANDREW'S REWRITE!
|
||||
|
||||
testSelectorWithNamespacedAttributes: function() {
|
||||
if (Prototype.BrowserFeatures.XPath) {
|
||||
this.assertUndefined(new Selector('html[xml:lang]').xpath);
|
||||
this.assertUndefined(new Selector('body p[xml:lang]').xpath);
|
||||
} else
|
||||
this.info("Could not test XPath bypass: no XPath to begin with!");
|
||||
|
||||
this.assertElementsMatch($$('[xml:lang]'), 'html', '#item_3');
|
||||
this.assertElementsMatch($$('*[xml:lang]'), 'html', '#item_3');
|
||||
},
|
||||
|
||||
testSelectorWithChild: function() {
|
||||
this.assertEnumEqual($('link_1', 'link_2'), $$('p.first > a'));
|
||||
this.assertEnumEqual($('father', 'uncle'), $$('div#grandfather > div'));
|
||||
this.assertEnumEqual($('level2_1', 'level2_2'), $$('#level1>span'));
|
||||
this.assertEnumEqual($('level2_1', 'level2_2'), $$('#level1 > span'));
|
||||
this.assertEnumEqual($('level3_1', 'level3_2'), $$('#level2_1 > *'));
|
||||
this.assertEnumEqual([], $$('div > #nonexistent'));
|
||||
$RunBenchmarks && this.wait(500, function() {
|
||||
this.benchmark(function() { $$('#level1 > span') }, 1000);
|
||||
});
|
||||
},
|
||||
|
||||
testSelectorWithAdjacence: function() {
|
||||
this.assertEnumEqual([$('uncle')], $$('div.brothers + div.brothers'));
|
||||
this.assertEnumEqual([$('uncle')], $$('div.brothers + div'));
|
||||
this.assertEqual($('level2_2'), reduce($$('#level2_1+span')));
|
||||
this.assertEqual($('level2_2'), reduce($$('#level2_1 + span')));
|
||||
this.assertEqual($('level2_2'), reduce($$('#level2_1 + *')));
|
||||
this.assertEnumEqual([], $$('#level2_2 + span'));
|
||||
this.assertEqual($('level3_2'), reduce($$('#level3_1 + span')));
|
||||
this.assertEqual($('level3_2'), reduce($$('#level3_1 + *')));
|
||||
this.assertEnumEqual([], $$('#level3_2 + *'));
|
||||
this.assertEnumEqual([], $$('#level3_1 + em'));
|
||||
$RunBenchmarks && this.wait(500, function() {
|
||||
this.benchmark(function() { $$('#level3_1 + span') }, 1000);
|
||||
});
|
||||
},
|
||||
|
||||
testSelectorWithLaterSibling: function() {
|
||||
this.assertEnumEqual([$('list')], $$('h1 ~ ul'));
|
||||
this.assertEqual($('level2_2'), reduce($$('#level2_1 ~ span')));
|
||||
this.assertEnumEqual($('level2_2', 'level2_3'), reduce($$('#level2_1 ~ *')));
|
||||
this.assertEnumEqual([], $$('#level2_2 ~ span'));
|
||||
this.assertEnumEqual([], $$('#level3_2 ~ *'));
|
||||
this.assertEnumEqual([], $$('#level3_1 ~ em'));
|
||||
this.assertEnumEqual([$('level3_2')], $$('#level3_1 ~ #level3_2'));
|
||||
this.assertEnumEqual([$('level3_2')], $$('span ~ #level3_2'));
|
||||
this.assertEnumEqual([], $$('div ~ #level3_2'));
|
||||
this.assertEnumEqual([], $$('div ~ #level2_3'));
|
||||
$RunBenchmarks && this.wait(500, function() {
|
||||
this.benchmark(function() { $$('#level2_1 ~ span') }, 1000);
|
||||
});
|
||||
},
|
||||
|
||||
testSelectorWithNewAttributeOperators: function() {
|
||||
this.assertEnumEqual($('father', 'uncle'), $$('div[class^=bro]'), 'matching beginning of string');
|
||||
this.assertEnumEqual($('father', 'uncle'), $$('div[class$=men]'), 'matching end of string');
|
||||
this.assertEnumEqual($('father', 'uncle'), $$('div[class*="ers m"]'), 'matching substring')
|
||||
this.assertEnumEqual($('level2_1', 'level2_2', 'level2_3'), $$('#level1 *[id^="level2_"]'));
|
||||
this.assertEnumEqual($('level2_1', 'level2_2', 'level2_3'), $$('#level1 *[id^=level2_]'));
|
||||
this.assertEnumEqual($('level2_1', 'level3_1'), $$('#level1 *[id$="_1"]'));
|
||||
this.assertEnumEqual($('level2_1', 'level3_1'), $$('#level1 *[id$=_1]'));
|
||||
this.assertEnumEqual($('level2_1', 'level3_2', 'level2_2', 'level2_3'), $$('#level1 *[id*="2"]'));
|
||||
this.assertEnumEqual($('level2_1', 'level3_2', 'level2_2', 'level2_3'), $$('#level1 *[id*=2]'));
|
||||
$RunBenchmarks && this.wait(500, function() {
|
||||
this.benchmark(function() { $$('#level1 *[id^=level2_]') }, 1000, '[^=]');
|
||||
this.benchmark(function() { $$('#level1 *[id$=_1]') }, 1000, '[$=]');
|
||||
this.benchmark(function() { $$('#level1 *[id*=_2]') }, 1000, '[*=]');
|
||||
});
|
||||
},
|
||||
|
||||
testSelectorWithDuplicates: function() {
|
||||
this.assertEnumEqual($$('div div'), $$('div div').uniq());
|
||||
this.assertEnumEqual($('dupL2', 'dupL3', 'dupL4', 'dupL5'), $$('#dupContainer span span'));
|
||||
$RunBenchmarks && this.wait(500, function() {
|
||||
this.benchmark(function() { $$('#dupContainer span span') }, 1000);
|
||||
});
|
||||
},
|
||||
|
||||
testSelectorWithFirstLastOnlyNthNthLastChild: function() {
|
||||
this.assertEnumEqual([$('level2_1')], $$('#level1>*:first-child'));
|
||||
this.assertEnumEqual($('level2_1', 'level3_1', 'level_only_child'), $$('#level1 *:first-child'));
|
||||
this.assertEnumEqual([$('level2_3')], $$('#level1>*:last-child'));
|
||||
this.assertEnumEqual($('level3_2', 'level_only_child', 'level2_3'), $$('#level1 *:last-child'));
|
||||
this.assertEnumEqual([$('level2_3')], $$('#level1>div:last-child'));
|
||||
this.assertEnumEqual([$('level2_3')], $$('#level1 div:last-child'));
|
||||
this.assertEnumEqual([], $$('#level1>div:first-child'));
|
||||
this.assertEnumEqual([], $$('#level1>span:last-child'));
|
||||
this.assertEnumEqual($('level2_1', 'level3_1'), $$('#level1 span:first-child'));
|
||||
this.assertEnumEqual([], $$('#level1:first-child'));
|
||||
this.assertEnumEqual([], $$('#level1>*:only-child'));
|
||||
this.assertEnumEqual([$('level_only_child')], $$('#level1 *:only-child'));
|
||||
this.assertEnumEqual([], $$('#level1:only-child'));
|
||||
this.assertEnumEqual([$('link_2')], $$('#p *:nth-last-child(2)'), 'nth-last-child');
|
||||
this.assertEnumEqual([$('link_2')], $$('#p *:nth-child(3)'), 'nth-child');
|
||||
this.assertEnumEqual([$('link_2')], $$('#p a:nth-child(3)'), 'nth-child');
|
||||
this.assertEnumEqual($('item_2', 'item_3'), $$('#list > li:nth-child(n+2)'));
|
||||
this.assertEnumEqual($('item_1', 'item_2'), $$('#list > li:nth-child(-n+2)'));
|
||||
$RunBenchmarks && this.wait(500, function() {
|
||||
this.benchmark(function() { $$('#level1 *:first-child') }, 1000, ':first-child');
|
||||
this.benchmark(function() { $$('#level1 *:last-child') }, 1000, ':last-child');
|
||||
this.benchmark(function() { $$('#level1 *:only-child') }, 1000, ':only-child');
|
||||
});
|
||||
},
|
||||
|
||||
testSelectorWithFirstLastNthNthLastOfType: function() {
|
||||
this.assertEnumEqual([$('link_2')], $$('#p a:nth-of-type(2)'), 'nth-of-type');
|
||||
this.assertEnumEqual([$('link_1')], $$('#p a:nth-of-type(1)'), 'nth-of-type');
|
||||
this.assertEnumEqual([$('link_2')], $$('#p a:nth-last-of-type(1)'), 'nth-last-of-type');
|
||||
this.assertEnumEqual([$('link_1')], $$('#p a:first-of-type'), 'first-of-type');
|
||||
this.assertEnumEqual([$('link_2')], $$('#p a:last-of-type'), 'last-of-type');
|
||||
},
|
||||
|
||||
testSelectorWithNot: function() {
|
||||
this.assertEnumEqual([$('link_2')], $$('#p a:not(a:first-of-type)'), 'first-of-type');
|
||||
this.assertEnumEqual([$('link_1')], $$('#p a:not(a:last-of-type)'), 'last-of-type');
|
||||
this.assertEnumEqual([$('link_2')], $$('#p a:not(a:nth-of-type(1))'), 'nth-of-type');
|
||||
this.assertEnumEqual([$('link_1')], $$('#p a:not(a:nth-last-of-type(1))'), 'nth-last-of-type');
|
||||
this.assertEnumEqual([$('link_2')], $$('#p a:not([rel~=nofollow])'), 'attribute 1');
|
||||
this.assertEnumEqual([$('link_2')], $$('#p a:not(a[rel^=external])'), 'attribute 2');
|
||||
this.assertEnumEqual([$('link_2')], $$('#p a:not(a[rel$=nofollow])'), 'attribute 3');
|
||||
this.assertEnumEqual([$('em')], $$('#p a:not(a[rel$="nofollow"]) > em'), 'attribute 4')
|
||||
this.assertEnumEqual([$('item_2')], $$('#list li:not(#item_1):not(#item_3)'), 'adjacent :not clauses');
|
||||
this.assertEnumEqual([$('son')], $$('#grandfather > div:not(#uncle) #son'));
|
||||
this.assertEnumEqual([$('em')], $$('#p a:not(a[rel$="nofollow"]) em'), 'attribute 4 + all descendants');
|
||||
this.assertEnumEqual([$('em')], $$('#p a:not(a[rel$="nofollow"])>em'), 'attribute 4 (without whitespace)');
|
||||
},
|
||||
|
||||
testSelectorWithEnabledDisabledChecked: function() {
|
||||
this.assertEnumEqual([$('disabled_text_field')], $$('#troubleForm > *:disabled'));
|
||||
this.assertEnumEqual($('troubleForm').getInputs().without($('disabled_text_field'), $('hidden')), $$('#troubleForm > *:enabled'));
|
||||
this.assertEnumEqual($('checked_box', 'checked_radio'), $$('#troubleForm *:checked'));
|
||||
},
|
||||
|
||||
testSelectorWithEmpty: function() {
|
||||
$('level3_1').innerHTML = "";
|
||||
this.assertEnumEqual($('level3_1', 'level3_2', 'level2_3'),
|
||||
$$('#level1 *:empty'), '#level1 *:empty');
|
||||
this.assertEnumEqual([], $$('#level_only_child:empty'), 'newlines count as content!');
|
||||
},
|
||||
|
||||
testIdenticalResultsFromEquivalentSelectors: function() {
|
||||
this.assertEnumEqual($$('div.brothers'), $$('div[class~=brothers]'));
|
||||
this.assertEnumEqual($$('div.brothers'), $$('div[class~=brothers].brothers'));
|
||||
this.assertEnumEqual($$('div:not(.brothers)'), $$('div:not([class~=brothers])'));
|
||||
this.assertEnumEqual($$('li ~ li'), $$('li:not(:first-child)'));
|
||||
this.assertEnumEqual($$('ul > li'), $$('ul > li:nth-child(n)'));
|
||||
this.assertEnumEqual($$('ul > li:nth-child(even)'), $$('ul > li:nth-child(2n)'));
|
||||
this.assertEnumEqual($$('ul > li:nth-child(odd)'), $$('ul > li:nth-child(2n+1)'));
|
||||
this.assertEnumEqual($$('ul > li:first-child'), $$('ul > li:nth-child(1)'));
|
||||
this.assertEnumEqual($$('ul > li:last-child'), $$('ul > li:nth-last-child(1)'));
|
||||
this.assertEnumEqual($$('ul > li:nth-child(n-999)'), $$('ul > li'));
|
||||
this.assertEnumEqual($$('ul>li'), $$('ul > li'));
|
||||
this.assertEnumEqual($$('#p a:not(a[rel$="nofollow"])>em'), $$('#p a:not(a[rel$="nofollow"]) > em'))
|
||||
},
|
||||
|
||||
testSelectorsThatShouldReturnNothing: function() {
|
||||
this.assertEnumEqual([], $$('span:empty > *'));
|
||||
this.assertEnumEqual([], $$('div.brothers:not(.brothers)'));
|
||||
this.assertEnumEqual([], $$('#level2_2 :only-child:not(:last-child)'));
|
||||
this.assertEnumEqual([], $$('#level2_2 :only-child:not(:first-child)'));
|
||||
},
|
||||
|
||||
testCommasFor$$: function() {
|
||||
this.assertEnumEqual($('list', 'p', 'link_1', 'item_1', 'item_3', 'troubleForm'), $$('#list, .first,*[xml:lang="es-us"] , #troubleForm'));
|
||||
this.assertEnumEqual($('list', 'p', 'link_1', 'item_1', 'item_3', 'troubleForm'), $$('#list, .first,', '*[xml:lang="es-us"] , #troubleForm'));
|
||||
this.assertEnumEqual($('commaParent', 'commaChild'), $$('form[title*="commas,"], input[value="#commaOne,#commaTwo"]'));
|
||||
this.assertEnumEqual($('commaParent', 'commaChild'), $$('form[title*="commas,"]', 'input[value="#commaOne,#commaTwo"]'));
|
||||
},
|
||||
|
||||
testSelectorExtendsAllNodes: function(){
|
||||
var element = document.createElement('div');
|
||||
(3).times(function(){
|
||||
element.appendChild(document.createElement('div'));
|
||||
});
|
||||
element.setAttribute('id','scratch_element');
|
||||
$$('body')[0].appendChild(element);
|
||||
|
||||
var results = $$('#scratch_element div');
|
||||
this.assert(typeof results[0].show == 'function');
|
||||
this.assert(typeof results[1].show == 'function');
|
||||
this.assert(typeof results[2].show == 'function');
|
||||
},
|
||||
|
||||
testCopiedNodesGetIncluded: function() {
|
||||
this.assertElementsMatch(
|
||||
Selector.matchElements($('counted_container').descendants(), 'div'),
|
||||
'div.is_counted'
|
||||
);
|
||||
$('counted_container').innerHTML += $('counted_container').innerHTML;
|
||||
this.assertElementsMatch(
|
||||
Selector.matchElements($('counted_container').descendants(), 'div'), 'div.is_counted',
|
||||
'div.is_counted'
|
||||
);
|
||||
},
|
||||
|
||||
testSelectorNotInsertedNodes: function() {
|
||||
window.debug = true;
|
||||
var wrapper = new Element("div");
|
||||
wrapper.update("<table><tr><td id='myTD'></td></tr></table>");
|
||||
this.assertNotNullOrUndefined(wrapper.select('[id=myTD]')[0],
|
||||
'selecting: [id=myTD]');
|
||||
this.assertNotNullOrUndefined(wrapper.select('#myTD')[0],
|
||||
'selecting: #myTD');
|
||||
this.assertNotNullOrUndefined(wrapper.select('td')[0],
|
||||
'selecting: td');
|
||||
this.assert($$('#myTD').length == 0,
|
||||
'should not turn up in document-rooted search');
|
||||
window.debug = false;
|
||||
},
|
||||
|
||||
testElementDown: function() {
|
||||
var a = $('dupL4');
|
||||
var b = $('dupContainer').down('#dupL4');
|
||||
|
||||
this.assertEqual(a, b);
|
||||
},
|
||||
testElementDownWithDotAndColon: function() {
|
||||
var a = $('dupL4_dotcolon');
|
||||
var b = $('dupContainer.withdot:active').down('#dupL4_dotcolon');
|
||||
var c = $('dupContainer.withdot:active').select('#dupL4_dotcolon');
|
||||
|
||||
this.assertEqual(a, b);
|
||||
this.assertEnumEqual([a], c);
|
||||
},
|
||||
|
||||
testDescendantSelectorBuggy: function() {
|
||||
var el = document.createElement('div');
|
||||
el.innerHTML = '<ul><li></li></ul><div><ul><li></li></ul></div>';
|
||||
document.body.appendChild(el);
|
||||
this.assertEqual(2, $(el).select('ul li').length);
|
||||
document.body.removeChild(el);
|
||||
},
|
||||
|
||||
testFindElementWithIndexWhenElementsAreNotInDocumentOrder: function() {
|
||||
var ancestors = $("target_1").ancestors();
|
||||
this.assertEqual($("container_2"), Selector.findElement(ancestors, "[container], .container", 0));
|
||||
this.assertEqual($("container_1"), Selector.findElement(ancestors, "[container], .container", 1));
|
||||
}
|
||||
});
|
|
@ -1,555 +0,0 @@
|
|||
new Test.Unit.Runner({
|
||||
testInterpret: function(){
|
||||
this.assertIdentical('true', String.interpret(true));
|
||||
this.assertIdentical('123', String.interpret(123));
|
||||
this.assertIdentical('foo bar', String.interpret('foo bar'));
|
||||
this.assertIdentical(
|
||||
'object string',
|
||||
String.interpret({ toString: function(){ return 'object string' } }));
|
||||
|
||||
this.assertIdentical('0', String.interpret(0));
|
||||
this.assertIdentical('false', String.interpret(false));
|
||||
this.assertIdentical('', String.interpret(undefined));
|
||||
this.assertIdentical('', String.interpret(null));
|
||||
this.assertIdentical('', String.interpret(''));
|
||||
},
|
||||
|
||||
testGsubWithReplacementFunction: function() {
|
||||
var source = 'foo boo boz';
|
||||
|
||||
this.assertEqual('Foo Boo BoZ',
|
||||
source.gsub(/[^o]+/, function(match) {
|
||||
return match[0].toUpperCase()
|
||||
}));
|
||||
this.assertEqual('f2 b2 b1z',
|
||||
source.gsub(/o+/, function(match) {
|
||||
return match[0].length;
|
||||
}));
|
||||
this.assertEqual('f0 b0 b1z',
|
||||
source.gsub(/o+/, function(match) {
|
||||
return match[0].length % 2;
|
||||
}));
|
||||
|
||||
},
|
||||
|
||||
testGsubWithReplacementString: function() {
|
||||
var source = 'foo boo boz';
|
||||
|
||||
this.assertEqual('foobooboz',
|
||||
source.gsub(/\s+/, ''));
|
||||
this.assertEqual(' z',
|
||||
source.gsub(/(.)(o+)/, ''));
|
||||
|
||||
this.assertEqual('ウィメンズ2007<br/>クルーズコレクション',
|
||||
'ウィメンズ2007\nクルーズコレクション'.gsub(/\n/,'<br/>'));
|
||||
this.assertEqual('ウィメンズ2007<br/>クルーズコレクション',
|
||||
'ウィメンズ2007\nクルーズコレクション'.gsub('\n','<br/>'));
|
||||
|
||||
this.assertEqual('barfbarobarobar barbbarobarobar barbbarobarzbar',
|
||||
source.gsub('', 'bar'));
|
||||
this.assertEqual('barfbarobarobar barbbarobarobar barbbarobarzbar',
|
||||
source.gsub(new RegExp(''), 'bar'));
|
||||
},
|
||||
|
||||
testGsubWithReplacementTemplateString: function() {
|
||||
var source = 'foo boo boz';
|
||||
|
||||
this.assertEqual('-oo-#{1}- -oo-#{1}- -o-#{1}-z',
|
||||
source.gsub(/(.)(o+)/, '-#{2}-\\#{1}-'));
|
||||
this.assertEqual('-foo-f- -boo-b- -bo-b-z',
|
||||
source.gsub(/(.)(o+)/, '-#{0}-#{1}-'));
|
||||
this.assertEqual('-oo-f- -oo-b- -o-b-z',
|
||||
source.gsub(/(.)(o+)/, '-#{2}-#{1}-'));
|
||||
this.assertEqual(' z',
|
||||
source.gsub(/(.)(o+)/, '#{3}'));
|
||||
},
|
||||
|
||||
testGsubWithTroublesomeCharacters: function() {
|
||||
this.assertEqual('ab', 'a|b'.gsub('|', ''));
|
||||
//'ab'.gsub('', ''); // freeze
|
||||
this.assertEqual('ab', 'ab(?:)'.gsub('(?:)', ''));
|
||||
this.assertEqual('ab', 'ab()'.gsub('()', ''));
|
||||
this.assertEqual('ab', 'ab'.gsub('^', ''));
|
||||
this.assertEqual('ab', 'a?b'.gsub('?', ''))
|
||||
this.assertEqual('ab', 'a+b'.gsub('+', ''));
|
||||
this.assertEqual('ab', 'a*b'.gsub('*', ''));
|
||||
this.assertEqual('ab', 'a{1}b'.gsub('{1}', ''));
|
||||
this.assertEqual('ab', 'a.b'.gsub('.', ''));
|
||||
},
|
||||
|
||||
testSubWithReplacementFunction: function() {
|
||||
var source = 'foo boo boz';
|
||||
|
||||
this.assertEqual('Foo boo boz',
|
||||
source.sub(/[^o]+/, function(match) {
|
||||
return match[0].toUpperCase()
|
||||
}), 1);
|
||||
this.assertEqual('Foo Boo boz',
|
||||
source.sub(/[^o]+/, function(match) {
|
||||
return match[0].toUpperCase()
|
||||
}, 2), 2);
|
||||
this.assertEqual(source,
|
||||
source.sub(/[^o]+/, function(match) {
|
||||
return match[0].toUpperCase()
|
||||
}, 0), 0);
|
||||
this.assertEqual(source,
|
||||
source.sub(/[^o]+/, function(match) {
|
||||
return match[0].toUpperCase()
|
||||
}, -1), -1);
|
||||
},
|
||||
|
||||
testSubWithReplacementString: function() {
|
||||
var source = 'foo boo boz';
|
||||
|
||||
this.assertEqual('oo boo boz',
|
||||
source.sub(/[^o]+/, ''));
|
||||
this.assertEqual('oooo boz',
|
||||
source.sub(/[^o]+/, '', 2));
|
||||
this.assertEqual('-f-oo boo boz',
|
||||
source.sub(/[^o]+/, '-#{0}-'));
|
||||
this.assertEqual('-f-oo- b-oo boz',
|
||||
source.sub(/[^o]+/, '-#{0}-', 2));
|
||||
},
|
||||
|
||||
testScan: function() {
|
||||
var source = 'foo boo boz', results = [];
|
||||
var str = source.scan(/[o]+/, function(match) {
|
||||
results.push(match[0].length);
|
||||
});
|
||||
this.assertEnumEqual([2, 2, 1], results);
|
||||
this.assertEqual(source, source.scan(/x/, this.fail));
|
||||
this.assert(typeof str == 'string');
|
||||
},
|
||||
|
||||
testToArray: function() {
|
||||
this.assertEnumEqual([],''.toArray());
|
||||
this.assertEnumEqual(['a'],'a'.toArray());
|
||||
this.assertEnumEqual(['a','b'],'ab'.toArray());
|
||||
this.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() {
|
||||
this.assertEqual('', ''.camelize());
|
||||
this.assertEqual('', '-'.camelize());
|
||||
this.assertEqual('foo', 'foo'.camelize());
|
||||
this.assertEqual('foo_bar', 'foo_bar'.camelize());
|
||||
this.assertEqual('FooBar', '-foo-bar'.camelize());
|
||||
this.assertEqual('FooBar', 'FooBar'.camelize());
|
||||
|
||||
this.assertEqual('fooBar', 'foo-bar'.camelize());
|
||||
this.assertEqual('borderBottomWidth', 'border-bottom-width'.camelize());
|
||||
|
||||
this.assertEqual('classNameTest','class-name-test'.camelize());
|
||||
this.assertEqual('classNameTest','className-test'.camelize());
|
||||
this.assertEqual('classNameTest','class-nameTest'.camelize());
|
||||
|
||||
/* this.benchmark(function(){
|
||||
'class-name-test'.camelize();
|
||||
},10000); */
|
||||
},
|
||||
|
||||
testCapitalize: function() {
|
||||
this.assertEqual('',''.capitalize());
|
||||
this.assertEqual('Ä','ä'.capitalize());
|
||||
this.assertEqual('A','A'.capitalize());
|
||||
this.assertEqual('Hello','hello'.capitalize());
|
||||
this.assertEqual('Hello','HELLO'.capitalize());
|
||||
this.assertEqual('Hello','Hello'.capitalize());
|
||||
this.assertEqual('Hello world','hello WORLD'.capitalize());
|
||||
},
|
||||
|
||||
testUnderscore: function() {
|
||||
this.assertEqual('', ''.underscore());
|
||||
this.assertEqual('_', '-'.underscore());
|
||||
this.assertEqual('foo', 'foo'.underscore());
|
||||
this.assertEqual('foo', 'Foo'.underscore());
|
||||
this.assertEqual('foo_bar', 'foo_bar'.underscore());
|
||||
this.assertEqual('border_bottom', 'borderBottom'.underscore());
|
||||
this.assertEqual('border_bottom_width', 'borderBottomWidth'.underscore());
|
||||
this.assertEqual('border_bottom_width', 'border-Bottom-Width'.underscore());
|
||||
},
|
||||
|
||||
testDasherize: function() {
|
||||
this.assertEqual('', ''.dasherize());
|
||||
this.assertEqual('foo', 'foo'.dasherize());
|
||||
this.assertEqual('Foo', 'Foo'.dasherize());
|
||||
this.assertEqual('foo-bar', 'foo-bar'.dasherize());
|
||||
this.assertEqual('border-bottom-width', 'border_bottom_width'.dasherize());
|
||||
},
|
||||
|
||||
testTruncate: function() {
|
||||
var source = 'foo boo boz foo boo boz foo boo boz foo boo boz';
|
||||
this.assertEqual(source, source.truncate(source.length));
|
||||
this.assertEqual('foo boo boz foo boo boz foo...', source.truncate(0));
|
||||
this.assertEqual('fo...', source.truncate(5));
|
||||
this.assertEqual('foo b', source.truncate(5, ''));
|
||||
|
||||
this.assert(typeof 'foo'.truncate(5) == 'string');
|
||||
this.assert(typeof 'foo bar baz'.truncate(5) == 'string');
|
||||
},
|
||||
|
||||
testStrip: function() {
|
||||
this.assertEqual('hello world', ' hello world '.strip());
|
||||
this.assertEqual('hello world', 'hello world'.strip());
|
||||
this.assertEqual('hello \n world', ' hello \n world '.strip());
|
||||
this.assertEqual('', ' '.strip());
|
||||
},
|
||||
|
||||
testStripTags: function() {
|
||||
this.assertEqual('hello world', 'hello world'.stripTags());
|
||||
this.assertEqual('hello world', 'hello <span>world</span>'.stripTags());
|
||||
this.assertEqual('hello world', '<a href="#" onclick="moo!">hello</a> world'.stripTags());
|
||||
this.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());
|
||||
this.assertEqual('1\n2', '1\n2'.stripTags());
|
||||
this.assertEqual('one < two blah baz', 'one < two <a href="#" title="foo > bar">blah</a> <input disabled>baz'.stripTags());
|
||||
},
|
||||
|
||||
testStripScripts: function() {
|
||||
this.assertEqual('foo bar', 'foo bar'.stripScripts());
|
||||
this.assertEqual('foo bar', ('foo <script>boo();<'+'/script>bar').stripScripts());
|
||||
this.assertEqual('foo bar', ('foo <script type="text/javascript">boo();\nmoo();<'+'/script>bar').stripScripts());
|
||||
},
|
||||
|
||||
testExtractScripts: function() {
|
||||
this.assertEnumEqual([], 'foo bar'.extractScripts());
|
||||
this.assertEnumEqual(['boo();'], ('foo <script>boo();<'+'/script>bar').extractScripts());
|
||||
this.assertEnumEqual(['boo();','boo();\nmoo();'],
|
||||
('foo <script>boo();<'+'/script><script type="text/javascript">boo();\nmoo();<'+'/script>bar').extractScripts());
|
||||
this.assertEnumEqual(['boo();','boo();\nmoo();'],
|
||||
('foo <script>boo();<'+'/script>blub\nblub<script type="text/javascript">boo();\nmoo();<'+'/script>bar').extractScripts());
|
||||
},
|
||||
|
||||
testEvalScripts: function() {
|
||||
this.assertEqual(0, evalScriptsCounter);
|
||||
|
||||
('foo <script>evalScriptsCounter++<'+'/script>bar').evalScripts();
|
||||
this.assertEqual(1, evalScriptsCounter);
|
||||
|
||||
var stringWithScripts = '';
|
||||
(3).times(function(){ stringWithScripts += 'foo <script>evalScriptsCounter++<'+'/script>bar' });
|
||||
stringWithScripts.evalScripts();
|
||||
this.assertEqual(4, evalScriptsCounter);
|
||||
},
|
||||
|
||||
testEscapeHTML: function() {
|
||||
this.assertEqual('foo bar', 'foo bar'.escapeHTML());
|
||||
this.assertEqual('foo <span>bar</span>', 'foo <span>bar</span>'.escapeHTML());
|
||||
this.assertEqual('foo ß bar', 'foo ß bar'.escapeHTML());
|
||||
|
||||
this.assertEqual('ウィメンズ2007\nクルーズコレクション',
|
||||
'ウィメンズ2007\nクルーズコレクション'.escapeHTML());
|
||||
|
||||
this.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());
|
||||
|
||||
this.assertEqual(largeTextEscaped, largeTextUnescaped.escapeHTML());
|
||||
|
||||
this.assertEqual('1\n2', '1\n2'.escapeHTML());
|
||||
|
||||
this.benchmark(function() { largeTextUnescaped.escapeHTML() }, 1000);
|
||||
},
|
||||
|
||||
testUnescapeHTML: function() {
|
||||
this.assertEqual('foo bar', 'foo bar'.unescapeHTML());
|
||||
this.assertEqual('foo <span>bar</span>', 'foo <span>bar</span>'.unescapeHTML());
|
||||
this.assertEqual('foo ß bar', 'foo ß bar'.unescapeHTML());
|
||||
|
||||
this.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());
|
||||
|
||||
this.assertEqual(largeTextUnescaped, largeTextEscaped.unescapeHTML());
|
||||
|
||||
this.assertEqual('1\n2', '1\n2'.unescapeHTML());
|
||||
this.assertEqual('Pride & Prejudice', '<h1>Pride & Prejudice</h1>'.unescapeHTML());
|
||||
|
||||
this.assertIdentical('<', '&lt;'.unescapeHTML());
|
||||
|
||||
this.benchmark(function() { largeTextEscaped.unescapeHTML() }, 1000);
|
||||
|
||||
},
|
||||
|
||||
testTemplateEvaluation: function() {
|
||||
var source = '<tr><td>#{name}</td><td>#{age}</td></tr>';
|
||||
var person = {name: 'Sam', age: 21};
|
||||
var template = new Template(source);
|
||||
|
||||
this.assertEqual('<tr><td>Sam</td><td>21</td></tr>',
|
||||
template.evaluate(person));
|
||||
this.assertEqual('<tr><td></td><td></td></tr>',
|
||||
template.evaluate({}));
|
||||
},
|
||||
|
||||
testTemplateEvaluationWithEmptyReplacement: function() {
|
||||
var template = new Template('##{}');
|
||||
this.assertEqual('#', template.evaluate({}));
|
||||
this.assertEqual('#', template.evaluate({foo: 'bar'}));
|
||||
|
||||
template = new Template('#{}');
|
||||
this.assertEqual('', template.evaluate({}));
|
||||
},
|
||||
|
||||
testTemplateEvaluationWithFalses: function() {
|
||||
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);
|
||||
|
||||
this.assertEqual('<tr><td>0</td><td>false</td><td></td><td></td><td></td></tr>',
|
||||
template.evaluate(falses));
|
||||
},
|
||||
|
||||
testTemplateEvaluationWithNested: function() {
|
||||
var source = '#{name} #{manager.name} #{manager.age} #{manager.undef} #{manager.age.undef} #{colleagues.first.name}';
|
||||
var subject = { manager: { name: 'John', age: 29 }, name: 'Stephan', age: 22, colleagues: { first: { name: 'Mark' }} };
|
||||
this.assertEqual('Stephan', new Template('#{name}').evaluate(subject));
|
||||
this.assertEqual('John', new Template('#{manager.name}').evaluate(subject));
|
||||
this.assertEqual('29', new Template('#{manager.age}').evaluate(subject));
|
||||
this.assertEqual('', new Template('#{manager.undef}').evaluate(subject));
|
||||
this.assertEqual('', new Template('#{manager.age.undef}').evaluate(subject));
|
||||
this.assertEqual('Mark', new Template('#{colleagues.first.name}').evaluate(subject));
|
||||
this.assertEqual('Stephan John 29 Mark', new Template(source).evaluate(subject));
|
||||
},
|
||||
|
||||
testTemplateEvaluationWithIndexing: function() {
|
||||
var source = '#{0} = #{[0]} - #{1} = #{[1]} - #{[2][0]} - #{[2].name} - #{first[0]} - #{[first][0]} - #{[\]]} - #{first[\]]}';
|
||||
var subject = [ 'zero', 'one', [ 'two-zero' ] ];
|
||||
subject[2].name = 'two-zero-name';
|
||||
subject.first = subject[2];
|
||||
subject[']'] = '\\';
|
||||
subject.first[']'] = 'first\\';
|
||||
this.assertEqual('zero', new Template('#{[0]}').evaluate(subject), "#{[0]}");
|
||||
this.assertEqual('one', new Template('#{[1]}').evaluate(subject), "#{[1]}");
|
||||
this.assertEqual('two-zero', new Template('#{[2][0]}').evaluate(subject), '#{[2][0]}');
|
||||
this.assertEqual('two-zero-name', new Template('#{[2].name}').evaluate(subject), '#{[2].name}');
|
||||
this.assertEqual('two-zero', new Template('#{first[0]}').evaluate(subject), '#{first[0]}');
|
||||
this.assertEqual('\\', new Template('#{[\]]}').evaluate(subject), '#{[\]]}');
|
||||
this.assertEqual('first\\', new Template('#{first[\]]}').evaluate(subject), '#{first[\]]}');
|
||||
this.assertEqual('empty - empty2', new Template('#{[]} - #{m[]}').evaluate({ '': 'empty', m: {'': 'empty2'}}), '#{[]} - #{m[]}');
|
||||
this.assertEqual('zero = zero - one = one - two-zero - two-zero-name - two-zero - two-zero - \\ - first\\', new Template(source).evaluate(subject));
|
||||
},
|
||||
|
||||
testTemplateToTemplateReplacements: function() {
|
||||
var source = 'My name is #{name}, my job is #{job}';
|
||||
var subject = {
|
||||
name: 'Stephan',
|
||||
getJob: function() { return 'Web developer'; },
|
||||
toTemplateReplacements: function() { return { name: this.name, job: this.getJob() } }
|
||||
};
|
||||
this.assertEqual('My name is Stephan, my job is Web developer', new Template(source).evaluate(subject));
|
||||
|
||||
var strActual = new Template('foo #{bar} baz').evaluate({
|
||||
toTemplateReplacements: function(){ return null; }
|
||||
});
|
||||
this.assertIdentical('foo baz', strActual);
|
||||
this.assertIdentical('foo', new Template('foo#{bar}').evaluate(null));
|
||||
},
|
||||
|
||||
testTemplateEvaluationCombined: function() {
|
||||
var source = '#{name} is #{age} years old, managed by #{manager.name}, #{manager.age}.\n' +
|
||||
'Colleagues include #{colleagues[0].name} and #{colleagues[1].name}.';
|
||||
var subject = {
|
||||
name: 'Stephan', age: 22,
|
||||
manager: { name: 'John', age: 29 },
|
||||
colleagues: [ { name: 'Mark' }, { name: 'Indy' } ]
|
||||
};
|
||||
this.assertEqual('Stephan is 22 years old, managed by John, 29.\n' +
|
||||
'Colleagues include Mark and Indy.',
|
||||
new Template(source).evaluate(subject));
|
||||
},
|
||||
|
||||
testInterpolate: function() {
|
||||
var subject = { name: 'Stephan' };
|
||||
var pattern = /(^|.|\r|\n)(#\((.*?)\))/;
|
||||
this.assertEqual('#{name}: Stephan', '\\#{name}: #{name}'.interpolate(subject));
|
||||
this.assertEqual('#(name): Stephan', '\\#(name): #(name)'.interpolate(subject, pattern));
|
||||
},
|
||||
|
||||
testToQueryParams: function() {
|
||||
// only the query part
|
||||
var result = {a:undefined, b:'c'};
|
||||
this.assertHashEqual({}, ''.toQueryParams(), 'empty query');
|
||||
this.assertHashEqual({}, 'foo?'.toQueryParams(), 'empty query with URL');
|
||||
this.assertHashEqual(result, 'foo?a&b=c'.toQueryParams(), 'query with URL');
|
||||
this.assertHashEqual(result, 'foo?a&b=c#fragment'.toQueryParams(), 'query with URL and fragment');
|
||||
this.assertHashEqual(result, 'a;b=c'.toQueryParams(';'), 'custom delimiter');
|
||||
|
||||
this.assertHashEqual({a:undefined}, 'a'.toQueryParams(), 'key without value');
|
||||
this.assertHashEqual({a:'b'}, 'a=b&=c'.toQueryParams(), 'empty key');
|
||||
this.assertHashEqual({a:'b', c:''}, 'a=b&c='.toQueryParams(), 'empty value');
|
||||
|
||||
this.assertHashEqual({'a b':'c', d:'e f', g:'h'},
|
||||
'a%20b=c&d=e%20f&g=h'.toQueryParams(), 'proper decoding');
|
||||
this.assertHashEqual({a:'b=c=d'}, 'a=b=c=d'.toQueryParams(), 'multiple equal signs');
|
||||
this.assertHashEqual({a:'b', c:'d'}, '&a=b&&&c=d'.toQueryParams(), 'proper splitting');
|
||||
|
||||
this.assertEnumEqual($w('r g b'), 'col=r&col=g&col=b'.toQueryParams()['col'],
|
||||
'collection without square brackets');
|
||||
var msg = 'empty values inside collection';
|
||||
this.assertEnumEqual(['r', '', 'b'], 'c=r&c=&c=b'.toQueryParams()['c'], msg);
|
||||
this.assertEnumEqual(['', 'blue'], 'c=&c=blue'.toQueryParams()['c'], msg);
|
||||
this.assertEnumEqual(['blue', ''], 'c=blue&c='.toQueryParams()['c'], msg);
|
||||
},
|
||||
|
||||
testInspect: function() {
|
||||
this.assertEqual('\'\'', ''.inspect());
|
||||
this.assertEqual('\'test\'', 'test'.inspect());
|
||||
this.assertEqual('\'test \\\'test\\\' "test"\'', 'test \'test\' "test"'.inspect());
|
||||
this.assertEqual('\"test \'test\' \\"test\\"\"', 'test \'test\' "test"'.inspect(true));
|
||||
this.assertEqual('\'\\b\\t\\n\\f\\r"\\\\\'', '\b\t\n\f\r"\\'.inspect());
|
||||
this.assertEqual('\"\\b\\t\\n\\f\\r\\"\\\\\"', '\b\t\n\f\r"\\'.inspect(true));
|
||||
this.assertEqual('\'\\b\\t\\n\\f\\r\'', '\x08\x09\x0a\x0c\x0d'.inspect());
|
||||
this.assertEqual('\'\\u001a\'', '\x1a'.inspect());
|
||||
},
|
||||
|
||||
testInclude: function() {
|
||||
this.assert('hello world'.include('h'));
|
||||
this.assert('hello world'.include('hello'));
|
||||
this.assert('hello world'.include('llo w'));
|
||||
this.assert('hello world'.include('world'));
|
||||
this.assert(!'hello world'.include('bye'));
|
||||
this.assert(!''.include('bye'));
|
||||
},
|
||||
|
||||
testStartsWith: function() {
|
||||
this.assert('hello world'.startsWith('h'));
|
||||
this.assert('hello world'.startsWith('hello'));
|
||||
this.assert(!'hello world'.startsWith('bye'));
|
||||
this.assert(!''.startsWith('bye'));
|
||||
this.assert(!'hell'.startsWith('hello'));
|
||||
},
|
||||
|
||||
testEndsWith: function() {
|
||||
this.assert('hello world'.endsWith('d'));
|
||||
this.assert('hello world'.endsWith(' world'));
|
||||
this.assert(!'hello world'.endsWith('planet'));
|
||||
this.assert(!''.endsWith('planet'));
|
||||
this.assert('hello world world'.endsWith(' world'));
|
||||
this.assert(!'z'.endsWith('az'));
|
||||
},
|
||||
|
||||
testBlank: function() {
|
||||
this.assert(''.blank());
|
||||
this.assert(' '.blank());
|
||||
this.assert('\t\r\n '.blank());
|
||||
this.assert(!'a'.blank());
|
||||
this.assert(!'\t y \n'.blank());
|
||||
},
|
||||
|
||||
testEmpty: function() {
|
||||
this.assert(''.empty());
|
||||
this.assert(!' '.empty());
|
||||
this.assert(!'\t\r\n '.empty());
|
||||
this.assert(!'a'.empty());
|
||||
this.assert(!'\t y \n'.empty());
|
||||
},
|
||||
|
||||
testSucc: function() {
|
||||
this.assertEqual('b', 'a'.succ());
|
||||
this.assertEqual('B', 'A'.succ());
|
||||
this.assertEqual('1', '0'.succ());
|
||||
this.assertEqual('abce', 'abcd'.succ());
|
||||
this.assertEqual('{', 'z'.succ());
|
||||
this.assertEqual(':', '9'.succ());
|
||||
},
|
||||
|
||||
testTimes: function() {
|
||||
|
||||
this.assertEqual('', ''.times(0));
|
||||
this.assertEqual('', ''.times(5));
|
||||
this.assertEqual('', 'a'.times(-1));
|
||||
this.assertEqual('', 'a'.times(0));
|
||||
this.assertEqual('a', 'a'.times(1));
|
||||
this.assertEqual('aa', 'a'.times(2));
|
||||
this.assertEqual('aaaaa', 'a'.times(5));
|
||||
this.assertEqual('foofoofoofoofoo', 'foo'.times(5));
|
||||
this.assertEqual('', 'foo'.times(-5));
|
||||
|
||||
/*window.String.prototype.oldTimes = function(count) {
|
||||
var result = '';
|
||||
for (var i = 0; i < count; i++) result += this;
|
||||
return result;
|
||||
};
|
||||
|
||||
this.benchmark(function() {
|
||||
'foo'.times(15);
|
||||
}, 1000, 'new: ');
|
||||
|
||||
this.benchmark(function() {
|
||||
'foo'.oldTimes(15);
|
||||
}, 1000, 'previous: ');*/
|
||||
},
|
||||
|
||||
testToJSON: function() {
|
||||
this.assertEqual('\"\"', ''.toJSON());
|
||||
this.assertEqual('\"test\"', 'test'.toJSON());
|
||||
},
|
||||
|
||||
testIsJSON: function() {
|
||||
this.assert(!''.isJSON());
|
||||
this.assert(!' '.isJSON());
|
||||
this.assert('""'.isJSON());
|
||||
this.assert('"foo"'.isJSON());
|
||||
this.assert('{}'.isJSON());
|
||||
this.assert('[]'.isJSON());
|
||||
this.assert('null'.isJSON());
|
||||
this.assert('123'.isJSON());
|
||||
this.assert('true'.isJSON());
|
||||
this.assert('false'.isJSON());
|
||||
this.assert('"\\""'.isJSON());
|
||||
this.assert(!'\\"'.isJSON());
|
||||
this.assert(!'new'.isJSON());
|
||||
this.assert(!'\u0028\u0029'.isJSON());
|
||||
// we use '@' as a placeholder for characters authorized only inside brackets,
|
||||
// so this tests make sure it is not considered authorized elsewhere.
|
||||
this.assert(!'@'.isJSON());
|
||||
},
|
||||
|
||||
testEvalJSON: function() {
|
||||
var valid = '{"test": \n\r"hello world!"}';
|
||||
var invalid = '{"test": "hello world!"';
|
||||
var dangerous = '{});attackTarget = "attack succeeded!";({}';
|
||||
|
||||
// use smaller huge string size for KHTML
|
||||
var size = navigator.userAgent.include('KHTML') ? 20 : 100;
|
||||
var longString = '"' + '123456789\\"'.times(size * 10) + '"';
|
||||
var object = '{' + longString + ': ' + longString + '},';
|
||||
var huge = '[' + object.times(size) + '{"test": 123}]';
|
||||
|
||||
this.assertEqual('hello world!', valid.evalJSON().test);
|
||||
this.assertEqual('hello world!', valid.evalJSON(true).test);
|
||||
this.assertRaise('SyntaxError', function() { invalid.evalJSON() });
|
||||
this.assertRaise('SyntaxError', function() { invalid.evalJSON(true) });
|
||||
|
||||
attackTarget = "scared";
|
||||
dangerous.evalJSON();
|
||||
this.assertEqual("attack succeeded!", attackTarget);
|
||||
|
||||
attackTarget = "Not scared!";
|
||||
this.assertRaise('SyntaxError', function(){dangerous.evalJSON(true)});
|
||||
this.assertEqual("Not scared!", attackTarget);
|
||||
|
||||
this.assertEqual('hello world!', ('/*-secure- \r \n ' + valid + ' \n */').evalJSON().test);
|
||||
var temp = Prototype.JSONFilter;
|
||||
Prototype.JSONFilter = /^\/\*([\s\S]*)\*\/$/; // test custom delimiters.
|
||||
this.assertEqual('hello world!', ('/*' + valid + '*/').evalJSON().test);
|
||||
Prototype.JSONFilter = temp;
|
||||
|
||||
this.assertMatch(123, huge.evalJSON(true).last().test);
|
||||
|
||||
this.assertEqual('', '""'.evalJSON());
|
||||
this.assertEqual('foo', '"foo"'.evalJSON());
|
||||
this.assert('object', typeof '{}'.evalJSON());
|
||||
this.assert(Object.isArray('[]'.evalJSON()));
|
||||
this.assertNull('null'.evalJSON());
|
||||
this.assert(123, '123'.evalJSON());
|
||||
this.assertIdentical(true, 'true'.evalJSON());
|
||||
this.assertIdentical(false, 'false'.evalJSON());
|
||||
this.assertEqual('"', '"\\""'.evalJSON());
|
||||
}
|
||||
});
|
|
@ -1,27 +0,0 @@
|
|||
<!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>Unit test file | <%= title %> | <%= template_name %> template | <%= timestamp %></title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
var eventResults = {};
|
||||
var originalElement = window.Element;
|
||||
</script>
|
||||
<%= script_tag('assets/prototype.js') %>
|
||||
<%= script_tag('lib_assets/unittest.js') %>
|
||||
<%= link_tag('lib_assets/unittest.css') %>
|
||||
<%= css_fixtures %>
|
||||
<%= js_fixtures %>
|
||||
<%= test_file %>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="testlog"></div>
|
||||
|
||||
<%= html_fixtures %>
|
||||
</body>
|
||||
</html>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
eventResults.endOfDocument = true;
|
||||
</script>
|
|
@ -1,148 +0,0 @@
|
|||
var testObj = {
|
||||
isNice: function() {
|
||||
return true;
|
||||
},
|
||||
isBroken: function() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
new Test.Unit.Runner({
|
||||
|
||||
testIsRunningFromRake: function() {
|
||||
if (window.location.toString().startsWith('http')) {
|
||||
this.assert(this.isRunningFromRake);
|
||||
this.info('These tests are running from rake.')
|
||||
} else {
|
||||
this.assert(!this.isRunningFromRake);
|
||||
this.info('These tests are *not* running from rake.')
|
||||
}
|
||||
},
|
||||
|
||||
testBuildMessage: function() {
|
||||
this.assertEqual("'foo' 'bar'", this.buildMessage('', '? ?', 'foo', 'bar'))
|
||||
},
|
||||
|
||||
testAssertEqual: function() {
|
||||
this.assertEqual(0, 0);
|
||||
this.assertEqual(0, 0, "test");
|
||||
|
||||
this.assertEqual(0,'0');
|
||||
this.assertEqual(65.0, 65);
|
||||
|
||||
this.assertEqual("a", "a");
|
||||
this.assertEqual("a", "a", "test");
|
||||
|
||||
this.assertNotEqual(0, 1);
|
||||
this.assertNotEqual("a","b");
|
||||
this.assertNotEqual({},{});
|
||||
this.assertNotEqual([],[]);
|
||||
this.assertNotEqual([],{});
|
||||
},
|
||||
|
||||
testAssertEnumEqual: function() {
|
||||
this.assertEnumEqual([], []);
|
||||
this.assertEnumEqual(['a', 'b'], ['a', 'b']);
|
||||
this.assertEnumEqual(['1', '2'], [1, 2]);
|
||||
this.assertEnumNotEqual(['1', '2'], [1, 2, 3]);
|
||||
},
|
||||
|
||||
testAssertHashEqual: function() {
|
||||
this.assertHashEqual({}, {});
|
||||
this.assertHashEqual({a:'b'}, {a:'b'});
|
||||
this.assertHashEqual({a:'b', c:'d'}, {c:'d', a:'b'});
|
||||
this.assertHashNotEqual({a:'b', c:'d'}, {c:'d', a:'boo!'});
|
||||
},
|
||||
|
||||
testAssertRespondsTo: function() {
|
||||
this.assertRespondsTo('isNice', testObj);
|
||||
this.assertRespondsTo('isBroken', testObj);
|
||||
},
|
||||
|
||||
testAssertIdentical: function() {
|
||||
this.assertIdentical(0, 0);
|
||||
this.assertIdentical(0, 0, "test");
|
||||
this.assertIdentical(1, 1);
|
||||
this.assertIdentical('a', 'a');
|
||||
this.assertIdentical('a', 'a', "test");
|
||||
this.assertIdentical('', '');
|
||||
this.assertIdentical(undefined, undefined);
|
||||
this.assertIdentical(null, null);
|
||||
this.assertIdentical(true, true);
|
||||
this.assertIdentical(false, false);
|
||||
|
||||
var obj = {a:'b'};
|
||||
this.assertIdentical(obj, obj);
|
||||
|
||||
this.assertNotIdentical({1:2,3:4},{1:2,3:4});
|
||||
|
||||
this.assertIdentical(1, 1.0); // both are typeof == 'number'
|
||||
|
||||
this.assertNotIdentical(1, '1');
|
||||
this.assertNotIdentical(1, '1.0');
|
||||
},
|
||||
|
||||
testAssertNullAndAssertUndefined: function() {
|
||||
this.assertNull(null);
|
||||
this.assertNotNull(undefined);
|
||||
this.assertNotNull(0);
|
||||
this.assertNotNull('');
|
||||
this.assertNotUndefined(null);
|
||||
this.assertUndefined(undefined);
|
||||
this.assertNotUndefined(0);
|
||||
this.assertNotUndefined('');
|
||||
this.assertNullOrUndefined(null);
|
||||
this.assertNullOrUndefined(undefined);
|
||||
this.assertNotNullOrUndefined(0);
|
||||
this.assertNotNullOrUndefined('');
|
||||
},
|
||||
|
||||
testAssertMatch: function() {
|
||||
this.assertMatch(/knowmad.jpg$/, 'http://script.aculo.us/images/knowmad.jpg');
|
||||
this.assertMatch(/Fuc/, 'Thomas Fuchs');
|
||||
this.assertMatch(/^\$(\d{1,3}(\,\d{3})*|(\d+))(\.\d{2})?$/, '$19.95');
|
||||
this.assertMatch(/(\d{3}\) ?)|(\d{3}[- \.])?\d{3}[- \.]\d{4}(\s(x\d+)?){0,1}$/, '704-343-9330');
|
||||
this.assertMatch(/^(?:(?:(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(\/|-|\.)(?:0?2\1(?:29)))|(?:(?:(?:1[6-9]|[2-9]\d)?\d{2})(\/|-|\.)(?:(?:(?:0?[13578]|1[02])\2(?:31))|(?:(?:0?[1,3-9]|1[0-2])\2(29|30))|(?:(?:0?[1-9])|(?:1[0-2]))\2(?:0?[1-9]|1\d|2[0-8]))))$/, '2001-06-16');
|
||||
this.assertMatch(/^((0?[123456789])|(1[012]))\s*:\s*([012345]\d)(\s*:\s*([012345]\d))?\s*[ap]m\s*-\s*((0?[123456789])|(1[012]))\s*:\s*([012345]\d)(\s*:\s*([012345]\d))?\s*[ap]m$/i, '2:00PM-2:15PM');
|
||||
this.assertNoMatch(/zubar/, 'foo bar');
|
||||
},
|
||||
|
||||
testAssertInstanceOf: function() {
|
||||
this.assertInstanceOf(String, new String);
|
||||
this.assertInstanceOf(RegExp, /foo/);
|
||||
this.assertNotInstanceOf(String, {});
|
||||
},
|
||||
|
||||
testAssertVisible: function() {
|
||||
this.assertVisible('testcss1');
|
||||
this.assertNotVisible('testcss1_span');
|
||||
//this.assertNotVisible('testcss2', "Due to a Safari bug, this test fails in Safari.");
|
||||
|
||||
Element.hide('testcss1');
|
||||
this.assertNotVisible('testcss1');
|
||||
this.assertNotVisible('testcss1_span');
|
||||
Element.show('testcss1');
|
||||
this.assertVisible('testcss1');
|
||||
this.assertNotVisible('testcss1_span');
|
||||
|
||||
Element.show('testcss1_span');
|
||||
this.assertVisible('testcss1_span');
|
||||
Element.hide('testcss1');
|
||||
this.assertNotVisible('testcss1_span'); // hidden by parent
|
||||
},
|
||||
|
||||
testAssertElementsMatch: function() {
|
||||
this.assertElementsMatch($$('#tlist'), '#tlist');
|
||||
this.assertElementMatches($('tlist'), '#tlist');
|
||||
}
|
||||
});
|
||||
|
||||
new Test.Unit.Runner({
|
||||
testDummy: function() {
|
||||
this.assert(true);
|
||||
},
|
||||
|
||||
testMultipleTestRunner: function() {
|
||||
this.assertEqual('passed', $('testlog_2').down('td', 1).innerHTML);
|
||||
}
|
||||
}, {testLog: 'testlog_2'});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue