diff --git a/src/app.html b/src/app.html index 77cd551892..76728518e4 100644 --- a/src/app.html +++ b/src/app.html @@ -19,7 +19,6 @@ -
diff --git a/src/index.html b/src/index.html index fc3e2295a7..68fcf9cf32 100644 --- a/src/index.html +++ b/src/index.html @@ -9,6 +9,7 @@ Firefox Marketplace + @@ -36,7 +37,6 @@ - @@ -46,6 +46,7 @@ + @@ -55,7 +56,6 @@ -
diff --git a/src/media/css/header.styl b/src/media/css/header.styl index 09d3dc3360..6ff2b1bbe7 100644 --- a/src/media/css/header.styl +++ b/src/media/css/header.styl @@ -1,6 +1,12 @@ // styles for the global site header only. @import 'lib'; +// Add the header's height back as padding. This is overriden when the header +// is not `position: fixed`. +body { + padding-top: $header-height; +} + // ***************** Wide Mobile (600+ px) // usable on any header @@ -9,8 +15,8 @@ border-top: 1px solid $salt-flat-white; border-bottom: 1px solid $seavan-salt-white; display-flex(); - height: 48px; - max-height: 48px; + height: $header-height; + max-height: $header-height; position: relative; width: 100%; @@ -25,7 +31,7 @@ color: $castle-skull-gray; font-size: 18px; font-weight: 400; - line-height: 48px; + line-height: $header-height; z-index: 10; } } @@ -45,7 +51,7 @@ display: block; } > nav { - height: 48px; + height: $header-height; display-flex(); position: relative; text-align: left; @@ -56,7 +62,7 @@ } } .site { - height: 48px; + height: $header-height; margin: 0 5px; width: 200px; @@ -197,10 +203,10 @@ .header-button { color: $castle-skull-gray; display: block; - height: 48px; - line-height: 48px; + height: $header-height; + line-height: $header-height; text-align: center; - width: 48px; + width: $header-height; // $header-height so it's square. // header buttons with icons &.icon { @@ -425,15 +431,7 @@ flex(1); } } - -[data-page-type~=root] { - main { - padding-top: 48px; - } -} main { - padding-top: 38px; - &:before { // This makes a sweet gradient below the header // (which will appear when scrolling past the category drop-down menu). @@ -441,7 +439,7 @@ main { display: block; gradientLinear(unquote('to top, rgba(15,15,15,0) 0%, rgba(15,15,15,.3) 100%')); height: 2px; - top: 48px; + top: $header-height; position: fixed; width: 100%; z-index: 18; @@ -465,7 +463,7 @@ main { } } [data-page-type] main { - padding: 0 0 48px; + padding: 0 0 $header-height; &:before { display: none; @@ -477,6 +475,10 @@ main { // This probably shouldn't be media query based. @media $wider-desktop { + body { + padding-top: 0; + } + .header-button.settings { right: 0; top: 0; @@ -514,7 +516,7 @@ main { > nav { height: 60px; margin: 0 auto; - max-width: 1024px; + max-width: $max-site-width; } .site { margin: 0; @@ -594,13 +596,12 @@ main { } // ***************** Wide Mobile (> 671px) - @media $at-least-desktop { .secondary-header { background: none; border: 0; - height: 48px; - max-height: 48px; + height: $header-height; + max-height: $header-height; position: relative; &:before { diff --git a/src/media/css/incompatible.styl b/src/media/css/incompatible.styl deleted file mode 100644 index b63821f6b6..0000000000 --- a/src/media/css/incompatible.styl +++ /dev/null @@ -1,76 +0,0 @@ -@import 'lib'; - -#incompatibility-banner { - background: $grey-gardens-gray; - color: $cement-gray; - display: none; - margin-bottom: -48px; - margin-top: 48px; - overflow: hidden; - position: relative; - - &:before, - &:after { - content: ""; - display: block; - glow(rgba(0,0,0,0.85), 3px); - height: 1px; - position: relative; - top: -1px; - width: 100%; - } - &:after { - bottom: -1px; - top: auto; - } - a { - display: table-cell; - - &.close { - margin-top: -17px; - top: 50%; - } - } - p { - background: url(../img/logos/firefox-256.png) no-repeat; - background-size: 102px 96px; - margin: 0 auto; - max-width: $desktop-content; - min-height: 95px; - padding: 28px 60px 28px 110px; - position: relative; - text-align: left; - } -} - -@media $wider-desktop { - #incompatibility-banner { - margin-top: 0; - } -} - -@media $narrower-than-desktop { - #incompatibility-banner { - p { - background-size: 85px 80px; - min-height: 80px; - padding: 20px 60px 20px 90px; - } - } -} - -.show-incompatibility-banner { - #incompatibility-banner { - display: block; - } - main { - padding-top: 48px; - } -} - -@media $narrower-than-desktop { - .show-incompatibility-banner main { - padding-top: 0; - } -} - diff --git a/src/media/css/lib.styl b/src/media/css/lib.styl index 0f358e69b3..fc134e8cd4 100644 --- a/src/media/css/lib.styl +++ b/src/media/css/lib.styl @@ -1,6 +1,8 @@ // Widths $desktop-content = 690px; $desktop-ftr = 732px; +$header-height = 48px; +$max-site-width = 1024px; // Use these as @media $at-least-desktop {} $at-least-desktop = unquote('(min-width: 710px)'); diff --git a/src/media/css/navbar.styl b/src/media/css/navbar.styl index c1ee2d1a77..ab9b412488 100644 --- a/src/media/css/navbar.styl +++ b/src/media/css/navbar.styl @@ -28,9 +28,9 @@ body[data-page-type~=leaf], body[data-page-type~=search] { .site-nav { - // Slide up and down. + // Slide out of view. bottom: 50px; - margin-bottom: -40px; + margin-bottom: -50px; } } .site-nav { @@ -182,7 +182,6 @@ } .site-nav { background-color: #E0E0E0; - bottom: 38px; height: 50px; margin: 0 auto; z-index: 25; diff --git a/src/media/css/site-banner.styl b/src/media/css/site-banner.styl new file mode 100644 index 0000000000..dc9ec70082 --- /dev/null +++ b/src/media/css/site-banner.styl @@ -0,0 +1,52 @@ +@import 'lib'; + +.mkt-banner { + background: $grey-gardens-gray; + color: $cement-gray; + display: block; + overflow: hidden; + position: relative; + line-height: 1.3; + + .mkt-banner-content { + align-items: center; + background: url("../img/logos/firefox-64.png") no-repeat; + background-position: 0 50%; + background-size: 64px 60px; + display-flex(); + flex-line(); + margin: 0 auto; + max-width: $max-site-width; + min-height: 60px; + padding: 10px 40px 10px 70px; + position: relative; + text-align: left; + } + + .close { + margin-top: -17px; + top: 50%; + } +} + +.mkt-banner.mkt-banner-success { + background: #64be3c; + color: #fff; + + .mkt-banner-content { + font-size: 15px; + font-weight: 300; + } + + small { + display: block; + font-size: 12px; + padding-top: 5px; + } + + a { + color: #fff; + font-weight: 500; + text-decoration: underline; + } +} diff --git a/src/media/css/site.styl b/src/media/css/site.styl index 028baf5f81..3eb8786bfd 100644 --- a/src/media/css/site.styl +++ b/src/media/css/site.styl @@ -98,13 +98,6 @@ body { position: relative; } -@media $small-desktop { - nav.site-nav { - // Account for header height change. - margin-bottom: -38px; - } -} - .row { clear: both; } diff --git a/src/media/img/logos/firefox-64.png b/src/media/img/logos/firefox-64.png new file mode 100644 index 0000000000..136171e1db Binary files /dev/null and b/src/media/img/logos/firefox-64.png differ diff --git a/src/media/js/components.js b/src/media/js/components.js new file mode 100644 index 0000000000..598c12ae26 --- /dev/null +++ b/src/media/js/components.js @@ -0,0 +1,154 @@ +define('components', ['document-register-element'], function () { + // Abstract element with attribute -> class mappings. + var MktHTMLElement = function () {}; + MktHTMLElement.prototype = Object.create(HTMLElement.prototype, { + attributeChangedCallback: { + value: function (name, previousValue, value) { + // Handle setting classes based on attributeClasses. + if (this.attributeClasses.hasOwnProperty(name)) { + var className = this.attributeClasses[name]; + if (value === null) { + this.classList.remove(className); + } else { + this.classList.add(className); + } + } + }, + }, + attributeClasses: { + value: {}, + }, + createdCallback: { + value: function () { + var self = this; + Object.keys(this.attributeClasses).forEach(function (attr) { + var className = self.attributeClasses[attr]; + if (self.hasAttribute(attr) && className) { + self.classList.add(className); + } + self.__defineGetter__(attr, function () { + // Treat `foo=""` as `foo=true`. + return self.getAttribute(attr) || + self.hasAttribute(attr); + }); + self.__defineSetter__(attr, function (value) { + if (value === null || value === false) { + self.removeAttribute(attr); + } else { + self.setAttribute(attr, value || true); + } + }); + }); + }, + }, + }); + + var MktBanner = document.registerElement('mkt-banner', { + prototype: Object.create(MktHTMLElement.prototype, { + attributeClasses: { + value: { + success: 'mkt-banner-success', + dismiss: null, + }, + }, + createdCallback: { + value: function () { + MktHTMLElement.prototype.createdCallback.call(this); + this.classList.add('mkt-banner'); + + if (this.rememberDismissal && this.dismissed) { + this.dismissBanner(); + } + + // Format the initial HTML. + this.html(this.innerHTML); + }, + }, + html: { + value: function (html) { + var self = this; + + var content = document.createElement('div'); + content.classList.add('mkt-banner-content'); + + // Wrap the provided content in a span so it gets flexed as + // one element. + var contentSpan = document.createElement('span'); + contentSpan.innerHTML = html; + content.appendChild(contentSpan); + + if (!this.undismissable) { + var closeButton = document.createElement('a'); + closeButton.classList.add('close'); + closeButton.href = '#'; + closeButton.textContent = gettext('Close'); + closeButton.title = gettext('Close'); + closeButton.addEventListener('click', function (e) { + e.preventDefault(); + self.dismissBanner(); + }); + content.appendChild(closeButton); + } + + this.innerHTML = ''; + this.appendChild(content); + }, + }, + dismissed: { + get: function () { + return this.storage.getItem(this.storageKey); + }, + }, + dismissBanner: { + value: function () { + if (this.rememberDismissal) { + this.storage.setItem(this.storageKey, true); + } + this.parentNode.removeChild(this); + }, + }, + rememberDismissal: { + get: function () { + return this.dismiss === 'remember'; + }, + }, + storage: { + get: function () { + return require('storage'); + }, + }, + storageKey: { + get: function () { + return 'hide_' + this.id.replace(/-/g, '_'); + }, + }, + undismissable: { + get: function () { + return this.dismiss === 'off'; + }, + }, + }), + }); + + var MktLogin = document.registerElement('mkt-login', { + prototype: Object.create(MktHTMLElement.prototype, { + createdCallback: { + value: function () { + if (this.isLink) { + var link = document.createElement('a'); + link.href = '#'; + link.classList.add('persona'); + link.textContent = this.textContent; + this.innerHTML = ''; + this.appendChild(link); + } + }, + }, + isLink: { + get: function () { + return this.hasAttribute('link'); + }, + }, + }), + }); +}); diff --git a/src/media/js/lib/document-register-element.js b/src/media/js/lib/document-register-element.js new file mode 100644 index 0000000000..26e9b5c1f8 --- /dev/null +++ b/src/media/js/lib/document-register-element.js @@ -0,0 +1,504 @@ +/*! +Copyright (C) 2014 by WebReflection + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ +(function(window, document, Object, REGISTER_ELEMENT){'use strict'; + +// in case it's there or already patched +if (REGISTER_ELEMENT in document) return; + +// DO NOT USE THIS FILE DIRECTLY, IT WON'T WORK +// THIS IS A PROJECT BASED ON A BUILD SYSTEM +// THIS FILE IS JUST WRAPPED UP RESULTING IN +// build/document-register-element.js +// and its .max.js counter part + +var + // IE < 11 only + old WebKit for attributes + feature detection + EXPANDO_UID = '__' + REGISTER_ELEMENT + (Math.random() * 10e4 >> 0), + + // shortcuts and costants + EXTENDS = 'extends', + DOM_ATTR_MODIFIED = 'DOMAttrModified', + DOM_SUBTREE_MODIFIED = 'DOMSubtreeModified', + + // valid and invalid node names + validName = /^[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)+$/, + invalidNames = [ + 'ANNOTATION-XML', + 'COLOR-PROFILE', + 'FONT-FACE', + 'FONT-FACE-SRC', + 'FONT-FACE-URI', + 'FONT-FACE-FORMAT', + 'FONT-FACE-NAME', + 'MISSING-GLYPH' + ], + + // registered types and their prototypes + types = [], + protos = [], + + // to query subnodes + query = '', + + // html shortcut used to feature detect + documentElement = document.documentElement, + + // ES5 inline helpers || basic patches + indexOf = types.indexOf || function (v) { + for(var i = this.length; i-- && this[i] !== v;){} + return i; + }, + + // other helpers / shortcuts + OP = Object.prototype, + hOP = OP.hasOwnProperty, + iPO = OP.isPrototypeOf, + + defineProperty = Object.defineProperty, + gOPD = Object.getOwnPropertyDescriptor, + gOPN = Object.getOwnPropertyNames, + gPO = Object.getPrototypeOf, + sPO = Object.setPrototypeOf, + + hasProto = !!Object.__proto__, + + // used to create unique instances + create = Object.create || function Bridge(proto) { + // silly broken polyfill probably ever used but short enough to work + return proto ? ((Bridge.prototype = proto), new Bridge) : this; + }, + + // will set the prototype if possible + // or copy over all properties + setPrototype = sPO || ( + hasProto ? + function (o, p) { + o.__proto__ = p; + return o; + } : ( + (gOPN && gOPD) ? + (function(){ + function setProperties(o, p) { + for (var + key, + names = gOPN(p), + i = 0, length = names.length; + i < length; i++ + ) { + key = names[i]; + if (!hOP.call(o, key)) { + defineProperty(o, key, gOPD(p, key)); + } + } + } + return function (o, p) { + do { + setProperties(o, p); + } while (p = gPO(p)); + return o; + }; + }()) : + function (o, p) { + for (var key in p) { + o[key] = p[key]; + } + return o; + } + )), + + // based on setting prototype capability + // will check proto or the expando attribute + // in order to setup the node once + patchIfNotAlready = sPO || hasProto ? + function (node, proto) { + if (!iPO.call(proto, node)) { + setupNode(node, proto); + } + } : + function (node, proto) { + if (!node[EXPANDO_UID]) { + node[EXPANDO_UID] = Object(true); + setupNode(node, proto); + } + } + , + + // DOM shortcuts and helpers, if any + + MutationObserver = window.MutationObserver || + window.WebKitMutationObserver, + + HTMLElementPrototype = ( + window.HTMLElement || + window.Element || + window.Node + ).prototype, + + cloneNode = HTMLElementPrototype.cloneNode, + setAttribute = HTMLElementPrototype.setAttribute, + + // replaced later on + createElement = document.createElement, + + // shared observer for all attributes + attributesObserver = MutationObserver && { + attributes: true, + characterData: true, + attributeOldValue: true + }, + + // useful to detect only if there's no MutationObserver + DOMAttrModified = MutationObserver || function(e) { + doesNotSupportDOMAttrModified = false; + documentElement.removeEventListener( + DOM_ATTR_MODIFIED, + DOMAttrModified + ); + }, + + // internal flags + setListener = false, + doesNotSupportDOMAttrModified = true, + + // optionally defined later on + onSubtreeModified, + callDOMAttrModified, + getAttributesMirror, + observer +; + +if (!MutationObserver) { + documentElement.addEventListener(DOM_ATTR_MODIFIED, DOMAttrModified); + documentElement.setAttribute(EXPANDO_UID, 1); + documentElement.removeAttribute(EXPANDO_UID); + if (doesNotSupportDOMAttrModified) { + onSubtreeModified = function (e) { + var + node = this, + oldAttributes, + newAttributes, + key + ; + if (node === e.target) { + oldAttributes = node[EXPANDO_UID]; + node[EXPANDO_UID] = (newAttributes = getAttributesMirror(node)); + for (key in newAttributes) { + if (!(key in oldAttributes)) { + // attribute was added + return callDOMAttrModified( + 0, + node, + key, + oldAttributes[key], + newAttributes[key], + 'ADDITION' + ); + } else if (newAttributes[key] !== oldAttributes[key]) { + // attribute was changed + return callDOMAttrModified( + 1, + node, + key, + oldAttributes[key], + newAttributes[key], + 'MODIFICATION' + ); + } + } + // checking if it has been removed + for (key in oldAttributes) { + if (!(key in newAttributes)) { + // attribute removed + return callDOMAttrModified( + 2, + node, + key, + oldAttributes[key], + newAttributes[key], + 'REMOVAL' + ); + } + } + } + }; + callDOMAttrModified = function ( + attrChange, + currentTarget, + attrName, + prevValue, + newValue, + action + ) { + var e = { + attrChange: attrChange, + currentTarget: currentTarget, + attrName: attrName, + prevValue: prevValue, + newValue: newValue + }; + e[action] = attrChange; + onDOMAttrModified(e); + }; + getAttributesMirror = function (node) { + for (var + attr, name, + result = {}, + attributes = node.attributes, + i = 0, length = attributes.length; + i < length; i++ + ) { + attr = attributes[i]; + name = attr.name; + if (name !== 'setAttribute') { + result[name] = attr.value; + } + } + return result; + }; + } +} + +function loopAndVerify(list, action) { + for (var i = 0, length = list.length; i < length; i++) { + verifyAndSetupAndAction(list[i], action); + } +} + +function loopAndSetup(list) { + for (var i = 0, length = list.length, node; i < length; i++) { + node = list[i]; + setupNode(node, protos[getTypeIndex(node)]); + } +} + +function executeAction(action) { + return function (node) { + if (iPO.call(HTMLElementPrototype, node)) { + verifyAndSetupAndAction(node, action); + loopAndVerify( + node.querySelectorAll(query), + action + ); + } + }; +} + +function getTypeIndex(target) { + var is = target.getAttribute('is'); + return indexOf.call( + types, + is ? + is.toUpperCase() : + target.nodeName + ); +} + +function onDOMAttrModified(e) { + var + node = e.currentTarget, + attrChange = e.attrChange, + prevValue = e.prevValue, + newValue = e.newValue + ; + if (node.attributeChangedCallback && + e.attrName !== 'style') { + node.attributeChangedCallback( + e.attrName, + attrChange === e.ADDITION ? null : prevValue, + attrChange === e.REMOVAL ? null : newValue + ); + } +} + +function onDOMNode(action) { + var executor = executeAction(action); + return function (e) { + executor(e.target); + }; +} + +function patchedSetAttribute(name, value) { + var self = this; + setAttribute.call(self, name, value); + onSubtreeModified.call(self, {target: self}); +} + +function setupNode(node, proto) { + setPrototype(node, proto); + if (observer) { + observer.observe(node, attributesObserver); + } else { + if (doesNotSupportDOMAttrModified) { + node.setAttribute = patchedSetAttribute; + node[EXPANDO_UID] = getAttributesMirror(node); + node.addEventListener(DOM_SUBTREE_MODIFIED, onSubtreeModified); + } + node.addEventListener(DOM_ATTR_MODIFIED, onDOMAttrModified); + } + if (node.createdCallback) { + node.created = true; + node.createdCallback(); + node.created = false; + } +} + +function verifyAndSetupAndAction(node, action) { + var + fn, + i = getTypeIndex(node), + attached = 'attached', + detached = 'detached' + ; + if (-1 < i) { + patchIfNotAlready(node, protos[i]); + i = 0; + if (action === attached && !node[attached]) { + node[detached] = false; + node[attached] = true; + i = 1; + } else if (action === detached && !node[detached]) { + node[attached] = false; + node[detached] = true; + i = 1; + } + if (i && (fn = node[action + 'Callback'])) fn.call(node); + } +} + +// set as enumerable, writable and configurable +document[REGISTER_ELEMENT] = function registerElement(type, options) { + upperType = type.toUpperCase(); + if (!setListener) { + // only first time document.registerElement is used + // we need to set this listener + // setting it by default might slow down for no reason + setListener = true; + if (MutationObserver) { + observer = (function(attached, detached){ + function checkEmAll(list, callback) { + for (var i = 0, length = list.length; i < length; callback(list[i++])){} + } + return new MutationObserver(function (records) { + for (var + current, node, + i = 0, length = records.length; i < length; i++ + ) { + current = records[i]; + if (current.type === 'childList') { + checkEmAll(current.addedNodes, attached); + checkEmAll(current.removedNodes, detached); + } else { + node = current.target; + if (node.attributeChangedCallback && + current.attributeName !== 'style') { + node.attributeChangedCallback( + current.attributeName, + current.oldValue, + node.getAttribute(current.attributeName) + ); + } + } + } + }); + }(executeAction('attached'), executeAction('detached'))); + observer.observe( + document, + { + childList: true, + subtree: true + } + ); + } else { + document.addEventListener('DOMNodeInserted', onDOMNode('attached')); + document.addEventListener('DOMNodeRemoved', onDOMNode('detached')); + } + + document.addEventListener('readystatechange', function (e) { + loopAndVerify( + document.querySelectorAll(query), + 'attached' + ); + }); + + document.createElement = function (localName, typeExtension) { + var i, node = createElement.apply(document, arguments); + if (typeExtension) { + node.setAttribute('is', localName = typeExtension.toLowerCase()); + } + i = indexOf.call(types, localName.toUpperCase()); + if (-1 < i) setupNode(node, protos[i]); + return node; + }; + + HTMLElementPrototype.cloneNode = function (deep) { + var + node = cloneNode.call(this, !!deep), + i = getTypeIndex(node) + ; + if (-1 < i) setupNode(node, protos[i]); + if (deep) loopAndSetup(node.querySelectorAll(query)); + return node; + }; + } + + if (-1 < indexOf.call(types, upperType)) { + throw new Error('A ' + type + ' type is already registered'); + } + + if (!validName.test(upperType) || -1 < indexOf.call(invalidNames, upperType)) { + throw new Error('The type ' + type + ' is invalid'); + } + + var + constructor = function () { + return document.createElement(nodeName, extending && upperType); + }, + opt = options || OP, + extending = hOP.call(opt, EXTENDS), + nodeName = extending ? options[EXTENDS] : upperType, + i = types.push(upperType) - 1, + upperType + ; + + query = query.concat( + query.length ? ',' : '', + extending ? nodeName + '[is="' + type.toLowerCase() + '"]' : nodeName + ); + + constructor.prototype = ( + protos[i] = hOP.call(opt, 'prototype') ? + opt.prototype : + create(HTMLElementPrototype) + ); + + loopAndVerify( + document.querySelectorAll(query), + 'attached' + ); + + return constructor; +}; + + +}(window, document, Object, 'registerElement')); +define('document-register-element', [], function () {}); diff --git a/src/media/js/main.js b/src/media/js/main.js index 46743611e3..ea3dd18fc3 100644 --- a/src/media/js/main.js +++ b/src/media/js/main.js @@ -17,6 +17,7 @@ require.config({ 'settings': ['settings_local', 'settings'], 'format': 'lib/format', 'hammerjs': 'hammer', + 'document-register-element': 'lib/document-register-element', }, }); @@ -30,6 +31,7 @@ define( 'apps_buttons', 'cache', 'capabilities', + 'components', 'consumer_info', 'mobilenetwork', 'content-ratings', @@ -67,6 +69,7 @@ function(_) { var apps = require('apps'); var buttons = require('apps_buttons'); var capabilities = require('capabilities'); + var consumer_info = require('consumer_info'); var format = require('format'); var $ = require('jquery'); var settings = require('settings'); @@ -170,7 +173,7 @@ function(_) { // Refresh list of installed apps in case user uninstalled apps // and switched back. if (require('user').logged_in()) { - require('consumer_info').fetch(true); + consumer_info.fetch(true); } apps.getInstalled().done(buttons.mark_btns_as_uninstalled); } @@ -186,16 +189,28 @@ function(_) { $('#site-footer').html( nunjucks.env.render('footer.html', context)); - if (!navigator.mozApps && - !navigator.userAgent.match(/googlebot/i) && - !require('storage').getItem('hide_incompatibility_banner')) { + if (!window['incompatibility-banner'] && + !navigator.mozApps && + !navigator.userAgent.match(/googlebot/i)) { console.log('Adding incompatibility banner'); - $('#incompatibility-banner').html( - nunjucks.env.render('incompatible.html')); - z.body.addClass('show-incompatibility-banner'); + $('#site-nav').after(nunjucks.env.render('incompatible.html')); } - z.body.toggleClass('logged-in', require('user').logged_in()); + var logged_in = require('user').logged_in(); + + // Wait for the switches to be pulled down. + consumer_info.promise.then(function () { + var fxAccountsMigration = settings.switches.indexOf( + 'fx-accounts-migration') !== -1; + + if (fxAccountsMigration && !window['fx-accounts-banner']) { + $('#site-nav').after( + nunjucks.env.render('fx-accounts-banner.html', + {logged_in: logged_in})); + } + }); + + z.body.toggleClass('logged-in', logged_in); z.page.trigger('reloaded_chrome'); }).trigger('reload_chrome'); @@ -209,13 +224,6 @@ function(_) { require('navigation').back(); }); - z.body.on('click', '#incompatibility-banner .close', function(e) { - e.preventDefault(); - console.log('Hiding incompatibility banner'); - z.body.removeClass('show-incompatibility-banner'); - require('storage').setItem('hide_incompatibility_banner', true); - }); - var ImageDeferrer = require('image-deferrer'); var iconDeferrer = ImageDeferrer.Deferrer(100, null); var screenshotDeferrer = ImageDeferrer.Deferrer(null, 200); @@ -246,7 +254,7 @@ function(_) { false ); - require('consumer_info').promise.done(function() { + consumer_info.promise.done(function() { console.log('Triggering initial navigation'); if (!z.spaceheater) { z.page.trigger('navigate', [window.location.pathname + window.location.search]); diff --git a/src/server.html b/src/server.html index e523661a25..6ac3ff21b0 100644 --- a/src/server.html +++ b/src/server.html @@ -30,7 +30,6 @@ -
diff --git a/src/templates/fx-accounts-banner.html b/src/templates/fx-accounts-banner.html new file mode 100644 index 0000000000..cb1c3131a0 --- /dev/null +++ b/src/templates/fx-accounts-banner.html @@ -0,0 +1,15 @@ + + {{ _('Firefox Accounts has arrived!') }} + + {% if logged_in %} + {{ _("Transfer your account now. It's easy.") }} + {% else %} + {{ _("Simply sign in to transfer your account.") }} + {% endif %} + + + + {{ _('Learn more about Firefox Accounts.') }} + + + diff --git a/src/templates/incompatible.html b/src/templates/incompatible.html index 25e68971b1..8f4465c95d 100644 --- a/src/templates/incompatible.html +++ b/src/templates/incompatible.html @@ -1,5 +1,4 @@ -

- {{ _('You must use Firefox to install Firefox apps.') }} + + {{ _('You must use Firefox to install Firefox apps.') }} {{ _('Download.') }} - {{ _('Close') }} -

+