Fixed a variety of non-ASCII chars and similar [#610 state:resolved]
This commit is contained in:
parent
6e4a3cdec8
commit
ad89bfb738
|
@ -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)
|
||||
|
|
34
src/ajax.js
34
src/ajax.js
|
@ -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 — 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"
|
||||
|
|
|
@ -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]].
|
||||
**/
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
/** section: Ajax
|
||||
* class Ajax.PeriodicalUpdater
|
||||
*
|
||||
* Periodically performs an Ajax request and updates a container’s 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]] — _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;
|
||||
|
|
|
@ -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 — or its contents are invalid — `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'];
|
||||
|
|
|
@ -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 — 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]] — 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-- }
|
||||
});
|
||||
|
|
|
@ -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 request’s 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 {
|
||||
|
|
|
@ -1,99 +1,99 @@
|
|||
/** section: Ajax
|
||||
* class Ajax.Updater < Ajax.Request
|
||||
*
|
||||
* A class that performs an Ajax request and updates a container’s 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 —
|
||||
* `top`, `bottom`, `before`, or `after` — 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 — _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 you’re 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 — 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(' ');
|
||||
}
|
||||
|
|
10
src/dom.js
10
src/dom.js
|
@ -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).
|
||||
*
|
||||
*
|
||||
**/
|
||||
|
||||
|
||||
|
|
626
src/dom/dom.js
626
src/dom/dom.js
File diff suppressed because it is too large
Load Diff
198
src/dom/event.js
198
src/dom/event.js
|
@ -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 — 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 you’ll 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 you’re 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 —
|
||||
* 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 event’s 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);
|
||||
|
|
142
src/dom/form.js
142
src/dom/form.js
|
@ -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 form’s `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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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 $$() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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 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
|
||||
*
|
||||
* <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 (§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 (§12.6.4)
|
||||
* exists to enumerate the properties of the object appearing on the right side
|
||||
* of the `in` keyword. Only properties specifically marked as _non-enumerable_
|
||||
* are ignored by such a loop. By default, the `prototype` and `length`
|
||||
* properties are so marked, which prevents you from enumerating over array
|
||||
* methods when using for…in. This comfort led developers to use `for…in` as a
|
||||
* methods when using for...in. This comfort led developers to use `for...in` as a
|
||||
* shortcut for indexing loops, when it is not its actual purpose.
|
||||
*
|
||||
*
|
||||
* However, Prototype has no way to mark the methods it adds to
|
||||
* `Array.prototype` as non-enumerable. Therefore, using `for…in` on arrays
|
||||
* `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 you’ll
|
||||
*
|
||||
*
|
||||
* 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]] — _or_ relying on
|
||||
* repetitive array construction (such as uniq), may yield unsatisfactory
|
||||
* performance. In such cases, you’re 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 array’s 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` doesn’t exist in the array.
|
||||
* Returns the position of the first occurrence of `item` within the array — 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` doesn’t exist in the array.
|
||||
* Returns the position of the last occurrence of `item` within the array — or
|
||||
* `-1` if `item` doesn't exist in the array.
|
||||
**/
|
||||
function lastIndexOf(item, i) {
|
||||
i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
|
||||
var n = this.slice(0, i).reverse().indexOf(item);
|
||||
return (n < 0) ? n : i - n - 1;
|
||||
}
|
||||
|
||||
|
||||
// Replaces a built-in function. No PDoc needed.
|
||||
function concat() {
|
||||
var array = slice.call(this, 0), item;
|
||||
|
@ -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;
|
||||
|
|
|
@ -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 — 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
|
||||
}
|
||||
};
|
||||
})();
|
||||
})();
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 —
|
||||
* objects that act as collections of values. It is a cornerstone of
|
||||
* Prototype.
|
||||
*
|
||||
*
|
||||
* `Enumerable` is a _mixin_: a set of methods intended not for standaone
|
||||
* use, but for incorporation into other objects.
|
||||
*
|
||||
*
|
||||
* Prototype mixes `Enumerable` into several classes. The most visible cases
|
||||
* are [[Array]] and [[Hash]], but you’ll find it in less obvious spots as
|
||||
* 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 — 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, let’s say you’ve 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 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
|
||||
*
|
||||
* 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,
|
||||
|
|
|
@ -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 interpreter’s 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,
|
||||
|
|
|
@ -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 hash’s `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 hash’s `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 hash’s `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,
|
||||
|
|
|
@ -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 string’s 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,
|
||||
|
|
|
@ -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 — among them `String`, `Array`, `Enumerable` and `Hash`.
|
||||
* These attempt to provide useful string representations (from a
|
||||
* developer’s 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 JavaScript’s 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) — 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 — it
|
||||
* relies on the `for…in` loop, for which the ECMAScript spec does not
|
||||
* prescribe an enumeration order. Sort the resulting array if you wish to
|
||||
* normalize the order of the object keys.
|
||||
|
@ -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 — it
|
||||
* relies on the `for…in` loop, for which the ECMAScript spec does not
|
||||
* prescribe an enumeration order.
|
||||
*
|
||||
|
|
|
@ -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({
|
|||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 — 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" — 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 ('<\n>'.unescapeHTML() !== '<\n>') {
|
|||
String.prototype.unescapeHTML = function() {
|
||||
return this.stripTags().replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&');
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 `<&= %>`
|
||||
* constructs:
|
||||
*
|
||||
*
|
||||
* // matches symbols like '<&= field %>'
|
||||
* var syntax = /(^|.|\r|\n)(\<%=\s*(\w+)\s*%\>)/;
|
||||
*
|
||||
* var syntax = /(^|.|\r|\n)(\<%=\s*(\w+)\s*%\>)/;
|
||||
*
|
||||
* var t = new Template(
|
||||
* '<div>Name: <b><&= name %></b>, Age: <b><&=age%></b></div>',
|
||||
* syntax);
|
||||
* t.evaluate( {name: 'John Smith', age: 26} );
|
||||
* // -> <div>Name: <b>John Smith</b>, Age: <b>26</b></div>
|
||||
*
|
||||
*
|
||||
* There are important constraints to any custom syntax. Any syntax must
|
||||
* provide at least three groupings in the regular expression. The first
|
||||
* grouping is to capture what comes before the symbol, to detect the backslash
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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+= " ";
|
||||
return "\n" + spaces;
|
||||
});
|
||||
parsed = parsed.replace(/\t/g, " ");
|
||||
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: /<!\s*(--([^-]|[\r\n]|-[^-])*--\s*)>/
|
||||
},
|
||||
tag : {
|
||||
exp: /(<\/?)([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: /<!DOCTYPE([^&]|&[^g]|&g[^t])*>/
|
||||
}
|
||||
});
|
||||
|
||||
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+= " ";
|
||||
return "\n" + spaces;
|
||||
});
|
||||
parsed = parsed.replace(/\t/g, " ");
|
||||
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: /<!\s*(--([^-]|[\r\n]|-[^-])*--\s*)>/
|
||||
},
|
||||
tag : {
|
||||
exp: /(<\/?)([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: /<!DOCTYPE([^&]|&[^g]|&g[^t])*>/
|
||||
}
|
||||
});
|
||||
|
||||
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/
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue