written 0.0.5 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/LICENSE +8 -0
  4. data/README.md +63 -0
  5. data/Rakefile +17 -27
  6. data/lib/written.rb +0 -8
  7. data/lib/written/app/assets/images/written/placeholder.png +0 -0
  8. data/lib/written/app/assets/javascripts/written.coffee +2 -0
  9. data/lib/written/app/assets/javascripts/written/core/content.coffee +53 -35
  10. data/lib/written/app/assets/javascripts/written/core/cursor.coffee +33 -12
  11. data/lib/written/app/assets/javascripts/written/core/document.coffee +16 -11
  12. data/lib/written/app/assets/javascripts/written/core/extensions/string.coffee +9 -0
  13. data/lib/written/app/assets/javascripts/written/core/extensions/text.coffee +2 -0
  14. data/lib/written/app/assets/javascripts/written/core/history.coffee +2 -0
  15. data/lib/written/app/assets/javascripts/written/core/observer.coffee +6 -2
  16. data/lib/written/app/assets/javascripts/written/core/stringify.coffee +15 -0
  17. data/lib/written/app/assets/javascripts/written/parsers/block.coffee +69 -0
  18. data/lib/written/app/assets/javascripts/written/parsers/block/code.coffee +79 -15
  19. data/lib/written/app/assets/javascripts/written/parsers/block/heading.coffee +60 -5
  20. data/lib/written/app/assets/javascripts/written/parsers/block/image.coffee +103 -9
  21. data/lib/written/app/assets/javascripts/written/parsers/block/olist.coffee +94 -12
  22. data/lib/written/app/assets/javascripts/written/parsers/block/paragraph.coffee +63 -5
  23. data/lib/written/app/assets/javascripts/written/parsers/block/quote.coffee +92 -0
  24. data/lib/written/app/assets/javascripts/written/parsers/block/ulist.coffee +93 -12
  25. data/lib/written/app/assets/javascripts/written/parsers/inline.coffee +81 -0
  26. data/lib/written/app/assets/javascripts/written/parsers/inline/code.coffee +57 -0
  27. data/lib/written/app/assets/javascripts/written/parsers/inline/italic.coffee +40 -7
  28. data/lib/written/app/assets/javascripts/written/parsers/inline/link.coffee +43 -13
  29. data/lib/written/app/assets/javascripts/written/parsers/inline/list.coffee +27 -0
  30. data/lib/written/app/assets/javascripts/written/parsers/inline/strong.coffee +41 -7
  31. data/lib/written/app/assets/javascripts/written/parsers/parsers.coffee +21 -107
  32. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/CustomElements.js +32 -0
  33. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/base.js +40 -0
  34. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/boot.js +124 -0
  35. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/observe.js +318 -0
  36. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/register.js +369 -0
  37. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/traverse.js +86 -0
  38. data/lib/written/app/assets/javascripts/written/polyfills/CustomElements/upgrade.js +130 -0
  39. data/lib/written/app/assets/javascripts/written/polyfills/MutationObserver/MutationObserver.js +575 -0
  40. data/lib/written/app/assets/javascripts/written/polyfills/WeakMap/WeakMap.js +49 -0
  41. data/lib/written/app/assets/javascripts/written/polyfills/base.coffee +10 -0
  42. data/lib/written/app/assets/javascripts/written/polyfills/dom.js +104 -0
  43. data/lib/written/app/assets/javascripts/written/uploaders/aws.coffee +125 -0
  44. data/lib/written/app/assets/stylesheets/written.scss +80 -11
  45. data/lib/written/version.rb +1 -1
  46. data/test/server/app/assets/javascripts/application.coffee +20 -2
  47. data/test/server/app/assets/stylesheets/application.scss +2 -2
  48. data/test/server/app/assets/stylesheets/prism.css +0 -1
  49. data/test/server/app/views/posts/show.html.erb +10 -3
  50. metadata +26 -20
  51. data/lib/written/app/assets/javascripts/written/core/ext.coffee +0 -109
  52. data/lib/written/app/assets/javascripts/written/core/extensions.coffee +0 -2
  53. data/lib/written/document.rb +0 -42
  54. data/lib/written/node.rb +0 -21
  55. data/lib/written/nodes/code.rb +0 -65
  56. data/lib/written/nodes/heading.rb +0 -15
  57. data/lib/written/nodes/image.rb +0 -14
  58. data/lib/written/nodes/ordered_list.rb +0 -18
  59. data/lib/written/nodes/unordered_list.rb +0 -19
  60. data/lib/written/parsers.rb +0 -11
  61. data/lib/written/parsers/base.rb +0 -26
  62. data/lib/written/parsers/code.rb +0 -60
  63. data/lib/written/parsers/heading.rb +0 -19
  64. data/lib/written/parsers/image.rb +0 -19
  65. data/lib/written/parsers/link.rb +0 -12
  66. data/lib/written/parsers/list.rb +0 -33
  67. data/lib/written/parsers/word.rb +0 -16
