diff --git a/authority/jsapi/picker.css b/authority/jsapi/picker.css index d8fac46cb0440134d19f53478fca54645047027e..5de639d084b8f0d11e533819c3a8b4a03a887a13 100644 --- a/authority/jsapi/picker.css +++ b/authority/jsapi/picker.css @@ -7,14 +7,14 @@ body { padding: 0; margin: 0; } -.sitename { +span.sitename, span.email { font-weight: bold; } .title { font-size: 2em; font-weight: bold; margin: auto; - margin-top: 2em; + margin-top: 1.5em; margin-bottom: 1em; } @@ -53,8 +53,9 @@ body { #bottom-bar button { height: 25px; border: 1px solid rgb(145, 145, 145); - -moz-border-radius: 3px; + -moz-border-radius: 4px; -webkit-border-radius: 4px; + border-radius: 4px; background-color: rgb(242, 242, 242); padding-left: 2em; padding-right: 2em; @@ -77,13 +78,17 @@ body { div.actions > div.action { float: left; +} + +div.actions > div.action a { cursor: pointer; + color: rgb(65, 126, 208); + text-decoration: none; } div.actions { position: fixed; bottom: 68px; - color: rgb(65, 126, 208); font-size: .8em; margin-left: 79px; } @@ -175,3 +180,25 @@ span.good { div.subtle { opacity: .7; } + +div.dialog div.attention, div.dialog div.attention_awesome { + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + width: 400px; + padding:13px; + margin: auto; + text-align: left; +} + +div.dialog div.attention { + border: 1px solid rgb(219,231,238); + background-color: rgb(244,247,251); +} + +div.dialog div.attention_awesome { + border: 1px solid rgb(0,161,51); + background-color: rgb(0,161,51); + color: white; + font-weight: bold; +} \ No newline at end of file diff --git a/authority/jsapi/picker.html b/authority/jsapi/picker.html index 5fae4682fd46bc0da4e71cb90adfa02f5df01cd3..18ea82a32c0393f0e101a4b984ed2524e734696f 100644 --- a/authority/jsapi/picker.html +++ b/authority/jsapi/picker.html @@ -7,10 +7,10 @@ <div id="default_dialog" class="dialog"> <div class="title"> Sign in with Firefox ID </div> <div class="content"> - <div class="summary">Enter your email and Firefox ID password to log into <span class="sitename"></span>:</div> + <div class="summary">Enter your email and Firefox ID password to log into <span class="sitename bad"></span>:</div> <div class="input"> <div class="label"> Email </div> - <div class="input"> <input type="text"></input></div> + <div class="input"> <input type="text"></input></div> </div> <div class="input"> <div class="label"> Password </div> @@ -18,7 +18,7 @@ <div class="note"> <a href="#">I forgot my password</a> </div> </div> <div class="actions"> - <div class="action"> Create Your Firefox ID </div> + <div class="action"><a href="#">Create Your Firefox ID</a></div> </div> </div> </div> @@ -28,7 +28,7 @@ <div class="summary">Firefox ID makes logging in safer and easier. To begin, please provide an email address and pick a password:</div> <div class="input"> <div class="label"> Email </div> - <div class="input"> <input type="text"></input></div> + <div class="input"> <input type="text"></input></div> <div class="note"></div> </div> <div class="input"> @@ -42,6 +42,27 @@ </div> </div> </div> +<div id="confirm_email_dialog" class="dialog"> + <div class="title"> Confirm Your Email </div> + <div class="content"> + <div class="summary">Welcome, <span class="email good"></span>. You will receive a <strong>confirmation email</strong> in a few moments. To activate your Firefox ID, please <strong>visit the link</strong> included in the email.</div> + <div class="attention"> + Waiting for email confirmation... <span class="bad"> THIS ISN'T REAL! JUST WAIT 5 SECONDS!</span> + </div> + <div class="actions"> + <div class="action"> No email yet? <a href="#">Resend it!</a></div> + </div> + </div> +</div> +<div id="confirmed_email_dialog" class="dialog"> + <div class="title"> Confirm Your Email </div> + <div class="content"> + <div class="summary">Welcome, <span class="email good"></span>. You will receive a <strong>confirmation email</strong> in a few moments. To activate your Firefox ID, please <strong>visit the link</strong> included in the email.</div> + <div class="attention_awesome "> + Your email has been confirmed and your Firefox ID created! </span> + </div> + </div> +</div> <div id="ids_dialog" class="dialog"> <div class="title"> Sign in with Firefox ID </div> <div class="content"> diff --git a/authority/jsapi/picker.js b/authority/jsapi/picker.js index ac8fe7b19131a47434aca57603dcd9c00bc477a7..3a97fdc76d5af8baa518ccf32bbb0d04bdeec043 100644 --- a/authority/jsapi/picker.js +++ b/authority/jsapi/picker.js @@ -1,174 +1,266 @@ // this is the picker code! it runs in the identity provider's domain, and // fiddles the dom expressed by picker.html +(function() { + var chan = Channel.build( + { + window: window.opener, + origin: "*", + scope: "mozid" + }); -var chan = Channel.build( - { - window: window.opener, - origin: "*", - scope: "mozid" - }); + function runIDsDialog(cb) { + // iterate over all of the available identities and add a links to them + var list = document.getElementById('identities'); -function runIDsDialog(cb) { - // iterate over all of the available identities and add a links to them - var list = document.getElementById('identities'); - - var first = true; - [ "foo@bar.com", "baz@bing.com" ].forEach(function(i) { - var div = document.createElement("div"); - var button = document.createElement("input"); - button.setAttribute('type', 'radio'); - button.checked = first; - first = false; - button.name = "id_selection"; - button.value = i; - div.appendChild(button); - var label = document.createElement("div"); - label.innerText = i; - label.addEventListener("click", function(evt) { - console.log("clicked label: "); - this.parentNode.firstChild.checked = true; + var first = true; + [ "foo@bar.com", "baz@bing.com" ].forEach(function(i) { + var div = document.createElement("div"); + var button = document.createElement("input"); + button.setAttribute('type', 'radio'); + button.checked = first; + first = false; + button.name = "id_selection"; + button.value = i; + div.appendChild(button); + var label = document.createElement("div"); + label.innerText = i; + label.addEventListener("click", function(evt) { + console.log("clicked label: "); + this.parentNode.firstChild.checked = true; + }); + div.appendChild(label); + list.appendChild(div); }); - div.appendChild(label); - list.appendChild(div); - }); - // now make the body visible... - document.getElementById("body").style.display = "block"; + // now make the body visible... + document.getElementById("body").style.display = "block"; - document.getElementById('signin').addEventListener("click", function(evt) { - var is = document.forms["identities"].elements['id_selection']; - var id = undefined; - for (var i = 0; i < is.length; i++) { - if (is[i].checked) { - id = is[i].value; - break; + document.getElementById('signin').addEventListener("click", function(evt) { + var is = document.forms["identities"].elements['id_selection']; + var id = undefined; + for (var i = 0; i < is.length; i++) { + if (is[i].checked) { + id = is[i].value; + break; + } } - } - if (id) { - trans.complete(id); - window.self.close(); - } else { + if (id) { + trans.complete(id); + window.self.close(); + } else { + trans.error("noSelection", "no id selected by user"); + window.self.close(); + } + }); + + document.getElementById('cancel').addEventListener("click", function(evt) { trans.error("noSelection", "no id selected by user"); window.self.close(); - } - }); + }); + } - document.getElementById('cancel').addEventListener("click", function(evt) { - trans.error("noSelection", "no id selected by user"); - window.self.close(); - }); -} + function runDefaultDialog(onsuccess, onerror) { + $(".dialog").hide(); + + $("#back").hide(); + $("#cancel").show().unbind('click').click(function() { + onerror("canceled"); + }); + $("#submit").show().unbind('click').click(function() { + onerror("notImplemented"); + }).text("Sign In"); + $("#default_dialog div.note > a").unbind('click').click(function() { + onerror("notImplemented"); + }); + $("#default_dialog div.note > a").unbind('click').click(function() { + onerror("notImplemented"); + }); + $("#default_dialog div.actions div.action").unbind('click').click(function() { + runCreateDialog(onsuccess, onerror); + }); + $("#default_dialog").fadeIn(500); + } -function runDefaultDialog(onsuccess, onerror) { - $(".dialog").hide(); + // a handle to a timeout of a running email check + var emailCheckState = undefined; + // the next email to check, if one is entered while a check is running + var nextEmailToCheck = undefined; + // a set of emails that we've checked for this session + var checkedEmails = { - $("#back").hide(); - $("#cancel").show().unbind('click').click(function() { - onerror("canceled"); - }); - $("#submit").show().unbind('click').click(function() { - onerror("notImplemented"); - }).text("Sign In"); - $("#default_dialog div.note > a").unbind('click').click(function() { - onerror("notImplemented"); - }); - $("#default_dialog div.note > a").unbind('click').click(function() { - onerror("notImplemented"); - }); - $("#default_dialog div.actions div.action").unbind('click').click(function() { - runCreateDialog(onsuccess, onerror); - }); - $("#default_dialog").fadeIn(500); -} + }; -function runCreateDialog(onsuccess, onerror) { - $(".dialog").hide(); + function runConfirmEmailDialog(email, onsuccess, onerror) { + $(".dialog").hide(); - $("#back").show().unbind('click').click(function() { - runDefaultDialog(onsuccess, onerror); - }); - $("#cancel").show().unbind('click').click(function() { - onerror("canceled"); - }); - $("#submit").show().unbind('click').click(function() { - onerror("notImplemented"); - }).text("Continue").addClass("disabled"); - - function checkInput() { - $("#submit").removeClass("disabled"); - - // first we should check the email entry - $("#create_dialog div.note:eq(0)").html($('<span class="warning"/>').text("Checking address")); - // (XXX) - - // next let's check the password entry - var pass = $("#create_dialog input:eq(1)").val(); - var match = pass === $("#create_dialog input:eq(2)").val(); - if (!match) { - $("#submit").addClass("disabled"); - $("#create_dialog div.note:eq(1)").html($('<span class="bad"/>').text("Passwords different")); - } else { - if (!pass) { + $("span.email").text(email); + + // XXX: till we implement email confirmation, this is a faked up step. just wait 5s + var fakeyTimeout = setTimeout(function() { + runConfirmedEmailDialog(email, onsuccess, onerror); + }, 5000); + + $("#back").show().unbind('click').click(function() { + window.clearTimeout(fakeyTimeout); + runCreateDialog(onsuccess, onerror); + }); + + $("#cancel").show().unbind('click').click(function() { + window.clearTimeout(fakeyTimeout); + onerror("canceled"); + }); + $("#submit").hide(); + + $("#create_email_dialog div.actions div.action a").unbind('click').click(function() { + // XXX: resend the email! + return true; + }); + $("#confirm_email_dialog").fadeIn(500); + + } + + function runConfirmedEmailDialog(email, onsuccess, onerror) { + $(".dialog").hide(); + + $("span.email").text(email); + + $("#back").hide(); + + $("#cancel").show().unbind('click').click(function() { + onerror("canceled"); + }); + $("#submit").show().unbind('click').click(function() { + runIDsDialog(onsuccess, onerror); + }).text("Continue"); + + $("#confirmed_email_dialog").show(); + } + + function runCreateDialog(onsuccess, onerror) { + $(".dialog").hide(); + + $("#back").show().unbind('click').click(function() { + runDefaultDialog(onsuccess, onerror); + }); + $("#cancel").show().unbind('click').click(function() { + onerror("canceled"); + }); + $("#submit").show().unbind('click').click(function() { + // ignore the click if we're disabled + if ($(this).hasClass('disabled')) return true; + + // user wishes to create a firefox ID! now let's validate the email flow + runConfirmEmailDialog($("#create_dialog input:eq(0)").val(), onsuccess, onerror); + }).text("Continue").addClass("disabled"); + + + function checkInput() { + $("#submit").removeClass("disabled"); + + // check the email address + var email = $("#create_dialog input:eq(0)").val(); + if (typeof email === 'string' && email.length) { + var valid = checkedEmails[email]; + if (typeof valid === 'boolean') { + if (valid) { + $("#create_dialog div.note:eq(0)").html($('<span class="good"/>').text("Email is valid")); + } else { + $("#create_dialog div.note:eq(0)").html($('<span class="bad"/>').text("Email in use")); + $("#submit").addClass("disabled"); + } + } else { + // this is an email that needs to be checked! + if (emailCheckState !== 'querying') { + if (emailCheckState) window.clearTimeout(emailCheckState); + emailCheckState = setTimeout(function() { + // XXX: this is where we should actually bounce off the server and enter the 'querying' state + // for now, we just call it valid + checkedEmails[nextEmailToCheck] = true; + emailCheckState = undefined; + checkInput(); + }, 500); + } + nextEmailToCheck = email; + $("#submit").addClass("disabled"); + $("#create_dialog div.note:eq(0)").html($('<span class="warning"/>').text("Checking address")); + } + } else { + $("#create_dialog div.note:eq(0)").empty(); $("#submit").addClass("disabled"); - $("#create_dialog div.note:eq(1)").html($('<span class="bad"/>').text("Enter a password")); - } else if (pass.length < 5) { + } + + // next let's check the password entry + var pass = $("#create_dialog input:eq(1)").val(); + var match = pass === $("#create_dialog input:eq(2)").val(); + if (!match) { $("#submit").addClass("disabled"); - $("#create_dialog div.note:eq(1)").html($('<span class="bad"/>').text("Password too short")); + $("#create_dialog div.note:eq(1)").html($('<span class="bad"/>').text("Passwords different")); } else { - $("#create_dialog div.note:eq(1)").html($('<span class="good"/>').text("Password OK")) + if (!pass) { + $("#submit").addClass("disabled"); + $("#create_dialog div.note:eq(1)").html($('<span class="bad"/>').text("Enter a password")); + } else if (pass.length < 5) { + $("#submit").addClass("disabled"); + $("#create_dialog div.note:eq(1)").html($('<span class="bad"/>').text("Password too short")); + } else { + $("#create_dialog div.note:eq(1)").html($('<span class="good"/>').text("Password OK")) + } } } - } - // watch input dialogs - $("#create_dialog input:first").unbind('keyup').bind('keyup', function() { - checkInput(); - }); + // watch input dialogs + $("#create_dialog input:first").unbind('keyup').bind('keyup', function() { + checkInput(); + }); + + $("#create_dialog input:gt(0)").unbind('keyup').bind('keyup', checkInput); - $("#create_dialog input:gt(0)").unbind('keyup').bind('keyup', checkInput); + // do a check at load time, in case the user is using the back button (enables the continue button!) + checkInput(); - $("#create_dialog").fadeIn(500); -} + $("#create_dialog").fadeIn(500); + } -runCreateDialog(); + runCreateDialog(); -function errorOut(trans, code) { - function getVerboseMessage(code) { - var msgs = { - "canceled": "user canceled selection", - "notImplemented": "the user tried to invoke behavior that's not yet implemented" - }; - var msg = msgs[code]; - if (!msg) { - alert("need verbose message for " + code); - msg = "unknown error" + function errorOut(trans, code) { + function getVerboseMessage(code) { + var msgs = { + "canceled": "user canceled selection", + "notImplemented": "the user tried to invoke behavior that's not yet implemented" + }; + var msg = msgs[code]; + if (!msg) { + alert("need verbose message for " + code); + msg = "unknown error" + } + return msg; } - return msg; + trans.error(code, getVerboseMessage(code)); + window.self.close(); } - trans.error(code, getVerboseMessage(code)); - window.self.close(); -} -chan.bind("getVerifiedEmail", function(trans, s) { - trans.delayReturn(true); + chan.bind("getVerifiedEmail", function(trans, s) { + trans.delayReturn(true); - // set the requesting site - $(".sitename").text(trans.origin.replace(/^.*:\/\//, "")); + // set the requesting site + $(".sitename").text(trans.origin.replace(/^.*:\/\//, "")); - // XXX: check to see if there's any pubkeys stored in the browser - var haveIDs = false; + // XXX: check to see if there's any pubkeys stored in the browser + var haveIDs = false; - if (haveIDs) { - runIDs(function(rv) { - trans.complete(rv); - }, function(error) { - errorOut(trans, error); - }); - } else { - runDefaultDialog(function(rv) { - trans.complete(rv); - }, function(error) { - errorOut(trans, error); - }); - } -}); + if (haveIDs) { + runIDsDialog(function(rv) { + trans.complete(rv); + }, function(error) { + errorOut(trans, error); + }); + } else { + runDefaultDialog(function(rv) { + trans.complete(rv); + }, function(error) { + errorOut(trans, error); + }); + } + }); +})(); \ No newline at end of file