Fixed a variety of non-ASCII chars and similar [#610 state:resolved]

This commit is contained in:
Tobie Langel 2009-06-11 22:48:38 +02:00
parent 6e4a3cdec8
commit ad89bfb738
31 changed files with 1337 additions and 1335 deletions

View File

@ -1,3 +1,5 @@
* Fixed a variety of non-ASCII chars and similar [#610 state:resolved] (T.J. Crowder)
* Add Chrome 1+ to the list of supported browsers. (kangax)
* Fix `Template#evaluate` "eating" previous character if `null` was returned from `toTemplateReplacements` function. (Nir, Jürgen Hörmann, kangax)

View File

@ -1,18 +1,18 @@
/**
* == 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]].
*
*
* <h4>Request headers</h4>
*
*
* 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`).
@ -20,18 +20,18 @@
* text/xml, * / *`
* * `Content-type` is automatically determined based on the `contentType`
* and `encoding` options.
*
*
* <h4>Ajax options</h4>
*
*
* 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
* seriously discouraged it halts all script execution for the duration of
* seriously discouraged &mdash; 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
@ -63,16 +63,16 @@
* * `sanitizeJSON` ([[Boolean]]; default is `false` for same-origin requests,
* `true` otherwise): Sanitizes the contents of
* [[Ajax.Response#responseText]] before evaluating it.
*
*
* <h4>Common callbacks</h4>
*
*
* 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.
@ -102,9 +102,9 @@
* 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
* automatic behaviors are processed. Guaranteed to run regardless of what
* happened during the request.
*
*
**/
//= require "ajax/ajax"

View File

@ -10,10 +10,10 @@ var Ajax = {
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]].
**/

View File

@ -11,10 +11,10 @@ Ajax.Base = Class.create({
evalJS: true
};
Object.extend(this.options, options || { });
this.options.method = this.options.method.toLowerCase();
if (Object.isString(this.options.parameters))
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();

View File

@ -1,28 +1,28 @@
/** section: Ajax
* class Ajax.PeriodicalUpdater
*
* Periodically performs an Ajax request and updates a containers contents
*
* 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
* 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.
*
*
* <h4>Additional options</h4>
*
*
* `Ajax.PeriodicalUpdater` features all the common options and callbacks
* described in the [[Ajax section]] _plus_ those added by `Ajax.Updater`.
*
* described in the [[Ajax section]] &mdash; _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`
@ -33,13 +33,13 @@
* (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.
*
*
* <h4>Disabling and re-enabling a <code>PeriodicalUpdater</code></h4>
*
*
* 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, {
@ -54,7 +54,7 @@ Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
* `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) {
@ -63,7 +63,7 @@ Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
this.frequency = (this.options.frequency || 2);
this.decay = (this.options.decay || 1);
this.updater = { };
this.container = container;
this.url = url;
@ -73,7 +73,7 @@ Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
/**
* Ajax.PeriodicalUpdater#start() -> undefined
*
*
* Starts the periodical updater (if it had previously been stopped with
* [[Ajax.PeriodicalUpdater#stop]]).
**/
@ -84,9 +84,9 @@ Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
/**
* Ajax.PeriodicalUpdater#stop() -> undefined
*
*
* Stops the periodical updater.
*
*
* Also calls the `onComplete` callback, if one has been defined.
**/
stop: function() {
@ -97,7 +97,7 @@ Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
updateComplete: function(response) {
if (this.options.decay) {
this.decay = (response.responseText == this.lastText ?
this.decay = (response.responseText == this.lastText ?
this.decay * this.options.decay : 1);
this.lastText = response.responseText;

View File

@ -1,22 +1,22 @@
/** section: Ajax
* class Ajax.Request
*
*
* Initiates and processes an Ajax request.
*
*
* `Ajax.Request` is a general-purpose class for making HTTP requests.
*
*
* <h4>Automatic JavaScript response evaluation</h4>
*
*
* 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`
@ -25,30 +25,30 @@
* * `text/javascript`
* * `text/x-ecmascript`
* * `text/x-javascript`
*
*
* The MIME-type string is examined in a case-insensitive manner.
*
*
* <h4>Methods you may find useful</h4>
*
*
* 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
@ -57,16 +57,16 @@
* // 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
* is no such header &mdash; or its contents are invalid &mdash; `headerJSON` will be set
* to `null`.
*
*
* new Ajax.Request('/your/url', {
* onSuccess: function(transport) {
* transport.headerJSON
@ -75,7 +75,7 @@
**/
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
@ -84,7 +84,7 @@ Ajax.Request = Class.create(Ajax.Base, {
* `http`).
* - options (Object): Configuration for the request. See the
* [[Ajax section]] for more information.
*
*
* Creates a new `Ajax.Request`.
**/
initialize: function($super, url, options) {
@ -103,7 +103,7 @@ Ajax.Request = Class.create(Ajax.Base, {
params['_method'] = this.method;
this.method = 'post';
}
this.parameters = params;
if (params = Object.toQueryString(params)) {
@ -113,17 +113,17 @@ Ajax.Request = Class.create(Ajax.Base, {
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.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();
@ -133,7 +133,7 @@ Ajax.Request = Class.create(Ajax.Base, {
/* Force Firefox to handle ready state 4 for synchronous requests */
if (!this.options.asynchronous && this.transport.overrideMimeType)
this.onStateChange();
}
catch (e) {
this.dispatchException(e);
@ -145,7 +145,7 @@ Ajax.Request = Class.create(Ajax.Base, {
if (readyState > 1 && !((readyState == 4) && this._complete))
this.respondToReadyState(this.transport.readyState);
},
setRequestHeaders: function() {
var headers = {
'X-Requested-With': 'XMLHttpRequest',
@ -156,47 +156,47 @@ Ajax.Request = Class.create(Ajax.Base, {
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.
* 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)
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)
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 }
} catch (e) { return 0 }
},
respondToReadyState: function(readyState) {
var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
@ -209,10 +209,10 @@ Ajax.Request = Class.create(Ajax.Base, {
} catch (e) {
this.dispatchException(e);
}
var contentType = response.getHeader('Content-type');
if (this.options.evalJS == 'force'
|| (this.options.evalJS && this.isSameOrigin() && contentType
|| (this.options.evalJS && this.isSameOrigin() && contentType
&& contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
this.evalResponse();
}
@ -223,13 +223,13 @@ Ajax.Request = Class.create(Ajax.Base, {
} 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({
@ -238,12 +238,12 @@ Ajax.Request = Class.create(Ajax.Base, {
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.
**/
@ -252,7 +252,7 @@ Ajax.Request = Class.create(Ajax.Base, {
return this.transport.getResponseHeader(name) || null;
} catch (e) { return null; }
},
evalResponse: function() {
try {
return eval((this.transport.responseText || '').unfilterJSON());
@ -267,5 +267,5 @@ Ajax.Request = Class.create(Ajax.Base, {
}
});
Ajax.Request.Events =
Ajax.Request.Events =
['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];

View File

@ -1,29 +1,29 @@
/** section: Ajax
* Ajax.Responders
*
*
* 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
* time, the number of currently active Ajax requests &mdash; by monitoring their
* `onCreate` and `onComplete` events. The code for this is fairly simple:
*
*
* Ajax.Responders.register({
* onCreate: function() {
* Ajax.activeRequestCount++;
@ -32,16 +32,16 @@
* Ajax.activeRequestCount--;
* }
* });
*
*
* <h4>Responder callbacks</h4>
*
*
* 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
@ -61,14 +61,14 @@
* 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.
*
* automatic behaviors are processed. Guaranteed to run regardless of what
* happened during the request.
*
**/
Ajax.Responders = {
responders: [],
_each: function(iterator) {
this.responders._each(iterator);
},
@ -77,30 +77,30 @@ Ajax.Responders = {
* 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.
* [[Ajax.Responders#register]] &mdash; 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])) {
@ -115,6 +115,6 @@ Ajax.Responders = {
Object.extend(Ajax.Responders, Enumerable);
Ajax.Responders.register({
onCreate: function() { Ajax.activeRequestCount++ },
onCreate: function() { Ajax.activeRequestCount++ },
onComplete: function() { Ajax.activeRequestCount-- }
});

View File

@ -1,9 +1,9 @@
/** 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.
@ -11,49 +11,49 @@
/**
* Ajax.Response#readyState -> Number
*
* The requests current state.
*
*
* 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.
**/
@ -63,53 +63,53 @@ Ajax.Response = Class.create({
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.
@ -117,12 +117,12 @@ Ajax.Response = Class.create({
getAllHeaders: function() {
try {
return this.getAllResponseHeaders();
} catch (e) { return null }
} 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.
@ -130,10 +130,10 @@ Ajax.Response = Class.create({
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.
@ -141,7 +141,7 @@ Ajax.Response = Class.create({
getAllResponseHeaders: function() {
return this.transport.getAllResponseHeaders();
},
_getHeaderJSON: function() {
var json = this.getHeader('X-JSON');
if (!json) return null;
@ -153,11 +153,11 @@ Ajax.Response = Class.create({
this.request.dispatchException(e);
}
},
_getResponseJSON: function() {
var options = this.request.options;
if (!options.evalJSON || (options.evalJSON != 'force' &&
!(this.getHeader('Content-type') || '').include('application/json')) ||
if (!options.evalJSON || (options.evalJSON != 'force' &&
!(this.getHeader('Content-type') || '').include('application/json')) ||
this.responseText.blank())
return null;
try {

View File

@ -1,99 +1,99 @@
/** section: Ajax
* class Ajax.Updater < Ajax.Request
*
* A class that performs an Ajax request and updates a containers contents
*
* 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.
*
*
* <h4>Example</h4>
*
*
* 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.
*
*
* <h4>Callbacks</h4>
*
*
* `Ajax.Updater` supports all the callbacks listed in the [[Ajax section]].
* Note that the `onComplete` callback will be invoked **after** the element
* is updated.
*
*
* <h4>Additional options</h4>
*
*
* `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
* existing contents. The `insertion` option takes one of four strings &mdash;
* `top`, `bottom`, `before`, or `after` &mdash; and _inserts_ the contents of the
* response in the manner described by [[Element#insert]].
*
*
* <h4>About `evalScripts` and defining functions</h4>
*
*
* If you use `evalScripts: true`, any `<script>` block will be evaluated.
* This **does not** mean it will be evaluated in the global scope. In other
* words:
*
*
* * The evaluation scope will be that of Prototype's internal processing
* function. 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 inside Ajax responses _define functions_, they
* will need to be assigned to properties of the `window` object  _not_
* will need to be assigned to properties of the `window` object &mdash; _not_
* declared with the `function` keyword.
*
*
* For example, this won't work:
*
*
* // This kind of script won't work if processed by Ajax.Updater:
* function coolFunc() {
* // Amazing stuff!
* }
*
*
* Instead, use the following syntax:
*
*
* // This kind of script WILL work if processed by Ajax.Updater:
* coolFunc = function() {
* // Amazing stuff!
* }
*
*
* <h4>Single container, or success/failure split?</h4>
*
* The examples above all assume youre going to update the same container
*
* 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',
* response text &mdash; 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, {
@ -108,7 +108,7 @@ Ajax.Updater = Class.create(Ajax.Request, {
* `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) {
@ -128,11 +128,11 @@ Ajax.Updater = Class.create(Ajax.Request, {
},
updateContent: function(responseText) {
var receiver = this.container[this.success() ? 'success' : 'failure'],
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)) {
@ -140,7 +140,7 @@ Ajax.Updater = Class.create(Ajax.Request, {
receiver.insert(insertion);
}
else options.insertion(receiver, responseText);
}
}
else receiver.update(responseText);
}
}

View File

@ -10,15 +10,15 @@ 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});
}
@ -32,21 +32,21 @@ 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,
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
this.deltaX = window.pageXOffset
|| document.documentElement.scrollLeft
|| document.body.scrollLeft
|| 0;
this.deltaY = window.pageYOffset
|| document.documentElement.scrollTop
|| document.body.scrollTop
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)
@ -57,7 +57,7 @@ var Position = {
return (y >= this.offset[1] &&
y < this.offset[1] + element.offsetHeight &&
x >= this.offset[0] &&
x >= this.offset[0] &&
x < this.offset[0] + element.offsetWidth);
},
@ -70,18 +70,18 @@ var Position = {
return (this.ycomp >= this.offset[1] &&
this.ycomp < this.offset[1] + element.offsetHeight &&
this.xcomp >= this.offset[0] &&
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) /
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) /
return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
element.offsetWidth;
},
@ -161,21 +161,21 @@ Element.ClassNames.prototype = {
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(' ');
}

View File

@ -2,21 +2,21 @@
* == 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).
*
*
**/

File diff suppressed because it is too large Load Diff

View File

@ -1,36 +1,36 @@
(function() {
/** section: DOM
* Event
*
*
* The namespace for Prototype's event system.
*
*
* <h4>Events: a fine mess</h4>
*
*
* 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
* sneakier issues here and there waiting to bite your ankle &mdash; 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.
*
*
* <h4>Prototype to the rescue</h4>
*
* Of course, Prototype smooths it over so well youll forget these
*
* 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.
*
* The functions youre most likely to use a lot are [[Event.observe]],
*
* 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,
@ -49,7 +49,7 @@
cache: {}
};
var docEl = document.documentElement;
var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
&& 'onmouseleave' in docEl;
@ -79,39 +79,39 @@
/**
* 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) }
/**
* Event#element(@event) -> Element
*
*
* Returns the DOM element on which the event occurred.
**/
function element(event) {
@ -140,8 +140,8 @@
/**
* Event#findElement(@event, expression) -> Element
*
* Returns the first DOM element that matches a given CSS selector
*
* Returns the first DOM element that matches a given CSS selector &mdash;
* starting with the element on which the event occurred, then moving up
* its ancestor chain.
**/
@ -151,34 +151,34 @@
var elements = [element].concat(element.ancestors());
return Selector.findElement(elements, expression, 0);
}
/**
* 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));
@ -186,29 +186,29 @@
/**
* 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));
(docElement.clientTop || 0));
}
/**
* Event#stop(@event) -> undefined
*
* Stops the events propagation and prevents its eventual default action
*
* 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.
**/
@ -268,7 +268,7 @@
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, {
@ -277,7 +277,7 @@
pageX: pointer.x,
pageY: pointer.y
});
return Object.extend(event, methods);
};
} else {
@ -287,14 +287,14 @@
}
function _createResponder(element, eventName, handler) {
// We don't set a default on the call to Element#retrieve so that we can
// 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());
registry = Element.retrieve(element, 'prototype_event_registry', $H());
}
var respondersForEvent = registry.get(eventName);
@ -302,11 +302,11 @@
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;
if (respondersForEvent.pluck('handler').include(handler)) return false;
var responder;
if (eventName.include(":")) {
// Custom event.
@ -314,11 +314,11 @@
// 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);
};
@ -332,19 +332,19 @@
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 {
} else {
responder = function(event) {
Event.extend(event, element);
handler.call(element, event);
@ -356,14 +356,14 @@
respondersForEvent.push(responder);
return responder;
}
function _destroyCache() {
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
@ -376,42 +376,42 @@
// 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;
if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
_getDOMEventName = function(eventName) {
var translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
return eventName in translations ? translations[eventName] : eventName;
var translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
return eventName in translations ? translations[eventName] : eventName;
};
}
/**
* Event.observe(element, eventName, handler) -> Element
*
*
* Registers an event handler on a DOM element.
**/
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)
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);
}
element.attachEvent("onfilterchange", responder);
}
} else {
var actualEventName = _getDOMEventName(eventName);
// Ordinary event.
if (element.addEventListener)
element.addEventListener(actualEventName, responder, false);
@ -424,27 +424,27 @@
/**
* Event.stopObserving(element[, eventName[, handler]]) -> Element
*
*
* 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`.
**/
function stopObserving(element, eventName, handler) {
element = $(element);
var registry = Element.retrieve(element, 'prototype_event_registry');
if (Object.isUndefined(registry)) return element;
if (eventName && !handler) {
// If an event name is passed without a handler, we stop observing all
// handlers of that type.
var responders = registry.get(eventName);
if (Object.isUndefined(responders)) return element;
responders.each( function(r) {
Element.stopObserving(element, eventName, r.handler);
});
@ -454,24 +454,24 @@
// _all_ handlers on the element.
registry.each( function(pair) {
var eventName = pair.key, responders = pair.value;
responders.each( function(r) {
Element.stopObserving(element, eventName, r.handler);
});
});
});
return element;
}
var responders = registry.get(eventName);
// Fail gracefully if there are no responders assigned.
if (!responders) return;
var responder = responders.find( function(r) { return r.handler === handler; });
if (!responder) return element;
var actualEventName = _getDOMEventName(eventName);
if (eventName.include(':')) {
// Custom event.
if (element.removeEventListener)
@ -487,7 +487,7 @@
else
element.detachEvent('on' + actualEventName, responder);
}
registry.set(eventName, responders.without(responder));
return element;
@ -498,17 +498,17 @@
* - 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;
@ -553,8 +553,8 @@
* See [[Event.observe]].
**/
observe: observe,
/**
/**
* Element#stopObserving(element[, eventName[, handler]]) -> Element
* See [[Event.stopObserving]].
**/
@ -563,32 +563,32 @@
/** section: DOM
* document
*
*
* Prototype extends the built-in `document` object with several convenience
* methods related to events.
* 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
@ -628,7 +628,7 @@
fireContentLoadedEvent();
}
if (document.addEventListener) {
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
} else {
document.observe('readystatechange', checkReadyState);

View File

@ -1,20 +1,20 @@
/** 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) {
@ -22,17 +22,17 @@ var Form = {
form.reset();
return form;
},
/**
* Form.serializeElements(elements[, options]) -> String | Object
* - elements (Array): A collection of elements to include in the
* serialization.
* - options (Object): A list of options that affect the return value
* of the method.
*
*
* Serialize an array of form elements to a string suitable for Ajax
* requests.
*
* requests.
*
* If `options.hash` is `true`, returns an object of key/value pairs
* instead (where keys are control names).
**/
@ -40,12 +40,12 @@ var Form = {
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)))) {
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]];
@ -56,7 +56,7 @@ var Form = {
}
return result;
});
return options.hash ? data : Object.toQueryString(data);
}
};
@ -66,19 +66,19 @@ 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 a string suitable for Ajax requests.
*
*
* If `options.hash` is `true`, returns an object of key/value pairs
* instead (where keys are control names).
**/
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) {
@ -86,7 +86,7 @@ Form.Methods = {
element,
arr = [ ],
serializers = Form.Element.Serializers;
// `length` is not used to prevent interference with
// `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);
@ -97,25 +97,25 @@ Form.Methods = {
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))
@ -128,7 +128,7 @@ Form.Methods = {
/**
* Form#disable(@form) -> Element
*
*
* Disables the form as a whole. Form controls will be visible but
* uneditable.
**/
@ -140,7 +140,7 @@ Form.Methods = {
/**
* Form#enable(@form) -> Element
*
*
* Enables a fully- or partially-disabled form.
**/
enable: function(form) {
@ -151,7 +151,7 @@ Form.Methods = {
/**
* Form#findFirstElement(@form) -> Element
*
*
* Finds the first non-hidden, non-disabled control within the form.
**/
findFirstElement: function(form) {
@ -161,7 +161,7 @@ Form.Methods = {
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);
});
@ -169,7 +169,7 @@ Form.Methods = {
/**
* Form#focusFirstElement(@form) -> Element
*
*
* Gives keyboard focus to the first element of the form. Returns the form.
**/
focusFirstElement: function(form) {
@ -177,15 +177,15 @@ Form.Methods = {
form.findFirstElement().activate();
return form;
},
/**
* Form#request([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 forms `action` attribute.
*
* [[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.
@ -196,15 +196,15 @@ Form.Methods = {
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);
}
};
@ -213,19 +213,19 @@ Form.Methods = {
/** section: DOM
* 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.
@ -234,7 +234,7 @@ Form.Methods = {
Form.Element = {
/**
* Form.Element.focus(element) -> Element
*
*
* Gives keyboard focus to an element. Returns the element.
**/
focus: function(element) {
@ -244,7 +244,7 @@ Form.Element = {
/**
* Form.Element.select(element) -> Element
*
*
* Selects the current text in a text input. Returns the element.
**/
select: function(element) {
@ -254,10 +254,10 @@ Form.Element = {
};
Form.Element.Methods = {
/**
* Form.Element#serialize(@element) -> String
*
*
* Returns a URL-encoded string representation of a form control in the
* `name=value` format.
**/
@ -273,15 +273,15 @@ Form.Element.Methods = {
}
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) {
@ -292,7 +292,7 @@ Form.Element.Methods = {
/**
* Form.Element#setValue(@element, value) -> Element
*
*
* Sets `value` to be the value of the form control. Returns the element.
**/
setValue: function(element, value) {
@ -304,7 +304,7 @@ Form.Element.Methods = {
/**
* Form.Element#clear(@element) -> Element
*
*
* Clears the contents of a text input. Returns the element.
**/
clear: function(element) {
@ -314,16 +314,16 @@ Form.Element.Methods = {
/**
* 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.
**/
@ -337,10 +337,10 @@ Form.Element.Methods = {
} catch (e) { }
return element;
},
/**
* Form.Element#disable(@element) -> Element
*
*
* Disables a form control, effectively preventing its value from changing
* until it is enabled again.
**/
@ -349,10 +349,10 @@ Form.Element.Methods = {
element.disabled = true;
return element;
},
/**
* Form.Element#enable(@element) -> Element
*
*
* Enables a previously disabled form control.
**/
enable: function(element) {
@ -376,7 +376,7 @@ var $F = Form.Element.Methods.getValue;
Form.Element.Serializers = {
input: function(element, value) {
switch (element.type.toLowerCase()) {
case 'checkbox':
case 'checkbox':
case 'radio':
return Form.Element.Serializers.inputSelector(element, value);
default:
@ -393,10 +393,10 @@ Form.Element.Serializers = {
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' ?
return this[element.type == 'select-one' ?
'selectOne' : 'selectMany'](element);
else {
var opt, currentValue, single = !Object.isArray(value);
@ -413,23 +413,23 @@ Form.Element.Serializers = {
}
}
},
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;
@ -451,7 +451,7 @@ Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
this.element = $(element);
this.lastValue = this.getValue();
},
execute: function() {
var value = this.getValue();
if (Object.isString(this.lastValue) && Object.isString(value) ?
@ -468,7 +468,7 @@ Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
Form.Element.Observer = Class.create(Abstract.TimedObserver, {
/**
* new Form.Element.Observer(element, frequency, callback)
*
*
* Creates a timed observer for a specific form control.
**/
getValue: function() {
@ -482,7 +482,7 @@ Form.Element.Observer = Class.create(Abstract.TimedObserver, {
Form.Observer = Class.create(Abstract.TimedObserver, {
/**
* new Form.Observer(element, frequency, callback)
*
*
* Creates a timed observer that triggers when any value changes within
* the form.
**/
@ -500,14 +500,14 @@ 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) {
@ -515,15 +515,15 @@ Abstract.EventObserver = Class.create({
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 'checkbox':
case 'radio':
Event.observe(element, 'click', this.onElementEvent.bind(this));
break;
@ -531,7 +531,7 @@ Abstract.EventObserver = Class.create({
Event.observe(element, 'change', this.onElementEvent.bind(this));
break;
}
}
}
}
});

View File

@ -1,10 +1,10 @@
/* Portions of the Selector class are derived from Jack Slocum's DomQuery,
* part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
* license. Please see http://www.yui-ext.com/ for more information. */
/** section: DOM
* class Selector
*
*
* A class that queries the document for elements that match a given CSS
* selector.
**/
@ -12,12 +12,12 @@ var Selector = Class.create({
/**
* new Selector(expression)
* - expression (String): A CSS selector.
*
*
* Creates a `Selector` with the given CSS selector.
**/
initialize: function(expression) {
this.expression = expression.strip();
if (this.shouldUseSelectorsAPI()) {
this.mode = 'selectorsAPI';
} else if (this.shouldUseXPath()) {
@ -27,11 +27,11 @@ var Selector = Class.create({
this.mode = "normal";
this.compileMatcher();
}
},
shouldUseXPath: (function() {
// Some versions of Opera 9.x produce incorrect results when using XPath
// with descendant combinators.
// see: http://opera.remcol.ath.cx/bugs/index.php?action=bug&id=652
@ -40,26 +40,26 @@ var Selector = Class.create({
if (document.evaluate && window.XPathResult) {
var el = document.createElement('div');
el.innerHTML = '<ul><li></li></ul><div><ul><li></li></ul></div>';
var xpath = ".//*[local-name()='ul' or local-name()='UL']" +
"//*[local-name()='li' or local-name()='LI']";
var result = document.evaluate(xpath, el, null,
var result = document.evaluate(xpath, el, null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
isBuggy = (result.snapshotLength !== 2);
el = null;
}
return isBuggy;
})();
return function() {
return function() {
if (!Prototype.BrowserFeatures.XPath) return false;
var e = this.expression;
// Safari 3 chokes on :*-of-type and :empty
if (Prototype.Browser.WebKit &&
if (Prototype.Browser.WebKit &&
(e.include("-of-type") || e.include(":empty")))
return false;
@ -72,37 +72,37 @@ var Selector = Class.create({
return true;
}
})(),
shouldUseSelectorsAPI: function() {
if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false;
if (!Selector._div) Selector._div = new Element('div');
// Make sure the browser treats the selector as valid. Test on an
// isolated element to minimize cost of this check.
// Make sure the browser treats the selector as valid. Test on an
// isolated element to minimize cost of this check.
try {
Selector._div.querySelector(this.expression);
} catch(e) {
return false;
}
return true;
return true;
},
compileMatcher: function() {
var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
c = Selector.criteria, le, p, m, len = ps.length, name;
if (Selector._cache[e]) {
this.matcher = Selector._cache[e];
this.matcher = Selector._cache[e];
return;
}
this.matcher = ["this.matcher = function(root) {",
this.matcher = ["this.matcher = function(root) {",
"var r = root, h = Selector.handlers, c = false, n;"];
while (e && le != e && (/\S/).test(e)) {
@ -118,12 +118,12 @@ var Selector = Class.create({
}
}
}
this.matcher.push("return h.unique(n);\n}");
eval(this.matcher.join('\n'));
Selector._cache[this.expression] = this.matcher;
},
compileXPathMatcher: function() {
var e = this.expression, ps = Selector.patterns,
x = Selector.xpath, le, m, len = ps.length, name;
@ -138,30 +138,30 @@ var Selector = Class.create({
for (var i = 0; i<len; i++) {
name = ps[i].name;
if (m = e.match(ps[i].re)) {
this.matcher.push(Object.isFunction(x[name]) ? x[name](m) :
this.matcher.push(Object.isFunction(x[name]) ? x[name](m) :
new Template(x[name]).evaluate(m));
e = e.replace(m[0], '');
break;
}
}
}
this.xpath = this.matcher.join('');
Selector._cache[this.expression] = this.xpath;
},
/**
* 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(root) {
root = root || document;
var e = this.expression, results;
switch (this.mode) {
case 'selectorsAPI':
// querySelectorAll queries document-wide, then filters to descendants
@ -173,7 +173,7 @@ var Selector = Class.create({
id = id.replace(/[\.:]/g, "\\$0");
e = "#" + id + " " + e;
}
results = $A(root.querySelectorAll(e)).map(Element.extend);
root.id = oldId;
@ -184,10 +184,10 @@ var Selector = Class.create({
return this.matcher(root);
}
},
/**
* Selector#match(element) -> Boolean
*
*
* Tests whether a `element` matches the instance's CSS selector.
**/
match: function(element) {
@ -195,7 +195,7 @@ var Selector = Class.create({
var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
var le, p, m, len = ps.length, name;
while (e && le !== e && (/\S/).test(e)) {
le = e;
for (var i = 0; i<len; i++) {
@ -215,7 +215,7 @@ var Selector = Class.create({
}
}
}
var match = true, name, matches;
for (var i = 0, token; token = this.tokens[i]; i++) {
name = token[0], matches = token[1];
@ -223,14 +223,14 @@ var Selector = Class.create({
match = false; break;
}
}
return match;
},
toString: function() {
return this.expression;
},
inspect: function() {
return "#<Selector:" + this.expression.inspect() + ">";
}
@ -256,15 +256,15 @@ if (Prototype.BrowserFeatures.SelectorsAPI &&
Object.extend(Selector, {
_cache: { },
xpath: {
descendant: "//*",
child: "/*",
adjacent: "/following-sibling::*[1]",
laterSibling: '/following-sibling::*',
tagName: function(m) {
tagName: function(m) {
if (m[1] == '*') return '';
return "[local-name()='" + m[1].toLowerCase() +
return "[local-name()='" + m[1].toLowerCase() +
"' or local-name()='" + m[1].toUpperCase() + "']";
},
className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
@ -304,7 +304,7 @@ Object.extend(Selector, {
'not': function(m) {
var e = m[6], p = Selector.patterns,
x = Selector.xpath, le, v, len = p.length, name;
var exclusion = [];
while (e && le != e && (/\S/).test(e)) {
le = e;
@ -317,10 +317,10 @@ Object.extend(Selector, {
break;
}
}
}
}
return "[not(" + exclusion.join(" and ") + ")]";
},
'nth-child': function(m) {
'nth-child': function(m) {
return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
},
'nth-last-child': function(m) {
@ -332,7 +332,7 @@ Object.extend(Selector, {
'nth-last-of-type': function(m) {
return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
},
'first-of-type': function(m) {
'first-of-type': function(m) {
m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
},
'last-of-type': function(m) {
@ -359,9 +359,9 @@ Object.extend(Selector, {
}
}
},
criteria: {
tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
className: 'n = h.className(n, r, "#{1}", c); c = false;',
id: 'n = h.id(n, r, "#{1}", c); c = false;',
attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
@ -371,7 +371,7 @@ Object.extend(Selector, {
},
pseudo: function(m) {
if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
},
descendant: 'c = "descendant";',
child: 'c = "child";',
@ -395,7 +395,7 @@ Object.extend(Selector, {
{ name: 'attrPresence', re: /^\[((?:[\w-]+:)?[\w-]+)\]/ },
{ name: 'attr', re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ }
],
// for Selector.match and Element#match
assertions: {
tagName: function(element, matches) {
@ -417,9 +417,9 @@ Object.extend(Selector, {
attr: function(element, matches) {
var nodeValue = Element.readAttribute(element, matches[1]);
return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
}
}
},
handlers: {
// UTILITY FUNCTIONS
// joins two collections
@ -428,7 +428,7 @@ Object.extend(Selector, {
a.push(node);
return a;
},
// marks an array of nodes for counting
mark: function(nodes) {
var _true = Prototype.emptyFunction;
@ -436,13 +436,13 @@ Object.extend(Selector, {
node._countedByPrototype = _true;
return nodes;
},
unmark: (function(){
// IE improperly serializes _countedByPrototype in (inner|outer)HTML
// due to node properties being mapped directly to attributes
var PROPERTIES_ATTRIBUTES_MAP = (function(){
var el = document.createElement('div'),
var el = document.createElement('div'),
isBuggy = false,
propName = '_countedByPrototype',
value = 'x'
@ -451,7 +451,7 @@ Object.extend(Selector, {
el = null;
return isBuggy;
})();
return PROPERTIES_ATTRIBUTES_MAP ?
function(nodes) {
for (var i = 0, node; node = nodes[i]; i++)
@ -462,7 +462,7 @@ Object.extend(Selector, {
for (var i = 0, node; node = nodes[i]; i++)
node._countedByPrototype = void 0;
return nodes;
}
}
})(),
// mark each child node with its position (for nth calls)
@ -480,7 +480,7 @@ Object.extend(Selector, {
if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
}
},
// filters out duplicates and extends all nodes
unique: function(nodes) {
if (nodes.length == 0) return nodes;
@ -493,7 +493,7 @@ Object.extend(Selector, {
}
return Selector.handlers.unmark(results);
},
// COMBINATOR FUNCTIONS
descendant: function(nodes) {
var h = Selector.handlers;
@ -501,7 +501,7 @@ Object.extend(Selector, {
h.concat(results, node.getElementsByTagName('*'));
return results;
},
child: function(nodes) {
var h = Selector.handlers;
for (var i = 0, results = [], node; node = nodes[i]; i++) {
@ -510,7 +510,7 @@ Object.extend(Selector, {
}
return results;
},
adjacent: function(nodes) {
for (var i = 0, results = [], node; node = nodes[i]; i++) {
var next = this.nextElementSibling(node);
@ -518,7 +518,7 @@ Object.extend(Selector, {
}
return results;
},
laterSibling: function(nodes) {
var h = Selector.handlers;
for (var i = 0, results = [], node; node = nodes[i]; i++)
@ -531,13 +531,13 @@ Object.extend(Selector, {
if (node.nodeType == 1) return node;
return null;
},
previousElementSibling: function(node) {
while (node = node.previousSibling)
if (node.nodeType == 1) return node;
return null;
},
// TOKEN FUNCTIONS
tagName: function(nodes, root, tagName, combinator) {
var uTagName = tagName.toUpperCase();
@ -557,10 +557,10 @@ Object.extend(Selector, {
return results;
} else return root.getElementsByTagName(tagName);
},
id: function(nodes, root, id, combinator) {
var targetNode = $(id), h = Selector.handlers;
if (root == document) {
// We don't have to deal with orphan nodes. Easy.
if (!targetNode) return [];
@ -576,7 +576,7 @@ Object.extend(Selector, {
}
}
}
if (nodes) {
if (combinator) {
if (combinator == 'child') {
@ -590,7 +590,7 @@ Object.extend(Selector, {
if (Selector.handlers.previousElementSibling(targetNode) == node)
return [targetNode];
} else nodes = h[combinator](nodes);
}
}
for (var i = 0, node; node = nodes[i]; i++)
if (node == targetNode) return [targetNode];
return [];
@ -613,17 +613,17 @@ Object.extend(Selector, {
results.push(node);
}
return results;
},
},
attrPresence: function(nodes, root, attr, combinator) {
if (!nodes) nodes = root.getElementsByTagName("*");
if (nodes && combinator) nodes = this[combinator](nodes);
var results = [];
for (var i = 0, node; node = nodes[i]; i++)
if (Element.hasAttribute(node, attr)) results.push(node);
return results;
return results;
},
attr: function(nodes, root, attr, value, operator, combinator) {
if (!nodes) nodes = root.getElementsByTagName("*");
if (nodes && combinator) nodes = this[combinator](nodes);
@ -635,14 +635,14 @@ Object.extend(Selector, {
}
return results;
},
pseudo: function(nodes, name, value, root, combinator) {
if (nodes && combinator) nodes = this[combinator](nodes);
if (!nodes) nodes = root.getElementsByTagName("*");
return Selector.pseudos[name](nodes, value, root);
}
},
pseudos: {
'first-child': function(nodes, value, root) {
for (var i = 0, results = [], node; node = nodes[i]; i++) {
@ -662,32 +662,32 @@ Object.extend(Selector, {
var h = Selector.handlers;
for (var i = 0, results = [], node; node = nodes[i]; i++)
if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
results.push(node);
results.push(node);
return results;
},
'nth-child': function(nodes, formula, root) {
return Selector.pseudos.nth(nodes, formula, root);
},
'nth-last-child': function(nodes, formula, root) {
'nth-child': function(nodes, formula, root) {
return Selector.pseudos.nth(nodes, formula, root);
},
'nth-last-child': function(nodes, formula, root) {
return Selector.pseudos.nth(nodes, formula, root, true);
},
'nth-of-type': function(nodes, formula, root) {
},
'nth-of-type': function(nodes, formula, root) {
return Selector.pseudos.nth(nodes, formula, root, false, true);
},
'nth-last-of-type': function(nodes, formula, root) {
'nth-last-of-type': function(nodes, formula, root) {
return Selector.pseudos.nth(nodes, formula, root, true, true);
},
'first-of-type': function(nodes, formula, root) {
},
'first-of-type': function(nodes, formula, root) {
return Selector.pseudos.nth(nodes, "1", root, false, true);
},
'last-of-type': function(nodes, formula, root) {
},
'last-of-type': function(nodes, formula, root) {
return Selector.pseudos.nth(nodes, "1", root, true, true);
},
'only-of-type': function(nodes, formula, root) {
var p = Selector.pseudos;
return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
},
// handles the an+b logic
getIndices: function(a, b, total) {
if (a == 0) return b > 0 ? [b] : [];
@ -696,7 +696,7 @@ Object.extend(Selector, {
return memo;
});
},
// handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
nth: function(nodes, formula, root, reverse, ofType) {
if (nodes.length == 0) return [];
@ -725,10 +725,10 @@ Object.extend(Selector, {
}
}
h.unmark(nodes);
h.unmark(indexed);
return results;
h.unmark(indexed);
return results;
},
'empty': function(nodes, value, root) {
for (var i = 0, results = [], node; node = nodes[i]; i++) {
// IE treats comments as element nodes
@ -737,7 +737,7 @@ Object.extend(Selector, {
}
return results;
},
'not': function(nodes, selector, root) {
var h = Selector.handlers, selectorType, m;
var exclusions = new Selector(selector).findElements(root);
@ -747,27 +747,27 @@ Object.extend(Selector, {
h.unmark(exclusions);
return results;
},
'enabled': function(nodes, value, root) {
for (var i = 0, results = [], node; node = nodes[i]; i++)
if (!node.disabled && (!node.type || node.type !== 'hidden'))
results.push(node);
return results;
},
'disabled': function(nodes, value, root) {
for (var i = 0, results = [], node; node = nodes[i]; i++)
if (node.disabled) results.push(node);
return results;
},
'checked': function(nodes, value, root) {
for (var i = 0, results = [], node; node = nodes[i]; i++)
if (node.checked) results.push(node);
return results;
}
},
operators: {
'=': function(nv, v) { return nv == v; },
'!=': function(nv, v) { return nv != v; },
@ -778,13 +778,13 @@ Object.extend(Selector, {
'|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
'-').include('-' + (v || "").toUpperCase() + '-'); }
},
/**
* Selector.split(expression) -> [String...]
*
*
* Takes a string of CSS selectors separated by commas; returns an array
* of individual selectors.
*
*
* Safer than doing a naive `Array#split`, since selectors can have commas
* in other places.
**/
@ -798,9 +798,9 @@ Object.extend(Selector, {
/**
* 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: function(elements, expression) {
@ -811,32 +811,32 @@ Object.extend(Selector, {
h.unmark(matches);
return results;
},
/**
* 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) {
if (Object.isNumber(expression)) {
if (Object.isNumber(expression)) {
index = expression; expression = false;
}
return Selector.matchElements(elements, expression || '*')[index || 0];
},
/**
* Selector.findChildElements(element, expressions) -> [Element...]
*
*
* Searches beneath `element` for any elements that match the selector
* (or selectors) specified in `expressions`.
**/
findChildElements: function(element, expressions) {
expressions = Selector.split(expressions.join(','));
var results = [], h = Selector.handlers;
var results = [], h = Selector.handlers;
for (var i = 0, l = expressions.length, selector; i < l; i++) {
selector = new Selector(expressions[i].strip());
h.concat(results, selector.findElements(element));
@ -854,12 +854,12 @@ if (Prototype.Browser.IE) {
if (node.tagName !== "!") a.push(node);
return a;
}
});
});
}
/** related to: Selector
* $$(expression...) -> [Element...]
*
*
* Returns all elements in the document that match the provided CSS selectors.
**/
function $$() {

View File

@ -13,14 +13,14 @@ var Abstract = { };
/**
* 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 {
@ -28,7 +28,7 @@ var Try = {
break;
} catch (e) { }
}
return returnValue;
}
};

View File

@ -1,8 +1,8 @@
/** 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.
*
* 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.
**/
@ -37,98 +37,98 @@ Array.from = $A;
/** section: Language
* class Array
*
*
* 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 a ton of methods in
* already.
* * 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.
*
* <h4>Why you should stop using forin to iterate</h4>
*
* Many JavaScript authors have been misled into using the `forin` JavaScript
* construct to loop over array elements. This kind of code just wont work
*
* <h4>Why you should stop using for...in to iterate</h4>
*
* 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
* on Array (&sect;15.4.4), including nice methods like `concat`, `join`, `pop`, and
* `push`.
*
* This same standard explicitely defines that the `for…in` construct (§12.6.4)
*
* This same standard explicitly defines that the `for...in` construct (&sect;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 forin. This comfort led developers to use `forin` as a
* 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 `forin` on arrays
* `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).
*
*
* <h4>What you should use instead</h4>
*
*
* 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 youll
*
*
* 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.
*
*
*
*
* <h4>A note on performance</h4>
*
*
* 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
* every loop iteration) in methods like [[Array#each]] &mdash; _or_ relying on
* repetitive array construction (such as uniq), may yield unsatisfactory
* performance. In such cases, youre better off writing manual indexing loops,
* 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
* Empties an array.
@ -166,7 +166,7 @@ Array.from = $A;
/**
* Array#flatten() -> Array
* Returns a flat (one-dimensional) version of the array.
* Returns a "flat" (one-dimensional) version of the array.
*
* Nested arrays are recursively injected "inline." This can prove very
* useful when handling the results of a recursive collection algorithm,
@ -229,12 +229,12 @@ Array.from = $A;
* Returns an array containing every item that is shared between the two
* given arrays.
**/
function intersect(array) {
return this.uniq().findAll(function(item) {
function intersect(array) {
return this.uniq().findAll(function(item) {
return array.detect(function(value) { return item === value });
});
});
}
/** alias of: Array#clone
* Array#toArray() -> Array
**/
@ -253,7 +253,7 @@ Array.from = $A;
* Returns the size of the array.
*
* This is just a local optimization of the mixed-in [[Enumerable#size]]
* which avoids array cloning and uses the arrays native length property.
* which avoids array cloning and uses the array's native length property.
**/
function size() {
return this.length;
@ -281,15 +281,15 @@ Array.from = $A;
});
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 position of the first occurrence of `item` within the array or
* `-1` if `item` doesnt exist in the array.
* Returns the position of the first occurrence of `item` within the array &mdash; or
* `-1` if `item` doesn't exist in the array.
**/
function indexOf(item, i) {
i || (i = 0);
@ -299,22 +299,22 @@ Array.from = $A;
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` doesnt exist in the array.
* Returns the position of the last occurrence of `item` within the array &mdash; 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;
@ -323,18 +323,18 @@ Array.from = $A;
if (Object.isArray(item) && !('callee' in item)) {
for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
array.push(item[j]);
} else {
} else {
array.push(item);
}
}
return array;
}
Object.extend(arrayProto, Enumerable);
if (!arrayProto._reverse)
arrayProto._reverse = arrayProto.reverse;
Object.extend(arrayProto, {
_each: _each,
clear: clear,
@ -352,14 +352,14 @@ Array.from = $A;
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;

View File

@ -2,9 +2,9 @@
/** 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).
**/
@ -40,31 +40,31 @@ var Class = (function() {
var parent = null, properties = $A(arguments);
if (Object.isFunction(properties[0]))
parent = properties.shift();
function klass() {
this.initialize.apply(this, arguments);
}
Object.extend(klass, Class.Methods);
klass.superclass = parent;
klass.subclasses = [];
if (parent) {
subclass.prototype = parent.prototype;
klass.prototype = new subclass;
parent.subclasses.push(klass);
}
for (var i = 0; i < properties.length; i++)
klass.addMethods(properties[i]);
if (!klass.prototype.initialize)
klass.prototype.initialize = Prototype.emptyFunction;
klass.prototype.constructor = klass;
return klass;
return klass;
}
/**
* Class#addMethods(methods) -> Class
* - methods (Object): The methods to add to the class.
@ -77,7 +77,7 @@ var Class = (function() {
* defined.
*
* New methods propagate down the inheritance chain. If the class has
* subclasses, those subclasses will receive the new methods even in the
* subclasses, those subclasses will receive the new methods &mdash; 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.
@ -85,16 +85,16 @@ var Class = (function() {
function addMethods(source) {
var ancestor = this.superclass && this.superclass.prototype;
var properties = Object.keys(source);
// IE6 doesn't enumerate toString and valueOf properties,
// Force copy if they're not coming from Object.prototype.
// IE6 doesn't enumerate toString and valueOf properties,
// Force copy if they're not coming from Object.prototype.
if (!Object.keys({ toString: true }).length) {
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) &&
@ -109,14 +109,14 @@ var Class = (function() {
}
this.prototype[property] = value;
}
return this;
return this;
}
return {
create: create,
Methods: {
addMethods: addMethods
}
};
})();
})();

View File

@ -1,12 +1,12 @@
/** 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.
**/
Date.prototype.toJSON = function() {

View File

@ -1,84 +1,84 @@
/** section: Language
* mixin Enumerable
*
* `Enumerable` provides a large set of useful methods for enumerations
*
* `Enumerable` provides a large set of useful methods for enumerations &mdash;
* 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 youll find it in less obvious spots as
* 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.
*
*
* <h4>The <code>context</code> parameter</h4>
*
*
* 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
* iterator will be _bound_ to &mdash; 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.
*
*
* <h4>Mixing <code>Enumerable</code> into your own objects</h4>
*
* So, lets say youve created your very own collection-like object (say,
* 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.
*
* "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 youre done with this, you just need to mix `Enumerable` in, which
* youll usually do before defining your methods, so as to make sure whatever
*
* 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 = { };
@ -104,7 +104,7 @@ var Enumerable = (function() {
}
return this;
}
/**
* Enumerable#eachSlice(number[, iterator = Prototype.K[, context]]) -> Enumerable
*
@ -167,11 +167,11 @@ var Enumerable = (function() {
});
return results;
}
/**
* Enumerable#detect(iterator[, context]) -> firstElement | undefined
*
* Finds the first element for which the iterator returns a truthy value.
* Finds the first element for which the iterator returns a "truthy" value.
* Aliased by the [[Enumerable#find]] method.
**/
function detect(iterator, context) {
@ -184,11 +184,11 @@ var Enumerable = (function() {
});
return result;
}
/**
* Enumerable#findAll(iterator[, context]) -> Array
*
* Returns all the elements for which the iterator returned truthy value.
* Returns all the elements for which the iterator returned "truthy" value.
* Aliased as [[Enumerable#select]].
**/
function findAll(iterator, context) {
@ -199,7 +199,7 @@ var Enumerable = (function() {
});
return results;
}
/**
* Enumerable#grep(regex[, iterator = Prototype.K[, context]]) -> Array
*
@ -212,14 +212,14 @@ var Enumerable = (function() {
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
*
@ -239,7 +239,7 @@ var Enumerable = (function() {
});
return found;
}
/**
* Enumerable#inGroupsOf(size[, filler = null]) -> [group...]
*
@ -253,7 +253,7 @@ var Enumerable = (function() {
return slice;
});
}
/**
* Enumerable#inject(accumulator, iterator[, context]) -> accumulatedValue
*
@ -267,7 +267,7 @@ var Enumerable = (function() {
});
return memo;
}
/**
* Enumerable#invoke(methodName[, arg...]) -> Array
*
@ -280,7 +280,7 @@ var Enumerable = (function() {
return value[method].apply(value, args);
});
}
/**
* Enumerable#max([iterator = Prototype.K[, context]]) -> maxValue
*
@ -299,7 +299,7 @@ var Enumerable = (function() {
});
return result;
}
/**
* Enumerable#min([iterator = Prototype.K[, context]]) -> minValue
*
@ -318,7 +318,7 @@ var Enumerable = (function() {
});
return result;
}
/**
* Enumerable#partition([iterator = Prototype.K[, context]]) -> [TrueArray, FalseArray]
*
@ -336,7 +336,7 @@ var Enumerable = (function() {
});
return [trues, falses];
}
/**
* Enumerable#pluck(propertyName) -> Array
*
@ -350,11 +350,11 @@ var Enumerable = (function() {
});
return results;
}
/**
* Enumerable#reject(iterator[, context]) -> Array
*
* Returns all the elements for which the iterator returned a falsy value.
* Returns all the elements for which the iterator returned a "falsy" value.
**/
function reject(iterator, context) {
var results = [];
@ -364,7 +364,7 @@ var Enumerable = (function() {
});
return results;
}
/**
* Enumerable#sortBy(iterator[, context]) -> Array
*
@ -382,7 +382,7 @@ var Enumerable = (function() {
return a < b ? -1 : a > b ? 1 : 0;
}).pluck('value');
}
/**
* Enumerable#toArray() -> Array
*
@ -391,7 +391,7 @@ var Enumerable = (function() {
function toArray() {
return this.map();
}
/**
* Enumerable#zip(sequence...[, iterator = Prototype.K]) -> Array
*
@ -410,7 +410,7 @@ var Enumerable = (function() {
return iterator(collections.pluck(index));
});
}
/**
* Enumerable#size() -> Number
*
@ -419,7 +419,7 @@ var Enumerable = (function() {
function size() {
return this.toArray().length;
}
/**
* Enumerable#inspect() -> String
*
@ -428,39 +428,39 @@ var Enumerable = (function() {
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
* Enumerable#select(iterator[, context]) -> Array
**/
/** alias of: Enumerable#findAll
* Enumerable#filter(iterator[, context]) -> Array
* Enumerable#filter(iterator[, context]) -> Array
**/
/** alias of: Enumerable#include
* Enumerable#member(object) -> Boolean
**/
/** alias of: Enumerable#toArray
* Enumerable#entries() -> Array
* Enumerable#entries() -> Array
**/
/** alias of: Enumerable#detect
* Enumerable#find(iterator[, context]) -> firstElement | undefined
**/
return {
each: each,
eachSlice: eachSlice,

View File

@ -1,22 +1,22 @@
/** 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
*
@ -66,7 +66,7 @@ Object.extend(Function.prototype, (function() {
/**
* Function#curry(args...) -> Function
* Partially applies the function, returning a function with one or more
* arguments already filled in.
* arguments already "filled in."
*
* Function#curry works just like [[Function#bind]] without the initial
* scope argument. Use the latter if you need to partially apply a function
@ -94,7 +94,7 @@ Object.extend(Function.prototype, (function() {
* To schedule a function to run as soon as the interpreter is idle, use
* [[Function#defer]].
**/
function delay(timeout) {
function delay(timeout) {
var __method = this, args = slice.call(arguments, 1);
timeout = timeout * 1000
return window.setTimeout(function() {
@ -106,8 +106,8 @@ Object.extend(Function.prototype, (function() {
* Function#defer(args...) -> Number
* 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 interpreters call stack is empty.
* 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
@ -122,7 +122,7 @@ Object.extend(Function.prototype, (function() {
* Function#wrap(wrapperFunction) -> Function
* - wrapperFunction (Function): The function to act as a wrapper.
*
* Returns a function wrapped around the original function.
* 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
@ -152,7 +152,7 @@ Object.extend(Function.prototype, (function() {
return __method.apply(null, a);
};
}
return {
argumentNames: argumentNames,
bind: bind,

View File

@ -1,8 +1,8 @@
/** section: Language, related to: Hash
* $H([object]) -> Hash
*
*
* Creates a `Hash`.
*
*
* `$H` is a convenience wrapper around the Hash constructor, with a safeguard
* that lets you pass an existing Hash object and get it back untouched
* (instead of uselessly cloning it).
@ -13,24 +13,24 @@ function $H(object) {
/** section: Language
* class Hash
*
*
* 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.
*
*
* <h4>Creating a hash</h4>
*
*
* There are two ways to construct a Hash instance: the first is regular
* JavaScript object instantiation with the `new` keyword, and the second is
* using the [[$H]] function. There is one difference between them: if a `Hash`
* is passed to `$H`, it will be returned as-is, wherease the same hash passed
* to `new Hash` will be _cloned_ instead.
*
*
**/
var Hash = Class.create(Enumerable, (function() {
/**
@ -55,7 +55,7 @@ var Hash = Class.create(Enumerable, (function() {
/**
* Hash#set(key, value) -> value
*
* Sets the hashs `key` property to `value` and returns `value`.
* Sets the hash's `key` property to `value` and returns `value`.
**/
function set(key, value) {
return this._object[key] = value;
@ -64,7 +64,7 @@ var Hash = Class.create(Enumerable, (function() {
/**
* Hash#get(key) -> value
*
* Returns the value of the hashs `key` property.
* Returns the value of the hash's `key` property.
**/
function get(key) {
// simulating poorly supported hasOwnProperty
@ -75,7 +75,7 @@ var Hash = Class.create(Enumerable, (function() {
/**
* Hash#unset(key) -> value
*
* Deletes the hashs `key` property and returns its value.
* Deletes the hash's `key` property and returns its value.
**/
function unset(key) {
var value = this._object[key];
@ -94,7 +94,7 @@ var Hash = Class.create(Enumerable, (function() {
/**
* Hash#keys() -> [String...]
*
*
* Provides an Array of keys (that is, property names) for the hash.
**/
function keys() {
@ -112,13 +112,13 @@ var Hash = Class.create(Enumerable, (function() {
/**
* 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;
var match = this.detect(function(pair) {
return pair.value === value;
});
return match && match.key;
}
@ -128,7 +128,7 @@ var Hash = Class.create(Enumerable, (function() {
*
* Returns a new hash with `object`'s key/value pairs merged in.
* To modify the original hash in place, use [[Hash#update]].
*
*
**/
function merge(object) {
return this.clone().update(object);
@ -156,13 +156,13 @@ var Hash = Class.create(Enumerable, (function() {
/** related to: String#toQueryParams
* Hash#toQueryString() -> String
*
*
* Turns a hash into its URL-encoded query string representation.
**/
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)));
@ -173,7 +173,7 @@ var Hash = Class.create(Enumerable, (function() {
/** related to: Object.inspect
* Hash#inspect() -> String
*
*
* Returns the debug-oriented string representation of the hash.
**/
function inspect() {
@ -184,7 +184,7 @@ var Hash = Class.create(Enumerable, (function() {
/** related to: Object.toJSON
* Hash#toJSON() -> String
*
*
* Returns a JSON string.
**/
function toJSON() {
@ -199,7 +199,7 @@ var Hash = Class.create(Enumerable, (function() {
function clone() {
return new Hash(this);
}
return {
initialize: initialize,
_each: _each,

View File

@ -1,16 +1,16 @@
/** 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]].
* * Ruby-like 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() {
/**
@ -33,7 +33,7 @@ Object.extend(Number.prototype, (function() {
function succ() {
return this + 1;
}
/**
* Number#times(iterator) -> Number
*
@ -45,11 +45,11 @@ Object.extend(Number.prototype, (function() {
$R(0, this, true).each(iterator, context);
return this;
}
/**
* Number#toPaddedString(length[, radix]) -> String
*
* Converts the number into a string padded with 0s so that the strings length
* Converts the number into a string padded with 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.
**/
@ -57,7 +57,7 @@ Object.extend(Number.prototype, (function() {
var string = this.toString(radix || 10);
return '0'.times(length - string.length) + string;
}
/** related to: Object.toJSON
* Number#toJSON() -> String
*
@ -66,7 +66,7 @@ Object.extend(Number.prototype, (function() {
function toJSON() {
return isFinite(this) ? this.toString() : 'null';
}
/**
* Number#abs() -> Number
*
@ -75,7 +75,7 @@ Object.extend(Number.prototype, (function() {
function abs() {
return Math.abs(this);
}
/**
* Number#round() -> Number
*
@ -84,7 +84,7 @@ Object.extend(Number.prototype, (function() {
function round() {
return Math.round(this);
}
/**
* Number#ceil() -> Number
*
@ -93,7 +93,7 @@ Object.extend(Number.prototype, (function() {
function ceil() {
return Math.ceil(this);
}
/**
* Number#floor() -> Number
*
@ -102,7 +102,7 @@ Object.extend(Number.prototype, (function() {
function floor() {
return Math.floor(this);
}
return {
toColorPart: toColorPart,
succ: succ,

View File

@ -1,20 +1,20 @@
/** 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() {
function getClass(object) {
return Object.prototype.toString.call(object)
.match(/^\[object\s(.*)\]$/)[1];
.match(/^\[object\s(.*)\]$/)[1];
}
/**
* Object.extend(destination, source) -> Object
* - destination (Object): The object to receive the new properties.
@ -41,9 +41,9 @@
* 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`.
* library-defined &mdash; among them `String`, `Array`, `Enumerable` and `Hash`.
* These attempt to provide useful string representations (from a
* developers standpoint) for their respective types.
* developer's standpoint) for their respective types.
**/
function inspect(object) {
try {
@ -104,11 +104,11 @@
*
* 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 JavaScripts native `encodeURIComponent`
* 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
* irrelevant anyway) &mdash; except for array-based parts, which are serialized
* in array order.
**/
function toQueryString(object) {
@ -121,7 +121,7 @@
*
* Converts the object to its HTML representation.
*
* Returns the return value of `object`s `toHTML` method if it exists; else
* Returns the return value of `object`'s `toHTML` method if it exists; else
* runs `object` through [[String.interpret]].
**/
function toHTML(object) {
@ -134,7 +134,7 @@
*
* Returns an array of the object's property names.
*
* Note that the order of the resulting array is browser-dependent it
* Note that the order of the resulting array is browser-dependent &mdash; it
* relies on the `for&#8230;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.
@ -152,7 +152,7 @@
*
* Returns an array of the object's values.
*
* Note that the order of the resulting array is browser-dependent it
* Note that the order of the resulting array is browser-dependent &mdash; it
* relies on the `for&#8230;in` loop, for which the ECMAScript spec does not
* prescribe an enumeration order.
*

View File

@ -1,8 +1,8 @@
/** 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.
@ -35,7 +35,7 @@ var PeriodicalExecuter = Class.create({
execute: function() {
this.callback(this);
},
/**
* PeriodicalExecuter#stop() -> undefined
*
@ -60,4 +60,4 @@ var PeriodicalExecuter = Class.create({
}
}
}
});
});

View File

@ -1,19 +1,19 @@
/** section: Language
* class ObjectRange
*
*
* 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).
@ -22,8 +22,8 @@
/** section: Language
* $R(start, end[, exclusive = false]) -> ObjectRange
*
* Creates a new ObjectRange object.
* This method is a convenience wrapper around the [[ObjectRange]] constructor,
* 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) {
@ -33,9 +33,9 @@ function $R(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.
**/
@ -44,7 +44,7 @@ var ObjectRange = Class.create(Enumerable, (function() {
this.end = end;
this.exclusive = exclusive;
}
function _each(iterator) {
var value = this.start;
while (this.include(value)) {
@ -52,20 +52,20 @@ var ObjectRange = Class.create(Enumerable, (function() {
value = value.succ();
}
}
/**
* ObjectRange#include(value) -> Boolean
*
* Determines whether the value is included in the range.
**/
function include(value) {
if (value < this.start)
if (value < this.start)
return false;
if (this.exclusive)
return value < this.end;
return value <= this.end;
}
return {
initialize: initialize,
_each: _each,

View File

@ -1,12 +1,12 @@
/** 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;

View File

@ -1,8 +1,8 @@
/** 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
@ -29,13 +29,13 @@ Object.extend(String, {
});
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
*
@ -46,7 +46,7 @@ Object.extend(String.prototype, (function() {
function gsub(pattern, replacement) {
var result = '', source = this, match;
replacement = prepareReplacement(replacement);
if (Object.isString(pattern))
pattern = RegExp.escape(pattern);
@ -87,7 +87,7 @@ Object.extend(String.prototype, (function() {
/** related to: String#gsub
* String#scan(pattern, iterator) -> String
*
* Allows iterating over every occurrence of the given pattern (which can be a
* Allows iterating over every occurrence of the given pattern (which can be a
* string or a regular expression).
* Returns the original string.
**/
@ -105,7 +105,7 @@ Object.extend(String.prototype, (function() {
function truncate(length, truncation) {
length = length || 30;
truncation = Object.isUndefined(truncation) ? '...' : truncation;
return this.length > length ?
return this.length > length ?
this.slice(0, length - truncation.length) + truncation : String(this);
}
@ -123,7 +123,7 @@ Object.extend(String.prototype, (function() {
*
* Strips a string of any HTML tags.
*
* Note that `stripTags` will only strip HTML 4.01 tags like `div`,
* Note that `stripTags` will only strip HTML 4.01 tags &mdash; like `div`,
* `span`, and `abbr`. It _will not_ strip namespace-prefixed tags such
* as `h:table` or `xsl:template`.
**/
@ -183,11 +183,11 @@ Object.extend(String.prototype, (function() {
function unescapeHTML() {
var div = document.createElement('div');
div.innerHTML = this.stripTags();
return div.childNodes[0] ? (div.childNodes.length > 1 ?
$A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
return div.childNodes[0] ? (div.childNodes.length > 1 ?
$A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
div.childNodes[0].nodeValue) : '';
}
/**
* String#parseQuery([separator = '&']) -> Object
**/
@ -252,14 +252,14 @@ Object.extend(String.prototype, (function() {
/**
* String#camelize() -> String
*
* Converts a string separated by dashes into a camelCase equivalent.
* Converts a string separated by dashes into a camelCase equivalent.
* For instance, 'foo-bar' would be converted to 'fooBar'.
**/
function camelize() {
var parts = this.split('-'), len = parts.length;
if (len == 1) return parts[0];
var camelized = this.charAt(0) == '-'
var camelized = this.charAt(0) == '-'
? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
: parts[0];
@ -300,7 +300,7 @@ Object.extend(String.prototype, (function() {
/** related to: Object.inspect
* String#inspect([useDoubleQuotes = false]) -> String
*
* Returns a debug-oriented version of the string (i.e. wrapped in single or
* Returns a debug-oriented version of the string (i.e. wrapped in single or
* double quotes, with backslashes and quotes escaped).
**/
function inspect(useDoubleQuotes) {
@ -334,7 +334,7 @@ Object.extend(String.prototype, (function() {
/**
* String#isJSON() -> Boolean
*
* Check if the string is valid JSON by the use of regular expressions.
* Check if the string is valid JSON by the use of regular expressions.
* This security method is called internally.
**/
function isJSON() {
@ -348,7 +348,7 @@ Object.extend(String.prototype, (function() {
* 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_.
@ -401,7 +401,7 @@ Object.extend(String.prototype, (function() {
/**
* String#blank() -> Boolean
*
* Check if the string is "blank" either empty (length of `0`) or containing
* Check if the string is "blank" &mdash; either empty (length of `0`) or containing
* only whitespace.
**/
function blank() {
@ -411,13 +411,13 @@ Object.extend(String.prototype, (function() {
/**
* String#interpolate(object[, pattern]) -> String
*
* Treats the string as a [[Template]] and fills it with `object`s
* 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,
@ -472,4 +472,4 @@ if ('&lt;\n&gt;'.unescapeHTML() !== '<\n>') {
String.prototype.unescapeHTML = function() {
return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
};
}
}

View File

@ -1,57 +1,57 @@
/** 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.
*
*
* <h4>Straightforward templates</h4>
*
*
* 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
* 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 = {
* 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."
*
*
* <h4>Templates are meant to be reused</h4>
*
*
* 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
*
* // 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);
@ -59,13 +59,13 @@
* // -> 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.
*
*
* <h4>Escape sequence</h4>
*
*
* 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.
@ -74,9 +74,9 @@
* var data = { lang:'Ruby', variable: '(not used)' };
* t.evaluate(data);
* // -> in Ruby we also use the #{variable} syntax for templates.
*
*
* <h4>Custom syntaxes</h4>
*
*
* 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
@ -84,16 +84,16 @@
* object to match the replaceable symbols in the template string. Let's put
* together a template that uses a syntax similar to the ubiquitous `<&#38;= %>`
* constructs:
*
*
* // matches symbols like '<&#38;= field %>'
* var syntax = /(^|.|\r|\n)(\<%=\s*(\w+)\s*%\>)/;
*
* var syntax = /(^|.|\r|\n)(\<%=\s*(\w+)\s*%\>)/;
*
* var t = new Template(
* '<div>Name: <b><&#38;= name %></b>, Age: <b><&#38;=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
@ -101,14 +101,14 @@
* 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`.
**/
@ -116,12 +116,12 @@ var Template = Class.create({
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.
* 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))
@ -129,10 +129,10 @@ var Template = Class.create({
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];
var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
match = pattern.exec(expr);
@ -145,7 +145,7 @@ var Template = Class.create({
expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
match = pattern.exec(expr);
}
return before + String.interpret(ctx);
});
}

18
src/prototype.js vendored
View File

@ -5,10 +5,10 @@
* 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`
@ -34,31 +34,31 @@ var Prototype = {
// First, try the named class
if (typeof window.HTMLDivElement !== 'undefined')
return true;
var div = document.createElement('div');
var form = document.createElement('form');
var 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*$/,
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"

View File

@ -1,251 +1,251 @@
/* Unobtrustive Code Highlighter By Dan Webb 11/2005
Version: 0.4
Usage:
Add a script tag for this script and any stylesets you need to use
to the page in question, add correct class names to CODE elements,
define CSS styles for elements. That's it!
Known to work on:
IE 5.5+ PC
Firefox/Mozilla PC/Mac
Opera 7.23 + PC
Safari 2
Known to degrade gracefully on:
IE5.0 PC
Note: IE5.0 fails due to the use of lookahead in some stylesets. To avoid script errors
in older browsers use expressions that use lookahead in string format when defining stylesets.
This script is inspired by star-light by entirely cunning Dean Edwards
http://dean.edwards.name/star-light/.
*/
// replace callback support for safari.
if ("a".replace(/a/, function() {return "b"}) != "b") (function(){
var default_replace = String.prototype.replace;
String.prototype.replace = function(search,replace){
// replace is not function
if(typeof replace != "function"){
return default_replace.apply(this,arguments)
}
var str = "" + this;
var callback = replace;
// search string is not RegExp
if(!(search instanceof RegExp)){
var idx = str.indexOf(search);
return (
idx == -1 ? str :
default_replace.apply(str,[search,callback(search, idx, str)])
)
}
var reg = search;
var result = [];
var lastidx = reg.lastIndex;
var re;
while((re = reg.exec(str)) != null){
var idx = re.index;
var args = re.concat(idx, str);
result.push(
str.slice(lastidx,idx),
callback.apply(null,args).toString()
);
if(!reg.global){
lastidx += RegExp.lastMatch.length;
break
}else{
lastidx = reg.lastIndex;
}
}
result.push(str.slice(lastidx));
return result.join("")
}
})();
var CodeHighlighter = { styleSets : new Array };
CodeHighlighter.addStyle = function(name, rules) {
// using push test to disallow older browsers from adding styleSets
if ([].push) this.styleSets.push({
name : name,
rules : rules,
ignoreCase : arguments[2] || false
})
function setEvent() {
// set highlighter to run on load (use LowPro if present)
if (typeof Event != 'undefined' && typeof Event.onReady == 'function')
return Event.onReady(CodeHighlighter.init.bind(CodeHighlighter));
var old = window.onload;
if (typeof window.onload != 'function') {
window.onload = function() { CodeHighlighter.init() };
} else {
window.onload = function() {
old();
CodeHighlighter.init();
}
}
}
// only set the event when the first style is added
if (this.styleSets.length==1) setEvent();
}
CodeHighlighter.init = function() {
if (!document.getElementsByTagName) return;
if ("a".replace(/a/, function() {return "b"}) != "b") return; // throw out Safari versions that don't support replace function
// throw out older browsers
var codeEls = document.getElementsByTagName("CODE");
// collect array of all pre elements
codeEls.filter = function(f) {
var a = new Array;
for (var i = 0; i < this.length; i++) if (f(this[i])) a[a.length] = this[i];
return a;
}
var rules = new Array;
rules.toString = function() {
// joins regexes into one big parallel regex
var exps = new Array;
for (var i = 0; i < this.length; i++) exps.push(this[i].exp);
return exps.join("|");
}
function addRule(className, rule) {
// add a replace rule
var exp = (typeof rule.exp != "string")?String(rule.exp).substr(1, String(rule.exp).length-2):rule.exp;
// converts regex rules to strings and chops of the slashes
rules.push({
className : className,
exp : "(" + exp + ")",
length : (exp.match(/(^|[^\\])\([^?]/g) || "").length + 1, // number of subexps in rule
replacement : rule.replacement || null
});
}
function parse(text, ignoreCase) {
// main text parsing and replacement
return text.replace(new RegExp(rules, (ignoreCase)?"gi":"g"), function() {
var i = 0, j = 1, rule;
while (rule = rules[i++]) {
if (arguments[j]) {
// if no custom replacement defined do the simple replacement
if (!rule.replacement) return "<span class=\"" + rule.className + "\">" + arguments[0] + "</span>";
else {
// replace $0 with the className then do normal replaces
var str = rule.replacement.replace("$0", rule.className);
for (var k = 1; k <= rule.length - 1; k++) str = str.replace("$" + k, arguments[j + k]);
return str;
}
} else j+= rule.length;
}
});
}
function highlightCode(styleSet) {
// clear rules array
var parsed;
rules.length = 0;
// get stylable elements by filtering out all code elements without the correct className
var stylableEls = codeEls.filter(function(item) {return (item.className.indexOf(styleSet.name)>=0)});
// add style rules to parser
for (var className in styleSet.rules) addRule(className, styleSet.rules[className]);
// replace for all elements
for (var i = 0; i < stylableEls.length; i++) {
// EVIL hack to fix IE whitespace badness if it's inside a <pre>
if (/MSIE/.test(navigator.appVersion) && stylableEls[i].parentNode.nodeName == 'PRE') {
stylableEls[i] = stylableEls[i].parentNode;
parsed = stylableEls[i].innerHTML.replace(/(<code[^>]*>)([^<]*)<\/code>/i, function() {
return arguments[1] + parse(arguments[2], styleSet.ignoreCase) + "</code>"
});
parsed = parsed.replace(/\n( *)/g, function() {
var spaces = "";
for (var i = 0; i < arguments[1].length; i++) spaces+= "&nbsp;";
return "\n" + spaces;
});
parsed = parsed.replace(/\t/g, "&nbsp;&nbsp;&nbsp;&nbsp;");
parsed = parsed.replace(/\n(<\/\w+>)?/g, "<br />$1").replace(/<br \/>[\n\r\s]*<br \/>/g, "<p><br></p>");
} else parsed = parse(stylableEls[i].innerHTML, styleSet.ignoreCase);
stylableEls[i].innerHTML = parsed;
}
}
// run highlighter on all stylesets
for (var i=0; i < this.styleSets.length; i++) {
highlightCode(this.styleSets[i]);
}
};
CodeHighlighter.addStyle("css", {
comment : {
exp : /\/\*[^*]*\*+([^\/][^*]*\*+)*\//
},
keywords : {
exp : /@\w[\w\s]*/
},
selectors : {
exp : "([\\w-:\\[.#][^{};>]*)(?={)"
},
properties : {
exp : "([\\w-]+)(?=\\s*:)"
},
units : {
exp : /([0-9])(em|en|px|%|pt)\b/,
replacement : "$1<span class=\"$0\">$2</span>"
},
urls : {
exp : /url\([^\)]*\)/
}
});
CodeHighlighter.addStyle("html", {
comment : {
exp: /&lt;!\s*(--([^-]|[\r\n]|-[^-])*--\s*)&gt;/
},
tag : {
exp: /(&lt;\/?)([a-zA-Z]+\s?)/,
replacement: "$1<span class=\"$0\">$2</span>"
},
string : {
exp : /'[^']*'|"[^"]*"/
},
attribute : {
exp: /\b([a-zA-Z-:]+)(=)/,
replacement: "<span class=\"$0\">$1</span>$2"
},
doctype : {
exp: /&lt;!DOCTYPE([^&]|&[^g]|&g[^t])*&gt;/
}
});
CodeHighlighter.addStyle("javascript",{
comment : {
exp : /(\/\/[^\n]*(\n|$))|(\/\*[^*]*\*+([^\/][^*]*\*+)*\/)/
},
brackets : {
exp : /\(|\)/
},
regex : {
exp : /\/(.*?)[g|s|m]?\/[;|\n]/
},
string : {
exp : /'(?:\.|(\\\')|[^\''])*'|"(?:\.|(\\\")|[^\""])*"/
},
keywords : {
exp : /\b(arguments|break|case|continue|default|delete|do|else|false|for|function|if|in|instanceof|new|null|return|switch|this|true|typeof|var|void|while|with)\b/
},
global : {
exp : /\b(toString|valueOf|window|element|prototype|constructor|document|escape|unescape|parseInt|parseFloat|setTimeout|clearTimeout|setInterval|clearInterval|NaN|isNaN|Infinity|alert|prompt|confirm)\b/
}
});
/* Unobtrustive Code Highlighter By Dan Webb 11/2005
Version: 0.4
Usage:
Add a script tag for this script and any stylesets you need to use
to the page in question, add correct class names to CODE elements,
define CSS styles for elements. That's it!
Known to work on:
IE 5.5+ PC
Firefox/Mozilla PC/Mac
Opera 7.23 + PC
Safari 2
Known to degrade gracefully on:
IE5.0 PC
Note: IE5.0 fails due to the use of lookahead in some stylesets. To avoid script errors
in older browsers use expressions that use lookahead in string format when defining stylesets.
This script is inspired by star-light by entirely cunning Dean Edwards
http://dean.edwards.name/star-light/.
*/
// replace callback support for safari.
if ("a".replace(/a/, function() {return "b"}) != "b") (function(){
var default_replace = String.prototype.replace;
String.prototype.replace = function(search,replace){
// replace is not function
if(typeof replace != "function"){
return default_replace.apply(this,arguments)
}
var str = "" + this;
var callback = replace;
// search string is not RegExp
if(!(search instanceof RegExp)){
var idx = str.indexOf(search);
return (
idx == -1 ? str :
default_replace.apply(str,[search,callback(search, idx, str)])
)
}
var reg = search;
var result = [];
var lastidx = reg.lastIndex;
var re;
while((re = reg.exec(str)) != null){
var idx = re.index;
var args = re.concat(idx, str);
result.push(
str.slice(lastidx,idx),
callback.apply(null,args).toString()
);
if(!reg.global){
lastidx += RegExp.lastMatch.length;
break
}else{
lastidx = reg.lastIndex;
}
}
result.push(str.slice(lastidx));
return result.join("")
}
})();
var CodeHighlighter = { styleSets : new Array };
CodeHighlighter.addStyle = function(name, rules) {
// using push test to disallow older browsers from adding styleSets
if ([].push) this.styleSets.push({
name : name,
rules : rules,
ignoreCase : arguments[2] || false
})
function setEvent() {
// set highlighter to run on load (use LowPro if present)
if (typeof Event != 'undefined' && typeof Event.onReady == 'function')
return Event.onReady(CodeHighlighter.init.bind(CodeHighlighter));
var old = window.onload;
if (typeof window.onload != 'function') {
window.onload = function() { CodeHighlighter.init() };
} else {
window.onload = function() {
old();
CodeHighlighter.init();
}
}
}
// only set the event when the first style is added
if (this.styleSets.length==1) setEvent();
}
CodeHighlighter.init = function() {
if (!document.getElementsByTagName) return;
if ("a".replace(/a/, function() {return "b"}) != "b") return; // throw out Safari versions that don't support replace function
// throw out older browsers
var codeEls = document.getElementsByTagName("CODE");
// collect array of all pre elements
codeEls.filter = function(f) {
var a = new Array;
for (var i = 0; i < this.length; i++) if (f(this[i])) a[a.length] = this[i];
return a;
}
var rules = new Array;
rules.toString = function() {
// joins regexes into one big parallel regex
var exps = new Array;
for (var i = 0; i < this.length; i++) exps.push(this[i].exp);
return exps.join("|");
}
function addRule(className, rule) {
// add a replace rule
var exp = (typeof rule.exp != "string")?String(rule.exp).substr(1, String(rule.exp).length-2):rule.exp;
// converts regex rules to strings and chops of the slashes
rules.push({
className : className,
exp : "(" + exp + ")",
length : (exp.match(/(^|[^\\])\([^?]/g) || "").length + 1, // number of subexps in rule
replacement : rule.replacement || null
});
}
function parse(text, ignoreCase) {
// main text parsing and replacement
return text.replace(new RegExp(rules, (ignoreCase)?"gi":"g"), function() {
var i = 0, j = 1, rule;
while (rule = rules[i++]) {
if (arguments[j]) {
// if no custom replacement defined do the simple replacement
if (!rule.replacement) return "<span class=\"" + rule.className + "\">" + arguments[0] + "</span>";
else {
// replace $0 with the className then do normal replaces
var str = rule.replacement.replace("$0", rule.className);
for (var k = 1; k <= rule.length - 1; k++) str = str.replace("$" + k, arguments[j + k]);
return str;
}
} else j+= rule.length;
}
});
}
function highlightCode(styleSet) {
// clear rules array
var parsed;
rules.length = 0;
// get stylable elements by filtering out all code elements without the correct className
var stylableEls = codeEls.filter(function(item) {return (item.className.indexOf(styleSet.name)>=0)});
// add style rules to parser
for (var className in styleSet.rules) addRule(className, styleSet.rules[className]);
// replace for all elements
for (var i = 0; i < stylableEls.length; i++) {
// EVIL hack to fix IE whitespace badness if it's inside a <pre>
if (/MSIE/.test(navigator.appVersion) && stylableEls[i].parentNode.nodeName == 'PRE') {
stylableEls[i] = stylableEls[i].parentNode;
parsed = stylableEls[i].innerHTML.replace(/(<code[^>]*>)([^<]*)<\/code>/i, function() {
return arguments[1] + parse(arguments[2], styleSet.ignoreCase) + "</code>"
});
parsed = parsed.replace(/\n( *)/g, function() {
var spaces = "";
for (var i = 0; i < arguments[1].length; i++) spaces+= "&nbsp;";
return "\n" + spaces;
});
parsed = parsed.replace(/\t/g, "&nbsp;&nbsp;&nbsp;&nbsp;");
parsed = parsed.replace(/\n(<\/\w+>)?/g, "<br />$1").replace(/<br \/>[\n\r\s]*<br \/>/g, "<p><br></p>");
} else parsed = parse(stylableEls[i].innerHTML, styleSet.ignoreCase);
stylableEls[i].innerHTML = parsed;
}
}
// run highlighter on all stylesets
for (var i=0; i < this.styleSets.length; i++) {
highlightCode(this.styleSets[i]);
}
};
CodeHighlighter.addStyle("css", {
comment : {
exp : /\/\*[^*]*\*+([^\/][^*]*\*+)*\//
},
keywords : {
exp : /@\w[\w\s]*/
},
selectors : {
exp : "([\\w-:\\[.#][^{};>]*)(?={)"
},
properties : {
exp : "([\\w-]+)(?=\\s*:)"
},
units : {
exp : /([0-9])(em|en|px|%|pt)\b/,
replacement : "$1<span class=\"$0\">$2</span>"
},
urls : {
exp : /url\([^\)]*\)/
}
});
CodeHighlighter.addStyle("html", {
comment : {
exp: /&lt;!\s*(--([^-]|[\r\n]|-[^-])*--\s*)&gt;/
},
tag : {
exp: /(&lt;\/?)([a-zA-Z]+\s?)/,
replacement: "$1<span class=\"$0\">$2</span>"
},
string : {
exp : /'[^']*'|"[^"]*"/
},
attribute : {
exp: /\b([a-zA-Z-:]+)(=)/,
replacement: "<span class=\"$0\">$1</span>$2"
},
doctype : {
exp: /&lt;!DOCTYPE([^&]|&[^g]|&g[^t])*&gt;/
}
});
CodeHighlighter.addStyle("javascript",{
comment : {
exp : /(\/\/[^\n]*(\n|$))|(\/\*[^*]*\*+([^\/][^*]*\*+)*\/)/
},
brackets : {
exp : /\(|\)/
},
regex : {
exp : /\/(.*?)[g|s|m]?\/[;|\n]/
},
string : {
exp : /'(?:\.|(\\\')|[^\''])*'|"(?:\.|(\\\")|[^\""])*"/
},
keywords : {
exp : /\b(arguments|break|case|continue|default|delete|do|else|false|for|function|if|in|instanceof|new|null|return|switch|this|true|typeof|var|void|while|with)\b/
},
global : {
exp : /\b(toString|valueOf|window|element|prototype|constructor|document|escape|unescape|parseInt|parseFloat|setTimeout|clearTimeout|setInterval|clearInterval|NaN|isNaN|Infinity|alert|prompt|confirm)\b/
}
});