@@ -0,0 +1,318 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
4
+ * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
5
+ * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6
+ * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
7
+ * Code distributed by Google as part of the polymer project is also
8
+ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
9
+ */
10
+
11
+ /**
12
+ * Implements custom element observation and attached/detached callbacks
13
+ * @module observe
14
+ */
15
+
16
+ window.CustomElements.addModule(function(scope){
17
+
18
+ // imports
19
+ var flags = scope.flags;
20
+ var forSubtree = scope.forSubtree;
21
+ var forDocumentTree = scope.forDocumentTree;
22
+
23
+ /*
24
+ Manage nodes attached to document trees
25
+ */
26
+
27
+ // manage lifecycle on added node and it's subtree; upgrade the node and
28
+ // entire subtree if necessary and process attached for the node and entire
29
+ // subtree
30
+ function addedNode(node, isAttached) {
31
+ return added(node, isAttached) || addedSubtree(node, isAttached);
32
+ }
33
+
34
+ // manage lifecycle on added node; upgrade if necessary and process attached
35
+ function added(node, isAttached) {
36
+ if (scope.upgrade(node, isAttached)) {
37
+ // Return true to indicate
38
+ return true;
39
+ }
40
+ if (isAttached) {
41
+ attached(node);
42
+ }
43
+ }
44
+
45
+ // manage lifecycle on added node's subtree only; allows the entire subtree
46
+ // to upgrade if necessary and process attached
47
+ function addedSubtree(node, isAttached) {
48
+ forSubtree(node, function(e) {
49
+ if (added(e, isAttached)) {
50
+ return true;
51
+ }
52
+ });
53
+ }
54
+
55
+ // On platforms without MutationObserver, mutations may not be
56
+ // reliable and therefore attached/detached are not reliable. We think this
57
+ // occurs sometimes under heavy DOM operation load, but it is not easy to
58
+ // reproduce.
59
+ // To make these callbacks less likely to fail in this scenario,
60
+ // we *optionally* defer all inserts and removes
61
+ // to give a chance for elements to be attached into dom.
62
+ // This helps ensure attachedCallback fires for elements that are created and
63
+ // immediately added to dom.
64
+ // This change can significantly alter the performance characteristics
65
+ // of attaching elements and therefore we only enable it if the user has
66
+ // explicitly provided the `throttle-attached` flag.
67
+ var hasThrottledAttached = (window.MutationObserver._isPolyfilled &&
68
+ flags['throttle-attached']);
69
+ // bc
70
+ scope.hasPolyfillMutations = hasThrottledAttached;
71
+ // exposed for testing
72
+ scope.hasThrottledAttached = hasThrottledAttached;
73
+
74
+ var isPendingMutations = false;
75
+ var pendingMutations = [];
76
+ function deferMutation(fn) {
77
+ pendingMutations.push(fn);
78
+ if (!isPendingMutations) {
79
+ isPendingMutations = true;
80
+ setTimeout(takeMutations);
81
+ }
82
+ }
83
+
84
+ function takeMutations() {
85
+ isPendingMutations = false;
86
+ var $p = pendingMutations;
87
+ for (var i=0, l=$p.length, p; (i<l) && (p=$p[i]); i++) {
88
+ p();
89
+ }
90
+ pendingMutations = [];
91
+ }
92
+
93
+ function attached(element) {
94
+ if (hasThrottledAttached) {
95
+ deferMutation(function() {
96
+ _attached(element);
97
+ });
98
+ } else {
99
+ _attached(element);
100
+ }
101
+ }
102
+
103
+ // NOTE: due to how MO works (see comments below), an element may be attached
104
+ // multiple times so we protect against extra processing here.
105
+ function _attached(element) {
106
+ // track element for insertion if it's upgraded and cares about insertion
107
+ // bail if the element is already marked as attached
108
+ if (element.__upgraded__ && !element.__attached) {
109
+ element.__attached = true;
110
+ if (element.attachedCallback) {
111
+ element.attachedCallback();
112
+ }
113
+ }
114
+ }
115
+
116
+ /*
117
+ Manage nodes detached from document trees
118
+ */
119
+
120
+ // manage lifecycle on detached node and it's subtree; process detached
121
+ // for the node and entire subtree
122
+ function detachedNode(node) {
123
+ detached(node);
124
+ forSubtree(node, function(e) {
125
+ detached(e);
126
+ });
127
+ }
128
+
129
+ function detached(element) {
130
+ if (hasThrottledAttached) {
131
+ deferMutation(function() {
132
+ _detached(element);
133
+ });
134
+ } else {
135
+ _detached(element);
136
+ }
137
+ }
138
+
139
+ // NOTE: due to how MO works (see comments below), an element may be detached
140
+ // multiple times so we protect against extra processing here.
141
+ function _detached(element) {
142
+ // track element for removal if it's upgraded and cares about removal
143
+ // bail if the element is already marked as not attached
144
+ if (element.__upgraded__ && element.__attached) {
145
+ element.__attached = false;
146
+ if (element.detachedCallback) {
147
+ element.detachedCallback();
148
+ }
149
+ }
150
+ }
151
+
152
+ // recurse up the tree to check if an element is actually in the main document.
153
+ function inDocument(element) {
154
+ var p = element;
155
+ var doc = window.wrap(document);
156
+ while (p) {
157
+ if (p == doc) {
158
+ return true;
159
+ }
160
+ p = p.parentNode || ((p.nodeType === Node.DOCUMENT_FRAGMENT_NODE) && p.host);
161
+ }
162
+ }
163
+
164
+ // Install an element observer on all shadowRoots owned by node.
165
+ function watchShadow(node) {
166
+ if (node.shadowRoot && !node.shadowRoot.__watched) {
167
+ flags.dom && console.log('watching shadow-root for: ', node.localName);
168
+ // watch all unwatched roots...
169
+ var root = node.shadowRoot;
170
+ while (root) {
171
+ observe(root);
172
+ root = root.olderShadowRoot;
173
+ }
174
+ }
175
+ }
176
+
177
+ /*
178
+ NOTE: In order to process all mutations, it's necessary to recurse into
179
+ any added nodes. However, it's not possible to determine a priori if a node
180
+ will get its own mutation record. This means
181
+ *nodes can be seen multiple times*.
182
+
183
+ Here's an example:
184
+
185
+ (1) In this case, recursion is required to see `child`:
186
+
187
+ node.innerHTML = '<div><child></child></div>'
188
+
189
+ (2) In this case, child will get its own mutation record:
190
+
191
+ node.appendChild(div).appendChild(child);
192
+
193
+ We cannot know ahead of time if we need to walk into the node in (1) so we
194
+ do and see child; however, if it was added via case (2) then it will have its
195
+ own record and therefore be seen 2x.
196
+ */
197
+ function handler(root, mutations) {
198
+ // for logging only
199
+ if (flags.dom) {
200
+ var mx = mutations[0];
201
+ if (mx && mx.type === 'childList' && mx.addedNodes) {
202
+ if (mx.addedNodes) {
203
+ var d = mx.addedNodes[0];
204
+ while (d && d !== document && !d.host) {
205
+ d = d.parentNode;
206
+ }
207
+ var u = d && (d.URL || d._URL || (d.host && d.host.localName)) || '';
208
+ u = u.split('/?').shift().split('/').pop();
209
+ }
210
+ }
211
+ console.group('mutations (%d) [%s]', mutations.length, u || '');
212
+ }
213
+ // handle mutations
214
+ // NOTE: do an `inDocument` check dynamically here. It's possible that `root`
215
+ // is a document in which case the answer here can never change; however
216
+ // `root` may be an element like a shadowRoot that can be added/removed
217
+ // from the main document.
218
+ var isAttached = inDocument(root);
219
+ mutations.forEach(function(mx) {
220
+ if (mx.type === 'childList') {
221
+ forEach(mx.addedNodes, function(n) {
222
+ if (!n.localName) {
223
+ return;
224
+ }
225
+ addedNode(n, isAttached);
226
+ });
227
+ forEach(mx.removedNodes, function(n) {
228
+ if (!n.localName) {
229
+ return;
230
+ }
231
+ detachedNode(n);
232
+ });
233
+ }
234
+ });
235
+ flags.dom && console.groupEnd();
236
+ };
237
+
238
+
239
+ /*
240
+ When elements are added to the dom, upgrade and attached/detached may be
241
+ asynchronous. `CustomElements.takeRecords` can be called to process any
242
+ pending upgrades and attached/detached callbacks synchronously.
243
+ */
244
+ function takeRecords(node) {
245
+ node = window.wrap(node);
246
+ // If the optional node is not supplied, assume we mean the whole document.
247
+ if (!node) {
248
+ node = window.wrap(document);
249
+ }
250
+ // Find the root of the tree, which will be an Document or ShadowRoot.
251
+ while (node.parentNode) {
252
+ node = node.parentNode;
253
+ }
254
+ var observer = node.__observer;
255
+ if (observer) {
256
+ handler(node, observer.takeRecords());
257
+ takeMutations();
258
+ }
259
+ }
260
+
261
+ var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
262
+
263
+
264
+ // observe a node tree; bail if it's already being observed.
265
+ function observe(inRoot) {
266
+ if (inRoot.__observer) {
267
+ return;
268
+ }
269
+ // For each ShadowRoot, we create a new MutationObserver, so the root can be
270
+ // garbage collected once all references to the `inRoot` node are gone.
271
+ // Give the handler access to the root so that an 'in document' check can
272
+ // be done.
273
+ var observer = new MutationObserver(handler.bind(this, inRoot));
274
+ observer.observe(inRoot, {childList: true, subtree: true});
275
+ inRoot.__observer = observer;
276
+ }
277
+
278
+ // upgrade an entire document and observe it for elements changes.
279
+ function upgradeDocument(doc) {
280
+ doc = window.wrap(doc);
281
+ flags.dom && console.group('upgradeDocument: ', (doc.baseURI).split('/').pop());
282
+ var isMainDocument = (doc === window.wrap(document));
283
+ addedNode(doc, isMainDocument);
284
+ observe(doc);
285
+ flags.dom && console.groupEnd();
286
+ }
287
+
288
+ /*
289
+ This method is intended to be called when the document tree (including imports)
290
+ has pending custom elements to upgrade. It can be called multiple times and
291
+ should do nothing if no elements are in need of upgrade.
292
+ */
293
+ function upgradeDocumentTree(doc) {
294
+ forDocumentTree(doc, upgradeDocument);
295
+ }
296
+
297
+
298
+ // Patch `createShadowRoot()` if Shadow DOM is available, otherwise leave
299
+ // undefined to aid feature detection of Shadow DOM.
300
+ var originalCreateShadowRoot = Element.prototype.createShadowRoot;
301
+ if (originalCreateShadowRoot) {
302
+ Element.prototype.createShadowRoot = function() {
303
+ var root = originalCreateShadowRoot.call(this);
304
+ window.CustomElements.watchShadow(this);
305
+ return root;
306
+ };
307
+ }
308
+
309
+ // exports
310
+ scope.watchShadow = watchShadow;
311
+ scope.upgradeDocumentTree = upgradeDocumentTree;
312
+ scope.upgradeDocument = upgradeDocument;
313
+ scope.upgradeSubtree = addedSubtree;
314
+ scope.upgradeAll = addedNode;
315
+ scope.attached = attached;
316
+ scope.takeRecords = takeRecords;
317
+
318
+ });
@@ -0,0 +1,369 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
4
+ * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
5
+ * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6
+ * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
7
+ * Code distributed by Google as part of the polymer project is also
8
+ * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
9
+ */
10
+
11
+ /**
12
+ * Implements `document.registerElement`
13
+ * @module register
14
+ */
15
+
16
+ /**
17
+ * Polyfilled extensions to the `document` object.
18
+ * @class Document
19
+ */
20
+
21
+ window.CustomElements.addModule(function(scope) {
22
+
23
+ // imports
24
+ var isIE = scope.isIE;
25
+ var upgradeDocumentTree = scope.upgradeDocumentTree;
26
+ var upgradeAll = scope.upgradeAll;
27
+ var upgradeWithDefinition = scope.upgradeWithDefinition;
28
+ var implementPrototype = scope.implementPrototype;
29
+ var useNative = scope.useNative;
30
+
31
+ /**
32
+ * Registers a custom tag name with the document.
33
+ *
34
+ * When a registered element is created, a `readyCallback` method is called
35
+ * in the scope of the element. The `readyCallback` method can be specified on
36
+ * either `options.prototype` or `options.lifecycle` with the latter taking
37
+ * precedence.
38
+ *
39
+ * @method register
40
+ * @param {String} name The tag name to register. Must include a dash ('-'),
41
+ * for example 'x-component'.
42
+ * @param {Object} options
43
+ * @param {String} [options.extends]
44
+ * (_off spec_) Tag name of an element to extend (or blank for a new
45
+ * element). This parameter is not part of the specification, but instead
46
+ * is a hint for the polyfill because the extendee is difficult to infer.
47
+ * Remember that the input prototype must chain to the extended element's
48
+ * prototype (or HTMLElement.prototype) regardless of the value of
49
+ * `extends`.
50
+ * @param {Object} options.prototype The prototype to use for the new
51
+ * element. The prototype must inherit from HTMLElement.
52
+ * @param {Object} [options.lifecycle]
53
+ * Callbacks that fire at important phases in the life of the custom
54
+ * element.
55
+ *
56
+ * @example
57
+ * FancyButton = document.registerElement("fancy-button", {
58
+ * extends: 'button',
59
+ * prototype: Object.create(HTMLButtonElement.prototype, {
60
+ * readyCallback: {
61
+ * value: function() {
62
+ * console.log("a fancy-button was created",
63
+ * }
64
+ * }
65
+ * })
66
+ * });
67
+ * @return {Function} Constructor for the newly registered type.
68
+ */
69
+ function register(name, options) {
70
+ //console.warn('document.registerElement("' + name + '", ', options, ')');
71
+ // construct a defintion out of options
72
+ // TODO(sjmiles): probably should clone options instead of mutating it
73
+ var definition = options || {};
74
+ if (!name) {
75
+ throw new Error('document.registerElement: first argument `name` must not be empty');
76
+ }
77
+ if (name.indexOf('-') < 0) {
78
+ throw new Error('document.registerElement: first argument (\'name\') must contain a dash (\'-\'). Argument provided was \'' + String(name) + '\'.');
79
+ }
80
+ // prevent registering reserved names
81
+ if (isReservedTag(name)) {
82
+ throw new Error('Failed to execute \'registerElement\' on \'Document\': Registration failed for type \'' + String(name) + '\'. The type name is invalid.');
83
+ }
84
+ // elements may only be registered once
85
+ if (getRegisteredDefinition(name)) {
86
+ throw new Error('DuplicateDefinitionError: a type with name \'' + String(name) + '\' is already registered');
87
+ }
88
+ // prototype is optional, default to an extension of HTMLElement
89
+ if (!definition.prototype) {
90
+ definition.prototype = Object.create(HTMLElement.prototype);
91
+ }
92
+ // record name
93
+ definition.__name = name.toLowerCase();
94
+ // ensure extended name is also treated case-insensitively
95
+ if (definition.extends) {
96
+ definition.extends = definition.extends.toLowerCase();
97
+ }
98
+ // ensure a lifecycle object so we don't have to null test it
99
+ definition.lifecycle = definition.lifecycle || {};
100
+ // build a list of ancestral custom elements (for native base detection)
101
+ // TODO(sjmiles): we used to need to store this, but current code only
102
+ // uses it in 'resolveTagName': it should probably be inlined
103
+ definition.ancestry = ancestry(definition.extends);
104
+ // extensions of native specializations of HTMLElement require localName
105
+ // to remain native, and use secondary 'is' specifier for extension type
106
+ resolveTagName(definition);
107
+ // some platforms require modifications to the user-supplied prototype
108
+ // chain
109
+ resolvePrototypeChain(definition);
110
+ // overrides to implement attributeChanged callback
111
+ overrideAttributeApi(definition.prototype);
112
+ // 7.1.5: Register the DEFINITION with DOCUMENT
113
+ registerDefinition(definition.__name, definition);
114
+ // 7.1.7. Run custom element constructor generation algorithm with PROTOTYPE
115
+ // 7.1.8. Return the output of the previous step.
116
+ definition.ctor = generateConstructor(definition);
117
+ definition.ctor.prototype = definition.prototype;
118
+ // force our .constructor to be our actual constructor
119
+ definition.prototype.constructor = definition.ctor;
120
+ // if initial parsing is complete
121
+ if (scope.ready) {
122
+ // upgrade any pre-existing nodes of this type
123
+ upgradeDocumentTree(document);
124
+ }
125
+ return definition.ctor;
126
+ }
127
+
128
+ // attribute watching
129
+ function overrideAttributeApi(prototype) {
130
+ // overrides to implement callbacks
131
+ // TODO(sjmiles): should support access via .attributes NamedNodeMap
132
+ // TODO(sjmiles): preserves user defined overrides, if any
133
+ if (prototype.setAttribute._polyfilled) {
134
+ return;
135
+ }
136
+ var setAttribute = prototype.setAttribute;
137
+ prototype.setAttribute = function(name, value) {
138
+ changeAttribute.call(this, name, value, setAttribute);
139
+ };
140
+ var removeAttribute = prototype.removeAttribute;
141
+ prototype.removeAttribute = function(name) {
142
+ changeAttribute.call(this, name, null, removeAttribute);
143
+ };
144
+ prototype.setAttribute._polyfilled = true;
145
+ }
146
+
147
+ // https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/
148
+ // index.html#dfn-attribute-changed-callback
149
+ function changeAttribute(name, value, operation) {
150
+ name = name.toLowerCase();
151
+ var oldValue = this.getAttribute(name);
152
+ operation.apply(this, arguments);
153
+ var newValue = this.getAttribute(name);
154
+ if (this.attributeChangedCallback &&
155
+ (newValue !== oldValue)) {
156
+ this.attributeChangedCallback(name, oldValue, newValue);
157
+ }
158
+ }
159
+
160
+ function isReservedTag(name) {
161
+ for (var i = 0; i < reservedTagList.length; i++) {
162
+ if (name === reservedTagList[i]) {
163
+ return true;
164
+ }
165
+ }
166
+ }
167
+
168
+ var reservedTagList = [
169
+ 'annotation-xml', 'color-profile', 'font-face', 'font-face-src',
170
+ 'font-face-uri', 'font-face-format', 'font-face-name', 'missing-glyph'
171
+ ];
172
+
173
+ function ancestry(extnds) {
174
+ var extendee = getRegisteredDefinition(extnds);
175
+ if (extendee) {
176
+ return ancestry(extendee.extends).concat([extendee]);
177
+ }
178
+ return [];
179
+ }
180
+
181
+ function resolveTagName(definition) {
182
+ // if we are explicitly extending something, that thing is our
183
+ // baseTag, unless it represents a custom component
184
+ var baseTag = definition.extends;
185
+ // if our ancestry includes custom components, we only have a
186
+ // baseTag if one of them does
187
+ for (var i=0, a; (a=definition.ancestry[i]); i++) {
188
+ baseTag = a.is && a.tag;
189
+ }
190
+ // our tag is our baseTag, if it exists, and otherwise just our name
191
+ definition.tag = baseTag || definition.__name;
192
+ if (baseTag) {
193
+ // if there is a base tag, use secondary 'is' specifier
194
+ definition.is = definition.__name;
195
+ }
196
+ }
197
+
198
+ function resolvePrototypeChain(definition) {
199
+ // if we don't support __proto__ we need to locate the native level
200
+ // prototype for precise mixing in
201
+ if (!Object.__proto__) {
202
+ // default prototype
203
+ var nativePrototype = HTMLElement.prototype;
204
+ // work out prototype when using type-extension
205
+ if (definition.is) {
206
+ var inst = document.createElement(definition.tag);
207
+ nativePrototype = Object.getPrototypeOf(inst);
208
+ }
209
+ // ensure __proto__ reference is installed at each point on the prototype
210
+ // chain.
211
+ // NOTE: On platforms without __proto__, a mixin strategy is used instead
212
+ // of prototype swizzling. In this case, this generated __proto__ provides
213
+ // limited support for prototype traversal.
214
+ var proto = definition.prototype, ancestor;
215
+ var foundPrototype = false;
216
+ while (proto) {
217
+ if (proto == nativePrototype) {
218
+ foundPrototype = true;
219
+ }
220
+ ancestor = Object.getPrototypeOf(proto);
221
+ if (ancestor) {
222
+ proto.__proto__ = ancestor;
223
+ }
224
+ proto = ancestor;
225
+ }
226
+ if (!foundPrototype) {
227
+ // Note the spec actually allows this, but it results in broken elements
228
+ // and is difficult to polyfill correctly, so we throw
229
+ console.warn(definition.tag + ' prototype not found in prototype chain for ' +
230
+ definition.is);
231
+ }
232
+ // cache this in case of mixin
233
+ definition.native = nativePrototype;
234
+ }
235
+ }
236
+
237
+ // SECTION 4
238
+
239
+ function instantiate(definition) {
240
+ // 4.a.1. Create a new object that implements PROTOTYPE
241
+ // 4.a.2. Let ELEMENT by this new object
242
+ //
243
+ // the custom element instantiation algorithm must also ensure that the
244
+ // output is a valid DOM element with the proper wrapper in place.
245
+ //
246
+ return upgradeWithDefinition(domCreateElement(definition.tag), definition);
247
+ }
248
+
249
+ // element registry (maps tag names to definitions)
250
+
251
+ var registry = {};
252
+
253
+ function getRegisteredDefinition(name) {
254
+ if (name) {
255
+ return registry[name.toLowerCase()];
256
+ }
257
+ }
258
+
259
+ function registerDefinition(name, definition) {
260
+ registry[name] = definition;
261
+ }
262
+
263
+ function generateConstructor(definition) {
264
+ return function() {
265
+ return instantiate(definition);
266
+ };
267
+ }
268
+
269
+ var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
270
+ function createElementNS(namespace, tag, typeExtension) {
271
+ // NOTE: we do not support non-HTML elements,
272
+ // just call createElementNS for non HTML Elements
273
+ if (namespace === HTML_NAMESPACE) {
274
+ return createElement(tag, typeExtension);
275
+ } else {
276
+ return domCreateElementNS(namespace, tag);
277
+ }
278
+ }
279
+
280
+ function createElement(tag, typeExtension) {
281
+ // TODO(sjmiles): ignore 'tag' when using 'typeExtension', we could
282
+ // error check it, or perhaps there should only ever be one argument
283
+ if (tag) {
284
+ tag = tag.toLowerCase();
285
+ }
286
+ if (typeExtension) {
287
+ typeExtension = typeExtension.toLowerCase();
288
+ }
289
+ var definition = getRegisteredDefinition(typeExtension || tag);
290
+ if (definition) {
291
+ if (tag == definition.tag && typeExtension == definition.is) {
292
+ return new definition.ctor();
293
+ }
294
+ // Handle empty string for type extension.
295
+ if (!typeExtension && !definition.is) {
296
+ return new definition.ctor();
297
+ }
298
+ }
299
+ var element;
300
+ if (typeExtension) {
301
+ element = createElement(tag);
302
+ element.setAttribute('is', typeExtension);
303
+ return element;
304
+ }
305
+ element = domCreateElement(tag);
306
+ // Custom tags should be HTMLElements even if not upgraded.
307
+ if (tag.indexOf('-') >= 0) {
308
+ implementPrototype(element, HTMLElement);
309
+ }
310
+ return element;
311
+ }
312
+
313
+ // capture native createElement before we override it
314
+ var domCreateElement = document.createElement.bind(document);
315
+ var domCreateElementNS = document.createElementNS.bind(document);
316
+
317
+ // Create a custom 'instanceof'. This is necessary when CustomElements
318
+ // are implemented via a mixin strategy, as for example on IE10.
319
+ var isInstance;
320
+ if (!Object.__proto__ && !useNative) {
321
+ isInstance = function(obj, ctor) {
322
+ // Allows instanceof(<div>, HTMLElement.prototype) to work
323
+ if (obj instanceof ctor) {
324
+ return true;
325
+ }
326
+ var p = obj;
327
+ while (p) {
328
+ // NOTE: this is not technically correct since we're not checking if
329
+ // an object is an instance of a constructor; however, this should
330
+ // be good enough for the mixin strategy.
331
+ if (p === ctor.prototype) {
332
+ return true;
333
+ }
334
+ p = p.__proto__;
335
+ }
336
+ return false;
337
+ };
338
+ } else {
339
+ isInstance = function(obj, base) {
340
+ return obj instanceof base;
341
+ };
342
+ }
343
+
344
+ // wrap a dom object method that works on nodes such that it forces upgrade
345
+ function wrapDomMethodToForceUpgrade(obj, methodName) {
346
+ var orig = obj[methodName];
347
+ obj[methodName] = function() {
348
+ var n = orig.apply(this, arguments);
349
+ upgradeAll(n);
350
+ return n;
351
+ };
352
+ }
353
+
354
+ wrapDomMethodToForceUpgrade(Node.prototype, 'cloneNode');
355
+ wrapDomMethodToForceUpgrade(document, 'importNode');
356
+
357
+ // exports
358
+ document.registerElement = register;
359
+ document.createElement = createElement; // override
360
+ document.createElementNS = createElementNS; // override
361
+ scope.registry = registry;
362
+ scope.instanceof = isInstance;
363
+ scope.reservedTagList = reservedTagList;
364
+ scope.getRegisteredDefinition = getRegisteredDefinition;
365
+
366
+ // bc
367
+ document.register = document.registerElement;
368
+
369
+ });