diff --git a/dist/gaia-header-es5.js b/dist/gaia-header-es5.js new file mode 100644 index 0000000..0830c62 --- /dev/null +++ b/dist/gaia-header-es5.js @@ -0,0 +1,1442 @@ +"use strict"; + +!(function (e) { + if ("object" == typeof exports && "undefined" != typeof module) module.exports = e();else if ("function" == typeof define && define.amd) define([], e);else { + var f;"undefined" != typeof window ? f = window : "undefined" != typeof global ? f = global : "undefined" != typeof self && (f = self), f.GaiaHeader = e(); + } +})(function () { + var define, module, exports;return (function e(t, n, r) { + function s(o, u) { + if (!n[o]) { + if (!t[o]) { + var a = typeof require == "function" && require;if (!u && a) { + return a(o, !0); + }if (i) { + return i(o, !0); + }var f = new Error("Cannot find module '" + o + "'");throw (f.code = "MODULE_NOT_FOUND", f); + }var l = n[o] = { exports: {} };t[o][0].call(l.exports, function (e) { + var n = t[o][1][e];return s(n ? n : e); + }, l, l.exports, e, t, n, r); + }return n[o].exports; + }var i = typeof require == "function" && require;for (var o = 0; o < r.length; o++) s(r[o]);return s; + })({ 1: [function (require, module, exports) { + ;(function (define) { + define(function (require, exports, module) { + "use strict"; + + /** + * Simple logger. + * + * @return {Function} + */ + var debug = 0 ? console.log.bind(console) : function () {}; + + /** + * Global canvas cache. + * + * @type {Object} + */ + var cache = {}; + + /** + * Default min/max font-size. + * + * @type {Number} + */ + var MIN = 16; + var MAX = 24; + + /** + * The number of pixels to subtract from + * the given `config.space` to ensure + * HTML text doesn't overflow container. + * + * Ideally we would use 1px, but in some + * cases italicised text in canvas is ~2px + * longer than the same text in HTML. + * + * http://bugzil.la/1126391 + * + * @type {Number} + */ + var BUFFER = 3; + + /** + * Get the font-size that closest fits + * the given space with the given font. + * + * Config: + * + * - {String} `text` The text string + * - {String} `font` Font shorthand string + * - {Number} `space` Width (px) to fit the text into + * - {Number} `min` Min font-size (px) (optional) + * - {Number} `max` Max font-size (px) (optional) + * + * @param {Object} config + * @return {Object} {fontSize,overflowing,textWidth} + */ + module.exports = function (config) { + debug("font fit", config); + var space = config.space - BUFFER; + var min = config.min || MIN; + var max = config.max || MAX; + var text = trim(config.text); + var fontSize = max; + var textWidth; + var font; + + do { + font = config.font.replace(/\d+px/, fontSize + "px"); + textWidth = getTextWidth(text, font); + } while (textWidth > space && fontSize !== min && fontSize--); + + return { + textWidth: textWidth, + fontSize: fontSize, + overflowing: textWidth > space + }; + }; + + /** + * Get the width of the given text + * with the given font style. + * + * @param {String} text + * @param {String} font (CSS shorthand) + * @return {Number} (px) + */ + function getTextWidth(text, font) { + var ctx = getCanvasContext(font); + var width = ctx.measureText(text).width; + debug("got text width", width); + return width; + } + + /** + * Get a canvas context configured + * to the given font style. + * + * @param {String} font + * @return {CanvasRenderingContext2D} + */ + function getCanvasContext(font) { + debug("get canvas context", font); + + var cached = cache[font]; + if (cached) { + return cached; + } + + var canvas = document.createElement("canvas"); + canvas.setAttribute("moz-opaque", "true"); + canvas.setAttribute("width", "1px"); + canvas.setAttribute("height", "1px"); + debug("created canvas", canvas); + + var ctx = canvas.getContext("2d", { willReadFrequently: true }); + ctx.font = font; + + return cache[font] = ctx; + } + + /** + * Trim leading, trailing + * and excess whitespace. + * + * @param {String} text + * @return {String} + */ + function trim(text) { + return text.replace(/\s+/g, " ").trim(); + } + }); + })(typeof define == "function" && define.amd ? define : (function (n, w) { + "use strict";return typeof module == "object" ? function (c) { + c(require, exports, module); + } : function (c) { + var m = { exports: {} };c(function (n) { + return w[n]; + }, m.exports, m);w[n] = m.exports; + }; + })("font-fit", this)); + }, {}], 2: [function (require, module, exports) { + /* jshint node:true */ + /* globals define */ + ;(function (define) { + "use strict";define(function (require, exports, module) { + /** + * Locals + */ + + var textContent = Object.getOwnPropertyDescriptor(Node.prototype, "textContent"); + var innerHTML = Object.getOwnPropertyDescriptor(Element.prototype, "innerHTML"); + var removeAttribute = Element.prototype.removeAttribute; + var setAttribute = Element.prototype.setAttribute; + var noop = function noop() {}; + + /** + * Register a new component. + * + * @param {String} name + * @param {Object} props + * @return {constructor} + * @public + */ + exports.register = function (name, props) { + var baseProto = getBaseProto(props["extends"]); + + // Clean up + delete props["extends"]; + + // Pull out CSS that needs to be in the light-dom + if (props.template) { + var output = processCss(props.template, name); + + props.template = document.createElement("template"); + props.template.innerHTML = output.template; + props.lightCss = output.lightCss; + + props.globalCss = props.globalCss || ""; + props.globalCss += output.globalCss; + } + + // Inject global CSS into the document, + // and delete as no longer needed + injectGlobalCss(props.globalCss); + delete props.globalCss; + + // Merge base getter/setter attributes with the user's, + // then define the property descriptors on the prototype. + var descriptors = mixin(props.attrs || {}, base.descriptors); + + // Store the orginal descriptors somewhere + // a little more private and delete the original + props._attrs = props.attrs; + delete props.attrs; + + // Create the prototype, extended from base and + // define the descriptors directly on the prototype + var proto = createProto(baseProto, props); + Object.defineProperties(proto, descriptors); + + // Register the custom-element and return the constructor + try { + return document.registerElement(name, { prototype: proto }); + } catch (e) { + if (e.name !== "NotSupportedError") { + throw e; + } + } + }; + + var base = { + properties: { + GaiaComponent: true, + attributeChanged: noop, + attached: noop, + detached: noop, + created: noop, + + createdCallback: function createdCallback() { + if (this.rtl) { + addDirObserver(); + } + injectLightCss(this); + this.created(); + }, + + /** + * It is very common to want to keep object + * properties in-sync with attributes, + * for example: + * + * el.value = 'foo'; + * el.setAttribute('value', 'foo'); + * + * So we support an object on the prototype + * named 'attrs' to provide a consistent + * way for component authors to define + * these properties. When an attribute + * changes we keep the attr[name] + * up-to-date. + * + * @param {String} name + * @param {String||null} from + * @param {String||null} to + */ + attributeChangedCallback: function attributeChangedCallback(name, from, to) { + var prop = toCamelCase(name); + if (this._attrs && this._attrs[prop]) { + this[prop] = to; + } + this.attributeChanged(name, from, to); + }, + + attachedCallback: function attachedCallback() { + this.attached(); + }, + detachedCallback: function detachedCallback() { + this.detached(); + }, + + /** + * A convenient method for setting up + * a shadow-root using the defined template. + * + * @return {ShadowRoot} + */ + setupShadowRoot: function setupShadowRoot() { + if (!this.template) { + return; + } + var node = document.importNode(this.template.content, true); + this.createShadowRoot().appendChild(node); + return this.shadowRoot; + }, + + /** + * Sets an attribute internally + * and externally. This is so that + * we can style internal shadow-dom + * content. + * + * @param {String} name + * @param {String} value + */ + setAttr: function setAttr(name, value) { + var internal = this.shadowRoot.firstElementChild; + setAttribute.call(internal, name, value); + setAttribute.call(this, name, value); + }, + + /** + * Removes an attribute internally + * and externally. This is so that + * we can style internal shadow-dom + * content. + * + * @param {String} name + * @param {String} value + */ + removeAttr: function removeAttr(name) { + var internal = this.shadowRoot.firstElementChild; + removeAttribute.call(internal, name); + removeAttribute.call(this, name); + } + }, + + descriptors: { + textContent: { + set: function set(value) { + textContent.set.call(this, value); + if (this.lightStyle) { + this.appendChild(this.lightStyle); + } + }, + + get: function get() { + return textContent.get(); + } + }, + + innerHTML: { + set: function set(value) { + innerHTML.set.call(this, value); + if (this.lightStyle) { + this.appendChild(this.lightStyle); + } + }, + + get: innerHTML.get + } + } + }; + + /** + * The default base prototype to use + * when `extends` is undefined. + * + * @type {Object} + */ + var defaultPrototype = createProto(HTMLElement.prototype, base.properties); + + /** + * Returns a suitable prototype based + * on the object passed. + * + * @param {HTMLElementPrototype|undefined} proto + * @return {HTMLElementPrototype} + * @private + */ + function getBaseProto(proto) { + if (!proto) { + return defaultPrototype; + } + proto = proto.prototype || proto; + return !proto.GaiaComponent ? createProto(proto, base.properties) : proto; + } + + /** + * Extends the given proto and mixes + * in the given properties. + * + * @param {Object} proto + * @param {Object} props + * @return {Object} + */ + function createProto(proto, props) { + return mixin(Object.create(proto), props); + } + + /** + * Detects presence of shadow-dom + * CSS selectors. + * + * @return {Boolean} + */ + var hasShadowCSS = (function () { + var div = document.createElement("div"); + try { + div.querySelector(":host");return true; + } catch (e) { + return false; + } + })(); + + /** + * Regexs used to extract shadow-css + * + * @type {Object} + */ + var regex = { + shadowCss: /(?:\:host|\:\:content)[^{]*\{[^}]*\}/g, + ":host": /(?:\:host)/g, + ":host()": /\:host\((.+)\)(?: \:\:content)?/g, + ":host-context": /\:host-context\((.+)\)([^{,]+)?/g, + "::content": /(?:\:\:content)/g + }; + + /** + * Extracts the :host and ::content rules + * from the shadow-dom CSS and rewrites + * them to work from the ", + + // Test hook + nextTick: nextTick + }); + + /** + * Utils + */ + + /** + * Determines whether passed element + * contributes to the layout in gaia-header. + * + * @param {Element} el + * @return {Boolean} + */ + function contributesToLayout(el) { + return el.tagName !== "STYLE"; + } + + /** + * Set a 'style id' property that + * can be retrieved later. + * + * Used to determine whether a title's + * `style` needs to be updated or not. + * + * @param {Element} el + * @param {String} id + */ + function setStyleId(el, id) { + el._styleId = id; + } + + /** + * Get a 'style id' property. + * + * Used to determine whether a title's + * `style` needs to be updated or not. + * + * @param {Element} el + * @param {String} id + */ + function getStyleId(el) { + return el._styleId; + } + + /** + * Calls callback at next 'microtask'. + * Returns an object that has + * a `.clear()` method. + * + * @param {Function} fn + * @return {Object} { clear } + */ + function nextTick(fn) { + var cleared; + Promise.resolve().then(function () { + if (!cleared) { + fn(); + } + }); + return { clear: function clear() { + cleared = true; + } }; + } + }); + })(typeof define == "function" && define.amd ? define : (function (n, w) { + "use strict";return typeof module == "object" ? function (c) { + c(require, exports, module); + } : function (c) { + var m = { exports: {} };c(function (n) { + return w[n]; + }, m.exports, m);w[n] = m.exports; + }; + })("gaia-header", this)); + }, { "font-fit": 1, "gaia-component": 2, "gaia-icons": 3 }] }, {}, [4])(4); +}); diff --git a/dist/gaia-header.js b/dist/gaia-header.js index 8208261..d49bf97 100644 --- a/dist/gaia-header.js +++ b/dist/gaia-header.js @@ -133,14 +133,15 @@ c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){ return w[n];},m.exports,m);w[n]=m.exports;};})('font-fit',this)); },{}],2:[function(require,module,exports){ -;(function(define){define(function(require,exports,module){ -'use strict'; - +/* jshint node:true */ +/* globals define */ +;(function(define){'use strict';define(function(require,exports,module){ /** * Locals */ -var textContent = Object.getOwnPropertyDescriptor(Node.prototype, 'textContent'); +var textContent = Object.getOwnPropertyDescriptor(Node.prototype, + 'textContent'); var innerHTML = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML'); var removeAttribute = Element.prototype.removeAttribute; var setAttribute = Element.prototype.setAttribute; @@ -179,7 +180,7 @@ exports.register = function(name, props) { // Merge base getter/setter attributes with the user's, // then define the property descriptors on the prototype. - var descriptors = Object.assign(props.attrs || {}, base.descriptors); + var descriptors = mixin(props.attrs || {}, base.descriptors); // Store the orginal descriptors somewhere // a little more private and delete the original @@ -294,7 +295,9 @@ var base = { if (this.lightStyle) { this.appendChild(this.lightStyle); } }, - get: textContent.get + get: function() { + return textContent.get(); + } }, innerHTML: { @@ -327,9 +330,8 @@ var defaultPrototype = createProto(HTMLElement.prototype, base.properties); function getBaseProto(proto) { if (!proto) { return defaultPrototype; } proto = proto.prototype || proto; - return !proto.GaiaComponent - ? createProto(proto, base.properties) - : proto; + return !proto.GaiaComponent ? + createProto(proto, base.properties) : proto; } /** @@ -341,7 +343,7 @@ function getBaseProto(proto) { * @return {Object} */ function createProto(proto, props) { - return Object.assign(Object.create(proto), props); + return mixin(Object.create(proto), props); } /** @@ -419,11 +421,11 @@ function processCss(template, name) { * @param {String} css */ function injectGlobalCss(css) { - if (!css) return; + if (!css) {return;} var style = document.createElement('style'); style.innerHTML = css.trim(); - headReady().then(() => { - document.head.appendChild(style) + headReady().then(function() { + document.head.appendChild(style); }); } @@ -434,7 +436,7 @@ function injectGlobalCss(css) { * @private */ function headReady() { - return new Promise(resolve => { + return new Promise(function(resolve) { if (document.head) { return resolve(); } window.addEventListener('load', function fn() { window.removeEventListener('load', fn); @@ -514,6 +516,22 @@ function addDirObserver() { } } +/** + * Copy the values of all properties from + * source object `target` to a target object `source`. + * It will return the target object. + * + * @param {Object} target + * @param {Object} source + * @returns {Object} + */ +function mixin(target, source) { + for (var key in source) { + target[key] = source[key]; + } + return target; +} + });})(typeof define=='function'&&define.amd?define :(function(n,w){'use strict';return typeof module=='object'?function(c){ c(require,exports,module);}:function(c){var m={exports:{}};c(function(n){ @@ -1158,7 +1176,7 @@ module.exports = component.register('gaia-header', { if (action === this._action) { return; } this.setAttr('action', action); this._action = action; - }, + } }, titleStart: { diff --git a/gaia-header.js b/gaia-header.js index a5da940..ea40cfa 100644 --- a/gaia-header.js +++ b/gaia-header.js @@ -596,7 +596,7 @@ module.exports = component.register('gaia-header', { if (action === this._action) { return; } this.setAttr('action', action); this._action = action; - }, + } }, titleStart: { diff --git a/package.json b/package.json index c257a09..17f2aa2 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "license": "MIT", "main": "gaia-header.js", "devDependencies": { + "babel": "^5.1.13", "bower": "^1.3.5", "browserify": "^5.11.2", "karma": "^0.12.16", @@ -17,7 +18,9 @@ "install": "bower install", "test": "./node_modules/karma/bin/karma start test/karma.conf.js --single-run", "test-dev": "./node_modules/karma/bin/karma start test/karma.conf.js", - "build": "mkdir -p dist && browserify gaia-header.js --outfile ./dist/gaia-header.js --standalone GaiaHeader" + "bundle": "mkdir -p dist && browserify gaia-header.js --outfile ./dist/gaia-header.js --standalone GaiaHeader", + "es5": "mkdir -p dist && babel ./dist/gaia-header.js --out-file ./dist/gaia-header-es5.js", + "build": "npm run bundle && npm run es5" }, "repository": { "type": "git",