Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
195 lines
6.2 KiB
JavaScript
Executable file
195 lines
6.2 KiB
JavaScript
Executable file
/**
|
|
* element 0.0.1+
|
|
* Copyright (c) 2013-2015, The Dojo Foundation All Rights Reserved.
|
|
* Available via the MIT or new BSD license.
|
|
* see: http://github.com/jrburke/element for details
|
|
*/
|
|
/*jshint browser: true */
|
|
/*globals define */
|
|
define(function(require, exports, module) {
|
|
'use strict';
|
|
var slice = Array.prototype.slice,
|
|
callbackSuffix = 'Callback',
|
|
callbackSuffixLength = callbackSuffix.length,
|
|
charRegExp = /[^a-z]/g,
|
|
idToTag = function(id) {
|
|
return id.toLowerCase().replace(charRegExp, '-');
|
|
},
|
|
moduleConfig = module.config();
|
|
|
|
if (moduleConfig.hasOwnProperty('idToTag')) {
|
|
idToTag = moduleConfig.idToTag;
|
|
}
|
|
|
|
/**
|
|
* Converts an attribute like a-long-attr to aLongAttr
|
|
* @param {String} attrName The attribute name
|
|
* @return {String}
|
|
*/
|
|
function makePropName(attrName) {
|
|
var parts = attrName.split('-');
|
|
for (var i = 1; i < parts.length; i++) {
|
|
parts[i] = parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
|
|
}
|
|
return parts.join('');
|
|
}
|
|
|
|
/**
|
|
* Given an attribute name, set the corresponding property
|
|
* name on the custom element instance, if it has such a
|
|
* property.
|
|
* @param {Object} instance the custom element instance.
|
|
* @param {String} attrName the attribute name.
|
|
* @param {String} attrValue The attribute value.
|
|
*/
|
|
function setPropFromAttr(instance, attrName, attrValue) {
|
|
var proto = Object.getPrototypeOf(instance),
|
|
propName = makePropName(attrName),
|
|
descriptor = Object.getOwnPropertyDescriptor(proto, propName);
|
|
|
|
// Only check immediate prototype for a property that
|
|
// matches, to avoid calling base setters that may be
|
|
// on original HTML-based element that could cause
|
|
// bad effects. Needs more testing for those cases to
|
|
// confirm, but since element is a mixin approach, this
|
|
// approach is safe.
|
|
if (descriptor && descriptor.set) {
|
|
instance[propName] = attrValue;
|
|
}
|
|
}
|
|
|
|
function makePropFn(prop) {
|
|
return function() {
|
|
var i, ret,
|
|
args = slice.call(arguments),
|
|
fns = this._element.props[prop];
|
|
|
|
for (i = 0; i < fns.length; i++) {
|
|
ret = fns[i].apply(this, args);
|
|
}
|
|
|
|
// Last function wins on the return value.
|
|
return ret;
|
|
};
|
|
}
|
|
|
|
function mixFnProp(proto, prop, value, operation) {
|
|
if (proto.hasOwnProperty(prop)) {
|
|
var existing = proto._element.props[prop];
|
|
if (!existing) {
|
|
existing = proto._element.props[prop] = [proto[prop]];
|
|
proto[prop] = makePropFn(prop);
|
|
}
|
|
operation = operation || 'push';
|
|
existing[operation](value);
|
|
} else {
|
|
proto[prop] = value;
|
|
}
|
|
}
|
|
|
|
function mix(proto, mixin) {
|
|
// Allow a top level of a mixin to be an array of other
|
|
// mixins.
|
|
if (Array.isArray(mixin)) {
|
|
mixin.forEach(function(mixin) {
|
|
mix(proto, mixin);
|
|
});
|
|
return;
|
|
}
|
|
|
|
Object.keys(mixin).forEach(function(key) {
|
|
var suffixIndex,
|
|
descriptor = Object.getOwnPropertyDescriptor(mixin, key);
|
|
|
|
// Any property that ends in Callback, like the custom element
|
|
// lifecycle events, can be multiplexed.
|
|
suffixIndex = key.indexOf(callbackSuffix);
|
|
if (suffixIndex > 0 &&
|
|
suffixIndex === key.length - callbackSuffixLength) {
|
|
mixFnProp(proto, key, descriptor.value);
|
|
} else {
|
|
Object.defineProperty(proto, key, descriptor);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Main module export. These methods are visible to
|
|
* any module.
|
|
*/
|
|
var element = {
|
|
/**
|
|
* The AMD loader plugin API. Called by an AMD loader
|
|
* to handle 'element!' resources.
|
|
* @param {String} id module ID to load.
|
|
* @param {Function} req context-specific `require` function.
|
|
* @param {Function} onload function to call once loading is complete.
|
|
* @param {Object} config config from the loader. Normally just has
|
|
* config.isBuild if in a build scenario.
|
|
*/
|
|
load: function(id, req, onload, config) {
|
|
// Normal dependency request.
|
|
req([id], function(mod) {
|
|
// For builds do nothing else. Also if no module export or
|
|
// it is a function because the module already called
|
|
// document.register itself, then do not bother with the
|
|
// other work.
|
|
if (config.isBuild || !mod || typeof mod === 'function') {
|
|
return onload();
|
|
}
|
|
|
|
// Create the prototype for the custom element.
|
|
// Allow the module to be an array of mixins.
|
|
// If it is an array, then mix them all in to the
|
|
// prototype.
|
|
var proto = Object.create(HTMLElement.prototype);
|
|
|
|
// Define a property to hold all the element-specific information
|
|
Object.defineProperty(proto, '_element', {
|
|
enumerable: false,
|
|
configurable: false,
|
|
writable: false,
|
|
value: {}
|
|
});
|
|
proto._element.props = {};
|
|
|
|
mix(proto, mod);
|
|
|
|
// Wire attributes to this element's custom/getter setters.
|
|
// Because of the 'unshift' use, this will actually execute
|
|
// before the templateCreatedCallback, which is good. The
|
|
// exterior API should set up the internal state before
|
|
// other parts of createdCallback run.
|
|
mixFnProp(proto, 'createdCallback', function attrCreated() {
|
|
var i, item,
|
|
attrs = this.attributes;
|
|
|
|
for (i = 0; i < attrs.length; i++) {
|
|
item = attrs.item(i);
|
|
setPropFromAttr(this, item.nodeName, item.value);
|
|
}
|
|
}, 'unshift');
|
|
|
|
// Listen for attribute changed calls, and just trigger getter/setter
|
|
// calling if matching property. Make sure it is the first one in
|
|
// the listener set.
|
|
mixFnProp(proto, 'attributeChangedCallback',
|
|
function attrChanged(name, oldValue, newValue) {
|
|
// Only called if value has changed, so no need to check
|
|
// oldValue !== newValue
|
|
setPropFromAttr(this, name, newValue);
|
|
}, 'unshift');
|
|
|
|
// Translate any characters that are unfit for custom element
|
|
// names to dashes
|
|
var tagId = idToTag(id);
|
|
|
|
onload(document.registerElement(tagId, {
|
|
prototype: proto
|
|
}));
|
|
});
|
|
}
|
|
};
|
|
|
|
return element;
|
|
});
|