314 lines
8.8 KiB
JavaScript
314 lines
8.8 KiB
JavaScript
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 (typeof responder[callback] == 'function') {
|
|
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 = function() {};
|
|
Ajax.Base.prototype = {
|
|
setOptions: function(options) {
|
|
this.options = {
|
|
method: 'post',
|
|
asynchronous: true,
|
|
contentType: 'application/x-www-form-urlencoded',
|
|
encoding: 'UTF-8',
|
|
parameters: ''
|
|
}
|
|
Object.extend(this.options, options || {});
|
|
|
|
this.options.method = this.options.method.toLowerCase();
|
|
if (typeof this.options.parameters == 'string')
|
|
this.options.parameters = this.options.parameters.toQueryParams();
|
|
}
|
|
}
|
|
|
|
Ajax.Request = Class.create();
|
|
Ajax.Request.Events =
|
|
['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
|
|
|
|
Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
|
|
_complete: false,
|
|
|
|
initialize: function(url, options) {
|
|
this.transport = Ajax.getTransport();
|
|
this.setOptions(options);
|
|
this.request(url);
|
|
},
|
|
|
|
request: function(url) {
|
|
this.url = url;
|
|
this.method = this.options.method;
|
|
var params = this.options.parameters;
|
|
|
|
if (!['get', 'post'].include(this.method)) {
|
|
// simulate other verbs over post
|
|
params['_method'] = this.method;
|
|
this.method = 'post';
|
|
}
|
|
|
|
params = Hash.toQueryString(params);
|
|
if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='
|
|
|
|
// when GET, append parameters to URL
|
|
if (this.method == 'get' && params)
|
|
this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
|
|
|
|
try {
|
|
Ajax.Responders.dispatch('onCreate', this, this.transport);
|
|
|
|
this.transport.open(this.method.toUpperCase(), this.url,
|
|
this.options.asynchronous);
|
|
|
|
if (this.options.asynchronous)
|
|
setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
|
|
|
|
this.transport.onreadystatechange = this.onStateChange.bind(this);
|
|
this.setRequestHeaders();
|
|
|
|
var body = this.method == 'post' ? (this.options.postBody || params) : null;
|
|
|
|
this.transport.send(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 (typeof extras.push == 'function')
|
|
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() {
|
|
return !this.transport.status
|
|
|| (this.transport.status >= 200 && this.transport.status < 300);
|
|
},
|
|
|
|
respondToReadyState: function(readyState) {
|
|
var state = Ajax.Request.Events[readyState];
|
|
var transport = this.transport, json = this.evalJSON();
|
|
|
|
if (state == 'Complete') {
|
|
try {
|
|
this._complete = true;
|
|
(this.options['on' + this.transport.status]
|
|
|| this.options['on' + (this.success() ? 'Success' : 'Failure')]
|
|
|| Prototype.emptyFunction)(transport, json);
|
|
} catch (e) {
|
|
this.dispatchException(e);
|
|
}
|
|
|
|
if ((this.getHeader('Content-type') || 'text/javascript').strip().
|
|
match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
|
|
this.evalResponse();
|
|
}
|
|
|
|
try {
|
|
(this.options['on' + state] || Prototype.emptyFunction)(transport, json);
|
|
Ajax.Responders.dispatch('on' + state, this, transport, json);
|
|
} catch (e) {
|
|
this.dispatchException(e);
|
|
}
|
|
|
|
if (state == 'Complete') {
|
|
// avoid memory leak in MSIE: clean up
|
|
this.transport.onreadystatechange = Prototype.emptyFunction;
|
|
}
|
|
},
|
|
|
|
getHeader: function(name) {
|
|
try {
|
|
return this.transport.getResponseHeader(name);
|
|
} catch (e) { return null }
|
|
},
|
|
|
|
evalJSON: function() {
|
|
try {
|
|
var json = this.getHeader('X-JSON');
|
|
return json ? eval('(' + json + ')') : null;
|
|
} catch (e) { return null }
|
|
},
|
|
|
|
evalResponse: function() {
|
|
try {
|
|
return eval(this.transport.responseText);
|
|
} catch (e) {
|
|
this.dispatchException(e);
|
|
}
|
|
},
|
|
|
|
dispatchException: function(exception) {
|
|
(this.options.onException || Prototype.emptyFunction)(this, exception);
|
|
Ajax.Responders.dispatch('onException', this, exception);
|
|
}
|
|
});
|
|
|
|
Ajax.Updater = Class.create();
|
|
|
|
Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
|
|
initialize: function(container, url, options) {
|
|
this.container = {
|
|
success: (container.success || container),
|
|
failure: (container.failure || (container.success ? null : container))
|
|
}
|
|
|
|
this.transport = Ajax.getTransport();
|
|
this.setOptions(options);
|
|
|
|
var onComplete = this.options.onComplete || Prototype.emptyFunction;
|
|
this.options.onComplete = (function(transport, param) {
|
|
this.updateContent();
|
|
onComplete(transport, param);
|
|
}).bind(this);
|
|
|
|
this.request(url);
|
|
},
|
|
|
|
updateContent: function() {
|
|
var receiver = this.container[this.success() ? 'success' : 'failure'];
|
|
var response = this.transport.responseText;
|
|
|
|
if (!this.options.evalScripts) response = response.stripScripts();
|
|
|
|
if (receiver = $(receiver)) {
|
|
if (this.options.insertion)
|
|
new this.options.insertion(receiver, response);
|
|
else
|
|
receiver.update(response);
|
|
}
|
|
|
|
if (this.success()) {
|
|
if (this.onComplete)
|
|
setTimeout(this.onComplete.bind(this), 10);
|
|
}
|
|
}
|
|
});
|
|
|
|
Ajax.PeriodicalUpdater = Class.create();
|
|
Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
|
|
initialize: function(container, url, options) {
|
|
this.setOptions(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(request) {
|
|
if (this.options.decay) {
|
|
this.decay = (request.responseText == this.lastText ?
|
|
this.decay * this.options.decay : 1);
|
|
|
|
this.lastText = request.responseText;
|
|
}
|
|
this.timer = setTimeout(this.onTimerEvent.bind(this),
|
|
this.decay * this.frequency * 1000);
|
|
},
|
|
|
|
onTimerEvent: function() {
|
|
this.updater = new Ajax.Updater(this.container, this.url, this.options);
|
|
}
|
|
});
|