joosy 1.2.0.alpha.73 → 1.2.0.beta.1
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/Gruntfile.coffee +56 -18
- data/bower.json +1 -1
- data/build/joosy/form.js +1 -0
- data/build/joosy/resources.js +1 -0
- data/build/joosy.js +2 -2774
- data/package.json +5 -4
- data/source/joosy/application.coffee +9 -7
- data/source/joosy/{extensions/resources-form/form.coffee → form.coffee} +58 -51
- data/source/joosy/helpers/form.coffee +241 -0
- data/source/joosy/helpers/index.coffee +3 -0
- data/source/joosy/helpers/routes.coffee +3 -1
- data/source/joosy/helpers/view.coffee +9 -9
- data/source/joosy/joosy.coffee +3 -5
- data/source/joosy/module.coffee +9 -4
- data/source/joosy/modules/dom.coffee +33 -31
- data/source/joosy/modules/events.coffee +24 -20
- data/source/joosy/modules/filters.coffee +38 -35
- data/source/joosy/modules/page/title.coffee +3 -3
- data/source/joosy/modules/renderer.coffee +23 -18
- data/source/joosy/modules/resources/identity_map.coffee +45 -0
- data/source/joosy/modules/resources/model.coffee +146 -0
- data/source/joosy/modules/widgets_manager.coffee +8 -8
- data/source/joosy/resources/array.coffee +0 -5
- data/source/joosy/resources/hash.coffee +8 -13
- data/source/joosy/resources/index.coffee +2 -0
- data/source/joosy/{extensions/resources → resources}/rest.coffee +48 -19
- data/source/joosy/resources/scalar.coffee +8 -10
- data/source/joosy/router.coffee +13 -12
- data/source/joosy/templaters/jst.coffee +3 -2
- data/source/joosy/widget.coffee +17 -15
- data/source/joosy.coffee +2 -0
- data/source/vendor/es5-shim.js +1316 -0
- data/source/vendor/inflections.js +598 -0
- data/source/vendor/metamorph.js +457 -0
- data/spec/helpers/matchers.coffee +4 -4
- data/spec/joosy/core/application_spec.coffee +1 -1
- data/spec/joosy/core/helpers/view_spec.coffee +2 -2
- data/spec/joosy/core/joosy_spec.coffee +8 -4
- data/spec/joosy/core/modules/dom_spec.coffee +7 -7
- data/spec/joosy/core/modules/events_spec.coffee +2 -2
- data/spec/joosy/core/modules/filters_spec.coffee +7 -8
- data/spec/joosy/core/modules/module_spec.coffee +5 -5
- data/spec/joosy/core/router_spec.coffee +3 -3
- data/spec/joosy/core/widget_spec.coffee +6 -6
- data/spec/joosy/environments/amd_spec.coffee +4 -2
- data/spec/joosy/environments/global_spec.coffee +1 -1
- data/spec/joosy/{extensions/form → form}/form_spec.coffee +9 -16
- data/spec/joosy/{extensions/form → form}/helpers/forms_spec.coffee +5 -5
- data/spec/joosy/{core/resources → resources}/array_spec.coffee +2 -2
- data/spec/joosy/{core/resources → resources}/hash_spec.coffee +0 -8
- data/spec/joosy/{core/modules/resources → resources/modules}/cacher_spec.coffee +0 -0
- data/spec/joosy/resources/modules/identity_map_spec.coffee +47 -0
- data/spec/joosy/{extensions/resources/base_spec.coffee → resources/modules/model_spec.coffee} +28 -48
- data/spec/joosy/{extensions/resources → resources}/rest_spec.coffee +29 -22
- data/spec/joosy/{core/resources → resources}/scalar_spec.coffee +8 -8
- data/templates/application/application.coffee.tt +0 -2
- data/templates/environment/app/haml/index.haml +2 -2
- data/templates/environment/package.json +1 -1
- metadata +23 -19
- data/build/joosy/extensions/resources-form.js +0 -590
- data/build/joosy/extensions/resources.js +0 -561
- data/source/joosy/extensions/resources/base.coffee +0 -282
- data/source/joosy/extensions/resources/index.coffee +0 -1
- data/source/joosy/extensions/resources-form/helpers/form.coffee +0 -104
- data/source/joosy/extensions/resources-form/index.coffee +0 -1
- data/source/metamorph.coffee +0 -410
@@ -0,0 +1,457 @@
|
|
1
|
+
// ==========================================================================
|
2
|
+
// Project: metamorph
|
3
|
+
// Copyright: ©2013 Tilde, Inc. All rights reserved.
|
4
|
+
// ==========================================================================
|
5
|
+
|
6
|
+
(function(window) {
|
7
|
+
|
8
|
+
var K = function(){},
|
9
|
+
guid = 0,
|
10
|
+
document = window.document,
|
11
|
+
disableRange = ('undefined' === typeof ENV ? {} : ENV).DISABLE_RANGE_API,
|
12
|
+
|
13
|
+
// Feature-detect the W3C range API, the extended check is for IE9 which only partially supports ranges
|
14
|
+
supportsRange = (!disableRange) && document && ('createRange' in document) && (typeof Range !== 'undefined') && Range.prototype.createContextualFragment,
|
15
|
+
|
16
|
+
// Internet Explorer prior to 9 does not allow setting innerHTML if the first element
|
17
|
+
// is a "zero-scope" element. This problem can be worked around by making
|
18
|
+
// the first node an invisible text node. We, like Modernizr, use ­
|
19
|
+
needsShy = document && (function(){
|
20
|
+
var testEl = document.createElement('div');
|
21
|
+
testEl.innerHTML = "<div></div>";
|
22
|
+
testEl.firstChild.innerHTML = "<script></script>";
|
23
|
+
return testEl.firstChild.innerHTML === '';
|
24
|
+
})(),
|
25
|
+
|
26
|
+
|
27
|
+
// IE 8 (and likely earlier) likes to move whitespace preceeding
|
28
|
+
// a script tag to appear after it. This means that we can
|
29
|
+
// accidentally remove whitespace when updating a morph.
|
30
|
+
movesWhitespace = document && (function() {
|
31
|
+
var testEl = document.createElement('div');
|
32
|
+
testEl.innerHTML = "Test: <script type='text/x-placeholder'></script>Value";
|
33
|
+
return testEl.childNodes[0].nodeValue === 'Test:' &&
|
34
|
+
testEl.childNodes[2].nodeValue === ' Value';
|
35
|
+
})();
|
36
|
+
|
37
|
+
// Constructor that supports either Metamorph('foo') or new
|
38
|
+
// Metamorph('foo');
|
39
|
+
//
|
40
|
+
// Takes a string of HTML as the argument.
|
41
|
+
|
42
|
+
var Metamorph = function(html) {
|
43
|
+
var self;
|
44
|
+
|
45
|
+
if (this instanceof Metamorph) {
|
46
|
+
self = this;
|
47
|
+
} else {
|
48
|
+
self = new K();
|
49
|
+
}
|
50
|
+
|
51
|
+
self.innerHTML = html;
|
52
|
+
var myGuid = 'metamorph-'+(guid++);
|
53
|
+
self.start = myGuid + '-start';
|
54
|
+
self.end = myGuid + '-end';
|
55
|
+
|
56
|
+
return self;
|
57
|
+
};
|
58
|
+
|
59
|
+
K.prototype = Metamorph.prototype;
|
60
|
+
|
61
|
+
var rangeFor, htmlFunc, removeFunc, outerHTMLFunc, appendToFunc, afterFunc, prependFunc, startTagFunc, endTagFunc;
|
62
|
+
|
63
|
+
outerHTMLFunc = function() {
|
64
|
+
return this.startTag() + this.innerHTML + this.endTag();
|
65
|
+
};
|
66
|
+
|
67
|
+
startTagFunc = function() {
|
68
|
+
/*
|
69
|
+
* We replace chevron by its hex code in order to prevent escaping problems.
|
70
|
+
* Check this thread for more explaination:
|
71
|
+
* http://stackoverflow.com/questions/8231048/why-use-x3c-instead-of-when-generating-html-from-javascript
|
72
|
+
*/
|
73
|
+
return "<script id='" + this.start + "' type='text/x-placeholder'>\x3C/script>";
|
74
|
+
};
|
75
|
+
|
76
|
+
endTagFunc = function() {
|
77
|
+
/*
|
78
|
+
* We replace chevron by its hex code in order to prevent escaping problems.
|
79
|
+
* Check this thread for more explaination:
|
80
|
+
* http://stackoverflow.com/questions/8231048/why-use-x3c-instead-of-when-generating-html-from-javascript
|
81
|
+
*/
|
82
|
+
return "<script id='" + this.end + "' type='text/x-placeholder'>\x3C/script>";
|
83
|
+
};
|
84
|
+
|
85
|
+
// If we have the W3C range API, this process is relatively straight forward.
|
86
|
+
if (supportsRange) {
|
87
|
+
|
88
|
+
// Get a range for the current morph. Optionally include the starting and
|
89
|
+
// ending placeholders.
|
90
|
+
rangeFor = function(morph, outerToo) {
|
91
|
+
var range = document.createRange();
|
92
|
+
var before = document.getElementById(morph.start);
|
93
|
+
var after = document.getElementById(morph.end);
|
94
|
+
|
95
|
+
if (outerToo) {
|
96
|
+
range.setStartBefore(before);
|
97
|
+
range.setEndAfter(after);
|
98
|
+
} else {
|
99
|
+
range.setStartAfter(before);
|
100
|
+
range.setEndBefore(after);
|
101
|
+
}
|
102
|
+
|
103
|
+
return range;
|
104
|
+
};
|
105
|
+
|
106
|
+
htmlFunc = function(html, outerToo) {
|
107
|
+
// get a range for the current metamorph object
|
108
|
+
var range = rangeFor(this, outerToo);
|
109
|
+
|
110
|
+
// delete the contents of the range, which will be the
|
111
|
+
// nodes between the starting and ending placeholder.
|
112
|
+
range.deleteContents();
|
113
|
+
|
114
|
+
// create a new document fragment for the HTML
|
115
|
+
var fragment = range.createContextualFragment(html);
|
116
|
+
|
117
|
+
// insert the fragment into the range
|
118
|
+
range.insertNode(fragment);
|
119
|
+
};
|
120
|
+
|
121
|
+
removeFunc = function() {
|
122
|
+
// get a range for the current metamorph object including
|
123
|
+
// the starting and ending placeholders.
|
124
|
+
var range = rangeFor(this, true);
|
125
|
+
|
126
|
+
// delete the entire range.
|
127
|
+
range.deleteContents();
|
128
|
+
};
|
129
|
+
|
130
|
+
appendToFunc = function(node) {
|
131
|
+
var range = document.createRange();
|
132
|
+
range.setStart(node);
|
133
|
+
range.collapse(false);
|
134
|
+
var frag = range.createContextualFragment(this.outerHTML());
|
135
|
+
node.appendChild(frag);
|
136
|
+
};
|
137
|
+
|
138
|
+
afterFunc = function(html) {
|
139
|
+
var range = document.createRange();
|
140
|
+
var after = document.getElementById(this.end);
|
141
|
+
|
142
|
+
range.setStartAfter(after);
|
143
|
+
range.setEndAfter(after);
|
144
|
+
|
145
|
+
var fragment = range.createContextualFragment(html);
|
146
|
+
range.insertNode(fragment);
|
147
|
+
};
|
148
|
+
|
149
|
+
prependFunc = function(html) {
|
150
|
+
var range = document.createRange();
|
151
|
+
var start = document.getElementById(this.start);
|
152
|
+
|
153
|
+
range.setStartAfter(start);
|
154
|
+
range.setEndAfter(start);
|
155
|
+
|
156
|
+
var fragment = range.createContextualFragment(html);
|
157
|
+
range.insertNode(fragment);
|
158
|
+
};
|
159
|
+
|
160
|
+
} else {
|
161
|
+
/**
|
162
|
+
* This code is mostly taken from jQuery, with one exception. In jQuery's case, we
|
163
|
+
* have some HTML and we need to figure out how to convert it into some nodes.
|
164
|
+
*
|
165
|
+
* In this case, jQuery needs to scan the HTML looking for an opening tag and use
|
166
|
+
* that as the key for the wrap map. In our case, we know the parent node, and
|
167
|
+
* can use its type as the key for the wrap map.
|
168
|
+
**/
|
169
|
+
var wrapMap = {
|
170
|
+
select: [ 1, "<select multiple='multiple'>", "</select>" ],
|
171
|
+
fieldset: [ 1, "<fieldset>", "</fieldset>" ],
|
172
|
+
table: [ 1, "<table>", "</table>" ],
|
173
|
+
tbody: [ 2, "<table><tbody>", "</tbody></table>" ],
|
174
|
+
tr: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
|
175
|
+
colgroup: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
|
176
|
+
map: [ 1, "<map>", "</map>" ],
|
177
|
+
_default: [ 0, "", "" ]
|
178
|
+
};
|
179
|
+
|
180
|
+
var findChildById = function(element, id) {
|
181
|
+
if (element.getAttribute('id') === id) { return element; }
|
182
|
+
|
183
|
+
var len = element.childNodes.length, idx, node, found;
|
184
|
+
for (idx=0; idx<len; idx++) {
|
185
|
+
node = element.childNodes[idx];
|
186
|
+
found = node.nodeType === 1 && findChildById(node, id);
|
187
|
+
if (found) { return found; }
|
188
|
+
}
|
189
|
+
};
|
190
|
+
|
191
|
+
var setInnerHTML = function(element, html) {
|
192
|
+
var matches = [];
|
193
|
+
if (movesWhitespace) {
|
194
|
+
// Right now we only check for script tags with ids with the
|
195
|
+
// goal of targeting morphs.
|
196
|
+
html = html.replace(/(\s+)(<script id='([^']+)')/g, function(match, spaces, tag, id) {
|
197
|
+
matches.push([id, spaces]);
|
198
|
+
return tag;
|
199
|
+
});
|
200
|
+
}
|
201
|
+
|
202
|
+
element.innerHTML = html;
|
203
|
+
|
204
|
+
// If we have to do any whitespace adjustments do them now
|
205
|
+
if (matches.length > 0) {
|
206
|
+
var len = matches.length, idx;
|
207
|
+
for (idx=0; idx<len; idx++) {
|
208
|
+
var script = findChildById(element, matches[idx][0]),
|
209
|
+
node = document.createTextNode(matches[idx][1]);
|
210
|
+
script.parentNode.insertBefore(node, script);
|
211
|
+
}
|
212
|
+
}
|
213
|
+
};
|
214
|
+
|
215
|
+
/**
|
216
|
+
* Given a parent node and some HTML, generate a set of nodes. Return the first
|
217
|
+
* node, which will allow us to traverse the rest using nextSibling.
|
218
|
+
*
|
219
|
+
* We need to do this because innerHTML in IE does not really parse the nodes.
|
220
|
+
**/
|
221
|
+
var firstNodeFor = function(parentNode, html) {
|
222
|
+
var arr = wrapMap[parentNode.tagName.toLowerCase()] || wrapMap._default;
|
223
|
+
var depth = arr[0], start = arr[1], end = arr[2];
|
224
|
+
|
225
|
+
if (needsShy) { html = '­'+html; }
|
226
|
+
|
227
|
+
var element = document.createElement('div');
|
228
|
+
|
229
|
+
setInnerHTML(element, start + html + end);
|
230
|
+
|
231
|
+
for (var i=0; i<=depth; i++) {
|
232
|
+
element = element.firstChild;
|
233
|
+
}
|
234
|
+
|
235
|
+
// Look for ­ to remove it.
|
236
|
+
if (needsShy) {
|
237
|
+
var shyElement = element;
|
238
|
+
|
239
|
+
// Sometimes we get nameless elements with the shy inside
|
240
|
+
while (shyElement.nodeType === 1 && !shyElement.nodeName) {
|
241
|
+
shyElement = shyElement.firstChild;
|
242
|
+
}
|
243
|
+
|
244
|
+
// At this point it's the actual unicode character.
|
245
|
+
if (shyElement.nodeType === 3 && shyElement.nodeValue.charAt(0) === "\u00AD") {
|
246
|
+
shyElement.nodeValue = shyElement.nodeValue.slice(1);
|
247
|
+
}
|
248
|
+
}
|
249
|
+
|
250
|
+
return element;
|
251
|
+
};
|
252
|
+
|
253
|
+
/**
|
254
|
+
* In some cases, Internet Explorer can create an anonymous node in
|
255
|
+
* the hierarchy with no tagName. You can create this scenario via:
|
256
|
+
*
|
257
|
+
* div = document.createElement("div");
|
258
|
+
* div.innerHTML = "<table>­<script></script><tr><td>hi</td></tr></table>";
|
259
|
+
* div.firstChild.firstChild.tagName //=> ""
|
260
|
+
*
|
261
|
+
* If our script markers are inside such a node, we need to find that
|
262
|
+
* node and use *it* as the marker.
|
263
|
+
**/
|
264
|
+
var realNode = function(start) {
|
265
|
+
while (start.parentNode.tagName === "") {
|
266
|
+
start = start.parentNode;
|
267
|
+
}
|
268
|
+
|
269
|
+
return start;
|
270
|
+
};
|
271
|
+
|
272
|
+
/**
|
273
|
+
* When automatically adding a tbody, Internet Explorer inserts the
|
274
|
+
* tbody immediately before the first <tr>. Other browsers create it
|
275
|
+
* before the first node, no matter what.
|
276
|
+
*
|
277
|
+
* This means the the following code:
|
278
|
+
*
|
279
|
+
* div = document.createElement("div");
|
280
|
+
* div.innerHTML = "<table><script id='first'></script><tr><td>hi</td></tr><script id='last'></script></table>
|
281
|
+
*
|
282
|
+
* Generates the following DOM in IE:
|
283
|
+
*
|
284
|
+
* + div
|
285
|
+
* + table
|
286
|
+
* - script id='first'
|
287
|
+
* + tbody
|
288
|
+
* + tr
|
289
|
+
* + td
|
290
|
+
* - "hi"
|
291
|
+
* - script id='last'
|
292
|
+
*
|
293
|
+
* Which means that the two script tags, even though they were
|
294
|
+
* inserted at the same point in the hierarchy in the original
|
295
|
+
* HTML, now have different parents.
|
296
|
+
*
|
297
|
+
* This code reparents the first script tag by making it the tbody's
|
298
|
+
* first child.
|
299
|
+
**/
|
300
|
+
var fixParentage = function(start, end) {
|
301
|
+
if (start.parentNode !== end.parentNode) {
|
302
|
+
end.parentNode.insertBefore(start, end.parentNode.firstChild);
|
303
|
+
}
|
304
|
+
};
|
305
|
+
|
306
|
+
htmlFunc = function(html, outerToo) {
|
307
|
+
// get the real starting node. see realNode for details.
|
308
|
+
var start = realNode(document.getElementById(this.start));
|
309
|
+
var end = document.getElementById(this.end);
|
310
|
+
var parentNode = end.parentNode;
|
311
|
+
var node, nextSibling, last;
|
312
|
+
|
313
|
+
// make sure that the start and end nodes share the same
|
314
|
+
// parent. If not, fix it.
|
315
|
+
fixParentage(start, end);
|
316
|
+
|
317
|
+
// remove all of the nodes after the starting placeholder and
|
318
|
+
// before the ending placeholder.
|
319
|
+
node = start.nextSibling;
|
320
|
+
while (node) {
|
321
|
+
nextSibling = node.nextSibling;
|
322
|
+
last = node === end;
|
323
|
+
|
324
|
+
// if this is the last node, and we want to remove it as well,
|
325
|
+
// set the `end` node to the next sibling. This is because
|
326
|
+
// for the rest of the function, we insert the new nodes
|
327
|
+
// before the end (note that insertBefore(node, null) is
|
328
|
+
// the same as appendChild(node)).
|
329
|
+
//
|
330
|
+
// if we do not want to remove it, just break.
|
331
|
+
if (last) {
|
332
|
+
if (outerToo) { end = node.nextSibling; } else { break; }
|
333
|
+
}
|
334
|
+
|
335
|
+
node.parentNode.removeChild(node);
|
336
|
+
|
337
|
+
// if this is the last node and we didn't break before
|
338
|
+
// (because we wanted to remove the outer nodes), break
|
339
|
+
// now.
|
340
|
+
if (last) { break; }
|
341
|
+
|
342
|
+
node = nextSibling;
|
343
|
+
}
|
344
|
+
|
345
|
+
// get the first node for the HTML string, even in cases like
|
346
|
+
// tables and lists where a simple innerHTML on a div would
|
347
|
+
// swallow some of the content.
|
348
|
+
node = firstNodeFor(start.parentNode, html);
|
349
|
+
|
350
|
+
// copy the nodes for the HTML between the starting and ending
|
351
|
+
// placeholder.
|
352
|
+
while (node) {
|
353
|
+
nextSibling = node.nextSibling;
|
354
|
+
parentNode.insertBefore(node, end);
|
355
|
+
node = nextSibling;
|
356
|
+
}
|
357
|
+
};
|
358
|
+
|
359
|
+
// remove the nodes in the DOM representing this metamorph.
|
360
|
+
//
|
361
|
+
// this includes the starting and ending placeholders.
|
362
|
+
removeFunc = function() {
|
363
|
+
var start = realNode(document.getElementById(this.start));
|
364
|
+
var end = document.getElementById(this.end);
|
365
|
+
|
366
|
+
this.html('');
|
367
|
+
start.parentNode.removeChild(start);
|
368
|
+
end.parentNode.removeChild(end);
|
369
|
+
};
|
370
|
+
|
371
|
+
appendToFunc = function(parentNode) {
|
372
|
+
var node = firstNodeFor(parentNode, this.outerHTML());
|
373
|
+
var nextSibling;
|
374
|
+
|
375
|
+
while (node) {
|
376
|
+
nextSibling = node.nextSibling;
|
377
|
+
parentNode.appendChild(node);
|
378
|
+
node = nextSibling;
|
379
|
+
}
|
380
|
+
};
|
381
|
+
|
382
|
+
afterFunc = function(html) {
|
383
|
+
// get the real starting node. see realNode for details.
|
384
|
+
var end = document.getElementById(this.end);
|
385
|
+
var insertBefore = end.nextSibling;
|
386
|
+
var parentNode = end.parentNode;
|
387
|
+
var nextSibling;
|
388
|
+
var node;
|
389
|
+
|
390
|
+
// get the first node for the HTML string, even in cases like
|
391
|
+
// tables and lists where a simple innerHTML on a div would
|
392
|
+
// swallow some of the content.
|
393
|
+
node = firstNodeFor(parentNode, html);
|
394
|
+
|
395
|
+
// copy the nodes for the HTML between the starting and ending
|
396
|
+
// placeholder.
|
397
|
+
while (node) {
|
398
|
+
nextSibling = node.nextSibling;
|
399
|
+
parentNode.insertBefore(node, insertBefore);
|
400
|
+
node = nextSibling;
|
401
|
+
}
|
402
|
+
};
|
403
|
+
|
404
|
+
prependFunc = function(html) {
|
405
|
+
var start = document.getElementById(this.start);
|
406
|
+
var parentNode = start.parentNode;
|
407
|
+
var nextSibling;
|
408
|
+
var node;
|
409
|
+
|
410
|
+
node = firstNodeFor(parentNode, html);
|
411
|
+
var insertBefore = start.nextSibling;
|
412
|
+
|
413
|
+
while (node) {
|
414
|
+
nextSibling = node.nextSibling;
|
415
|
+
parentNode.insertBefore(node, insertBefore);
|
416
|
+
node = nextSibling;
|
417
|
+
}
|
418
|
+
};
|
419
|
+
}
|
420
|
+
|
421
|
+
Metamorph.prototype.html = function(html) {
|
422
|
+
this.checkRemoved();
|
423
|
+
if (html === undefined) { return this.innerHTML; }
|
424
|
+
|
425
|
+
htmlFunc.call(this, html);
|
426
|
+
|
427
|
+
this.innerHTML = html;
|
428
|
+
};
|
429
|
+
|
430
|
+
Metamorph.prototype.replaceWith = function(html) {
|
431
|
+
this.checkRemoved();
|
432
|
+
htmlFunc.call(this, html, true);
|
433
|
+
};
|
434
|
+
|
435
|
+
Metamorph.prototype.remove = removeFunc;
|
436
|
+
Metamorph.prototype.outerHTML = outerHTMLFunc;
|
437
|
+
Metamorph.prototype.appendTo = appendToFunc;
|
438
|
+
Metamorph.prototype.after = afterFunc;
|
439
|
+
Metamorph.prototype.prepend = prependFunc;
|
440
|
+
Metamorph.prototype.startTag = startTagFunc;
|
441
|
+
Metamorph.prototype.endTag = endTagFunc;
|
442
|
+
|
443
|
+
Metamorph.prototype.isRemoved = function() {
|
444
|
+
var before = document.getElementById(this.start);
|
445
|
+
var after = document.getElementById(this.end);
|
446
|
+
|
447
|
+
return !before || !after;
|
448
|
+
};
|
449
|
+
|
450
|
+
Metamorph.prototype.checkRemoved = function() {
|
451
|
+
if (this.isRemoved()) {
|
452
|
+
throw new Error("Cannot perform operations on a Metamorph that is not in the DOM.");
|
453
|
+
}
|
454
|
+
};
|
455
|
+
|
456
|
+
window.Metamorph = Metamorph;
|
457
|
+
})(this);
|
@@ -2,7 +2,7 @@ beforeEach ->
|
|
2
2
|
@addMatchers
|
3
3
|
|
4
4
|
#
|
5
|
-
# Checks whether listed array of callbacks was
|
5
|
+
# Checks whether listed array of callbacks was
|
6
6
|
# called in exact order one by one
|
7
7
|
#
|
8
8
|
# @example
|
@@ -10,7 +10,7 @@ beforeEach ->
|
|
10
10
|
#
|
11
11
|
toBeSequenced: ->
|
12
12
|
# Are we working with array?
|
13
|
-
if !
|
13
|
+
if !Array.isArray(@actual) || @actual.length == 0
|
14
14
|
@message = -> 'Not array or empty array given'
|
15
15
|
return false
|
16
16
|
|
@@ -22,7 +22,7 @@ beforeEach ->
|
|
22
22
|
|
23
23
|
# Were they called in a proper order?
|
24
24
|
if @actual.length > 1
|
25
|
-
for spy, i in @actual.
|
25
|
+
for spy, i in @actual.slice(1)
|
26
26
|
unless spy.calledAfter @actual[i]
|
27
27
|
@message = -> "Spy ##{i+1} wasn't called after spy ##{i}"
|
28
28
|
return false
|
@@ -69,4 +69,4 @@ beforeEach ->
|
|
69
69
|
else
|
70
70
|
flag &&= tag.attr(name) == val
|
71
71
|
|
72
|
-
flag
|
72
|
+
flag
|
@@ -109,7 +109,7 @@ describe "Joosy.Application", ->
|
|
109
109
|
|
110
110
|
it "sequences paint hooks", ->
|
111
111
|
spies = []
|
112
|
-
|
112
|
+
spies.push sinon.spy() for i in [1..11]
|
113
113
|
|
114
114
|
class Layout extends Joosy.Layout
|
115
115
|
@beforePaint (complete) -> spies[0](); complete()
|
@@ -8,7 +8,7 @@ describe "Joosy.Helpers.View", ->
|
|
8
8
|
expect(tag).toEqual '<div id="id">content</div>'
|
9
9
|
|
10
10
|
it "renders tag with lambda content", ->
|
11
|
-
tag = h.contentTag 'div', {id: 'id'}, ->
|
11
|
+
tag = h.contentTag 'div', {id: 'id'}, ->
|
12
12
|
h.contentTag 'div', 'content', {id: 'id2'}
|
13
13
|
|
14
|
-
expect(tag).toEqual '<div id="id"><div id="id2">content</div></div>'
|
14
|
+
expect(tag).toEqual '<div id="id"><div id="id2">content</div></div>'
|
@@ -1,15 +1,19 @@
|
|
1
1
|
describe "Joosy", ->
|
2
2
|
|
3
|
+
unique = (array) ->
|
4
|
+
array.filter (item, index, array) ->
|
5
|
+
array.indexOf(item) == index
|
6
|
+
|
3
7
|
it "generates proper UUIDs", ->
|
4
8
|
uuids = []
|
5
|
-
|
6
|
-
expect(
|
9
|
+
uuids.push Joosy.uuid() for i in [1..2]
|
10
|
+
expect(unique(uuids).length).toEqual(2)
|
7
11
|
expect(uuids[0]).toMatch /[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}/
|
8
12
|
|
9
13
|
it "generates proper UIDs", ->
|
10
14
|
uids = []
|
11
|
-
|
12
|
-
expect(
|
15
|
+
uids.push Joosy.uid() for i in [1..5]
|
16
|
+
expect(unique(uids).length).toEqual(5)
|
13
17
|
|
14
18
|
it "builds proper URLs", ->
|
15
19
|
expect(Joosy.buildUrl 'http://www.org').toEqual('http://www.org')
|
@@ -36,9 +36,9 @@ describe "Joosy.Modules.DOM", ->
|
|
36
36
|
first: 'overrided'
|
37
37
|
third: 'third'
|
38
38
|
|
39
|
-
expect((new B).__elements).toEqual
|
39
|
+
expect((new B).__elements).toEqual
|
40
40
|
posts: '.post'
|
41
|
-
content:
|
41
|
+
content:
|
42
42
|
post1: '#post1'
|
43
43
|
post2: '#post2'
|
44
44
|
first: 'overrided'
|
@@ -46,10 +46,10 @@ describe "Joosy.Modules.DOM", ->
|
|
46
46
|
third: 'third'
|
47
47
|
footer: '.footer'
|
48
48
|
|
49
|
-
expect((new @DOM).__elements).toEqual
|
49
|
+
expect((new @DOM).__elements).toEqual
|
50
50
|
posts: '.post'
|
51
51
|
footer: '.footer'
|
52
|
-
content:
|
52
|
+
content:
|
53
53
|
post1: '#post1'
|
54
54
|
post2: '#post2'
|
55
55
|
|
@@ -102,17 +102,17 @@ describe "Joosy.Modules.DOM", ->
|
|
102
102
|
'test $footer': 'onFooterTest'
|
103
103
|
'custom' : 'overrided'
|
104
104
|
|
105
|
-
expect((new B).__events).toEqual
|
105
|
+
expect((new B).__events).toEqual
|
106
106
|
'test': 'onDOMTest'
|
107
107
|
'test .post': 'callback2'
|
108
108
|
'test $footer': 'onFooterTest'
|
109
109
|
'custom' : 'overrided'
|
110
110
|
|
111
|
-
expect((new @DOM).__events).toEqual
|
111
|
+
expect((new @DOM).__events).toEqual
|
112
112
|
'test': 'onDOMTest'
|
113
113
|
|
114
114
|
it "delegates", ->
|
115
|
-
callbacks = 1
|
115
|
+
callbacks = [1..3].map -> sinon.spy()
|
116
116
|
|
117
117
|
@DOM.mapEvents
|
118
118
|
'test .post': callbacks[2]
|
@@ -88,8 +88,8 @@ describe "Joosy.Modules.Events", ->
|
|
88
88
|
expect(@callback.callCount).toEqual 1
|
89
89
|
|
90
90
|
it "allows simultaneous usage", ->
|
91
|
-
|
92
|
-
|
91
|
+
@eventer.bind "event#{i}", @callback for i in [1..3]
|
92
|
+
@eventer.wait "event#{i}", @callback for i in [1..3]
|
93
93
|
|
94
94
|
@eventer.trigger 'event2'
|
95
95
|
|
@@ -36,7 +36,7 @@ describe "Joosy.Modules.Filters", ->
|
|
36
36
|
expect(target.__afterUnloads).toBeUndefined()
|
37
37
|
|
38
38
|
it "runs callbacks", ->
|
39
|
-
callbacks = 0
|
39
|
+
callbacks = [0..2].map -> sinon.spy()
|
40
40
|
@Filters.beforeLoad callbacks[0]
|
41
41
|
@Filters.afterLoad callbacks[1]
|
42
42
|
@Filters.afterUnload callbacks[2]
|
@@ -45,14 +45,14 @@ describe "Joosy.Modules.Filters", ->
|
|
45
45
|
@filters.__runAfterLoads 1, 2
|
46
46
|
@filters.__runAfterUnloads 1, 2
|
47
47
|
|
48
|
-
for i in 0
|
48
|
+
for i in [0..2]
|
49
49
|
expect(callbacks[i].callCount).toEqual 1
|
50
50
|
expect(callbacks[i].alwaysCalledWithExactly 1, 2).toBeTruthy()
|
51
51
|
|
52
52
|
describe "chaining", ->
|
53
53
|
|
54
54
|
it "evaluates", ->
|
55
|
-
callbacks = 0
|
55
|
+
callbacks = [0..1].map =>
|
56
56
|
callback = sinon.stub()
|
57
57
|
@Filters.beforeLoad callback
|
58
58
|
callback
|
@@ -66,7 +66,7 @@ describe "Joosy.Modules.Filters", ->
|
|
66
66
|
expect(callbacks[1].callCount).toEqual 1
|
67
67
|
|
68
68
|
it "breaks on false", ->
|
69
|
-
callbacks = 0
|
69
|
+
callbacks = [0..2].map =>
|
70
70
|
callback = sinon.stub()
|
71
71
|
@Filters.beforeLoad callback
|
72
72
|
callback
|
@@ -81,7 +81,7 @@ describe "Joosy.Modules.Filters", ->
|
|
81
81
|
expect(callbacks[2].callCount).toEqual 0
|
82
82
|
|
83
83
|
it "accepts method names as callbacks", ->
|
84
|
-
@filters['callback' + i] = sinon.spy() for i in 0
|
84
|
+
@filters['callback' + i] = sinon.spy() for i in [0..2]
|
85
85
|
|
86
86
|
@Filters.beforeLoad 'callback0'
|
87
87
|
@Filters.afterLoad 'callback1'
|
@@ -91,8 +91,7 @@ describe "Joosy.Modules.Filters", ->
|
|
91
91
|
@filters.__runAfterLoads()
|
92
92
|
@filters.__runAfterUnloads()
|
93
93
|
|
94
|
-
expect(@filters['callback' + i].callCount).toEqual 1 for i in 0
|
95
|
-
|
94
|
+
expect(@filters['callback' + i].callCount).toEqual 1 for i in [0..2]
|
96
95
|
describe 'sequenced', ->
|
97
96
|
|
98
97
|
beforeEach ->
|
@@ -165,7 +164,7 @@ describe "Joosy.Modules.Filters", ->
|
|
165
164
|
expect(spy.args[2][1]).toEqual 'test2'
|
166
165
|
|
167
166
|
it "runs multiple callbacks", ->
|
168
|
-
spies = 0
|
167
|
+
spies = [0..2].map -> sinon.spy()
|
169
168
|
context = @filters
|
170
169
|
|
171
170
|
@Filters.beforeLoad (argument, complete) ->
|
@@ -9,9 +9,9 @@ describe "Joosy.Module", ->
|
|
9
9
|
for a in [A, B, C, D]
|
10
10
|
for b in [A, B, C, D]
|
11
11
|
if (a == b) ||
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
((a == B) && (b == A)) ||
|
13
|
+
((a == C) && (b != D))
|
14
|
+
expect(Joosy.Module.hasAncestor a, b).toBeTruthy()
|
15
15
|
else
|
16
16
|
expect(Joosy.Module.hasAncestor a, b).toBeFalsy()
|
17
17
|
|
@@ -19,8 +19,8 @@ describe "Joosy.Module", ->
|
|
19
19
|
it "has minimal set of properties", ->
|
20
20
|
class Klass extends Joosy.Module
|
21
21
|
|
22
|
-
expect(Object.
|
23
|
-
expect(Object.
|
22
|
+
expect(Object.keys Klass).toEqual ['__namespace__', '__className', 'hasAncestor', 'aliasMethodChain', 'aliasStaticMethodChain', 'merge', 'include', 'extend', '__super__']
|
23
|
+
expect(Object.keys Klass.prototype).toEqual ['constructor']
|
24
24
|
|
25
25
|
it "includes", ->
|
26
26
|
Module =
|