iqjax 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2011 innoQ Deutschland GmbH
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,41 @@
1
+ iQjax
2
+ =====
3
+
4
+ This is an unobtrusive JavaScript component for doing Ajax in conformity
5
+ with the [ROCA-Style](http://roca-style.org). It enables your web application
6
+ to do Ajax without bigger impacts to your server side.
7
+
8
+ An iQjax enabled DOM node loads the target of annotated links it contains into a
9
+ specific container DOM node. This is done asynchronously via Ajax. If the
10
+ content contains a form, the form handling (especially error handling) will be
11
+ automatically done in the container too. After a form was successfully submitted,
12
+ iQjax provides methods to update the iQjax container without reloading the whole
13
+ page.
14
+
15
+ E.g. given a iQjax enabled list of person names with links pointing to a
16
+ form to edit the respective person name. Clicking on such a link will
17
+ load the form to a given container DOM node. After submitting the form
18
+ with a changed name, the user would expect the list of names to be updated.
19
+ iQjax can do this by searching the new list entry in the response of
20
+ the form POST. Then it replaces the old node with the new one.
21
+
22
+ Documentation
23
+ -------------
24
+
25
+ We distinguish six API parts of ROCA-Style JavaScript components:
26
+
27
+ # The socket HTML markup the component initializes upon,
28
+ # the constructor method together with it's parameters,
29
+ # methods callable upon the component,
30
+ # events triggered by the component,
31
+ # HTML markup the component generates and
32
+ # Ajax-Requests the component fires.
33
+
34
+ ### Socket markup
35
+
36
+ iQjax must be initialized on a DOM node with a `data-iqjax` attribute set.
37
+ The attribute must contain a selector for the container the Ajax-response
38
+ should be put in.
39
+
40
+
41
+ ... TO BE DONE ...
@@ -0,0 +1,10 @@
1
+ require 'bundler'
2
+ require 'rake/testtask'
3
+
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ Rake::TestTask.new do |t|
7
+ t.test_files = FileList['test/**/*.rb']
8
+ end
9
+
10
+ task :default => [:test]
@@ -0,0 +1,21 @@
1
+ require File.expand_path('../lib/iqjax/version', __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "iqjax"
5
+ s.version = Iqjax::VERSION
6
+ s.platform = Gem::Platform::RUBY
7
+ s.authors = ["FND", "Till Schulte-Coerne"]
8
+ s.email = ["", "till.schulte-coerne@innoq.com"]
9
+ s.homepage = "http://github.com/innoq/iqjax"
10
+ s.summary = "iQjax - a JS library for real unobtrusive Ajax"
11
+ s.description = s.summary
12
+ s.extra_rdoc_files = ['README.md', 'LICENSE']
13
+
14
+ s.add_dependency "bundler"
15
+ s.add_dependency "railties", ">= 3.2.0", "< 5.0"
16
+
17
+ s.files = %w(LICENSE README.md Rakefile iqjax.gemspec iqjax.js) + Dir.glob("{lib,vendor,test}/**/*")
18
+ s.test_files = Dir.glob("{test}/**/*")
19
+
20
+ s.require_path = 'lib'
21
+ end
@@ -0,0 +1,200 @@
1
+ /*jslint vars: true, unparam: true, browser: true, white: true */
2
+ /*global jQuery */
3
+
4
+ /* Copyright 2012 innoQ Deutschland GmbH
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License");
7
+ you may not use this file except in compliance with the License.
8
+ You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ See the License for the specific language governing permissions and
16
+ limitations under the License.
17
+ */
18
+
19
+ // dynamic loading of content
20
+ // inspired by https://github.com/defunkt/jquery-iqjax - though minus the
21
+ // History API and based on Rails's data-remote functionality and error handling
22
+ (function($) {
23
+
24
+ "use strict";
25
+
26
+ var iqjax_uri, requestMethod;
27
+
28
+ var IQjax = function(context, target) {
29
+ // NB: context and target must not be descendants of each other
30
+ this.context = context;
31
+ this.target = target;
32
+ this.indicator = $(".indicator", target);
33
+ if(!this.indicator.length) {
34
+ this.indicator = $('<div class="indicator hidden"></div>');
35
+ }
36
+
37
+ // NB: redefining `this` in event handlers (via `proxy`) is dangerously
38
+ // misleading, but avoids non-standard function signatures for event
39
+ // handlers - plus for instance methods, it's actually more intuitive
40
+
41
+ // cancel buttons
42
+ var uri = document.location.toString().split("#")[0]; // XXX: use as selector makes for brittle heuristic?
43
+ this.target.on("click", 'a[href="' + uri + '"]', $.proxy(this.onCancel, this));
44
+
45
+ var selector = "a[data-remote], form[data-remote]";
46
+ var handlers = {
47
+ "ajax:beforeSend": $.proxy(this.beforeSend, this),
48
+ "ajax:success": $.proxy(this.onSuccess, this),
49
+ "ajax:error": $.proxy(this.onError, this)
50
+ };
51
+ this.context.add(this.target).on(handlers, selector);
52
+
53
+ // dirty state: protect against accidental dismissal
54
+ var self = this;
55
+ this.target.on("change", "input, textarea, select", function(ev) {
56
+ self.dirty = true;
57
+ });
58
+ };
59
+ $.extend(IQjax.prototype, {
60
+ onCancel: function(ev) {
61
+ if(!this.checkDirty(ev)) {
62
+ this.reset();
63
+ }
64
+ ev.preventDefault();
65
+ },
66
+ beforeSend: function(ev, xhr, settings) {
67
+ if(this.checkDirty(ev)) {
68
+ return false;
69
+ }
70
+
71
+ var contextAction = $.contains(this.context[0], ev.currentTarget); // TODO: rename -- XXX: hacky?
72
+ if(contextAction) {
73
+ this.reset();
74
+ }
75
+ $(ev.currentTarget).addClass("active");
76
+ this.target.prepend(this.indicator);
77
+ this.indicator.show();
78
+ settings.url = iqjax_uri(settings.url);
79
+ this.target.children().not(this.indicator).css("opacity", 0.5);
80
+ },
81
+ onSuccess: function(ev, data, status, xhr) {
82
+ if(!ev.currentTarget.parentNode) {
83
+ // FIXME: it's not clear under what circumstances this occurs;
84
+ // apparently, for reasons yet unknown, this event is erroneously
85
+ // triggered twice (and not all such duplicate events are
86
+ // intercepted by this hack, e.g. DELETE operations)
87
+ return;
88
+ }
89
+
90
+ var targetAction = $.contains(this.target[0], ev.currentTarget), // TODO: rename -- XXX: hacky?
91
+ el = $(ev.currentTarget),
92
+ reqMethod = "GET",
93
+ origin;
94
+
95
+ if(el.is("form")) {
96
+ if(targetAction) {
97
+ this.onUpdate.call(this, data, status, xhr); // TODO: should trigger event
98
+ return;
99
+ } else {
100
+ reqMethod = requestMethod(el);
101
+ origin = el.closest(".iqjax-entity"); // TODO: document
102
+ }
103
+ }
104
+
105
+ if(origin && reqMethod === "DELETE") {
106
+ this.indicator.hide();
107
+ origin.slideUp($.proxy(origin.remove, origin));
108
+ } else {
109
+ this.display(data);
110
+ }
111
+ },
112
+ onError: function(ev, xhr, error, exc) {
113
+ var cType = xhr.getResponseHeader("Content-Type"),
114
+ isHTML = cType ? cType.match(/\btext\/html\b/) : false;
115
+ this.display(xhr.responseText || error, !isHTML);
116
+ },
117
+ onUpdate: function(data, status, xhr) {
118
+ var src = xhr.getResponseHeader("X-IQJAX"), // TODO: document
119
+ item = $("<div />").html(data).find("#" + src),
120
+ origin = $(".active", this.context),
121
+ container = origin.closest("[data-iqjax-collection]"), // TODO: document
122
+ form = $("form.active", this.target),
123
+ reqMethod = requestMethod(form);
124
+
125
+ this.target.empty();
126
+
127
+ container = container.jquery ? container : $(container);
128
+ // account for nested update targets -- XXX: hacky!?
129
+ if(reqMethod === "PUT" && origin.closest(".iqjax-entity")[0] === container[0]) {
130
+ container = container.parent().closest("[data-iqjax-collection]");
131
+ }
132
+ container = $(container.data("iqjax-collection"));
133
+
134
+ if(reqMethod === "GET") { // multi-step forms
135
+ this.display(data);
136
+ } else {
137
+ this.reset();
138
+ }
139
+
140
+ var el = $("#" + src, self.context);
141
+ if(el.length) {
142
+ el.replaceWith(item);
143
+ } else { // new
144
+ container.append(item);
145
+ }
146
+
147
+ item.addClass("success").removeClass("success", "glacial");
148
+ this.context.trigger("iqjax:update", { item: item, doc: data });
149
+ },
150
+ reset: function() {
151
+ this.target.empty();
152
+ this.dirty = false;
153
+ $(".active", this.context).removeClass("active");
154
+ },
155
+ display: function(txt, plain) {
156
+ this.indicator.hide();
157
+ this.target[plain ? "text" : "html"](txt).prepend(this.indicator);
158
+ // intercept interactions to prevent full page reload
159
+ $("form", this.target).attr("data-remote", true);
160
+ this.context.trigger("iqjax:content", { iqjax: this });
161
+ },
162
+ // dirty state: protect against accidental dismissal
163
+ dirtyMsg: "Es gibt ungespeicherte Änderungen - fortfahren (Änderungen werden verworfen)?", // TODO: rephrase
164
+ checkDirty: function(ev) {
165
+ var isSubmit = $(ev.currentTarget).is("form");
166
+ if(this.dirty && !isSubmit) {
167
+ if(confirm(this.dirtyMsg)) {
168
+ this.dirty = false;
169
+ } else {
170
+ return true;
171
+ }
172
+ }
173
+ }
174
+ });
175
+
176
+ // hack to prevent cache confusion (browser caches should distinguish between
177
+ // iQjax and non-iQjax requests) - it'd be more elegant to modify the jqXHR's
178
+ // `data` property here, but it appears that's being overridden by jQuery later
179
+ iqjax_uri = function(uri) {
180
+ return uri + (uri.indexOf("?") === -1 ? "?" : "&") + "_iqjax=1";
181
+ };
182
+
183
+ requestMethod = function(form) {
184
+ var m = $("input[name=_method]", form).val() || form.attr("method") || "GET";
185
+ return m.toUpperCase();
186
+ };
187
+
188
+ // uses dynamic in-page loading for child elements matching `a[data-remote]`
189
+ // options.target is the DOM element within which contents are to be displayed
190
+ // (defaults to selector contained in context element's `data-iqjax` attribute)
191
+ $.fn.iqjax = function(options) {
192
+ options = options || {};
193
+ return this.each(function(i, node) {
194
+ var context = $(this),
195
+ target = $(options.target || context.data("iqjax")); // TODO: document
196
+ new IQjax(context, target);
197
+ });
198
+ };
199
+
200
+ }(jQuery));
@@ -0,0 +1,2 @@
1
+ require 'iqjax/version'
2
+ require 'iqjax/engine'
@@ -0,0 +1,5 @@
1
+ module Iqjax
2
+ class Engine < ::Rails::Engine
3
+ end
4
+ end
5
+
@@ -0,0 +1,4 @@
1
+ module Iqjax
2
+ VERSION = "0.1.0"
3
+ end
4
+
@@ -0,0 +1,55 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <meta charset="utf-8">
6
+ <title>iQjax QUnit Test Suite</title>
7
+ <link rel="stylesheet" href="lib/qunit.css">
8
+ </head>
9
+
10
+ <body>
11
+ <h1 id="qunit-header">QUnit Test Suite</h1>
12
+ <h2 id="qunit-banner"></h2>
13
+ <div id="qunit-testrunner-toolbar"></div>
14
+ <h2 id="qunit-userAgent"></h2>
15
+ <ol id="qunit-tests"></ol>
16
+ <div id="qunit-fixture">
17
+
18
+ <div id="basic" data-iqjax-collection="#other-item-list">
19
+
20
+ <div data-iqjax-collection="#item-list">
21
+ <p><a href="/items/new" id="new" data-remote="true">New</a></p>
22
+ <ul id="item-list">
23
+ <li id="item1">
24
+ <a href="/items/1/edit" data-remote="true">Edit</a>
25
+ </li>
26
+ <li id="item2">
27
+ <a href="/items/2/edit" data-remote="true">Edit</a>
28
+ </li>
29
+ <li id="dummy-item-pointing-to-item3">
30
+ <a href="/items/3/edit" data-remote="true">Edit</a>
31
+ </li>
32
+ </ul>
33
+ </div>
34
+
35
+ <ul id="other-item-list">
36
+ <li id="item3">Change me!</li>
37
+ </ul>
38
+
39
+ </div>
40
+
41
+ <div id="my-container1"></div>
42
+
43
+ </div>
44
+
45
+ <script src="lib/qunit.js"></script>
46
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
47
+ <script src="lib/jquery.mockjax.js"></script>
48
+
49
+ <script src="../vendor/jquery_ujs.js"></script>
50
+
51
+ <script src="../iqjax.js"></script>
52
+ <script src="tests.js"></script>
53
+ </body>
54
+
55
+ </html>
@@ -0,0 +1,382 @@
1
+ /*!
2
+ * MockJax - jQuery Plugin to Mock Ajax requests
3
+ *
4
+ * Version: 1.4.0
5
+ * Released: 2011-02-04
6
+ * Source: http://github.com/appendto/jquery-mockjax
7
+ * Docs: http://enterprisejquery.com/2010/07/mock-your-ajax-requests-with-mockjax-for-rapid-development
8
+ * Plugin: mockjax
9
+ * Author: Jonathan Sharp (http://jdsharp.com)
10
+ * License: MIT,GPL
11
+ *
12
+ * Copyright (c) 2010 appendTo LLC.
13
+ * Dual licensed under the MIT or GPL licenses.
14
+ * http://appendto.com/open-source-licenses
15
+ */
16
+ (function($) {
17
+ var _ajax = $.ajax,
18
+ mockHandlers = [];
19
+
20
+ function parseXML(xml) {
21
+ if ( window['DOMParser'] == undefined && window.ActiveXObject ) {
22
+ DOMParser = function() { };
23
+ DOMParser.prototype.parseFromString = function( xmlString ) {
24
+ var doc = new ActiveXObject('Microsoft.XMLDOM');
25
+ doc.async = 'false';
26
+ doc.loadXML( xmlString );
27
+ return doc;
28
+ };
29
+ }
30
+
31
+ try {
32
+ var xmlDoc = ( new DOMParser() ).parseFromString( xml, 'text/xml' );
33
+ if ( $.isXMLDoc( xmlDoc ) ) {
34
+ var err = $('parsererror', xmlDoc);
35
+ if ( err.length == 1 ) {
36
+ throw('Error: ' + $(xmlDoc).text() );
37
+ }
38
+ } else {
39
+ throw('Unable to parse XML');
40
+ }
41
+ } catch( e ) {
42
+ var msg = ( e.name == undefined ? e : e.name + ': ' + e.message );
43
+ $(document).trigger('xmlParseError', [ msg ]);
44
+ return undefined;
45
+ }
46
+ return xmlDoc;
47
+ }
48
+
49
+ $.extend({
50
+ ajax: function(origSettings) {
51
+ var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings),
52
+ mock = false;
53
+ // Iterate over our mock handlers (in registration order) until we find
54
+ // one that is willing to intercept the request
55
+ $.each(mockHandlers, function(k, v) {
56
+ if ( !mockHandlers[k] ) {
57
+ return;
58
+ }
59
+ var m = null;
60
+ // If the mock was registered with a function, let the function decide if we
61
+ // want to mock this request
62
+ if ( $.isFunction(mockHandlers[k]) ) {
63
+ m = mockHandlers[k](s);
64
+ } else {
65
+ m = mockHandlers[k];
66
+ // Inspect the URL of the request and check if the mock handler's url
67
+ // matches the url for this ajax request
68
+ if ( $.isFunction(m.url.test) ) {
69
+ // The user provided a regex for the url, test it
70
+ if ( !m.url.test( s.url ) ) {
71
+ m = null;
72
+ }
73
+ } else {
74
+ // Look for a simple wildcard '*' or a direct URL match
75
+ var star = m.url.indexOf('*');
76
+ if ( ( m.url != '*' && m.url != s.url && star == -1 ) ||
77
+ ( star > -1 && m.url.substr(0, star) != s.url.substr(0, star) ) ) {
78
+ // The url we tested did not match the wildcard *
79
+ m = null;
80
+ }
81
+ }
82
+ if ( m ) {
83
+ // Inspect the data submitted in the request (either POST body or GET query string)
84
+ if ( m.data && s.data ) {
85
+ var identical = false;
86
+ // Deep inspect the identity of the objects
87
+ (function ident(mock, live) {
88
+ // Test for situations where the data is a querystring (not an object)
89
+ if (typeof live === 'string') {
90
+ // Querystring may be a regex
91
+ identical = $.isFunction( mock.test ) ? mock.test(live) : mock == live;
92
+ return identical;
93
+ }
94
+ $.each(mock, function(k, v) {
95
+ if ( live[k] === undefined ) {
96
+ identical = false;
97
+ return false;
98
+ } else {
99
+ identical = true;
100
+ if ( typeof live[k] == 'object' ) {
101
+ return ident(mock[k], live[k]);
102
+ } else {
103
+ if ( $.isFunction( mock[k].test ) ) {
104
+ identical = mock[k].test(live[k]);
105
+ } else {
106
+ identical = ( mock[k] == live[k] );
107
+ }
108
+ return identical;
109
+ }
110
+ }
111
+ });
112
+ })(m.data, s.data);
113
+ // They're not identical, do not mock this request
114
+ if ( identical == false ) {
115
+ m = null;
116
+ }
117
+ }
118
+ // Inspect the request type
119
+ if ( m && m.type && m.type != s.type ) {
120
+ // The request type doesn't match (GET vs. POST)
121
+ m = null;
122
+ }
123
+ }
124
+ }
125
+ if ( m ) {
126
+ mock = true;
127
+
128
+ // Handle console logging
129
+ var c = $.extend({}, $.mockjaxSettings, m);
130
+ if ( c.log && $.isFunction(c.log) ) {
131
+ c.log('MOCK ' + s.type.toUpperCase() + ': ' + s.url, $.extend({}, s));
132
+ }
133
+
134
+ var jsre = /=\?(&|$)/, jsc = (new Date()).getTime();
135
+
136
+ // Handle JSONP Parameter Callbacks, we need to replicate some of the jQuery core here
137
+ // because there isn't an easy hook for the cross domain script tag of jsonp
138
+ if ( s.dataType === "jsonp" ) {
139
+ if ( s.type.toUpperCase() === "GET" ) {
140
+ if ( !jsre.test( s.url ) ) {
141
+ s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?";
142
+ }
143
+ } else if ( !s.data || !jsre.test(s.data) ) {
144
+ s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
145
+ }
146
+ s.dataType = "json";
147
+ }
148
+
149
+ // Build temporary JSONP function
150
+ if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
151
+ jsonp = s.jsonpCallback || ("jsonp" + jsc++);
152
+
153
+ // Replace the =? sequence both in the query string and the data
154
+ if ( s.data ) {
155
+ s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
156
+ }
157
+
158
+ s.url = s.url.replace(jsre, "=" + jsonp + "$1");
159
+
160
+ // We need to make sure
161
+ // that a JSONP style response is executed properly
162
+ s.dataType = "script";
163
+
164
+ // Handle JSONP-style loading
165
+ window[ jsonp ] = window[ jsonp ] || function( tmp ) {
166
+ data = tmp;
167
+ success();
168
+ complete();
169
+ // Garbage collect
170
+ window[ jsonp ] = undefined;
171
+
172
+ try {
173
+ delete window[ jsonp ];
174
+ } catch(e) {}
175
+
176
+ if ( head ) {
177
+ head.removeChild( script );
178
+ }
179
+ };
180
+ }
181
+
182
+ var rurl = /^(\w+:)?\/\/([^\/?#]+)/,
183
+ parts = rurl.exec( s.url ),
184
+ remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);
185
+
186
+ // Test if we are going to create a script tag (if so, intercept & mock)
187
+ if ( s.dataType === "script" && s.type.toUpperCase() === "GET" && remote ) {
188
+ // Synthesize the mock request for adding a script tag
189
+ var callbackContext = origSettings && origSettings.context || s;
190
+
191
+ function success() {
192
+ // If a local callback was specified, fire it and pass it the data
193
+ if ( s.success ) {
194
+ s.success.call( callbackContext, ( m.response ? m.response.toString() : m.responseText || ''), status, {} );
195
+ }
196
+
197
+ // Fire the global callback
198
+ if ( s.global ) {
199
+ trigger( "ajaxSuccess", [{}, s] );
200
+ }
201
+ }
202
+
203
+ function complete() {
204
+ // Process result
205
+ if ( s.complete ) {
206
+ s.complete.call( callbackContext, {} , status );
207
+ }
208
+
209
+ // The request was completed
210
+ if ( s.global ) {
211
+ trigger( "ajaxComplete", [{}, s] );
212
+ }
213
+
214
+ // Handle the global AJAX counter
215
+ if ( s.global && ! --jQuery.active ) {
216
+ jQuery.event.trigger( "ajaxStop" );
217
+ }
218
+ }
219
+
220
+ function trigger(type, args) {
221
+ (s.context ? jQuery(s.context) : jQuery.event).trigger(type, args);
222
+ }
223
+
224
+ if ( m.response && $.isFunction(m.response) ) {
225
+ m.response(origSettings);
226
+ } else {
227
+ $.globalEval(m.responseText);
228
+ }
229
+ success();
230
+ complete();
231
+ return false;
232
+ }
233
+ mock = _ajax.call($, $.extend(true, {}, origSettings, {
234
+ // Mock the XHR object
235
+ xhr: function() {
236
+ // Extend with our default mockjax settings
237
+ m = $.extend({}, $.mockjaxSettings, m);
238
+
239
+ if ( m.contentType ) {
240
+ m.headers['content-type'] = m.contentType;
241
+ }
242
+
243
+ // Return our mock xhr object
244
+ return {
245
+ status: m.status,
246
+ readyState: 1,
247
+ open: function() { },
248
+ send: function() {
249
+ // This is a substitute for < 1.4 which lacks $.proxy
250
+ var process = (function(that) {
251
+ return function() {
252
+ return (function() {
253
+ // The request has returned
254
+ this.status = m.status;
255
+ this.readyState = 4;
256
+
257
+ // We have an executable function, call it to give
258
+ // the mock handler a chance to update it's data
259
+ if ( $.isFunction(m.response) ) {
260
+ m.response(origSettings);
261
+ }
262
+ // Copy over our mock to our xhr object before passing control back to
263
+ // jQuery's onreadystatechange callback
264
+ if ( s.dataType == 'json' && ( typeof m.responseText == 'object' ) ) {
265
+ this.responseText = JSON.stringify(m.responseText);
266
+ } else if ( s.dataType == 'xml' ) {
267
+ if ( typeof m.responseXML == 'string' ) {
268
+ this.responseXML = parseXML(m.responseXML);
269
+ } else {
270
+ this.responseXML = m.responseXML;
271
+ }
272
+ } else {
273
+ this.responseText = m.responseText;
274
+ }
275
+ // jQuery < 1.4 doesn't have onreadystate change for xhr
276
+ if ( $.isFunction(this.onreadystatechange) ) {
277
+ this.onreadystatechange( m.isTimeout ? 'timeout' : undefined );
278
+ }
279
+ }).apply(that);
280
+ };
281
+ })(this);
282
+
283
+ if ( m.proxy ) {
284
+ // We're proxying this request and loading in an external file instead
285
+ _ajax({
286
+ global: false,
287
+ url: m.proxy,
288
+ type: m.proxyType,
289
+ data: m.data,
290
+ dataType: s.dataType,
291
+ complete: function(xhr, txt) {
292
+ m.responseXML = xhr.responseXML;
293
+ m.responseText = xhr.responseText;
294
+ this.responseTimer = setTimeout(process, m.responseTime || 0);
295
+ }
296
+ });
297
+ } else {
298
+ // type == 'POST' || 'GET' || 'DELETE'
299
+ if ( s.async === false ) {
300
+ // TODO: Blocking delay
301
+ process();
302
+ } else {
303
+ this.responseTimer = setTimeout(process, m.responseTime || 50);
304
+ }
305
+ }
306
+ },
307
+ abort: function() {
308
+ clearTimeout(this.responseTimer);
309
+ },
310
+ setRequestHeader: function() { },
311
+ getResponseHeader: function(header) {
312
+ // 'Last-modified', 'Etag', 'content-type' are all checked by jQuery
313
+ if ( m.headers && m.headers[header] ) {
314
+ // Return arbitrary headers
315
+ return m.headers[header];
316
+ } else if ( header.toLowerCase() == 'last-modified' ) {
317
+ return m.lastModified || (new Date()).toString();
318
+ } else if ( header.toLowerCase() == 'etag' ) {
319
+ return m.etag || '';
320
+ } else if ( header.toLowerCase() == 'content-type' ) {
321
+ return m.contentType || 'text/plain';
322
+ }
323
+ },
324
+ getAllResponseHeaders: function() {
325
+ var headers = '';
326
+ $.each(m.headers, function(k, v) {
327
+ headers += k + ': ' + v + "\n";
328
+ });
329
+ return headers;
330
+ }
331
+ };
332
+ }
333
+ }));
334
+ return false;
335
+ }
336
+ });
337
+ // We don't have a mock request, trigger a normal request
338
+ if ( !mock ) {
339
+ return _ajax.apply($, arguments);
340
+ } else {
341
+ return mock;
342
+ }
343
+ }
344
+ });
345
+
346
+ $.mockjaxSettings = {
347
+ //url: null,
348
+ //type: 'GET',
349
+ log: function(msg) {
350
+ window['console'] && window.console.log && window.console.log(msg);
351
+ },
352
+ status: 200,
353
+ responseTime: 500,
354
+ isTimeout: false,
355
+ contentType: 'text/plain',
356
+ response: '',
357
+ responseText: '',
358
+ responseXML: '',
359
+ proxy: '',
360
+ proxyType: 'GET',
361
+
362
+ lastModified: null,
363
+ etag: '',
364
+ headers: {
365
+ etag: 'IJF@H#@923uf8023hFO@I#H#',
366
+ 'content-type' : 'text/plain'
367
+ }
368
+ };
369
+
370
+ $.mockjax = function(settings) {
371
+ var i = mockHandlers.length;
372
+ mockHandlers[i] = settings;
373
+ return i;
374
+ };
375
+ $.mockjaxClear = function(i) {
376
+ if ( arguments.length == 1 ) {
377
+ mockHandlers[i] = null;
378
+ } else {
379
+ mockHandlers = [];
380
+ }
381
+ };
382
+ })(jQuery);