written 0.0.5 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ });