Compare commits

...

34 Commits

Author SHA1 Message Date
Andrew Dupont
3ad847ce4b Fix issue where certain versions of Safari treat class names case-insensitively in Selector/$$ queries. [#390 state:resolved] 2008-11-18 23:26:22 -06:00
Andrew Dupont
9f09a8c791 Refactored class.js. 2008-11-18 22:28:06 -06:00
Andrew Dupont
275a344173 Deleted generated unit test files and added them to .gitignore. 2008-11-18 22:21:27 -06:00
Nick Stakenburg
d12e77c56d Fixed event.js so more unit tests pass on IE. Changed the use of '_' prefix to match other parts of the rewrite branch. 2008-10-26 16:19:37 -05:00
Andrew Dupont
10573fb1a9 Updated .gitignore to ignore files generated by rake tasks. 2008-10-26 16:16:02 -05:00
Andrew Dupont
62a1f7e2ce Fix issue where Function#argumentNames returned incorrect results in IE when comments were intermixed with argument names. [#397 state:resolved] 2008-10-26 16:15:51 -05:00
Andrew Dupont
0cc28be265 Reorganized unit tests to match the file structure of the source. 2008-10-26 15:28:56 -05:00
Andrew Dupont
be44ca0089 Merge branch 'rewrite' of git@github.com:sstephenson/prototype into rewrite 2008-10-02 01:01:48 -05:00
Andrew Dupont
b878bfe3ff Refactored event.js. 2008-10-02 01:00:05 -05:00
Tobie Langel
cd0734b8a7 string.js rewrite. 2008-10-01 15:08:05 +02:00
Tobie Langel
f788cf7048 Revert "Variables renaming in function.js for readability."
This reverts commit 160f85f518.
2008-10-01 15:06:03 +02:00
Tobie Langel
ba30188e5d string.js rewrite. 2008-10-01 15:01:35 +02:00
Tobie Langel
160f85f518 Variables renaming in function.js for readability. 2008-10-01 01:46:48 +02:00
Tobie Langel
d9087c26a9 function.js: rename private 'combine' method to 'update', add a private 'merge' methos. 2008-10-01 01:36:03 +02:00
Tobie Langel
3a93b6ccae Improve performance in function.js by using Array#slice directly. 2008-10-01 00:58:33 +02:00
Tobie Langel
d55932fc8c function.js rewrite. 2008-09-30 10:12:54 +02:00
Tobie Langel
ec5069ed3a Refactor object.js. 2008-09-30 01:04:25 +02:00
Tobie Langel
fee81bffee Fix typo in file name. 2008-09-30 00:52:45 +02:00
Tobie Langel
15d4d5fe7a Fixed conflicting merge. 2008-09-30 00:48:16 +02:00
Tobie Langel
5607889e41 Fix a bug introduced in my previous commit on protodoc.rb 2008-09-30 00:05:23 +02:00
Tobie Langel
9af3c039e1 Split ajax.js 2008-09-30 00:05:23 +02:00
Tobie Langel
8d3911ec09 Split src code into lang, dom and ajax directories. 2008-09-30 00:05:23 +02:00
Tobie Langel
e3f3e95b84 Split base.js up. 2008-09-30 00:05:22 +02:00
Tobie Langel
373ef2d839 Extract the template class out of string.js and into its own file . 2008-09-30 00:05:22 +02:00
Tobie Langel
dc70b13aed Reorganize range.js. 2008-09-30 00:05:22 +02:00
Tobie Langel
f64c9568c8 Reorganize number.js. 2008-09-30 00:05:22 +02:00
Tobie Langel
1669e41cda Reorganize hash.js. 2008-09-30 00:05:22 +02:00
Tobie Langel
bcbdf3b247 Reorganize enumerable.js. 2008-09-30 00:05:22 +02:00
Tobie Langel
22f9cfc626 More clean-up to array.js. 2008-09-30 00:05:22 +02:00
Tobie Langel
e63f50aa60 Reorganizing array.js source code. 2008-09-30 00:05:22 +02:00
Tobie Langel
a94c420a34 Reorganize hash.js. 2008-09-28 19:41:53 +02:00
Tobie Langel
b7e817927e Reorganize enumerable.js. 2008-09-28 19:25:03 +02:00
Tobie Langel
f5d8a093f2 More clean-up to array.js. 2008-09-28 19:14:17 +02:00
Tobie Langel
f46a3c2430 Reorganizing array.js source code. 2008-09-28 18:57:01 +02:00
45 changed files with 2331 additions and 1441 deletions

4
.gitignore vendored
View File

@ -1 +1,3 @@
.DS_Store
.DS_Store
pkg
test/unit/tmp/*

View File

@ -1,3 +1,9 @@
* Fix issue where certain versions of Safari treat class names case-insensitively in Selector/$$ queries. (Andrew Dupont, kangax, Brice)
* Fix issue where Function#argumentNames returned incorrect results in IE when comments were intermixed with argument names. (Christophe Porteneuve, T.J. Crowder)
* Performance improvements in Function methods (Samuel Lebeau, kangax, jddalton, Tobie Langel).
*1.6.0.3* (September 29, 2008)
* Add support for the Chrome browser in jstest.rb. (Andrew Dupont)

3
dist/.gitignore vendored
View File

@ -1 +1,2 @@
prototype.js
prototype.js
prototype_update_helper.js

View File

@ -21,6 +21,7 @@ module Protodoc
include Environment
def initialize(filename)
filename = File.join(filename.split('/'))
@filename = File.expand_path(filename)
@template = ERB.new(IO.read(@filename), nil, '%')
end

View File

@ -1,391 +0,0 @@
var Ajax = {
getTransport: function() {
return Try.these(
function() {return new XMLHttpRequest()},
function() {return new ActiveXObject('Msxml2.XMLHTTP')},
function() {return new ActiveXObject('Microsoft.XMLHTTP')}
) || false;
},
activeRequestCount: 0
};
Ajax.Responders = {
responders: [],
_each: function(iterator) {
this.responders._each(iterator);
},
register: function(responder) {
if (!this.include(responder))
this.responders.push(responder);
},
unregister: function(responder) {
this.responders = this.responders.without(responder);
},
dispatch: function(callback, request, transport, json) {
this.each(function(responder) {
if (Object.isFunction(responder[callback])) {
try {
responder[callback].apply(responder, [request, transport, json]);
} catch (e) { }
}
});
}
};
Object.extend(Ajax.Responders, Enumerable);
Ajax.Responders.register({
onCreate: function() { Ajax.activeRequestCount++ },
onComplete: function() { Ajax.activeRequestCount-- }
});
Ajax.Base = Class.create({
initialize: function(options) {
this.options = {
method: 'post',
asynchronous: true,
contentType: 'application/x-www-form-urlencoded',
encoding: 'UTF-8',
parameters: '',
evalJSON: true,
evalJS: true
};
Object.extend(this.options, options || { });
this.options.method = this.options.method.toLowerCase();
if (Object.isString(this.options.parameters))
this.options.parameters = this.options.parameters.toQueryParams();
else if (Object.isHash(this.options.parameters))
this.options.parameters = this.options.parameters.toObject();
}
});
Ajax.Request = Class.create(Ajax.Base, {
_complete: false,
initialize: function($super, url, options) {
$super(options);
this.transport = Ajax.getTransport();
this.request(url);
},
request: function(url) {
this.url = url;
this.method = this.options.method;
var params = Object.clone(this.options.parameters);
if (!['get', 'post'].include(this.method)) {
// simulate other verbs over post
params['_method'] = this.method;
this.method = 'post';
}
this.parameters = params;
if (params = Object.toQueryString(params)) {
// when GET, append parameters to URL
if (this.method == 'get')
this.url += (this.url.include('?') ? '&' : '?') + params;
else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
params += '&_=';
}
try {
var response = new Ajax.Response(this);
if (this.options.onCreate) this.options.onCreate(response);
Ajax.Responders.dispatch('onCreate', this, response);
this.transport.open(this.method.toUpperCase(), this.url,
this.options.asynchronous);
if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
this.transport.onreadystatechange = this.onStateChange.bind(this);
this.setRequestHeaders();
this.body = this.method == 'post' ? (this.options.postBody || params) : null;
this.transport.send(this.body);
/* Force Firefox to handle ready state 4 for synchronous requests */
if (!this.options.asynchronous && this.transport.overrideMimeType)
this.onStateChange();
}
catch (e) {
this.dispatchException(e);
}
},
onStateChange: function() {
var readyState = this.transport.readyState;
if (readyState > 1 && !((readyState == 4) && this._complete))
this.respondToReadyState(this.transport.readyState);
},
setRequestHeaders: function() {
var headers = {
'X-Requested-With': 'XMLHttpRequest',
'X-Prototype-Version': Prototype.Version,
'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
};
if (this.method == 'post') {
headers['Content-type'] = this.options.contentType +
(this.options.encoding ? '; charset=' + this.options.encoding : '');
/* Force "Connection: close" for older Mozilla browsers to work
* around a bug where XMLHttpRequest sends an incorrect
* Content-length header. See Mozilla Bugzilla #246651.
*/
if (this.transport.overrideMimeType &&
(navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
headers['Connection'] = 'close';
}
// user-defined headers
if (typeof this.options.requestHeaders == 'object') {
var extras = this.options.requestHeaders;
if (Object.isFunction(extras.push))
for (var i = 0, length = extras.length; i < length; i += 2)
headers[extras[i]] = extras[i+1];
else
$H(extras).each(function(pair) { headers[pair.key] = pair.value });
}
for (var name in headers)
this.transport.setRequestHeader(name, headers[name]);
},
success: function() {
var status = this.getStatus();
return !status || (status >= 200 && status < 300);
},
getStatus: function() {
try {
return this.transport.status || 0;
} catch (e) { return 0 }
},
respondToReadyState: function(readyState) {
var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
if (state == 'Complete') {
try {
this._complete = true;
(this.options['on' + response.status]
|| this.options['on' + (this.success() ? 'Success' : 'Failure')]
|| Prototype.emptyFunction)(response, response.headerJSON);
} catch (e) {
this.dispatchException(e);
}
var contentType = response.getHeader('Content-type');
if (this.options.evalJS == 'force'
|| (this.options.evalJS && this.isSameOrigin() && contentType
&& contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
this.evalResponse();
}
try {
(this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
} catch (e) {
this.dispatchException(e);
}
if (state == 'Complete') {
// avoid memory leak in MSIE: clean up
this.transport.onreadystatechange = Prototype.emptyFunction;
}
},
isSameOrigin: function() {
var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
protocol: location.protocol,
domain: document.domain,
port: location.port ? ':' + location.port : ''
}));
},
getHeader: function(name) {
try {
return this.transport.getResponseHeader(name) || null;
} catch (e) { return null }
},
evalResponse: function() {
try {
return eval((this.transport.responseText || '').unfilterJSON());
} catch (e) {
this.dispatchException(e);
}
},
dispatchException: function(exception) {
(this.options.onException || Prototype.emptyFunction)(this, exception);
Ajax.Responders.dispatch('onException', this, exception);
}
});
Ajax.Request.Events =
['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
Ajax.Response = Class.create({
initialize: function(request){
this.request = request;
var transport = this.transport = request.transport,
readyState = this.readyState = transport.readyState;
if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
this.status = this.getStatus();
this.statusText = this.getStatusText();
this.responseText = String.interpret(transport.responseText);
this.headerJSON = this._getHeaderJSON();
}
if(readyState == 4) {
var xml = transport.responseXML;
this.responseXML = Object.isUndefined(xml) ? null : xml;
this.responseJSON = this._getResponseJSON();
}
},
status: 0,
statusText: '',
getStatus: Ajax.Request.prototype.getStatus,
getStatusText: function() {
try {
return this.transport.statusText || '';
} catch (e) { return '' }
},
getHeader: Ajax.Request.prototype.getHeader,
getAllHeaders: function() {
try {
return this.getAllResponseHeaders();
} catch (e) { return null }
},
getResponseHeader: function(name) {
return this.transport.getResponseHeader(name);
},
getAllResponseHeaders: function() {
return this.transport.getAllResponseHeaders();
},
_getHeaderJSON: function() {
var json = this.getHeader('X-JSON');
if (!json) return null;
json = decodeURIComponent(escape(json));
try {
return json.evalJSON(this.request.options.sanitizeJSON ||
!this.request.isSameOrigin());
} catch (e) {
this.request.dispatchException(e);
}
},
_getResponseJSON: function() {
var options = this.request.options;
if (!options.evalJSON || (options.evalJSON != 'force' &&
!(this.getHeader('Content-type') || '').include('application/json')) ||
this.responseText.blank())
return null;
try {
return this.responseText.evalJSON(options.sanitizeJSON ||
!this.request.isSameOrigin());
} catch (e) {
this.request.dispatchException(e);
}
}
});
Ajax.Updater = Class.create(Ajax.Request, {
initialize: function($super, container, url, options) {
this.container = {
success: (container.success || container),
failure: (container.failure || (container.success ? null : container))
};
options = Object.clone(options);
var onComplete = options.onComplete;
options.onComplete = (function(response, json) {
this.updateContent(response.responseText);
if (Object.isFunction(onComplete)) onComplete(response, json);
}).bind(this);
$super(url, options);
},
updateContent: function(responseText) {
var receiver = this.container[this.success() ? 'success' : 'failure'],
options = this.options;
if (!options.evalScripts) responseText = responseText.stripScripts();
if (receiver = $(receiver)) {
if (options.insertion) {
if (Object.isString(options.insertion)) {
var insertion = { }; insertion[options.insertion] = responseText;
receiver.insert(insertion);
}
else options.insertion(receiver, responseText);
}
else receiver.update(responseText);
}
}
});
Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
initialize: function($super, container, url, options) {
$super(options);
this.onComplete = this.options.onComplete;
this.frequency = (this.options.frequency || 2);
this.decay = (this.options.decay || 1);
this.updater = { };
this.container = container;
this.url = url;
this.start();
},
start: function() {
this.options.onComplete = this.updateComplete.bind(this);
this.onTimerEvent();
},
stop: function() {
this.updater.options.onComplete = undefined;
clearTimeout(this.timer);
(this.onComplete || Prototype.emptyFunction).apply(this, arguments);
},
updateComplete: function(response) {
if (this.options.decay) {
this.decay = (response.responseText == this.lastText ?
this.decay * this.options.decay : 1);
this.lastText = response.responseText;
}
this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
},
onTimerEvent: function() {
this.updater = new Ajax.Updater(this.container, this.url, this.options);
}
});

