romo 0.19.10 → 0.20.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.
@@ -1,16 +1,12 @@
1
1
  var Romo = function() {
2
- this._eventCallbacks = [];
2
+ this.popupStack = new RomoPopupStack();
3
+ this.parentChildElems = new RomoParentChildElems();
3
4
  }
4
5
 
5
- // TODO: rework w/o jQuery
6
6
  Romo.prototype.doInit = function() {
7
- this.parentChildElems = new RomoParentChildElems();
8
-
9
- $.each(this._eventCallbacks, function(idx, eventCallback) {
10
- $('body').on(eventCallback.eventName, eventCallback.callback);
11
- });
12
-
13
- this.triggerInitUI($('body'));
7
+ this.popupStack.doInit();
8
+ this.parentChildElems.doInit();
9
+ this.initElems(Romo.f('body'));
14
10
  }
15
11
 
16
12
  // element finders
@@ -19,42 +15,78 @@ Romo.prototype.f = function(selector) {
19
15
  return this.array(document.querySelectorAll(selector));
20
16
  }
21
17
 
22
- Romo.prototype.find = function(parentElem, selector) {
23
- return this.array(parentElem.querySelectorAll(selector));
18
+ Romo.prototype.find = function(parentElems, selector) {
19
+ return this.array(parentElems).reduce(
20
+ Romo.proxy(function(foundElems, parentElem) {
21
+ return foundElems.concat(this.array(parentElem.querySelectorAll(selector)));
22
+ }, this),
23
+ []
24
+ );
24
25
  }
25
26
 
26
- Romo.prototype.children = function(parentElem) {
27
- return this.array(parentElem.children);
27
+ Romo.prototype.is = function(elem, selector) {
28
+ return (
29
+ elem.matches ||
30
+ elem.matchesSelector ||
31
+ elem.msMatchesSelector ||
32
+ elem.mozMatchesSelector ||
33
+ elem.webkitMatchesSelector ||
34
+ elem.oMatchesSelector
35
+ ).call(elem, selector);
36
+ };
37
+
38
+ Romo.prototype.children = function(parentElem, selector) {
39
+ var childElems = this.array(parentElem.children);
40
+ if (selector) {
41
+ return childElems.filter(function(childElem) {
42
+ return Romo.is(childElem, selector);
43
+ });
44
+ } else {
45
+ return childElems;
46
+ }
28
47
  }
29
48
 
30
49
  Romo.prototype.parent = function(childElem) {
31
50
  return childElem.parentNode;
32
51
  }
33
52
 
34
- Romo.prototype.siblings = function(elem) {
35
- return Array.prototype.filter.call(elem.parentNode.children, function(childElem) {
36
- return childElem !== elem;
37
- });
38
- }
39
-
40
- Romo.prototype.prev = function(fromElem) {
41
- return fromElem.previousElementSibling;
53
+ Romo.prototype.parents = function(childElem, selector) {
54
+ var parentElem = this.parent(childElem);
55
+ if (parentElem && parentElem !== document) {
56
+ if (!selector || Romo.is(parentElem, selector)) {
57
+ if (Romo.is(parentElem, 'body')) {
58
+ return [parentElem];
59
+ } else {
60
+ return [parentElem].concat(this.parents(parentElem, selector));
61
+ }
62
+ } else {
63
+ if (Romo.is(parentElem, 'body')) {
64
+ return [];
65
+ } else {
66
+ return this.parents(parentElem, selector);
67
+ }
68
+ }
69
+ } else {
70
+ return [];
71
+ }
42
72
  }
43
73
 
44
- Romo.prototype.next = function(fromElem) {
45
- return fromElem.nextElementSibling;
74
+ Romo.prototype.scrollableParents = function(childElem, selector) {
75
+ return Romo.parents(childElem, selector).filter(function(parentElem) {
76
+ return (
77
+ Romo._overflowScrollableRegex.test(Romo.css(parentElem, 'overflow')) ||
78
+ Romo._overflowScrollableRegex.test(Romo.css(parentElem, 'overflow-y')) ||
79
+ Romo._overflowScrollableRegex.test(Romo.css(parentElem, 'overflow-x'))
80
+ );
81
+ });
46
82
  }
47
83
 
48
84
  Romo.prototype.closest = function(fromElem, selector) {
49
85
  if (fromElem.closest) {
50
86
  return fromElem.closest(selector);
51
87
  } else {
52
- var matchesSelector = fromElem.matches ||
53
- fromElem.webkitMatchesSelector ||
54
- fromElem.mozMatchesSelector ||
55
- fromElem.msMatchesSelector;
56
88
  while (fromElem) {
57
- if (matchesSelector.call(fromElem, selector)) {
89
+ if (Romo.is(fromElem, selector)) {
58
90
  return fromElem;
59
91
  } else {
60
92
  fromElem = fromElem.parentElement;
@@ -64,80 +96,133 @@ Romo.prototype.closest = function(fromElem, selector) {
64
96
  }
65
97
  }
66
98
 
67
- // TODO: rework w/o jQuery
68
- Romo.prototype.selectNext = function(elem, selector) {
69
- // like `$().next()`, but takes a selector; returns first next elem that
70
- // matches the given selector or an empty collection if non matches
71
- var el = elem.next();
72
- while(el.length) {
73
- if (selector === undefined || el.is(selector)) {
74
- return el;
99
+ Romo.prototype.siblings = function(elem, selector) {
100
+ return Romo.children(Romo.parent(elem), selector).filter(function(childElem) {
101
+ return childElem !== elem;
102
+ });
103
+ }
104
+
105
+ Romo.prototype.prev = function(fromElem, selector) {
106
+ var elem = fromElem.previousElementSibling;
107
+ if (elem === null) {
108
+ elem = undefined;
109
+ }
110
+
111
+ while(elem) {
112
+ if (!selector || Romo.is(elem, selector)) {
113
+ return elem;
114
+ }
115
+ elem = elem.previousElementSibling;
116
+ if (elem === null) {
117
+ elem = undefined;
75
118
  }
76
- el = el.next();
77
119
  }
78
- return el;
120
+ return elem;
79
121
  }
80
122
 
81
- // TODO: rework w/o jQuery
82
- Romo.prototype.selectPrev = function(elem, selector) {
83
- // like `$().prev()`, but takes a selector; returns first prev elem that
84
- // matches the given selector or an empty collection if non matches
85
- var el = elem.prev();
86
- while(el.length) {
87
- if (selector === undefined || el.is(selector)) {
88
- return el;
123
+ Romo.prototype.next = function(fromElem, selector) {
124
+ var elem = fromElem.nextElementSibling;
125
+ if (elem === null) {
126
+ elem = undefined;
127
+ }
128
+
129
+ while(elem) {
130
+ if (!selector || Romo.is(elem, selector)) {
131
+ return elem;
132
+ }
133
+ elem = elem.nextElementSibling;
134
+ if (elem === null) {
135
+ elem = undefined;
89
136
  }
90
- el = el.prev();
91
137
  }
92
- return el;
138
+ return elem;
93
139
  }
94
140
 
95
141
  // attributes, styles, classes
96
142
 
97
143
  Romo.prototype.attr = function(elem, attrName) {
98
- return elem.getAttribute ? elem.getAttribute(attrName) : undefined;
144
+ var a = elem.getAttribute(attrName);
145
+ if (a === null) {
146
+ return undefined;
147
+ } else {
148
+ return a;
149
+ }
99
150
  }
100
151
 
101
- Romo.prototype.setAttr = function(elem, attrName, attrValue) {
102
- if (elem.setAttribute) {
103
- elem.setAttribute(attrName, attrValue);
104
- }
105
- return attrValue;
152
+ Romo.prototype.setAttr = function(elems, attrName, attrValue) {
153
+ var v = String(attrValue);
154
+ Romo.array(elems).forEach(function(elem) {
155
+ elem.setAttribute(attrName, v);
156
+ });
157
+ return v;
158
+ }
159
+
160
+ Romo.prototype.rmAttr = function(elems, attrName) {
161
+ Romo.array(elems).forEach(function(elem) {
162
+ elem.removeAttribute(attrName);
163
+ });
106
164
  }
107
165
 
108
166
  Romo.prototype.data = function(elem, dataName) {
109
167
  return this._deserializeValue(this.attr(elem, "data-"+dataName));
110
168
  }
111
169
 
112
- Romo.prototype.setData = function(elem, dataName, dataValue) {
113
- return this.setAttr(elem, "data-"+dataName, String(dataValue));
170
+ Romo.prototype.setData = function(elems, dataName, dataValue) {
171
+ return this.setAttr(elems, "data-"+dataName, dataValue);
172
+ }
173
+
174
+ Romo.prototype.rmData = function(elems, dataName) {
175
+ this.rmAttr(elems, "data-"+dataName);
114
176
  }
115
177
 
116
178
  Romo.prototype.style = function(elem, styleName) {
117
179
  return elem.style[styleName];
118
180
  }
119
181
 
120
- Romo.prototype.setStyle = function(elem, styleName, styleValue) {
121
- elem.style[styleName] = styleValue;
182
+ Romo.prototype.setStyle = function(elems, styleName, styleValue) {
183
+ Romo.array(elems).forEach(function(elem) {
184
+ elem.style[styleName] = styleValue;
185
+ });
122
186
  return styleValue;
123
187
  }
124
188
 
189
+ Romo.prototype.rmStyle = function(elems, styleName) {
190
+ Romo.array(elems).forEach(function(elem) {
191
+ elem.style[styleName] = '';
192
+ });
193
+ }
194
+
125
195
  Romo.prototype.css = function(elem, styleName) {
126
196
  return window.getComputedStyle(elem, null).getPropertyValue(styleName);
127
197
  }
128
198
 
129
- Romo.prototype.addClass = function(elem, className) {
130
- elem.classList.add(className);
199
+ Romo.prototype.addClass = function(elems, className) {
200
+ var classNames = className.split(' ').filter(function(n){ return n; });
201
+ Romo.array(elems).forEach(function(elem) {
202
+ classNames.forEach(function(name) {
203
+ elem.classList.add(name);
204
+ });
205
+ });
131
206
  return className;
132
207
  }
133
208
 
134
- Romo.prototype.removeClass = function(elem, className) {
135
- elem.classList.remove(className);
209
+ Romo.prototype.removeClass = function(elems, className) {
210
+ var classNames = className.split(' ').filter(function(n){ return n; });
211
+ Romo.array(elems).forEach(function(elem) {
212
+ classNames.forEach(function(name) {
213
+ elem.classList.remove(name);
214
+ });
215
+ });
136
216
  return className;
137
217
  }
138
218
 
139
- Romo.prototype.toggleClass = function(elem, className) {
140
- elem.classList.toggle(className);
219
+ Romo.prototype.toggleClass = function(elems, className) {
220
+ var classNames = className.split(' ').filter(function(n){ return n; });
221
+ Romo.array(elems).forEach(function(elem) {
222
+ classNames.forEach(function(name) {
223
+ elem.classList.toggle(name);
224
+ });
225
+ });
141
226
  return className;
142
227
  }
143
228
 
@@ -145,19 +230,24 @@ Romo.prototype.hasClass = function(elem, className) {
145
230
  return elem.classList.contains(className);
146
231
  }
147
232
 
148
- Romo.prototype.show = function(elem) {
149
- elem.style.display = '';
233
+ Romo.prototype.show = function(elems) {
234
+ Romo.array(elems).forEach(function(elem) {
235
+ elem.style.display = '';
236
+ });
150
237
  }
151
238
 
152
- Romo.prototype.hide = function(elem) {
153
- elem.style.display = 'none';
239
+ Romo.prototype.hide = function(elems) {
240
+ Romo.array(elems).forEach(function(elem) {
241
+ elem.style.display = 'none';
242
+ });
154
243
  }
155
244
 
156
245
  Romo.prototype.offset = function(elem) {
157
- var rect = elem.getBoundingClientRect();
246
+ var elemRect = elem.getBoundingClientRect();
247
+ var bodyRect = document.body.getBoundingClientRect();
158
248
  return {
159
- top: rect.top + document.body.scrollTop,
160
- left: rect.left + document.body.scrollLeft
249
+ top: elemRect.top - bodyRect.top,
250
+ left: elemRect.left - bodyRect.left
161
251
  };
162
252
  }
163
253
 
@@ -169,28 +259,26 @@ Romo.prototype.scrollLeft = function(elem) {
169
259
  return ('scrollLeft' in elem) ? elem.scrollLeft : elem.pageXOffset;
170
260
  }
171
261
 
172
- Romo.prototype.setScrollTop = function(elem, value) {
173
- if ('scrollTop' in elem) {
174
- elem.scrollTop = value;
175
- } else {
176
- elem.scrollTo(elem.scrollX, value);
177
- }
178
- }
179
-
180
- Romo.prototype.setScrollLeft = function(elem, value) {
181
- if ('scrollLeft' in elem) {
182
- elem.scrollLeft = value;
183
- } else {
184
- elem.scrollTo(value, elem.scrollY);
185
- }
262
+ Romo.prototype.setScrollTop = function(elems, value) {
263
+ Romo.array(elems).forEach(function(elem) {
264
+ if ('scrollTop' in elem) {
265
+ elem.scrollTop = value;
266
+ } else {
267
+ elem.scrollTo(elem.scrollX, value);
268
+ }
269
+ });
186
270
  }
187
271
 
188
- // TODO: rework w/o jQuery
189
- Romo.prototype.getComputedStyle = function(node, styleName) {
190
- return window.getComputedStyle(node, null).getPropertyValue(styleName);
272
+ Romo.prototype.setScrollLeft = function(elems, value) {
273
+ Romo.array(elems).forEach(function(elem) {
274
+ if ('scrollLeft' in elem) {
275
+ elem.scrollLeft = value;
276
+ } else {
277
+ elem.scrollTo(value, elem.scrollY);
278
+ }
279
+ });
191
280
  }
192
281
 
193
- // TODO: rework w/o jQuery
194
282
  Romo.prototype.parseZIndex = function(elem) {
195
283
  // for the case where z-index is set directly on the elem
196
284
  var val = this.parseElemZIndex(elem);
@@ -199,27 +287,26 @@ Romo.prototype.parseZIndex = function(elem) {
199
287
  }
200
288
 
201
289
  // for the case where z-index is inherited from a parent elem
202
- var parentIndexes = this.toArray(elem.parents()).reduce($.proxy(function(prev, curr) {
203
- var pval = this.parseElemZIndex($(curr));
204
- if (pval !== 0) {
205
- prev.push(pval);
290
+ var pval = 0;
291
+ var parentIndexes = Romo.parents(elem).forEach(Romo.proxy(function(parentElem) {
292
+ if (pval === 0) {
293
+ pval = this.parseElemZIndex(parentElem);
206
294
  }
207
- return prev;
208
- }, this), []);
209
- parentIndexes.push(0); // in case z-index is 'auto' all the way up
210
- return parentIndexes[0];
295
+ }, this));
296
+
297
+ // z-index is 'auto' all the way up
298
+ return pval;
211
299
  }
212
300
 
213
- // TODO: rework w/o jQuery
214
301
  Romo.prototype.parseElemZIndex = function(elem) {
215
- var val = parseInt(this.getComputedStyle(elem[0], "z-index"));
302
+ var val = parseInt(this.css(elem, "z-index"), 10);
216
303
  if (!isNaN(val)) {
217
304
  return val;
218
305
  }
219
306
  return 0;
220
307
  }
221
308
 
222
- // DOM manipulation
309
+ // elems init
223
310
 
224
311
  Romo.prototype.elems = function(htmlString) {
225
312
  var context = document.implementation.createHTMLDocument();
@@ -230,12 +317,46 @@ Romo.prototype.elems = function(htmlString) {
230
317
  base.href = document.location.href;
231
318
  context.head.appendChild(base);
232
319
 
233
- context.body.innerHTML = htmlString;
234
- return this.array(context.body.children);
320
+ var results = Romo._elemsTagNameRegEx.exec(htmlString);
321
+ if (!results){ return []; }
322
+
323
+ var tagName = results[1].toLowerCase();
324
+ var wrap = Romo._elemsWrapMap[tagName];
325
+ if (!wrap) {
326
+ context.body.innerHTML = htmlString;
327
+ return this.array(context.body.children);
328
+ } else {
329
+ context.body.innerHTML = wrap[1] + htmlString + wrap[2];
330
+ var parentElem = context.body;
331
+ var i = wrap[0];
332
+ while(i-- !== 0) {
333
+ parentElem = parentElem.lastChild;
334
+ }
335
+ return this.array(parentElem.children)
336
+ }
337
+ }
338
+
339
+ Romo.prototype.addElemsInitSelector = function(selector, componentClass) {
340
+ this._elemsInitComponents[selector] = componentClass;
341
+ }
342
+
343
+ Romo.prototype.initElems = function(elems) {
344
+ return this._elemsInitTrigger(Romo.array(elems));
345
+ }
346
+
347
+ Romo.prototype.initElemsHtml = function(htmlString) {
348
+ return this.initElems(this.elems(htmlString));
235
349
  }
236
350
 
237
- Romo.prototype.remove = function(elem) {
238
- return elem.parentNode.removeChild(elem);
351
+ // DOM manipulation
352
+
353
+ Romo.prototype.remove = function(elems) {
354
+ return Romo.array(elems).map(function(elem) {
355
+ if (elem.parentNode) {
356
+ elem.parentNode.removeChild(elem);
357
+ }
358
+ return elem;
359
+ });
239
360
  }
240
361
 
241
362
  Romo.prototype.replace = function(elem, replacementElem) {
@@ -244,131 +365,200 @@ Romo.prototype.replace = function(elem, replacementElem) {
244
365
  }
245
366
 
246
367
  Romo.prototype.replaceHtml = function(elem, htmlString) {
247
- return this.replace(elem, this.elems(htmlString)[0]);
368
+ var replacementElem = Romo.elems(htmlString)[0];
369
+ if (replacementElem === undefined && (typeof(htmlString) !== 'string' || htmlString.trim() !== '')) {
370
+ throw new Error("Invalid HTML string, doesn't contain an HTML element.");
371
+ }
372
+ return this.replace(elem, replacementElem);
373
+ }
374
+
375
+ Romo.prototype.initReplace = function(elem, replacementElem) {
376
+ return this.initElems(this.replace(elem, replacementElem))[0];
377
+ }
378
+
379
+ Romo.prototype.initReplaceHtml = function(elem, htmlString) {
380
+ return this.initElems(this.replaceHtml(elem, htmlString))[0];
248
381
  }
249
382
 
250
383
  Romo.prototype.update = function(elem, childElems) {
251
384
  elem.innerHTML = '';
252
- return childElems.map(function(childElem) {
385
+ return Romo.array(childElems).map(function(childElem) {
253
386
  return elem.appendChild(childElem);
254
387
  });
255
388
  }
256
389
 
257
390
  Romo.prototype.updateHtml = function(elem, htmlString) {
258
- return this.update(elem, this.elems(htmlString));
391
+ var childElems = Romo.elems(htmlString);
392
+ if (childElems.length === 0 && (typeof(htmlString) !== 'string' || htmlString.trim() !== '')) {
393
+ throw new Error("Invalid HTML string, doesn't contain any HTML elements.");
394
+ }
395
+ return this.update(elem, childElems);
396
+ }
397
+
398
+ Romo.prototype.updateText = function(elem, textString) {
399
+ elem.innerText = textString;
400
+ }
401
+
402
+ Romo.prototype.initUpdate = function(elem, childElems) {
403
+ return this.initElems(this.update(elem, childElems));
404
+ }
405
+
406
+ Romo.prototype.initUpdateHtml = function(elem, htmlString) {
407
+ return this.initElems(this.updateHtml(elem, htmlString));
259
408
  }
260
409
 
261
410
  Romo.prototype.prepend = function(elem, childElems) {
262
411
  var refElem = elem.firstChild;
263
- return childElems.reverse().map(function(childElem) {
412
+ return Romo.array(childElems).reverse().map(function(childElem) {
264
413
  refElem = elem.insertBefore(childElem, refElem);
265
414
  return refElem;
266
415
  }).reverse();
267
416
  }
268
417
 
269
418
  Romo.prototype.prependHtml = function(elem, htmlString) {
270
- return this.prepend(elem, this.elems(htmlString));
419
+ var childElems = Romo.elems(htmlString);
420
+ if (childElems.length === 0 && (typeof(htmlString) !== 'string' || htmlString.trim() !== '')) {
421
+ throw new Error("Invalid HTML string, doesn't contain any HTML elements.");
422
+ }
423
+ return this.prepend(elem, childElems);
424
+ }
425
+
426
+ Romo.prototype.initPrepend = function(elem, childElems) {
427
+ return this.initElems(this.prepend(elem, childElems));
428
+ }
429
+
430
+ Romo.prototype.initPrependHtml = function(elem, htmlString) {
431
+ return this.initElems(this.prependHtml(elem, htmlString));
271
432
  }
272
433
 
273
434
  Romo.prototype.append = function(elem, childElems) {
274
- return childElems.map(function(childElem) {
435
+ return Romo.array(childElems).map(function(childElem) {
275
436
  return elem.appendChild(childElem);
276
437
  });
277
438
  }
278
439
 
279
440
  Romo.prototype.appendHtml = function(elem, htmlString) {
280
- return this.append(elem, this.elems(htmlString));
441
+ var childElems = Romo.elems(htmlString);
442
+ if (childElems.length === 0 && (typeof(htmlString) !== 'string' || htmlString.trim() !== '')) {
443
+ throw new Error("Invalid HTML string, doesn't contain any HTML elements.");
444
+ }
445
+ return this.append(elem, childElems);
446
+ }
447
+
448
+ Romo.prototype.initAppend = function(elem, childElems) {
449
+ return this.initElems(this.append(elem, childElems));
450
+ }
451
+
452
+ Romo.prototype.initAppendHtml = function(elem, htmlString) {
453
+ return this.initElems(this.appendHtml(elem, htmlString));
281
454
  }
282
455
 
283
456
  Romo.prototype.before = function(elem, siblingElems) {
284
457
  var refElem = elem;
285
458
  var parentElem = elem.parentNode;
286
- return siblingElems.reverse().map(function(siblingElem) {
459
+ return Romo.array(siblingElems).reverse().map(function(siblingElem) {
287
460
  refElem = parentElem.insertBefore(siblingElem, refElem);
288
461
  return refElem;
289
462
  }).reverse();
290
463
  }
291
464
 
292
465
  Romo.prototype.beforeHtml = function(elem, htmlString) {
293
- return this.before(elem, this.elems(htmlString));
466
+ var siblingElems = Romo.elems(htmlString);
467
+ if (siblingElems.length === 0 && (typeof(htmlString) !== 'string' || htmlString.trim() !== '')) {
468
+ throw new Error("Invalid HTML string, doesn't contain any HTML elements.");
469
+ }
470
+ return this.before(elem, siblingElems);
471
+ }
472
+
473
+ Romo.prototype.initBefore = function(elem, siblingElems) {
474
+ return this.initElems(this.before(elem, siblingElems));
475
+ }
476
+
477
+ Romo.prototype.initBeforeHtml = function(elem, htmlString) {
478
+ return this.initElems(this.beforeHtml(elem, htmlString));
294
479
  }
295
480
 
296
481
  Romo.prototype.after = function(elem, siblingElems) {
297
482
  var refElem = this.next(elem);
298
483
  var parentElem = elem.parentNode;
299
- return siblingElems.map(function(siblingElem) {
484
+ return Romo.array(siblingElems).map(function(siblingElem) {
300
485
  return parentElem.insertBefore(siblingElem, refElem);
301
486
  });
302
487
  }
303
488
 
304
489
  Romo.prototype.afterHtml = function(elem, htmlString) {
305
- return this.after(elem, this.elems(htmlString));
490
+ var siblingElems = Romo.elems(htmlString);
491
+ if (siblingElems.length === 0 && (typeof(htmlString) !== 'string' || htmlString.trim() !== '')) {
492
+ throw new Error("Invalid HTML string, doesn't contain any HTML elements.");
493
+ }
494
+ return this.after(elem, siblingElems);
306
495
  }
307
496
 
308
- // init UI (TODO: rework w/o jQuery)
309
-
310
- Romo.prototype.onInitUI = function(callback) {
311
- this._addEventCallback('romo:initUI', callback);
497
+ Romo.prototype.initAfter = function(elem, siblingElems) {
498
+ return this.initElems(this.after(elem, siblingElems));
312
499
  }
313
500
 
314
- Romo.prototype.triggerInitUI = function(elems) {
315
- elems.trigger('romo:initUI');
501
+ Romo.prototype.initAfterHtml = function(elem, htmlString) {
502
+ return this.initElems(this.afterHtml(elem, htmlString));
316
503
  }
317
504
 
318
- Romo.prototype.initUIElems = function(e, selector) {
319
- var elems = $(e.target).find(selector).get();
320
- if ($(e.target).is(selector)) {
321
- elems.push(e.target)
322
- }
323
- return $(elems);
324
- }
505
+ // events
325
506
 
326
- Romo.prototype.initHtml = function(elems, data) {
327
- elems.each($.proxy(function(index, elem) {
328
- var htmlElems = $(data)
329
- $(elem).html(htmlElems);
330
- this.triggerInitUI(htmlElems);
331
- }, this));
332
- }
507
+ Romo.prototype.on = function(elems, eventName, fn) {
508
+ var proxyFn = function(e) {
509
+ var result = fn.apply(e.target, e.detail === undefined ? [e] : [e].concat(e.detail));
510
+ if (result === false) {
511
+ e.preventDefault();
512
+ e.stopPropagation();
513
+ }
514
+ return result;
515
+ }
516
+ proxyFn._romofid = this._fn(fn)._romofid;
333
517
 
334
- Romo.prototype.initReplace = function(elems, data) {
335
- elems.each($.proxy(function(index, elem) {
336
- var replacementElem = $(data);
337
- $(elem).replaceWith(replacementElem);
338
- this.triggerInitUI(replacementElem);
518
+ Romo.array(elems).forEach(Romo.proxy(function(elem) {
519
+ elem.addEventListener(eventName, proxyFn);
520
+ var key = this._handlerKey(elem, eventName, proxyFn);
521
+ if (this._handlers[key] === undefined) {
522
+ this._handlers[key] = [];
523
+ }
524
+ this._handlers[key].push(proxyFn);
339
525
  }, this));
340
526
  }
341
527
 
342
- Romo.prototype.initPrepend = function(elems, data) {
343
- elems.each($.proxy(function(index, elem) {
344
- var prependedElem = $(data);
345
- $(elem).prepend(prependedElem);
346
- this.triggerInitUI(prependedElem);
528
+ Romo.prototype.off = function(elems, eventName, fn) {
529
+ Romo.array(elems).forEach(Romo.proxy(function(elem) {
530
+ var key = this._handlerKey(elem, eventName, fn);
531
+ (this._handlers[key] || []).forEach(function(proxyFn) {
532
+ elem.removeEventListener(eventName, proxyFn);
533
+ });
534
+ this._handlers[key] = [];
347
535
  }, this));
348
536
  }
349
537
 
350
- Romo.prototype.initAppend = function(elems, data) {
351
- elems.each($.proxy(function(index, elem) {
352
- var appendedElem = $(data);
353
- $(elem).append(appendedElem);
354
- this.triggerInitUI(appendedElem);
355
- }, this));
538
+ Romo.prototype.trigger = function(elems, customEventName, args) {
539
+ var event = undefined;
540
+ if (typeof window.CustomEvent === "function") {
541
+ event = new CustomEvent(customEventName, { detail: args });
542
+ } else {
543
+ event = document.createEvent('CustomEvent');
544
+ event.initCustomEvent(customEventName, false, false, args);
545
+ }
546
+ Romo.array(elems).forEach(function(elem) {
547
+ elem.dispatchEvent(event);
548
+ });
356
549
  }
357
550
 
358
- Romo.prototype.initBefore = function(elems, data) {
359
- elems.each($.proxy(function(index, elem) {
360
- var insertedElem = $(data);
361
- $(elem).before(insertedElem);
362
- this.triggerInitUI(insertedElem);
363
- }, this));
551
+ Romo.prototype.pushFn = function(fn) {
552
+ // push the function to delay running until the end of the reactor stack
553
+ setTimeout(fn, 1);
364
554
  }
365
555
 
366
- Romo.prototype.initAfter = function(elems, data) {
367
- elems.each($.proxy(function(index, elem) {
368
- var insertedElem = $(data);
369
- $(elem).after(insertedElem);
370
- this.triggerInitUI(insertedElem);
371
- }, this));
556
+ Romo.prototype.ready = function(eventHandlerFn) {
557
+ if (document.readyState === 'complete' || document.readyState !== 'loading') {
558
+ eventHandlerFn();
559
+ } else {
560
+ this.on(document, 'DOMContentLoaded', eventHandlerFn);
561
+ }
372
562
  }
373
563
 
374
564
  // page handling
@@ -381,20 +571,43 @@ Romo.prototype.redirectPage = function(redirectUrl) {
381
571
  window.location = redirectUrl;
382
572
  }
383
573
 
384
- // param serialization (TODO: rework w/o jQuery)
574
+ // param serialization
575
+
576
+ // Romo.param({ a: 2, b: 'three', c: 4 }); #=> "a=2&b=three&c=4"
577
+ // Romo.param({ a: [ 2, 3, 4 ] }); #=> "a[]=2&a[]=3&a[]=4"
578
+ // Romo.param({ a: 2, b: '', c: 4 }); #=> "a=2&b=&c=4"
579
+ // Romo.param({ a: 2, b: '', c: 4 }, { removeEmpty: true }); #=> "a=2&c=4"
580
+ // Romo.param({ a: [ 2, 3, 4 ], b: [''] }); #=> "a[]=2&a[]=3&a[]=4&b[]="
581
+ // Romo.param({ a: [ 2, 3, 4 ], b: [''] }, { removeEmpty: true }); #=> "a[]=2&a[]=3&a[]=4"
582
+ // Romo.param({ a: '123-ABC' }); #=> "a=123%2DABC"
583
+ // Romo.param({ a: '123-ABC' }, { decodeValues: true }); #=> "a=123-ABC"
385
584
 
386
585
  Romo.prototype.param = function(data, opts) {
387
- var paramData = $.extend({}, data);
586
+ var keyValues = [];
587
+
588
+ var processKeyValue = function(keyValues, key, value, opts) {
589
+ var v = String(value);
590
+ if (!opts || !opts.removeEmpty || v !== '') {
591
+ keyValues.push([key, v]);
592
+ }
593
+ }
388
594
 
389
- if (opts && opts.removeEmpty) {
390
- $.each(paramData, function(key, value) {
391
- if (value === '') {
392
- delete paramData[key];
595
+ for (var key in data) {
596
+ var value = data[key];
597
+ if (Array.isArray(value)) {
598
+ if (!opts || !opts.removeEmpty || value.length !== 0) {
599
+ value.forEach(function(listValue) {
600
+ processKeyValue(keyValues, key+'[]', listValue, opts);
601
+ })
393
602
  }
394
- })
603
+ } else {
604
+ processKeyValue(keyValues, key, value, opts);
605
+ }
395
606
  }
396
607
 
397
- var paramString = $.param(paramData);
608
+ var paramString = keyValues.map(function(keyValue){
609
+ return keyValue.join('=');
610
+ }).join('&');
398
611
 
399
612
  if (opts && opts.decodeValues) {
400
613
  paramString = this.decodeParam(paramString);
@@ -443,13 +656,25 @@ Romo.prototype.decodeParamMap = [
443
656
  Romo.prototype.ajax = function(settings) {
444
657
  var httpMethod = (settings.type || 'GET').toUpperCase();
445
658
  var xhrUrl = settings.url || window.location.toString();
446
- var xhrData = settings.data ? settings.data : null
447
- if (xhrData && httpMethod === 'GET') {
448
- var xhrQuery = Romo.param(xhrData);
449
- if (xhrQuery !== '') {
450
- xhrUrl = (xhrUrl + '&' + xhrQuery).replace(/[&?]{1,2}/, '?');
659
+ var xhrData = settings.data ? settings.data : undefined
660
+ if (xhrData && Object.keys(xhrData).length > 0) {
661
+ if (httpMethod === 'GET') {
662
+ var xhrQuery = Romo.param(xhrData);
663
+ if (xhrQuery !== '') {
664
+ xhrUrl = (xhrUrl + '&' + xhrQuery).replace(/[&?]{1,2}/, '?');
665
+ }
666
+ xhrData = undefined;
667
+ } else {
668
+ var formData = new FormData;
669
+ for (var name in xhrData) {
670
+ Romo.array(xhrData[name]).forEach(function(value){
671
+ formData.append(name, value)
672
+ });
673
+ }
674
+ xhrData = formData;
451
675
  }
452
- xhrData = null;
676
+ } else {
677
+ xhrData = undefined;
453
678
  }
454
679
 
455
680
  var xhr = new XMLHttpRequest();
@@ -466,13 +691,13 @@ Romo.prototype.ajax = function(settings) {
466
691
  }
467
692
  xhr.onreadystatechange = function() {
468
693
  if (xhr.readyState === 4) {
469
- if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
694
+ if (settings.success !== undefined && ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)) {
470
695
  if (xhr.responseType === 'arraybuffer' || xhr.responseType === 'blob') {
471
696
  settings.success.call(window, xhr.response, xhr.status, xhr, settings);
472
697
  } else {
473
698
  settings.success.call(window, xhr.responseText, xhr.status, xhr, settings);
474
699
  }
475
- } else {
700
+ } else if(settings.error !== undefined) {
476
701
  settings.error.call(window, xhr.statusText || null, xhr.status, xhr, settings);
477
702
  }
478
703
  }
@@ -480,67 +705,84 @@ Romo.prototype.ajax = function(settings) {
480
705
  xhr.send(xhrData);
481
706
  },
482
707
 
483
- // events
708
+ // utils
484
709
 
485
- Romo.prototype.on = function(elem, eventName, fn) {
486
- // var proxyFn = function(e) {
487
- // var result = fn.apply(elem, e.detail === undefined ? [e] : [e].concat(e.detail));
488
- // if (result === false) {
489
- // e.preventDefault();
490
- // e.stopPropagation();
491
- // }
492
- // return result;
493
- // }
494
- // proxyFn._romofid = this._fn(fn)._romofid;
495
-
496
- // var key = this._handlerKey(elem, eventName, proxyFn);
497
- // if (!this._handlers[key]) {
498
- // elem.addEventListener(eventName, proxyFn);
499
- // this._handlers[key] = proxyFn;
500
- // }
501
-
502
- // Giant Hack to temporarily support jQuery and non-jQuery triggers
503
- // see: https://bugs.jquery.com/ticket/11047
504
- $(elem).on(eventName, fn);
505
- }
506
-
507
- Romo.prototype.off = function(elem, eventName, fn) {
508
- // var key = this._handlerKey(elem, eventName, fn);
509
- // var proxyFn = this._handlers[key];
510
- // if (proxyFn) {
511
- // elem.removeEventListener(eventName, proxyFn);
512
- // this._handlers[key] = undefined;
513
- // }
514
-
515
- // Giant Hack to temporarily support jQuery and non-jQuery triggers
516
- // see: https://bugs.jquery.com/ticket/11047
517
- $(elem).off(eventName, fn);
518
- }
519
-
520
- Romo.prototype.trigger = function(elem, customEventName, args) {
521
- // var event = undefined;
522
- // if (typeof window.CustomEvent === "function") {
523
- // event = new CustomEvent(customEventName, { detail: args });
524
- // } else {
525
- // event = document.createEvent('CustomEvent');
526
- // event.initCustomEvent(customEventName, false, false, args);
527
- // }
528
- // elem.dispatchEvent(event);
529
- $(elem).trigger(customEventName, args);
530
- }
710
+ Romo.prototype.array = function(value) {
711
+ // short circuit `Romo.f`, `Romo.find`, and `Romo.elems` calls (and others
712
+ // that return NodeList or HTMLCollection objects), this ensures these calls
713
+ // remain fast and don't run through all of the logic to detect if an object
714
+ // is like an array
715
+ var valString = Object.prototype.toString.call(value);
716
+ if (
717
+ valString === '[object NodeList]' ||
718
+ valString === '[object HTMLCollection]' ||
719
+ Array.isArray(value)
720
+ ) {
721
+ return Array.prototype.slice.call(value)
722
+ }
531
723
 
532
- Romo.prototype.ready = function(eventHandlerFn) {
533
- if (document.readyState === 'complete' || document.readyState !== 'loading') {
534
- eventHandlerFn();
724
+ // short circuit for passing individual elems and "not truthy" values, this
725
+ // ensures these remain fast (the individual elems) and avoids running into
726
+ // the is like an array logic; this fixes issues with select and form elems
727
+ // being like an array and returning unexpected results. This also fixes
728
+ // passing in null/undefined values.
729
+ if (!value || typeof(value.nodeType) === 'number') {
730
+ return [value];
731
+ }
732
+
733
+ var object = Object(value)
734
+ var length = undefined;
735
+ if (!!object && 'length' in object) {
736
+ length = object.length;
737
+ }
738
+
739
+ // some browsers return 'function' for HTML elements
740
+ var isFunction = (
741
+ typeof(object) === 'function' &&
742
+ typeof(object.nodeType) !== 'number'
743
+ );
744
+ var likeArray = (
745
+ typeof(value) !== 'string' &&
746
+ !isFunction &&
747
+ object !== window &&
748
+ ( Array.isArray(object) ||
749
+ length === 0 ||
750
+ ( typeof(length) === 'number' &&
751
+ length > 0 &&
752
+ (length - 1) in object
753
+ )
754
+ )
755
+ );
756
+ if(likeArray) {
757
+ return Array.prototype.slice.call(value);
535
758
  } else {
536
- this.on(document, 'DOMContentLoaded', eventHandlerFn);
759
+ return [value];
537
760
  }
538
761
  }
539
762
 
540
- // utils
763
+ Romo.prototype.assign = function(target) {
764
+ if(Object.assign) {
765
+ return Object.assign.apply(Object, arguments);
766
+ } else {
767
+ if (target == null) { // TypeError if undefined or null
768
+ throw new TypeError('Cannot convert undefined or null to object');
769
+ }
770
+ var to = Object(target);
771
+
772
+ for (var index = 1; index < arguments.length; index++) {
773
+ var nextSource = arguments[index];
541
774
 
542
- Romo.prototype.array = function(collection) {
543
- return Array.prototype.slice.call(collection);
775
+ if (nextSource != null) { // Skip over if undefined or null
776
+ for (var nextKey in nextSource) {
777
+ // Avoid bugs when hasOwnProperty is shadowed
778
+ if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
779
+ to[nextKey] = nextSource[nextKey];
780
+ }
781
+ }
782
+ }
783
+ }
784
+ return to;
785
+ }
544
786
  }
545
787
 
546
788
  Romo.prototype.proxy = function(fn, context) {
@@ -550,12 +792,6 @@ Romo.prototype.proxy = function(fn, context) {
550
792
  return proxyFn;
551
793
  }
552
794
 
553
- // TODO: rework w/o jQuery
554
- Romo.prototype.toArray = function(elems) {
555
- // converts a collection of elements `$()` to an array of nodes
556
- return $.map(elems, function(node){ return node; })
557
- }
558
-
559
795
  Romo.prototype.nonInputTextKeyCodes = function() {
560
796
  // https://css-tricks.com/snippets/javascript/javascript-keycodes/
561
797
  return [
@@ -603,9 +839,13 @@ Romo.prototype._eid = 1;
603
839
  Romo.prototype._fid = 1;
604
840
 
605
841
  Romo.prototype._el = function(elem) {
606
- elem._romoeid || (
607
- elem._romoeid = (this.attr(elem, 'data-romo-eid') || this.setAttr(elem, 'data-romo-eid', this._eid++))
608
- );
842
+ if (!elem._romoeid) {
843
+ if (elem !== window && elem !== document) {
844
+ elem._romoeid = (this.data(elem, 'romo-eid') || this.setData(elem, 'romo-eid', this._eid++));
845
+ } else {
846
+ elem._romoeid = elem === window ? 'window' : 'document';
847
+ }
848
+ }
609
849
  return elem;
610
850
  }
611
851
 
@@ -622,66 +862,252 @@ Romo.prototype._handlerKey = function(elem, eventName, fn) {
622
862
 
623
863
  Romo.prototype._deserializeValue = function(value) {
624
864
  try {
625
- if (value === "true") { return true; } // "true" => true
626
- if (value === "false") { return false; } // "false" => false
627
- if (value === "undefined") { return undefined; } // "undefined" => undefined
628
- if (value === "null") { return null; } // "null" => null
629
- if (+value+"" === value) { return +value; } // "42.5" => 42.5
630
- if (/^[\[\{]/.test(value)) { JSON.parse(value); } // JSON => parse if valid
631
- return value; // String => self
865
+ if (value === "true") { return true; } // "true" => true
866
+ if (value === "false") { return false; } // "false" => false
867
+ if (value === "undefined") { return undefined; } // "undefined" => undefined
868
+ if (value === "null") { return null; } // "null" => null
869
+ if (value === null) { return undefined; } // null => undefined
870
+ if (+value+"" === value) { return +value; } // "42.5" => 42.5
871
+ if (/^[\[\{]/.test(value)) { return JSON.parse(value); } // JSON => parse if valid
872
+ return value; // String => self
632
873
  } catch(e) {
633
874
  return value
634
875
  }
635
876
  }
636
877
 
637
- // TODO: rework w/o jQuery
638
- Romo.prototype._addEventCallback = function(name, callback) {
639
- this._eventCallbacks.push({ eventName: name, callback: callback });
878
+ Romo.prototype._overflowScrollableRegex = /(auto|scroll)/;
879
+ Romo.prototype._elemsTagNameRegEx = /<([a-z0-9-]+)[\s\/>]+/i;
880
+
881
+ Romo.prototype._elemsWrapMap = {
882
+ 'caption': [1, "<table>", "</table>"],
883
+ 'colgroup': [1, "<table>", "</table>"],
884
+ 'col': [2, "<table><colgroup>", "</colgroup></table>"],
885
+ 'thead': [1, "<table>", "</table>"],
886
+ 'tbody': [1, "<table>", "</table>"],
887
+ 'tfoot': [1, "<table>", "</table>"],
888
+ 'tr': [2, "<table><tbody>", "</tbody></table>"],
889
+ 'th': [3, "<table><tbody><tr>", "</tr></tbody></table>"],
890
+ 'td': [3, "<table><tbody><tr>", "</tr></tbody></table>"]
891
+ };
892
+
893
+ Romo.prototype._elemsInitComponents = {};
894
+
895
+ Romo.prototype._elemsInitTrigger = function(onElems) {
896
+ for (var selector in this._elemsInitComponents) {
897
+ var componentClass = this._elemsInitComponents[selector];
898
+ this._elemsInitFind(onElems, selector).forEach(function(initElem){ new componentClass(initElem); });
899
+ }
900
+ return onElems;
901
+ }
902
+
903
+ Romo.prototype._elemsInitFind = function(onElems, selector) {
904
+ var elems = onElems.filter(function(onElem){ return Romo.is(onElem, selector); });
905
+ return elems.concat(Romo.find(onElems, selector));;
906
+ }
907
+
908
+ // RomoComponent
909
+
910
+ var RomoComponent = function(constructorFn) {
911
+ var component = function() {
912
+ RomoComponent.addEventFunctions(this);
913
+ constructorFn.apply(this, arguments);
914
+ }
915
+ component.prototype.romoEvFn = {};
916
+ component.prototype.doInit = function() {} // override as needed
917
+ return component;
918
+ }
919
+
920
+ RomoComponent.addEventFunctions = function(klassInstance) {
921
+ for(var name in klassInstance.romoEvFn) {
922
+ klassInstance[name] = RomoComponent.eventProxyFn(klassInstance.romoEvFn[name]);
923
+ }
924
+ }
925
+
926
+ RomoComponent.eventProxyFn = function(fn) {
927
+ return function(){ return fn.apply(this, arguments); };
928
+ }
929
+
930
+ // RomoPopupStack
931
+
932
+ var RomoPopupStack = function() {
933
+ this.popupSelector = undefined;
934
+ this.styleClasses = [];
935
+ this.items = [];
936
+
937
+ this._buildItemClass();
938
+ }
939
+
940
+ RomoPopupStack.prototype.doInit = function(styleClass) {
941
+ this.bodyElem = Romo.f('body')[0];
942
+ Romo.on(this.bodyElem, 'click', Romo.proxy(this._onBodyClick, this));
943
+ Romo.on(this.bodyElem, 'keyup', Romo.proxy(this._onBodyKeyUp, this));
944
+ Romo.on(window, 'resize', Romo.proxy(this._onWindowResize, this));
945
+ Romo.on(window, 'scroll', Romo.proxy(this._onWindowScroll, this));
946
+ }
947
+
948
+ RomoPopupStack.prototype.addStyleClass = function(styleClass) {
949
+ this.styleClasses.push(styleClass);
950
+ this.popupSelector = this.styleClasses.map(function(s){ return '.'+s; }).join(', ');
951
+ }
952
+
953
+ RomoPopupStack.prototype.addElem = function(popupElem, boundOpenFn, boundCloseFn, boundPlaceFn) {
954
+ this.items.push(new this.itemClass(popupElem, boundCloseFn, boundPlaceFn));
955
+
956
+ // allow any body click events to propagate and run first. This ensures
957
+ // any existing stack is in the appropriate state before opening a new popup.
958
+ Romo.pushFn(boundOpenFn);
959
+ }
960
+
961
+ RomoPopupStack.prototype.closeThru = function(popupElem) {
962
+ // allow any body click events to propagate and run first. This ensures
963
+ // any existing stack is in the appropriate state before opening a new popup.
964
+ Romo.pushFn(Romo.proxy(function() {
965
+ if (this._includes(popupElem)) {
966
+ this.closeTo(popupElem);
967
+ this._closeTop();
968
+ }
969
+ }, this));
970
+ }
971
+
972
+ RomoPopupStack.prototype.closeTo = function(popupElem) {
973
+ if (this._includes(popupElem)) {
974
+ while (this.items.length > 0 && !this.items[this.items.length-1].isFor(popupElem)) {
975
+ this._closeTop();
976
+ }
977
+ }
978
+ }
979
+
980
+ RomoPopupStack.prototype.placeAllPopups = function(includingFixed) {
981
+ this.items.filter(function(item) {
982
+ return includingFixed || Romo.css(item.popupElem, 'position') !== 'fixed';
983
+ }).forEach(function(item){
984
+ item.placeFn();
985
+ });
640
986
  }
641
987
 
642
- // RomoParentChildElems (TODO: rework w/o jQuery)
988
+ // private
989
+
990
+ RomoPopupStack.prototype._buildItemClass = function() {
991
+ this.itemClass = function(popupElem, closeFn, placeFn) {
992
+ this.popupElem = popupElem;
993
+ this.closeFn = closeFn;
994
+ this.placeFn = placeFn;
995
+ }
996
+ this.itemClass.prototype.isFor = function(popupElem) {
997
+ return this.popupElem === popupElem;
998
+ }
999
+ }
1000
+
1001
+ RomoPopupStack.prototype._closeTop = function() {
1002
+ var item;
1003
+ if (this.items.length > 0) {
1004
+ item = this.items.pop();
1005
+ item.closeFn();
1006
+ Romo.trigger(this.bodyElem, 'romoPopupStack:popupClose', [item.popupElem, this]);
1007
+ Romo.trigger(item.popupElem, 'romoPopupStack:popupClose', [this]);
1008
+ }
1009
+ return item;
1010
+ }
1011
+
1012
+ RomoPopupStack.prototype._closeAll = function() {
1013
+ while (this.items.length > 0) {
1014
+ this._closeTop();
1015
+ }
1016
+ }
1017
+
1018
+ RomoPopupStack.prototype._includes = function(popupElem) {
1019
+ return this.items.reduce(function(included, item) {
1020
+ return included || item.isFor(popupElem);
1021
+ }, false);
1022
+ }
1023
+
1024
+ RomoPopupStack.prototype._onBodyClick = function(e) {
1025
+ var popupElem = undefined;
1026
+ if (Romo.is(e.target, this.popupSelector)) {
1027
+ popupElem = e.target;
1028
+ } else {
1029
+ popupElem = Romo.parents(e.target, this.popupSelector)[0];
1030
+ }
1031
+
1032
+ if (popupElem === undefined || !this._includes(popupElem)) {
1033
+ this._closeAll();
1034
+ } else {
1035
+ this.closeTo(popupElem);
1036
+ }
1037
+ }
1038
+
1039
+ RomoPopupStack.prototype._onBodyKeyUp = function(e) {
1040
+ var closedItem;
1041
+ if (e.keyCode === 27 /* Esc */) {
1042
+ closedItem = this._closeTop();
1043
+ if (closedItem) {
1044
+ Romo.trigger(closedItem.popupElem, 'romoPopupStack:popupClosedByEsc', [this]);
1045
+ }
1046
+ }
1047
+ }
1048
+
1049
+ RomoPopupStack.prototype._onWindowResize = function(e) {
1050
+ this.placeAllPopups(true);
1051
+ }
1052
+
1053
+ RomoPopupStack.prototype._onWindowScroll = function(e) {
1054
+ this.placeAllPopups();
1055
+ }
1056
+
1057
+ // RomoParentChildElems
643
1058
 
644
1059
  var RomoParentChildElems = function() {
645
1060
  this.attrName = 'romo-parent-elem-id';
646
1061
  this.elemId = 0;
647
1062
  this.elems = {};
1063
+ }
648
1064
 
649
- var parentRemovedObserver = new MutationObserver($.proxy(function(mutationRecords) {
650
- mutationRecords.forEach($.proxy(function(mutationRecord) {
1065
+ RomoParentChildElems.prototype.doInit = function(parentElem, childElems) {
1066
+ var parentRemovedObserver = new MutationObserver(Romo.proxy(function(mutationRecords) {
1067
+ mutationRecords.forEach(Romo.proxy(function(mutationRecord) {
651
1068
  if (mutationRecord.type === 'childList' && mutationRecord.removedNodes.length > 0) {
652
- $.each($(mutationRecord.removedNodes), $.proxy(function(idx, node) {
653
- this.remove($(node));
1069
+ mutationRecord.removedNodes.forEach(Romo.proxy(function(removedNode) {
1070
+ this.remove(removedNode);
654
1071
  }, this));
655
1072
  }
656
1073
  }, this));
657
1074
  }, this));
658
1075
 
659
- parentRemovedObserver.observe($('body')[0], {
1076
+ parentRemovedObserver.observe(Romo.f('body')[0], {
660
1077
  childList: true,
661
1078
  subtree: true
662
1079
  });
663
1080
  }
664
1081
 
665
1082
  RomoParentChildElems.prototype.add = function(parentElem, childElems) {
666
- parentElem.attr('data-'+this.attrName, this._push(childElems, parentElem.data(this.attrName)));
1083
+ // delay adding b/c the parent elem may be manipulated in the DOM resulting in the parent elem
1084
+ // being removed and then re-added to the DOM. if the child elems are associated immediately,
1085
+ // any "remove" from DOM manipulation would incorrectly remove the popup.
1086
+ Romo.pushFn(Romo.proxy(function() {
1087
+ Romo.setData(parentElem, this.attrName, this._push(childElems, Romo.data(parentElem, this.attrName)));
1088
+ }, this));
667
1089
  }
668
1090
 
669
- RomoParentChildElems.prototype.remove = function(nodeElem) {
670
- if (nodeElem.data('romo-parent-removed-observer-disabled') !== true) {
671
- if (nodeElem.data(this.attrName) !== undefined) {
672
- this._removeChildElems(nodeElem); // node is a parent elem itself
1091
+ RomoParentChildElems.prototype.remove = function(elemNode) {
1092
+ if (elemNode.nodeType !== Node.ELEMENT_NODE){ return false; }
1093
+
1094
+ if (Romo.data(elemNode, 'romo-parent-removed-observer-disabled') !== true) {
1095
+ if (Romo.data(elemNode, this.attrName) !== undefined) {
1096
+ // node is a parent elem itself
1097
+ this._removeChildElems(elemNode);
673
1098
  }
674
- $.each(nodeElem.find('[data-'+this.attrName+']'), $.proxy(function(idx, parent) {
675
- this._removeChildElems($(parent));
1099
+ Romo.find(elemNode, '[data-'+this.attrName+']').forEach(Romo.proxy(function(childParentElem) {
1100
+ this._removeChildElems(childParentElem);
676
1101
  }, this));
677
1102
  }
678
1103
  }
679
1104
 
680
- // private RomoParentChildElems
1105
+ // private
681
1106
 
682
1107
  RomoParentChildElems.prototype._removeChildElems = function(parentElem) {
683
- $.each(this._pop(parentElem.data(this.attrName)), function(idx, elem) {
684
- $(elem).remove();
1108
+ this._pop(Romo.data(parentElem, this.attrName)).forEach(function(childElem) {
1109
+ Romo.remove(childElem);
1110
+ Romo.trigger(childElem, 'romoParentChildElems:childRemoved', [childElem]);
685
1111
  });
686
1112
  };
687
1113
 
@@ -692,7 +1118,8 @@ RomoParentChildElems.prototype._push = function(items, id) {
692
1118
  if (this.elems[id] === undefined) {
693
1119
  this.elems[id] = []
694
1120
  }
695
- items.forEach($.proxy(function(item){ this.elems[id].push(item) }, this));
1121
+ this.elems[id] = this.elems[id].concat(items);
1122
+
696
1123
  return id;
697
1124
  };
698
1125
 
@@ -704,9 +1131,7 @@ RomoParentChildElems.prototype._pop = function(id) {
704
1131
 
705
1132
  // Init
706
1133
 
707
- window.Romo = new Romo();
708
-
709
- // TODO: rework w/o jQuery
710
- $(function() {
1134
+ var Romo = new Romo();
1135
+ Romo.ready(function() {
711
1136
  Romo.doInit();
712
- })
1137
+ });