receptive 0.1.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9f4b45907709281428dd8d3864bed092143f4e7542c0f1a5b9d9aed2c005b95f
4
+ data.tar.gz: 22a456c8dd79e115ba8cbe2fb3706e75b4f70b31b310ae6412067405616fe34a
5
+ SHA512:
6
+ metadata.gz: b543eb6ee8a093fd62a9b3dce5db88418b1cdfeb102e1a0a461e14cfd9284d74bb2b91c1efeda359f4bcc49def4f0d5bbe137988bedaa1864109387def270dea
7
+ data.tar.gz: d11818c1327dd29fb395304bd299ac1b4cc1d981e0cb3b03ea32d80108de0c82d382cd7fa73722c3ef1cbb4f1595e77c5ef15b18103e401464dedf9ef8cb7075
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.0
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in receptive.gemspec
6
+ gemspec
@@ -0,0 +1,43 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ receptive (0.1.0.alpha)
5
+ opal (>= 0.10.5, < 0.12)
6
+ opal-jquery (~> 0.4.2)
7
+ opal-minitest (= 0.0.5)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ concurrent-ruby (1.0.5)
13
+ hike (1.2.3)
14
+ minitest (5.11.3)
15
+ opal (0.10.5)
16
+ hike (~> 1.2)
17
+ sourcemap (~> 0.1.0)
18
+ sprockets (~> 3.1)
19
+ tilt (>= 1.4)
20
+ opal-jquery (0.4.2)
21
+ opal (>= 0.7.0, < 0.11.0)
22
+ opal-minitest (0.0.5)
23
+ opal (>= 0.8)
24
+ rake (~> 10)
25
+ rack (2.0.4)
26
+ rake (10.5.0)
27
+ sourcemap (0.1.1)
28
+ sprockets (3.7.1)
29
+ concurrent-ruby (~> 1.0)
30
+ rack (> 1, < 3)
31
+ tilt (2.0.8)
32
+
33
+ PLATFORMS
34
+ ruby
35
+
36
+ DEPENDENCIES
37
+ bundler (~> 1.16)
38
+ minitest (~> 5.0)
39
+ rake (~> 10.0)
40
+ receptive!
41
+
42
+ BUNDLED WITH
43
+ 1.16.1
@@ -0,0 +1,52 @@
1
+ # Receptive handbook
2
+
3
+ ## A basic view
4
+
5
+ The README example, annotated:
6
+
7
+ ```html
8
+ <div class="hello-world">
9
+ <input type="text">
10
+ <button>Greet</button>
11
+ <span class="output"></span>
12
+ </div>
13
+ ```
14
+
15
+ ```rb
16
+ require 'opal'
17
+ require 'receptive'
18
+
19
+ class HelloWorld
20
+ extend Receptive::View
21
+
22
+ self.selector = ".hello-world" # this will set the root-node for our view
23
+
24
+ # `on` takes an event, and optionally a selector
25
+ on(:click, 'button') do |event| # leaving the selector black will listen for events
26
+ # on the root-node
27
+
28
+ @greeting_text = find('input').text # `find` operates only on inside the root-node
29
+
30
+ render! # `render!` will execute the render method below
31
+ end # wrapped in `requestAnimationFrame()`
32
+
33
+ def self.render # where possible is advisable to separate data collection
34
+ find('.output').text = @greeting_text # from the rendering/updating of the node, this has been
35
+ end # proven almost invariably useful in managing UI interactions
36
+ end
37
+
38
+ Receptive::App.run
39
+ ```
40
+
41
+ ---
42
+
43
+ *upcoming chapters…*
44
+
45
+ ## Application lifecycle
46
+ ## Incremental DOM
47
+ ## The `render!` method
48
+ ## `window` and `document` events
49
+ ## Why still jQuery?
50
+ ## Using actions and the global status
51
+
52
+
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Elia Schito
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,76 @@
1
+ # Receptive
2
+
3
+ Receptive is a toolkit that will help you add behavior to your existing, server-generated HTML. It's not intended for single-page-application but rather about taking care of specific DOM nodes.
4
+
5
+ This is perfect for you if:
6
+
7
+ - **you already generate all your views from the server** (and don't want to throw everything away following the latest JavaScript fad)
8
+ - **you need your site to work without JavaScript** for SEO or any other reasons
9
+ - **you have a bunch of jQuery stuff around** and always wanted to organize it properly
10
+ - **you like Ruby** and don't want to get caught by all the subtle quirks of JavaScript
11
+
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ gem 'receptive'
19
+ ```
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+
26
+ ## How it works
27
+
28
+ Just keep your HTML as it is:
29
+
30
+ ```html
31
+ <div class="hello-world">
32
+ <input type="text">
33
+ <button>Greet</button>
34
+ <span class="output"></span>
35
+ </div>
36
+ ```
37
+
38
+ Then write a view with a reference to it:
39
+
40
+ ```rb
41
+ class HelloWorld
42
+ extend Receptive::View
43
+ self.selector = ".hello-world"
44
+
45
+ on(:click, 'button') do |event|
46
+ @greeting_text = find('input').text
47
+ render!
48
+ end
49
+
50
+ def self.render
51
+ find('.output').text = @greeting_text
52
+ end
53
+ end
54
+ ```
55
+
56
+ Read on in the [handbook](./HANDBOOK.md)
57
+
58
+ ## Other features
59
+
60
+ - script[async] compatible
61
+ - pjax/turbolinks compatible
62
+
63
+
64
+ ## Development
65
+
66
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
67
+
68
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
69
+
70
+ ## Contributing
71
+
72
+ Bug reports and pull requests are welcome on GitHub at https://github.com/elia/receptive.
73
+
74
+ ## License
75
+
76
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,22 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test_mri) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :test_opal do
11
+ require 'opal'
12
+ ENV['RUBYOPT'] += ' -rbundler/setup -ropal/minitest '
13
+ files = nil
14
+ cd('test-opal') do
15
+ files = Dir['**/*_test.rb'].map{|f| "-r#{f.shellescape}"}
16
+ end
17
+ sh "opal -Ilib-opal -Itest-opal #{files.join(' ')} -e ':done'"
18
+ end
19
+
20
+ task :test => [:test_mri, :test_opal]
21
+
22
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "receptive"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,13 @@
1
+ require 'console'
2
+
3
+ module Receptive
4
+ def self.silence_logs
5
+ debug = $DEBUG
6
+ $DEBUG = false
7
+ yield
8
+ $DEBUG = debug
9
+ end
10
+ end
11
+
12
+ require 'receptive/view'
13
+ require 'receptive/app'
@@ -0,0 +1,48 @@
1
+ require 'receptive/view'
2
+
3
+ module Receptive::App
4
+ extend self
5
+
6
+ def trigger(event_name, data = nil)
7
+ $console.log(
8
+ "%c event %c #{event_name} %c ",
9
+ 'background-color:#eee;color:#444;border-radius:2px 0 0 2px',
10
+ 'background-color:#94D1F5;color:#0A3EA3;border-radius:0 2px 2px 0',
11
+ 'background-color:none;border:none;font-family:monospace;',
12
+ `data != null && data.$inspect && data.$inspect()`,
13
+ self
14
+ ) if $DEBUG
15
+ Document.trigger(event_name, data)
16
+ end
17
+
18
+ def on(event_name, &block)
19
+ Document.on(event_name, &block)
20
+ end
21
+
22
+ def run
23
+ trigger('app:starting', self)
24
+ ::Receptive::View.extenders.each { |c| c.setup_persistent_events }
25
+ trigger('app:started', self)
26
+
27
+ # Managing dom:ready for sync & async script tags
28
+ app_loaded = ->*{ trigger('app:loaded') }
29
+
30
+ %x{
31
+ if (document.readyState === 'complete') {
32
+ setTimeout(#{app_loaded}, 1);
33
+ } else {
34
+ document.addEventListener('DOMContentLoaded', #{app_loaded}, false);
35
+ }
36
+ }
37
+
38
+ # Managing visibility changes
39
+ visibility_change = -> { trigger(`document`.JS[:hidden] ? 'visibility:hidden' : 'visibility:visible') }
40
+ %x{
41
+ if (!document.hidden) {
42
+ setTimeout(#{visibility_change}, 1);
43
+ } else {
44
+ document.addEventListener('visibilitychange', #{visibility_change}, false);
45
+ }
46
+ }
47
+ end
48
+ end
@@ -0,0 +1,1223 @@
1
+ // https://ajax.googleapis.com/ajax/libs/incrementaldom/0.5.1/incremental-dom.js
2
+
3
+ /**
4
+ * @license
5
+ * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS-IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+
20
+ (function (global, factory) {
21
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
22
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
23
+ (factory((global.IncrementalDOM = {})));
24
+ }(this, function (exports) { 'use strict';
25
+
26
+ /**
27
+ * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
28
+ *
29
+ * Licensed under the Apache License, Version 2.0 (the "License");
30
+ * you may not use this file except in compliance with the License.
31
+ * You may obtain a copy of the License at
32
+ *
33
+ * http://www.apache.org/licenses/LICENSE-2.0
34
+ *
35
+ * Unless required by applicable law or agreed to in writing, software
36
+ * distributed under the License is distributed on an "AS-IS" BASIS,
37
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
38
+ * See the License for the specific language governing permissions and
39
+ * limitations under the License.
40
+ */
41
+
42
+ /**
43
+ * A cached reference to the hasOwnProperty function.
44
+ */
45
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
46
+
47
+ /**
48
+ * A constructor function that will create blank objects.
49
+ * @constructor
50
+ */
51
+ function Blank() {}
52
+
53
+ Blank.prototype = Object.create(null);
54
+
55
+ /**
56
+ * Used to prevent property collisions between our "map" and its prototype.
57
+ * @param {!Object<string, *>} map The map to check.
58
+ * @param {string} property The property to check.
59
+ * @return {boolean} Whether map has property.
60
+ */
61
+ var has = function (map, property) {
62
+ return hasOwnProperty.call(map, property);
63
+ };
64
+
65
+ /**
66
+ * Creates an map object without a prototype.
67
+ * @return {!Object}
68
+ */
69
+ var createMap = function () {
70
+ return new Blank();
71
+ };
72
+
73
+ /**
74
+ * The property name where we store Incremental DOM data.
75
+ */
76
+ var DATA_PROP = '__incrementalDOMData';
77
+
78
+ /**
79
+ * Keeps track of information needed to perform diffs for a given DOM node.
80
+ * @param {!string} nodeName
81
+ * @param {?string=} key
82
+ * @constructor
83
+ */
84
+ function NodeData(nodeName, key) {
85
+ /**
86
+ * The attributes and their values.
87
+ * @const {!Object<string, *>}
88
+ */
89
+ this.attrs = createMap();
90
+
91
+ /**
92
+ * An array of attribute name/value pairs, used for quickly diffing the
93
+ * incomming attributes to see if the DOM node's attributes need to be
94
+ * updated.
95
+ * @const {Array<*>}
96
+ */
97
+ this.attrsArr = [];
98
+
99
+ /**
100
+ * The incoming attributes for this Node, before they are updated.
101
+ * @const {!Object<string, *>}
102
+ */
103
+ this.newAttrs = createMap();
104
+
105
+ /**
106
+ * Whether or not the statics have been applied for the node yet.
107
+ * {boolean}
108
+ */
109
+ this.staticsApplied = false;
110
+
111
+ /**
112
+ * The key used to identify this node, used to preserve DOM nodes when they
113
+ * move within their parent.
114
+ * @const
115
+ */
116
+ this.key = key;
117
+
118
+ /**
119
+ * Keeps track of children within this node by their key.
120
+ * {!Object<string, !Element>}
121
+ */
122
+ this.keyMap = createMap();
123
+
124
+ /**
125
+ * Whether or not the keyMap is currently valid.
126
+ * @type {boolean}
127
+ */
128
+ this.keyMapValid = true;
129
+
130
+ /**
131
+ * Whether or the associated node is, or contains, a focused Element.
132
+ * @type {boolean}
133
+ */
134
+ this.focused = false;
135
+
136
+ /**
137
+ * The node name for this node.
138
+ * @const {string}
139
+ */
140
+ this.nodeName = nodeName;
141
+
142
+ /**
143
+ * @type {?string}
144
+ */
145
+ this.text = null;
146
+ }
147
+
148
+ /**
149
+ * Initializes a NodeData object for a Node.
150
+ *
151
+ * @param {Node} node The node to initialize data for.
152
+ * @param {string} nodeName The node name of node.
153
+ * @param {?string=} key The key that identifies the node.
154
+ * @return {!NodeData} The newly initialized data object
155
+ */
156
+ var initData = function (node, nodeName, key) {
157
+ var data = new NodeData(nodeName, key);
158
+ node[DATA_PROP] = data;
159
+ return data;
160
+ };
161
+
162
+ /**
163
+ * Retrieves the NodeData object for a Node, creating it if necessary.
164
+ *
165
+ * @param {?Node} node The Node to retrieve the data for.
166
+ * @return {!NodeData} The NodeData for this Node.
167
+ */
168
+ var getData = function (node) {
169
+ importNode(node);
170
+ return node[DATA_PROP];
171
+ };
172
+
173
+ /**
174
+ * Imports node and its subtree, initializing caches.
175
+ *
176
+ * @param {?Node} node The Node to import.
177
+ */
178
+ var importNode = function (node) {
179
+ if (node[DATA_PROP]) {
180
+ return;
181
+ }
182
+
183
+ var isElement = node instanceof Element;
184
+ var nodeName = isElement ? node.localName : node.nodeName;
185
+ var key = isElement ? node.getAttribute('key') : null;
186
+ var data = initData(node, nodeName, key);
187
+
188
+ if (key) {
189
+ getData(node.parentNode).keyMap[key] = node;
190
+ }
191
+
192
+ if (isElement) {
193
+ var attributes = node.attributes;
194
+ var attrs = data.attrs;
195
+ var newAttrs = data.newAttrs;
196
+ var attrsArr = data.attrsArr;
197
+
198
+ for (var i = 0; i < attributes.length; i += 1) {
199
+ var attr = attributes[i];
200
+ var name = attr.name;
201
+ var value = attr.value;
202
+
203
+ attrs[name] = value;
204
+ newAttrs[name] = undefined;
205
+ attrsArr.push(name);
206
+ attrsArr.push(value);
207
+ }
208
+ }
209
+
210
+ for (var child = node.firstChild; child; child = child.nextSibling) {
211
+ importNode(child);
212
+ }
213
+ };
214
+
215
+ /**
216
+ * Gets the namespace to create an element (of a given tag) in.
217
+ * @param {string} tag The tag to get the namespace for.
218
+ * @param {?Node} parent
219
+ * @return {?string} The namespace to create the tag in.
220
+ */
221
+ var getNamespaceForTag = function (tag, parent) {
222
+ if (tag === 'svg') {
223
+ return 'http://www.w3.org/2000/svg';
224
+ }
225
+
226
+ if (getData(parent).nodeName === 'foreignObject') {
227
+ return null;
228
+ }
229
+
230
+ return parent.namespaceURI;
231
+ };
232
+
233
+ /**
234
+ * Creates an Element.
235
+ * @param {Document} doc The document with which to create the Element.
236
+ * @param {?Node} parent
237
+ * @param {string} tag The tag for the Element.
238
+ * @param {?string=} key A key to identify the Element.
239
+ * @return {!Element}
240
+ */
241
+ var createElement = function (doc, parent, tag, key) {
242
+ var namespace = getNamespaceForTag(tag, parent);
243
+ var el = undefined;
244
+
245
+ if (namespace) {
246
+ el = doc.createElementNS(namespace, tag);
247
+ } else {
248
+ el = doc.createElement(tag);
249
+ }
250
+
251
+ initData(el, tag, key);
252
+
253
+ return el;
254
+ };
255
+
256
+ /**
257
+ * Creates a Text Node.
258
+ * @param {Document} doc The document with which to create the Element.
259
+ * @return {!Text}
260
+ */
261
+ var createText = function (doc) {
262
+ var node = doc.createTextNode('');
263
+ initData(node, '#text', null);
264
+ return node;
265
+ };
266
+
267
+ /**
268
+ * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
269
+ *
270
+ * Licensed under the Apache License, Version 2.0 (the "License");
271
+ * you may not use this file except in compliance with the License.
272
+ * You may obtain a copy of the License at
273
+ *
274
+ * http://www.apache.org/licenses/LICENSE-2.0
275
+ *
276
+ * Unless required by applicable law or agreed to in writing, software
277
+ * distributed under the License is distributed on an "AS-IS" BASIS,
278
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
279
+ * See the License for the specific language governing permissions and
280
+ * limitations under the License.
281
+ */
282
+
283
+ /** @const */
284
+ var notifications = {
285
+ /**
286
+ * Called after patch has compleated with any Nodes that have been created
287
+ * and added to the DOM.
288
+ * @type {?function(Array<!Node>)}
289
+ */
290
+ nodesCreated: null,
291
+
292
+ /**
293
+ * Called after patch has compleated with any Nodes that have been removed
294
+ * from the DOM.
295
+ * Note it's an applications responsibility to handle any childNodes.
296
+ * @type {?function(Array<!Node>)}
297
+ */
298
+ nodesDeleted: null
299
+ };
300
+
301
+ /**
302
+ * Keeps track of the state of a patch.
303
+ * @constructor
304
+ */
305
+ function Context() {
306
+ /**
307
+ * @type {(Array<!Node>|undefined)}
308
+ */
309
+ this.created = notifications.nodesCreated && [];
310
+
311
+ /**
312
+ * @type {(Array<!Node>|undefined)}
313
+ */
314
+ this.deleted = notifications.nodesDeleted && [];
315
+ }
316
+
317
+ /**
318
+ * @param {!Node} node
319
+ */
320
+ Context.prototype.markCreated = function (node) {
321
+ if (this.created) {
322
+ this.created.push(node);
323
+ }
324
+ };
325
+
326
+ /**
327
+ * @param {!Node} node
328
+ */
329
+ Context.prototype.markDeleted = function (node) {
330
+ if (this.deleted) {
331
+ this.deleted.push(node);
332
+ }
333
+ };
334
+
335
+ /**
336
+ * Notifies about nodes that were created during the patch opearation.
337
+ */
338
+ Context.prototype.notifyChanges = function () {
339
+ if (this.created && this.created.length > 0) {
340
+ notifications.nodesCreated(this.created);
341
+ }
342
+
343
+ if (this.deleted && this.deleted.length > 0) {
344
+ notifications.nodesDeleted(this.deleted);
345
+ }
346
+ };
347
+
348
+ /**
349
+ * Copyright 2016 The Incremental DOM Authors. All Rights Reserved.
350
+ *
351
+ * Licensed under the Apache License, Version 2.0 (the "License");
352
+ * you may not use this file except in compliance with the License.
353
+ * You may obtain a copy of the License at
354
+ *
355
+ * http://www.apache.org/licenses/LICENSE-2.0
356
+ *
357
+ * Unless required by applicable law or agreed to in writing, software
358
+ * distributed under the License is distributed on an "AS-IS" BASIS,
359
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
360
+ * See the License for the specific language governing permissions and
361
+ * limitations under the License.
362
+ */
363
+
364
+ /**
365
+ * @param {!Node} node
366
+ * @return {boolean} True if the node the root of a document, false otherwise.
367
+ */
368
+ var isDocumentRoot = function (node) {
369
+ // For ShadowRoots, check if they are a DocumentFragment instead of if they
370
+ // are a ShadowRoot so that this can work in 'use strict' if ShadowRoots are
371
+ // not supported.
372
+ return node instanceof Document || node instanceof DocumentFragment;
373
+ };
374
+
375
+ /**
376
+ * @param {!Node} node The node to start at, inclusive.
377
+ * @param {?Node} root The root ancestor to get until, exclusive.
378
+ * @return {!Array<!Node>} The ancestry of DOM nodes.
379
+ */
380
+ var getAncestry = function (node, root) {
381
+ var ancestry = [];
382
+ var cur = node;
383
+
384
+ while (cur !== root) {
385
+ ancestry.push(cur);
386
+ cur = cur.parentNode;
387
+ }
388
+
389
+ return ancestry;
390
+ };
391
+
392
+ /**
393
+ * @param {!Node} node
394
+ * @return {!Node} The root node of the DOM tree that contains node.
395
+ */
396
+ var getRoot = function (node) {
397
+ var cur = node;
398
+ var prev = cur;
399
+
400
+ while (cur) {
401
+ prev = cur;
402
+ cur = cur.parentNode;
403
+ }
404
+
405
+ return prev;
406
+ };
407
+
408
+ /**
409
+ * @param {!Node} node The node to get the activeElement for.
410
+ * @return {?Element} The activeElement in the Document or ShadowRoot
411
+ * corresponding to node, if present.
412
+ */
413
+ var getActiveElement = function (node) {
414
+ var root = getRoot(node);
415
+ return isDocumentRoot(root) ? root.activeElement : null;
416
+ };
417
+
418
+ /**
419
+ * Gets the path of nodes that contain the focused node in the same document as
420
+ * a reference node, up until the root.
421
+ * @param {!Node} node The reference node to get the activeElement for.
422
+ * @param {?Node} root The root to get the focused path until.
423
+ * @return {!Array<Node>}
424
+ */
425
+ var getFocusedPath = function (node, root) {
426
+ var activeElement = getActiveElement(node);
427
+
428
+ if (!activeElement || !node.contains(activeElement)) {
429
+ return [];
430
+ }
431
+
432
+ return getAncestry(activeElement, root);
433
+ };
434
+
435
+ /**
436
+ * Like insertBefore, but instead instead of moving the desired node, instead
437
+ * moves all the other nodes after.
438
+ * @param {?Node} parentNode
439
+ * @param {!Node} node
440
+ * @param {?Node} referenceNode
441
+ */
442
+ var moveBefore = function (parentNode, node, referenceNode) {
443
+ var insertReferenceNode = node.nextSibling;
444
+ var cur = referenceNode;
445
+
446
+ while (cur !== node) {
447
+ var next = cur.nextSibling;
448
+ parentNode.insertBefore(cur, insertReferenceNode);
449
+ cur = next;
450
+ }
451
+ };
452
+
453
+ /** @type {?Context} */
454
+ var context = null;
455
+
456
+ /** @type {?Node} */
457
+ var currentNode = null;
458
+
459
+ /** @type {?Node} */
460
+ var currentParent = null;
461
+
462
+ /** @type {?Document} */
463
+ var doc = null;
464
+
465
+ /**
466
+ * @param {!Array<Node>} focusPath The nodes to mark.
467
+ * @param {boolean} focused Whether or not they are focused.
468
+ */
469
+ var markFocused = function (focusPath, focused) {
470
+ for (var i = 0; i < focusPath.length; i += 1) {
471
+ getData(focusPath[i]).focused = focused;
472
+ }
473
+ };
474
+
475
+ /**
476
+ * Returns a patcher function that sets up and restores a patch context,
477
+ * running the run function with the provided data.
478
+ * @param {function((!Element|!DocumentFragment),!function(T),T=): ?Node} run
479
+ * @return {function((!Element|!DocumentFragment),!function(T),T=): ?Node}
480
+ * @template T
481
+ */
482
+ var patchFactory = function (run) {
483
+ /**
484
+ * TODO(moz): These annotations won't be necessary once we switch to Closure
485
+ * Compiler's new type inference. Remove these once the switch is done.
486
+ *
487
+ * @param {(!Element|!DocumentFragment)} node
488
+ * @param {!function(T)} fn
489
+ * @param {T=} data
490
+ * @return {?Node} node
491
+ * @template T
492
+ */
493
+ var f = function (node, fn, data) {
494
+ var prevContext = context;
495
+ var prevDoc = doc;
496
+ var prevCurrentNode = currentNode;
497
+ var prevCurrentParent = currentParent;
498
+ var previousInAttributes = false;
499
+ var previousInSkip = false;
500
+
501
+ context = new Context();
502
+ doc = node.ownerDocument;
503
+ currentParent = node.parentNode;
504
+
505
+ if ('production' !== 'production') {}
506
+
507
+ var focusPath = getFocusedPath(node, currentParent);
508
+ markFocused(focusPath, true);
509
+ var retVal = run(node, fn, data);
510
+ markFocused(focusPath, false);
511
+
512
+ if ('production' !== 'production') {}
513
+
514
+ context.notifyChanges();
515
+
516
+ context = prevContext;
517
+ doc = prevDoc;
518
+ currentNode = prevCurrentNode;
519
+ currentParent = prevCurrentParent;
520
+
521
+ return retVal;
522
+ };
523
+ return f;
524
+ };
525
+
526
+ /**
527
+ * Patches the document starting at node with the provided function. This
528
+ * function may be called during an existing patch operation.
529
+ * @param {!Element|!DocumentFragment} node The Element or Document
530
+ * to patch.
531
+ * @param {!function(T)} fn A function containing elementOpen/elementClose/etc.
532
+ * calls that describe the DOM.
533
+ * @param {T=} data An argument passed to fn to represent DOM state.
534
+ * @return {!Node} The patched node.
535
+ * @template T
536
+ */
537
+ var patchInner = patchFactory(function (node, fn, data) {
538
+ currentNode = node;
539
+
540
+ enterNode();
541
+ fn(data);
542
+ exitNode();
543
+
544
+ if ('production' !== 'production') {}
545
+
546
+ return node;
547
+ });
548
+
549
+ /**
550
+ * Patches an Element with the the provided function. Exactly one top level
551
+ * element call should be made corresponding to `node`.
552
+ * @param {!Element} node The Element where the patch should start.
553
+ * @param {!function(T)} fn A function containing elementOpen/elementClose/etc.
554
+ * calls that describe the DOM. This should have at most one top level
555
+ * element call.
556
+ * @param {T=} data An argument passed to fn to represent DOM state.
557
+ * @return {?Node} The node if it was updated, its replacedment or null if it
558
+ * was removed.
559
+ * @template T
560
+ */
561
+ var patchOuter = patchFactory(function (node, fn, data) {
562
+ var startNode = /** @type {!Element} */{ nextSibling: node };
563
+ var expectedNextNode = null;
564
+ var expectedPrevNode = null;
565
+
566
+ if ('production' !== 'production') {}
567
+
568
+ currentNode = startNode;
569
+ fn(data);
570
+
571
+ if ('production' !== 'production') {}
572
+
573
+ if (node !== currentNode && node.parentNode) {
574
+ removeChild(currentParent, node, getData(currentParent).keyMap);
575
+ }
576
+
577
+ return startNode === currentNode ? null : currentNode;
578
+ });
579
+
580
+ /**
581
+ * Checks whether or not the current node matches the specified nodeName and
582
+ * key.
583
+ *
584
+ * @param {!Node} matchNode A node to match the data to.
585
+ * @param {?string} nodeName The nodeName for this node.
586
+ * @param {?string=} key An optional key that identifies a node.
587
+ * @return {boolean} True if the node matches, false otherwise.
588
+ */
589
+ var matches = function (matchNode, nodeName, key) {
590
+ var data = getData(matchNode);
591
+
592
+ // Key check is done using double equals as we want to treat a null key the
593
+ // same as undefined. This should be okay as the only values allowed are
594
+ // strings, null and undefined so the == semantics are not too weird.
595
+ return nodeName === data.nodeName && key == data.key;
596
+ };
597
+
598
+ /**
599
+ * Aligns the virtual Element definition with the actual DOM, moving the
600
+ * corresponding DOM node to the correct location or creating it if necessary.
601
+ * @param {string} nodeName For an Element, this should be a valid tag string.
602
+ * For a Text, this should be #text.
603
+ * @param {?string=} key The key used to identify this element.
604
+ */
605
+ var alignWithDOM = function (nodeName, key) {
606
+ if (currentNode && matches(currentNode, nodeName, key)) {
607
+ return;
608
+ }
609
+
610
+ var parentData = getData(currentParent);
611
+ var currentNodeData = currentNode && getData(currentNode);
612
+ var keyMap = parentData.keyMap;
613
+ var node = undefined;
614
+
615
+ // Check to see if the node has moved within the parent.
616
+ if (key) {
617
+ var keyNode = keyMap[key];
618
+ if (keyNode) {
619
+ if (matches(keyNode, nodeName, key)) {
620
+ node = keyNode;
621
+ } else if (keyNode === currentNode) {
622
+ context.markDeleted(keyNode);
623
+ } else {
624
+ removeChild(currentParent, keyNode, keyMap);
625
+ }
626
+ }
627
+ }
628
+
629
+ // Create the node if it doesn't exist.
630
+ if (!node) {
631
+ if (nodeName === '#text') {
632
+ node = createText(doc);
633
+ } else {
634
+ node = createElement(doc, currentParent, nodeName, key);
635
+ }
636
+
637
+ if (key) {
638
+ keyMap[key] = node;
639
+ }
640
+
641
+ context.markCreated(node);
642
+ }
643
+
644
+ // Re-order the node into the right position, preserving focus if either
645
+ // node or currentNode are focused by making sure that they are not detached
646
+ // from the DOM.
647
+ if (getData(node).focused) {
648
+ // Move everything else before the node.
649
+ moveBefore(currentParent, node, currentNode);
650
+ } else if (currentNodeData && currentNodeData.key && !currentNodeData.focused) {
651
+ // Remove the currentNode, which can always be added back since we hold a
652
+ // reference through the keyMap. This prevents a large number of moves when
653
+ // a keyed item is removed or moved backwards in the DOM.
654
+ currentParent.replaceChild(node, currentNode);
655
+ parentData.keyMapValid = false;
656
+ } else {
657
+ currentParent.insertBefore(node, currentNode);
658
+ }
659
+
660
+ currentNode = node;
661
+ };
662
+
663
+ /**
664
+ * @param {?Node} node
665
+ * @param {?Node} child
666
+ * @param {?Object<string, !Element>} keyMap
667
+ */
668
+ var removeChild = function (node, child, keyMap) {
669
+ node.removeChild(child);
670
+ context.markDeleted( /** @type {!Node}*/child);
671
+
672
+ var key = getData(child).key;
673
+ if (key) {
674
+ delete keyMap[key];
675
+ }
676
+ };
677
+
678
+ /**
679
+ * Clears out any unvisited Nodes, as the corresponding virtual element
680
+ * functions were never called for them.
681
+ */
682
+ var clearUnvisitedDOM = function () {
683
+ var node = currentParent;
684
+ var data = getData(node);
685
+ var keyMap = data.keyMap;
686
+ var keyMapValid = data.keyMapValid;
687
+ var child = node.lastChild;
688
+ var key = undefined;
689
+
690
+ if (child === currentNode && keyMapValid) {
691
+ return;
692
+ }
693
+
694
+ while (child !== currentNode) {
695
+ removeChild(node, child, keyMap);
696
+ child = node.lastChild;
697
+ }
698
+
699
+ // Clean the keyMap, removing any unusued keys.
700
+ if (!keyMapValid) {
701
+ for (key in keyMap) {
702
+ child = keyMap[key];
703
+ if (child.parentNode !== node) {
704
+ context.markDeleted(child);
705
+ delete keyMap[key];
706
+ }
707
+ }
708
+
709
+ data.keyMapValid = true;
710
+ }
711
+ };
712
+
713
+ /**
714
+ * Changes to the first child of the current node.
715
+ */
716
+ var enterNode = function () {
717
+ currentParent = currentNode;
718
+ currentNode = null;
719
+ };
720
+
721
+ /**
722
+ * @return {?Node} The next Node to be patched.
723
+ */
724
+ var getNextNode = function () {
725
+ if (currentNode) {
726
+ return currentNode.nextSibling;
727
+ } else {
728
+ return currentParent.firstChild;
729
+ }
730
+ };
731
+
732
+ /**
733
+ * Changes to the next sibling of the current node.
734
+ */
735
+ var nextNode = function () {
736
+ currentNode = getNextNode();
737
+ };
738
+
739
+ /**
740
+ * Changes to the parent of the current node, removing any unvisited children.
741
+ */
742
+ var exitNode = function () {
743
+ clearUnvisitedDOM();
744
+
745
+ currentNode = currentParent;
746
+ currentParent = currentParent.parentNode;
747
+ };
748
+
749
+ /**
750
+ * Makes sure that the current node is an Element with a matching tagName and
751
+ * key.
752
+ *
753
+ * @param {string} tag The element's tag.
754
+ * @param {?string=} key The key used to identify this element. This can be an
755
+ * empty string, but performance may be better if a unique value is used
756
+ * when iterating over an array of items.
757
+ * @return {!Element} The corresponding Element.
758
+ */
759
+ var coreElementOpen = function (tag, key) {
760
+ nextNode();
761
+ alignWithDOM(tag, key);
762
+ enterNode();
763
+ return (/** @type {!Element} */currentParent
764
+ );
765
+ };
766
+
767
+ /**
768
+ * Closes the currently open Element, removing any unvisited children if
769
+ * necessary.
770
+ *
771
+ * @return {!Element} The corresponding Element.
772
+ */
773
+ var coreElementClose = function () {
774
+ if ('production' !== 'production') {}
775
+
776
+ exitNode();
777
+ return (/** @type {!Element} */currentNode
778
+ );
779
+ };
780
+
781
+ /**
782
+ * Makes sure the current node is a Text node and creates a Text node if it is
783
+ * not.
784
+ *
785
+ * @return {!Text} The corresponding Text Node.
786
+ */
787
+ var coreText = function () {
788
+ nextNode();
789
+ alignWithDOM('#text', null);
790
+ return (/** @type {!Text} */currentNode
791
+ );
792
+ };
793
+
794
+ /**
795
+ * Gets the current Element being patched.
796
+ * @return {!Element}
797
+ */
798
+ var currentElement = function () {
799
+ if ('production' !== 'production') {}
800
+ return (/** @type {!Element} */currentParent
801
+ );
802
+ };
803
+
804
+ /**
805
+ * @return {Node} The Node that will be evaluated for the next instruction.
806
+ */
807
+ var currentPointer = function () {
808
+ if ('production' !== 'production') {}
809
+ return getNextNode();
810
+ };
811
+
812
+ /**
813
+ * Skips the children in a subtree, allowing an Element to be closed without
814
+ * clearing out the children.
815
+ */
816
+ var skip = function () {
817
+ if ('production' !== 'production') {}
818
+ currentNode = currentParent.lastChild;
819
+ };
820
+
821
+ /**
822
+ * Skips the next Node to be patched, moving the pointer forward to the next
823
+ * sibling of the current pointer.
824
+ */
825
+ var skipNode = nextNode;
826
+
827
+ /**
828
+ * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
829
+ *
830
+ * Licensed under the Apache License, Version 2.0 (the "License");
831
+ * you may not use this file except in compliance with the License.
832
+ * You may obtain a copy of the License at
833
+ *
834
+ * http://www.apache.org/licenses/LICENSE-2.0
835
+ *
836
+ * Unless required by applicable law or agreed to in writing, software
837
+ * distributed under the License is distributed on an "AS-IS" BASIS,
838
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
839
+ * See the License for the specific language governing permissions and
840
+ * limitations under the License.
841
+ */
842
+
843
+ /** @const */
844
+ var symbols = {
845
+ default: '__default'
846
+ };
847
+
848
+ /**
849
+ * @param {string} name
850
+ * @return {string|undefined} The namespace to use for the attribute.
851
+ */
852
+ var getNamespace = function (name) {
853
+ if (name.lastIndexOf('xml:', 0) === 0) {
854
+ return 'http://www.w3.org/XML/1998/namespace';
855
+ }
856
+
857
+ if (name.lastIndexOf('xlink:', 0) === 0) {
858
+ return 'http://www.w3.org/1999/xlink';
859
+ }
860
+ };
861
+
862
+ /**
863
+ * Applies an attribute or property to a given Element. If the value is null
864
+ * or undefined, it is removed from the Element. Otherwise, the value is set
865
+ * as an attribute.
866
+ * @param {!Element} el
867
+ * @param {string} name The attribute's name.
868
+ * @param {?(boolean|number|string)=} value The attribute's value.
869
+ */
870
+ var applyAttr = function (el, name, value) {
871
+ if (value == null) {
872
+ el.removeAttribute(name);
873
+ } else {
874
+ var attrNS = getNamespace(name);
875
+ if (attrNS) {
876
+ el.setAttributeNS(attrNS, name, value);
877
+ } else {
878
+ el.setAttribute(name, value);
879
+ }
880
+ }
881
+ };
882
+
883
+ /**
884
+ * Applies a property to a given Element.
885
+ * @param {!Element} el
886
+ * @param {string} name The property's name.
887
+ * @param {*} value The property's value.
888
+ */
889
+ var applyProp = function (el, name, value) {
890
+ el[name] = value;
891
+ };
892
+
893
+ /**
894
+ * Applies a value to a style declaration. Supports CSS custom properties by
895
+ * setting properties containing a dash using CSSStyleDeclaration.setProperty.
896
+ * @param {CSSStyleDeclaration} style
897
+ * @param {!string} prop
898
+ * @param {*} value
899
+ */
900
+ var setStyleValue = function (style, prop, value) {
901
+ if (prop.indexOf('-') >= 0) {
902
+ style.setProperty(prop, /** @type {string} */value);
903
+ } else {
904
+ style[prop] = value;
905
+ }
906
+ };
907
+
908
+ /**
909
+ * Applies a style to an Element. No vendor prefix expansion is done for
910
+ * property names/values.
911
+ * @param {!Element} el
912
+ * @param {string} name The attribute's name.
913
+ * @param {*} style The style to set. Either a string of css or an object
914
+ * containing property-value pairs.
915
+ */
916
+ var applyStyle = function (el, name, style) {
917
+ if (typeof style === 'string') {
918
+ el.style.cssText = style;
919
+ } else {
920
+ el.style.cssText = '';
921
+ var elStyle = el.style;
922
+ var obj = /** @type {!Object<string,string>} */style;
923
+
924
+ for (var prop in obj) {
925
+ if (has(obj, prop)) {
926
+ setStyleValue(elStyle, prop, obj[prop]);
927
+ }
928
+ }
929
+ }
930
+ };
931
+
932
+ /**
933
+ * Updates a single attribute on an Element.
934
+ * @param {!Element} el
935
+ * @param {string} name The attribute's name.
936
+ * @param {*} value The attribute's value. If the value is an object or
937
+ * function it is set on the Element, otherwise, it is set as an HTML
938
+ * attribute.
939
+ */
940
+ var applyAttributeTyped = function (el, name, value) {
941
+ var type = typeof value;
942
+
943
+ if (type === 'object' || type === 'function') {
944
+ applyProp(el, name, value);
945
+ } else {
946
+ applyAttr(el, name, /** @type {?(boolean|number|string)} */value);
947
+ }
948
+ };
949
+
950
+ /**
951
+ * Calls the appropriate attribute mutator for this attribute.
952
+ * @param {!Element} el
953
+ * @param {string} name The attribute's name.
954
+ * @param {*} value The attribute's value.
955
+ */
956
+ var updateAttribute = function (el, name, value) {
957
+ var data = getData(el);
958
+ var attrs = data.attrs;
959
+
960
+ if (attrs[name] === value) {
961
+ return;
962
+ }
963
+
964
+ var mutator = attributes[name] || attributes[symbols.default];
965
+ mutator(el, name, value);
966
+
967
+ attrs[name] = value;
968
+ };
969
+
970
+ /**
971
+ * A publicly mutable object to provide custom mutators for attributes.
972
+ * @const {!Object<string, function(!Element, string, *)>}
973
+ */
974
+ var attributes = createMap();
975
+
976
+ // Special generic mutator that's called for any attribute that does not
977
+ // have a specific mutator.
978
+ attributes[symbols.default] = applyAttributeTyped;
979
+
980
+ attributes['style'] = applyStyle;
981
+
982
+ /**
983
+ * The offset in the virtual element declaration where the attributes are
984
+ * specified.
985
+ * @const
986
+ */
987
+ var ATTRIBUTES_OFFSET = 3;
988
+
989
+ /**
990
+ * Builds an array of arguments for use with elementOpenStart, attr and
991
+ * elementOpenEnd.
992
+ * @const {Array<*>}
993
+ */
994
+ var argsBuilder = [];
995
+
996
+ /**
997
+ * @param {string} tag The element's tag.
998
+ * @param {?string=} key The key used to identify this element. This can be an
999
+ * empty string, but performance may be better if a unique value is used
1000
+ * when iterating over an array of items.
1001
+ * @param {?Array<*>=} statics An array of attribute name/value pairs of the
1002
+ * static attributes for the Element. These will only be set once when the
1003
+ * Element is created.
1004
+ * @param {...*} var_args, Attribute name/value pairs of the dynamic attributes
1005
+ * for the Element.
1006
+ * @return {!Element} The corresponding Element.
1007
+ */
1008
+ var elementOpen = function (tag, key, statics, var_args) {
1009
+ if ('production' !== 'production') {}
1010
+
1011
+ var node = coreElementOpen(tag, key);
1012
+ var data = getData(node);
1013
+
1014
+ if (!data.staticsApplied) {
1015
+ if (statics) {
1016
+ for (var _i = 0; _i < statics.length; _i += 2) {
1017
+ var name = /** @type {string} */statics[_i];
1018
+ var value = statics[_i + 1];
1019
+ updateAttribute(node, name, value);
1020
+ }
1021
+ }
1022
+ // Down the road, we may want to keep track of the statics array to use it
1023
+ // as an additional signal about whether a node matches or not. For now,
1024
+ // just use a marker so that we do not reapply statics.
1025
+ data.staticsApplied = true;
1026
+ }
1027
+
1028
+ /*
1029
+ * Checks to see if one or more attributes have changed for a given Element.
1030
+ * When no attributes have changed, this is much faster than checking each
1031
+ * individual argument. When attributes have changed, the overhead of this is
1032
+ * minimal.
1033
+ */
1034
+ var attrsArr = data.attrsArr;
1035
+ var newAttrs = data.newAttrs;
1036
+ var isNew = !attrsArr.length;
1037
+ var i = ATTRIBUTES_OFFSET;
1038
+ var j = 0;
1039
+
1040
+ for (; i < arguments.length; i += 2, j += 2) {
1041
+ var _attr = arguments[i];
1042
+ if (isNew) {
1043
+ attrsArr[j] = _attr;
1044
+ newAttrs[_attr] = undefined;
1045
+ } else if (attrsArr[j] !== _attr) {
1046
+ break;
1047
+ }
1048
+
1049
+ var value = arguments[i + 1];
1050
+ if (isNew || attrsArr[j + 1] !== value) {
1051
+ attrsArr[j + 1] = value;
1052
+ updateAttribute(node, _attr, value);
1053
+ }
1054
+ }
1055
+
1056
+ if (i < arguments.length || j < attrsArr.length) {
1057
+ for (; i < arguments.length; i += 1, j += 1) {
1058
+ attrsArr[j] = arguments[i];
1059
+ }
1060
+
1061
+ if (j < attrsArr.length) {
1062
+ attrsArr.length = j;
1063
+ }
1064
+
1065
+ /*
1066
+ * Actually perform the attribute update.
1067
+ */
1068
+ for (i = 0; i < attrsArr.length; i += 2) {
1069
+ var name = /** @type {string} */attrsArr[i];
1070
+ var value = attrsArr[i + 1];
1071
+ newAttrs[name] = value;
1072
+ }
1073
+
1074
+ for (var _attr2 in newAttrs) {
1075
+ updateAttribute(node, _attr2, newAttrs[_attr2]);
1076
+ newAttrs[_attr2] = undefined;
1077
+ }
1078
+ }
1079
+
1080
+ return node;
1081
+ };
1082
+
1083
+ /**
1084
+ * Declares a virtual Element at the current location in the document. This
1085
+ * corresponds to an opening tag and a elementClose tag is required. This is
1086
+ * like elementOpen, but the attributes are defined using the attr function
1087
+ * rather than being passed as arguments. Must be folllowed by 0 or more calls
1088
+ * to attr, then a call to elementOpenEnd.
1089
+ * @param {string} tag The element's tag.
1090
+ * @param {?string=} key The key used to identify this element. This can be an
1091
+ * empty string, but performance may be better if a unique value is used
1092
+ * when iterating over an array of items.
1093
+ * @param {?Array<*>=} statics An array of attribute name/value pairs of the
1094
+ * static attributes for the Element. These will only be set once when the
1095
+ * Element is created.
1096
+ */
1097
+ var elementOpenStart = function (tag, key, statics) {
1098
+ if ('production' !== 'production') {}
1099
+
1100
+ argsBuilder[0] = tag;
1101
+ argsBuilder[1] = key;
1102
+ argsBuilder[2] = statics;
1103
+ };
1104
+
1105
+ /***
1106
+ * Defines a virtual attribute at this point of the DOM. This is only valid
1107
+ * when called between elementOpenStart and elementOpenEnd.
1108
+ *
1109
+ * @param {string} name
1110
+ * @param {*} value
1111
+ */
1112
+ var attr = function (name, value) {
1113
+ if ('production' !== 'production') {}
1114
+
1115
+ argsBuilder.push(name);
1116
+ argsBuilder.push(value);
1117
+ };
1118
+
1119
+ /**
1120
+ * Closes an open tag started with elementOpenStart.
1121
+ * @return {!Element} The corresponding Element.
1122
+ */
1123
+ var elementOpenEnd = function () {
1124
+ if ('production' !== 'production') {}
1125
+
1126
+ var node = elementOpen.apply(null, argsBuilder);
1127
+ argsBuilder.length = 0;
1128
+ return node;
1129
+ };
1130
+
1131
+ /**
1132
+ * Closes an open virtual Element.
1133
+ *
1134
+ * @param {string} tag The element's tag.
1135
+ * @return {!Element} The corresponding Element.
1136
+ */
1137
+ var elementClose = function (tag) {
1138
+ if ('production' !== 'production') {}
1139
+
1140
+ var node = coreElementClose();
1141
+
1142
+ if ('production' !== 'production') {}
1143
+
1144
+ return node;
1145
+ };
1146
+
1147
+ /**
1148
+ * Declares a virtual Element at the current location in the document that has
1149
+ * no children.
1150
+ * @param {string} tag The element's tag.
1151
+ * @param {?string=} key The key used to identify this element. This can be an
1152
+ * empty string, but performance may be better if a unique value is used
1153
+ * when iterating over an array of items.
1154
+ * @param {?Array<*>=} statics An array of attribute name/value pairs of the
1155
+ * static attributes for the Element. These will only be set once when the
1156
+ * Element is created.
1157
+ * @param {...*} var_args Attribute name/value pairs of the dynamic attributes
1158
+ * for the Element.
1159
+ * @return {!Element} The corresponding Element.
1160
+ */
1161
+ var elementVoid = function (tag, key, statics, var_args) {
1162
+ elementOpen.apply(null, arguments);
1163
+ return elementClose(tag);
1164
+ };
1165
+
1166
+ /**
1167
+ * Declares a virtual Text at this point in the document.
1168
+ *
1169
+ * @param {string|number|boolean} value The value of the Text.
1170
+ * @param {...(function((string|number|boolean)):string)} var_args
1171
+ * Functions to format the value which are called only when the value has
1172
+ * changed.
1173
+ * @return {!Text} The corresponding text node.
1174
+ */
1175
+ var text = function (value, var_args) {
1176
+ if ('production' !== 'production') {}
1177
+
1178
+ var node = coreText();
1179
+ var data = getData(node);
1180
+
1181
+ if (data.text !== value) {
1182
+ data.text = /** @type {string} */value;
1183
+
1184
+ var formatted = value;
1185
+ for (var i = 1; i < arguments.length; i += 1) {
1186
+ /*
1187
+ * Call the formatter function directly to prevent leaking arguments.
1188
+ * https://github.com/google/incremental-dom/pull/204#issuecomment-178223574
1189
+ */
1190
+ var fn = arguments[i];
1191
+ formatted = fn(formatted);
1192
+ }
1193
+
1194
+ node.data = formatted;
1195
+ }
1196
+
1197
+ return node;
1198
+ };
1199
+
1200
+ exports.patch = patchInner;
1201
+ exports.patchInner = patchInner;
1202
+ exports.patchOuter = patchOuter;
1203
+ exports.currentElement = currentElement;
1204
+ exports.currentPointer = currentPointer;
1205
+ exports.skip = skip;
1206
+ exports.skipNode = skipNode;
1207
+ exports.elementVoid = elementVoid;
1208
+ exports.elementOpenStart = elementOpenStart;
1209
+ exports.elementOpenEnd = elementOpenEnd;
1210
+ exports.elementOpen = elementOpen;
1211
+ exports.elementClose = elementClose;
1212
+ exports.text = text;
1213
+ exports.attr = attr;
1214
+ exports.symbols = symbols;
1215
+ exports.attributes = attributes;
1216
+ exports.applyAttr = applyAttr;
1217
+ exports.applyProp = applyProp;
1218
+ exports.notifications = notifications;
1219
+ exports.importNode = importNode;
1220
+
1221
+ }));
1222
+
1223
+ //!#! sourceMappingURL=https://ajax.googleapis.com/ajax/libs/incrementaldom/0.5.1/incremental-dom.js.map