cable_ready 2.0.1 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c8c64467099b8329108e645ffa5adfe5ec22b9bd
4
- data.tar.gz: '00009233f2ea4e0036817aa75483c8e0770e0940'
3
+ metadata.gz: 7cde8a8432716d621f81ee6da1cf536e4e8fd1c1
4
+ data.tar.gz: 66d5621b36f528677331b4067ed20ea8379033d7
5
5
  SHA512:
6
- metadata.gz: 48efff7995c40f4d0101ad2384dbd06473986fa0f326f9324b71490ab072c29426fe7b865acf7433e9375c5f0faaf21590c69059f24f4a7e9339cde71f959557
7
- data.tar.gz: b7f3b004feedf1872754f38e077acab826c2f5eda9582490ab877a97e09a21bc26b677fa5ee3bc29cd0e1cd4e4a2eeab21b1be717227801b5d8cd20ee6700951
6
+ metadata.gz: 4893b800ff4e87f8960dc116b1bc6520c537d7534454d3c90cbf9e5bfaccbe8921520424fd2ed37312a8761ab54c028e5603ebf698a0ea023ced65bd030e0802
7
+ data.tar.gz: 2a101cb0562f074ac1335fb44b9193b30b7ac115f5ba7601bb0d5287020085251564832b49d7af5f5031571884bbdac171710d42958836488eff9e8b4cf0f210
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cable_ready (2.0.1)
4
+ cable_ready (2.0.2)
5
5
  activesupport (>= 5.0.0)
6
6
 
7
7
  GEM
@@ -25,7 +25,8 @@ GEM
25
25
  tins (~> 1.6)
26
26
  debug_inspector (0.0.3)
27
27
  docile (1.1.5)
28
- i18n (0.8.6)
28
+ i18n (0.9.0)
29
+ concurrent-ruby (~> 1.0)
29
30
  interception (0.5)
30
31
  json (2.1.0)
31
32
  method_source (0.8.2)
@@ -72,7 +73,7 @@ GEM
72
73
  thread_safe (0.3.6)
73
74
  tilt (2.0.7)
74
75
  tins (1.14.0)
75
- tzinfo (1.2.3)
76
+ tzinfo (1.2.4)
76
77
  thread_safe (~> 0.1)
77
78
 
78
79
  PLATFORMS