11
src/ajax/ajax.js Normal file
View File

@ -0,0 +1,11 @@
var Ajax = {
getTransport: function() {
return Try.these(
function() {return new XMLHttpRequest()},
function() {return new ActiveXObject('Msxml2.XMLHTTP')},
function() {return new ActiveXObject('Microsoft.XMLHTTP')}
) || false;
},
activeRequestCount: 0
};

21
src/ajax/base.js Normal file
View File

@ -0,0 +1,21 @@
Ajax.Base = Class.create({
initialize: function(options) {
this.options = {
method: 'post',
asynchronous: true,
contentType: 'application/x-www-form-urlencoded',
encoding: 'UTF-8',
parameters: '',
evalJSON: true,
evalJS: true
};
Object.extend(this.options, options || { });
this.options.method = this.options.method.toLowerCase();
if (Object.isString(this.options.parameters))
this.options.parameters = this.options.parameters.toQueryParams();
else if (Object.isHash(this.options.parameters))
this.options.parameters = this.options.parameters.toObject();
}
});

View File

@ -0,0 +1,40 @@
Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
initialize: function($super, container, url, options) {
$super(options);
this.onComplete = this.options.onComplete;
this.frequency = (this.options.frequency || 2);
this.decay = (this.options.decay || 1);
this.updater = { };
this.container = container;
this.url = url;
this.start();
},
start: function() {
this.options.onComplete = this.updateComplete.bind(this);
this.onTimerEvent();
},
stop: function() {
this.updater.options.onComplete = undefined;
clearTimeout(this.timer);
(this.onComplete || Prototype.emptyFunction).apply(this, arguments);
},
updateComplete: function(response) {
if (this.options.decay) {
this.decay = (response.responseText == this.lastText ?
this.decay * this.options.decay : 1);
this.lastText = response.responseText;
}
this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
},
onTimerEvent: function() {
this.updater = new Ajax.Updater(this.container, this.url, this.options);
}
});

172
src/ajax/request.js Normal file
View File

