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 +13 -0
- data/README.md +41 -0
- data/Rakefile +10 -0
- data/iqjax.gemspec +21 -0
- data/iqjax.js +200 -0
- data/lib/iqjax.rb +2 -0
- data/lib/iqjax/engine.rb +5 -0
- data/lib/iqjax/version.rb +4 -0
- data/test/index.html +55 -0
- data/test/lib/jquery.mockjax.js +382 -0
- data/test/lib/qunit.css +226 -0
- data/test/lib/qunit.js +1597 -0
- data/test/lib/run-qunit.js +90 -0
- data/test/test.rb +36 -0
- data/test/tests.js +175 -0
- data/vendor/assets/javascripts/iqjax.js +200 -0
- data/vendor/jquery_ujs.js +367 -0
- metadata +128 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
var fs = require("fs");
|
2
|
+
|
3
|
+
function ping() {
|
4
|
+
fs.write("/dev/stdout", ".", "w");
|
5
|
+
}
|
6
|
+
|
7
|
+
function snapshot(page, filepath) {
|
8
|
+
page.viewportSize = { width: 1024, height: 768 };
|
9
|
+
page.render(filepath);
|
10
|
+
}
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Wait until the test condition is true or a timeout occurs. Useful for waiting
|
14
|
+
* on a server response or for a ui change (fadeIn, etc.) to occur.
|
15
|
+
*
|
16
|
+
* @param testFx javascript condition that evaluates to a boolean,
|
17
|
+
* it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
|
18
|
+
* as a callback function.
|
19
|
+
* @param onReady what to do when testFx condition is fulfilled,
|
20
|
+
* it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
|
21
|
+
* as a callback function.
|
22
|
+
* @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
|
23
|
+
*/
|
24
|
+
function waitFor(testFx, onReady, timeOutMillis) {
|
25
|
+
var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3001, //< Default Max Timout is 3s
|
26
|
+
start = new Date().getTime(),
|
27
|
+
condition = false,
|
28
|
+
interval = setInterval(function() {
|
29
|
+
ping();
|
30
|
+
if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) {
|
31
|
+
// If not time-out yet and condition not yet fulfilled
|
32
|
+
condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
|
33
|
+
} else {
|
34
|
+
if(!condition) {
|
35
|
+
// If condition still not fulfilled (timeout but condition is 'false')
|
36
|
+
console.log("'waitFor()' timeout");
|
37
|
+
phantom.exit(1);
|
38
|
+
} else {
|
39
|
+
// Condition fulfilled (timeout and/or condition is 'true')
|
40
|
+
console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms.");
|
41
|
+
typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled
|
42
|
+
clearInterval(interval); //< Stop this interval
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}, 25); //< repeat check every 25 ms
|
46
|
+
ping();
|
47
|
+
};
|
48
|
+
|
49
|
+
|
50
|
+
if (phantom.args.length === 0 || phantom.args.length > 2) {
|
51
|
+
console.log('Usage: run-qunit.js URL');
|
52
|
+
phantom.exit(1);
|
53
|
+
}
|
54
|
+
|
55
|
+
var page = new WebPage();
|
56
|
+
|
57
|
+
// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
|
58
|
+
page.onConsoleMessage = function(msg) {
|
59
|
+
console.log(msg);
|
60
|
+
};
|
61
|
+
|
62
|
+
page.open(phantom.args[0], function(status){
|
63
|
+
if (status !== "success") {
|
64
|
+
console.log("Unable to access network");
|
65
|
+
phantom.exit(1);
|
66
|
+
} else {
|
67
|
+
waitFor(function(){
|
68
|
+
return page.evaluate(function(){
|
69
|
+
var el = document.getElementById('qunit-testresult');
|
70
|
+
if (el && el.innerText.match('completed')) {
|
71
|
+
return true;
|
72
|
+
}
|
73
|
+
return false;
|
74
|
+
});
|
75
|
+
}, function(){
|
76
|
+
var failedNum = page.evaluate(function(){
|
77
|
+
var el = document.getElementById('qunit-testresult');
|
78
|
+
console.log(el.innerText);
|
79
|
+
try {
|
80
|
+
return el.getElementsByClassName('failed')[0].innerHTML;
|
81
|
+
} catch (e) { }
|
82
|
+
return 10000;
|
83
|
+
});
|
84
|
+
if(parseInt(failedNum, 10) > 0) {
|
85
|
+
snapshot(page, "snapshot.png");
|
86
|
+
}
|
87
|
+
phantom.exit((parseInt(failedNum, 10) > 0) ? 1 : 0);
|
88
|
+
});
|
89
|
+
}
|
90
|
+
});
|
data/test/test.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Copyright 2012 innoQ Deutschland GmbH
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require 'test/unit'
|
18
|
+
|
19
|
+
class ClientTest < Test::Unit::TestCase
|
20
|
+
|
21
|
+
def test_PhantomJS_availability
|
22
|
+
system("phantomjs --version")
|
23
|
+
assert $?.success?
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_client_side_test_suites
|
27
|
+
assert _run("index.html")
|
28
|
+
end
|
29
|
+
|
30
|
+
def _run(suite)
|
31
|
+
path = File.expand_path(File.dirname(__FILE__))
|
32
|
+
system("cd #{path} && phantomjs lib/run-qunit.js #{suite}")
|
33
|
+
return $?.success?
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/test/tests.js
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
/*jslint vars: true, unparam: true, browser: true, white: true */
|
2
|
+
/*global jQuery, QUnit, module, test, ok, strictEqual, raises */
|
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
|
+
(function($) {
|
20
|
+
|
21
|
+
"use strict";
|
22
|
+
|
23
|
+
module("iQjax", {
|
24
|
+
|
25
|
+
setup: function() {
|
26
|
+
var fixtures = $("#qunit-fixture");
|
27
|
+
this.fixturesHtml = fixtures.html();
|
28
|
+
fixtures.empty(); // prevents duplicate IDs
|
29
|
+
this.ctx = $("<div><p /></div>").appendTo(document.body);
|
30
|
+
|
31
|
+
$.mockjaxSettings['log'] = function(msg) { };
|
32
|
+
|
33
|
+
// Mock /items/:id/edit
|
34
|
+
$.mockjax(function(settings) {
|
35
|
+
var match = settings.url.match(/^\/items\/([0-9]+)\/edit$/);
|
36
|
+
if (match) {
|
37
|
+
return {
|
38
|
+
responseTime: 10,
|
39
|
+
responseText: '<form action="/items/' + match[1] +
|
40
|
+
'" method="POST"><input type="Submit" value="Save"></form>'
|
41
|
+
};
|
42
|
+
}
|
43
|
+
return;
|
44
|
+
});
|
45
|
+
// Mock /items/new
|
46
|
+
$.mockjax({
|
47
|
+
url: "/items/new",
|
48
|
+
responseText: '<form action="/items" method="POST"><input type="Submit" value="Save"></form>'
|
49
|
+
});
|
50
|
+
// Mock /items/:id and /items (the #create action)
|
51
|
+
$.mockjax(function(settings) {
|
52
|
+
var match = settings.url.match(/^\/items(\/([0-9]+))?(\?.*)?$/);
|
53
|
+
if (match) {
|
54
|
+
var itemName = match[2] || "new";
|
55
|
+
return {
|
56
|
+
responseTime: 10,
|
57
|
+
headers: {
|
58
|
+
"X-IQJAX": "item" + itemName
|
59
|
+
},
|
60
|
+
responseText: '<li id="item' + itemName +
|
61
|
+
'">Item from Server</li>'
|
62
|
+
};
|
63
|
+
}
|
64
|
+
return;
|
65
|
+
});
|
66
|
+
},
|
67
|
+
|
68
|
+
teardown: function() {
|
69
|
+
$("#qunit-fixture").html(this.fixturesHtml);
|
70
|
+
this.ctx.remove();
|
71
|
+
QUnit.reset();
|
72
|
+
}
|
73
|
+
|
74
|
+
});
|
75
|
+
|
76
|
+
test("uses container for result of AJAX call", function() {
|
77
|
+
this.ctx.html(this.fixturesHtml);
|
78
|
+
var root = $("#basic", this.ctx),
|
79
|
+
target = $("#my-container1");
|
80
|
+
|
81
|
+
root.iqjax({ target: "#my-container1" }).bind({
|
82
|
+
"iqjax:content": function() {
|
83
|
+
QUnit.start();
|
84
|
+
strictEqual($("form input[type=submit]", target).length, 1,
|
85
|
+
"target container should contain a form on `iqjax:content`");
|
86
|
+
$("form", target).submit();
|
87
|
+
QUnit.stop();
|
88
|
+
},
|
89
|
+
"iqjax:update": function() {
|
90
|
+
strictEqual(target.children().length, 0,
|
91
|
+
"target container should be empty on `iqjax:update`");
|
92
|
+
strictEqual($("#item1", root).text(), "Item from Server",
|
93
|
+
"collection item should be replaced by the item from the server on `iqjax:update`");
|
94
|
+
QUnit.start();
|
95
|
+
}
|
96
|
+
});
|
97
|
+
|
98
|
+
root.find("#item1 a").click();
|
99
|
+
QUnit.stop();
|
100
|
+
});
|
101
|
+
|
102
|
+
test("appends new items to the list", function() {
|
103
|
+
this.ctx.html(this.fixturesHtml);
|
104
|
+
var root = $("#basic", this.ctx),
|
105
|
+
target = $("#my-container1");
|
106
|
+
|
107
|
+
root.iqjax({ target: "#my-container1" }).bind({
|
108
|
+
"iqjax:content": function() {
|
109
|
+
QUnit.start();
|
110
|
+
strictEqual($("form input[type=submit]", target).length, 1,
|
111
|
+
"target container should contain the form");
|
112
|
+
$("form", target).submit();
|
113
|
+
QUnit.stop();
|
114
|
+
},
|
115
|
+
"iqjax:update": function() {
|
116
|
+
strictEqual($("#itemnew", root).text(), "Item from Server");
|
117
|
+
QUnit.start();
|
118
|
+
}
|
119
|
+
});
|
120
|
+
|
121
|
+
root.find("#new").click();
|
122
|
+
QUnit.stop();
|
123
|
+
|
124
|
+
});
|
125
|
+
|
126
|
+
test("replaces items outside the collection scope", function() {
|
127
|
+
this.ctx.html(this.fixturesHtml);
|
128
|
+
var root = $("#basic", this.ctx),
|
129
|
+
target = $("#my-container1");
|
130
|
+
|
131
|
+
var oldContent = $("#item-list", root).text();
|
132
|
+
root.iqjax({ target: "#my-container1" }).bind({
|
133
|
+
"iqjax:content": function() {
|
134
|
+
QUnit.start();
|
135
|
+
$("form", target).submit();
|
136
|
+
QUnit.stop();
|
137
|
+
},
|
138
|
+
"iqjax:update": function() {
|
139
|
+
strictEqual($("#other-item-list #item3", root).text(), "Item from Server");
|
140
|
+
strictEqual($("#item-list", root).text(), oldContent);
|
141
|
+
QUnit.start();
|
142
|
+
}
|
143
|
+
});
|
144
|
+
|
145
|
+
root.find("#dummy-item-pointing-to-item3 a").click();
|
146
|
+
QUnit.stop();
|
147
|
+
|
148
|
+
});
|
149
|
+
|
150
|
+
test("replaces element with result of iQjax call", function() {
|
151
|
+
return; // FIXME: TEST DISABLED
|
152
|
+
this.ctx.html(this.fixturesHtml);
|
153
|
+
var root = $("#basic", this.ctx);
|
154
|
+
|
155
|
+
root.iqjax({
|
156
|
+
"iqjax:content": function() {
|
157
|
+
QUnit.start();
|
158
|
+
// #item1 should be replaced by the form
|
159
|
+
strictEqual($("form input[type=submit]", root).length, 1);
|
160
|
+
$("form", root).submit();
|
161
|
+
QUnit.stop();
|
162
|
+
},
|
163
|
+
"iqjax:update": function() {
|
164
|
+
QUnit.start();
|
165
|
+
// form should be replaced by the "new" li element
|
166
|
+
strictEqual($("#item1", root).text(), "Item from Server");
|
167
|
+
}
|
168
|
+
|
169
|
+
});
|
170
|
+
|
171
|
+
root.find("#item1 a").click();
|
172
|
+
QUnit.stop();
|
173
|
+
});
|
174
|
+
|
175
|
+
}(jQuery));
|
@@ -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));
|