From 3611594e1a0cfcbf181aae80aa87ca0d18218594 Mon Sep 17 00:00:00 2001 From: Shane Tomlinson Date: Wed, 16 Nov 2016 14:43:19 +0000 Subject: [PATCH] Persona end of life. * All frontend routes show a "Persona has shutdown... See more" message. * All wsapi requests return 410 (Gone). * All navigator.id calls are gutted except those that open the Persona EOL dialog. * Add tests in eol-tests to ensure all wsapi routes return 410. So long, and thanks for all the fish. --- eol-tests/wsapi-routes-test.js | 68 ++ eol-tests/wsapi-test.js | 71 +++ lib/static/views.js | 23 +- lib/wsapi.js | 477 +------------- resources/static/common/css/style.css | 560 +---------------- resources/static/include_js/_include.js | 547 +--------------- resources/static/pages/css/style.css | 790 +----------------------- resources/views/layout.ejs | 47 +- scripts/create_include.js | 2 - scripts/run_locally.js | 9 +- scripts/test | 46 +- scripts/test_backend | 3 +- 12 files changed, 235 insertions(+), 2408 deletions(-) create mode 100755 eol-tests/wsapi-routes-test.js create mode 100755 eol-tests/wsapi-test.js diff --git a/eol-tests/wsapi-routes-test.js b/eol-tests/wsapi-routes-test.js new file mode 100755 index 000000000..c0c5bb81f --- /dev/null +++ b/eol-tests/wsapi-routes-test.js @@ -0,0 +1,68 @@ +#!/usr/bin/env node + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +require('../tests/lib/test_env.js'); + +const assert = require('assert'); +const http = require('http'); +const vows = require('vows'); +const start_stop = require('../tests/lib/start-stop.js'); +const wsapi = require('../lib/wsapi.js'); + +const WSAPI_PREFIX = '/wsapi/'; +const allAPIs = wsapi.allAPIs(); + +var suite = vows.describe('wsapi routes'); + +// disable vows (often flakey?) async error behavior +suite.options.error = false; + +start_stop.addStartupBatches(suite); + +const batch = {}; + +Object.keys(allAPIs).forEach(function (apiName) { + const API = allAPIs[apiName]; + addRouteTest(API.method, apiName, 410); +}); + +addRouteTest('get', 'non-existent', 404); +addRouteTest('post', 'non-existent', 404); + +suite.addBatch(batch); + +function addRouteTest (method, pathname, expectedStatus) { + batch[method + ': ' + pathname] = { + topic: function () { + makeRequest(method, pathname, this.callback); + }, + + 'returns the expected status': function (res) { + assert.equal(res.statusCode, expectedStatus); + } + }; +} + +function makeRequest(method, pathname, done) { + var req = http.request({ + host: '127.0.0.1', + port: '10002', + path: WSAPI_PREFIX + pathname, + agent: false, + method: method.toUpperCase() + }, function (res) { + res.on('end', done(res)); + }); + + req.end(); +} + +start_stop.addShutdownBatches(suite); + +// run or export the suite. +if (process.argv[1] === __filename) suite.run(); +else suite.export(module); diff --git a/eol-tests/wsapi-test.js b/eol-tests/wsapi-test.js new file mode 100755 index 000000000..d6fa806c3 --- /dev/null +++ b/eol-tests/wsapi-test.js @@ -0,0 +1,71 @@ +#!/usr/bin/env node + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +require('../tests/lib/test_env.js'); + +const assert = require('assert'); +const vows = require('vows'); +const start_stop = require('../tests/lib/start-stop.js'); +const wsapi = require('../lib/wsapi.js'); + +var suite = vows.describe('wsapi'); + +// disable vows (often flakey?) async error behavior +suite.options.error = false; + +start_stop.addStartupBatches(suite); + +suite.addBatch({ + 'allAPIs': { + topic: function() { + return wsapi.allAPIs(); + }, + + 'works': function(allAPIs) { + assert.equal(typeof allAPIs, 'object'); + assert.equal(Object.keys(allAPIs).length, 38); + } + } +}); + +var appMock; +suite.addBatch({ + 'routeSetup': { + topic: function() { + appMock = { + getCount: 0, + postCount: 0, + routeCount: 0, + + get: function (route, callback) { + this.getCount++; + this.routeCount++; + }, + + post: function () { + this.postCount++; + this.routeCount++; + } + }; + + wsapi.routeSetup(appMock); + return true; + }, + + 'sets up the appropriate number of routes': function () { + assert.equal(appMock.getCount, 16); + assert.equal(appMock.postCount, 22); + assert.equal(appMock.routeCount, 38); + } + } +}); + +start_stop.addShutdownBatches(suite); + +// run or export the suite. +if (process.argv[1] === __filename) suite.run(); +else suite.export(module); diff --git a/lib/static/views.js b/lib/static/views.js index ae275ed58..646160feb 100644 --- a/lib/static/views.js +++ b/lib/static/views.js @@ -209,7 +209,7 @@ exports.setup = function(app) { app.get('/sign_in', function(req, res) { renderCachableView(req, res, 'dialog.ejs', { title: _('A Better Way to Sign In'), - layout: 'dialog_layout.ejs', + layout: 'layout.ejs', useJavascript: true, measureDomLoading: config.get('measure_dom_loading'), production: config.get('use_minified_resources'), @@ -219,7 +219,8 @@ exports.setup = function(app) { app.get('/communication_iframe', function(req, res) { renderCachableView(req, res, 'communication_iframe.ejs', { - layout: false, + title: _('Persona communication iframe'), + layout: 'layout.ejs', production: config.get('use_minified_resources') }); }); @@ -227,7 +228,7 @@ exports.setup = function(app) { app.get("/unsupported_dialog", function(req,res) { renderCachableView(req, res, 'unsupported_dialog.ejs', { title: _('Unsupported Browser'), - layout: 'dialog_layout.ejs', + layout: 'layout.ejs', useJavascript: false, // without the javascript bundle, there is no point in measuring the // window opened time. @@ -239,7 +240,7 @@ exports.setup = function(app) { app.get("/unsupported_dialog_without_watch", function(req,res) { renderCachableView(req, res, 'unsupported_dialog_without_watch.ejs', { title: _('Unsupported Browser without Watch'), - layout: 'dialog_layout.ejs', + layout: 'layout.ejs', useJavascript: false, // without the javascript bundle, there is no point in measuring the // window opened time. @@ -251,7 +252,7 @@ exports.setup = function(app) { app.get("/cookies_disabled", function(req,res) { renderCachableView(req, res, 'cookies_disabled.ejs', { title: _('Cookies Are Disabled'), - layout: 'dialog_layout.ejs', + layout: 'layout.ejs', useJavascript: false, // without the javascript bundle, there is no point in measuring the // window opened time. @@ -263,23 +264,25 @@ exports.setup = function(app) { // Used for a relay page for communication. app.get("/relay", function(req, res) { renderCachableView(req, res, 'relay.ejs', { - layout: false, - production: config.get('use_minified_resources') + layout: 'layout.ejs', + production: config.get('use_minified_resources'), + title: _('Persona relay page') }); }); // Native IdP Support app.get('/provision', function(req, res) { renderCachableView(req, res, 'provision.ejs', { - layout: false, - production: config.get('use_minified_resources') + layout: 'layout.ejs', + production: config.get('use_minified_resources'), + title: _('Persona provisioning page') }); }); app.get('/auth', function(req, res) { renderCachableView(req, res, 'dialog.ejs', { title: _('A Better Way to Sign In'), - layout: 'authenticate_layout.ejs', + layout: 'layout.ejs', useJavascript: true, measureDomLoading: config.get('measure_dom_loading'), production: config.get('use_minified_resources'), diff --git a/lib/wsapi.js b/lib/wsapi.js index 06cf85d99..b6bc3538b 100644 --- a/lib/wsapi.js +++ b/lib/wsapi.js @@ -2,230 +2,14 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -// an abstraction that implements all of the cookie handling, CSRF protection, -// etc of the wsapi. This module also routes request to the approriate handlers -// underneath wsapi/ -// -// each handler under wsapi/ supports the following exports: -// exports.process - function(req, res) - process a request -// exports.writes_db - must be true if the processing causes a database write -// exports.method - either 'get' or 'post' -// exports.authed - whether the wsapi requires authentication -// exports.args - an array of arguments that should be verified -// exports.i18n - boolean, does this operation display user facing strings +const fs = require('fs'); +const path = require('path'); -const -sessions = require('client-sessions'), -express = require('express'), -secrets = require('./secrets'), -config = require('./configuration'), -logger = require('./logging/logging.js').logger, -httputils = require('./httputils.js'), -forward = require('./http_forward.js').forward, -url = require('url'), -fs = require('fs'), -path = require('path'), -validate = require('./validate'), -version = require('./version.js'), -bcrypt = require('./bcrypt'), -i18n = require('i18n-abide'), -i18n_check = require('./i18n_client_check'), -db = require('./db'), -http = require('http'), -https = require('https'); +const WSAPI_PREFIX = '/wsapi/'; -var abide = i18n.abide({ - supported_languages: config.get('supported_languages'), - default_lang: config.get('default_lang'), - translation_directory: config.get('translation_directory'), - disable_locale_check: config.get('disable_locale_check') -}); - -i18n_check(); - -const COOKIE_SECRET = secrets.hydrateSecret('browserid_cookie', config.get('var_path')); -var COOKIE_KEY = 'browserid_state'; - -// to support testing of browserid, we'll add a hash fragment to the cookie name for -// sites other than login.persona.org. This is to address a bug in IE, see issue #296 -if (config.get('public_url').indexOf('https://login.persona.org') !== 0) { - const crypto = require('crypto'); - var hash = crypto.createHash('md5'); - hash.update(config.get('public_url')); - COOKIE_KEY += "_" + hash.digest('hex').slice(0, 6); -} - -const WSAPI_PREFIX = '/wsapi'; - -logger.info('session cookie name is: ' + COOKIE_KEY); - -function clearAuthenticatedUser(session) { - session.reset(['csrf']); -} - -function isAuthed(req, requiredLevel) { - if (req.session && req.session.userid && req.session.auth_level) { - // 'password' authentication allows access to all apis. - // 'assertion' authentication, grants access to only those apis - // that don't require 'password' - if (requiredLevel === 'assertion' || req.session.auth_level === 'password') { - return true; - } - } - return false; -} - -function bcryptPassword(password, cb) { - var startTime = new Date(); - bcrypt.encrypt(config.get('bcrypt_work_factor'), password, function() { - var reqTime = new Date() - startTime; - logger.info('bcrypt.encrypt_time', reqTime); - cb.apply(null, arguments); - }); -} - -function authenticateSession(options, cb) { - var session = options.session; - var uid = options.uid; - var level = options.level; - var duration_ms = options.duration_ms; - var unverified = options.unverified; - - // The caller should provide the timestamp when the password was - // last reset when it's available. When provided, this avoids a database - // read. This prevents us from performing a write against mysql master - // followed by a read against mysql slaves a couple ms later. This - // pattern would put a rediculously tight consistency requirement on - // our database deployment. - // - // See issue #3309 for more context - var lastReset = options.lastPasswordReset; - - if (['assertion', 'password'].indexOf(level) === -1) - cb(new Error("invalid authentication level: " + level)); - - function withPasswordReset(err, lastPasswordReset) { - if (err) - return cb(err); - if (lastPasswordReset === undefined) - return cb(new Error("authenticateSession called with undefined lastPasswordReset")); - // if the user is *already* authenticated as this uid with an equal or - // better level of auth, let's not lower them. Issue #1049 - if (session.userid === uid && session.auth_level === 'password' && - session.auth_level !== level) { - logger.info("not resetting cookies to 'assertion' authenticate a user who is already password authenticated"); - } else { - if (duration_ms) { - session.setDuration(duration_ms); - } - session.userid = uid; - session.auth_level = level; - session.lastPasswordReset = lastPasswordReset; - session.unverified = unverified; - } - cb(null); - } - - // if the client provided last reset timestamp, use that. otherwise, - // hit the database. - if (typeof lastReset === 'number') { - process.nextTick(function() { - withPasswordReset(null, lastReset); - }); - } else { - db.lastPasswordReset(uid, withPasswordReset); - } -} - -function checkCSRF(req, resp, next) { - // only on POSTs - if (req.method !== "POST") - return next(); - - // there must be a session - if (req.session === undefined || typeof req.session.csrf !== 'string') { - logger.warn("POST calls to /wsapi require a cookie to be sent, this user may have cookies disabled"); - return httputils.forbidden(resp, "no cookie"); - } - - // and the token must match what is sent in the post body - if (!req.body || !req.session || !req.session.csrf || req.body.csrf !== req.session.csrf) { - // if any of these things are false, then we'll block the request - var b = req.body ? req.body.csrf : ""; - var s = req.session ? req.session.csrf : ""; - logger.warn("CSRF validation failure, token mismatch. got:" + b + " want:" + s); - return httputils.badRequest(resp, "CSRF violation"); - } - - // all good - next(); -} - -function checkCodeVersion(req, resp, next) { - version(function(expectedCodeVersion) { - var requestedCodeVersion = req.headers['browserid-git-sha']; - - if (requestedCodeVersion !== expectedCodeVersion) { - logger.warn("Code version mis-match: " + req.url + " expected: " + expectedCodeVersion + " received: " + requestedCodeVersion); - - logger.warn("wsapi_code_mismatch." + req.url); - } - - next(); - }); -} - -function checkExpiredSession(req, resp, next) { - // all requests (both GET and POST) must have a session - if (req.session === undefined) { - logger.warn("calls to /wsapi require a cookie to be sent, this user may have cookies disabled"); - return httputils.forbidden(resp, "no cookie"); - } - if (!req.session.userid) { - // not yet authenticated, so nothing to expire, avoid the DB fetch - return next(); - } - db.lastPasswordReset(req.session.userid, function(err, token) { - if (err) return databaseDown(resp, err); - // if token is 0 (or undefined), they haven't changed their password - // since the server was updated to use lastPasswordResets. Allow the - // session to pass, otherwise the server upgrade would gratuitously - // expire innocent sessions. - if (token && token !== req.session.lastPasswordReset) { - logger.warn("expired cookie (password changed since issued)"); - req.session.reset(); - } - next(); - }); -} - -function langContext(req) { - return { - lang: req.lang, - locale: req.locale, - gettext: req.gettext, - ngettext: req.ngettext, - format: req.format - }; -} - -function databaseDown(res, err) { - // For CEF, this is logged by the caller so params from the http - // request can be recorded. - logger.warn('database is down, cannot process request: ' + err); - httputils.serviceUnavailable(res, "database unavailable"); -} - -function operationFromURL (path) { - var purl = url.parse(path); - return purl.pathname.substr(1); // drop leading slash -} - -var APIs; -function allAPIs () { - if (APIs) return APIs; - - APIs = {}; +// Exported to help run tests. +exports.allAPIs = function () { + var APIs = {}; fs.readdirSync(path.join(__dirname, 'wsapi')).forEach(function (f) { // skip files that don't have a .js suffix or start with a dot @@ -239,244 +23,19 @@ function allAPIs () { return APIs; } -// common functions exported, for use by different api calls -exports.clearAuthenticatedUser = clearAuthenticatedUser; -exports.isAuthed = isAuthed; -exports.bcryptPassword = bcryptPassword; -exports.authenticateSession = authenticateSession; -exports.forwardWritesTo = undefined; -exports.langContext = langContext; -exports.databaseDown = databaseDown; +// Originally used by the router process to decide where to send +// the request, either db_reader or db_writer. Terminate here. +// Should be all that's necessary to block all wsapi requests. +exports.routeSetup = function (app) { + // Load all valid wsapi routes. + const APIs = exports.allAPIs(); -// Explicitly forward a request over HTTP to the dbwriter. This -// is only useful in a process that is not the dbwriter. -exports.requestToDBWriter = function(opts, cb) { - if (!exports.forwardWritesTo) { - throw new Error("cannot forward request to dbwriter, I don't know her"+ - "url"); - } - - if (!opts) opts = {}; - if (!opts.headers) opts.headers = {}; - if (opts.body) { - opts.headers['Content-Length'] = opts.body.length; - } - opts.method = (opts.method || "get").toUpperCase(); - - var m = exports.forwardWritesTo.scheme === 'http' ? http : https; - var req = m.request({ - host: exports.forwardWritesTo.host, - port: exports.forwardWritesTo.port, - path: opts.path, - method: opts.method || "GET", - headers: opts.headers, - rejectUnauthorized: true, - agent: false - }, function(res) { - var respBody = ""; - res.on('data', function(chunk) { - respBody += chunk; - }); - res.on('end', function() { - try { - if (res.statusCode !== 200) throw "non-200 response: " + res.statusCode; - cb(null, { - headers: res.headers, - body: JSON.parse(respBody) - }); - cb = null; - } catch(e) { - if (cb) cb(e); - cb = null; - } + for (var apiName in APIs) { + var api = APIs[apiName]; + var pathname = WSAPI_PREFIX + apiName; + // only routes that were originally available are now gone. + app[api.method](pathname, function (req, res) { + res.status(410).json({}); }); - }).on('error', function(e) { - if (cb) cb(e); - cb = null; - }); - if (opts.body) { - req.write(opts.body); } - req.end(); -}; - -exports.setup = function(options, app) { - // If externally we're serving content over SSL we can enable things - // like strict transport security and change the way cookies are set - const overSSL = (config.get('scheme') === 'https'); - - // stash our forward-to url so different wsapi handlers can use it - exports.forwardWritesTo = options.forward_writes; - - - // cookie sessions are only applied to calls to /wsapi - // as all other resources can be aggressively cached - // by layers higher up based on cache control headers. - // the fallout is that all code that interacts with sessions - // should be under /wsapi - app.use(WSAPI_PREFIX, function(req, resp, next) { - // explicitly disallow caching on all /wsapi calls (issue #294) - resp.setHeader('Cache-Control', 'no-cache, max-age=0'); - - const operation = operationFromURL(req.url); - - // count the number of WSAPI operation - logger.info("wsapi." + operation); - - // check to see if the api is known here, before spending more time with - // the request. - if (!wsapis.hasOwnProperty(operation) || - wsapis[operation].method.toLowerCase() !== req.method.toLowerCase()) - { - // if the fake verification api is enabled (for load testing), - // then let this request fall through - if (operation !== 'fake_verification' || !process.env.BROWSERID_FAKE_VERIFICATION) - return httputils.badRequest(resp, "no such api"); - } - - next(); - }); - - app.use(WSAPI_PREFIX, express.cookieParser()); - app.use(WSAPI_PREFIX, express.bodyParser()); - - var cookieOpts = { - path: '/wsapi', - httpOnly: true, - maxAge: config.get('authentication_duration_ms') - }; - - if (overSSL) cookieOpts.secureProxy = true; - - app.use(WSAPI_PREFIX, sessions({ - secret: COOKIE_SECRET, - requestKey: 'session', - cookieName: COOKIE_KEY, - duration: config.get('authentication_duration_ms'), - cookie: cookieOpts - })); - app.use(WSAPI_PREFIX, checkExpiredSession); - app.use(WSAPI_PREFIX, checkCSRF); - - // load all of the APIs supported by this process - var wsapis = { }; - - function describeOperation(name, op) { - var str = " " + name + " ("; - str += op.method.toUpperCase() + " - "; - str += (op.authed ? "" : "not ") + "authed"; - if (op.args) { - var keys = Array.isArray(op.args) ? op.args : Object.keys(op.args); - str += " - " + keys.join(", "); - } - if (op.internal) str += ' - internal'; - str += ")"; - logger.debug(str); - } - - var all = allAPIs(); - Object.keys(all).forEach(function (operation) { - try { - var api = all[operation]; - - // - don't register read apis if we are configured as a writer, - // with the exception of ping which tests database connection health. - // - don't register write apis if we are not configured as a writer - if ((options.only_write_apis && !api.writes_db && operation !== 'ping') || - (!options.only_write_apis && api.writes_db)) - return; - - wsapis[operation] = api; - - // set up the argument validator - if (api.args) { - wsapis[operation].validate = validate(api.args); - } else { - wsapis[operation].validate = function(req,res,next) { next(); }; - } - - } catch(e) { - var msg = "error registering " + operation + " api: " + e; - logger.error(msg); - throw msg; - } - }); - - // debug output - all supported apis - logger.debug("WSAPIs:"); - Object.keys(wsapis).forEach(function(api) { - describeOperation(api, wsapis[api]); - }); - - app.use(WSAPI_PREFIX, function wsapiMiddleware(req, resp, next) { - const operation = operationFromURL(req.url); - - // the fake_verification wsapi is implemented elsewhere. - if (operation === 'fake_verification') return next(); - - // at this point, we *know* 'operation' is valid API, give checks performed - // above - - // does the request require authentication? - if (wsapis[operation].authed && !isAuthed(req, wsapis[operation].authed)) { - return httputils.badRequest(resp, "requires authentication"); - } - - // validate the arguments of the request - wsapis[operation].validate(req, resp, function() { - if (wsapis[operation].i18n) { - abide(req, resp, function () { - wsapis[operation].process(req, resp); - }); - } else { - wsapis[operation].process(req, resp); - } - }); - }); -}; - - -exports.routeSetup = function (app, options) { - var wsapis = allAPIs(); - - app.use(WSAPI_PREFIX, checkCodeVersion); - - app.use(WSAPI_PREFIX, function(req, resp, next) { - var operation = operationFromURL(req.url); - - // not a WSAPI request - if (!operation) return next(); - - var api = wsapis[operation]; - - // check to see if the api is known here, before spending more time with - // the request. - if (!wsapis.hasOwnProperty(operation) || - api.method.toLowerCase() !== req.method.toLowerCase()) { - // if the fake verification api is enabled (for load testing), - // then let this request fall through - if (operation !== 'fake_verification' || !process.env.BROWSERID_FAKE_VERIFICATION) - return httputils.badRequest(resp, "no such api"); - } - - if (api.internal) { - return httputils.notFound(resp); - } - - var destination_path = WSAPI_PREFIX + req.url; - var destination_url = (api.writes_db ? - options.write_url : options.read_url) + destination_path; - - var cb = function() { - forward( - destination_url, req, resp, - function(err) { - if (err) { - logger.error("error forwarding request:", err); - } - }); - }; - return express.bodyParser()(req, resp, cb); - - }); }; diff --git a/resources/static/common/css/style.css b/resources/static/common/css/style.css index b6fbbc8ff..320e098c3 100644 --- a/resources/static/common/css/style.css +++ b/resources/static/common/css/style.css @@ -19,8 +19,14 @@ html, body { body { font: 14px/21px 'Open Sans', "Lucida Sans", "Lucida Grande", "Lucida Sans Unicode", Verdana, sans-serif; overflow-y: auto; + background-color: #6a7b86; + background-image: url("/pages/i/marketplace-header.png"), url("/common/i/grain.png"); + background-position: center top, center top; + background-repeat: repeat-x, repeat; + color: #fff; } + /* for floats */ .cf:after { content: "."; @@ -39,40 +45,11 @@ html[xmlns] .cf { height: 1%; } -header { -} - -header, section, footer { - display: block; - width: 100%; -} - -footer { - color: #484848; - font-weight: 300; -} ul, li { list-style-type: none; } -strong { - font-weight: 700; -} - -.hidden { - /* This funkiness is so that labels still appear for screen readers */ - visibility: hidden; - display: block; - height: 0; -} - -.tooltip { - color: #a50022; - display: none; - margin: .5em 0; -} - a { color: #348fd0; text-decoration: none; @@ -84,533 +61,8 @@ a:hover { color: #000; } -input[type=text], -input[type=email], -input[type=password] { - width: 100%; - color: #383838; - font-size: 13px; - margin-top: 6px; - padding: 5px; - border-width: 1px; - border-style: solid; - border-color: #b2b2b2; - outline: none; - border-radius: 3px; - box-shadow: 1px 1px 0 rgba(255,255,255,0.5); -} - -input[type=text]:focus, -input[type=email]:focus, -input[type=password]:focus { - border: 1px solid #42a4e0; - box-shadow: 1px 1px 0 rgba(255,255,255,.5), 0 0 1px 3px rgba(73,173,227, .4); -} - -input[type=text].invalid, -input[type=email].invalid, -input[type=password].invalid { - border: 1px solid #a50022; - box-shadow: none; -} - -input[type=text]:disabled, -input[type=email]:disabled, -input[type=password]:disabled { - background-color: #f0f0f0; - color: #4f4f4f; - /* The opacity and -webkit-text-fill-color are to override mobile Safari's - * default stylings that make reading input elements very difficult. - * issue #1311 */ - -webkit-text-fill-color: #4f4f4f; - opacity: 1; - /* Remove the box-shadow and border-color that come with a focused input - * field */ - box-shadow: none; - border-color: #b2b2b2; -} - -/* - * All three browser types must be styled individually. - * See http://stackoverflow.com/questions/2610497/change-an-inputs-html5-placeholder-color-with-css - * issue #2187 - */ -input:-moz-placeholder { - color:#aaa; -} - -input:-ms-input-placeholder { - color:#aaa; -} - -input:-webkit-input-placeholder { - color:#aaa; -} - -label { - display: block; -} - -label + input[type=text], -label + input[type=password], -label + input[type=email] { - margin-top: 8px; -} - -label.hidden + input[type=text], -label.hidden + input[type=password], -label.hidden + input[type=email] { - margin-top: 0; -} - -input[type=radio], -input[type=checkbox] { - cursor: pointer; - margin-left: 2px; /* necessary or chrome cuts off part of the radio button */ -} - -button, -.button { - font-size: 14px; - font-weight: bold; - line-height: 14px; - /* The difference between top and bottom padding is to make up for the tiny - * offset that browsers use to display lowercase letters. - */ - padding: 6px 10px 7px; - float: right; - border: 0; - color: #fff; - text-shadow: 0 1px rgba(0,0,0,0.5); - cursor: pointer; - white-space: nowrap; - - border-radius: 3px; - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.3), 0 1px 0 rgba(0, 0, 0, 0.2); - - background-color: #4eb5e5; - background-image: -webkit-gradient(linear, left top, left bottom, from(#4eb5e5), to(#3196cf)); - background-image: -webkit-linear-gradient(top, #4eb5e5, #3196cf); - background-image: -moz-linear-gradient(top, #4eb5e5, #3196cf); - background-image: -ms-linear-gradient(top, #4eb5e5, #3196cf); - background-image: -o-linear-gradient(top, #4eb5e5, #3196cf); - background-image: linear-gradient(top, #4eb5e5, #3196cf); -} - -button:hover, -button:focus, -.button:hover, -.button:focus { - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.3), 0 1px 0 rgba(0, 0, 0, 0.2), 0 2px 0 rgba(0, 0, 0, 0.1); - - background-color: #4aafe5; - background-image: -webkit-gradient(linear, left top, left bottom, from(#4aafe5), to(#2c89c8)); - background-image: -webkit-linear-gradient(top, #4aafe5, #2c89c8); - background-image: -moz-linear-gradient(top, #4aafe5, #2c89c8); - background-image: -ms-linear-gradient(top, #4aafe5, #2c89c8); - background-image: -o-linear-gradient(top, #4aafe5, #2c89c8); - background-image: linear-gradient(top, #4aafe5, #2c89c8); -} - -button:focus, -.button:focus { - box-shadow: 0 0 1px #fff, 0 0 1px 3px #49ADE3; - box-shadow: 0 0 1px rgba(255, 255, 255, 0.5), 0 0 1px 3px rgba(73, 173, 227, 0.6); -} - -button:active, -.button:active { - background-color: #184a73; - background-image: -webkit-gradient(linear, left top, left bottom, from(#184a73), to(#276084)); - background-image: -webkit-linear-gradient(top, #184a73, #276084); - background-image: -moz-linear-gradient(top, #184a73, #276084); - background-image: -ms-linear-gradient(top, #184a73, #276084); - background-image: -o-linear-gradient(top, #184a73, #276084); - background-image: linear-gradient(top, #184a73, #276084); - color: #97b6ca; - text-shadow: 0 1px rgba(0,0,0,0.4); - box-shadow: inset 0 2px 1px rgba(0,0,0,0.3); -} - -button::-moz-focus-inner, .button::-moz-focus-inner { - padding: 0; - border: 0 -} - -.submit button { - padding: 6px 45px 7px 10px; - background-color: #4eb5e5; - background-image: url("/common/i/button-arrow.png"); - background-image: url("/common/i/button-arrow.png#iefix"), -webkit-gradient(linear, left top, left bottom, from(#4eb5e5), to(#3196cf)); - background-image: url("/common/i/button-arrow.png#iefix"), -webkit-linear-gradient(top, #4eb5e5, #3196cf); - background-image: url("/common/i/button-arrow.png#iefix"), -moz-linear-gradient(top, #4eb5e5, #3196cf); - background-image: url("/common/i/button-arrow.png#iefix"), -ms-linear-gradient(top, #4eb5e5, #3196cf); - background-image: url("/common/i/button-arrow.png#iefix"), -o-linear-gradient(top, #4eb5e5, #3196cf); - background-image: url("/common/i/button-arrow.png#iefix"), linear-gradient(top, #4eb5e5, #3196cf); - background-repeat: no-repeat, no-repeat; - background-position: center right, center; -} - -.submit button:hover, -.submit button:focus, -.submit .button:hover, -.submit .button:focus { - background-color: #4aafe5; - background-image: url("/common/i/button-arrow-hover.png"); - background-image: url("/common/i/button-arrow-hover.png#iefix"), -webkit-gradient(linear, left top, left bottom, from(#4aafe5), to(#2c89c8)); - background-image: url("/common/i/button-arrow-hover.png#iefix"), -webkit-linear-gradient(top, #4aafe5, #2c89c8); - background-image: url("/common/i/button-arrow-hover.png#iefix"), -moz-linear-gradient(top, #4aafe5, #2c89c8); - background-image: url("/common/i/button-arrow-hover.png#iefix"), -ms-linear-gradient(top, #4aafe5, #2c89c8); - background-image: url("/common/i/button-arrow-hover.png#iefix"), -o-linear-gradient(top, #4aafe5, #2c89c8); - background-image: url("/common/i/button-arrow-hover.png#iefix"), linear-gradient(top, #4aafe5, #2c89c8); -} - -.submit button:active, -.submit .button:active { - background-color: #184a73; - background-image: url("/common/i/button-arrow-active.png#iefix"), -webkit-gradient(linear, left top, left bottom, from(#184a73), to(#276084)); - background-image: url("/common/i/button-arrow-active.png#iefix"), -webkit-linear-gradient(top, #184a73, #276084); - background-image: url("/common/i/button-arrow-active.png#iefix"), -moz-linear-gradient(top, #184a73, #276084); - background-image: url("/common/i/button-arrow-active.png#iefix"), -ms-linear-gradient(top, #184a73, #276084); - background-image: url("/common/i/button-arrow-active.png#iefix"), -o-linear-gradient(top, #184a73, #276084); - background-image: url("/common/i/button-arrow-active.png#iefix"), linear-gradient(top, #184a73, #276084); -} - -/* Override all previously applied styles so that the button does not change - * styles even if the user hovers, focuses or clicks on the button. -*/ -button[disabled], -button[disabled]:hover, .button[disabled]:hover, -button[disabled]:focus, .button[disabled]:focus, -button[disabled]:active, .button[disabled]:active, -.submit_disabled button, .submit_disabled .button, -.submit_disabled button:hover, .submit_disabled .button:hover, -.submit_disabled button:focus, .submit_disabled .button:focus, -.submit_disabled button:active, .submit_disabled .button:active { - color: #d8dde0; - cursor: default; - background-color: #bcc4ca; - background-image: none; /* Fix for IE9 still showing the blue arrow */ - background-image: -webkit-gradient(linear, left top, left bottom, from(#bcc4ca), to(#a0a7ae)); - background-image: -webkit-linear-gradient(top, #bcc4ca, #a0a7ae); - background-image: -moz-linear-gradient(top, #bcc4ca, #a0a7ae); - background-image: -ms-linear-gradient(top, #bcc4ca, #a0a7ae); - background-image: -o-linear-gradient(top, #bcc4ca, #a0a7ae); - background-image: linear-gradient(top, #bcc4ca, #a0a7ae); - text-shadow: 0 1px #444, 0 0 2px #555; - -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; - opacity: .5; - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.3), 0 1px 0 rgba(0, 0, 0, 0.2); -} - -.submit_disabled .submit button, .submit_disabled .submit .button, -.submit_disabled .submit button:hover, .submit_disabled .submit .button:hover, -.submit_disabled .submit button:focus, .submit_disabled .submit .button:focus, -.submit_disabled .submit button:active, .submit_disabled .submit .button:active { - background-color: #4eb5e5; - background-image: url("/common/i/button-loader.gif#iefix"), -webkit-gradient(linear, left top, left bottom, from(#4eb5e5), to(#3196cf)); - background-image: url("/common/i/button-loader.gif#iefix"), -webkit-linear-gradient(top, #4eb5e5, #3196cf); - background-image: url("/common/i/button-loader.gif#iefix"), -moz-linear-gradient(top, #4eb5e5, #3196cf); - background-image: url("/common/i/button-loader.gif#iefix"), -ms-linear-gradient(top, #4eb5e5, #3196cf); - background-image: url("/common/i/button-loader.gif#iefix"), -o-linear-gradient(top, #4eb5e5, #3196cf); - background-image: url("/common/i/button-loader.gif#iefix"), linear-gradient(top, #4eb5e5, #3196cf); - background-position: 95% center; -} - -button.negative { - background-color: #d94f30; - background-image: -webkit-gradient(linear, left top, left bottom, from(#d94f30), to(#ad1804)); - background-image: -webkit-linear-gradient(top, #d94f30, #ad1804); - background-image: -moz-linear-gradient(top, #d94f30, #ad1804); - background-image: -ms-linear-gradient(top, #d94f30, #ad1804); - background-image: -o-linear-gradient(top, #d94f30, #ad1804); - background-image: linear-gradient(top, #d94f30, #ad1804); -} - -button.negative:hover, -button.negative:focus, -.button.negative:hover, -.button.negative:focus { - background-color: #e3653f; - background-image: -webkit-gradient(linear, left top, left bottom, from(#e3653f), to(#c01c03)); - background-image: -webkit-linear-gradient(top, #e3653f, #c01c03); - background-image: -moz-linear-gradient(top, #e3653f, #c01c03); - background-image: -ms-linear-gradient(top, #e3653f, #c01c03); - background-image: -o-linear-gradient(top, #e3653f, #c01c03); - background-image: linear-gradient(top, #e3653f, #c01c03); -} - -button.negative:active, -.button.negative:active { - box-shadow: 0 0 5px #333 inset; - color: #cfa391; - - background-color: #83311e; - background-image: -webkit-gradient(linear, left top, left bottom, from(#83311e), to(#670d01)); - background-image: -webkit-linear-gradient(top, #83311e, #670d01); - background-image: -moz-linear-gradient(top, #83311e, #670d01); - background-image: -ms-linear-gradient(top, #83311e, #670d01); - background-image: -o-linear-gradient(top, #83311e, #670d01); - background-image: linear-gradient(top, #83311e, #670d01); -} - - -.tospp { - line-height: 14px; -} - -.buttonrow { - line-height: 28px; -} - -.buttonrow > .right { - margin-right: 15px; -} - -.buttonrow > .right.emphasize { - margin-right: 0; - margin-top: 8px; -} - - -a.secondary[disabled], .submit_disabled a.secondary, .submit_disabled a.secondary:focus, .submit_disabled a.secondary:active { - color: #999; -} - -.right { - float: right; -} - -.left { - float: left; -} - -.center { - text-align: center; -} - -.headline-main, h1, h2, h3, h4 { - font-weight: normal; - text-shadow: 0px 1px 0px rgba(255,255,255,0.75); -} - -.headline-main { - font-size: 48px; - letter-spacing: -2px; - line-height: 100%; -} - -h1 { - font-size: 36px; - letter-spacing: -1.5px; - line-height: 100%; -} - -.white { - color: #fff; - text-shadow: 0px 1px 0px rgba(0,0,0,0.25); -} - -.thin { - font-weight: 300; -} - -h2 { - font-size: 32px; - letter-spacing: -1px; - line-height: 100%; -} - -h3 { - font-size: 28px; - letter-spacing: -0.5px; - line-height: 100%; -} - -h4 { - font-size: 24px; - letter-spacing: -0.25px; - line-height: 100%; -} - -.small, small { - font-size: 12px; - line-height: 100%; -} - -header ul li { - display: inline-block; -} footer ul li { display: inline-block; margin: 0 10px 0 0; } - -footer .help { - float: right; - cursor: help; -} - -.cancelVerify { - font-weight: bold; -} - -.message_screen { - background-color: #dadee1; -} - -.message_screen .contents { - max-width: 430px; - margin: 0 auto; -} - -.message_screen h2 { - font-size: 20px; -} - -.message_screen p { - margin-top: 20px; -} - -#error h2 { - font-size: 22px; -} - -#error .emphasis { - margin-top: 15px; - color: #aa1401; -} - -#error .contents, #wait .contents { - max-width: 430px; - margin: 0 auto; -} - -.openMoreInfo { - display: block; - margin-top: 15px; -} - -.moreInfo { - display: none; - color: #999; -} - -/** - * These animations are used for the loading spinner. No animated gifs here. - */ - -@-webkit-keyframes spin { - from { - -webkit-transform: rotate(0deg); - } - to { - -webkit-transform: rotate(365deg); - } -} -@-moz-keyframes spin { - from { - -moz-transform: rotate(0deg); - } - to { - -moz-transform: rotate(365deg); - } -} -@keyframes spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(365deg); - } -} - - -.loadingSpinner { - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAACC9JREFUeNrMWluMFEUUrdvz2DePXUEXlIeK+ADBiIqJoBJN1PhjVDAS/4zGV2KMH2o0fuivmphgQGPij8ag8qUmJpCYmOAmJqKofICIJiACLrqww+zOTHd5btetmZpmeqanZxbpyZ2q7umuvqfuo+69NbRg+5iKO7SQ7Qe2g6/ob6A5aG5B51r0l4EW4vwStINofbk3g7aMe46hPY7z/Wj3ov0W7a/KGbPZUXxo7VnXsqqzYwB0H2gjKXUrmJiV8LmlDeaMZ3QL6MM0jKQFMgx6Esw/Aw7mN7qB4mZXn31dm9tvFroG9FK7DHkpQDyO97IavKYEBEURJDxi1OhFXF+lHdWOUkdAMMAK8LgHtBWncxvxTe1jiQO2ZkYkghc8AeZ+Qne15ZJSs5tAOlqdUG2KJAmQt8DyO41m3AVDDWzDgVoGTSdRL5wfQLOz28b+Keh+l8GwjViy+xtc9DEiNaa1+g7XfsC1v9H+g58qopLspleAbgKtjXiwgxjoYbRn2gZCMRoCRrYxiOgMV/sU3hNKhVcVnO4CiK1ov8DlYov37pLZz6FZrw2YE6CvQROp3C81FvXLoMfkN19UkLSjLroG5it0n8P5vhTvL1tQnR6NVOsedq3CZFlAnKX26JxkJ4DudnUeHFEgfWDwTZnxKYDxAEZTTQieNoLYg/ZetH9qdX4cWa9erV4HLedwRuxBg+uATQB9j4xExtC/E+2kOo8OVyIcGjxNxrv4MvOhFkEk3M+xJ8L5OvFA5/RgXiqaSbdcR57HzXkyM83qFJAJeH20PaxGQLPp/wIxFWjVA26X92Waeq3VoM3AWpIHAwnkGEyPzMGz6B/KOG6upJOF3d0AkYNirx3MqoU5L0YiFN69Ed85dCesJIR49llKO9B+En2Y54YNR88wiDxesmEopy6ERZ/ydaxEGOJdquaafDZuMewByafeaLRusnR43GlZbLoPgmeX1O2zcuoCcBoHwgK5Ee2loFPa2IOnqTrJvXBXn6PdHTcAi9HDy85g5vwOIt84SawfyqoRAdFsbNaMVWhngwqiTr5nKFw/wOPHoXhiiG9iyQzgy1Odq1nVJsiAmA91Ot0ChHW/l4l3quj6IHYAX79LCpooH5gFMAUwUQrSA7GGfduQUafTgU4kZTxCF4FvRJsUiCRYvZj6Qb+ADpIga0UZAdMP8nV7UtBVF8uGnVXzAGIyIQgrkREy9hrYuJBqAd3+dpMjHoFnoIRgphgYtWsGgIU3FZZotLoca8QKUC8ll4Rr7Hlt3GxFBrcTzEB+S6MerGZzMx7aIATj16cHVTsq4YSBzMfasDSfUYt6zGJXaBOEzUfKZN4VOPkRCbhUthvIIMMwVFYPd3YtkDK+RvD7MjA/CtfHxl0MdPXZNLFWIMuAG3qwepVtTS5tDj6Nr7lgdgm7QEmMLfliT8MZYwtliaHSuu+skzz5EfX10wKxD42Ehl+zExdIWEeA3CeD7kQGWcmPA6taVM/PwjSSoNBGAMIzK3+lgYcLpH5KXQpxWCLHHQlUy7wSZy1uVxL88JDHIYGxAxvrxHmtbhWVeGU/SgZQRcgX4z+FdgloUZI1xHqr2aEkqGWsb/OLcvsLP8V5ykOOtH0HzASuLsCMrmwWopAMzXYwG9PBOUOQkCO+j2O0iq6v/reozXlxEtlHXIsyrtjHG3wyxICmQHc0k4SWFX3AM7FW0OaKHoYl8FhsSwmqhRkdKxFSP4LpA6Beq16qJhUurN1Apu7bEATrZL+XPmC0XE1r44IpXho2CvLiVIvv+0aZdLZiI2ABM8kZIvoPNAKSB/oeWR+CTgzVFrnYZrhs0+Aj0vDiklLPM0ztFO/V4xq9eLLj6G8ArXNBeDYro5rL7TR8V6oWTkTGAwCd1wkmg4PDL/HwkIzlGj6XhooY+VEIYB4nUWxMWY+a7XF0BChce7Sxt1DbtOq12HQT92tn+SM042SSvqobFsM/if4ol0a76PqTHn3alKJ0GKip1uWgP8DhZ8psq/lVD1aLxbDe0Gqcv6pM8flcHIOcFehahkA6JjD2Iova+wCwF8TlfxsVAwwJqSNSOnpBGzWcyWOO7AjbzWRquiBGFjdeaLdwXkTKumOyGaO1myO470q0r+B85QwAYHsYlR3iQNenFm3tIe4BvafMbJAYv3XHFceT8YueAj0CmtelAHYUnC4Gs33yHgugpV1mY+7YAUkwo5vx+2FlJGSCSaqWUifkWd51ulqZjf/vFe86JS+rklRwRlhVyTBdEJW26uRJ5VNJDY5igMSC/UAWw7t5+4zDorAyLyG/rbyQkQ6vuNdJIfyw0FFldqGmVK2GlxdHMSgA+lmVtCy+WnYBgMCTwrl18SQgdTsbPe6xjQwzm2TUCae4bdswGcR8FERqI1LQuEqyzLJsQZQESCCMlyWJnJTih1buzpiu7gLIghiKoqhiNlVb/vMBA+yAbP9iWyDjmk+IJOrASIzEJaWCk13aCCErTHLdu2TjOem7jNrZtxtKfJ6Rd5wR6aaSiD12kwn3HyTjqU5zvhKN62R/VHlyLsxpJ5UOmrjQ6PWM+CsGMKFapC7t/POBE7C30X0X7bgyW819yu5qRcAo1egPRDWGQ0PW4fs9XQs/+DynjQqGqoyL4ypB/pXmTzW8a/UzaA3efj3aBaJCPHNFC8ZtY2bfs7GnYx9lkfQ4781TGwlk2n8HFSX0H4M0eM/xCgZExhMNyu/W0KvrkOi7tZm8SGFa8p6TykTg42l2KTr9v1ZZpMPErvpiWRyHZYXuk2TIgtHyTEE8GQP4V6jYCSP/CTAAr5kcJEeom/4AAAAASUVORK5CYII='); - display: block; - margin: 0 auto 25px; - width: 50px; - height: 50px; - -webkit-animation: 0.9s spin infinite linear; - -moz-animation: 0.9s spin infinite linear; - animation: 0.9s spin infinite linear; -} - - -.submit { - margin-top: 10px; -} - -.submit > p { - margin-top: 10px; -} - -.tospp { - line-height: 1.2; -} - - -#showDevelopment { - position: absolute; - /** - * The TOS/PP agreement close button is in the upper right corner. To avoid - * interfering with it, push the development button down a bit. - */ - top: 50px; - right: 0; - width: 50px; - height: 50px; - cursor: default; - z-index: 99999; -} - -#development { - display: none; -} - -.development #development { - display: block; - position: absolute; - right: 0; - top: 10px; - z-index: 100000; - background-color: #000; - background-color: rgba(0,0,0, .75); - border-radius: 5px 0 0 5px; -} - -#development li { - display: block; - float: none; - padding: 5px 10px; -} - -#development li a { - color: #fff; -} - -#development input[type=text] { - padding: 3px 5px; -} - -.notice { - text-align: center; - background: #D62626; - border: 1px solid #8A241B; - padding: 5px; - padding-left: 15px; - padding-right: 15px; -} - -.notice p { - padding: 0.4em; -} - -.notice a { - color: #fff; - border-bottom: 1px dotted #fff; - font-weight: normal; -} - diff --git a/resources/static/include_js/_include.js b/resources/static/include_js/_include.js index 85a98972a..1045f7c44 100644 --- a/resources/static/include_js/_include.js +++ b/resources/static/include_js/_include.js @@ -2,118 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - var BrowserSupport = (function() { - var win = window, - nav = navigator, - reason; - - // For unit testing - function setTestEnv(newNav, newWindow) { - nav = newNav; - win = newWindow; - } - - function getInternetExplorerVersion() { - var rv = -1; // Return value assumes failure. - if (nav.appName == 'Microsoft Internet Explorer') { - var ua = nav.userAgent; - var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); - if (re.exec(ua) != null) - rv = parseFloat(RegExp.$1); - } - - return rv; - } - - function checkIE() { - var ieVersion = getInternetExplorerVersion(), - ieNosupport = ieVersion > -1 && ieVersion < 8; - - if(ieNosupport) { - return "BAD_IE_VERSION"; - } - } - - function explicitNosupport() { - return checkIE(); - } - - function checkLocalStorage() { - // Firefox/Fennec/Chrome blow up when trying to access or - // write to localStorage. We must do two explicit checks, first - // whether the browser has localStorage. Second, we must check - // whether the localStorage can be written to. Firefox (at v11) - // throws an exception when querying win['localStorage'] - // when cookies are disabled. Chrome (v17) excepts when trying to - // write to localStorage when cookies are disabled. If an - // exception is thrown, then localStorage is disabled. If no - // exception is thrown, hasLocalStorage will be true if the - // browser supports localStorage and it can be written to. - try { - var hasLocalStorage = 'localStorage' in win - // Firefox will except here if cookies are disabled. - && win['localStorage'] !== null; - - if(hasLocalStorage) { - // browser has localStorage, check if it can be written to. If - // cookies are disabled, some browsers (Chrome) will except here. - win['localStorage'].setItem("test", "true"); - win['localStorage'].removeItem("test"); - } - else { - // Browser does not have local storage. - return "LOCALSTORAGE_NOT_SUPPORTED"; - } - } catch(e) { - return "LOCALSTORAGE_DISABLED"; - } - } - - function checkPostMessage() { - if(!win.postMessage) { - return "POSTMESSAGE_NOT_SUPPORTED"; - } - } - - function checkJSON() { - if(!(window.JSON && window.JSON.stringify && window.JSON.parse)) { - return "JSON_NOT_SUPPORTED"; - } - } - - function isSupported() { - reason = explicitNosupport() || checkLocalStorage() || checkPostMessage() || checkJSON(); - - return !reason; - } - - - function getNoSupportReason() { - return reason; - } - - return { - /** - * Set the test environment. - * @method setTestEnv - */ - setTestEnv: setTestEnv, - /** - * Check whether the current browser is supported - * @method isSupported - * @returns {boolean} - */ - isSupported: isSupported, - /** - * Called after isSupported, if isSupported returns false. Gets the reason - * why browser is not supported. - * @method getNoSupportReason - * @returns {string} - */ - getNoSupportReason: getNoSupportReason - }; - }()); - if (!navigator.id) { navigator.id = {}; } @@ -130,449 +18,36 @@ (isFennec ? undefined : "menubar=0,location=1,resizable=1,scrollbars=1,status=0,width=700,height=375"); - // Chrome for iOS - // - https://developers.google.com/chrome/mobile/docs/user-agent - // Windows Phone - // - http://stackoverflow.com/questions/11381673/javascript-solution-to-detect-mobile-browser - var needsPopupFix = userAgent.match(/CriOS/) || - userAgent.match(/Windows Phone/); - - var REQUIRES_WATCH = "WATCH_NEEDED"; var WINDOW_NAME = "__persona_dialog"; var w; - // table of registered observers - var observers = { - login: null, - logout: null, - match: null, - ready: null - }; - - var loggedInUser; - - var compatMode = undefined; - function checkCompat(requiredMode) { - if (requiredMode === true) { - // this deprecation warning should be re-enabled when the .watch and .request APIs become final. - // try { console.log("this site uses deprecated APIs (see documentation for navigator.id.request())"); } catch(e) { } - } - - if (compatMode === undefined) compatMode = requiredMode; - else if (compatMode != requiredMode) { - throw new Error("you cannot combine the navigator.id.watch() API with navigator.id.getVerifiedEmail() or navigator.id.get()" + - "this site should instead use navigator.id.request() and navigator.id.watch()"); - } - } - - var commChan, - waitingForDOM = false, - browserSupported = BrowserSupport.isSupported(); - - function domReady(callback) { - if (document.addEventListener) { - document.addEventListener('DOMContentLoaded', function contentLoaded() { - document.removeEventListener('DOMContentLoaded', contentLoaded); - callback(); - }, false); - } else if (document.attachEvent && document.readyState) { - document.attachEvent('onreadystatechange', function ready() { - var state = document.readyState; - // 'interactive' is the same as DOMContentLoaded, - // but not all browsers use it, sadly. - if (state === 'loaded' || state === 'complete' || state === 'interactive') { - document.detachEvent('onreadystatechange', ready); - callback(); - } - }); - } - } - - - // this is for calls that are non-interactive - function _open_hidden_iframe() { - // If this is an unsupported browser, do not even attempt to add the - // IFRAME as doing so will cause an exception to be thrown in IE6 and IE7 - // from within the communication_iframe. - if(!browserSupported) return; - var doc = window.document; - - // can't attach iframe and make commChan without the body - if (!doc.body) { - if (!waitingForDOM) { - domReady(_open_hidden_iframe); - waitingForDOM = true; - } - return; - } - - try { - if (!commChan) { - var iframe = doc.createElement("iframe"); - iframe.style.display = "none"; - doc.body.appendChild(iframe); - iframe.src = ipServer + "/communication_iframe"; - commChan = Channel.build({ - window: iframe.contentWindow, - origin: ipServer, - scope: "mozid_ni", - onReady: function() { - // once the channel is set up, we'll fire a loaded message. this is the - // cutoff point where we'll say if 'setLoggedInUser' was not called before - // this point, then it wont be called (XXX: optimize and improve me) - commChan.call({ - method: 'loaded', - success: function(){ - // NOTE: Do not modify without reading GH-2017 - if (observers.ready) observers.ready(); - }, error: function() { - } - }); - } - }); - - commChan.bind('logout', function(trans, params) { - if (observers.logout) observers.logout(); - }); - - commChan.bind('login', function(trans, params) { - if (observers.login) observers.login(params); - }); - - commChan.bind('match', function(trans, params) { - if (observers.match) observers.match(); - }); - - if (defined(loggedInUser)) { - commChan.notify({ - method: 'loggedInUser', - params: loggedInUser - }); - } - } - } catch(e) { - // channel building failed! let's ignore the error and allow higher - // level code to handle user messaging. - commChan = undefined; - } - } - - function defined(item) { - return typeof item !== "undefined"; + function openPopup() { + w = window.open( + ipServer + '/sign_in', + WINDOW_NAME, + windowOpenOpts); } - function warn(message) { - try { - console.warn(message); - } catch(e) { - /* ignore error */ - } - } - - function checkDeprecated(options, field) { - if(defined(options[field])) { - warn(field + " has been deprecated"); - return true; - } - } - - function checkRenamed(options, oldName, newName) { - if (defined(options[oldName]) && - defined(options[newName])) { - throw new Error("you cannot supply *both* " + oldName + " and " + newName); - } - else if(checkDeprecated(options, oldName)) { - options[newName] = options[oldName]; - delete options[oldName]; - } - } - - function internalWatch(options) { - if (typeof options !== 'object') return; - - if (options.onlogin && typeof options.onlogin !== 'function' || - options.onlogout && typeof options.onlogout !== 'function' || - options.onmatch && typeof options.onmatch !== 'function' || - options.onready && typeof options.onready !== 'function') - { - throw new Error("non-function where function expected in parameters to navigator.id.watch()"); - } - - if (!options.onlogin) throw new Error("'onlogin' is a required argument to navigator.id.watch()"); - if (!options.onlogout && (options.onmatch || ('loggedInUser' in options))) - throw new Error('stateless api only allows onlogin and onready options'); - - observers.login = options.onlogin || null; - observers.logout = options.onlogout || null; - observers.match = options.onmatch || null; - // NOTE: Do not modify without reading GH-2017 - observers.ready = options.onready || null; - - // back compat support for loggedInEmail - checkRenamed(options, "loggedInEmail", "loggedInUser"); - loggedInUser = options.loggedInUser; - if (loggedInUser === false) { - loggedInUser = null; - } - if (!isNull(loggedInUser) && - !isUndefined(loggedInUser) && - !isString(loggedInUser)) - { - throw new Error("loggedInUser is not a valid type"); - } - - - _open_hidden_iframe(); - } - - function isNull(arg) { - return arg === null; - } - - function isUndefined(arg) { - return (typeof arg === 'undefined'); - } - - function isString(arg) { - return Object.prototype.toString.apply(arg) === "[object String]"; - } - - var api_called; - function getRPAPI() { - var rp_api = api_called; - if (rp_api === "request") { - if (observers.logout) { - rp_api = observers.ready ? "watch_with_onready" : "watch_without_onready"; - } else { - rp_api = "stateless"; - } - } - - return rp_api; - } - - function internalRequest(options) { - checkDeprecated(options, "requiredEmail"); - checkRenamed(options, "tosURL", "termsOfService"); - checkRenamed(options, "privacyURL", "privacyPolicy"); - - if (options.termsOfService && !options.privacyPolicy) { - warn("termsOfService ignored unless privacyPolicy also defined"); - } - - if (options.privacyPolicy && !options.termsOfService) { - warn("privacyPolicy ignored unless termsOfService also defined"); - } - - options.rp_api = getRPAPI(); - var couldDoRedirectIfNeeded = (!needsPopupFix || api_called === 'request' || api_called === 'auth'); - - // reset the api_called in case the site implementor changes which api - // method called the next time around. - api_called = null; - - options.start_time = (new Date()).getTime(); - - // focus an existing window - if (w) { - try { - w.focus(); - } - catch(e) { - /* IE7 blows up here, do nothing */ - } - return; - } - - function isSupported() { - return BrowserSupport.isSupported() && couldDoRedirectIfNeeded; - } - - function noSupportReason() { - var reason = BrowserSupport.getNoSupportReason(); - if (!reason && !couldDoRedirectIfNeeded) { - return REQUIRES_WATCH; - } - } - - if (!isSupported()) { - var reason = noSupportReason(); - var url = "unsupported_dialog"; - - if(reason === "LOCALSTORAGE_DISABLED") { - url = "cookies_disabled"; - } else if (reason === REQUIRES_WATCH) { - url = "unsupported_dialog_without_watch"; - } - - w = window.open( - ipServer + "/" + url, - WINDOW_NAME, - windowOpenOpts); - return; - } - - // notify the iframe that the dialog is running so we - // don't do duplicative work - if (commChan) commChan.notify({ method: 'dialog_running' }); - - function doPopupFix() { - if (commChan) { - return commChan.call({ - method: 'redirect_flow', - params: JSON.stringify(options), - success: function() { - // use call/success so that we do not have to depend on - // the postMessage being synchronous. - window.location = ipServer + '/sign_in'; - } - }); - } - } - - if (needsPopupFix) { - return doPopupFix(); - } - - w = WinChan.open({ - url: ipServer + '/sign_in', - relay_url: ipServer + '/relay', - window_features: windowOpenOpts, - window_name: WINDOW_NAME, - params: { - method: "get", - params: options - } - }, function(err, r) { - // unpause the iframe to detect future changes in login state - if (commChan) { - // update the loggedInUser in the case that an assertion was generated, as - // this will prevent the comm iframe from thinking that state has changed - // and generating a new assertion. IF, however, this request is not a success, - // then we do not change the loggedInUser - and we will let the comm frame determine - // if generating a logout event is the right thing to do - if (!err && r && r.email) { - commChan.notify({ method: 'loggedInUser', params: r.email }); - } - // prevent the authentication status check if an assertion is - // generated in the dialog or the dialog returned with an error. - // This prevents .onmatch from being fired for: - // 1. assertion already generated in the dialog - // 2. user is signed in to the site, opens the dialog, then cancels - // the dialog without generating an assertion. - // See #3170 & #3701 - var checkAuthStatus = !(err || r && r.assertion); - commChan.notify({ - method: 'dialog_complete', - params: checkAuthStatus - }); - } - - // clear the window handle - w = undefined; - if (!err && r && r.assertion) { - try { - if (observers.login) observers.login(r.assertion); - } catch(clientError) { - // client's observer threw an exception - // help developers debug by logging the error - console.log(clientError); - throw clientError; - } - } - - // if either err indicates the user canceled the signin (expected) or a - // null response was sent (unexpected), invoke the .oncancel() handler. - if (err === 'client closed window' || !r) { - if (options && options.oncancel) options.oncancel(); - delete options.oncancel; - } - }); - }; - navigator.id = { - request: function(options) { - if (this != navigator.id) - throw new Error("all navigator.id calls must be made on the navigator.id object"); - - if (!observers.login) - throw new Error("navigator.id.watch must be called before navigator.id.request"); - - options = options || {}; - checkCompat(false); - api_called = "request"; - // returnTo is used for post-email-verification redirect - if (!options.returnTo) options.returnTo = document.location.pathname; - return internalRequest(options); + request: function() { + return openPopup(); }, watch: function(options) { - if (this != navigator.id) - throw new Error("all navigator.id calls must be made on the navigator.id object"); - checkCompat(false); - internalWatch(options); + // intentionally empty }, // logout from the current website // The callback parameter is DEPRECATED, instead you should use the // the .onlogout observer of the .watch() api. logout: function(callback) { - if (this != navigator.id) - throw new Error("all navigator.id calls must be made on the navigator.id object"); - // allocate iframe if it is not allocated - _open_hidden_iframe(); - // send logout message if the commChan exists - if (commChan) commChan.notify({ method: 'logout' }); - if (typeof callback === 'function') { - warn('navigator.id.logout callback argument has been deprecated.'); - setTimeout(callback, 0); - } + // intentionally empty }, // get an assertion get: function(callback, passedOptions) { - var opts = {}; - passedOptions = passedOptions || {}; - opts.privacyPolicy = passedOptions.privacyPolicy || undefined; - opts.termsOfService = passedOptions.termsOfService || undefined; - opts.privacyURL = passedOptions.privacyURL || undefined; - opts.tosURL = passedOptions.tosURL || undefined; - opts.siteName = passedOptions.siteName || undefined; - opts.siteLogo = passedOptions.siteLogo || undefined; - opts.backgroundColor = passedOptions.backgroundColor || undefined; - opts.experimental_emailHint = passedOptions.experimental_emailHint || undefined; - // api_called could have been set to getVerifiedEmail already - api_called = api_called || "get"; - if (checkDeprecated(passedOptions, "silent")) { - // Silent has been deprecated, do nothing. Placing the check here - // prevents the callback from being called twice, once with null and - // once after internalWatch has been called. See issue #1532 - if (callback) setTimeout(function() { callback(null); }, 0); - return; - } - - checkCompat(true); - internalWatch({ - onlogin: function(assertion) { - if (callback) { - callback(assertion); - callback = null; - } - }, - onlogout: function() {} - }); - opts.oncancel = function() { - if (callback) { - callback(null); - callback = null; - } - observers.login = observers.logout = observers.match = observers.ready = null; - }; - internalRequest(opts); + openPopup(); }, // backwards compatibility with old API getVerifiedEmail: function(callback) { - warn("navigator.id.getVerifiedEmail has been deprecated"); - checkCompat(true); - api_called = "getVerifiedEmail"; - navigator.id.get(callback); + openPopup(); }, // _shimmed was originally required in April 2011 (79d3119db036725c5b51a305758a7816fdc8920a) // so we could deal with firefox behavior - which was in certain reload scenarios to caching diff --git a/resources/static/pages/css/style.css b/resources/static/pages/css/style.css index d979f6588..0b96ac32e 100644 --- a/resources/static/pages/css/style.css +++ b/resources/static/pages/css/style.css @@ -2,584 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#noscript-warning { - position: absolute; - position: fixed; - display: block; - background-color: #ef1010; - top: 0; - left: 0; - padding: 1px; - width: 100%; - color: #fff; - text-align: center; -} - -body { - background-color: #6a7b86; - background-image: url("/pages/i/marketplace-header.png"), url("/common/i/grain.png"); - background-position: center top, center top; - background-repeat: repeat-x, repeat; - color: #fff; -} - -/** - * In an embedded context, get rid of all background colors - */ -.embedded { - background: #fff; -} - -#errorBackground { - position: absolute; - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - filter: alpha(opacity=0); /* Needed for IE6 and IE7 on the main site */ - -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; - opacity: 0; - z-index: -2; - background: #000; - zoom: 1; - -webkit-transition: opacity 750ms; - -moz-transition: opacity 750ms; - -ms-transition: opacity 750ms; - -o-transition: opacity 750ms; - transition: opacity 750ms; -} - -.waiting #errorBackground, .error #errorBackground, .delay #errorBackground { - z-index: 1001; - display: block; - -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; - opacity: .6; -} - -#error, #wait, #delay { - z-index: -2; - filter: alpha(opacity=0); /* Needed for IE6 and IE7 on the main site */ - -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; - opacity: 0; - -webkit-transition: opacity 750ms; - -moz-transition: opacity 750ms; - -ms-transition: opacity 750ms; - -o-transition: opacity 750ms; - transition: opacity 750ms; - position: absolute; /* For a couple of browsers without position: fixed support */ - position: fixed; - top: 35%; - left: 20%; - right: 20%; - border: 2px solid #000; - border-radius: 5px; - text-align: center; - color: #333; -} - - -.waiting #wait { - z-index: 1002; - -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; - opacity: 1; -} - -.delay #delay { - z-index: 1003; - -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; - opacity: 1; -} - -.error #error { - z-index: 1004; - -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; - opacity: 1; -} - - -#error > div, #wait > div, #delay > div { - padding: 10px; - z-index: 1001; -} - - -#wrapper { - width: 896px; - margin: 0 auto; -} - -#content { - padding: 50px 0; -} - -/** - * embedded content should not show any of the sandstone background - */ -.embedded #content { - padding: 0; -} - -h1 { - margin-bottom: 35px; -} - -.headline-main, h1 { - font-weight: 300; -} - - -#legal { - padding: 75px 125px; -} - -/** - * embedded content doesn't really need that humongous padding. - */ -.embedded #legal { - padding: 25px 50px; -} - -#manage { - padding: 75px; -} - -#legal, #manage { - text-shadow: 1px 1px 0 rgba(255,255,255,0.5); - background-color: #fff; - text-align: justify; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25); - border-radius: 5px; - color: #444; -} - -#legal p, -#legal h2, -#legal ul { - padding: 0 0 21px 0; -} - -#legal li { - border-bottom: 1px solid #EEEEEE; - margin: 7px 0 0; - padding: 0 0 7px; - list-style-type: square; -} - -#legal li:last-child { - border: none; -} - -#legal h2 { - font-size: 21px; - color: #222; -} - -#legal h3 { - font-size: 18px; - color: #222; - padding: 49px 0 7px; -} - -#legal h4 { - font-size: 14px; - margin: 14px 0 7px 0; - color: #222; -} - -#legal h5, -#legal strong { - font-size: 12px; - color: #666; -} - -#legal p, -#legal ul { - color: #666; -} - -#manage section { - margin-top: 20px; -} - -.buttonrow { - margin: 0 0 14px; -} - -.buttonrow > h2 { - display: inline-block; - font-size: 1em; -} - -.edit .buttonrow > .edit { - display: none; -} - -.buttonrow > .done { - display: none; - background-color: #006EC6; - border: 1px solid #003E70; - color: #EEEEEE; - text-shadow: -1px -1px 0 #006EC6; - - -webkit-box-shadow: 0 0 5px #003763 inset; - -moz-box-shadow: 0 0 5px #003763 inset; - -o-box-shadow: 0 0 5px #003763 inset; - box-shadow: 0 0 5px #003763 inset; - - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #3AA7FF), color-stop(100%, #006EC6)); - background-image: -moz-linear-gradient(#3AA7FF 0%, #006EC6 100%); - background-image: -o-linear-gradient(#3AA7FF 0%, #006EC6 100%); - background-image: linear-gradient(#3AA7FF 0%, #006EC6 100%); -} - -.edit .buttonrow > .done { - display: inline-block; -} - -#manage #emailList { - border-top: 1px solid #eee; -} - -#emailList li { - padding: 10px 0; - border-bottom: 1px solid #eee; - overflow: hidden; - line-height: 30px; - min-height: 30px; -} - -#emailList .email { - display: inline-block; - float: left; - white-space: nowrap; -} - -#emailList button { - display: none; -} - -.edit #emailList button { - display: inline-block; -} - -#logout_everywhere .completion_text { - float: right; - display: none; - color: #090; -} - - -button.delete { - background-color: #EA7676; - border: 1px solid #B13D3D; - text-shadow: -1px -1px 0 #C84343; - box-shadow: 0 0 0 1px #EA7676 inset; - border-radius: 5px; - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #EA7676), color-stop(100%, #C84343)); - background-image: -moz-linear-gradient(#EA7676 0%, #C84343 100%); - background-image: -ms-linear-gradient(#EA7676 0%, #C84343 100%); - background-image: -o-linear-gradient(#EA7676 0%, #C84343 100%); - background-image: linear-gradient(#EA7676 0%, #C84343 100%); -} - -button.delete:hover { - background-color: #f07979; - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #f07979), color-stop(100%, #c34141)); - background-image: -moz-linear-gradient(#f07979 0%, #c34141 100%); - background-image: -ms-linear-gradient(#f07979 0%, #c34141 100%); - background-image: -o-linear-gradient(#f07979 0%, #c34141 100%); - background-image: linear-gradient(#f07979 0%, #c34141 100%); -} - -button.delete:active { - background-color: #C84343; - border: 1px solid #672424; - color: #EEEEEE; - text-shadow: -1px -1px 0 #AA3D3D; - - -webkit-box-shadow: 0 0 5px #003763 inset; - -moz-box-shadow: 0 0 5px #003763 inset; - -o-box-shadow: 0 0 5px #003763 inset; - box-shadow: 0 0 5px #003763 inset; - - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #C84343), color-stop(100%, #AA3D3D)); - background-image: -moz-linear-gradient(center top , #C84343 0%, #AA3D3D 100%); - background-image: -o-linear-gradient(#C84343 0%, #AA3D3D 100%); - background-image: linear-gradient(#C84343 0%, #AA3D3D 100%); -} - - -#edit_password { - margin-bottom: 10px; - display: none; -} - -.canSetPassword #edit_password { - display: block; -} - -#edit_password label { - width: 40%; - display: inline-block; -} - -.showedit { - display: none; -} - -.edit .showedit { - display: block; -} - -#changePassword{ - margin-top:21px; -} - -#disclaimer { - text-align: right; -} - -#hAlign { - width: 700px; - margin: 0 auto; - position: relative; -} - -#vAlign { - height: 1000px; - width: 700px; /* the width here is to keep the cell from collapsing */ - display: table-cell; - vertical-align: middle; -} - -#signUp { - padding: 0 0 0 250px; -} - -#signUp h1 { - max-width: 390px; -} - -.tour { - font-size: 18px; - line-height: 39px; -} - -.tour a { - margin: 0 7px; - text-shadow: 0 1px 0 #555; -} - -.tour .button { - font-size: 19px; - border: 1px solid; - border-radius: 7px; - border-color: #68b8e8 #5da8dc #2f597b #5aa4d9; - display:inline-block; - padding: 11px 25px; - background-image: -webkit-linear-gradient(top, #42a5e1, #2970aa); - background-image: -moz-linear-gradient(top, #42a5e1, #2970aa); - background-image: -ms-linear-gradient(top, #42a5e1, #2970aa); - background-image: -o-linear-gradient(top, #42a5e1, #2970aa); - background-image: linear-gradient(top, #42a5e1, #2970aa); - box-shadow: 0 1px 2px rgba(0,0,0,.5); -} - -.tour .button:hover { - color: #fff; - border-color: #338fd1 #277ec4 #0e6bb6 #277ec4; - background-image: -webkit-linear-gradient(top, #338fd1, #0e6bb6); - background-image: -moz-linear-gradient(top, #338fd1, #0e6bb6); - background-image: -ms-linear-gradient(top, #338fd1, #0e6bb6); - background-image: -o-linear-gradient(top, #338fd1, #0e6bb6); - background-image: linear-gradient(top, #338fd1, #0e6bb6); -} - -.tour .button:active { - background-image: -webkit-gradient(linear, left top, left bottom, from(#184a73), to(#276084)); - background-image: -webkit-linear-gradient(top, #184a73, #276084); - background-image: -moz-linear-gradient(top, #184a73, #276084); - background-image: -ms-linear-gradient(top, #184a73, #276084); - background-image: -o-linear-gradient(top, #184a73, #276084); - background-image: linear-gradient(top, #184a73, #276084); - color: #97b6ca; -} - -.create { - float: none; - vertical-align: middle; -} - -#card { - width: 200px; - height: 200px; - position: absolute; - z-index: 1; - left: 0; - background-image: url('/pages/i/badge.png'); - background-position: 0px center; - background-repeat: no-repeat; - - -webkit-transition: background-position 0.4s ease; - -moz-transition: background-position 0.4s ease; - -o-transition: background-position 0.4s ease; - transition: background-position 0.4s ease; -} - -#card.insert { - background-position: 170px center; -} - -#card img { - float: right; -} - - -#signUpForm, #congrats { - margin: 0 auto; - width: 475px; - padding: 20px; - background-color: #556875; - background-color: rgba(0,0,0,0.1); - - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - -o-border-radius: 5px; - border-radius: 5px; -} - -#signUpForm h1 { - margin-bottom: 20px; -} - -#signUpForm h2 { - margin-bottom: 20px; - font-weight: 300; - font-size: 22px; -} - -#signUpForm a { - color: #6dc7ff; - text-shadow: 0 1px 0 #888; -} - -#signUpForm a:hover { - color: #58a7e7; -} - - -#signUpForm li { - margin: 10px 0 0; - padding: 0; -} - -#signUpForm li:first-child { - margin: 0; -} - -#signUpForm > .siteinfo { - margin-bottom: 10px; -} - -.siteinfo, #congrats, .password_entry, .vpassword_entry, .verify_primary, .known_secondary .start, .unknown_secondary .start, .primary .start, .unknown_secondary .forgot { - display: none; -} - -.enter_password .password_entry, .needs_password .vpassword_entry { - display: block; -} - -label.vpassword_entry { - margin-top: 15px; -} - -.submit { - margin-top: 15px; -} - -.submit > p { - line-height: 28px; -} - -.submit .remember { - float: left; -} - -.tospp { - font-size: 13px; - clear: both; -} - -#congrats .siteinfo { - margin-top: 10px; -} - -#congrats .website { - display: block; - text-align: center; -} - -#redirection { - text-align: center; -} - - -.notifications > .notification { - border-radius: 3px; - display: none; -} - -.notifications .notification.error { - color: red; - background-color: rgba(255,0,0,0.25); -} - -.notification p { - margin-top: 8px; -} - - -#wrapper > header { - font-weight: bold; - z-index: 1; -} - -/* Hide the nav until the user's auth status is known. This - * reduces the amount of flicker because buttons are not drawn - * unless they are needed. - */ -header .nav { - display: none; -} - -header .nav a { - font-size: 16px; - padding: 4px 8px; - color: #fff; - text-shadow: 0 1px 0 #333; - text-shadow: 0 1px 0 rgba(0, 0, 0, .5); -} - -header .nav a:hover { - color: #383838; - background-color: #f4f3f0; - border-radius: 7px; - text-shadow: 0 1px 0 #fff; - text-shadow: 0 1px 0 rgba(255, 255, 255, .5); -} - -header ul { - float: right; - line-height: 37px; -} - -header li { - margin: 0 0 0 10px; -} - - .home { width: 205px; height: 50px; @@ -588,204 +10,58 @@ header li { display: inline-block; } -header a.signIn, header a.signOut { - padding: 6px 20px; - border-radius: 7px; - border: 1px solid #333; - box-shadow: 0 0 1px #777; -} - -.authenticated .ifNotAuthenticated, .ifAuthenticated { - display: none; -} - -.authenticated .ifAuthenticated { - display: inline; -} - -header, footer { - padding: 20px 0; -} - -section > header { - padding: 0; +header { + left: 0; + padding: 20px; + position: absolute; + right: 0; + top: 0; } footer { background-color: #eff1f3; + bottom: 0; + color: #484848; + left: 0; margin-top: 10px; + padding: 20px; + position: absolute; + right: 0; + width: 100%; } footer .cf { - width: 896px; + width: 100%; margin: 0 auto; } -footer ul li:first-child { - margin-right: 35px; -} - -footer ul li:first-child a { - color: #484848; -} - -footer ul li:first-child a:hover { - border-bottom: 1px dotted #000; -} - -.newsbanner { - display: none; - background-color: #faca33; - line-height: 32px; - border-radius: 4px; - text-align: center; - color: #626160; - text-shadow: 1px 1px 0 rgba(255,255,255,0.5); -} - -.newuser .newsbanner { - margin-bottom: 50px; - display: block; -} - -/* How It Works - ***************/ - - h2.title { - font-size: 48px; - font-weight: normal; - color: #fff; - text-shadow: 0 1px rgba(0, 0, 0, 0.5); - text-align: center; - letter-spacing: -2px; - padding-bottom: 30px; +#wrapper { + align-items: center; + display: flex; + justify-content: center; margin: 0; + min-height: 100%; + width: 100%; } -.blurb, a.developers { - -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.13); - -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.13); - -ms-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.13); - box-shadow: 0 1px 0 rgba(255, 255, 255, 0.13); - background: #63727d; - background: rgba(13, 28, 41, 0.1); - font-size: 14px; - color: #fff; -} - -.blurb { - margin-top: 30px; - padding: 30px; - text-align: left; - line-height: 1.5; - overflow: hidden; - display: block; -} - -.blurb h1, .blurb p, .blurb a, a.developers{ - text-shadow: 0 1px rgba(0, 0, 0, 0.5); - font-weight: normal; +.notice { + align-self: center; + background: #8A241B; + border: 1px solid #710B02; + max-width: 50%; + padding-left: 15px; + padding-right: 15px; + padding: 5px; + text-align: center; } -.blurb img{ - max-width: 100%; - vertical-align: bottom; +.notice p { + padding: 0.4em; } -.blurb a { - color: #fff; +.notice a { border-bottom: 1px dotted #fff; + color: #fff; font-weight: normal; } -.blurb a:hover { - color: #53b7fb; -} -.blurb.half { - width: 48%; - float: left; -} -.blurb.half.first { - margin-right: 4%; -} -.blurb .info, .blurb .graphic { - width: 50%; - float: left; -} -.blurb .first { - padding-right: 30px; -} -.blurb .graphic { - text-align: center; -} -.blurb h1 { - font-size: 32px; - font-weight: normal; - letter-spacing: -1px; - line-height: 1.1; - margin-bottom: 20px; -} -.blurb p { - margin-bottom: 1em; -} -.blurb p:last-of-type { - margin-bottom: 0; -} - -.privacy { - -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1); - -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1); - -ms-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1); - box-shadow: 0 1px 0 rgba(255, 255, 255, 0.1); - zoom: 1; - border-bottom: 1px solid rgba(0, 0, 0, 0.2); - padding-bottom: 60px; - margin: 100px 0 60px; -} -.privacy:before, .privacy:after { - content: ""; - display: table; -} -.privacy:after { - clear: both; -} -a.developers { - -webkit-transition: all 300ms ease; - -moz-transition: all 300ms ease; - -ms-transition: all 300ms ease; - transition: all 300ms ease; - display: block; - padding: 13px 15px; - line-height: 1.4; - text-align: center; -} -a.developers:hover { - background: #3b4e5c; - background: rgba(13, 28, 41, 0.2); -} -a.developers img { - margin: 0 5px -7px 0; - /* The logo at the bottom of the /about page has a border by default */ - border: none; -} -a.developers span { - color: #53b7fb; - font-weight: bold; - margin-right: 10px; - display: inline-block; -} - -article.flexible { - padding-bottom: 0; -} -article.flexible .info { - margin-bottom: 30px; -} - -.notice { - margin: 100px 0 0 0; -} - -.about { - margin-top: 50px; -} diff --git a/resources/views/layout.ejs b/resources/views/layout.ejs index 638cc1d4a..2ba94973b 100644 --- a/resources/views/layout.ejs +++ b/resources/views/layout.ejs @@ -13,61 +13,30 @@ - <%- cachify_js(util.format('/production/%s/browserid.js', locale)) %> > <% /* the title comes from the server when the page is loaded. It still needs translated, so wrap it in its own gettext */ %> <%= format(gettext("Mozilla Persona: %s"), [gettext(title)]) %> -" <%- typeof start_blank !== "undefined" ? 'style="display: none;"' : "" %>> -<% if (enable_development_menu) { %> -   -<% } %> -
+ +
-<% if (typeof embedded === "undefined" || embedded !== true) { %> - -<% } %> -
-

As of November 30th 2016, the persona.org service is no longer supported. It will be shut down in December 2016.  More Info...

+

The persona.org service has shut down.
+ More Info... +

- -
-
-
- - <%- body %> -
-<% if (typeof embedded === "undefined" || embedded !== true) { %> -