@ -0,0 +1,172 @@
Ajax.Request = Class.create(Ajax.Base, {
_complete: false,
initialize: function($super, url, options) {
$super(options);
this.transport = Ajax.getTransport();
this.request(url);
},
request: function(url) {
this.url = url;
this.method = this.options.method;
var params = Object.clone(this.options.parameters);
if (!['get', 'post'].include(this.method)) {
// simulate other verbs over post
params['_method'] = this.method;
this.method = 'post';
}
this.parameters = params;
if (params = Object.toQueryString(params)) {
// when GET, append parameters to URL
if (this.method == 'get')
this.url += (this.url.include('?') ? '&' : '?') + params;
else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
params += '&_=';
}
try {
var response = new Ajax.Response(this);
if (this.options.onCreate) this.options.onCreate(response);
Ajax.Responders.dispatch('onCreate', this, response);
this.transport.open(this.method.toUpperCase(), this.url,
this.options.asynchronous);
if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
this.transport.onreadystatechange = this.onStateChange.bind(this);
this.setRequestHeaders();
this.body = this.method == 'post' ? (this.options.postBody || params) : null;
this.transport.send(this.body);
/* Force Firefox to handle ready state 4 for synchronous requests */
if (!this.options.asynchronous && this.transport.overrideMimeType)
this.onStateChange();
}
catch (e) {
this.dispatchException(e);
}
},
onStateChange: function() {
var readyState = this.transport.readyState;
if (readyState > 1 && !((readyState == 4) && this._complete))
this.respondToReadyState(this.transport.readyState);
},
setRequestHeaders: function() {
var headers = {
'X-Requested-With': 'XMLHttpRequest',
'X-Prototype-Version': Prototype.Version,
'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
};
if (this.method == 'post') {
headers['Content-type'] = this.options.contentType +
(this.options.encoding ? '; charset=' + this.options.encoding : '');
/* Force "Connection: close" for older Mozilla browsers to work
* around a bug where XMLHttpRequest sends an incorrect
* Content-length header. See Mozilla Bugzilla #246651.
*/
if (this.transport.overrideMimeType &&
(navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
headers['Connection'] = 'close';
}
// user-defined headers
if (typeof this.options.requestHeaders == 'object') {
var extras = this.options.requestHeaders;
if (Object.isFunction(extras.push))
for (var i = 0, length = extras.length; i < length; i += 2)
headers[extras[i]] = extras[i+1];
else
$H(extras).each(function(pair) { headers[pair.key] = pair.value });
}
for (var name in headers)
this.transport.setRequestHeader(name, headers[name]);
},
success: function() {
var status = this.getStatus();
return !status || (status >= 200 && status < 300);
},
getStatus: function() {
try {
return this.transport.status || 0;
} catch (e) { return 0 }
},
respondToReadyState: function(readyState) {
var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
if (state == 'Complete') {
try {
this._complete = true;
(this.options['on' + response.status]
|| this.options['on' + (this.success() ? 'Success' : 'Failure')]
|| Prototype.emptyFunction)(response, response.headerJSON);
} catch (e) {
this.dispatchException(e);
}
var contentType = response.getHeader('Content-type');
if (this.options.evalJS == 'force'
|| (this.options.evalJS && this.isSameOrigin() && contentType
&& contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
this.evalResponse();
}
try {
(this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
} catch (e) {
this.dispatchException(e);
}
if (state == 'Complete') {
// avoid memory leak in MSIE: clean up
this.transport.onreadystatechange = Prototype.emptyFunction;
}
},
isSameOrigin: function() {
var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
protocol: location.protocol,
domain: document.domain,
port: location.port ? ':' + location.port : ''
}));
},
getHeader: function(name) {
try {
return this.transport.getResponseHeader(name) || null;
} catch (e) { return null }
},
evalResponse: function() {
try {
return eval((this.transport.responseText || '').unfilterJSON());
} catch (e) {
this.dispatchException(e);
}
},
dispatchException: function(exception) {
(this.options.onException || Prototype.emptyFunction)(this, exception);
Ajax.Responders.dispatch('onException', this, exception);
}
});
Ajax.Request.Events =
['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];

33
src/ajax/responders.js Normal file
View File

@ -0,0 +1,33 @@
Ajax.Responders = {
responders: [],
_each: function(iterator) {
this.responders._each(iterator);
},
register: function(responder) {
if (!this.include(responder))
this.responders.push(responder);
},
unregister: function(responder) {
this.responders = this.responders.without(responder);
},
dispatch: function(callback, request, transport, json) {
this.each(function(responder) {
if (Object.isFunction(responder[callback])) {
try {
responder[callback].apply(responder, [request, transport, json]);
} catch (e) { }
}
});
}
};
Object.extend(Ajax.Responders, Enumerable);
Ajax.Responders.register({
onCreate: function() { Ajax.activeRequestCount++ },
onComplete: function() { Ajax.activeRequestCount-- }
});

73
src/ajax/response.js Normal file
View File

@ -0,0 +1,73 @@
Ajax.Response = Class.create({
initialize: function(request){
this.request = request;
var transport = this.transport = request.transport,
readyState = this.readyState = transport.readyState;
if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
this.status = this.getStatus();
this.statusText = this.getStatusText();
this.responseText = String.interpret(transport.responseText);
this.headerJSON = this._getHeaderJSON();
}
if(readyState == 4) {
var xml = transport.responseXML;
this.responseXML = Object.isUndefined(xml) ? null : xml;
this.responseJSON = this._getResponseJSON();
}
},
status: 0,
statusText: '',
getStatus: Ajax.Request.prototype.getStatus,
getStatusText: function() {
try {
return this.transport.statusText || '';
} catch (e) { return '' }
},
getHeader: Ajax.Request.prototype.getHeader,
getAllHeaders: function() {
try {
return this.getAllResponseHeaders();
} catch (e) { return null }
},
getResponseHeader: function(name) {
return this.transport.getResponseHeader(name);
},
getAllResponseHeaders: function() {
return this.transport.getAllResponseHeaders();
},
_getHeaderJSON: function() {
var json = this.getHeader('X-JSON');
if (!json) return null;
json = decodeURIComponent(escape(json));
try {
return json.evalJSON(this.request.options.sanitizeJSON ||
!this.request.isSameOrigin());
} catch (e) {
this.request.dispatchException(e);
}
},
_getResponseJSON: function() {
var options = this.request.options;
if (!options.evalJSON || (options.evalJSON != 'force' &&
!(this.getHeader('Content-type') || '').include('application/json')) ||
this.responseText.blank())
return null;
try {
return this.responseText.evalJSON(options.sanitizeJSON ||
!this.request.isSameOrigin());
} catch (e) {
this.request.dispatchException(e);
}
}
});

35
src/ajax/updater.js Normal file
View File

@ -0,0 +1,35 @@
Ajax.Updater = Class.create(Ajax.Request, {
initialize: function($super, container, url, options) {
this.container = {
success: (container.success || container),
failure: (container.failure || (container.success ? null : container))
};
options = Object.clone(options);
var onComplete = options.onComplete;
options.onComplete = (function(response, json) {
this.updateContent(response.responseText);
if (Object.isFunction(onComplete)) onComplete(response, json);
}).bind(this);
$super(url, options);
},
updateContent: function(responseText) {
var receiver = this.container[this.success() ? 'success' : 'failure'],
options = this.options;
if (!options.evalScripts) responseText = responseText.stripScripts();
if (receiver = $(receiver)) {
if (options.insertion) {
if (Object.isString(options.insertion)) {
var insertion = { }; insertion[options.insertion] = responseText;
receiver.insert(insertion);
}
else options.insertion(receiver, responseText);
}
else receiver.update(responseText);
}
}
});

View File

@ -1,285 +0,0 @@
/* Based on Alex Arnell's inheritance implementation. */
var Class = {
create: function() {
var parent = null, properties = $A(arguments);
if (Object.isFunction(properties[0]))
parent = properties.shift();
function klass() {
this.initialize.apply(this, arguments);
}
Object.extend(klass, Class.Methods);
klass.superclass = parent;
klass.subclasses = [];
if (parent) {
var subclass = function() { };
subclass.prototype = parent.prototype;
klass.prototype = new subclass;
parent.subclasses.push(klass);
}
for (var i = 0; i < properties.length; i++)
klass.addMethods(properties[i]);
if (!klass.prototype.initialize)
klass.prototype.initialize = Prototype.emptyFunction;
klass.prototype.constructor = klass;
return klass;
}
};
Class.Methods = {
addMethods: function(source) {
var ancestor = this.superclass && this.superclass.prototype;
var properties = Object.keys(source);
if (!Object.keys({ toString: true }).length)
properties.push("toString", "valueOf");
for (var i = 0, length = properties.length; i < length; i++) {
var property = properties[i], value = source[property];
if (ancestor && Object.isFunction(value) &&
value.argumentNames().first() == "$super") {
var method = value;
value = (function(m) {
return function() { return ancestor[m].apply(this, arguments) };
})(property).wrap(method);
value.valueOf = method.valueOf.bind(method);
value.toString = method.toString.bind(method);
}
this.prototype[property] = value;
}
return this;
}
};
var Abstract = { };
Object.extend = function(destination, source) {
for (var property in source)
destination[property] = source[property];
return destination;
};
Object.extend(Object, {
inspect: function(object) {
try {
if (Object.isUndefined(object)) return 'undefined';
if (object === null) return 'null';
return object.inspect ? object.inspect() : String(object);
} catch (e) {
if (e instanceof RangeError) return '...';
throw e;
}
},
toJSON: function(object) {
var type = typeof object;
switch (type) {
case 'undefined':
case 'function':
case 'unknown': return;
case 'boolean': return object.toString();
}
if (object === null) return 'null';
if (object.toJSON) return object.toJSON();
if (Object.isElement(object)) return;
var results = [];
for (var property in object) {
var value = Object.toJSON(object[property]);
if (!Object.isUndefined(value))
results.push(property.toJSON() + ': ' + value);
}
return '{' + results.join(', ') + '}';
},
toQueryString: function(object) {
return $H(object).toQueryString();
},
toHTML: function(object) {
return object && object.toHTML ? object.toHTML() : String.interpret(object);
},
keys: function(object) {
var keys = [];
for (var property in object)
keys.push(property);
return keys;
},
values: function(object) {
var values = [];
for (var property in object)
values.push(object[property]);
return values;
},
clone: function(object) {
return Object.extend({ }, object);
},
isElement: function(object) {
return !!(object && object.nodeType == 1);
},
isArray: function(object) {
return object != null && typeof object == "object" &&
'splice' in object && 'join' in object;
},
isHash: function(object) {
return object instanceof Hash;
},
isFunction: function(object) {
return typeof object == "function";
},
isString: function(object) {
return typeof object == "string";
},
isNumber: function(object) {
return typeof object == "number";
},
isUndefined: function(object) {
return typeof object == "undefined";
}
});
Object.extend(Function.prototype, {
argumentNames: function() {
var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
.replace(/\s+/g, '').split(',');
return names.length == 1 && !names[0] ? [] : names;
},
bind: function() {
if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
var __method = this, args = $A(arguments), object = args.shift();
return function() {
return __method.apply(object, args.concat($A(arguments)));
}
},
bindAsEventListener: function() {
var __method = this, args = $A(arguments), object = args.shift();
return function(event) {
return __method.apply(object, [event || window.event].concat(args));
}
},
curry: function() {
if (!arguments.length) return this;
var __method = this, args = $A(arguments);
return function() {
return __method.apply(this, args.concat($A(arguments)));
}
},
delay: function() {
var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
return window.setTimeout(function() {
return __method.apply(__method, args);
}, timeout);
},
defer: function() {
var args = [0.01].concat($A(arguments));
return this.delay.apply(this, args);
},
wrap: function(wrapper) {
var __method = this;
return function() {
return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
}
},
methodize: function() {
if (this._methodized) return this._methodized;
var __method = this;
return this._methodized = function() {
return __method.apply(null, [this].concat($A(arguments)));
};
}
});
Date.prototype.toJSON = function() {
return '"' + this.getUTCFullYear() + '-' +
(this.getUTCMonth() + 1).toPaddedString(2) + '-' +
this.getUTCDate().toPaddedString(2) + 'T' +
this.getUTCHours().toPaddedString(2) + ':' +
this.getUTCMinutes().toPaddedString(2) + ':' +
this.getUTCSeconds().toPaddedString(2) + 'Z"';
};
var Try = {
these: function() {
var returnValue;
for (var i = 0, length = arguments.length; i < length; i++) {
var lambda = arguments[i];
try {
returnValue = lambda();
break;
} catch (e) { }
}
return returnValue;
}
};
RegExp.prototype.match = RegExp.prototype.test;
RegExp.escape = function(str) {
return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};
/*--------------------------------------------------------------------------*/
var PeriodicalExecuter = Class.create({
initialize: function(callback, frequency) {
this.callback = callback;
this.frequency = frequency;
this.currentlyExecuting = false;
this.registerCallback();
},
registerCallback: function() {
this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
},
execute: function() {
this.callback(this);
},
stop: function() {
if (!this.timer) return;
clearInterval(this.timer);
this.timer = null;
},
onTimerEvent: function() {
if (!this.currentlyExecuting) {
try {
this.currentlyExecuting = true;
this.execute();
} finally {
this.currentlyExecuting = false;
}
}
}
});

378
src/dom/event.js Normal file
View File

@ -0,0 +1,378 @@
(function() {
var Event = {
KEY_BACKSPACE: 8,
KEY_TAB: 9,
KEY_RETURN: 13,
KEY_ESC: 27,
KEY_LEFT: 37,
KEY_UP: 38,
KEY_RIGHT: 39,
KEY_DOWN: 40,
KEY_DELETE: 46,
KEY_HOME: 36,
KEY_END: 35,
KEY_PAGEUP: 33,
KEY_PAGEDOWN: 34,
KEY_INSERT: 45,
cache: {}
};
var _isButton;
if (Prototype.Browser.IE) {
// IE doesn't map left/right/middle the same way.
var buttonMap = { 0: 1, 1: 4, 2: 2 };
_isButton = function(event, code) {
return event.button === buttonMap[code];
};
} else if (Prototype.Browser.WebKit) {
// In Safari we have to account for when the user holds down
// the "meta" key.
_isButton = function(event, code) {
switch (code) {
case 0: return event.which == 1 && !event.metaKey;
case 1: return event.which == 1 && event.metaKey;
default: return false;
}
};
} else {
_isButton = function(event, code) {
return event.which ? (event.which === code + 1) : (event.button === code);
};
}
function isLeftClick(event) { return _isButton(event, 0) }
function isMiddleClick(event) { return _isButton(event, 1) }
function isRightClick(event) { return _isButton(event, 2) }
function element(event) {
event = Event.extend(event);
var node = event.target, type = event.type,
currentTarget = event.currentTarget;
if (currentTarget && currentTarget.tagName) {
// Firefox screws up the "click" event when moving between radio buttons
// via arrow keys. It also screws up the "load" and "error" events on images,
// reporting the document as the target instead of the original image.
if (type === 'load' || type === 'error' ||
(type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
&& currentTarget.type === 'radio'))
node = currentTarget;
}
// Fix a Safari bug where a text node gets passed as the target of an
// anchor click rather than the anchor itself.
if (node.nodeType == Node.TEXT_NODE)
node = node.parentNode;
return Element.extend(node);
}
function findElement(event, expression) {
var element = Event.element(event);
if (!expression) return element;
var elements = [element].concat(element.ancestors());
return Selector.findElement(elements, expression, 0);
}
function pointer(event) {
var docElement = document.documentElement,
body = document.body || { scrollLeft: 0, scrollTop: 0 };
return {
x: event.pageX || (event.clientX +
(docElement.scrollLeft || body.scrollLeft) -
(docElement.clientLeft || 0)),
y: event.pageY || (event.clientY +
(docElement.scrollTop || body.scrollTop) -
(docElement.clientTop || 0))
};
}
function pointerX(event) { return Event.pointer(event).x }
function pointerY(event) { return Event.pointer(event).y }
function stop(event) {
Event.extend(event);
event.preventDefault();
event.stopPropagation();
// Set a "stopped" property so that a custom event can be inspected
// after the fact to determine whether or not it was stopped.
event.stopped = true;
}
Event.Methods = {
isLeftClick: isLeftClick,
isMiddleClick: isMiddleClick,
isRightClick: isRightClick,
element: element,
findElement: findElement,
pointer: pointer,
pointerX: pointerX,
pointerY: pointerY,
stop: stop
};
// Compile the list of methods that get extended onto Events.
var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
m[name] = Event.Methods[name].methodize();
return m;
});
if (Prototype.Browser.IE) {
function _relatedTarget(event) {
var element;
switch (event.type) {
case 'mouseover': element = event.fromElement; break;
case 'mouseout': element = event.toElement; break;
default: return null;
}
return Element.extend(element);
}
Object.extend(methods, {
stopPropagation: function() { this.cancelBubble = true },
preventDefault: function() { this.returnValue = false },
inspect: function() { return '[object Event]' }
});
// IE's method for extending events.
Event.extend = function(event) {
if (!event) return false;
if (event._extendedByPrototype) return event;
event._extendedByPrototype = Prototype.emptyFunction;
var pointer = Event.pointer(event);
Object.extend(event, {
target: event.srcElement,
relatedTarget: _relatedTarget(event),
pageX: pointer.x,
pageY: pointer.y
});
return Object.extend(event, methods);
};
} else {
Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
Object.extend(Event.prototype, methods);
Event.extend = Prototype.K;
}
function _getEventID(element) {
if (element._prototypeEventID) return element._prototypeEventID[0];
return element._prototypeEventID = [++arguments.callee.id];
}
_getEventID.id = 1;
function _getDOMEventName(eventName) {
if (eventName && eventName.include(':')) return 'dataavailable';
return eventName;
}
function _getCacheForID(id) {
return Event.cache[id] = Event.cache[id] || { };
}
function _getRespondersForEvent(id, eventName) {
var c = _getCacheForID(id);
return c[eventName] = c[eventName] || [];
}
function _createResponder(element, eventName, handler) {
var id = _getEventID(element), r = _getRespondersForEvent(id, eventName);
// Work around the issue that permits a handler to be attached more than
// once to the same element & event type.
if (r.pluck('handler').include(handler)) return false;
var responder = function(event) {
if (!Event || !Event.extend ||
// If it's a custom event, but not the _correct_ custom event, ignore it.
(!Object.isUndefined(event.eventName) && event.eventName !== eventName))
return false;
Event.extend(event);
handler.call(element, event);
};
responder.handler = handler;
r.push(responder);
return responder;
}
function _findResponder(id, eventName, handler) {
var r = _getRespondersForEvent(id, eventName);
return r.find(function(responder) {
return responder.handler === handler;
});
}
function _destroyResponder(id, eventName, handler) {
var c = _getCacheForID(id);
if (Object.isUndefined(c[eventName])) return false;
c[eventName] = c[eventName].without(_findResponder(id, eventName, handler));
}
function _destroyCache() {
for (var id in Event.cache) {
for (var eventName in Event.cache[id])
Event.cache[id][eventName] = null;
}
}
// Internet Explorer needs to remove event handlers on page unload
// in order to avoid memory leaks.
if (Prototype.Browser.IE)
window.attachEvent('onunload', _destroyCache);
// Safari needs a dummy event handler on page unload so that it won't
// use its bfcache. Safari <= 3.1 has an issue with restoring the "document"
// object when page is returned to via the back button using its bfcache.
if (Prototype.Browser.WebKit)
window.addEventListener('unload', Prototype.emptyFunction, false);
function observe(element, eventName, handler) {
element = $(element);
var name = _getDOMEventName(eventName),
responder = _createResponder(element, eventName, handler);
if (!responder) return element;
if (element.addEventListener)
element.addEventListener(name, responder, false);
else
element.attachEvent("on" + name, responder);
return element;
}
function stopObserving(element, eventName, handler) {
element = $(element);
var id = _getEventID(element), name = _getDOMEventName(eventName);
if (eventName && !handler) {
// If an event name is passed without a handler, we stop observing all
// handlers of that type.
_getRespondersForEvent(id, eventName).each(function(r) {
element.stopObserving(eventName, r.handler);
});
return element;
} else if (!eventName) {
// If both the event name and the handler are omitted, we stop observing
// _all_ handlers on the element.
Object.keys(_getCacheForID(id)).each(function(eventName) {
element.stopObserving(eventName);
});
return element;
}
var responder = _findResponder(id, eventName, handler);
if (!responder) return element;
if (element.removeEventListener)
element.removeEventListener(name, responder, false);
else
element.detachEvent('on' + name, responder);
_destroyResponder(id, eventName, handler);
return element;
}
function fire(element, eventName, memo) {
element = $(element);
if (element == document && document.createEvent && !element.dispatchEvent)
element = document.documentElement;
var event;
if (document.createEvent) {
event = document.createEvent('HTMLEvents');
event.initEvent('dataavailable', true, true);
} else {
event = document.createEventObject();
event.eventType = 'ondataavailable';
}
event.eventName = eventName;
event.memo = memo || { };
if (document.createEvent)
element.dispatchEvent(event);
else
element.fireEvent(event.eventType, event);
return Event.extend(event);
}
Object.extend(Event, Event.Methods);
Object.extend(Event, {
fire: fire,
observe: observe,
stopObserving: stopObserving
});
Element.addMethods({
fire: fire,
observe: observe,
stopObserving: stopObserving
});
Object.extend(document, {
fire: fire.methodize(),
observe: observe.methodize(),
stopObserving: stopObserving.methodize(),
loaded: false
});
// Export to the global scope.
if (window.Event) Object.extend(window.Event, Event)
else window.Event = Event;
})();
(function() {
/* Support for the DOMContentLoaded event is based on work by Dan Webb,
Matthias Miller, Dean Edwards and John Resig. */
var _timer;
function _fireContentLoadedEvent() {
if (document.loaded) return;
if (_timer) window.clearInterval(_timer);
document.fire("dom:loaded");
document.loaded = true;
}
function _webkitContentLoadedCheck() {
var s = document.readyState;
if (s === "loaded" || s === "complete")
_fireContentLoadedEvent();
}
function _IEContentLoadedCheck() {
if (this.readyState == "complete") {
this.onreadystatechange = null;
_fireContentLoadedEvent();
}
}
if (document.addEventListener) {
if (Prototype.Browser.WebKit) {
_timer = window.setInterval(_webkitContentLoadedCheck, 0);
Event.observe(window, "load", _fireContentLoadedEvent);
} else {
document.addEventListener("DOMContentLoaded",
_fireContentLoadedEvent, false);
}
} else {
document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
$("__onDOMContentLoaded").onreadystatechange = _IEContentLoadedCheck;
}
})();

