/* * Copyright (c) 2014 The Polymer Project Authors. All rights reserved. * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt * Code distributed by Google as part of the polymer project is also * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ (function(scope) { // imports var api = scope.api; var isBase = scope.isBase; var extend = scope.extend; var hasShadowDOMPolyfill = window.ShadowDOMPolyfill; // prototype api var prototype = { register: function(name, extendeeName) { // build prototype combining extendee, Polymer base, and named api this.buildPrototype(name, extendeeName); // register our custom element with the platform this.registerPrototype(name, extendeeName); // reference constructor in a global named by 'constructor' attribute this.publishConstructor(); }, buildPrototype: function(name, extendeeName) { // get our custom prototype (before chaining) var extension = scope.getRegisteredPrototype(name); // get basal prototype var base = this.generateBasePrototype(extendeeName); // implement declarative features this.desugarBeforeChaining(extension, base); // join prototypes this.prototype = this.chainPrototypes(extension, base); // more declarative features this.desugarAfterChaining(name, extendeeName); }, desugarBeforeChaining: function(prototype, base) { // back reference declaration element // TODO(sjmiles): replace `element` with `elementElement` or `declaration` prototype.element = this; // transcribe `attributes` declarations onto own prototype's `publish` this.publishAttributes(prototype, base); // `publish` properties to the prototype and to attribute watch this.publishProperties(prototype, base); // infer observers for `observe` list based on method names this.inferObservers(prototype); // desugar compound observer syntax, e.g. 'a b c' this.explodeObservers(prototype); }, chainPrototypes: function(prototype, base) { // chain various meta-data objects to inherited versions this.inheritMetaData(prototype, base); // chain custom api to inherited var chained = this.chainObject(prototype, base); // x-platform fixup ensurePrototypeTraversal(chained); return chained; }, inheritMetaData: function(prototype, base) { // chain observe object to inherited this.inheritObject('observe', prototype, base); // chain publish object to inherited this.inheritObject('publish', prototype, base); // chain reflect object to inherited this.inheritObject('reflect', prototype, base); // chain our lower-cased publish map to the inherited version this.inheritObject('_publishLC', prototype, base); // chain our instance attributes map to the inherited version this.inheritObject('_instanceAttributes', prototype, base); // chain our event delegates map to the inherited version this.inheritObject('eventDelegates', prototype, base); }, // implement various declarative features desugarAfterChaining: function(name, extendee) { // build side-chained lists to optimize iterations this.optimizePropertyMaps(this.prototype); this.createPropertyAccessors(this.prototype); // install mdv delegate on template this.installBindingDelegate(this.fetchTemplate()); // install external stylesheets as if they are inline this.installSheets(); // adjust any paths in dom from imports this.resolveElementPaths(this); // compile list of attributes to copy to instances this.accumulateInstanceAttributes(); // parse on-* delegates declared on `this` element this.parseHostEvents(); // // install a helper method this.resolvePath to aid in // setting resource urls. e.g. // this.$.image.src = this.resolvePath('images/foo.png') this.addResolvePathApi(); // under ShadowDOMPolyfill, transforms to approximate missing CSS features if (hasShadowDOMPolyfill) { WebComponents.ShadowCSS.shimStyling(this.templateContent(), name, extendee); } // allow custom element access to the declarative context if (this.prototype.registerCallback) { this.prototype.registerCallback(this); } }, // if a named constructor is requested in element, map a reference // to the constructor to the given symbol publishConstructor: function() { var symbol = this.getAttribute('constructor'); if (symbol) { window[symbol] = this.ctor; } }, // build prototype combining extendee, Polymer base, and named api generateBasePrototype: function(extnds) { var prototype = this.findBasePrototype(extnds); if (!prototype) { // create a prototype based on tag-name extension var prototype = HTMLElement.getPrototypeForTag(extnds); // insert base api in inheritance chain (if needed) prototype = this.ensureBaseApi(prototype); // memoize this base memoizedBases[extnds] = prototype; } return prototype; }, findBasePrototype: function(name) { return memoizedBases[name]; }, // install Polymer instance api into prototype chain, as needed ensureBaseApi: function(prototype) { if (prototype.PolymerBase) { return prototype; } var extended = Object.create(prototype); // we need a unique copy of base api for each base prototype // therefore we 'extend' here instead of simply chaining api.publish(api.instance, extended); // TODO(sjmiles): sharing methods across prototype chains is // not supported by 'super' implementation which optimizes // by memoizing prototype relationships. // Probably we should have a version of 'extend' that is // share-aware: it could study the text of each function, // look for usage of 'super', and wrap those functions in // closures. // As of now, there is only one problematic method, so // we just patch it manually. // To avoid re-entrancy problems, the special super method // installed is called `mixinSuper` and the mixin method // must use this method instead of the default `super`. this.mixinMethod(extended, prototype, api.instance.mdv, 'bind'); // return buffed-up prototype return extended; }, mixinMethod: function(extended, prototype, api, name) { var $super = function(args) { return prototype[name].apply(this, args); }; extended[name] = function() { this.mixinSuper = $super; return api[name].apply(this, arguments); } }, // ensure prototype[name] inherits from a prototype.prototype[name] inheritObject: function(name, prototype, base) { // require an object var source = prototype[name] || {}; // chain inherited properties onto a new object prototype[name] = this.chainObject(source, base[name]); }, // register 'prototype' to custom element 'name', store constructor registerPrototype: function(name, extendee) { var info = { prototype: this.prototype } // native element must be specified in extends var typeExtension = this.findTypeExtension(extendee); if (typeExtension) { info.extends = typeExtension; } // register the prototype with HTMLElement for name lookup HTMLElement.register(name, this.prototype); // register the custom type this.ctor = document.registerElement(name, info); }, findTypeExtension: function(name) { if (name && name.indexOf('-') < 0) { return name; } else { var p = this.findBasePrototype(name); if (p.element) { return this.findTypeExtension(p.element.extends); } } } }; // memoize base prototypes var memoizedBases = {}; // implementation of 'chainObject' depends on support for __proto__ if (Object.__proto__) { prototype.chainObject = function(object, inherited) { if (object && inherited && object !== inherited) { object.__proto__ = inherited; } return object; } } else { prototype.chainObject = function(object, inherited) { if (object && inherited && object !== inherited) { var chained = Object.create(inherited); object = extend(chained, object); } return object; } } // On platforms that do not support __proto__ (versions of IE), the prototype // chain of a custom element is simulated via installation of __proto__. // Although custom elements manages this, we install it here so it's // available during desugaring. function ensurePrototypeTraversal(prototype) { if (!Object.__proto__) { var ancestor = Object.getPrototypeOf(prototype); prototype.__proto__ = ancestor; if (isBase(ancestor)) { ancestor.__proto__ = Object.getPrototypeOf(ancestor); } } } // exports api.declaration.prototype = prototype; })(Polymer);