Compare commits
34 Commits
master
...
experiment
Author | SHA1 | Date | |
---|---|---|---|
|
3ad847ce4b | ||
|
9f09a8c791 | ||
|
275a344173 | ||
|
d12e77c56d | ||
|
10573fb1a9 | ||
|
62a1f7e2ce | ||
|
0cc28be265 | ||
|
be44ca0089 | ||
|
b878bfe3ff | ||
|
cd0734b8a7 | ||
|
f788cf7048 | ||
|
ba30188e5d | ||
|
160f85f518 | ||
|
d9087c26a9 | ||
|
3a93b6ccae | ||
|
d55932fc8c | ||
|
ec5069ed3a | ||
|
fee81bffee | ||
|
15d4d5fe7a | ||
|
5607889e41 | ||
|
9af3c039e1 | ||
|
8d3911ec09 | ||
|
e3f3e95b84 | ||
|
373ef2d839 | ||
|
dc70b13aed | ||
|
f64c9568c8 | ||
|
1669e41cda | ||
|
bcbdf3b247 | ||
|
22f9cfc626 | ||
|
e63f50aa60 | ||
|
a94c420a34 | ||
|
b7e817927e | ||
|
f5d8a093f2 | ||
|
f46a3c2430 |
4
.gitignore
vendored
4
.gitignore
vendored
@ -1 +1,3 @@
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
pkg
|
||||
test/unit/tmp/*
|
@ -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
3
dist/.gitignore
vendored
@ -1 +1,2 @@
|
||||
prototype.js
|
||||
prototype.js
|
||||
prototype_update_helper.js
|
@ -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
|
||||
|
391
src/ajax.js
391
src/ajax.js
@ -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
11
src/ajax/ajax.js
Normal 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
21
src/ajax/base.js
Normal 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();
|
||||
}
|
||||
});
|
40
src/ajax/periodical_updater.js
Normal file
40
src/ajax/periodical_updater.js
Normal 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
172
src/ajax/request.js
Normal 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
33
src/ajax/responders.js
Normal 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
73
src/ajax/response.js
Normal 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
35
src/ajax/updater.js
Normal 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);
|
||||
}
|
||||
}
|
||||
});
|
285
src/base.js
285
src/base.js
@ -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
378
src/dom/event.js
Normal 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;
|
||||
}
|
||||
})();
|
@ -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: { },
|
||||
|
348
src/event.js
348
src/event.js
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
})();
|
101
src/hash.js
101
src/hash.js
@ -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;
|
@ -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
64
src/lang/class.js
Normal 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
9
src/lang/date.js
Normal 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"';
|
||||
};
|
||||
|
@ -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
89
src/lang/function.js
Normal 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
116
src/lang/hash.js
Normal 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
51
src/lang/number.js
Normal 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
114
src/lang/object.js
Normal 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
|
||||
});
|
||||
})();
|
34
src/lang/periodical_executer.js
Normal file
34
src/lang/periodical_executer.js
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
@ -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
5
src/lang/regexp.js
Normal file
@ -0,0 +1,5 @@
|
||||
RegExp.prototype.match = RegExp.prototype.test;
|
||||
|
||||
RegExp.escape = function(str) {
|
||||
return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
|
||||
};
|
@ -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
34
src/lang/template.js
Normal 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)(#\{(.*?)\})/;
|
@ -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
35
src/prototype.js
vendored
@ -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
130
test/unit/class_test.js
Normal 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
5
test/unit/date_test.js
Normal 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());
|
||||
}
|
||||
});
|
83
test/unit/fixtures/class.js
Normal file
83
test/unit/fixtures/class.js
Normal 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);
|
||||
}
|
||||
});
|
13
test/unit/fixtures/function.js
Normal file
13
test/unit/fixtures/function.js
Normal 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;
|
6
test/unit/fixtures/object.html
Normal file
6
test/unit/fixtures/object.html
Normal file
@ -0,0 +1,6 @@
|
||||
<div id="test"></div>
|
||||
<ul id="list">
|
||||
<li></li>
|
||||
<li></li>
|
||||
<li></li>
|
||||
</ul>
|
7
test/unit/fixtures/object.js
Normal file
7
test/unit/fixtures/object.js
Normal 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
133
test/unit/function_test.js
Normal 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
174
test/unit/object_test.js
Normal 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);
|
||||
}
|
||||
});
|
15
test/unit/periodical_executer_test.js
Normal file
15
test/unit/periodical_executer_test.js
Normal 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
43
test/unit/prototype_test.js
vendored
Normal 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
42
test/unit/regexp_test.js
Normal 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')
|
||||
);
|
||||
}
|
||||
});
|
Loading…
Reference in New Issue
Block a user