View File

@ -38,6 +38,8 @@ var Selector = Class.create({
shouldUseSelectorsAPI: function() {
if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false;
if (!Selector._div) Selector._div = new Element('div');
@ -177,6 +179,25 @@ var Selector = Class.create({
}
});
if (Prototype.BrowserFeatures.SelectorsAPI &&
document.compatMode === 'BackCompat') {
// Versions of Safari 3 before 3.1.2 treat class names case-insensitively in
// quirks mode. If we detect this behavior, we should use a different
// approach.
Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){
var div = document.createElement('div'),
span = document.createElement('span');
div.id = "prototype_test_id";
span.className = 'Test';
div.appendChild(span);
var isIgnored = (div.querySelector('#prototype_test_id .test') !== null);
div = span = null;
alert(isIgnored);
return isIgnored;
})();
}
Object.extend(Selector, {
_cache: { },

View File

@ -1,348 +0,0 @@
if (!window.Event) var Event = { };
Object.extend(Event, {
KEY_BACKSPACE: 8,
KEY_TAB: 9,
KEY_RETURN: 13,
KEY_ESC: 27,
KEY_LEFT: 37,
KEY_UP: 38,
KEY_RIGHT: 39,
KEY_DOWN: 40,
KEY_DELETE: 46,
KEY_HOME: 36,
KEY_END: 35,
KEY_PAGEUP: 33,
KEY_PAGEDOWN: 34,
KEY_INSERT: 45,
cache: { },
relatedTarget: function(event) {
var element;
switch(event.type) {
case 'mouseover': element = event.fromElement; break;
case 'mouseout': element = event.toElement; break;
default: return null;
}
return Element.extend(element);
}
});
Event.Methods = (function() {
var isButton;
if (Prototype.Browser.IE) {
var buttonMap = { 0: 1, 1: 4, 2: 2 };
isButton = function(event, code) {
return event.button == buttonMap[code];
};
} else if (Prototype.Browser.WebKit) {
isButton = function(event, code) {
switch (code) {
case 0: return event.which == 1 && !event.metaKey;
case 1: return event.which == 1 && event.metaKey;
default: return false;
}
};
} else {
isButton = function(event, code) {
return event.which ? (event.which === code + 1) : (event.button === code);
};
}
return {
isLeftClick: function(event) { return isButton(event, 0) },
isMiddleClick: function(event) { return isButton(event, 1) },
isRightClick: function(event) { return isButton(event, 2) },
element: function(event) {
event = Event.extend(event);
var node = event.target,
type = event.type,
currentTarget = event.currentTarget;
if (currentTarget && currentTarget.tagName) {
// Firefox screws up the "click" event when moving between radio buttons
// via arrow keys. It also screws up the "load" and "error" events on images,
// reporting the document as the target instead of the original image.
if (type === 'load' || type === 'error' ||
(type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
&& currentTarget.type === 'radio'))
node = currentTarget;
}
if (node.nodeType == Node.TEXT_NODE) node = node.parentNode;
return Element.extend(node);
},
findElement: function(event, expression) {
var element = Event.element(event);
if (!expression) return element;
var elements = [element].concat(element.ancestors());
return Selector.findElement(elements, expression, 0);
},
pointer: function(event) {
var docElement = document.documentElement,
body = document.body || { scrollLeft: 0, scrollTop: 0 };
return {
x: event.pageX || (event.clientX +
(docElement.scrollLeft || body.scrollLeft) -
(docElement.clientLeft || 0)),
y: event.pageY || (event.clientY +
(docElement.scrollTop || body.scrollTop) -
(docElement.clientTop || 0))
};
},
pointerX: function(event) { return Event.pointer(event).x },
pointerY: function(event) { return Event.pointer(event).y },
stop: function(event) {
Event.extend(event);
event.preventDefault();
event.stopPropagation();
event.stopped = true;
}
};
})();
Event.extend = (function() {
var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
m[name] = Event.Methods[name].methodize();
return m;
});
if (Prototype.Browser.IE) {
Object.extend(methods, {
stopPropagation: function() { this.cancelBubble = true },
preventDefault: function() { this.returnValue = false },
inspect: function() { return "[object Event]" }
});
return function(event) {
if (!event) return false;
if (event._extendedByPrototype) return event;
event._extendedByPrototype = Prototype.emptyFunction;
var pointer = Event.pointer(event);
Object.extend(event, {
target: event.srcElement,
relatedTarget: Event.relatedTarget(event),
pageX: pointer.x,
pageY: pointer.y
});
return Object.extend(event, methods);
};
} else {
Event.prototype = Event.prototype || document.createEvent("HTMLEvents")['__proto__'];
Object.extend(Event.prototype, methods);
return Prototype.K;
}
})();
Object.extend(Event, (function() {
var cache = Event.cache;
function getEventID(element) {
if (element._prototypeEventID) return element._prototypeEventID[0];
arguments.callee.id = arguments.callee.id || 1;
return element._prototypeEventID = [++arguments.callee.id];
}
function getDOMEventName(eventName) {
if (eventName && eventName.include(':')) return "dataavailable";
return eventName;
}
function getCacheForID(id) {
return cache[id] = cache[id] || { };
}
function getWrappersForEventName(id, eventName) {
var c = getCacheForID(id);
return c[eventName] = c[eventName] || [];
}
function createWrapper(element, eventName, handler) {
var id = getEventID(element);
var c = getWrappersForEventName(id, eventName);
if (c.pluck("handler").include(handler)) return false;
var wrapper = function(event) {
if (!Event || !Event.extend ||
(event.eventName && event.eventName != eventName))
return false;
Event.extend(event);
handler.call(element, event);
};
wrapper.handler = handler;
c.push(wrapper);
return wrapper;
}
function findWrapper(id, eventName, handler) {
var c = getWrappersForEventName(id, eventName);
return c.find(function(wrapper) { return wrapper.handler == handler });
}
function destroyWrapper(id, eventName, handler) {
var c = getCacheForID(id);
if (!c[eventName]) return false;
c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
}
function destroyCache() {
for (var id in cache)
for (var eventName in cache[id])
cache[id][eventName] = null;
}
// Internet Explorer needs to remove event handlers on page unload
// in order to avoid memory leaks.
if (window.attachEvent) {
window.attachEvent("onunload", destroyCache);
}
// Safari has a dummy event handler on page unload so that it won't
// use its bfcache. Safari <= 3.1 has an issue with restoring the "document"
// object when page is returned to via the back button using its bfcache.
if (Prototype.Browser.WebKit) {
window.addEventListener('unload', Prototype.emptyFunction, false);
}
return {
observe: function(element, eventName, handler) {
element = $(element);
var name = getDOMEventName(eventName);
var wrapper = createWrapper(element, eventName, handler);
if (!wrapper) return element;
if (element.addEventListener) {
element.addEventListener(name, wrapper, false);
} else {
element.attachEvent("on" + name, wrapper);
}
return element;
},
stopObserving: function(element, eventName, handler) {
element = $(element);
var id = getEventID(element), name = getDOMEventName(eventName);
if (!handler && eventName) {
getWrappersForEventName(id, eventName).each(function(wrapper) {
element.stopObserving(eventName, wrapper.handler);
});
return element;
} else if (!eventName) {
Object.keys(getCacheForID(id)).each(function(eventName) {
element.stopObserving(eventName);
});
return element;
}
var wrapper = findWrapper(id, eventName, handler);
if (!wrapper) return element;
if (element.removeEventListener) {
element.removeEventListener(name, wrapper, false);
} else {
element.detachEvent("on" + name, wrapper);
}
destroyWrapper(id, eventName, handler);
return element;
},
fire: function(element, eventName, memo) {
element = $(element);
if (element == document && document.createEvent && !element.dispatchEvent)
element = document.documentElement;
var event;
if (document.createEvent) {
event = document.createEvent("HTMLEvents");
event.initEvent("dataavailable", true, true);
} else {
event = document.createEventObject();
event.eventType = "ondataavailable";
}
event.eventName = eventName;
event.memo = memo || { };
if (document.createEvent) {
element.dispatchEvent(event);
} else {
element.fireEvent(event.eventType, event);
}
return Event.extend(event);
}
};
})());
Object.extend(Event, Event.Methods);
Element.addMethods({
fire: Event.fire,
observe: Event.observe,
stopObserving: Event.stopObserving
});
Object.extend(document, {
fire: Element.Methods.fire.methodize(),
observe: Element.Methods.observe.methodize(),
stopObserving: Element.Methods.stopObserving.methodize(),
loaded: false
});
(function() {
/* Support for the DOMContentLoaded event is based on work by Dan Webb,
Matthias Miller, Dean Edwards and John Resig. */
var timer;
function fireContentLoadedEvent() {
if (document.loaded) return;
if (timer) window.clearInterval(timer);
document.fire("dom:loaded");
document.loaded = true;
}
if (document.addEventListener) {
if (Prototype.Browser.WebKit) {
timer = window.setInterval(function() {
if (/loaded|complete/.test(document.readyState))
fireContentLoadedEvent();
}, 0);
Event.observe(window, "load", fireContentLoadedEvent);
} else {
document.addEventListener("DOMContentLoaded",
fireContentLoadedEvent, false);
}
} else {
document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
$("__onDOMContentLoaded").onreadystatechange = function() {
if (this.readyState == "complete") {
this.onreadystatechange = null;
fireContentLoadedEvent();
}
};
}
})();

View File

@ -1,101 +0,0 @@
function $H(object) {
return new Hash(object);
};
var Hash = Class.create(Enumerable, (function() {
function toQueryPair(key, value) {
if (Object.isUndefined(value)) return key;
return key + '=' + encodeURIComponent(String.interpret(value));
}
return {
initialize: function(object) {
this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
},
_each: function(iterator) {
for (var key in this._object) {
var value = this._object[key], pair = [key, value];
pair.key = key;
pair.value = value;
iterator(pair);
}
},
set: function(key, value) {
return this._object[key] = value;
},
get: function(key) {
// simulating poorly supported hasOwnProperty
if (this._object[key] !== Object.prototype[key])
return this._object[key];
},
unset: function(key) {
var value = this._object[key];
delete this._object[key];
return value;
},
toObject: function() {
return Object.clone(this._object);
},
keys: function() {
return this.pluck('key');
},
values: function() {
return this.pluck('value');
},
index: function(value) {
var match = this.detect(function(pair) {
return pair.value === value;
});
return match && match.key;
},
merge: function(object) {
return this.clone().update(object);
},
update: function(object) {
return new Hash(object).inject(this, function(result, pair) {
result.set(pair.key, pair.value);
return result;
});
},
toQueryString: function() {
return this.inject([], function(results, pair) {
var key = encodeURIComponent(pair.key), values = pair.value;
if (values && typeof values == 'object') {
if (Object.isArray(values))
return results.concat(values.map(toQueryPair.curry(key)));
} else results.push(toQueryPair(key, values));
return results;
}).join('&');
},
inspect: function() {
return '#<Hash:{' + this.map(function(pair) {
return pair.map(Object.inspect).join(': ');
}).join(', ') + '}>';
},
toJSON: function() {
return Object.toJSON(this.toObject());
},
clone: function() {
return new Hash(this);
}
}
})());
Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
Hash.from = $H;

View File

@ -21,86 +21,95 @@ if (Prototype.Browser.WebKit) {
};
}
function $w(string) {
if (!Object.isString(string)) return [];
string = string.strip();
return string ? string.split(/\s+/) : [];
}
Array.from = $A;
Object.extend(Array.prototype, Enumerable);
if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
Object.extend(Array.prototype, {
_each: function(iterator) {
(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;
clear: function() {
function clear() {
this.length = 0;
return this;
},
first: function() {
}
function first() {
return this[0];
},
last: function() {
}
function last() {
return this[this.length - 1];
},
compact: function() {
}
function compact() {
return this.select(function(value) {
return value != null;
});
},
flatten: function() {
}
function flatten() {
return this.inject([], function(array, value) {
return array.concat(Object.isArray(value) ?
value.flatten() : [value]);
if (Object.isArray(value))
return array.concat(value.flatten());
array.push(value);
return array;
});
},
without: function() {
var values = $A(arguments);
}
function without() {
var values = slice.call(arguments, 0);
return this.select(function(value) {
return !values.include(value);
});
},
reverse: function(inline) {
}
function reverse(inline) {
return (inline !== false ? this : this.toArray())._reverse();
},
reduce: function() {
}
function reduce() {
return this.length > 1 ? this : this[0];
},
uniq: function(sorted) {
}
function uniq(sorted) {
return this.inject([], function(array, value, index) {
if (0 == index || (sorted ? array.last() != value : !array.include(value)))
array.push(value);
return array;
});
},
intersect: function(array) {
}
function intersect(array) {
return this.uniq().findAll(function(item) {
return array.detect(function(value) { return item === value });
});
},
clone: function() {
return [].concat(this);
},
size: function() {
}
function clone() {
return slice.call(this, 0);
}
function size() {
return this.length;
},
inspect: function() {
}
function inspect() {
return '[' + this.map(Object.inspect).join(', ') + ']';
},
toJSON: function() {
}
function toJSON() {
var results = [];
this.each(function(object) {
var value = Object.toJSON(object);
@ -108,37 +117,23 @@ Object.extend(Array.prototype, {
});
return '[' + results.join(', ') + ']';
}
});
// use native browser JS 1.6 implementation if available
if (Object.isFunction(Array.prototype.forEach))
Array.prototype._each = Array.prototype.forEach;
if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
i || (i = 0);
var length = this.length;
if (i < 0) i = length + i;
for (; i < length; i++)
if (this[i] === item) return i;
return -1;
};
if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
var n = this.slice(0, i).reverse().indexOf(item);
return (n < 0) ? n : i - n - 1;
};
Array.prototype.toArray = Array.prototype.clone;
function $w(string) {
if (!Object.isString(string)) return [];
string = string.strip();
return string ? string.split(/\s+/) : [];
}
if (Prototype.Browser.Opera){
Array.prototype.concat = function() {
function indexOf(item, i) {
i || (i = 0);
var length = this.length;
if (i < 0) i = length + i;
for (; i < length; i++)
if (this[i] === item) return i;
return -1;
}
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;
}
function concat() {
var array = [];
for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
for (var i = 0, length = arguments.length; i < length; i++) {
@ -150,5 +145,34 @@ if (Prototype.Browser.Opera){
}
}
return array;
};
}
}
Object.extend(arrayProto, Enumerable);
if (!arrayProto._reverse)
arrayProto._reverse = arrayProto.reverse;
Object.extend(arrayProto, {
_each: _each,
clear: clear,
first: first,
last: last,
compact: compact,
flatten: flatten,
without: without,
reverse: reverse,
reduce: reduce,
uniq: uniq,
intersect: intersect,
clone: clone,
toArray: clone,
size: size,
inspect: inspect,
toJSON: toJSON
});
// use native browser JS 1.6 implementation if available
if (!'indexOf' in arrayProto) arrayProto.indexOf = indexOf;
if (!'lastIndexOf' in arrayProto) arrayProto.lastIndexOf = lastIndexOf;
if (!'concat' in arrayProto) arrayProto.concat = concat;
})();

64
src/lang/class.js Normal file
View File

@ -0,0 +1,64 @@
/* Based on Alex Arnell's inheritance implementation. */
var Class = (function() {
function create() {
var parent = null, properties = $A(arguments);
if (Object.isFunction(properties[0]))
parent = properties.shift();
function klass() {
this.initialize.apply(this, arguments);
}
Object.extend(klass, Class.Methods);
klass.superclass = parent;
klass.subclasses = [];
if (parent) {
var subclass = function() {};
subclass.prototype = parent.prototype;
klass.prototype = new subclass;
parent.subclasses.push(klass);
}
for (var i = 0; i < properties.length; i++)
klass.addMethods(properties[i]);
if (!klass.prototype.initialize)
klass.prototype.initialize = Prototype.emptyFunction;
klass.prototype.constructor = klass;
return klass;
}
function addMethods(source) {
var ancestor = this.superclass && this.superclass.prototype;
var properties = Object.keys(source);
if (!Object.keys({ toString: true }).length)
properties.push("toString", "valueOf");
for (var i = 0, length = properties.length; i < length; i++) {
var property = properties[i], value = source[property];
if (ancestor && Object.isFunction(value) &&
value.argumentNames().first() == "$super") {
var method = value;
value = (function(m) {
return function() { return ancestor[m].apply(this, arguments); };
})(property).wrap(method);
value.valueOf = method.valueOf.bind(method);
value.toString = method.toString.bind(method);
}
this.prototype[property] = value;
}
return this;
}
return {
create: create,
Methods: {
addMethods: addMethods
}
};
})();

9
src/lang/date.js Normal file
View File

@ -0,0 +1,9 @@
Date.prototype.toJSON = function() {
return '"' + this.getUTCFullYear() + '-' +
(this.getUTCMonth() + 1).toPaddedString(2) + '-' +
this.getUTCDate().toPaddedString(2) + 'T' +
this.getUTCHours().toPaddedString(2) + ':' +
this.getUTCMinutes().toPaddedString(2) + ':' +
this.getUTCSeconds().toPaddedString(2) + 'Z"';
};

View File

