ballonizer 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/examples/ballonizer_app/config.ru +59 -0
- data/examples/ballonizer_app/index.html +159 -0
- data/examples/ballonizer_js_module/index.html +196 -0
- data/lib/assets/javascripts/ballonizer.js +482 -0
- data/lib/assets/stylesheets/ballonizer.css +78 -0
- data/lib/ballonizer.rb +201 -36
- data/spec/ballonizer_spec.rb +153 -2
- data/spec/javascripts/ballonizer_spec.js +568 -0
- data/spec/javascripts/fixtures/ballonized-xkcd-with-anchor-in-image.html +163 -0
- data/spec/javascripts/fixtures/ballonized-xkcd-with-ballons.html +163 -0
- data/spec/javascripts/fixtures/ballonized-xkcd-without-ballons.html +163 -0
- data/spec/javascripts/fixtures/xkcd.css +191 -0
- data/spec/javascripts/helpers/jasmine-jquery.js +660 -0
- data/spec/javascripts/helpers/jquery.simulate-ext.js +32 -0
- data/spec/javascripts/helpers/jquery.simulate.drag-n-drop.js +583 -0
- data/spec/javascripts/helpers/jquery.simulate.js +328 -0
- data/spec/javascripts/support/jasmine.yml +99 -0
- data/vendor/assets/javascripts/jquery-2.0.1.js +8837 -0
- data/vendor/assets/javascripts/jquery-ui-1.10.3.custom.min.js +6 -0
- data/vendor/assets/javascripts/jquery.json-2.4.min.js +24 -0
- data/vendor/assets/stylesheets/ui-lightness/images/animated-overlay.gif +0 -0
- data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
- data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- data/vendor/assets/stylesheets/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- data/vendor/assets/stylesheets/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
- data/vendor/assets/stylesheets/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
- data/vendor/assets/stylesheets/ui-lightness/images/ui-icons_ef8c08_256x240.png +0 -0
- data/vendor/assets/stylesheets/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
- data/vendor/assets/stylesheets/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
- data/vendor/assets/stylesheets/ui-lightness/jquery-ui-1.10.3.custom.min.css +5 -0
- metadata +51 -3
@@ -0,0 +1,568 @@
|
|
1
|
+
// The order of the requires bellow is important
|
2
|
+
//= require jquery-2.0.1.js
|
3
|
+
//= require jquery-ui-1.10.3.custom.min.js
|
4
|
+
//= require jquery.json-2.4.min.js
|
5
|
+
//= require jquery.simulate.js
|
6
|
+
//= require jquery.simulate-ext.js
|
7
|
+
//= require jquery.simulate.drag-n-drop.js
|
8
|
+
|
9
|
+
describe("Ballonizer", function () {
|
10
|
+
/* jshint strict: false */
|
11
|
+
// We disable the strict mode here because the libs included
|
12
|
+
// with "//= require" break the tests if with strict mode
|
13
|
+
|
14
|
+
// Variables pre-set before each test
|
15
|
+
var actionFormURL,
|
16
|
+
imageToBallonizeCSSSelector,
|
17
|
+
initialBallonText,
|
18
|
+
instance;
|
19
|
+
|
20
|
+
beforeEach(function () {
|
21
|
+
actionFormURL = "/action/path/to/submit";
|
22
|
+
imageToBallonizeCSSSelector = ".ballonizer_image_container";
|
23
|
+
initialBallonText = "double click to edit";
|
24
|
+
instance = null;
|
25
|
+
|
26
|
+
this.addMatchers({
|
27
|
+
toHaveBallonizerForm: function (actionFormURL) {
|
28
|
+
var lastBodyChild = this.actual.children().last();
|
29
|
+
|
30
|
+
expect(lastBodyChild).toBe("form.ballonizer_page_form" +
|
31
|
+
"[method='post'][action='" + actionFormURL + "']");
|
32
|
+
expect(lastBodyChild).toContain("input[type='submit']" +
|
33
|
+
"[name='ballonizer_submit']");
|
34
|
+
expect(lastBodyChild).toContain("input[type='hidden']" +
|
35
|
+
"[name='ballonizer_data']");
|
36
|
+
|
37
|
+
return true;
|
38
|
+
},
|
39
|
+
toBeAnEmptyObject: function () {
|
40
|
+
var obj = this.actual;
|
41
|
+
for (var p in obj) {
|
42
|
+
if (obj.hasOwnProperty(p)) {
|
43
|
+
this.message = "expected object to be empty but" +
|
44
|
+
"found the key '" + p + "'";
|
45
|
+
|
46
|
+
return false;
|
47
|
+
}
|
48
|
+
}
|
49
|
+
return true;
|
50
|
+
}
|
51
|
+
});
|
52
|
+
});
|
53
|
+
|
54
|
+
afterEach(function () {
|
55
|
+
// Remove left-overs created out of the #jasmine-fixtures container
|
56
|
+
$(".ballonizer_page_form").remove();
|
57
|
+
});
|
58
|
+
|
59
|
+
it("it's defined", function () {
|
60
|
+
expect(Ballonizer).toBeDefined();
|
61
|
+
});
|
62
|
+
|
63
|
+
describe("when exist images to ballonize in the document", function () {
|
64
|
+
describe("but none in the context", function () {
|
65
|
+
it("doesn't create a form", function () {
|
66
|
+
loadFixtures("ballonized-xkcd-without-ballons.html");
|
67
|
+
Ballonizer(actionFormURL, imageToBallonizeCSSSelector, $("#bottom"));
|
68
|
+
expect($("form.ballonizer_page_form")).not.toExist();
|
69
|
+
});
|
70
|
+
it(".getForm return null", function () {
|
71
|
+
instance = Ballonizer(actionFormURL, imageToBallonizeCSSSelector, $("#bottom"));
|
72
|
+
expect(instance.getForm()).toEqual(null);
|
73
|
+
});
|
74
|
+
});
|
75
|
+
describe("and they are in the context", function () {
|
76
|
+
it("create a hidden form as the last child of the context", function () {
|
77
|
+
loadFixtures("ballonized-xkcd-without-ballons.html");
|
78
|
+
Ballonizer(actionFormURL, imageToBallonizeCSSSelector, $("#jasmine-fixtures"));
|
79
|
+
expect($("#jasmine-fixtures")).toHaveBallonizerForm(actionFormURL);
|
80
|
+
expect($("form.ballonizer_page_form")).toBeHidden();
|
81
|
+
});
|
82
|
+
it("changes in .getForm result affect the node in the DOM", function () {
|
83
|
+
loadFixtures("ballonized-xkcd-without-ballons.html");
|
84
|
+
instance = Ballonizer(actionFormURL, imageToBallonizeCSSSelector, $("#jasmine-fixtures"));
|
85
|
+
instance.getForm().attr("method", "get");
|
86
|
+
expect($("form.ballonizer_page_form")).toHaveAttr("method", "get");
|
87
|
+
});
|
88
|
+
});
|
89
|
+
describe("when the ballonized image is double clicked", function () {
|
90
|
+
it("a new ballon will be created", function () {
|
91
|
+
loadFixtures("ballonized-xkcd-without-ballons.html");
|
92
|
+
instance = Ballonizer(actionFormURL, imageToBallonizeCSSSelector, $("#jasmine-fixtures"));
|
93
|
+
|
94
|
+
var ballonizedImg = $(".ballonizer_image_container img");
|
95
|
+
ballonizedImg.trigger("dblclick");
|
96
|
+
expect($(".ballonizer_ballon")).toExist();
|
97
|
+
});
|
98
|
+
it("the default action of clicking the image (if any) will not trigger", function () {
|
99
|
+
loadFixtures("ballonized-xkcd-with-anchor-in-image.html");
|
100
|
+
instance = Ballonizer(actionFormURL, imageToBallonizeCSSSelector, $("#jasmine-fixtures"));
|
101
|
+
var spyAnchor = spyOnEvent("#comic a", "click");
|
102
|
+
var ballonizedImg = $(".ballonizer_image_container img");
|
103
|
+
// In a real browser the dblclick event will be generated with one or
|
104
|
+
// two clicks events before it (http://api.jquery.com/dblclick/)
|
105
|
+
ballonizedImg.trigger("click");
|
106
|
+
expect(spyAnchor).toHaveBeenPrevented();
|
107
|
+
});
|
108
|
+
});
|
109
|
+
});
|
110
|
+
|
111
|
+
describe(".getBallonizedImageContainers", function () {
|
112
|
+
describe("when there's no image to ballonize", function () {
|
113
|
+
it("return an object without any keys", function () {
|
114
|
+
instance = Ballonizer(actionFormURL, imageToBallonizeCSSSelector, $("#jasmine-fixtures"));
|
115
|
+
var ballonizedImageContainers = instance.getBallonizedImageContainers();
|
116
|
+
expect(ballonizedImageContainers).toBeAnEmptyObject();
|
117
|
+
});
|
118
|
+
});
|
119
|
+
describe("when there's a image to ballonize", function () {
|
120
|
+
it("return an object with the img src as key and the object as value", function () {
|
121
|
+
loadFixtures("ballonized-xkcd-without-ballons.html");
|
122
|
+
instance = Ballonizer(actionFormURL, imageToBallonizeCSSSelector, $("#jasmine-fixtures"));
|
123
|
+
var ballonizedImageContainers = instance.getBallonizedImageContainers();
|
124
|
+
expect(ballonizedImageContainers.hasOwnProperty("http://imgs.xkcd.com/comics/cells.png")).toBeTruthy();
|
125
|
+
});
|
126
|
+
});
|
127
|
+
});
|
128
|
+
describe("BallonizedImageContainer", function () {
|
129
|
+
it("adds a hidden form for the ballon edition in the container", function () {
|
130
|
+
loadFixtures("ballonized-xkcd-without-ballons.html");
|
131
|
+
instance = Ballonizer(actionFormURL, imageToBallonizeCSSSelector, $("#jasmine-fixtures"));
|
132
|
+
expect($(".ballonizer_image_container")).toContain("form.ballonizer_image_form");
|
133
|
+
// What the value of method is not important, but the method attribute
|
134
|
+
// is required (http://www.w3.org/TR/html401/interact/forms.html#h-17.3)
|
135
|
+
expect($("form.ballonizer_image_form")).toHaveAttr("method");
|
136
|
+
expect($("form.ballonizer_image_form")).toBeHidden();
|
137
|
+
});
|
138
|
+
describe("when there's a image with ballons", function () {
|
139
|
+
it("getBallons return they", function () {
|
140
|
+
loadFixtures("ballonized-xkcd-with-ballons.html");
|
141
|
+
instance = Ballonizer(actionFormURL, imageToBallonizeCSSSelector, $("#jasmine-fixtures"));
|
142
|
+
var ballonizedImageContainers = instance.getBallonizedImageContainers();
|
143
|
+
var ballons = ballonizedImageContainers["http://imgs.xkcd.com/comics/cells.png"].getBallons();
|
144
|
+
|
145
|
+
expect(ballons.length).toEqual(2);
|
146
|
+
expect(ballons[0].getText()).toEqual("When you see a claim that a common drug or vitamin \"kills cancer cells in a petri dish\", keep in mind:");
|
147
|
+
expect(ballons[1].getText()).toEqual("So does a handgun.");
|
148
|
+
expect(ballons[0].getPositionAndSize()).toEqual(
|
149
|
+
{ left: 0, top: 0, width: 218, height: 82 }
|
150
|
+
);
|
151
|
+
expect(ballons[1].getPositionAndSize()).toEqual(
|
152
|
+
{ left: 21, top: 319, width: 170, height: 19 }
|
153
|
+
);
|
154
|
+
});
|
155
|
+
});
|
156
|
+
});
|
157
|
+
describe("InterfaceBallon", function () {
|
158
|
+
var containerWidth, containerHeight;
|
159
|
+
var getBallons = function () {
|
160
|
+
loadFixtures("ballonized-xkcd-with-ballons.html");
|
161
|
+
instance = Ballonizer(actionFormURL, imageToBallonizeCSSSelector, $("#jasmine-fixtures"));
|
162
|
+
var imageContainer = instance.getBallonizedImageContainers()["http://imgs.xkcd.com/comics/cells.png"],
|
163
|
+
ballons = imageContainer.getBallons(),
|
164
|
+
containerNode = imageContainer.getContainerNode();
|
165
|
+
|
166
|
+
containerWidth = containerNode.width();
|
167
|
+
containerHeight = containerNode.height();
|
168
|
+
|
169
|
+
return ballons;
|
170
|
+
};
|
171
|
+
var realWorldEvent = function (eventName, obj) {
|
172
|
+
switch (eventName) {
|
173
|
+
case "dblclick":
|
174
|
+
obj.mousedown().mouseup().click().mousedown().mouseup().click().dblclick();
|
175
|
+
break;
|
176
|
+
case "click":
|
177
|
+
obj.mousedown().mouseup().click();
|
178
|
+
break;
|
179
|
+
}
|
180
|
+
|
181
|
+
return obj;
|
182
|
+
};
|
183
|
+
var ballons = null;
|
184
|
+
|
185
|
+
it("creates a hidden edit ballon for each ballon", function () {
|
186
|
+
ballons = getBallons();
|
187
|
+
|
188
|
+
var imgContainer = ballons[0].getBallonizedImageContainer().getContainerNode();
|
189
|
+
var imgForm = $("form.ballonizer_image_form", imgContainer);
|
190
|
+
|
191
|
+
expect(imgForm.children().length).toEqual(2);
|
192
|
+
expect(imgForm.children()).toBeHidden();
|
193
|
+
expect(imgForm).toContain("textarea.ballonizer_edition_ballon");
|
194
|
+
|
195
|
+
var textfieldTexts = imgForm.children().toArray().map(function (e, ix) {
|
196
|
+
/* jshint unused: false */
|
197
|
+
return $(e).val();
|
198
|
+
}).sort();
|
199
|
+
var ballonTexts = jQuery.map(ballons, function (e, ix) {
|
200
|
+
/* jshint unused: false */
|
201
|
+
return e.getText();
|
202
|
+
}).sort();
|
203
|
+
|
204
|
+
expect(textfieldTexts).toEqual(ballonTexts);
|
205
|
+
});
|
206
|
+
it("initial state is 'initial'", function () {
|
207
|
+
ballons = getBallons();
|
208
|
+
expect(ballons[0].getState()).toEqual("initial");
|
209
|
+
expect(ballons[1].getState()).toEqual("initial");
|
210
|
+
});
|
211
|
+
describe("when in initial state", function () {
|
212
|
+
it("the ballon can be dragged (and this update the getBounds return)", function () {
|
213
|
+
ballons = getBallons();
|
214
|
+
// We are using the second ballon because the first
|
215
|
+
// can't be dragged in the x axis, it already occupy
|
216
|
+
// all the x axis
|
217
|
+
var oldBounds = ballons[1].getPositionAndSize();
|
218
|
+
ballons[1].getNode().simulate("drag-n-drop", { dx: -20, dy: -20 });
|
219
|
+
expect(ballons[1].getPositionAndSize()).toEqual({
|
220
|
+
left: (oldBounds.left - 20),
|
221
|
+
top: (oldBounds.top - 20),
|
222
|
+
width: oldBounds.width,
|
223
|
+
height: oldBounds.height
|
224
|
+
});
|
225
|
+
});
|
226
|
+
it("the ballon can be resized (and this update the getBounds return)", function () {
|
227
|
+
ballons = getBallons();
|
228
|
+
var oldBounds = ballons[0].getPositionAndSize();
|
229
|
+
var resizeHandle = $(".ui-icon-gripsmall-diagonal-se", ballons[0].getNode());
|
230
|
+
|
231
|
+
// The handle is hidden when not hovering over the ballon
|
232
|
+
// (which includes the handle)
|
233
|
+
resizeHandle.mouseover().simulate("drag-n-drop", {
|
234
|
+
dx: -20,
|
235
|
+
dy: -20
|
236
|
+
}).mouseout();
|
237
|
+
|
238
|
+
expect(ballons[0].getPositionAndSize()).toEqual({
|
239
|
+
left: oldBounds.left,
|
240
|
+
top: oldBounds.top,
|
241
|
+
width: oldBounds.width - 20,
|
242
|
+
height: oldBounds.height - 20
|
243
|
+
});
|
244
|
+
});
|
245
|
+
});
|
246
|
+
describe("when dblclicked 2x(mousedown + mouseup + click) + dblclick", function () {
|
247
|
+
it("alternate to edit mode initial -> edit)", function () {
|
248
|
+
ballons = getBallons();
|
249
|
+
realWorldEvent("dblclick", ballons[0].getNode());
|
250
|
+
expect(ballons[0].getState()).toEqual("edit");
|
251
|
+
});
|
252
|
+
it("hide the normal ballon and put the textarea (focused) in the place", function () {
|
253
|
+
ballons = getBallons();
|
254
|
+
|
255
|
+
// Test from the initial state
|
256
|
+
realWorldEvent("dblclick", ballons[0].getNode());
|
257
|
+
|
258
|
+
expect(ballons[0].getNormalNode()).toBeHidden();
|
259
|
+
expect(ballons[0].getEditionNode()).toBeVisible();
|
260
|
+
expect(ballons[0].getEditionNode()).toBeFocused();
|
261
|
+
|
262
|
+
expect(ballons[0].getEditionNode().attr("style")).toEqual(
|
263
|
+
ballons[0].getNormalNode().attr("style")
|
264
|
+
);
|
265
|
+
});
|
266
|
+
});
|
267
|
+
describe("when the focus is lost in edit mode", function () {
|
268
|
+
it("the ballon return to the initial mode with the new text", function () {
|
269
|
+
ballons = getBallons();
|
270
|
+
// Edit the text of the first ballon
|
271
|
+
realWorldEvent("dblclick", ballons[0].getNode());
|
272
|
+
var firstNewText = "This is the first ballon text";
|
273
|
+
ballons[0].getNode().val(firstNewText);
|
274
|
+
// simulate focus loss
|
275
|
+
ballons[0].getNode().blur();
|
276
|
+
expect(ballons[0].getNormalNode()).toBeVisible();
|
277
|
+
expect(ballons[0].getText()).toEqual(firstNewText);
|
278
|
+
expect(ballons[0].getNormalNode().text()).toEqual(firstNewText);
|
279
|
+
expect(ballons[0].getEditionNode()).toBeHidden();
|
280
|
+
});
|
281
|
+
describe("and the ballon is empty (or only with spaces)", function () {
|
282
|
+
it("the ballon is removed", function () {
|
283
|
+
ballons = getBallons();
|
284
|
+
var imgContainer = ballons[0].getBallonizedImageContainer();
|
285
|
+
|
286
|
+
realWorldEvent("dblclick", ballons[0].getNode());
|
287
|
+
ballons[0].getNode().val("");
|
288
|
+
ballons[0].getNode().blur();
|
289
|
+
|
290
|
+
ballons = imgContainer.getBallons();
|
291
|
+
|
292
|
+
expect(ballons.length).toEqual(1);
|
293
|
+
expect($(".ballonizer_ballon")).toHaveLength(1);
|
294
|
+
expect($("form.ballonizer_image_form > *")).toHaveLength(1);
|
295
|
+
});
|
296
|
+
});
|
297
|
+
});
|
298
|
+
describe("when the ballons change", function () {
|
299
|
+
var htmlUnescape = function (value) {
|
300
|
+
return (value)
|
301
|
+
.replace(/"/g, '"')
|
302
|
+
.replace(/'/g, "'")
|
303
|
+
.replace(/</g, '<')
|
304
|
+
.replace(/>/g, '>')
|
305
|
+
.replace(/&/g, '&');
|
306
|
+
};
|
307
|
+
|
308
|
+
describe("position", function () {
|
309
|
+
var changePosition = function (ballon, dx, dy) {
|
310
|
+
ballon.getNode().simulate("drag-n-drop", { dx: dx, dy: dy });
|
311
|
+
|
312
|
+
return ballon;
|
313
|
+
};
|
314
|
+
// We are using the second ballon in the tests because
|
315
|
+
// the first can't be dragged in the x axis, it already
|
316
|
+
// occupy all the x axis
|
317
|
+
it("the button to submit change appears", function () {
|
318
|
+
ballons = getBallons();
|
319
|
+
changePosition(ballons[1], -20, -20);
|
320
|
+
expect($("form.ballonizer_page_form > input[name='ballonizer_submit']")).toBeVisible();
|
321
|
+
});
|
322
|
+
it("the serialize methods shows the new values", function () {
|
323
|
+
ballons = getBallons();
|
324
|
+
var ballon = ballons[1],
|
325
|
+
image = ballon.getBallonizedImageContainer(),
|
326
|
+
ballonizer = image.getBallonizerInstance(),
|
327
|
+
bounds = ballon.getPositionAndSize(),
|
328
|
+
expectedLeft = (bounds.left - 20) / containerWidth,
|
329
|
+
expectedTop = (bounds.top - 20) / containerHeight;
|
330
|
+
|
331
|
+
changePosition(ballons[1], -20, -20);
|
332
|
+
|
333
|
+
expect(ballon.serialize().left).toEqual(expectedLeft);
|
334
|
+
expect(ballon.serialize().top).toEqual(expectedTop);
|
335
|
+
expect(image.serialize()[1].left).toEqual(expectedLeft);
|
336
|
+
expect(image.serialize()[1].top).toEqual(expectedTop);
|
337
|
+
expect(ballonizer.serialize()["http://imgs.xkcd.com/comics/cells.png"][1].left).toEqual(expectedLeft);
|
338
|
+
expect(ballonizer.serialize()["http://imgs.xkcd.com/comics/cells.png"][1].top).toEqual(expectedTop);
|
339
|
+
});
|
340
|
+
it("the page form data update", function () {
|
341
|
+
ballons = getBallons();
|
342
|
+
var formData = $("form.ballonizer_page_form > input[name='ballonizer_data']");
|
343
|
+
expect(formData).toHaveValue("");
|
344
|
+
changePosition(ballons[1], -20, -20);
|
345
|
+
expect(jQuery.parseJSON(htmlUnescape(formData.val()))).toEqual({
|
346
|
+
"http://imgs.xkcd.com/comics/cells.png": [
|
347
|
+
{ left: 0,
|
348
|
+
top: 0,
|
349
|
+
width: 1,
|
350
|
+
height: 0.24188790560471976,
|
351
|
+
text: 'When you see a claim that a common drug or vitamin "kills cancer cells in a petri dish", keep in mind:'
|
352
|
+
},
|
353
|
+
{ left: 0.0045871559633027525,
|
354
|
+
top: 0.8820058997050148,
|
355
|
+
width: 0.7798165137614679,
|
356
|
+
height: 0.05604719764011799,
|
357
|
+
text: 'So does a handgun.'
|
358
|
+
}
|
359
|
+
]
|
360
|
+
});
|
361
|
+
});
|
362
|
+
});
|
363
|
+
describe("size", function () {
|
364
|
+
var changeSize = function (ballon, dx, dy) {
|
365
|
+
var resizeHandle = $(".ui-icon-gripsmall-diagonal-se", ballon.getNode());
|
366
|
+
|
367
|
+
// The handle is hidden when not hovering over the ballon (which includes the handle)
|
368
|
+
resizeHandle.mouseover().simulate("drag-n-drop", { dx: dx, dy: dy }).mouseout();
|
369
|
+
|
370
|
+
return ballon;
|
371
|
+
};
|
372
|
+
it("the button to submit change appears", function () {
|
373
|
+
ballons = getBallons();
|
374
|
+
changeSize(ballons[0], -20, -20);
|
375
|
+
expect($("form.ballonizer_page_form > input[name='ballonizer_submit']")).toBeVisible();
|
376
|
+
});
|
377
|
+
it("the serialize methods shows the new values", function () {
|
378
|
+
ballons = getBallons();
|
379
|
+
var ballon = ballons[0],
|
380
|
+
image = ballon.getBallonizedImageContainer(),
|
381
|
+
ballonizer = image.getBallonizerInstance(),
|
382
|
+
bounds = ballon.getPositionAndSize(),
|
383
|
+
expectedWidth = (bounds.width - 20) / containerWidth,
|
384
|
+
expectedHeight = (bounds.height - 20) / containerHeight;
|
385
|
+
|
386
|
+
changeSize(ballons[0], -20, -20);
|
387
|
+
|
388
|
+
expect(ballon.serialize().width).toEqual(expectedWidth);
|
389
|
+
expect(ballon.serialize().height).toEqual(expectedHeight);
|
390
|
+
expect(image.serialize()[0].width).toEqual(expectedWidth);
|
391
|
+
expect(image.serialize()[0].height).toEqual(expectedHeight);
|
392
|
+
expect(ballonizer.serialize()["http://imgs.xkcd.com/comics/cells.png"][0].width).toEqual(expectedWidth);
|
393
|
+
expect(ballonizer.serialize()["http://imgs.xkcd.com/comics/cells.png"][0].height).toEqual(expectedHeight);
|
394
|
+
});
|
395
|
+
it("the page form data update", function () {
|
396
|
+
ballons = getBallons();
|
397
|
+
var formData = $("form.ballonizer_page_form > input[name='ballonizer_data']");
|
398
|
+
expect(formData).toHaveValue("");
|
399
|
+
changeSize(ballons[0], -20, -20);
|
400
|
+
expect(jQuery.parseJSON(htmlUnescape(formData.val()))).toEqual({
|
401
|
+
"http://imgs.xkcd.com/comics/cells.png": [
|
402
|
+
{ left: 0,
|
403
|
+
top: 0,
|
404
|
+
width: 0.908256880733945,
|
405
|
+
height: 0.18289085545722714,
|
406
|
+
text: 'When you see a claim that a common drug or vitamin "kills cancer cells in a petri dish", keep in mind:'
|
407
|
+
},
|
408
|
+
{ left: 0.0963302752293578,
|
409
|
+
top: 0.9410029498525073,
|
410
|
+
width: 0.7798165137614679,
|
411
|
+
height: 0.05604719764011799,
|
412
|
+
text: 'So does a handgun.'
|
413
|
+
}
|
414
|
+
]
|
415
|
+
});
|
416
|
+
});
|
417
|
+
});
|
418
|
+
describe("text", function () {
|
419
|
+
var changeText = function (ballon, text) {
|
420
|
+
realWorldEvent("dblclick", ballon.getNode());
|
421
|
+
ballon.getNode().val(text);
|
422
|
+
ballon.getNode().blur();
|
423
|
+
|
424
|
+
return ballon;
|
425
|
+
};
|
426
|
+
it("the button to submit change appears", function () {
|
427
|
+
ballons = getBallons();
|
428
|
+
changeText(ballons[0], "Ballon new text");
|
429
|
+
expect($("form.ballonizer_page_form > input[name='ballonizer_submit']")).toBeVisible();
|
430
|
+
});
|
431
|
+
it("the serialize methods shows the new values", function () {
|
432
|
+
ballons = getBallons();
|
433
|
+
var ballon = ballons[0],
|
434
|
+
text = "Ballon new text",
|
435
|
+
image = ballon.getBallonizedImageContainer(),
|
436
|
+
ballonizer = image.getBallonizerInstance();
|
437
|
+
|
438
|
+
changeText(ballon, text);
|
439
|
+
|
440
|
+
expect(ballon.serialize().text).toEqual(text);
|
441
|
+
expect(image.serialize()[0].text).toEqual(text);
|
442
|
+
expect(ballonizer.serialize()["http://imgs.xkcd.com/comics/cells.png"][0].text).toEqual(text);
|
443
|
+
});
|
444
|
+
it("the page form data update", function () {
|
445
|
+
ballons = getBallons();
|
446
|
+
var formData = $("form.ballonizer_page_form > input[name='ballonizer_data']");
|
447
|
+
expect(formData).toHaveValue("");
|
448
|
+
changeText(ballons[0], "Ballon new text");
|
449
|
+
expect(jQuery.parseJSON(htmlUnescape(formData.val()))).toEqual({
|
450
|
+
"http://imgs.xkcd.com/comics/cells.png": [
|
451
|
+
{ left: 0,
|
452
|
+
top: 0,
|
453
|
+
width: 1,
|
454
|
+
height: 0.24188790560471976,
|
455
|
+
text: 'Ballon new text'
|
456
|
+
},
|
457
|
+
{ left: 0.0963302752293578,
|
458
|
+
top: 0.9410029498525073,
|
459
|
+
width: 0.7798165137614679,
|
460
|
+
height: 0.05604719764011799,
|
461
|
+
text: 'So does a handgun.'
|
462
|
+
}
|
463
|
+
]
|
464
|
+
});
|
465
|
+
});
|
466
|
+
});
|
467
|
+
describe("a ballon being added", function () {
|
468
|
+
var addBallon = function () {
|
469
|
+
$(".ballonizer_image_container img").trigger(
|
470
|
+
jQuery.Event("dblclick", { pageX: 490, pageY: 444 })
|
471
|
+
);
|
472
|
+
};
|
473
|
+
|
474
|
+
it("the button to submit change appears", function () {
|
475
|
+
getBallons();
|
476
|
+
addBallon();
|
477
|
+
expect($("form.ballonizer_page_form > input[name='ballonizer_submit']")).toBeVisible();
|
478
|
+
});
|
479
|
+
it("the serialize methods shows the new values", function () {
|
480
|
+
ballons = getBallons();
|
481
|
+
addBallon();
|
482
|
+
var image = ballons[0].getBallonizedImageContainer(),
|
483
|
+
imageData = image.serialize(),
|
484
|
+
ballonizer = image.getBallonizerInstance(),
|
485
|
+
ballonizerData = ballonizer.serialize();
|
486
|
+
|
487
|
+
expect(imageData.length).toEqual(3);
|
488
|
+
expect(ballonizerData["http://imgs.xkcd.com/comics/cells.png"].length).toEqual(3);
|
489
|
+
});
|
490
|
+
it("the page form data update", function () {
|
491
|
+
getBallons();
|
492
|
+
var formData = $("form.ballonizer_page_form > input[name='ballonizer_data']");
|
493
|
+
expect(formData).toHaveValue("");
|
494
|
+
addBallon();
|
495
|
+
expect(jQuery.parseJSON(htmlUnescape(formData.val()))).toEqual({
|
496
|
+
"http://imgs.xkcd.com/comics/cells.png": [
|
497
|
+
{ left: 0,
|
498
|
+
top: 0,
|
499
|
+
width: 1,
|
500
|
+
height: 0.24188790560471976,
|
501
|
+
text: 'When you see a claim that a common drug or vitamin "kills cancer cells in a petri dish", keep in mind:'
|
502
|
+
},
|
503
|
+
{ left: 0.0963302752293578,
|
504
|
+
top: 0.9410029498525073,
|
505
|
+
width: 0.7798165137614679,
|
506
|
+
height: 0.05604719764011799,
|
507
|
+
text: 'So does a handgun.'
|
508
|
+
},
|
509
|
+
{ left: 0.40825688073394495,
|
510
|
+
top: 0.05604719764011799,
|
511
|
+
width: 0.591743119266055,
|
512
|
+
height: 0.12094395280235988,
|
513
|
+
text: 'double click to edit ballon text'
|
514
|
+
}
|
515
|
+
]
|
516
|
+
});
|
517
|
+
});
|
518
|
+
});
|
519
|
+
describe("a ballon being removed", function () {
|
520
|
+
var removeBallon = function (ballon) {
|
521
|
+
realWorldEvent("dblclick", ballon.getNode());
|
522
|
+
ballon.getNode().val("");
|
523
|
+
ballon.getNode().blur();
|
524
|
+
|
525
|
+
return ballon;
|
526
|
+
};
|
527
|
+
it("the button to submit change appears", function () {
|
528
|
+
ballons = getBallons();
|
529
|
+
removeBallon(ballons[0]);
|
530
|
+
expect($("form.ballonizer_page_form > input[name='ballonizer_submit']")).toBeVisible();
|
531
|
+
|
532
|
+
return ballons;
|
533
|
+
});
|
534
|
+
it("the serialize methods shows the new values", function () {
|
535
|
+
ballons = getBallons();
|
536
|
+
|
537
|
+
var image = ballons[0].getBallonizedImageContainer(),
|
538
|
+
ballonizer = image.getBallonizerInstance();
|
539
|
+
|
540
|
+
removeBallon(ballons[0]);
|
541
|
+
|
542
|
+
var imageData = image.serialize(),
|
543
|
+
ballonizerData = ballonizer.serialize();
|
544
|
+
|
545
|
+
expect(imageData.length).toEqual(1);
|
546
|
+
expect(ballonizerData["http://imgs.xkcd.com/comics/cells.png"].length).toEqual(1);
|
547
|
+
});
|
548
|
+
it("the page form data update", function () {
|
549
|
+
ballons = getBallons();
|
550
|
+
var formData = $("form.ballonizer_page_form > input[name='ballonizer_data']");
|
551
|
+
expect(formData).toHaveValue("");
|
552
|
+
removeBallon(ballons[0]);
|
553
|
+
expect(jQuery.parseJSON(htmlUnescape(formData.val()))).toEqual({
|
554
|
+
"http://imgs.xkcd.com/comics/cells.png": [
|
555
|
+
{ left: 0.0963302752293578,
|
556
|
+
top: 0.9410029498525073,
|
557
|
+
width: 0.7798165137614679,
|
558
|
+
height: 0.05604719764011799,
|
559
|
+
text: 'So does a handgun.'
|
560
|
+
}
|
561
|
+
]
|
562
|
+
});
|
563
|
+
});
|
564
|
+
});
|
565
|
+
});
|
566
|
+
});
|
567
|
+
});
|
568
|
+
|