data/README.md CHANGED
@@ -1,16 +1,14 @@
1
- [![Lines of Code](http://img.shields.io/badge/lines_of_code-112-brightgreen.svg?style=flat)](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
1
+ [![Lines of Code](http://img.shields.io/badge/lines_of_code-114-brightgreen.svg?style=flat)](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
2
2
  [![Code Status](http://img.shields.io/codeclimate/github/hopsoft/cable_ready.svg?style=flat)](https://codeclimate.com/github/hopsoft/cable_ready)
3
3
  [![Dependency Status](http://img.shields.io/gemnasium/hopsoft/cable_ready.svg?style=flat)](https://gemnasium.com/hopsoft/cable_ready)
4
4
 
5
5
  # CableReady
6
6
 
7
- ## Server Rendered SPAs :joy:
7
+ ## Out-of-Band Server Triggered DOM Operations
8
8
 
9
- CableReady provides a standard interface for invoking common client-side DOM operations
9
+ CableReady provides a simple interface for triggering client-side DOM operations
10
10
  from the server via [ActionCable](http://guides.rubyonrails.org/action_cable_overview.html).
11
11
 
12
- For a deeper dive into CableReady check out the [TodoMVC CableReady project](https://github.com/hopsoft/todomvc-cableready).
13
-
14
12
  ## Quick Start
15
13
 
16
14
  > Please read the official [ActionCable docs](http://guides.rubyonrails.org/action_cable_overview.html) to learn more about ActionCable before proceeding.
@@ -80,6 +78,18 @@ cable_ready["MyChannel"].dispatch_event(
80
78
 
81
79
  ### Element Mutations
82
80
 
81
+ #### [morph](https://github.com/patrick-steele-idem/morphdom)
82
+
83
+ Fast lightweight DOM diffing/patching without a virtual DOM.
84
+
85
+ ```ruby
86
+ cable_ready["MyChannel"].morph(
87
+ selector: "string", # required - string containing one or more CSS selectors separated by commas
88
+ focusSelector: "string", # [null] - string containing one or more CSS selectors separated by commas
89
+ html: "string" # [null] - the HTML to assign
90
+ )
91
+ ```
92
+
83
93
  #### [innerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML)
84
94
 
85
95
  Sets the innerHTML of a DOM element.
@@ -228,6 +238,16 @@ cable_ready["MyChannel"].set_dataset_property(
228
238
  )
229
239
  ```
230
240
 
241
+ ## JavaScript Development
242
+
243
+ The JavaScript source is located in `vendor/assets/javascripts/cable_ready/src`
244
+ & transpiles to `vendor/assets/javascripts/cable_ready/cable_ready.js` via Webpack.
245
+
246
+ ```sh
247
+ cd vendor/assets/javascripts/cable_ready/src
248
+ webpack
249
+ ```
250
+
231
251
  ---
232
252
 
233
253
  <a target='_blank' rel='nofollow' href='https://app.codesponsor.io/link/QMSjMHrtPhvfmCnk5Hbikhhr/hopsoft/cable_ready'>
@@ -108,6 +108,10 @@ module CableReady
108
108
  operations[:dispatch_event] << options
109
109
  end
110
110
 
111
+ def morph(options={})
112
+ operations[:morph] << options
113
+ end
114
+
111
115
  def inner_html(options={})
112
116
  operations[:inner_html] << options
113
117
  end
@@ -158,22 +162,23 @@ module CableReady
158
162
 
159
163
  private
160
164
 
161
- def stub
162
- {
163
- dispatch_event: [],
164
- inner_html: [],
165
- text_content: [],
166
- insert_adjacent_html: [],
167
- insert_adjacent_text: [],
168
- remove: [],
169
- replace: [],
170
- set_value: [],
171
- set_attribute: [],
172
- remove_attribute: [],
173
- add_css_class: [],
174
- remove_css_class: [],
175
- set_dataset_property: []
176
- }
177
- end
165
+ def stub
166
+ {
167
+ dispatch_event: [],
168
+ morph: [],
169
+ inner_html: [],
170
+ text_content: [],
171
+ insert_adjacent_html: [],
172
+ insert_adjacent_text: [],
173
+ remove: [],
174
+ replace: [],
175
+ set_value: [],
176
+ set_attribute: [],
177
+ remove_attribute: [],
178
+ add_css_class: [],
179
+ remove_css_class: [],
180
+ set_dataset_property: []
181
+ }
182
+ end
178
183
  end
179
184
  end
@@ -1,3 +1,3 @@
1
1
  module CableReady
2
- VERSION = "2.0.1"
2
+ VERSION = "2.0.2"
3
3
  end
@@ -1,108 +1,878 @@
1
- (function () {
2
- "use strict";
3
-
4
- var CableReadyOperations = {
5
- // DOM Events .....................................................................................................
6
-
7
- dispatchEvent: function (config) {
8
- if (CableReady.debug) { console.log("CableReady.dispatchEvent", config); }
9
- var target = document.querySelector(config.selector) || window;
10
- var event = new Event(config.name);
11
- event.detail = config.detail;
12
- target.dispatchEvent(event);
13
- },
1
+ var CableReady =
2
+ /******/ (function(modules) { // webpackBootstrap
3
+ /******/ // The module cache
4
+ /******/ var installedModules = {};
5
+ /******/
6
+ /******/ // The require function
7
+ /******/ function __webpack_require__(moduleId) {
8
+ /******/
9
+ /******/ // Check if module is in cache
10
+ /******/ if(installedModules[moduleId]) {
11
+ /******/ return installedModules[moduleId].exports;
12
+ /******/ }
13
+ /******/ // Create a new module (and put it into the cache)
14
+ /******/ var module = installedModules[moduleId] = {
15
+ /******/ i: moduleId,
16
+ /******/ l: false,
17
+ /******/ exports: {}
18
+ /******/ };
19
+ /******/
20
+ /******/ // Execute the module function
21
+ /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
22
+ /******/
23
+ /******/ // Flag the module as loaded
24
+ /******/ module.l = true;
25
+ /******/
26
+ /******/ // Return the exports of the module
27
+ /******/ return module.exports;
28
+ /******/ }
29
+ /******/
30
+ /******/
31
+ /******/ // expose the modules object (__webpack_modules__)
32
+ /******/ __webpack_require__.m = modules;
33
+ /******/
34
+ /******/ // expose the module cache
35
+ /******/ __webpack_require__.c = installedModules;
36
+ /******/
37
+ /******/ // define getter function for harmony exports
38
+ /******/ __webpack_require__.d = function(exports, name, getter) {
39
+ /******/ if(!__webpack_require__.o(exports, name)) {
40
+ /******/ Object.defineProperty(exports, name, {
41
+ /******/ configurable: false,
42
+ /******/ enumerable: true,
43
+ /******/ get: getter
44
+ /******/ });
45
+ /******/ }
46
+ /******/ };
47
+ /******/
48
+ /******/ // getDefaultExport function for compatibility with non-harmony modules
49
+ /******/ __webpack_require__.n = function(module) {
50
+ /******/ var getter = module && module.__esModule ?
51
+ /******/ function getDefault() { return module['default']; } :
52
+ /******/ function getModuleExports() { return module; };
53
+ /******/ __webpack_require__.d(getter, 'a', getter);
54
+ /******/ return getter;
55
+ /******/ };
56
+ /******/
57
+ /******/ // Object.prototype.hasOwnProperty.call
58
+ /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
59
+ /******/
60
+ /******/ // __webpack_public_path__
61
+ /******/ __webpack_require__.p = "";
62
+ /******/
63
+ /******/ // Load entry module and return exports
64
+ /******/ return __webpack_require__(__webpack_require__.s = 0);
65
+ /******/ })
66
+ /************************************************************************/
67
+ /******/ ([
68
+ /* 0 */
69
+ /***/ (function(module, exports, __webpack_require__) {
14
70
 
15
- // Element Mutations ..............................................................................................
71
+ "use strict";
16
72
 
17
- innerHtml: function (config) {
18
- if (CableReady.debug) { console.log("CableReady.innerHTML", config); }
19
- document.querySelector(config.selector).innerHTML = config.html;
20
- if (config.focusSelector) { document.querySelector(config.focusSelector).focus(); }
21
- },
22
73
 
23
- textContent: function (config) {
24
- if (CableReady.debug) { console.log("CableReady.textContent", config); }
25
- document.querySelector(config.selector).textContent = config.text;
26
- },
74
+ Object.defineProperty(exports, "__esModule", {
75
+ value: true
76
+ });
77
+ exports.perform = undefined;
27
78
 
28
- insertAdjacentHtml: function (config) {
29
- if (CableReady.debug) { console.log("CableReady.insertAdjacentHTML", config); }
30
- document.querySelector(config.selector).insertAdjacentHTML(config.position || "beforeend", config.html);
31
- if (config.focusSelector) { document.querySelector(config.focusSelector).focus(); }
32
- },
79
+ var _morphdom = __webpack_require__(1);
33
80
 
34
- insertAdjacentText: function (config) {
35
- if (CableReady.debug) { console.log("CableReady.insertAdjacentText", config); }
36
- document.querySelector(config.querySelector).insertAdjacentText(config.position || "beforeend", config.text);
37
- },
81
+ var _morphdom2 = _interopRequireDefault(_morphdom);
38
82
 
39
- remove: function (config) {
40
- if (CableReady.debug) { console.log("CableReady.remove", config); }
41
- document.querySelector(config.selector).remove();
42
- if (config.focusSelector) { document.querySelector(config.focusSelector).focus(); }
43
- },
83
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
44
84
 
45
- replace: function (config) {
46
- if (CableReady.debug) { console.log("CableReady.replace", config); }
47
- var element = document.querySelector(config.selector);
48
- var div = document.createElement("div");
49
- div.innerHTML = config.html;
50
- element.parentNode.replaceChild(div.firstElementChild, element);
51
- if (config.focusSelector) { document.querySelector(config.focusSelector).focus(); }
52
- },
85
+ var DOMOperations = {
86
+ // DOM Events ..............................................................................................
53
87
 
54
- setValue: function (config) {
55
- if (CableReady.debug) { console.log("CableReady.setValue", config); }
56
- document.querySelector(config.selector).value = config.value;
57
- },
88
+ dispatchEvent: function dispatchEvent(config) {
89
+ var target = document.querySelector(config.selector) || window;
90
+ var event = new Event(config.name);
91
+ event.detail = config.detail;
92
+ target.dispatchEvent(event);
93
+ },
58
94
 
59
- // Attribute Mutations ............................................................................................
95
+ // Element Mutations .......................................................................................
60
96
 
61
- setAttribute: function (config) {
62
- if (CableReady.debug) { console.log("CableReady.setAttribute", config); }
63
- document.querySelector(config.selector).setAttribute(config.name, config.value);
64
- },
97
+ morph: function morph(config) {
98
+ (0, _morphdom2.default)(document.querySelector(config.selector), config.html);
99
+ if (config.focusSelector) {
100
+ document.querySelector(config.focusSelector).focus();
101
+ }
102
+ },
65
103
 
66
- removeAttribute: function (config) {
67
- if (CableReady.debug) { console.log("CableReady.removeAttribute", config); }
68
- document.querySelector(config.selector).removeAttribute(config.name);
69
- },
104
+ innerHtml: function innerHtml(config) {
105
+ document.querySelector(config.selector).innerHTML = config.html;
106
+ if (config.focusSelector) {
107
+ document.querySelector(config.focusSelector).focus();
108
+ }
109
+ },
70
110
 
71
- // CSS Class Mutations ............................................................................................
111
+ textContent: function textContent(config) {
112
+ document.querySelector(config.selector).textContent = config.text;
113
+ },
72
114
 
73
- addCssClass: function (config) {
74
- if (CableReady.debug) { console.log("CableReady.addCssClass", config); }
75
- document.querySelector(config.selector).classList.add(config.name);
76
- },
115
+ insertAdjacentHtml: function insertAdjacentHtml(config) {
116
+ document.querySelector(config.selector).insertAdjacentHTML(config.position || "beforeend", config.html);
117
+ if (config.focusSelector) {
118
+ document.querySelector(config.focusSelector).focus();
119
+ }
120
+ },
77
121
 
78
- removeCssClass: function (config) {
79
- if (CableReady.debug) { console.log("CableReady.removeCssClass", config); }
80
- document.querySelector(config.selector).classList.remove(config.name);
81
- },
122
+ insertAdjacentText: function insertAdjacentText(config) {
123
+ document.querySelector(config.querySelector).insertAdjacentText(config.position || "beforeend", config.text);
124
+ },
82
125
 
83
- // Dataset Mutations ..............................................................................................
126
+ remove: function remove(config) {
127
+ document.querySelector(config.selector).remove();
128
+ if (config.focusSelector) {
129
+ document.querySelector(config.focusSelector).focus();
130
+ }
131
+ },
84
132
 
85
- setDatasetProperty: function (config) {
86
- if (CableReady.debug) { console.log("CableReady.setDatasetProperty", config); }
87
- document.querySelector(config.selector).dataset[config.name] = config.value;
133
+ replace: function replace(config) {
134
+ var element = document.querySelector(config.selector);
135
+ var div = document.createElement("div");
136
+ div.innerHTML = config.html;
137
+ if (config.focusSelector) {
138
+ document.querySelector(config.focusSelector).focus();
88
139
  }
89
- };
90
-
91
- window.CableReady = {
92
- debug: false,
93
- perform: function (operations) {
94
- for (var name in operations) {
95
- if (operations.hasOwnProperty(name)) {
96
- var entries = operations[name];
97
- for (var i = 0; i < entries.length; i++) {
98
- try {
99
- CableReadyOperations[name](entries[i]);
100
- } catch (e) {
101
- console.log("CableReady detected an error! " + e.message);
102
- }
103
- }
140
+ },
141
+
142
+ setValue: function setValue(config) {
143
+ document.querySelector(config.selector).value = config.value;
144
+ },
145
+
146
+ // Attribute Mutations .....................................................................................
147
+
148
+ setAttribute: function setAttribute(config) {
149
+ document.querySelector(config.selector).setAttribute(config.name, config.value);
150
+ },
151
+
152
+ removeAttribute: function removeAttribute(config) {
153
+ document.querySelector(config.selector).removeAttribute(config.name);
154
+ },
155
+
156
+ // CSS Class Mutations .....................................................................................
157
+
158
+ addCssClass: function addCssClass(config) {
159
+ document.querySelector(config.selector).classList.add(config.name);
160
+ },
161
+
162
+ removeCssClass: function removeCssClass(config) {
163
+ document.querySelector(config.selector).classList.remove(config.name);
164
+ },
165
+
166
+ // Dataset Mutations .......................................................................................
167
+
168
+ setDatasetProperty: function setDatasetProperty(config) {
169
+ document.querySelector(config.selector).dataset[config.name] = config.value;
170
+ }
171
+ };
172
+
173
+ var perform = exports.perform = function perform(operations) {
174
+ for (var name in operations) {
175
+ if (operations.hasOwnProperty(name)) {
176
+ var entries = operations[name];
177
+ for (var i = 0; i < entries.length; i++) {
178
+ try {
179
+ DOMOperations[name](entries[i]);
180
+ } catch (e) {
181
+ console.log("CableReady detected an error in " + name + "! " + e.message);
104
182
  }
105
183
  }
106
184
  }
107
- };
108
- })();
185
+ }
186
+ };
187
+
188
+ /***/ }),
189
+ /* 1 */
190
+ /***/ (function(module, exports, __webpack_require__) {
191
+
192
+ "use strict";
193
+
194
+
195
+ var range; // Create a range object for efficently rendering strings to elements.
196
+ var NS_XHTML = 'http://www.w3.org/1999/xhtml';
197
+
198
+ var doc = typeof document === 'undefined' ? undefined : document;
199
+
200
+ var testEl = doc ?
201
+ doc.body || doc.createElement('div') :
202
+ {};
203
+
204
+ // Fixes <https://github.com/patrick-steele-idem/morphdom/issues/32>
205
+ // (IE7+ support) <=IE7 does not support el.hasAttribute(name)
206
+ var actualHasAttributeNS;
207
+
208
+ if (testEl.hasAttributeNS) {
209
+ actualHasAttributeNS = function(el, namespaceURI, name) {
210
+ return el.hasAttributeNS(namespaceURI, name);
211
+ };
212
+ } else if (testEl.hasAttribute) {
213
+ actualHasAttributeNS = function(el, namespaceURI, name) {
214
+ return el.hasAttribute(name);
215
+ };
216
+ } else {
217
+ actualHasAttributeNS = function(el, namespaceURI, name) {
218
+ return el.getAttributeNode(namespaceURI, name) != null;
219
+ };
220
+ }
221
+
222
+ var hasAttributeNS = actualHasAttributeNS;
223
+
224
+
225
+ function toElement(str) {
226
+ if (!range && doc.createRange) {
227
+ range = doc.createRange();
228
+ range.selectNode(doc.body);
229
+ }
230
+
231
+ var fragment;
232
+ if (range && range.createContextualFragment) {
233
+ fragment = range.createContextualFragment(str);
234
+ } else {
235
+ fragment = doc.createElement('body');
236
+ fragment.innerHTML = str;
237
+ }
238
+ return fragment.childNodes[0];
239
+ }
240
+
241
+ /**
242
+ * Returns true if two node's names are the same.
243
+ *
244
+ * NOTE: We don't bother checking `namespaceURI` because you will never find two HTML elements with the same
245
+ * nodeName and different namespace URIs.
246
+ *
247
+ * @param {Element} a
248
+ * @param {Element} b The target element
249
+ * @return {boolean}
250
+ */
251
+ function compareNodeNames(fromEl, toEl) {
252
+ var fromNodeName = fromEl.nodeName;
253
+ var toNodeName = toEl.nodeName;
254
+
255
+ if (fromNodeName === toNodeName) {
256
+ return true;
257
+ }
258
+
259
+ if (toEl.actualize &&
260
+ fromNodeName.charCodeAt(0) < 91 && /* from tag name is upper case */
261
+ toNodeName.charCodeAt(0) > 90 /* target tag name is lower case */) {
262
+ // If the target element is a virtual DOM node then we may need to normalize the tag name
263
+ // before comparing. Normal HTML elements that are in the "http://www.w3.org/1999/xhtml"
264
+ // are converted to upper case
265
+ return fromNodeName === toNodeName.toUpperCase();
266
+ } else {
267
+ return false;
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Create an element, optionally with a known namespace URI.
273
+ *
274
+ * @param {string} name the element name, e.g. 'div' or 'svg'
275
+ * @param {string} [namespaceURI] the element's namespace URI, i.e. the value of
276
+ * its `xmlns` attribute or its inferred namespace.
277
+ *
278
+ * @return {Element}
279
+ */
280
+ function createElementNS(name, namespaceURI) {
281
+ return !namespaceURI || namespaceURI === NS_XHTML ?
282
+ doc.createElement(name) :
283
+ doc.createElementNS(namespaceURI, name);
284
+ }
285
+
286
+ /**
287
+ * Copies the children of one DOM element to another DOM element
288
+ */
289
+ function moveChildren(fromEl, toEl) {
290
+ var curChild = fromEl.firstChild;
291
+ while (curChild) {
292
+ var nextChild = curChild.nextSibling;
293
+ toEl.appendChild(curChild);
294
+ curChild = nextChild;
295
+ }
296
+ return toEl;
297
+ }
298
+
299
+ function morphAttrs(fromNode, toNode) {
300
+ var attrs = toNode.attributes;
301
+ var i;
302
+ var attr;
303
+ var attrName;
304
+ var attrNamespaceURI;
305
+ var attrValue;
306
+ var fromValue;
307
+
308
+ for (i = attrs.length - 1; i >= 0; --i) {
309
+ attr = attrs[i];
310
+ attrName = attr.name;
311
+ attrNamespaceURI = attr.namespaceURI;
312
+ attrValue = attr.value;
313
+
314
+ if (attrNamespaceURI) {
315
+ attrName = attr.localName || attrName;
316
+ fromValue = fromNode.getAttributeNS(attrNamespaceURI, attrName);
317
+
318
+ if (fromValue !== attrValue) {
319
+ fromNode.setAttributeNS(attrNamespaceURI, attrName, attrValue);
320
+ }
321
+ } else {
322
+ fromValue = fromNode.getAttribute(attrName);
323
+
324
+ if (fromValue !== attrValue) {
325
+ fromNode.setAttribute(attrName, attrValue);
326
+ }
327
+ }
328
+ }
329
+
330
+ // Remove any extra attributes found on the original DOM element that
331
+ // weren't found on the target element.
332
+ attrs = fromNode.attributes;
333
+
334
+ for (i = attrs.length - 1; i >= 0; --i) {
335
+ attr = attrs[i];
336
+ if (attr.specified !== false) {
337
+ attrName = attr.name;
338
+ attrNamespaceURI = attr.namespaceURI;
339
+
340
+ if (attrNamespaceURI) {
341
+ attrName = attr.localName || attrName;
342
+
343
+ if (!hasAttributeNS(toNode, attrNamespaceURI, attrName)) {
344
+ fromNode.removeAttributeNS(attrNamespaceURI, attrName);
345
+ }
346
+ } else {
347
+ if (!hasAttributeNS(toNode, null, attrName)) {
348
+ fromNode.removeAttribute(attrName);
349
+ }
350
+ }
351
+ }
352
+ }
353
+ }
354
+
355
+ function syncBooleanAttrProp(fromEl, toEl, name) {
356
+ if (fromEl[name] !== toEl[name]) {
357
+ fromEl[name] = toEl[name];
358
+ if (fromEl[name]) {
359
+ fromEl.setAttribute(name, '');
360
+ } else {
361
+ fromEl.removeAttribute(name, '');
362
+ }
363
+ }
364
+ }
365
+
366
+ var specialElHandlers = {
367
+ /**
368
+ * Needed for IE. Apparently IE doesn't think that "selected" is an
369
+ * attribute when reading over the attributes using selectEl.attributes
370
+ */
371
+ OPTION: function(fromEl, toEl) {
372
+ syncBooleanAttrProp(fromEl, toEl, 'selected');
373
+ },
374
+ /**
375
+ * The "value" attribute is special for the <input> element since it sets
376
+ * the initial value. Changing the "value" attribute without changing the
377
+ * "value" property will have no effect since it is only used to the set the
378
+ * initial value. Similar for the "checked" attribute, and "disabled".
379
+ */
380
+ INPUT: function(fromEl, toEl) {
381
+ syncBooleanAttrProp(fromEl, toEl, 'checked');
382
+ syncBooleanAttrProp(fromEl, toEl, 'disabled');
383
+
384
+ if (fromEl.value !== toEl.value) {
385
+ fromEl.value = toEl.value;
386
+ }
387
+
388
+ if (!hasAttributeNS(toEl, null, 'value')) {
389
+ fromEl.removeAttribute('value');
390
+ }
391
+ },
392
+
393
+ TEXTAREA: function(fromEl, toEl) {
394
+ var newValue = toEl.value;
395
+ if (fromEl.value !== newValue) {
396
+ fromEl.value = newValue;
397
+ }
398
+
399
+ var firstChild = fromEl.firstChild;
400
+ if (firstChild) {
401
+ // Needed for IE. Apparently IE sets the placeholder as the
402
+ // node value and vise versa. This ignores an empty update.
403
+ var oldValue = firstChild.nodeValue;
404
+
405
+ if (oldValue == newValue || (!newValue && oldValue == fromEl.placeholder)) {
406
+ return;
407
+ }
408
+
409
+ firstChild.nodeValue = newValue;
410
+ }
411
+ },
412
+ SELECT: function(fromEl, toEl) {
413
+ if (!hasAttributeNS(toEl, null, 'multiple')) {
414
+ var selectedIndex = -1;
415
+ var i = 0;
416
+ var curChild = toEl.firstChild;
417
+ while(curChild) {
418
+ var nodeName = curChild.nodeName;
419
+ if (nodeName && nodeName.toUpperCase() === 'OPTION') {
420
+ if (hasAttributeNS(curChild, null, 'selected')) {
421
+ selectedIndex = i;
422
+ break;
423
+ }
424
+ i++;
425
+ }
426
+ curChild = curChild.nextSibling;
427
+ }
428
+
429
+ fromEl.selectedIndex = i;
430
+ }
431
+ }
432
+ };
433
+
434
+ var ELEMENT_NODE = 1;
435
+ var TEXT_NODE = 3;
436
+ var COMMENT_NODE = 8;
437
+
438
+ function noop() {}
439
+
440
+ function defaultGetNodeKey(node) {
441
+ return node.id;
442
+ }
443
+
444
+ function morphdomFactory(morphAttrs) {
445
+
446
+ return function morphdom(fromNode, toNode, options) {
447
+ if (!options) {
448
+ options = {};
449
+ }
450
+
451
+ if (typeof toNode === 'string') {
452
+ if (fromNode.nodeName === '#document' || fromNode.nodeName === 'HTML') {
453
+ var toNodeHtml = toNode;
454
+ toNode = doc.createElement('html');
455
+ toNode.innerHTML = toNodeHtml;
456
+ } else {
457
+ toNode = toElement(toNode);
458
+ }
459
+ }
460
+
461
+ var getNodeKey = options.getNodeKey || defaultGetNodeKey;
462
+ var onBeforeNodeAdded = options.onBeforeNodeAdded || noop;
463
+ var onNodeAdded = options.onNodeAdded || noop;
464
+ var onBeforeElUpdated = options.onBeforeElUpdated || noop;
465
+ var onElUpdated = options.onElUpdated || noop;
466
+ var onBeforeNodeDiscarded = options.onBeforeNodeDiscarded || noop;
467
+ var onNodeDiscarded = options.onNodeDiscarded || noop;
468
+ var onBeforeElChildrenUpdated = options.onBeforeElChildrenUpdated || noop;
469
+ var childrenOnly = options.childrenOnly === true;
470
+
471
+ // This object is used as a lookup to quickly find all keyed elements in the original DOM tree.
472
+ var fromNodesLookup = {};
473
+ var keyedRemovalList;
474
+
475
+ function addKeyedRemoval(key) {
476
+ if (keyedRemovalList) {
477
+ keyedRemovalList.push(key);
478
+ } else {
479
+ keyedRemovalList = [key];
480
+ }
481
+ }
482
+
483
+ function walkDiscardedChildNodes(node, skipKeyedNodes) {
484
+ if (node.nodeType === ELEMENT_NODE) {
485
+ var curChild = node.firstChild;
486
+ while (curChild) {
487
+
488
+ var key = undefined;
489
+
490
+ if (skipKeyedNodes && (key = getNodeKey(curChild))) {
491
+ // If we are skipping keyed nodes then we add the key
492
+ // to a list so that it can be handled at the very end.
493
+ addKeyedRemoval(key);
494
+ } else {
495
+ // Only report the node as discarded if it is not keyed. We do this because
496
+ // at the end we loop through all keyed elements that were unmatched
497
+ // and then discard them in one final pass.
498
+ onNodeDiscarded(curChild);
499
+ if (curChild.firstChild) {
500
+ walkDiscardedChildNodes(curChild, skipKeyedNodes);
501
+ }
502
+ }
503
+
504
+ curChild = curChild.nextSibling;
505
+ }
506
+ }
507
+ }
508
+
509
+ /**
510
+ * Removes a DOM node out of the original DOM
511
+ *
512
+ * @param {Node} node The node to remove
513
+ * @param {Node} parentNode The nodes parent
514
+ * @param {Boolean} skipKeyedNodes If true then elements with keys will be skipped and not discarded.
515
+ * @return {undefined}
516
+ */
517
+ function removeNode(node, parentNode, skipKeyedNodes) {
518
+ if (onBeforeNodeDiscarded(node) === false) {
519
+ return;
520
+ }
521
+
522
+ if (parentNode) {
523
+ parentNode.removeChild(node);
524
+ }
525
+
526
+ onNodeDiscarded(node);
527
+ walkDiscardedChildNodes(node, skipKeyedNodes);
528
+ }
529
+
530
+ // // TreeWalker implementation is no faster, but keeping this around in case this changes in the future
531
+ // function indexTree(root) {
532
+ // var treeWalker = document.createTreeWalker(
533
+ // root,
534
+ // NodeFilter.SHOW_ELEMENT);
535
+ //
536
+ // var el;
537
+ // while((el = treeWalker.nextNode())) {
538
+ // var key = getNodeKey(el);
539
+ // if (key) {
540
+ // fromNodesLookup[key] = el;
541
+ // }
542
+ // }
543
+ // }
544
+
545
+ // // NodeIterator implementation is no faster, but keeping this around in case this changes in the future
546
+ //
547
+ // function indexTree(node) {
548
+ // var nodeIterator = document.createNodeIterator(node, NodeFilter.SHOW_ELEMENT);
549
+ // var el;
550
+ // while((el = nodeIterator.nextNode())) {
551
+ // var key = getNodeKey(el);
552
+ // if (key) {
553
+ // fromNodesLookup[key] = el;
554
+ // }
555
+ // }
556
+ // }
557
+
558
+ function indexTree(node) {
559
+ if (node.nodeType === ELEMENT_NODE) {
560
+ var curChild = node.firstChild;
561
+ while (curChild) {
562
+ var key = getNodeKey(curChild);
563
+ if (key) {
564
+ fromNodesLookup[key] = curChild;
565
+ }
566
+
567
+ // Walk recursively
568
+ indexTree(curChild);
569
+
570
+ curChild = curChild.nextSibling;
571
+ }
572
+ }
573
+ }
574
+
575
+ indexTree(fromNode);
576
+
577
+ function handleNodeAdded(el) {
578
+ onNodeAdded(el);
579
+
580
+ var curChild = el.firstChild;
581
+ while (curChild) {
582
+ var nextSibling = curChild.nextSibling;
583
+
584
+ var key = getNodeKey(curChild);
585
+ if (key) {
586
+ var unmatchedFromEl = fromNodesLookup[key];
587
+ if (unmatchedFromEl && compareNodeNames(curChild, unmatchedFromEl)) {
588
+ curChild.parentNode.replaceChild(unmatchedFromEl, curChild);
589
+ morphEl(unmatchedFromEl, curChild);
590
+ }
591
+ }
592
+
593
+ handleNodeAdded(curChild);
594
+ curChild = nextSibling;
595
+ }
596
+ }
597
+
598
+ function morphEl(fromEl, toEl, childrenOnly) {
599
+ var toElKey = getNodeKey(toEl);
600
+ var curFromNodeKey;
601
+
602
+ if (toElKey) {
603
+ // If an element with an ID is being morphed then it is will be in the final
604
+ // DOM so clear it out of the saved elements collection
605
+ delete fromNodesLookup[toElKey];
606
+ }
607
+
608
+ if (toNode.isSameNode && toNode.isSameNode(fromNode)) {
609
+ return;
610
+ }
611
+
612
+ if (!childrenOnly) {
613
+ if (onBeforeElUpdated(fromEl, toEl) === false) {
614
+ return;
615
+ }
616
+
617
+ morphAttrs(fromEl, toEl);
618
+ onElUpdated(fromEl);
619
+
620
+ if (onBeforeElChildrenUpdated(fromEl, toEl) === false) {
621
+ return;
622
+ }
623
+ }
624
+
625
+ if (fromEl.nodeName !== 'TEXTAREA') {
626
+ var curToNodeChild = toEl.firstChild;
627
+ var curFromNodeChild = fromEl.firstChild;
628
+ var curToNodeKey;
629
+
630
+ var fromNextSibling;
631
+ var toNextSibling;
632
+ var matchingFromEl;
633
+
634
+ outer: while (curToNodeChild) {
635
+ toNextSibling = curToNodeChild.nextSibling;
636
+ curToNodeKey = getNodeKey(curToNodeChild);
637
+
638
+ while (curFromNodeChild) {
639
+ fromNextSibling = curFromNodeChild.nextSibling;
640
+
641
+ if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) {
642
+ curToNodeChild = toNextSibling;
643
+ curFromNodeChild = fromNextSibling;
644
+ continue outer;
645
+ }
646
+
647
+ curFromNodeKey = getNodeKey(curFromNodeChild);
648
+
649
+ var curFromNodeType = curFromNodeChild.nodeType;
650
+
651
+ var isCompatible = undefined;
652
+
653
+ if (curFromNodeType === curToNodeChild.nodeType) {
654
+ if (curFromNodeType === ELEMENT_NODE) {
655
+ // Both nodes being compared are Element nodes
656
+
657
+ if (curToNodeKey) {
658
+ // The target node has a key so we want to match it up with the correct element
659
+ // in the original DOM tree
660
+ if (curToNodeKey !== curFromNodeKey) {
661
+ // The current element in the original DOM tree does not have a matching key so
662
+ // let's check our lookup to see if there is a matching element in the original
663
+ // DOM tree
664
+ if ((matchingFromEl = fromNodesLookup[curToNodeKey])) {
665
+ if (curFromNodeChild.nextSibling === matchingFromEl) {
666
+ // Special case for single element removals. To avoid removing the original
667
+ // DOM node out of the tree (since that can break CSS transitions, etc.),
668
+ // we will instead discard the current node and wait until the next
669
+ // iteration to properly match up the keyed target element with its matching
670
+ // element in the original tree
671
+ isCompatible = false;
672
+ } else {
673
+ // We found a matching keyed element somewhere in the original DOM tree.
674
+ // Let's moving the original DOM node into the current position and morph
675
+ // it.
676
+
677
+ // NOTE: We use insertBefore instead of replaceChild because we want to go through
678
+ // the `removeNode()` function for the node that is being discarded so that
679
+ // all lifecycle hooks are correctly invoked
680
+ fromEl.insertBefore(matchingFromEl, curFromNodeChild);
681
+
682
+ fromNextSibling = curFromNodeChild.nextSibling;
683
+
684
+ if (curFromNodeKey) {
685
+ // Since the node is keyed it might be matched up later so we defer
686
+ // the actual removal to later
687
+ addKeyedRemoval(curFromNodeKey);
688
+ } else {
689
+ // NOTE: we skip nested keyed nodes from being removed since there is
690
+ // still a chance they will be matched up later
691
+ removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
692
+ }
693
+
694
+ curFromNodeChild = matchingFromEl;
695
+ }
696
+ } else {
697
+ // The nodes are not compatible since the "to" node has a key and there
698
+ // is no matching keyed node in the source tree
699
+ isCompatible = false;
700
+ }
701
+ }
702
+ } else if (curFromNodeKey) {
703
+ // The original has a key
704
+ isCompatible = false;
705
+ }
706
+
707
+ isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild);
708
+ if (isCompatible) {
709
+ // We found compatible DOM elements so transform
710
+ // the current "from" node to match the current
711
+ // target DOM node.
712
+ morphEl(curFromNodeChild, curToNodeChild);
713
+ }
714
+
715
+ } else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) {
716
+ // Both nodes being compared are Text or Comment nodes
717
+ isCompatible = true;
718
+ // Simply update nodeValue on the original node to
719
+ // change the text value
720
+ if (curFromNodeChild.nodeValue !== curToNodeChild.nodeValue) {
721
+ curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
722
+ }
723
+
724
+ }
725
+ }
726
+
727
+ if (isCompatible) {
728
+ // Advance both the "to" child and the "from" child since we found a match
729
+ curToNodeChild = toNextSibling;
730
+ curFromNodeChild = fromNextSibling;
731
+ continue outer;
732
+ }
733
+
734
+ // No compatible match so remove the old node from the DOM and continue trying to find a
735
+ // match in the original DOM. However, we only do this if the from node is not keyed
736
+ // since it is possible that a keyed node might match up with a node somewhere else in the
737
+ // target tree and we don't want to discard it just yet since it still might find a
738
+ // home in the final DOM tree. After everything is done we will remove any keyed nodes
739
+ // that didn't find a home
740
+ if (curFromNodeKey) {
741
+ // Since the node is keyed it might be matched up later so we defer
742
+ // the actual removal to later
743
+ addKeyedRemoval(curFromNodeKey);
744
+ } else {
745
+ // NOTE: we skip nested keyed nodes from being removed since there is
746
+ // still a chance they will be matched up later
747
+ removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
748
+ }
749
+
750
+ curFromNodeChild = fromNextSibling;
751
+ }
752
+
753
+ // If we got this far then we did not find a candidate match for
754
+ // our "to node" and we exhausted all of the children "from"
755
+ // nodes. Therefore, we will just append the current "to" node
756
+ // to the end
757
+ if (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) {
758
+ fromEl.appendChild(matchingFromEl);
759
+ morphEl(matchingFromEl, curToNodeChild);
760
+ } else {
761
+ var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild);
762
+ if (onBeforeNodeAddedResult !== false) {
763
+ if (onBeforeNodeAddedResult) {
764
+ curToNodeChild = onBeforeNodeAddedResult;
765
+ }
766
+
767
+ if (curToNodeChild.actualize) {
768
+ curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc);
769
+ }
770
+ fromEl.appendChild(curToNodeChild);
771
+ handleNodeAdded(curToNodeChild);
772
+ }
773
+ }
774
+
775
+ curToNodeChild = toNextSibling;
776
+ curFromNodeChild = fromNextSibling;
777
+ }
778
+
779
+ // We have processed all of the "to nodes". If curFromNodeChild is
780
+ // non-null then we still have some from nodes left over that need
781
+ // to be removed
782
+ while (curFromNodeChild) {
783
+ fromNextSibling = curFromNodeChild.nextSibling;
784
+ if ((curFromNodeKey = getNodeKey(curFromNodeChild))) {
785
+ // Since the node is keyed it might be matched up later so we defer
786
+ // the actual removal to later
787
+ addKeyedRemoval(curFromNodeKey);
788
+ } else {
789
+ // NOTE: we skip nested keyed nodes from being removed since there is
790
+ // still a chance they will be matched up later
791
+ removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
792
+ }
793
+ curFromNodeChild = fromNextSibling;
794
+ }
795
+ }
796
+
797
+ var specialElHandler = specialElHandlers[fromEl.nodeName];
798
+ if (specialElHandler) {
799
+ specialElHandler(fromEl, toEl);
800
+ }
801
+ } // END: morphEl(...)
802
+
803
+ var morphedNode = fromNode;
804
+ var morphedNodeType = morphedNode.nodeType;
805
+ var toNodeType = toNode.nodeType;
806
+
807
+ if (!childrenOnly) {
808
+ // Handle the case where we are given two DOM nodes that are not
809
+ // compatible (e.g. <div> --> <span> or <div> --> TEXT)
810
+ if (morphedNodeType === ELEMENT_NODE) {
811
+ if (toNodeType === ELEMENT_NODE) {
812
+ if (!compareNodeNames(fromNode, toNode)) {
813
+ onNodeDiscarded(fromNode);
814
+ morphedNode = moveChildren(fromNode, createElementNS(toNode.nodeName, toNode.namespaceURI));
815
+ }
816
+ } else {
817
+ // Going from an element node to a text node
818
+ morphedNode = toNode;
819
+ }
820
+ } else if (morphedNodeType === TEXT_NODE || morphedNodeType === COMMENT_NODE) { // Text or comment node
821
+ if (toNodeType === morphedNodeType) {
822
+ if (morphedNode.nodeValue !== toNode.nodeValue) {
823
+ morphedNode.nodeValue = toNode.nodeValue;
824
+ }
825
+
826
+ return morphedNode;
827
+ } else {
828
+ // Text node to something else
829
+ morphedNode = toNode;
830
+ }
831
+ }
832
+ }
833
+
834
+ if (morphedNode === toNode) {
835
+ // The "to node" was not compatible with the "from node" so we had to
836
+ // toss out the "from node" and use the "to node"
837
+ onNodeDiscarded(fromNode);
838
+ } else {
839
+ morphEl(morphedNode, toNode, childrenOnly);
840
+
841
+ // We now need to loop over any keyed nodes that might need to be
842
+ // removed. We only do the removal if we know that the keyed node
843
+ // never found a match. When a keyed node is matched up we remove
844
+ // it out of fromNodesLookup and we use fromNodesLookup to determine
845
+ // if a keyed node has been matched up or not
846
+ if (keyedRemovalList) {
847
+ for (var i=0, len=keyedRemovalList.length; i<len; i++) {
848
+ var elToRemove = fromNodesLookup[keyedRemovalList[i]];
849
+ if (elToRemove) {
850
+ removeNode(elToRemove, elToRemove.parentNode, false);
851
+ }
852
+ }
853
+ }
854
+ }
855
+
856
+ if (!childrenOnly && morphedNode !== fromNode && fromNode.parentNode) {
857
+ if (morphedNode.actualize) {
858
+ morphedNode = morphedNode.actualize(fromNode.ownerDocument || doc);
859
+ }
860
+ // If we had to swap out the from node with a new node because the old
861
+ // node was not compatible with the target node then we need to
862
+ // replace the old DOM node in the original DOM tree. This is only
863
+ // possible if the original DOM node was part of a DOM tree which
864
+ // we know is the case if it has a parent node.
865
+ fromNode.parentNode.replaceChild(morphedNode, fromNode);
866
+ }
867
+
868
+ return morphedNode;
869
+ };
870
+ }
871
+
872
+ var morphdom = morphdomFactory(morphAttrs);
873
+
874
+ module.exports = morphdom;
875
+
876
+
877
+ /***/ })
878
+ /******/ ]);
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cable_ready
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Hopkins
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-15 00:00:00.000000000 Z
11
+ date: 2017-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport