tao_on_rails 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/tao_on_rails/rails/version.rb +1 -1
- data/lib/tasks/tao_icons.rake +43 -44
- data/vendor/assets/javascripts/custom-elements.js +861 -0
- data/vendor/assets/javascripts/native-shim.coffee +90 -0
- data/vendor/assets/javascripts/tao.coffee +4 -1
- data/vendor/assets/javascripts/tao/component.coffee +14 -0
- data/vendor/assets/javascripts/tao/icons.coffee +2 -0
- data/vendor/assets/javascripts/tao/module.coffee +41 -2
- data/vendor/assets/stylesheets/tao.scss +2 -5
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8b45a9f5ea2544ee0e862a0fda2951823782acf
|
4
|
+
data.tar.gz: 0f0d0a4e37d370970168da6ea3713648345f8129
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 401af2ff83fcc21a8cbb43bdbb60582f392b311ef2509dd7cfe4c61c3f1cf6c5411c37bdf68f47fda4ef3092308b202bdb1286e9df14e1095ce4cf4ac70b10f1
|
7
|
+
data.tar.gz: a724d766fa57de46db680b9397c96779ca24de274eeb9d36738fca24bfff297273fe19cbbbaa354658afcf2de4ee1611ac1ebd34a4ac4286b9898e70825e1768
|
data/lib/tasks/tao_icons.rake
CHANGED
@@ -1,55 +1,54 @@
|
|
1
1
|
namespace :tao do
|
2
|
-
namespace :svg_icons do
|
3
|
-
desc 'generate svg icons.'
|
4
|
-
task :generate => :environment do
|
5
|
-
Dir.mkdir "#{Rails.root}/vendor/assets/javascripts/tao"
|
6
|
-
|
7
|
-
File.open "#{Rails.root}/vendor/assets/javascripts/tao/icons.coffee", 'w' do |f|
|
8
|
-
f.puts %{tao.iconsHtml = '''#{svg_html}'''}
|
9
|
-
end
|
10
|
-
end
|
11
2
|
|
12
|
-
|
3
|
+
desc 'generate svg icons.'
|
4
|
+
task :generate_icons => :environment do
|
5
|
+
Dir.mkdir "#{Rails.root}/vendor/assets/javascripts/tao"
|
13
6
|
|
14
|
-
|
15
|
-
%{
|
7
|
+
File.open "#{Rails.root}/vendor/assets/javascripts/tao/icons.coffee", 'w' do |f|
|
8
|
+
f.puts %{tao.iconsHtml = '''#{svg_html}'''}
|
16
9
|
end
|
10
|
+
end
|
17
11
|
|
18
|
-
|
19
|
-
@svg_files ||= Dir.glob(File.expand_path("#{::Rails.root}/app/assets/icons/*.svg")).uniq
|
20
|
-
end
|
12
|
+
private
|
21
13
|
|
22
|
-
|
23
|
-
|
24
|
-
|
14
|
+
def svg_html
|
15
|
+
%{<svg id="tao-icons" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none">\n#{symbols}</svg>}
|
16
|
+
end
|
25
17
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
content.gsub(/<?.+\?>/,'')
|
30
|
-
.gsub(/<!.+?>/,'')
|
31
|
-
.gsub(/<title>.*<\/title>/, '')
|
32
|
-
.gsub(/<desc>.*<\/desc>/, '')
|
33
|
-
.gsub(/id=/,'class=')
|
34
|
-
.gsub(/<svg.+?>/, %Q{<svg id="icon-#{name}" #{dimensions(content)}>})
|
35
|
-
.gsub(/svg/,'symbol')
|
36
|
-
.gsub(/\n/, '') # Remove endlines
|
37
|
-
.gsub(/\s{2,}/, ' ') # Remove whitespace
|
38
|
-
.gsub(/>\s+</, '><') # Remove whitespace between tags
|
39
|
-
end
|
18
|
+
def svg_files
|
19
|
+
@svg_files ||= Dir.glob(File.expand_path("#{::Rails.root}/app/assets/icons/*.svg")).uniq
|
20
|
+
end
|
40
21
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
22
|
+
def symbols
|
23
|
+
svg_files.map {|file| " #{symbol(file)}\n"}.join
|
24
|
+
end
|
25
|
+
|
26
|
+
def symbol(path)
|
27
|
+
name = File.basename(path, ".*").underscore().dasherize()
|
28
|
+
content = File.read(path)
|
29
|
+
content.gsub(/<?.+\?>/,'')
|
30
|
+
.gsub(/<!.+?>/,'')
|
31
|
+
.gsub(/<title>.*<\/title>/, '')
|
32
|
+
.gsub(/<desc>.*<\/desc>/, '')
|
33
|
+
.gsub(/id=/,'class=')
|
34
|
+
.gsub(/<svg.+?>/, %Q{<svg id="icon-#{name}" #{dimensions(content)}>})
|
35
|
+
.gsub(/svg/,'symbol')
|
36
|
+
.gsub(/\n/, '') # Remove endlines
|
37
|
+
.gsub(/\s{2,}/, ' ') # Remove whitespace
|
38
|
+
.gsub(/>\s+</, '><') # Remove whitespace between tags
|
39
|
+
end
|
40
|
+
|
41
|
+
def dimensions(content)
|
42
|
+
dimension = content.scan(/<svg.+(viewBox=["'](.+?)["'])/).flatten
|
43
|
+
viewbox = dimension.first
|
44
|
+
#coords = dimension.last.split(' ')
|
45
|
+
|
46
|
+
#width = coords[2].to_i - coords[0].to_i
|
47
|
+
#height = coords[3].to_i - coords[1].to_i
|
48
|
+
#hack android svg
|
49
|
+
width = '100%'
|
50
|
+
height = '100%'
|
51
|
+
%Q{#{viewbox} width="#{width}" height="#{height}"}
|
53
52
|
end
|
54
53
|
|
55
54
|
end
|
@@ -0,0 +1,861 @@
|
|
1
|
+
//= require native-shim
|
2
|
+
|
3
|
+
'use strict';
|
4
|
+
|
5
|
+
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
6
|
+
|
7
|
+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
8
|
+
|
9
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
10
|
+
|
11
|
+
//= require native-shim
|
12
|
+
|
13
|
+
/**
|
14
|
+
* @license
|
15
|
+
* Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
16
|
+
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
17
|
+
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
18
|
+
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
19
|
+
* Code distributed by Google as part of the polymer project is also
|
20
|
+
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
21
|
+
*/
|
22
|
+
|
23
|
+
/**
|
24
|
+
* 2.3
|
25
|
+
* http://w3c.github.io/webcomponents/spec/custom/#dfn-element-definition
|
26
|
+
* @typedef {{
|
27
|
+
* name: string,
|
28
|
+
* localName: string,
|
29
|
+
* constructor: function(new:HTMLElement),
|
30
|
+
* connectedCallback: (Function|undefined),
|
31
|
+
* disconnectedCallback: (Function|undefined),
|
32
|
+
* attributeChangedCallback: (Function|undefined),
|
33
|
+
* observedAttributes: Array<string>,
|
34
|
+
* }}
|
35
|
+
*/
|
36
|
+
var CustomElementDefinition = void 0;
|
37
|
+
|
38
|
+
/**
|
39
|
+
* @typedef {{
|
40
|
+
* resolve: !function(undefined),
|
41
|
+
* promise: !Promise<undefined>,
|
42
|
+
* }}
|
43
|
+
*/
|
44
|
+
var Deferred = void 0;
|
45
|
+
|
46
|
+
(function () {
|
47
|
+
'use strict';
|
48
|
+
|
49
|
+
var doc = document;
|
50
|
+
var win = window;
|
51
|
+
|
52
|
+
/**
|
53
|
+
* Gets 'customElement' from window so that it could be modified after
|
54
|
+
* the polyfill loads.
|
55
|
+
* @function
|
56
|
+
* @return {CustomElementRegistry}
|
57
|
+
*/
|
58
|
+
var _customElements = function _customElements() {
|
59
|
+
return win['customElements'];
|
60
|
+
};
|
61
|
+
|
62
|
+
var _observerProp = '__$CE_observer';
|
63
|
+
var _attachedProp = '__$CE_attached';
|
64
|
+
var _upgradedProp = '__$CE_upgraded';
|
65
|
+
|
66
|
+
if (_customElements()) {
|
67
|
+
_customElements().flush = function () {};
|
68
|
+
if (!_customElements().forcePolyfill) {
|
69
|
+
nativeShim();
|
70
|
+
return;
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
// name validation
|
75
|
+
// https://html.spec.whatwg.org/multipage/scripting.html#valid-custom-element-name
|
76
|
+
|
77
|
+
/**
|
78
|
+
* @const
|
79
|
+
* @type {Array<string>}
|
80
|
+
*/
|
81
|
+
var reservedTagList = ['annotation-xml', 'color-profile', 'font-face', 'font-face-src', 'font-face-uri', 'font-face-format', 'font-face-name', 'missing-glyph'];
|
82
|
+
|
83
|
+
/**
|
84
|
+
* @param {!string} name
|
85
|
+
* @return {!Error|undefined}
|
86
|
+
*/
|
87
|
+
function checkValidCustomElementName(name) {
|
88
|
+
if (!(/^[a-z][.0-9_a-z]*-[\-.0-9_a-z]*$/.test(name) && reservedTagList.indexOf(name) === -1)) {
|
89
|
+
return new Error('The element name \'' + name + '\' is not valid.');
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
/**
|
94
|
+
* @param {!Node} root
|
95
|
+
* @return {TreeWalker}
|
96
|
+
*/
|
97
|
+
function createTreeWalker(root) {
|
98
|
+
// IE 11 requires the third and fourth arguments be present. If the third
|
99
|
+
// arg is null, it applies the default behaviour. However IE also requires
|
100
|
+
// the fourth argument be present even though the other browsers ignore it.
|
101
|
+
return doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null, false);
|
102
|
+
}
|
103
|
+
|
104
|
+
/**
|
105
|
+
* @param {!Node} node
|
106
|
+
* @return {boolean}
|
107
|
+
*/
|
108
|
+
function isElement(node) {
|
109
|
+
return node.nodeType === Node.ELEMENT_NODE;
|
110
|
+
}
|
111
|
+
|
112
|
+
/**
|
113
|
+
* @param {!Element} element
|
114
|
+
* @return {boolean}
|
115
|
+
*/
|
116
|
+
function isHtmlImport(element) {
|
117
|
+
return element.tagName === 'LINK' && element.rel && element.rel.toLowerCase().split(' ').indexOf('import') !== -1;
|
118
|
+
}
|
119
|
+
|
120
|
+
/**
|
121
|
+
* @param {!Element} element
|
122
|
+
* @return {boolean}
|
123
|
+
*/
|
124
|
+
function isConnected(element) {
|
125
|
+
var n = element;
|
126
|
+
do {
|
127
|
+
if (n[_attachedProp] || n.nodeType === Node.DOCUMENT_NODE) return true;
|
128
|
+
n = n.parentNode || n.nodeType === Node.DOCUMENT_FRAGMENT_NODE && n.host;
|
129
|
+
} while (n);
|
130
|
+
return false;
|
131
|
+
}
|
132
|
+
|
133
|
+
/**
|
134
|
+
* A registry of custom element definitions.
|
135
|
+
*
|
136
|
+
* See https://html.spec.whatwg.org/multipage/scripting.html#customelementsregistry
|
137
|
+
*
|
138
|
+
* @property {boolean} enableFlush Set to true to enable the flush() method
|
139
|
+
* to work. This should only be done for tests, as it causes a memory leak.
|
140
|
+
*/
|
141
|
+
|
142
|
+
var CustomElementRegistry = function () {
|
143
|
+
function CustomElementRegistry() {
|
144
|
+
_classCallCheck(this, CustomElementRegistry);
|
145
|
+
|
146
|
+
/** @private {!Map<string, !CustomElementDefinition>} **/
|
147
|
+
this._definitions = new Map();
|
148
|
+
|
149
|
+
/** @private {!Map<Function, string>} **/
|
150
|
+
this._constructors = new Map();
|
151
|
+
|
152
|
+
/** @private {!Map<string, !Deferred>} **/
|
153
|
+
this._whenDefinedMap = new Map();
|
154
|
+
|
155
|
+
/** @private {!Set<!MutationObserver>} **/
|
156
|
+
this._observers = new Set();
|
157
|
+
|
158
|
+
/** @private {!MutationObserver} **/
|
159
|
+
this._attributeObserver = new MutationObserver(
|
160
|
+
/** @type {function(Array<MutationRecord>, MutationObserver)} */
|
161
|
+
this._handleAttributeChange.bind(this));
|
162
|
+
|
163
|
+
/** @private {?HTMLElement} **/
|
164
|
+
this._newInstance = null;
|
165
|
+
|
166
|
+
/** @private {!Set<string>} **/
|
167
|
+
this._pendingHtmlImportUrls = new Set();
|
168
|
+
|
169
|
+
/** @type {boolean} **/
|
170
|
+
this.enableFlush = true;
|
171
|
+
|
172
|
+
/** @private {boolean} **/
|
173
|
+
this._upgradeScheduled = false;
|
174
|
+
|
175
|
+
/** @type {MutationObserver} **/
|
176
|
+
this._mainDocumentObserver = null;
|
177
|
+
}
|
178
|
+
|
179
|
+
// HTML spec part 4.13.4
|
180
|
+
// https://html.spec.whatwg.org/multipage/scripting.html#dom-customelementsregistry-define
|
181
|
+
/**
|
182
|
+
* @param {string} name
|
183
|
+
* @param {function(new:HTMLElement)} constructor
|
184
|
+
* @param {{extends: string}} options
|
185
|
+
* @return {undefined}
|
186
|
+
*/
|
187
|
+
|
188
|
+
|
189
|
+
_createClass(CustomElementRegistry, [{
|
190
|
+
key: 'define',
|
191
|
+
value: function define(name, constructor, options) {
|
192
|
+
// 1:
|
193
|
+
if (typeof constructor !== 'function') {
|
194
|
+
throw new TypeError('constructor must be a Constructor');
|
195
|
+
}
|
196
|
+
|
197
|
+
// 2. If constructor is an interface object whose corresponding interface
|
198
|
+
// either is HTMLElement or has HTMLElement in its set of inherited
|
199
|
+
// interfaces, throw a TypeError and abort these steps.
|
200
|
+
//
|
201
|
+
// It doesn't appear possible to check this condition from script
|
202
|
+
|
203
|
+
// 3:
|
204
|
+
var nameError = checkValidCustomElementName(name);
|
205
|
+
if (nameError) throw nameError;
|
206
|
+
|
207
|
+
// 4, 5:
|
208
|
+
// Note: we don't track being-defined names and constructors because
|
209
|
+
// define() isn't normally reentrant. The only time user code can run
|
210
|
+
// during define() is when getting callbacks off the prototype, which
|
211
|
+
// would be highly-unusual. We can make define() reentrant-safe if needed.
|
212
|
+
if (this._definitions.has(name)) {
|
213
|
+
throw new Error('An element with name \'' + name + '\' is already defined');
|
214
|
+
}
|
215
|
+
|
216
|
+
// 6, 7:
|
217
|
+
if (this._constructors.has(constructor)) {
|
218
|
+
throw new Error('Definition failed for \'' + name + '\': ' + 'The constructor is already used.');
|
219
|
+
}
|
220
|
+
|
221
|
+
// 8:
|
222
|
+
/** @type {string} */
|
223
|
+
var localName = name;
|
224
|
+
|
225
|
+
// 9, 10: We do not support extends currently.
|
226
|
+
|
227
|
+
// 11, 12, 13: Our define() isn't rentrant-safe
|
228
|
+
|
229
|
+
// 14.1:
|
230
|
+
/** @type {Object} */
|
231
|
+
var prototype = constructor.prototype;
|
232
|
+
|
233
|
+
// 14.2:
|
234
|
+
if ((typeof prototype === 'undefined' ? 'undefined' : _typeof(prototype)) !== 'object') {
|
235
|
+
throw new TypeError('Definition failed for \'' + name + '\': ' + 'constructor.prototype must be an object');
|
236
|
+
}
|
237
|
+
|
238
|
+
/**
|
239
|
+
* @param {string} callbackName
|
240
|
+
* @return {Function|undefined}
|
241
|
+
*/
|
242
|
+
function getCallback(callbackName) {
|
243
|
+
var callback = prototype[callbackName];
|
244
|
+
if (callback !== undefined && typeof callback !== 'function') {
|
245
|
+
throw new Error(localName + ' \'' + callbackName + '\' is not a Function');
|
246
|
+
}
|
247
|
+
return callback;
|
248
|
+
}
|
249
|
+
|
250
|
+
// 3, 4:
|
251
|
+
var connectedCallback = getCallback('connectedCallback');
|
252
|
+
|
253
|
+
// 5, 6:
|
254
|
+
var disconnectedCallback = getCallback('disconnectedCallback');
|
255
|
+
|
256
|
+
// Divergence from spec: we always throw if attributeChangedCallback is
|
257
|
+
// not a function.
|
258
|
+
|
259
|
+
// 7, 9.1:
|
260
|
+
var attributeChangedCallback = getCallback('attributeChangedCallback');
|
261
|
+
|
262
|
+
// 8, 9.2, 9.3:
|
263
|
+
var observedAttributes = attributeChangedCallback && constructor['observedAttributes'] || [];
|
264
|
+
|
265
|
+
// 15:
|
266
|
+
/** @type {CustomElementDefinition} */
|
267
|
+
var definition = {
|
268
|
+
name: name,
|
269
|
+
localName: localName,
|
270
|
+
constructor: constructor,
|
271
|
+
connectedCallback: connectedCallback,
|
272
|
+
disconnectedCallback: disconnectedCallback,
|
273
|
+
attributeChangedCallback: attributeChangedCallback,
|
274
|
+
observedAttributes: observedAttributes
|
275
|
+
};
|
276
|
+
|
277
|
+
// 16:
|
278
|
+
this._definitions.set(localName, definition);
|
279
|
+
this._constructors.set(constructor, localName);
|
280
|
+
|
281
|
+
// 17, 18, 19:
|
282
|
+
this._upgradeDoc();
|
283
|
+
|
284
|
+
// 20:
|
285
|
+
/** @type {Deferred} **/
|
286
|
+
var deferred = this._whenDefinedMap.get(localName);
|
287
|
+
if (deferred) {
|
288
|
+
deferred.resolve(undefined);
|
289
|
+
this._whenDefinedMap.delete(localName);
|
290
|
+
}
|
291
|
+
}
|
292
|
+
|
293
|
+
/**
|
294
|
+
* Returns the constructor defined for `name`, or `null`.
|
295
|
+
*
|
296
|
+
* @param {string} name
|
297
|
+
* @return {Function|undefined}
|
298
|
+
*/
|
299
|
+
|
300
|
+
}, {
|
301
|
+
key: 'get',
|
302
|
+
value: function get(name) {
|
303
|
+
// https://html.spec.whatwg.org/multipage/scripting.html#custom-elements-api
|
304
|
+
var def = this._definitions.get(name);
|
305
|
+
return def ? def.constructor : undefined;
|
306
|
+
}
|
307
|
+
|
308
|
+
/**
|
309
|
+
* Returns a `Promise` that resolves when a custom element for `name` has
|
310
|
+
* been defined.
|
311
|
+
*
|
312
|
+
* @param {string} name
|
313
|
+
* @return {!Promise}
|
314
|
+
*/
|
315
|
+
|
316
|
+
}, {
|
317
|
+
key: 'whenDefined',
|
318
|
+
value: function whenDefined(name) {
|
319
|
+
// https://html.spec.whatwg.org/multipage/scripting.html#dom-customelementsregistry-whendefined
|
320
|
+
var nameError = checkValidCustomElementName(name);
|
321
|
+
if (nameError) return Promise.reject(nameError);
|
322
|
+
if (this._definitions.has(name)) return Promise.resolve();
|
323
|
+
|
324
|
+
/** @type {Deferred} **/
|
325
|
+
var deferred = this._whenDefinedMap.get(name);
|
326
|
+
if (deferred) return deferred.promise;
|
327
|
+
|
328
|
+
var resolve = void 0;
|
329
|
+
var promise = new Promise(function (_resolve, _) {
|
330
|
+
resolve = _resolve;
|
331
|
+
});
|
332
|
+
deferred = { promise: promise, resolve: resolve };
|
333
|
+
this._whenDefinedMap.set(name, deferred);
|
334
|
+
return promise;
|
335
|
+
}
|
336
|
+
|
337
|
+
/**
|
338
|
+
* Causes all pending mutation records to be processed, and thus all
|
339
|
+
* customization, upgrades and custom element reactions to be called.
|
340
|
+
* `enableFlush` must be true for this to work. Only use during tests!
|
341
|
+
*/
|
342
|
+
|
343
|
+
}, {
|
344
|
+
key: 'flush',
|
345
|
+
value: function flush() {
|
346
|
+
if (this.enableFlush) {
|
347
|
+
// console.warn("flush!!!");
|
348
|
+
this._handleMutations(this._mainDocumentObserver.takeRecords());
|
349
|
+
this._handleAttributeChange(this._attributeObserver.takeRecords());
|
350
|
+
this._observers.forEach(
|
351
|
+
/**
|
352
|
+
* @param {!MutationObserver} observer
|
353
|
+
* @this {CustomElementRegistry}
|
354
|
+
*/
|
355
|
+
function (observer) {
|
356
|
+
this._handleMutations(observer.takeRecords());
|
357
|
+
}, this);
|
358
|
+
}
|
359
|
+
}
|
360
|
+
|
361
|
+
/**
|
362
|
+
* Upgrade all existing in document elements. This process is expensive so
|
363
|
+
* is optionally batched based on the state of HTMLImports. (Note,
|
364
|
+
* this batching might not be necessary if instead of walking the dom,
|
365
|
+
* a map of upgrade candidates was maintained.)
|
366
|
+
* @private
|
367
|
+
*/
|
368
|
+
|
369
|
+
}, {
|
370
|
+
key: '_upgradeDoc',
|
371
|
+
value: function _upgradeDoc() {
|
372
|
+
var _this2 = this;
|
373
|
+
|
374
|
+
if (!this._upgradeScheduled) {
|
375
|
+
this._upgradeScheduled = true;
|
376
|
+
var onReady = function onReady() {
|
377
|
+
_this2._upgradeScheduled = false;
|
378
|
+
if (!_this2._mainDocumentObserver) {
|
379
|
+
_this2._mainDocumentObserver = _this2._observeRoot(doc);
|
380
|
+
}
|
381
|
+
_this2._addNodes(doc.childNodes);
|
382
|
+
};
|
383
|
+
if (window['HTMLImports']) {
|
384
|
+
window['HTMLImports']['whenReady'](onReady);
|
385
|
+
} else {
|
386
|
+
onReady();
|
387
|
+
}
|
388
|
+
}
|
389
|
+
}
|
390
|
+
|
391
|
+
/**
|
392
|
+
* @param {?HTMLElement} instance
|
393
|
+
* @private
|
394
|
+
*/
|
395
|
+
|
396
|
+
}, {
|
397
|
+
key: '_setNewInstance',
|
398
|
+
value: function _setNewInstance(instance) {
|
399
|
+
this._newInstance = instance;
|
400
|
+
}
|
401
|
+
|
402
|
+
/**
|
403
|
+
* Observes a DOM root for mutations that trigger upgrades and reactions.
|
404
|
+
* @param {Node} root
|
405
|
+
* @private
|
406
|
+
*/
|
407
|
+
|
408
|
+
}, {
|
409
|
+
key: '_observeRoot',
|
410
|
+
value: function _observeRoot(root) {
|
411
|
+
// console.log('_observeRoot', root, root.baseURI);
|
412
|
+
// console.assert(!root[_observerProp]);
|
413
|
+
if (root[_observerProp] != null) {
|
414
|
+
//console.warn(`Root ${root} is already observed`);
|
415
|
+
return root[_observerProp];
|
416
|
+
}
|
417
|
+
root[_observerProp] = new MutationObserver(
|
418
|
+
/** @type {function(Array<MutationRecord>, MutationObserver)} */
|
419
|
+
this._handleMutations.bind(this));
|
420
|
+
root[_observerProp].observe(root, { childList: true, subtree: true });
|
421
|
+
if (this.enableFlush) {
|
422
|
+
// this is memory leak, only use in tests
|
423
|
+
this._observers.add(root[_observerProp]);
|
424
|
+
}
|
425
|
+
return root[_observerProp];
|
426
|
+
}
|
427
|
+
|
428
|
+
/**
|
429
|
+
* @param {Node} root
|
430
|
+
* @private
|
431
|
+
*/
|
432
|
+
|
433
|
+
}, {
|
434
|
+
key: '_unobserveRoot',
|
435
|
+
value: function _unobserveRoot(root) {
|
436
|
+
if (root[_observerProp] != null) {
|
437
|
+
root[_observerProp].disconnect();
|
438
|
+
if (this.enableFlush) {
|
439
|
+
this._observers.delete(root[_observerProp]);
|
440
|
+
}
|
441
|
+
root[_observerProp] = null;
|
442
|
+
}
|
443
|
+
}
|
444
|
+
|
445
|
+
/**
|
446
|
+
* @param {!Array<!MutationRecord>} mutations
|
447
|
+
* @private
|
448
|
+
*/
|
449
|
+
|
450
|
+
}, {
|
451
|
+
key: '_handleMutations',
|
452
|
+
value: function _handleMutations(mutations) {
|
453
|
+
for (var i = 0; i < mutations.length; i++) {
|
454
|
+
/** @type {!MutationRecord} */
|
455
|
+
var mutation = mutations[i];
|
456
|
+
if (mutation.type === 'childList') {
|
457
|
+
// Note: we can't get an ordering between additions and removals, and
|
458
|
+
// so might diverge from spec reaction ordering
|
459
|
+
var addedNodes = /** @type {!NodeList<!Node>} */mutation.addedNodes;
|
460
|
+
var removedNodes = /** @type {!NodeList<!Node>} */mutation.removedNodes;
|
461
|
+
this._removeNodes(removedNodes);
|
462
|
+
this._addNodes(addedNodes);
|
463
|
+
}
|
464
|
+
}
|
465
|
+
}
|
466
|
+
|
467
|
+
/**
|
468
|
+
* @param {!(NodeList<!Node>|Array<!Node>)} nodeList
|
469
|
+
* @param {?Set<Node>=} visitedNodes
|
470
|
+
* @private
|
471
|
+
*/
|
472
|
+
|
473
|
+
}, {
|
474
|
+
key: '_addNodes',
|
475
|
+
value: function _addNodes(nodeList, visitedNodes) {
|
476
|
+
visitedNodes = visitedNodes || new Set();
|
477
|
+
|
478
|
+
for (var i = 0; i < nodeList.length; i++) {
|
479
|
+
var root = nodeList[i];
|
480
|
+
|
481
|
+
if (!isElement(root)) {
|
482
|
+
continue;
|
483
|
+
}
|
484
|
+
|
485
|
+
// Since we're adding this node to an observed tree, we can unobserve
|
486
|
+
this._unobserveRoot(root);
|
487
|
+
|
488
|
+
var walker = createTreeWalker(root);
|
489
|
+
do {
|
490
|
+
var node = /** @type {!HTMLElement} */walker.currentNode;
|
491
|
+
this._addElement(node, visitedNodes);
|
492
|
+
} while (walker.nextNode());
|
493
|
+
}
|
494
|
+
}
|
495
|
+
|
496
|
+
/**
|
497
|
+
* @param {!HTMLElement} element
|
498
|
+
* @param {!Set<Node>=} visitedNodes
|
499
|
+
*/
|
500
|
+
|
501
|
+
}, {
|
502
|
+
key: '_addElement',
|
503
|
+
value: function _addElement(element, visitedNodes) {
|
504
|
+
if (visitedNodes.has(element)) return;
|
505
|
+
visitedNodes.add(element);
|
506
|
+
|
507
|
+
/** @type {?CustomElementDefinition} */
|
508
|
+
var definition = this._definitions.get(element.localName);
|
509
|
+
if (definition) {
|
510
|
+
if (!element[_upgradedProp]) {
|
511
|
+
this._upgradeElement(element, definition, true);
|
512
|
+
}
|
513
|
+
if (element[_upgradedProp] && !element[_attachedProp] && isConnected(element)) {
|
514
|
+
element[_attachedProp] = true;
|
515
|
+
if (definition.connectedCallback) {
|
516
|
+
definition.connectedCallback.call(element);
|
517
|
+
}
|
518
|
+
}
|
519
|
+
}
|
520
|
+
if (element.shadowRoot) {
|
521
|
+
// TODO(justinfagnani): do we need to check that the shadowRoot
|
522
|
+
// is observed?
|
523
|
+
this._addNodes(element.shadowRoot.childNodes, visitedNodes);
|
524
|
+
}
|
525
|
+
if (isHtmlImport(element)) {
|
526
|
+
this._addImport( /** @type {!HTMLLinkElement} */element, visitedNodes);
|
527
|
+
}
|
528
|
+
}
|
529
|
+
|
530
|
+
/**
|
531
|
+
* @param {!HTMLLinkElement} link
|
532
|
+
* @param {!Set<Node>=} visitedNodes
|
533
|
+
*/
|
534
|
+
|
535
|
+
}, {
|
536
|
+
key: '_addImport',
|
537
|
+
value: function _addImport(link, visitedNodes) {
|
538
|
+
var _this3 = this;
|
539
|
+
|
540
|
+
// During a tree walk to add or upgrade nodes, we may encounter multiple
|
541
|
+
// HTML imports that reference the same document, and may encounter
|
542
|
+
// imports in various states of loading.
|
543
|
+
|
544
|
+
// First, we only want to process the first import for a document in a
|
545
|
+
// walk, so we check visitedNodes for the document, not the link.
|
546
|
+
//
|
547
|
+
// Second, for documents that haven't loaded yet, we only want to add one
|
548
|
+
// listener, regardless of the number of links or walks, so we track
|
549
|
+
// pending loads in _pendingHtmlImportUrls.
|
550
|
+
|
551
|
+
// Check to see if the import is loaded
|
552
|
+
/** @type {?Document} */
|
553
|
+
var _import = link.import;
|
554
|
+
if (_import) {
|
555
|
+
// The import is loaded, but only process the first link element
|
556
|
+
if (visitedNodes.has(_import)) return;
|
557
|
+
visitedNodes.add(_import);
|
558
|
+
|
559
|
+
// The import is loaded observe it
|
560
|
+
if (!_import[_observerProp]) this._observeRoot(_import);
|
561
|
+
|
562
|
+
// walk the document
|
563
|
+
this._addNodes(_import.childNodes, visitedNodes);
|
564
|
+
} else {
|
565
|
+
var _ret = function () {
|
566
|
+
// The import is not loaded, so wait for it
|
567
|
+
/** @type {string} */
|
568
|
+
var importUrl = link.href;
|
569
|
+
if (_this3._pendingHtmlImportUrls.has(importUrl)) return {
|
570
|
+
v: void 0
|
571
|
+
};
|
572
|
+
_this3._pendingHtmlImportUrls.add(importUrl);
|
573
|
+
|
574
|
+
/**
|
575
|
+
* @const
|
576
|
+
* @type {CustomElementRegistry}
|
577
|
+
*/
|
578
|
+
var _this = _this3;
|
579
|
+
var onLoad = function onLoad() {
|
580
|
+
link.removeEventListener('load', /** @type {function(Event)} */onLoad);
|
581
|
+
if (!link.import[_observerProp]) _this._observeRoot(link.import);
|
582
|
+
// We don't pass visitedNodes because this is async and not part of
|
583
|
+
// the current tree walk.
|
584
|
+
_this._addNodes(link.import.childNodes);
|
585
|
+
};
|
586
|
+
link.addEventListener('load', onLoad);
|
587
|
+
}();
|
588
|
+
|
589
|
+
if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v;
|
590
|
+
}
|
591
|
+
}
|
592
|
+
|
593
|
+
/**
|
594
|
+
* @param {NodeList} nodeList
|
595
|
+
* @private
|
596
|
+
*/
|
597
|
+
|
598
|
+
}, {
|
599
|
+
key: '_removeNodes',
|
600
|
+
value: function _removeNodes(nodeList) {
|
601
|
+
for (var i = 0; i < nodeList.length; i++) {
|
602
|
+
var root = nodeList[i];
|
603
|
+
|
604
|
+
if (!isElement(root)) {
|
605
|
+
continue;
|
606
|
+
}
|
607
|
+
|
608
|
+
// Since we're detatching this element from an observed root, we need to
|
609
|
+
// reobserve it.
|
610
|
+
// TODO(justinfagnani): can we do this in a microtask so we don't thrash
|
611
|
+
// on creating and destroying MutationObservers on batch DOM mutations?
|
612
|
+
this._observeRoot(root);
|
613
|
+
|
614
|
+
var walker = createTreeWalker(root);
|
615
|
+
do {
|
616
|
+
var node = walker.currentNode;
|
617
|
+
if (node[_upgradedProp] && node[_attachedProp]) {
|
618
|
+
node[_attachedProp] = false;
|
619
|
+
var definition = this._definitions.get(node.localName);
|
620
|
+
if (definition && definition.disconnectedCallback) {
|
621
|
+
definition.disconnectedCallback.call(node);
|
622
|
+
}
|
623
|
+
}
|
624
|
+
} while (walker.nextNode());
|
625
|
+
}
|
626
|
+
}
|
627
|
+
|
628
|
+
/**
|
629
|
+
* Upgrades or customizes a custom element.
|
630
|
+
*
|
631
|
+
* @param {HTMLElement} element
|
632
|
+
* @param {CustomElementDefinition} definition
|
633
|
+
* @param {boolean} callConstructor
|
634
|
+
* @private
|
635
|
+
*/
|
636
|
+
|
637
|
+
}, {
|
638
|
+
key: '_upgradeElement',
|
639
|
+
value: function _upgradeElement(element, definition, callConstructor) {
|
640
|
+
var prototype = definition.constructor.prototype;
|
641
|
+
element.__proto__ = prototype;
|
642
|
+
if (callConstructor) {
|
643
|
+
this._setNewInstance(element);
|
644
|
+
new definition.constructor();
|
645
|
+
element[_upgradedProp] = true;
|
646
|
+
console.assert(this._newInstance == null);
|
647
|
+
}
|
648
|
+
|
649
|
+
var observedAttributes = definition.observedAttributes;
|
650
|
+
var attributeChangedCallback = definition.attributeChangedCallback;
|
651
|
+
if (attributeChangedCallback && observedAttributes.length > 0) {
|
652
|
+
this._attributeObserver.observe(element, {
|
653
|
+
attributes: true,
|
654
|
+
attributeOldValue: true,
|
655
|
+
attributeFilter: observedAttributes
|
656
|
+
});
|
657
|
+
|
658
|
+
// Trigger attributeChangedCallback for existing attributes.
|
659
|
+
// https://html.spec.whatwg.org/multipage/scripting.html#upgrades
|
660
|
+
for (var i = 0; i < observedAttributes.length; i++) {
|
661
|
+
var name = observedAttributes[i];
|
662
|
+
if (element.hasAttribute(name)) {
|
663
|
+
var value = element.getAttribute(name);
|
664
|
+
attributeChangedCallback.call(element, name, null, value, null);
|
665
|
+
}
|
666
|
+
}
|
667
|
+
}
|
668
|
+
}
|
669
|
+
|
670
|
+
/**
|
671
|
+
* @param {!Array<!MutationRecord>} mutations
|
672
|
+
* @private
|
673
|
+
*/
|
674
|
+
|
675
|
+
}, {
|
676
|
+
key: '_handleAttributeChange',
|
677
|
+
value: function _handleAttributeChange(mutations) {
|
678
|
+
for (var i = 0; i < mutations.length; i++) {
|
679
|
+
var mutation = mutations[i];
|
680
|
+
if (mutation.type === 'attributes') {
|
681
|
+
var target = /** @type {HTMLElement} */mutation.target;
|
682
|
+
// We should be gaurenteed to have a definition because this mutation
|
683
|
+
// observer is only observing custom elements observedAttributes
|
684
|
+
var definition = this._definitions.get(target.localName);
|
685
|
+
var name = /** @type {!string} */mutation.attributeName;
|
686
|
+
var oldValue = mutation.oldValue;
|
687
|
+
var newValue = target.getAttribute(name);
|
688
|
+
// Skip changes that were handled synchronously by setAttribute
|
689
|
+
if (newValue !== oldValue) {
|
690
|
+
var namespace = mutation.attributeNamespace;
|
691
|
+
definition.attributeChangedCallback.call(target, name, oldValue, newValue, namespace);
|
692
|
+
}
|
693
|
+
}
|
694
|
+
}
|
695
|
+
}
|
696
|
+
}]);
|
697
|
+
|
698
|
+
return CustomElementRegistry;
|
699
|
+
}();
|
700
|
+
|
701
|
+
// Closure Compiler Exports
|
702
|
+
|
703
|
+
|
704
|
+
window['CustomElementRegistry'] = CustomElementRegistry;
|
705
|
+
CustomElementRegistry.prototype['define'] = CustomElementRegistry.prototype.define;
|
706
|
+
CustomElementRegistry.prototype['get'] = CustomElementRegistry.prototype.get;
|
707
|
+
CustomElementRegistry.prototype['whenDefined'] = CustomElementRegistry.prototype.whenDefined;
|
708
|
+
CustomElementRegistry.prototype['flush'] = CustomElementRegistry.prototype.flush;
|
709
|
+
CustomElementRegistry.prototype['polyfilled'] = true;
|
710
|
+
// TODO(justinfagnani): remove these in production code
|
711
|
+
CustomElementRegistry.prototype['_observeRoot'] = CustomElementRegistry.prototype._observeRoot;
|
712
|
+
CustomElementRegistry.prototype['_addImport'] = CustomElementRegistry.prototype._addImport;
|
713
|
+
|
714
|
+
// patch window.HTMLElement
|
715
|
+
|
716
|
+
/** @const */
|
717
|
+
var origHTMLElement = win.HTMLElement;
|
718
|
+
/**
|
719
|
+
* @type {function(new: HTMLElement)}
|
720
|
+
*/
|
721
|
+
var newHTMLElement = function HTMLElement() {
|
722
|
+
var customElements = _customElements();
|
723
|
+
|
724
|
+
// If there's an being upgraded, return that
|
725
|
+
if (customElements._newInstance) {
|
726
|
+
var i = customElements._newInstance;
|
727
|
+
customElements._newInstance = null;
|
728
|
+
return i;
|
729
|
+
}
|
730
|
+
if (this.constructor) {
|
731
|
+
// Find the tagname of the constructor and create a new element with it
|
732
|
+
var tagName = customElements._constructors.get(this.constructor);
|
733
|
+
return _createElement(doc, tagName, undefined, false);
|
734
|
+
}
|
735
|
+
throw new Error('Unknown constructor. Did you call customElements.define()?');
|
736
|
+
};
|
737
|
+
win.HTMLElement = newHTMLElement;
|
738
|
+
win.HTMLElement.prototype = Object.create(origHTMLElement.prototype, {
|
739
|
+
constructor: { value: win.HTMLElement, configurable: true, writable: true }
|
740
|
+
});
|
741
|
+
|
742
|
+
// patch doc.createElement
|
743
|
+
// TODO(justinfagnani): why is the cast neccessary?
|
744
|
+
// Can we fix the Closure DOM externs?
|
745
|
+
var _origCreateElement =
|
746
|
+
/** @type {function(this:Document, string, (Object|undefined)=): !HTMLElement}}*/
|
747
|
+
doc.createElement;
|
748
|
+
|
749
|
+
/**
|
750
|
+
* Creates a new element and upgrades it if it's a custom element.
|
751
|
+
* @param {!Document} doc
|
752
|
+
* @param {!string} tagName
|
753
|
+
* @param {Object|undefined} options
|
754
|
+
* @param {boolean} callConstructor whether or not to call the elements
|
755
|
+
* constructor after upgrading. If an element is created by calling its
|
756
|
+
* constructor, then `callConstructor` should be false to prevent double
|
757
|
+
* initialization.
|
758
|
+
*/
|
759
|
+
function _createElement(doc, tagName, options, callConstructor) {
|
760
|
+
var customElements = _customElements();
|
761
|
+
var element = options ? _origCreateElement.call(doc, tagName, options) : _origCreateElement.call(doc, tagName);
|
762
|
+
var definition = customElements._definitions.get(tagName.toLowerCase());
|
763
|
+
if (definition) {
|
764
|
+
customElements._upgradeElement(element, definition, callConstructor);
|
765
|
+
}
|
766
|
+
if (tagName.toLowerCase() !== 'html') {
|
767
|
+
customElements._observeRoot(element);
|
768
|
+
}
|
769
|
+
return element;
|
770
|
+
};
|
771
|
+
doc.createElement = function (tagName, options) {
|
772
|
+
return _createElement(doc, tagName, options, true);
|
773
|
+
};
|
774
|
+
|
775
|
+
// patch doc.createElementNS
|
776
|
+
|
777
|
+
var HTMLNS = 'http://www.w3.org/1999/xhtml';
|
778
|
+
|
779
|
+
/** @type {function(this:Document,string,string):Element} */
|
780
|
+
var _origCreateElementNS = doc.createElementNS;
|
781
|
+
doc.createElementNS =
|
782
|
+
/** @type {function(this:Document,(string|null),string):!Element} */
|
783
|
+
function (namespaceURI, qualifiedName) {
|
784
|
+
if (namespaceURI === 'http://www.w3.org/1999/xhtml') {
|
785
|
+
return doc.createElement(qualifiedName);
|
786
|
+
} else {
|
787
|
+
return _origCreateElementNS.call(doc, namespaceURI, qualifiedName);
|
788
|
+
}
|
789
|
+
};
|
790
|
+
|
791
|
+
// patch Element.attachShadow
|
792
|
+
|
793
|
+
/** @type {function({closed: boolean})} */
|
794
|
+
var _origAttachShadow = Element.prototype['attachShadow'];
|
795
|
+
if (_origAttachShadow) {
|
796
|
+
Object.defineProperty(Element.prototype, 'attachShadow', {
|
797
|
+
value: function value(options) {
|
798
|
+
/** @type {!Node} */
|
799
|
+
var root = _origAttachShadow.call(this, options);
|
800
|
+
/** @type {CustomElementRegistry} */
|
801
|
+
var customElements = _customElements();
|
802
|
+
customElements._observeRoot(root);
|
803
|
+
return root;
|
804
|
+
}
|
805
|
+
});
|
806
|
+
}
|
807
|
+
|
808
|
+
// patch doc.importNode
|
809
|
+
|
810
|
+
var rawImportNode = doc.importNode;
|
811
|
+
doc.importNode = function (node, deep) {
|
812
|
+
var clone = /** @type{!Node} */rawImportNode.call(doc, node, deep);
|
813
|
+
var customElements = _customElements();
|
814
|
+
var nodes = isElement(clone) ? [clone] : clone.childNodes;
|
815
|
+
/** @type {CustomElementRegistry} */_customElements()._addNodes(nodes);
|
816
|
+
return clone;
|
817
|
+
};
|
818
|
+
|
819
|
+
// patch Element.setAttribute & removeAttribute
|
820
|
+
|
821
|
+
var _origSetAttribute = Element.prototype.setAttribute;
|
822
|
+
Element.prototype['setAttribute'] = function (name, value) {
|
823
|
+
changeAttribute(this, name, value, _origSetAttribute);
|
824
|
+
};
|
825
|
+
var _origRemoveAttribute = Element.prototype.removeAttribute;
|
826
|
+
Element.prototype['removeAttribute'] = function (name) {
|
827
|
+
changeAttribute(this, name, null, _origRemoveAttribute);
|
828
|
+
};
|
829
|
+
|
830
|
+
function changeAttribute(element, name, value, operation) {
|
831
|
+
name = name.toLowerCase();
|
832
|
+
var oldValue = element.getAttribute(name);
|
833
|
+
operation.call(element, name, value);
|
834
|
+
|
835
|
+
// Bail if this wasn't a fully upgraded custom element
|
836
|
+
if (element[_upgradedProp] == true) {
|
837
|
+
var definition = _customElements()._definitions.get(element.localName);
|
838
|
+
var observedAttributes = definition.observedAttributes;
|
839
|
+
var attributeChangedCallback = definition.attributeChangedCallback;
|
840
|
+
if (attributeChangedCallback && observedAttributes.indexOf(name) >= 0) {
|
841
|
+
var newValue = element.getAttribute(name);
|
842
|
+
if (newValue !== oldValue) {
|
843
|
+
attributeChangedCallback.call(element, name, oldValue, newValue, null);
|
844
|
+
}
|
845
|
+
}
|
846
|
+
}
|
847
|
+
}
|
848
|
+
|
849
|
+
Object.defineProperty(window, 'customElements', {
|
850
|
+
value: new CustomElementRegistry(),
|
851
|
+
configurable: true,
|
852
|
+
enumerable: true
|
853
|
+
});
|
854
|
+
|
855
|
+
// TODO(justinfagnani): Remove. Temporary for backward-compatibility
|
856
|
+
window['CustomElements'] = {
|
857
|
+
takeRecords: function takeRecords() {
|
858
|
+
if (_customElements().flush) _customElements().flush();
|
859
|
+
}
|
860
|
+
};
|
861
|
+
})();
|
@@ -0,0 +1,90 @@
|
|
1
|
+
|
2
|
+
window.nativeShim = ->
|
3
|
+
eval '''
|
4
|
+
'use strict';
|
5
|
+
|
6
|
+
const NativeHTMLElement = window.HTMLElement;
|
7
|
+
const nativeDefine = window.customElements.define;
|
8
|
+
const nativeGet = window.customElements.get;
|
9
|
+
|
10
|
+
/**
|
11
|
+
* Map of user-provided constructors to tag names.
|
12
|
+
*
|
13
|
+
* @type {Map<Function, string>}
|
14
|
+
*/
|
15
|
+
const tagnameByConstructor = new Map();
|
16
|
+
|
17
|
+
/**
|
18
|
+
* Map of tag anmes to user-provided constructors.
|
19
|
+
*
|
20
|
+
* @type {Map<string, Function>}
|
21
|
+
*/
|
22
|
+
const constructorByTagname = new Map();
|
23
|
+
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Whether the constructors are being called by a browser process, ie parsing
|
27
|
+
* or createElement.
|
28
|
+
*/
|
29
|
+
let browserConstruction = false;
|
30
|
+
|
31
|
+
/**
|
32
|
+
* Whether the constructors are being called by a user-space process, ie
|
33
|
+
* calling an element constructor.
|
34
|
+
*/
|
35
|
+
let userConstruction = false;
|
36
|
+
|
37
|
+
window.HTMLElement = function() {
|
38
|
+
if (!browserConstruction) {
|
39
|
+
const tagname = tagnameByConstructor.get(this.constructor);
|
40
|
+
const fakeClass = nativeGet.call(window.customElements, tagname);
|
41
|
+
|
42
|
+
// Make sure that the fake constructor doesn't call back to this constructor
|
43
|
+
userConstruction = true;
|
44
|
+
const instance = new (fakeClass)();
|
45
|
+
return instance;
|
46
|
+
}
|
47
|
+
// Else do nothing. This will be reached by ES5-style classes doing
|
48
|
+
// HTMLElement.call() during initialization
|
49
|
+
browserConstruction = false;
|
50
|
+
};
|
51
|
+
|
52
|
+
window.HTMLElement.prototype = Object.create(NativeHTMLElement.prototype);
|
53
|
+
window.HTMLElement.prototype.constructor = window.HTMLElement;
|
54
|
+
|
55
|
+
window.customElements.define = (tagname, elementClass) => {
|
56
|
+
const elementProto = elementClass.prototype;
|
57
|
+
const StandInElement = class extends NativeHTMLElement {
|
58
|
+
constructor() {
|
59
|
+
// Call the native HTMLElement constructor, this gives us the
|
60
|
+
// under-construction instance as `this`:
|
61
|
+
super();
|
62
|
+
|
63
|
+
// The prototype will be wrong up because the browser used our fake
|
64
|
+
// class, so fix it:
|
65
|
+
Object.setPrototypeOf(this, elementProto);
|
66
|
+
|
67
|
+
if (!userConstruction) {
|
68
|
+
// Make sure that user-defined constructor bottom's out to a do-nothing
|
69
|
+
// HTMLElement() call
|
70
|
+
browserConstruction = true;
|
71
|
+
// Call the user-defined constructor on our instance:
|
72
|
+
elementClass.call(this);
|
73
|
+
}
|
74
|
+
userConstruction = false;
|
75
|
+
}
|
76
|
+
};
|
77
|
+
const standInProto = StandInElement.prototype;
|
78
|
+
StandInElement.observedAttributes = elementClass.observedAttributes;
|
79
|
+
standInProto.connectedCallback = elementProto.connectedCallback;
|
80
|
+
standInProto.disconnectedCallback = elementProto.disconnectedCallback;
|
81
|
+
standInProto.attributeChangedCallback = elementProto.attributeChangedCallback;
|
82
|
+
standInProto.adoptedCallback = elementProto.adoptedCallback;
|
83
|
+
|
84
|
+
tagnameByConstructor.set(elementClass, tagname);
|
85
|
+
constructorByTagname.set(tagname, elementClass);
|
86
|
+
nativeDefine.call(window.customElements, tagname, StandInElement);
|
87
|
+
};
|
88
|
+
|
89
|
+
window.customElements.get = (tagname) => constructorByTagname.get(tagname);
|
90
|
+
'''
|
@@ -1,4 +1,6 @@
|
|
1
1
|
#= require lodash
|
2
|
+
#= require custom-elements
|
3
|
+
#= require native-shim
|
2
4
|
|
3
5
|
components = {}
|
4
6
|
|
@@ -60,6 +62,18 @@ TaoComponentBasedOn = (superClass = 'HTMLElement') ->
|
|
60
62
|
attributeChangedCallback: (attrName, oldValue, newValue) ->
|
61
63
|
@["#{_.camelCase attrName}Changed"]?(newValue, oldValue)
|
62
64
|
|
65
|
+
on: (args...) ->
|
66
|
+
$(@).on args...
|
67
|
+
|
68
|
+
off: (args...) ->
|
69
|
+
$(@).off args...
|
70
|
+
|
71
|
+
trigger: (args...) ->
|
72
|
+
$(@).triggerHandler(args...)
|
73
|
+
|
74
|
+
one: (args...) ->
|
75
|
+
$(@).one args...
|
76
|
+
|
63
77
|
_init: ->
|
64
78
|
# to be implemented
|
65
79
|
|
@@ -1,3 +1,42 @@
|
|
1
|
-
|
1
|
+
class TaoModule
|
2
2
|
|
3
|
-
|
3
|
+
@extend: (obj) ->
|
4
|
+
unless obj and typeof obj == 'object'
|
5
|
+
throw new Error('TaoModule.extend: param should be an object')
|
6
|
+
|
7
|
+
for key, val of obj when key not in ['included', 'extended']
|
8
|
+
@[key] = val
|
9
|
+
|
10
|
+
obj.extended?.call(@)
|
11
|
+
@
|
12
|
+
|
13
|
+
@include: (obj) ->
|
14
|
+
unless obj and typeof obj == 'object'
|
15
|
+
throw new Error('TaoModule.include: param should be an object')
|
16
|
+
|
17
|
+
for key, val of obj when key not in ['included', 'extended']
|
18
|
+
@::[key] = val
|
19
|
+
|
20
|
+
obj.included?.call(@)
|
21
|
+
@
|
22
|
+
|
23
|
+
constructor: (opts) ->
|
24
|
+
@_setOptions opts
|
25
|
+
@_init()
|
26
|
+
|
27
|
+
_setOptions: (opts) ->
|
28
|
+
@opts = $.extend {}, TaoModule.opts, opts
|
29
|
+
|
30
|
+
_init: ->
|
31
|
+
|
32
|
+
on: (args...) ->
|
33
|
+
$(@).on args...
|
34
|
+
|
35
|
+
off: (args...) ->
|
36
|
+
$(@).off args...
|
37
|
+
|
38
|
+
trigger: (args...) ->
|
39
|
+
$(@).triggerHandler(args...)
|
40
|
+
|
41
|
+
one: (args...) ->
|
42
|
+
$(@).one args...
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tao_on_rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Siyuan Liu
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-12-
|
12
|
+
date: 2016-12-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -176,10 +176,13 @@ files:
|
|
176
176
|
- lib/tao_on_rails/rails/version.rb
|
177
177
|
- lib/tasks/tao_icons.rake
|
178
178
|
- tao_on_rails.gemspec
|
179
|
+
- vendor/assets/javascripts/custom-elements.js
|
180
|
+
- vendor/assets/javascripts/native-shim.coffee
|
179
181
|
- vendor/assets/javascripts/tao.coffee
|
180
182
|
- vendor/assets/javascripts/tao/application.coffee
|
181
183
|
- vendor/assets/javascripts/tao/component.coffee
|
182
184
|
- vendor/assets/javascripts/tao/helpers.coffee
|
185
|
+
- vendor/assets/javascripts/tao/icons.coffee
|
183
186
|
- vendor/assets/javascripts/tao/module.coffee
|
184
187
|
- vendor/assets/javascripts/tao/page.coffee
|
185
188
|
- vendor/assets/stylesheets/tao.scss
|