@ -1,7 +1,7 @@
var $break = { };
var Enumerable = {
each: function(iterator, context) {
var Enumerable = (function() {
function each(iterator, context) {
var index = 0;
try {
this._each(function(value) {
@ -11,17 +11,17 @@ var Enumerable = {
if (e != $break) throw e;
}
return this;
},
}
eachSlice: function(number, iterator, context) {
function eachSlice(number, iterator, context) {
var index = -number, slices = [], array = this.toArray();
if (number < 1) return array;
while ((index += number) < array.length)
slices.push(array.slice(index, index+number));
return slices.collect(iterator, context);
},
}
all: function(iterator, context) {
function all(iterator, context) {
iterator = iterator || Prototype.K;
var result = true;
this.each(function(value, index) {
@ -29,9 +29,9 @@ var Enumerable = {
if (!result) throw $break;
});
return result;
},
}
any: function(iterator, context) {
function any(iterator, context) {
iterator = iterator || Prototype.K;
var result = false;
this.each(function(value, index) {
@ -39,18 +39,18 @@ var Enumerable = {
throw $break;
});
return result;
},
}
collect: function(iterator, context) {
function collect(iterator, context) {
iterator = iterator || Prototype.K;
var results = [];
this.each(function(value, index) {
results.push(iterator.call(context, value, index));
});
return results;
},
}
detect: function(iterator, context) {
function detect(iterator, context) {
var result;
this.each(function(value, index) {
if (iterator.call(context, value, index)) {
@ -59,18 +59,18 @@ var Enumerable = {
}
});
return result;
},
}
findAll: function(iterator, context) {
function findAll(iterator, context) {
var results = [];
this.each(function(value, index) {
if (iterator.call(context, value, index))
results.push(value);
});
return results;
},
}
grep: function(filter, iterator, context) {
function grep(filter, iterator, context) {
iterator = iterator || Prototype.K;
var results = [];
@ -82,9 +82,9 @@ var Enumerable = {
results.push(iterator.call(context, value, index));
});
return results;
},
}
include: function(object) {
function include(object) {
if (Object.isFunction(this.indexOf))
if (this.indexOf(object) != -1) return true;
@ -96,31 +96,31 @@ var Enumerable = {
}
});
return found;
},
}
inGroupsOf: function(number, fillWith) {
function inGroupsOf(number, fillWith) {
fillWith = Object.isUndefined(fillWith) ? null : fillWith;
return this.eachSlice(number, function(slice) {
while(slice.length < number) slice.push(fillWith);
return slice;
});
},
}
inject: function(memo, iterator, context) {
function inject(memo, iterator, context) {
this.each(function(value, index) {
memo = iterator.call(context, memo, value, index);
});
return memo;
},
}
invoke: function(method) {
function invoke(method) {
var args = $A(arguments).slice(1);
return this.map(function(value) {
return value[method].apply(value, args);
});
},
}
max: function(iterator, context) {
function max(iterator, context) {
iterator = iterator || Prototype.K;
var result;
this.each(function(value, index) {
@ -129,9 +129,9 @@ var Enumerable = {
result = value;
});
return result;
},
}
min: function(iterator, context) {
function min(iterator, context) {
iterator = iterator || Prototype.K;
var result;
this.each(function(value, index) {
@ -140,9 +140,9 @@ var Enumerable = {
result = value;
});
return result;
},
}
partition: function(iterator, context) {
function partition(iterator, context) {
iterator = iterator || Prototype.K;
var trues = [], falses = [];
this.each(function(value, index) {
@ -150,26 +150,26 @@ var Enumerable = {
trues : falses).push(value);
});
return [trues, falses];
},
}
pluck: function(property) {
function pluck(property) {
var results = [];
this.each(function(value) {
results.push(value[property]);
});
return results;
},
}
reject: function(iterator, context) {
function reject(iterator, context) {
var results = [];
this.each(function(value, index) {
if (!iterator.call(context, value, index))
results.push(value);
});
return results;
},
}
sortBy: function(iterator, context) {
function sortBy(iterator, context) {
return this.map(function(value, index) {
return {
value: value,
@ -179,13 +179,13 @@ var Enumerable = {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
}).pluck('value');
},
}
toArray: function() {
function toArray() {
return this.map();
},
}
zip: function() {
function zip() {
var iterator = Prototype.K, args = $A(arguments);
if (Object.isFunction(args.last()))
iterator = args.pop();
@ -194,24 +194,46 @@ var Enumerable = {
return this.map(function(value, index) {
return iterator(collections.pluck(index));
});
},
}
size: function() {
function size() {
return this.toArray().length;
},
}
inspect: function() {
function inspect() {
return '#<Enumerable:' + this.toArray().inspect() + '>';
}
};
Object.extend(Enumerable, {
map: Enumerable.collect,
find: Enumerable.detect,
select: Enumerable.findAll,
filter: Enumerable.findAll,
member: Enumerable.include,
entries: Enumerable.toArray,
every: Enumerable.all,
some: Enumerable.any
});
return {
each: each,
eachSlice: eachSlice,
all: all,
every: all,
any: any,
some: any,
collect: collect,
map: collect,
detect: detect,
findAll: findAll,
select: findAll,
filter: findAll,
grep: grep,
include: include,
member: include,
inGroupsOf: inGroupsOf,
inject: inject,
invoke: invoke,
max: max,
min: min,
partition: partition,
pluck: pluck,
reject: reject,
sortBy: sortBy,
toArray: toArray,
entries: toArray,
zip: zip,
size: size,
inspect: inspect,
find: detect
};
})();

89
src/lang/function.js Normal file
View File

@ -0,0 +1,89 @@
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() {
var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
.replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
.replace(/\s+/g, '').split(',');
return names.length == 1 && !names[0] ? [] : names;
}
function bind(context) {
if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
var __method = this, args = slice.call(arguments, 1);
return function() {
var a = merge(args, arguments);
return __method.apply(context, a);
}
}
function bindAsEventListener(context) {
var __method = this, args = slice.call(arguments, 1);
return function(event) {
var a = update([event || window.event], args);
return __method.apply(context, a);
}
}
function curry() {
if (!arguments.length) return this;
var __method = this, args = slice.call(arguments, 0);
return function() {
var a = merge(args, arguments);
return __method.apply(this, a);
}
}
function delay(timeout) {
var __method = this, args = slice.call(arguments, 1);
timeout = timeout * 1000
return window.setTimeout(function() {
return __method.apply(__method, args);
}, timeout);
}
function defer() {
var args = update([0.01], arguments);
return this.delay.apply(this, args);
}
function wrap(wrapper) {
var __method = this;
return function() {
var a = update([__method.bind(this)], arguments);
return wrapper.apply(this, a);
}
}
function methodize() {
if (this._methodized) return this._methodized;
var __method = this;
return this._methodized = function() {
var a = update([this], arguments);
return __method.apply(null, a);
};
}
return {
argumentNames: argumentNames,
bind: bind,
bindAsEventListener: bindAsEventListener,
curry: curry,
delay: delay,
defer: defer,
wrap: wrap,
methodize: methodize
}
})());

116
src/lang/hash.js Normal file
View File

@ -0,0 +1,116 @@
function $H(object) {
return new Hash(object);
};
var Hash = Class.create(Enumerable, (function() {
function initialize(object) {
this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
}
function _each(iterator) {
for (var key in this._object) {
var value = this._object[key], pair = [key, value];
pair.key = key;
pair.value = value;
iterator(pair);
}
}
function set(key, value) {
return this._object[key] = value;
}
function get(key) {
// simulating poorly supported hasOwnProperty
if (this._object[key] !== Object.prototype[key])
return this._object[key];
}
function unset(key) {
var value = this._object[key];
delete this._object[key];
return value;
}
function toObject() {
return Object.clone(this._object);
}
function keys() {
return this.pluck('key');
}
function values() {
return this.pluck('value');
}
function index(value) {
var match = this.detect(function(pair) {
return pair.value === value;
});
return match && match.key;
}
function merge(object) {
return this.clone().update(object);
}
function update(object) {
return new Hash(object).inject(this, function(result, pair) {
result.set(pair.key, pair.value);
return result;
});
}
function toQueryPair(key, value) {
if (Object.isUndefined(value)) return key;
return key + '=' + encodeURIComponent(String.interpret(value));
}
function toQueryString() {
return this.inject([], function(results, pair) {
var key = encodeURIComponent(pair.key), values = pair.value;
if (values && typeof values == 'object') {
if (Object.isArray(values))
return results.concat(values.map(toQueryPair.curry(key)));
} else results.push(toQueryPair(key, values));
return results;
}).join('&');
}
function inspect() {
return '#<Hash:{' + this.map(function(pair) {
return pair.map(Object.inspect).join(': ');
}).join(', ') + '}>';
}
function toJSON() {
return Object.toJSON(this.toObject());
}
function clone() {
return new Hash(this);
}
return {
initialize: initialize,
_each: _each,
set: set,
get: get,
unset: unset,
toObject: toObject,
toTemplateReplacements: toObject,
keys: keys,
values: values,
index: index,
merge: merge,
update: update,
toQueryString: toQueryString,
inspect: inspect,
toJSON: toJSON,
clone: clone
};
})());
Hash.from = $H;

51
src/lang/number.js Normal file
View File

@ -0,0 +1,51 @@
Object.extend(Number.prototype, (function() {
function toColorPart() {
return this.toPaddedString(2, 16);
}
function succ() {
return this + 1;
}
function times(iterator, context) {
$R(0, this, true).each(iterator, context);
return this;
}
function toPaddedString(length, radix) {
var string = this.toString(radix || 10);
return '0'.times(length - string.length) + string;
}
function toJSON() {
return isFinite(this) ? this.toString() : 'null';
}
function abs() {
return Math.abs(this);
}
function round() {
return Math.round(this);
}
function ceil() {
return Math.ceil(this);
}
function floor() {
return Math.floor(this);
}
return {
toColorPart: toColorPart,
succ: succ,
times: times,
toPaddedString: toPaddedString,
toJSON: toJSON,
abs: abs,
round: round,
ceil: ceil,
floor: floor
};
})());

114
src/lang/object.js Normal file
View File

@ -0,0 +1,114 @@
(function() {
function extend(destination, source) {
for (var property in source)
destination[property] = source[property];
return destination;
}
function inspect(object) {
try {
if (isUndefined(object)) return 'undefined';
if (object === null) return 'null';
return object.inspect ? object.inspect() : String(object);
} catch (e) {
if (e instanceof RangeError) return '...';
throw e;
}
}
function toJSON(object) {
var type = typeof object;
switch (type) {
case 'undefined':
case 'function':
case 'unknown': return;
case 'boolean': return object.toString();
}
if (object === null) return 'null';
if (object.toJSON) return object.toJSON();
if (isElement(object)) return;
var results = [];
for (var property in object) {
var value = toJSON(object[property]);
if (!isUndefined(value))
results.push(property.toJSON() + ': ' + value);
}
return '{' + results.join(', ') + '}';
}
function toQueryString(object) {
return $H(object).toQueryString();
}
function toHTML(object) {
return object && object.toHTML ? object.toHTML() : String.interpret(object);
}
function keys(object) {
var keys = [];
for (var property in object)
keys.push(property);
return keys;
}
function values(object) {
var values = [];
for (var property in object)
values.push(object[property]);
return values;
}
function clone(object) {
return extend({ }, object);
}
function isElement(object) {
return !!(object && object.nodeType == 1);
}
function isArray(object) {
return object != null && typeof object === "object" &&
'splice' in object && 'join' in object;
}
function isHash(object) {
return object instanceof Hash;
}
function isFunction(object) {
return typeof object === "function";
}
function isString(object) {
return typeof object === "string";
}
function isNumber(object) {
return typeof object === "number";
}
function isUndefined(object) {
return typeof object === "undefined";
}
extend(Object, {
extend: extend,
inspect: inspect,
toJSON: toJSON,
toQueryString: toQueryString,
toHTML: toHTML,
keys: keys,
values: values,
clone: clone,
isElement: isElement,
isArray: isArray,
isHash: isHash,
isFunction: isFunction,
isString: isString,
isNumber: isNumber,
isUndefined: isUndefined
});
})();

View File

@ -0,0 +1,34 @@
var PeriodicalExecuter = Class.create({
initialize: function(callback, frequency) {
this.callback = callback;
this.frequency = frequency;
this.currentlyExecuting = false;
this.registerCallback();
},
registerCallback: function() {
this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
},
execute: function() {
this.callback(this);
},
stop: function() {
if (!this.timer) return;
clearInterval(this.timer);
this.timer = null;
},
onTimerEvent: function() {
if (!this.currentlyExecuting) {
try {
this.currentlyExecuting = true;
this.execute();
} finally {
this.currentlyExecuting = false;
}
}
}
});

View File

@ -1,27 +1,34 @@
var ObjectRange = Class.create(Enumerable, {
initialize: function(start, end, exclusive) {
function $R(start, end, exclusive) {
return new ObjectRange(start, end, exclusive);
}
var ObjectRange = Class.create(Enumerable, (function() {
function initialize(start, end, exclusive) {
this.start = start;
this.end = end;
this.exclusive = exclusive;
},
}
_each: function(iterator) {
function _each(iterator) {
var value = this.start;
while (this.include(value)) {
iterator(value);
value = value.succ();
}
},
}
include: function(value) {
function include(value) {
if (value < this.start)
return false;
if (this.exclusive)
return value < this.end;
return value <= this.end;
}
});
return {
initialize: initialize,
_each: _each,
include: include
};
})());
var $R = function(start, end, exclusive) {
return new ObjectRange(start, end, exclusive);
};

5
src/lang/regexp.js Normal file
View File

@ -0,0 +1,5 @@
RegExp.prototype.match = RegExp.prototype.test;
RegExp.escape = function(str) {
return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};

View File

@ -12,11 +12,18 @@ Object.extend(String, {
}
});
Object.extend(String.prototype, {
gsub: function(pattern, replacement) {
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) };
}
function gsub(pattern, replacement) {
var result = '', source = this, match;
replacement = arguments.callee.prepareReplacement(replacement);
replacement = prepareReplacement(replacement);
while (source.length > 0) {
if (match = source.match(pattern)) {
result += source.slice(0, match.index);
@ -27,78 +34,78 @@ Object.extend(String.prototype, {
}
}
return result;
},
sub: function(pattern, replacement, count) {
replacement = this.gsub.prepareReplacement(replacement);
}
function sub(pattern, replacement, count) {
replacement = prepareReplacement(replacement);
count = Object.isUndefined(count) ? 1 : count;
return this.gsub(pattern, function(match) {
if (--count < 0) return match[0];
return replacement(match);
});
},
scan: function(pattern, iterator) {
}
function scan(pattern, iterator) {
this.gsub(pattern, iterator);
return String(this);
},
truncate: function(length, truncation) {
}
function truncate(length, truncation) {
length = length || 30;
truncation = Object.isUndefined(truncation) ? '...' : truncation;
return this.length > length ?
this.slice(0, length - truncation.length) + truncation : String(this);
},
}
strip: function() {
function strip() {
return this.replace(/^\s+/, '').replace(/\s+$/, '');
},
stripTags: function() {
return this.replace(/<\/?[^>]+>/gi, '');
},
}
stripScripts: function() {
function stripTags() {
return this.replace(/<\/?[^>]+>/gi, '');
}
function stripScripts() {
return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
},
extractScripts: function() {
}
function extractScripts() {
var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
return (this.match(matchAll) || []).map(function(scriptTag) {
return (scriptTag.match(matchOne) || ['', ''])[1];
});
},
evalScripts: function() {
return this.extractScripts().map(function(script) { return eval(script) });
},
}
escapeHTML: function() {
function evalScripts() {
return this.extractScripts().map(function(script) { return eval(script) });
}
function escapeHTML() {
var self = arguments.callee;
self.text.data = this;
return self.div.innerHTML;
},
}
unescapeHTML: function() {
function unescapeHTML() {
var div = new Element('div');
div.innerHTML = this.stripTags();
return div.childNodes[0] ? (div.childNodes.length > 1 ?
$A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
div.childNodes[0].nodeValue) : '';
},
toQueryParams: function(separator) {
}
function toQueryParams(separator) {
var match = this.strip().match(/([^?#]*)(#.*)?$/);
if (!match) return { };
return match[1].split(separator || '&').inject({ }, function(hash, pair) {
if ((pair = pair.split('='))[0]) {
var key = decodeURIComponent(pair.shift());
var value = pair.length > 1 ? pair.join('=') : pair[0];
if (value != undefined) value = decodeURIComponent(value);
if (key in hash) {
if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
hash[key].push(value);
@ -107,104 +114,138 @@ Object.extend(String.prototype, {
}
return hash;
});
},
toArray: function() {
return this.split('');
},
}
succ: function() {
function toArray() {
return this.split('');
}
function succ() {
return this.slice(0, this.length - 1) +
String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
},
times: function(count) {
}
function times(count) {
return count < 1 ? '' : new Array(count + 1).join(this);
},
camelize: function() {
}
function camelize() {
var parts = this.split('-'), len = parts.length;
if (len == 1) return parts[0];
var camelized = this.charAt(0) == '-'
? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
: parts[0];
for (var i = 1; i < len; i++)
camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
return camelized;
},
capitalize: function() {
}
function capitalize() {
return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
},
underscore: function() {
}
function underscore() {
return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
},
}
dasherize: function() {
function dasherize() {
return this.gsub(/_/,'-');
},
}
inspect: function(useDoubleQuotes) {
function inspect(useDoubleQuotes) {
var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
var character = String.specialChar[match[0]];
return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
});
if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
return "'" + escapedString.replace(/'/g, '\\\'') + "'";
},
toJSON: function() {
}
function toJSON() {
return this.inspect(true);
},
}
unfilterJSON: function(filter) {
function unfilterJSON(filter) {
return this.sub(filter || Prototype.JSONFilter, '#{1}');
},
}
isJSON: function() {
function isJSON() {
var str = this;
if (str.blank()) return false;
str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
},
evalJSON: function(sanitize) {
}
function evalJSON(sanitize) {
var json = this.unfilterJSON();
try {
if (!sanitize || json.isJSON()) return eval('(' + json + ')');
} catch (e) { }
throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
},
include: function(pattern) {
}
function include(pattern) {
return this.indexOf(pattern) > -1;
},
}
startsWith: function(pattern) {
function startsWith(pattern) {
return this.indexOf(pattern) === 0;
},
}
endsWith: function(pattern) {
function endsWith(pattern) {
var d = this.length - pattern.length;
return d >= 0 && this.lastIndexOf(pattern) === d;
},
empty: function() {
return this == '';
},
blank: function() {
return /^\s*$/.test(this);
},
}
interpolate: function(object, pattern) {
function empty() {
return this == '';
}
function blank() {
return /^\s*$/.test(this);
}
function interpolate(object, pattern) {
return new Template(this, pattern).evaluate(object);
}
});
return {
gsub: gsub,
sub: sub,
scan: scan,
truncate: truncate,
strip: strip,
stripTags: stripTags,
stripScripts: stripScripts,
extractScripts: extractScripts,
evalScripts: evalScripts,
escapeHTML: escapeHTML,
unescapeHTML: unescapeHTML,
toQueryParams: toQueryParams,
parseQuery: toQueryParams,
toArray: toArray,
succ: succ,
times: times,
camelize: camelize,
capitalize: capitalize,
underscore: underscore,
dasherize: dasherize,
inspect: inspect,
toJSON: toJSON,
unfilterJSON: unfilterJSON,
isJSON: isJSON,
evalJSON: evalJSON,
include: include,
startsWith: startsWith,
endsWith: endsWith,
empty: empty,
blank: blank,
interpolate: interpolate
};
})());
if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
escapeHTML: function() {
@ -215,14 +256,6 @@ if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.proto
}
});
String.prototype.gsub.prepareReplacement = function(replacement) {
if (Object.isFunction(replacement)) return replacement;
var template = new Template(replacement);
return function(match) { return template.evaluate(match) };
};
String.prototype.parseQuery = String.prototype.toQueryParams;
Object.extend(String.prototype.escapeHTML, {
div: document.createElement('div'),
text: document.createTextNode('')
@ -230,37 +263,3 @@ Object.extend(String.prototype.escapeHTML, {
String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);
var Template = Class.create({
initialize: function(template, pattern) {
this.template = template.toString();
this.pattern = pattern || Template.Pattern;
},
evaluate: function(object) {
if (Object.isFunction(object.toTemplateReplacements))
object = object.toTemplateReplacements();
return this.template.gsub(this.pattern, function(match) {
if (object == null) return '';
var before = match[1] || '';
if (before == '\\') return match[2];
var ctx = object, expr = match[3];
var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
match = pattern.exec(expr);
if (match == null) return before;
while (match != null) {
var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
ctx = ctx[comp];
if (null == ctx || '' == match[3]) break;
expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
match = pattern.exec(expr);
}
return before + String.interpret(ctx);
});
}
});
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;

34
src/lang/template.js Normal file
View File

@ -0,0 +1,34 @@
var Template = Class.create({
initialize: function(template, pattern) {
this.template = template.toString();
this.pattern = pattern || Template.Pattern;
},
evaluate: function(object) {
if (Object.isFunction(object.toTemplateReplacements))
object = object.toTemplateReplacements();
return this.template.gsub(this.pattern, function(match) {
if (object == null) return '';
var before = match[1] || '';
if (before == '\\') return match[2];
var ctx = object, expr = match[3];
var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
match = pattern.exec(expr);
if (match == null) return before;
while (match != null) {
var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
ctx = ctx[comp];
if (null == ctx || '' == match[3]) break;
expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
match = pattern.exec(expr);
}
return before + String.interpret(ctx);
});
}
});
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;

View File

@ -1,27 +0,0 @@
Object.extend(Number.prototype, {
toColorPart: function() {
return this.toPaddedString(2, 16);
},
succ: function() {
return this + 1;
},
times: function(iterator, context) {
$R(0, this, true).each(iterator, context);
return this;
},
toPaddedString: function(length, radix) {
var string = this.toString(radix || 10);
return '0'.times(length - string.length) + string;
},
toJSON: function() {
return isFinite(this) ? this.toString() : 'null';
}
});
$w('abs round ceil floor').each(function(method){
Number.prototype[method] = Math[method].methodize();
});

35
src/prototype.js vendored
View File

@ -33,11 +33,40 @@ var Prototype = {
if (Prototype.Browser.MobileSafari)
Prototype.BrowserFeatures.SpecificElementExtensions = false;
var Abstract = { };
<%= include 'base.js', 'string.js' %>
var Try = {
these: function() {
var returnValue;
for (var i = 0, length = arguments.length; i < length; i++) {
var lambda = arguments[i];
try {
returnValue = lambda();
break;
} catch (e) { }
}
return returnValue;
}
};
<%= include 'enumerable.js', 'array.js', 'number.js', 'hash.js', 'range.js' %>
<%= include 'lang/class.js', 'lang/object.js', 'lang/function.js' %>
<%= include 'ajax.js', 'dom.js', 'selector.js', 'form.js', 'event.js', 'deprecated.js' %>
<%= include 'lang/date.js', 'lang/regexp.js', 'lang/periodical_executer.js' %>
<%= include 'lang/string.js', 'lang/template.js' %>
<%= include 'lang/enumerable.js', 'lang/array.js', 'lang/hash.js' %>
<%= include 'lang/number.js', 'lang/range.js' %>
<%= include 'ajax/ajax.js', 'ajax/responders.js', 'ajax/base.js', 'ajax/request.js', 'ajax/response.js' %>
<%= include 'ajax/updater.js', 'ajax/periodical_updater.js' %>
<%= include 'dom/dom.js', 'dom/selector.js', 'dom/form.js', 'dom/event.js' %>
<%= include 'deprecated.js' %>
Element.addMethods();

130
test/unit/class_test.js Normal file
View File

@ -0,0 +1,130 @@
new Test.Unit.Runner({
testClassCreate: function() {
this.assert(Object.isFunction(Animal), 'Animal is not a constructor');
this.assertEnumEqual([Cat, Mouse, Dog, Ox], Animal.subclasses);
Animal.subclasses.each(function(subclass) {
this.assertEqual(Animal, subclass.superclass);
}, this);
var Bird = Class.create(Animal);
this.assertEqual(Bird, Animal.subclasses.last());
// for..in loop (for some reason) doesn't iterate over the constructor property in top-level classes
this.assertEnumEqual(Object.keys(new Animal).sort(), Object.keys(new Bird).without('constructor').sort());
},
testClassInstantiation: function() {
var pet = new Animal("Nibbles");
this.assertEqual("Nibbles", pet.name, "property not initialized");
this.assertEqual('Nibbles: Hi!', pet.say('Hi!'));
this.assertEqual(Animal, pet.constructor, "bad constructor reference");
this.assertUndefined(pet.superclass);
var Empty = Class.create();
this.assert('object', typeof new Empty);
},
testInheritance: function() {
var tom = new Cat('Tom');
this.assertEqual(Cat, tom.constructor, "bad constructor reference");
this.assertEqual(Animal, tom.constructor.superclass, 'bad superclass reference');
this.assertEqual('Tom', tom.name);
this.assertEqual('Tom: meow', tom.say('meow'));
this.assertEqual('Tom: Yuk! I only eat mice.', tom.eat(new Animal));
},
testSuperclassMethodCall: function() {
var tom = new Cat('Tom');
this.assertEqual('Tom: Yum!', tom.eat(new Mouse));
// augment the constructor and test
var Dodo = Class.create(Animal, {
initialize: function($super, name) {
$super(name);
this.extinct = true;
},
say: function($super, message) {
return $super(message) + " honk honk";
}
});
var gonzo = new Dodo('Gonzo');
this.assertEqual('Gonzo', gonzo.name);
this.assert(gonzo.extinct, 'Dodo birds should be extinct');
this.assertEqual("Gonzo: hello honk honk", gonzo.say("hello"));
},
testClassAddMethods: function() {
var tom = new Cat('Tom');
var jerry = new Mouse('Jerry');
Animal.addMethods({
sleep: function() {
return this.say('ZZZ');
}
});
Mouse.addMethods({
sleep: function($super) {
return $super() + " ... no, can't sleep! Gotta steal cheese!";
},
escape: function(cat) {
return this.say('(from a mousehole) Take that, ' + cat.name + '!');
}
});
this.assertEqual('Tom: ZZZ', tom.sleep(), "added instance method not available to subclass");
this.assertEqual("Jerry: ZZZ ... no, can't sleep! Gotta steal cheese!", jerry.sleep());
this.assertEqual("Jerry: (from a mousehole) Take that, Tom!", jerry.escape(tom));
// insure that a method has not propagated *up* the prototype chain:
this.assertUndefined(tom.escape);
this.assertUndefined(new Animal().escape);
Animal.addMethods({
sleep: function() {
return this.say('zZzZ');
}
});
this.assertEqual("Jerry: zZzZ ... no, can't sleep! Gotta steal cheese!", jerry.sleep());
},
testBaseClassWithMixin: function() {
var grass = new Plant('grass', 3);
this.assertRespondsTo('getValue', grass);
this.assertEqual('#<Plant: grass>', grass.inspect());
},
testSubclassWithMixin: function() {
var snoopy = new Dog('Snoopy', 12, 'male');
this.assertRespondsTo('reproduce', snoopy);
},
testSubclassWithMixins: function() {
var cow = new Ox('cow', 400, 'female');
this.assertEqual('#<Ox: cow>', cow.inspect());
this.assertRespondsTo('reproduce', cow);
this.assertRespondsTo('getValue', cow);
},
testClassWithToStringAndValueOfMethods: function() {
var Foo = Class.create({
toString: function() { return "toString" },
valueOf: function() { return "valueOf" }
});
var Parent = Class.create({
m1: function(){ return 'm1' },
m2: function(){ return 'm2' }
});
var Child = Class.create(Parent, {
m1: function($super) { return 'm1 child' },
m2: function($super) { return 'm2 child' }
});
this.assert(new Child().m1.toString().indexOf('m1 child') > -1);
this.assertEqual("toString", new Foo().toString());
this.assertEqual("valueOf", new Foo().valueOf());
}
});

5
test/unit/date_test.js Normal file
View File

@ -0,0 +1,5 @@
new Test.Unit.Runner({
testDateToJSON: function() {
this.assertEqual('\"1970-01-01T00:00:00Z\"', new Date(Date.UTC(1970, 0, 1)).toJSON());
}
});

View File

@ -0,0 +1,83 @@
// base class
var Animal = Class.create({
initialize: function(name) {
this.name = name;
},
name: "",
eat: function() {
return this.say("Yum!");
},
say: function(message) {
return this.name + ": " + message;
}
});
// subclass that augments a method
var Cat = Class.create(Animal, {
eat: function($super, food) {
if (food instanceof Mouse) return $super();
else return this.say("Yuk! I only eat mice.");
}
});
// empty subclass
var Mouse = Class.create(Animal, {});
//mixins
var Sellable = {
getValue: function(pricePerKilo) {
return this.weight * pricePerKilo;
},
inspect: function() {
return '#<Sellable: #{weight}kg>'.interpolate(this);
}
};
var Reproduceable = {
reproduce: function(partner) {
if (partner.constructor != this.constructor || partner.sex == this.sex)
return null;
var weight = this.weight / 10, sex = Math.random(1).round() ? 'male' : 'female';
return new this.constructor('baby', weight, sex);
}
};
// base class with mixin
var Plant = Class.create(Sellable, {
initialize: function(name, weight) {
this.name = name;
this.weight = weight;
},
inspect: function() {
return '#<Plant: #{name}>'.interpolate(this);
}
});
// subclass with mixin
var Dog = Class.create(Animal, Reproduceable, {
initialize: function($super, name, weight, sex) {
this.weight = weight;
this.sex = sex;
$super(name);
}
});
// subclass with mixins
var Ox = Class.create(Animal, Sellable, Reproduceable, {
initialize: function($super, name, weight, sex) {
this.weight = weight;
this.sex = sex;
$super(name);
},
eat: function(food) {
if (food instanceof Plant)
this.weight += food.weight;
},
inspect: function() {
return '#<Ox: #{name}>'.interpolate(this);
}
});

View File

@ -0,0 +1,13 @@
var arg1 = 1;
var arg2 = 2;
var arg3 = 3;
function TestObj() { };
TestObj.prototype.assertingEventHandler =
function(event, assertEvent, assert1, assert2, assert3, a1, a2, a3) {
assertEvent(event);
assert1(a1);
assert2(a2);
assert3(a3);
};
var globalBindTest = null;

View File

@ -0,0 +1,6 @@
<div id="test"></div>
<ul id="list">
<li></li>
<li></li>
<li></li>
</ul>

View File

@ -0,0 +1,7 @@
var Person = function(name){
this.name = name;
};
Person.prototype.toJSON = function() {
return '-' + this.name;
};

133
test/unit/function_test.js Normal file
View File

@ -0,0 +1,133 @@
new Test.Unit.Runner({
testFunctionArgumentNames: function() {
this.assertEnumEqual([], (function() {}).argumentNames());
this.assertEnumEqual(["one"], (function(one) {}).argumentNames());
this.assertEnumEqual(["one", "two", "three"], (function(one, two, three) {}).argumentNames());
this.assertEnumEqual(["one", "two", "three"], (function( one , two
, three ) {}).argumentNames());
this.assertEqual("$super", (function($super) {}).argumentNames().first());
function named1() {};
this.assertEnumEqual([], named1.argumentNames());
function named2(one) {};
this.assertEnumEqual(["one"], named2.argumentNames());
function named3(one, two, three) {};
this.assertEnumEqual(["one", "two", "three"], named3.argumentNames());
function named4(one,
two,
three) {}
this.assertEnumEqual($w('one two three'), named4.argumentNames());
function named5(/*foo*/ foo, /* bar */ bar, /*****/ baz) {}
this.assertEnumEqual($w("foo bar baz"), named5.argumentNames());
function named6(
/*foo*/ foo,
/**/bar,
/* baz */ /* baz */ baz,
// Skip a line just to screw with the regex...
/* thud */ thud) {}
this.assertEnumEqual($w("foo bar baz thud"), named6.argumentNames());
},
testFunctionBind: function() {
function methodWithoutArguments() { return this.hi };
function methodWithArguments() { return this.hi + ',' + $A(arguments).join(',') };
var func = Prototype.emptyFunction;
this.assertIdentical(func, func.bind());
this.assertIdentical(func, func.bind(undefined));
this.assertNotIdentical(func, func.bind(null));
this.assertEqual('without', methodWithoutArguments.bind({ hi: 'without' })());
this.assertEqual('with,arg1,arg2', methodWithArguments.bind({ hi: 'with' })('arg1','arg2'));
this.assertEqual('withBindArgs,arg1,arg2',
methodWithArguments.bind({ hi: 'withBindArgs' }, 'arg1', 'arg2')());
this.assertEqual('withBindArgsAndArgs,arg1,arg2,arg3,arg4',
methodWithArguments.bind({ hi: 'withBindArgsAndArgs' }, 'arg1', 'arg2')('arg3', 'arg4'));
},
testFunctionCurry: function() {
var split = function(delimiter, string) { return string.split(delimiter); };
var splitOnColons = split.curry(":");
this.assertNotIdentical(split, splitOnColons);
this.assertEnumEqual(split(":", "0:1:2:3:4:5"), splitOnColons("0:1:2:3:4:5"));
this.assertIdentical(split, split.curry());
},
testFunctionDelay: function() {
window.delayed = undefined;
var delayedFunction = function() { window.delayed = true; };
var delayedFunctionWithArgs = function() { window.delayedWithArgs = $A(arguments).join(' '); };
delayedFunction.delay(0.8);
delayedFunctionWithArgs.delay(0.8, 'hello', 'world');
this.assertUndefined(window.delayed);
this.wait(1000, function() {
this.assert(window.delayed);
this.assertEqual('hello world', window.delayedWithArgs);
});
},
testFunctionWrap: function() {
function sayHello(){
return 'hello world';
};
this.assertEqual('HELLO WORLD', sayHello.wrap(function(proceed) {
return proceed().toUpperCase();
})());
var temp = String.prototype.capitalize;
String.prototype.capitalize = String.prototype.capitalize.wrap(function(proceed, eachWord) {
if(eachWord && this.include(' ')) return this.split(' ').map(function(str){
return str.capitalize();
}).join(' ');
return proceed();
});
this.assertEqual('Hello world', 'hello world'.capitalize());
this.assertEqual('Hello World', 'hello world'.capitalize(true));
this.assertEqual('Hello', 'hello'.capitalize());
String.prototype.capitalize = temp;
},
testFunctionDefer: function() {
window.deferred = undefined;
var deferredFunction = function() { window.deferred = true; };
deferredFunction.defer();
this.assertUndefined(window.deferred);
this.wait(50, function() {
this.assert(window.deferred);
window.deferredValue = 0;
var deferredFunction2 = function(arg) { window.deferredValue = arg; };
deferredFunction2.defer('test');
this.wait(50, function() {
this.assertEqual('test', window.deferredValue);
});
});
},
testFunctionMethodize: function() {
var Foo = { bar: function(baz) { return baz } };
var baz = { quux: Foo.bar.methodize() };
this.assertEqual(Foo.bar.methodize(), baz.quux);
this.assertEqual(baz, Foo.bar(baz));
this.assertEqual(baz, baz.quux());
},
testBindAsEventListener: function() {
for( var i = 0; i < 10; ++i ){
var div = document.createElement('div');
div.setAttribute('id','test-'+i);
document.body.appendChild(div);
var tobj = new TestObj();
var eventTest = { test: true };
var call = tobj.assertingEventHandler.bindAsEventListener(tobj,
this.assertEqual.bind(this, eventTest),
this.assertEqual.bind(this, arg1),
this.assertEqual.bind(this, arg2),
this.assertEqual.bind(this, arg3), arg1, arg2, arg3 );
call(eventTest);
}
}
});

174
test/unit/object_test.js Normal file
View File

@ -0,0 +1,174 @@
new Test.Unit.Runner({
testObjectExtend: function() {
var object = {foo: 'foo', bar: [1, 2, 3]};
this.assertIdentical(object, Object.extend(object));
this.assertHashEqual({foo: 'foo', bar: [1, 2, 3]}, object);
this.assertIdentical(object, Object.extend(object, {bla: 123}));
this.assertHashEqual({foo: 'foo', bar: [1, 2, 3], bla: 123}, object);
this.assertHashEqual({foo: 'foo', bar: [1, 2, 3], bla: null},
Object.extend(object, {bla: null}));
},
testObjectToQueryString: function() {
this.assertEqual('a=A&b=B&c=C&d=D%23', Object.toQueryString({a: 'A', b: 'B', c: 'C', d: 'D#'}));
},
testObjectClone: function() {
var object = {foo: 'foo', bar: [1, 2, 3]};
this.assertNotIdentical(object, Object.clone(object));
this.assertHashEqual(object, Object.clone(object));
this.assertHashEqual({}, Object.clone());
var clone = Object.clone(object);
delete clone.bar;
this.assertHashEqual({foo: 'foo'}, clone,
"Optimizing Object.clone perf using prototyping doesn't allow properties to be deleted.");
},
testObjectInspect: function() {
this.assertEqual('undefined', Object.inspect());
this.assertEqual('undefined', Object.inspect(undefined));
this.assertEqual('null', Object.inspect(null));
this.assertEqual("'foo\\\\b\\\'ar'", Object.inspect('foo\\b\'ar'));
this.assertEqual('[]', Object.inspect([]));
this.assertNothingRaised(function() { Object.inspect(window.Node) });
},
testObjectToJSON: function() {
this.assertUndefined(Object.toJSON(undefined));
this.assertUndefined(Object.toJSON(Prototype.K));
this.assertEqual('\"\"', Object.toJSON(''));
this.assertEqual('[]', Object.toJSON([]));
this.assertEqual('[\"a\"]', Object.toJSON(['a']));
this.assertEqual('[\"a\", 1]', Object.toJSON(['a', 1]));
this.assertEqual('[\"a\", {\"b\": null}]', Object.toJSON(['a', {'b': null}]));
this.assertEqual('{\"a\": \"hello!\"}', Object.toJSON({a: 'hello!'}));
this.assertEqual('{}', Object.toJSON({}));
this.assertEqual('{}', Object.toJSON({a: undefined, b: undefined, c: Prototype.K}));
this.assertEqual('{\"b\": [false, true], \"c\": {\"a\": \"hello!\"}}',
Object.toJSON({'b': [undefined, false, true, undefined], c: {a: 'hello!'}}));
this.assertEqual('{\"b\": [false, true], \"c\": {\"a\": \"hello!\"}}',
Object.toJSON($H({'b': [undefined, false, true, undefined], c: {a: 'hello!'}})));
this.assertEqual('true', Object.toJSON(true));
this.assertEqual('false', Object.toJSON(false));
this.assertEqual('null', Object.toJSON(null));
var sam = new Person('sam');
this.assertEqual('-sam', Object.toJSON(sam));
this.assertEqual('-sam', sam.toJSON());
var element = $('test');
this.assertUndefined(Object.toJSON(element));
element.toJSON = function(){return 'I\'m a div with id test'};
this.assertEqual('I\'m a div with id test', Object.toJSON(element));
},
testObjectToHTML: function() {
this.assertIdentical('', Object.toHTML());
this.assertIdentical('', Object.toHTML(''));
this.assertIdentical('', Object.toHTML(null));
this.assertIdentical('0', Object.toHTML(0));
this.assertIdentical('123', Object.toHTML(123));
this.assertEqual('hello world', Object.toHTML('hello world'));
this.assertEqual('hello world', Object.toHTML({toHTML: function() { return 'hello world' }}));
},
testObjectIsArray: function() {
this.assert(Object.isArray([]));
this.assert(Object.isArray([0]));
this.assert(Object.isArray([0, 1]));
this.assert(!Object.isArray({}));
this.assert(!Object.isArray($('list').childNodes));
this.assert(!Object.isArray());
this.assert(!Object.isArray(''));
this.assert(!Object.isArray('foo'));
this.assert(!Object.isArray(0));
this.assert(!Object.isArray(1));
this.assert(!Object.isArray(null));
this.assert(!Object.isArray(true));
this.assert(!Object.isArray(false));
this.assert(!Object.isArray(undefined));
},
testObjectIsHash: function() {
this.assert(Object.isHash($H()));
this.assert(Object.isHash(new Hash()));
this.assert(!Object.isHash({}));
this.assert(!Object.isHash(null));
this.assert(!Object.isHash());
this.assert(!Object.isHash(''));
this.assert(!Object.isHash(2));
this.assert(!Object.isHash(false));
this.assert(!Object.isHash(true));
this.assert(!Object.isHash([]));
},
testObjectIsElement: function() {
this.assert(Object.isElement(document.createElement('div')));
this.assert(Object.isElement(new Element('div')));
this.assert(Object.isElement($('testlog')));
this.assert(!Object.isElement(document.createTextNode('bla')));
// falsy variables should not mess up return value type
this.assertIdentical(false, Object.isElement(0));
this.assertIdentical(false, Object.isElement(''));
this.assertIdentical(false, Object.isElement(NaN));
this.assertIdentical(false, Object.isElement(null));
this.assertIdentical(false, Object.isElement(undefined));
},
testObjectIsFunction: function() {
this.assert(Object.isFunction(function() { }));
this.assert(Object.isFunction(Class.create()));
this.assert(!Object.isFunction("a string"));
this.assert(!Object.isFunction($("testlog")));
this.assert(!Object.isFunction([]));
this.assert(!Object.isFunction({}));
this.assert(!Object.isFunction(0));
this.assert(!Object.isFunction(false));
this.assert(!Object.isFunction(undefined));
},
testObjectIsString: function() {
this.assert(!Object.isString(function() { }));
this.assert(Object.isString("a string"));
this.assert(!Object.isString(0));
this.assert(!Object.isString([]));
this.assert(!Object.isString({}));
this.assert(!Object.isString(false));
this.assert(!Object.isString(undefined));
},
testObjectIsNumber: function() {
this.assert(Object.isNumber(0));
this.assert(Object.isNumber(1.0));
this.assert(!Object.isNumber(function() { }));
this.assert(!Object.isNumber("a string"));
this.assert(!Object.isNumber([]));
this.assert(!Object.isNumber({}));
this.assert(!Object.isNumber(false));
this.assert(!Object.isNumber(undefined));
},
testObjectIsUndefined: function() {
this.assert(Object.isUndefined(undefined));
this.assert(!Object.isUndefined(null));
this.assert(!Object.isUndefined(false));
this.assert(!Object.isUndefined(0));
this.assert(!Object.isUndefined(""));
this.assert(!Object.isUndefined(function() { }));
this.assert(!Object.isUndefined([]));
this.assert(!Object.isUndefined({}));
},
// sanity check
testDoesntExtendObjectPrototype: function() {
// for-in is supported with objects
var iterations = 0, obj = { a: 1, b: 2, c: 3 };
for(property in obj) iterations++;
this.assertEqual(3, iterations);
// for-in is not supported with arrays
iterations = 0;
var arr = [1,2,3];
for(property in arr) iterations++;
this.assert(iterations > 3);
}
});

View File

@ -0,0 +1,15 @@
new Test.Unit.Runner({
testPeriodicalExecuterStop: function() {
var peEventCount = 0;
function peEventFired(pe) {
if (++peEventCount > 2) pe.stop();
}
// peEventFired will stop the PeriodicalExecuter after 3 callbacks
new PeriodicalExecuter(peEventFired, 0.05);
this.wait(600, function() {
this.assertEqual(3, peEventCount);
});
}
});

43
test/unit/prototype_test.js vendored Normal file
View File

@ -0,0 +1,43 @@
new Test.Unit.Runner({
testBrowserDetection: function() {
var results = $H(Prototype.Browser).map(function(engine){
return engine;
}).partition(function(engine){
return engine[1] === true
});
var trues = results[0], falses = results[1];
this.info('User agent string is: ' + navigator.userAgent);
this.assert(trues.size() == 0 || trues.size() == 1,
'There should be only one or no browser detected.');
// we should have definite trues or falses here
trues.each(function(result) {
this.assert(result[1] === true);
}, this);
falses.each(function(result) {
this.assert(result[1] === false);
}, this);
if(navigator.userAgent.indexOf('AppleWebKit/') > -1) {
this.info('Running on WebKit');
this.assert(Prototype.Browser.WebKit);
}
if(!!window.opera) {
this.info('Running on Opera');
this.assert(Prototype.Browser.Opera);
}
if(!!(window.attachEvent && !window.opera)) {
this.info('Running on IE');
this.assert(Prototype.Browser.IE);
}
if(navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1) {
this.info('Running on Gecko');
this.assert(Prototype.Browser.Gecko);
}
}
});

42
test/unit/regexp_test.js Normal file
View File

@ -0,0 +1,42 @@
new Test.Unit.Runner({
testRegExpEscape: function() {
this.assertEqual('word', RegExp.escape('word'));
this.assertEqual('\\/slashes\\/', RegExp.escape('/slashes/'));
this.assertEqual('\\\\backslashes\\\\', RegExp.escape('\\backslashes\\'));
this.assertEqual('\\\\border of word', RegExp.escape('\\border of word'));
this.assertEqual('\\(\\?\\:non-capturing\\)', RegExp.escape('(?:non-capturing)'));
this.assertEqual('non-capturing', new RegExp(RegExp.escape('(?:') + '([^)]+)').exec('(?:non-capturing)')[1]);
this.assertEqual('\\(\\?\\=positive-lookahead\\)', RegExp.escape('(?=positive-lookahead)'));
this.assertEqual('positive-lookahead', new RegExp(RegExp.escape('(?=') + '([^)]+)').exec('(?=positive-lookahead)')[1]);
this.assertEqual('\\(\\?<\\=positive-lookbehind\\)', RegExp.escape('(?<=positive-lookbehind)'));
this.assertEqual('positive-lookbehind', new RegExp(RegExp.escape('(?<=') + '([^)]+)').exec('(?<=positive-lookbehind)')[1]);
this.assertEqual('\\(\\?\\!negative-lookahead\\)', RegExp.escape('(?!negative-lookahead)'));
this.assertEqual('negative-lookahead', new RegExp(RegExp.escape('(?!') + '([^)]+)').exec('(?!negative-lookahead)')[1]);
this.assertEqual('\\(\\?<\\!negative-lookbehind\\)', RegExp.escape('(?<!negative-lookbehind)'));
this.assertEqual('negative-lookbehind', new RegExp(RegExp.escape('(?<!') + '([^)]+)').exec('(?<!negative-lookbehind)')[1]);
this.assertEqual('\\[\\\\w\\]\\+', RegExp.escape('[\\w]+'));
this.assertEqual('character class', new RegExp(RegExp.escape('[') + '([^\\]]+)').exec('[character class]')[1]);
this.assertEqual('<div>', new RegExp(RegExp.escape('<div>')).exec('<td><div></td>')[0]);
this.assertEqual('false', RegExp.escape(false));
this.assertEqual('undefined', RegExp.escape());
this.assertEqual('null', RegExp.escape(null));
this.assertEqual('42', RegExp.escape(42));
this.assertEqual('\\\\n\\\\r\\\\t', RegExp.escape('\\n\\r\\t'));
this.assertEqual('\n\r\t', RegExp.escape('\n\r\t'));
this.assertEqual('\\{5,2\\}', RegExp.escape('{5,2}'));
this.assertEqual(
'\\/\\(\\[\\.\\*\\+\\?\\^\\=\\!\\:\\$\\{\\}\\(\\)\\|\\[\\\\\\]\\\\\\\/\\\\\\\\\\]\\)\\/g',
RegExp.escape('/([.*+?^=!:${}()|[\\]\\/\\\\])/g')
);
}
});