Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

integrating train 2012.01.05

  • Loading branch information...
commit 925aef65c5732d36c06f1345b046df08b3fc1e7c 2 parents 3632b30 + 0051864
@lloyd lloyd authored
Showing with 7,957 additions and 1,686 deletions.
  1. +12 −0 ChangeLog
  2. +4 −0 bin/browserid
  3. +16 −5 bin/load_gen
  4. +9 −0 example/primary/.well-known/vep
  5. +70 −0 example/primary/index.html
  6. 0  example/{jquery-min.js → primary/jquery.js}
  7. +54 −0 example/primary/provision.html
  8. +1 −0  example/primary/sample.privatekey
  9. +1 −0  example/primary/sample.publickey
  10. +59 −0 example/primary/sign_in.html
  11. 0  example/{ → rp}/index.html
  12. +16 −0 example/rp/jquery-min.js
  13. +4 −2 lib/bcrypt.js
  14. +13 −2 lib/browserid/views.js
  15. +8 −3 lib/configuration.js
  16. +8 −1 lib/db.js
  17. +174 −79 lib/db/json.js
  18. +182 −81 lib/db/mysql.js
  19. +13 −0 lib/http_forward.js
  20. +1 −0  lib/load_gen/activities/add_email.js
  21. +5 −1 lib/load_gen/user_db.js
  22. +233 −0 lib/primary.js
  23. +35 −3 lib/verifier/certassertion.js
  24. +17 −26 lib/wsapi.js
  25. +2 −2 lib/wsapi/account_cancel.js
  26. +48 −0 lib/wsapi/add_email_with_assertion.js
  27. +50 −0 lib/wsapi/address_info.js
  28. +97 −0 lib/wsapi/auth_with_assertion.js
  29. +65 −56 lib/wsapi/authenticate_user.js
  30. +2 −2 lib/wsapi/cert_key.js
  31. +45 −7 lib/wsapi/complete_email_addition.js
  32. +6 −8 lib/wsapi/complete_user_creation.js
  33. +33 −0 lib/wsapi/create_account_with_assertion.js
  34. +4 −4 lib/wsapi/email_addition_status.js
  35. +13 −2 lib/wsapi/email_for_token.js
  36. +2 −2 lib/wsapi/list_emails.js
  37. +1 −1  lib/wsapi/remove_email.js
  38. +2 −2 lib/wsapi/session_context.js
  39. +1 −1  lib/wsapi/stage_email.js
  40. +11 −3 lib/wsapi/update_password.js
  41. +26 −18 lib/wsapi/user_creation_status.js
  42. +4 −3 package.json
  43. +1 −14 resources/.gitignore
  44. +7 −0 resources/static/auth_with_idp/main.js
  45. +6 −0 resources/static/css/common.css
  46. +23 −5 resources/static/css/style.css
  47. +27 −10 resources/static/dialog/controllers/actions.js
  48. +3 −3 resources/static/dialog/controllers/{addemail.js → add_email.js}
  49. +18 −11 resources/static/dialog/controllers/authenticate.js
  50. +1 −1  resources/static/dialog/controllers/{checkregistration.js → check_registration.js}
  51. +46 −17 resources/static/dialog/controllers/dialog.js
  52. +64 −0 resources/static/dialog/controllers/email_chosen.js
  53. +1 −1  resources/static/dialog/controllers/{forgotpassword.js → forgot_password.js}
  54. +14 −3 resources/static/dialog/controllers/page.js
  55. +9 −8 resources/static/dialog/controllers/{pickemail.js → pick_email.js}
  56. +99 −0 resources/static/dialog/controllers/primary_user_provisioned.js
  57. +116 −0 resources/static/dialog/controllers/provision_primary_user.js
  58. +111 −48 resources/static/dialog/controllers/required_email.js
  59. +95 −0 resources/static/dialog/controllers/set_password.js
  60. +101 −0 resources/static/dialog/controllers/verify_primary_user.js
  61. +1 −1  resources/static/dialog/css/m.css
  62. +12 −10 resources/static/dialog/css/popup.css
  63. +38 −24 resources/static/dialog/resources/helpers.js
  64. +1 −1  resources/static/dialog/resources/internal_api.js
  65. +94 −37 resources/static/dialog/resources/state_machine.js
  66. +4 −0 resources/static/dialog/start.js
  67. 0  resources/static/dialog/views/{addemail.ejs → add_email.ejs}
  68. +6 −2 resources/static/dialog/views/authenticate.ejs
  69. 0  resources/static/dialog/views/{confirmemail.ejs → confirm_email.ejs}
  70. +6 −1 resources/static/dialog/views/error.ejs
  71. 0  resources/static/dialog/views/{forgotpassword.ejs → forgot_password.ejs}
  72. 0  resources/static/dialog/views/{invalidRequiredEmail.ejs → invalid_required_email.ejs}
  73. 0  resources/static/dialog/views/{pickemail.ejs → pick_email.ejs}
  74. +20 −0 resources/static/dialog/views/primary_user_verified.ejs
  75. +55 −0 resources/static/dialog/views/required_email.ejs
  76. +0 −42 resources/static/dialog/views/requiredemail.ejs
  77. +40 −0 resources/static/dialog/views/set_password.ejs
  78. +1 −0  resources/static/dialog/views/site
  79. +47 −0 resources/static/dialog/views/verify_primary_user.ejs
  80. +8 −2 resources/static/include_js/include.js
  81. +0 −18 resources/static/lib/jquery-1.6.2.min.js
  82. +4 −0 resources/static/lib/jquery-1.7.1.min.js
  83. +1 −1  resources/static/lib/winchan.js
  84. +98 −22 resources/static/pages/add_email_address.js
  85. +2 −3 resources/static/pages/forgot.js
  86. +4 −0 resources/static/pages/manage_account.js
  87. +36 −8 resources/static/pages/page_helpers.js
  88. +104 −23 resources/static/pages/signup.js
  89. +5 −2 resources/static/pages/{browserid.js → start.js}
  90. +6 −15 resources/static/pages/verify_email_address.js
  91. +695 −0 resources/static/provisioning_api.js
  92. +39 −0 resources/static/shared/error-messages.js
  93. +27 −1 resources/static/shared/helpers.js
  94. +11 −0 resources/static/shared/javascript-extensions.js
  95. +157 −76 resources/static/shared/network.js
  96. +129 −0 resources/static/shared/provisioning.js
  97. +345 −88 resources/static/shared/user.js
  98. +2 −2 resources/static/shared/validation.js
  99. +2 −2 resources/static/shared/wait-messages.js
  100. +52 −58 resources/static/test/index.html
  101. +65 −1 resources/static/test/qunit/controllers/actions_unit_test.js
  102. +50 −11 resources/static/test/qunit/controllers/{addemail_unit_test.js → add_email_unit_test.js}
  103. +35 −14 resources/static/test/qunit/controllers/authenticate_unit_test.js
  104. 0  ...urces/static/test/qunit/controllers/{checkregistration_unit_test.js → check_registration_unit_test.js}
  105. +42 −58 resources/static/test/qunit/controllers/dialog_unit_test.js
  106. +78 −0 resources/static/test/qunit/controllers/email_chosen_unit_test.js
  107. 0  resources/static/test/qunit/controllers/{forgotpassword_unit_test.js → forgot_password_unit_test.js}
  108. +14 −0 resources/static/test/qunit/controllers/page_unit_test.js
  109. +7 −14 resources/static/test/qunit/controllers/{pickemail_unit_test.js → pick_email_unit_test.js}
  110. +166 −0 resources/static/test/qunit/controllers/primary_user_provisioned_unit_test.js
  111. +133 −0 resources/static/test/qunit/controllers/provision_primary_user_unit_test.js
  112. +145 −19 resources/static/test/qunit/controllers/required_email_unit_test.js
  113. +137 −0 resources/static/test/qunit/controllers/set_password_unit_test.js
  114. +125 −0 resources/static/test/qunit/controllers/verify_primary_user_unit_test.js
  115. +0 −1  resources/static/test/qunit/mocks/mocks.js
  116. +81 −0 resources/static/test/qunit/mocks/provisioning.js
  117. +57 −0 resources/static/test/qunit/mocks/winchan.js
  118. +58 −0 resources/static/test/qunit/mocks/window.js
  119. +25 −3 resources/static/test/qunit/mocks/xhr.js
  120. +143 −25 resources/static/test/qunit/pages/add_email_address_test.js
  121. +4 −8 resources/static/test/qunit/pages/forgot_unit_test.js
  122. +32 −2 resources/static/test/qunit/pages/manage_account_unit_test.js
  123. +31 −3 resources/static/test/qunit/pages/page_helpers_unit_test.js
  124. +4 −8 resources/static/test/qunit/pages/signin_unit_test.js
  125. +110 −27 resources/static/test/qunit/pages/signup_unit_test.js
  126. +5 −12 resources/static/test/qunit/pages/verify_email_address_test.js
  127. +36 −31 resources/static/test/qunit/resources/helpers_unit_test.js
  128. +75 −97 resources/static/test/qunit/resources/state_machine_unit_test.js
  129. +17 −1 resources/static/test/qunit/shared/helpers_unit_test.js
  130. +254 −160 resources/static/test/qunit/shared/network_unit_test.js
  131. +338 −100 resources/static/test/qunit/shared/user_unit_test.js
  132. +11 −0 resources/static/test/qunit/shared/validation_unit_test.js
  133. +48 −6 resources/static/test/qunit/testHelpers/helpers.js
  134. +64 −0 resources/views/add_email_address.ejs
  135. +9 −0 resources/views/authenticate_with_primary.ejs
  136. +1 −1  resources/views/communication_iframe.ejs
  137. +13 −6 resources/views/dialog_layout.ejs
  138. +9 −0 resources/views/idp_auth_complete.ejs
  139. +6 −5 resources/views/index.ejs
  140. +9 −4 resources/views/layout.ejs
  141. +21 −0 resources/views/signup.ejs
  142. +1 −1  resources/views/tos.ejs
  143. +63 −0 resources/views/verify_email_address.ejs
  144. +0 −28 resources/views/verifyemail.ejs
  145. +0 −64 resources/views/verifyuser.ejs
  146. +2 −2 scripts/browserid.spec
  147. +21 −0 scripts/check_primary_support
  148. +3 −3 scripts/compress.sh
  149. +10 −0 scripts/run_locally.js
  150. +1 −1  scripts/serve_example.js
  151. +90 −0 scripts/serve_example_primary.js
  152. +209 −0 tests/add-email-with-assertion-test.js
  153. +99 −0 tests/auth-with-assertion-test.js
  154. +28 −26 tests/cookie-session-security-test.js
  155. +218 −28 tests/db-test.js
  156. +30 −0 tests/lib/primary.js
  157. +2 −1  tests/lib/start-stop.js
  158. +3 −1 tests/list-emails-wsapi-test.js
  159. +8 −2 tests/password-bcrypt-update-test.js
  160. +12 −0 tests/password-update-test.js
  161. +116 −0 tests/post-limiting-test.js
  162. +233 −0 tests/primary-then-secondary-test.js
  163. +209 −38 tests/verifier-test.js
View
12 ChangeLog
@@ -1,3 +1,15 @@
+train-2011.01.05:
+ * client entropy pool mixes in randomness from server for better browser RNG: #298, #800
+ * new assertion format that avoids double (base64) encoding - 33% smaller: #507
+ * Turn license URL in ToS into a clickable link: #382
+ * limit post requests to 10kb: #822
+ * improved password length checks, check in client and server code more often
+ * after authenticating we store your userid rather than email in the session (many issues/possible attacks relate to this): #388
+ * session cookies are now encrypted, sent only when required, and generally more awesome: #416, #832
+ * IE8 display tweaks
+ * primary support 90% implemented but disabled in this train (*major* changes including schema, but not user visible)
+ * (hotfix on 2012.01.09) explicitly call .removeAllListeners() during http forwarding to eliminate memory leak: #839
+
train-2011.12.28:
* improve animation during cert/assertion procedures in dialog: #709
* user visible error message in dialog when under back breaking load: #738
View
4 bin/browserid
@@ -91,6 +91,10 @@ app.use(express.logger({
}
}));
+// limit all content bodies to 10kb, at which point we'll forcefully
+// close down the connection.
+app.use(express.limit("10kb"));
+
var statsd_config = config.get('statsd');
if (statsd_config && statsd_config.enabled) {
logger_statsd = require("connect-logger-statsd");
View
21 bin/load_gen
@@ -231,18 +231,27 @@ function poll() {
if (activitiesToRun.indexOf(act) !== -1) {
outstanding[act]++;
activity[act].startFunc(configuration, function(err) {
- if (err) winston.error(err);
outstanding[act]--;
- if (undefined === completed[act]) completed[act] = [ 0, 0 ];
- completed[act][err ? 1 : 0]++;
+ if (undefined === completed[act]) completed[act] = [ 0, 0, 0 ];
+ if (err) {
+ if (err.indexOf('server is too busy') != -1) {
+ completed[act][2]++;
+ } else {
+ completed[act][1]++;
+ winston.error(err);
+ }
+ } else {
+ completed[act][0]++;
+ }
});
} else {
- if (undefined === completed[act]) completed[act] = [ 0, 0 ];
+ if (undefined === completed[act]) completed[act] = [ 0, 0, 0 ];
completed[act][0]++;
}
}
var numErrors = 0;
+ var num503s = 0;
var numStarted = 0;
function updateAverages(elapsed) {
@@ -252,6 +261,7 @@ function poll() {
Object.keys(completed).forEach(function(k) {
numActCompleted += completed[k][0];
numErrors += completed[k][1];
+ num503s += completed[k][2];
});
completed = { };
var avgUsersThisPeriod = (numActCompleted / activitiesPerUserPerSecond) * (elapsed / 1000);
@@ -279,7 +289,8 @@ function poll() {
"\t", averages[1].toFixed(2),
"\t", averages[2].toFixed(2),
"\t", actSumString,
- "\t", numErrors ? "(" + numErrors + " ERRORS!)" : "");
+ "\t", numErrors ? "(" + numErrors + " ERRORS!)" : "",
+ "\t", num503s ? " (" + num503s + " 503s)" : "");
}
// ** how much time has elapsed since the last poll?
View
9 example/primary/.well-known/vep
@@ -0,0 +1,9 @@
+{
+ "provisioning": "/provision.html",
+ "authentication": "/sign_in.html",
+ "public-key": {
+ "algorithm":"RS",
+ "n":"12150646309575666544658791157045645163757575303887721078710172478749665834070170928206481109930468203684865378748391106975718718959563139020999088154811587703010353786258781016056954403240590264386124614262627869140351957459406743577995562584260319925426603313709939197457399455483061173844980456364611416651616781677992262613894501858312578942785385470086255995080524454431673067666784338623903663347118104807073332038428581918086381436489000619294471995801952293054002077519255312962379161724622526642212406262043172654176008908362058486885146430345217844546587383034154533029235541666677817563420349484368059586917",
+ "e":"65537"
+ }
+}
View
70 example/primary/index.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="initial-scale=1.0; maximum-scale=1.0; width=device-width;">
+<title>
+BrowserID Example Primary
+</title>
+<style type="text/css">
+body { margin: auto; font: 13px/1.5 Helvetica, Arial, 'Liberation Sans', FreeSans, sans-serif; }
+.title { font-size: 2em; font-weight: bold; text-align: center; margin: 1.5em; }
+.intro { font-size: 1.2em; width: 600px; margin: auto; }
+.main { text-align: center; margin-top: 2em; font-size: 1.2em; width: 500px; margin: auto; display: none; }
+#whoareyou { font-weight: bold; }
+
+</style>
+</head>
+<body>
+<div class="title">
+ Example BrowserID Primary
+</div>
+
+<div class="intro">
+ An example BrowserID Primary identity authority. Useful for local testing and development.
+</div>
+
+<div class="main" id="logged_in">
+ You are logged in as <span id="whoareyou"></span>. <a id="logout" href="#">logout</a>.
+</div>
+
+<div class="main" id="logged_out">
+ You are not logged in. Who would you like to be?
+ <input type="text">
+ <button>doit</button>
+</div>
+
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript">
+$(document).ready(function() {
+ function updateWhoIAm() {
+ $(".main").hide();
+ $.get('/api/whoami')
+ .success(function(r) {
+ if (r) {
+ $("#whoareyou").text(r);
+ $("#logged_in").show();
+ } else {
+ $("#logged_out").show();
+ }
+ });
+ }
+ $("button").click(function(e) {
+ $.get('/api/login', { user: $.trim($("input").val()) })
+ .success(function(r) {
+ updateWhoIAm();
+ });
+ });
+
+ $("#logout").click(function(e) {
+ $.get('/api/logout')
+ .success(function(r) {
+ updateWhoIAm();
+ });
+ });
+
+ updateWhoIAm();
+});
+</script>
+</body>
+</html>
View
0  example/jquery-min.js → example/primary/jquery.js
File renamed without changes
View
54 example/primary/provision.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript" src="https://browserid.org/provisioning_api.js"></script>
+<script type="text/javascript" src="/jquery.js"></script>
+<script type="text/javascript">
+
+ // an alias
+ var fail = navigator.id.raiseProvisioningFailure;
+
+ // begin provisioning! This both gives us indicated to browserid that we're
+ // a well formed provisioning page and gives us the parameters of the provisioning
+ navigator.id.beginProvisioning(function(email, cert_duration) {
+ var user = /^([^@]+)@/.exec(email)[1];
+
+ // now we have the email address that wishes to be provisioned!
+ // is he authenticated to eyedee.me?
+ $.get('/api/whoami')
+ .success(function(who) {
+ if (user != who) {
+ return fail('user is not authenticated as target user');
+ }
+
+ // Awesome! The user is authenticated as who we want to provision. let's
+ // generate a keypair
+ navigator.id.genKeyPair(function(pubkey) {
+ // finally, once we have a public key from the browser, we'll certify it, and
+ // go pass it back
+ $.ajax({
+ url: '/api/cert_key',
+ data: JSON.stringify({
+ pubkey: pubkey,
+ duration: cert_duration
+ }),
+ type: 'POST',
+ headers: { "Content-Type": 'application/json' },
+ dataType: 'json',
+ success: function(r) {
+ // all done! woo!
+ navigator.id.registerCertificate(r.cert);
+ },
+ error: function(r) {
+ fail("couldn't certify key");
+ }
+ });
+ });
+ })
+ .error(function() {
+ fail('user is not authenticated');
+ });
+ });
+</script>
+</head>
+</html>
View
1  example/primary/sample.privatekey
@@ -0,0 +1 @@
+{"algorithm":"RS","n":"12150646309575666544658791157045645163757575303887721078710172478749665834070170928206481109930468203684865378748391106975718718959563139020999088154811587703010353786258781016056954403240590264386124614262627869140351957459406743577995562584260319925426603313709939197457399455483061173844980456364611416651616781677992262613894501858312578942785385470086255995080524454431673067666784338623903663347118104807073332038428581918086381436489000619294471995801952293054002077519255312962379161724622526642212406262043172654176008908362058486885146430345217844546587383034154533029235541666677817563420349484368059586917","e":"65537","d":"4576260781837071842193157180361592071303664055813671962186294570898545886786914704989861806863508349047919986322940288592423594917052916069629682361493727501615950722587629763634798747809443360175819977323411869539211550207829724456958122453992362737374381640787683739122037791776987029525545151661621734244874349529048661411099247940582269058676233440040049437304921327491451073610454313255668312747483229646664526246661039878272676051442941399721167635066787800827207115116788251299159776482379477214028479230999290715576867912303554133701642412629365556930442426107748834086621121121510537980546710422816219192577"}
View
1  example/primary/sample.publickey
@@ -0,0 +1 @@
+{"algorithm":"RS","n":"12150646309575666544658791157045645163757575303887721078710172478749665834070170928206481109930468203684865378748391106975718718959563139020999088154811587703010353786258781016056954403240590264386124614262627869140351957459406743577995562584260319925426603313709939197457399455483061173844980456364611416651616781677992262613894501858312578942785385470086255995080524454431673067666784338623903663347118104807073332038428581918086381436489000619294471995801952293054002077519255312962379161724622526642212406262043172654176008908362058486885146430345217844546587383034154533029235541666677817563420349484368059586917","e":"65537"}
View
59 example/primary/sign_in.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="initial-scale=1.0; maximum-scale=1.0; width=device-width;">
+<title>
+BrowserID Example Primary
+</title>
+<style type="text/css">
+body { margin: auto; font: 13px/1.5 Helvetica, Arial, 'Liberation Sans', FreeSans, sans-serif; }
+.title { font-size: 2em; font-weight: bold; text-align: center; margin: 1.5em; }
+.intro { font-size: 1.2em; width: 600px; margin: auto; }
+.main { text-align: center; margin-top: 2em; font-size: 1.2em; width: 500px; margin: auto; }
+#who { font-weight: bold; }
+
+</style>
+</head>
+<body>
+<div class="title">
+ Sign into our example primary!
+</div>
+
+<div class="main" id="logged_out">
+ Sign in as <span id="who">...</span>
+ <button>doit</button>
+</div>
+
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript">
+
+function getParameterByName(name)
+{
+ name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
+ var regexS = "[\\?&]" + name + "=([^&#]*)";
+ var regex = new RegExp(regexS);
+ var results = regex.exec(window.location.href);
+ if(results == null)
+ return "";
+ else
+ return decodeURIComponent(results[1].replace(/\+/g, " "));
+}
+
+$(document).ready(function() {
+ try {
+ var who = getParameterByName("email").replace(/@.*$/, "");
+ $('#who').text(who);
+ } catch(e) {
+ alert("uh oh: " + e);
+ }
+ $("button").click(function(e) {
+ $.get('/api/login', { user: who })
+ .success(function(r) {
+ window.location = getParameterByName('return_to');
+ });
+ });
+});
+</script>
+</body>
+</html>
View
0  example/index.html → example/rp/index.html
File renamed without changes
View
16 example/rp/jquery-min.js
@@ -0,0 +1,16 @@
+/*!
+ * jQuery JavaScript Library v1.5.2
+ * http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Thu Mar 31 15:28:23 2011 -0400
+ */
+(function(a,b){function ci(a){return d.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cf(a){if(!b_[a]){var b=d("<"+a+">").appendTo("body"),c=b.css("display");b.remove();if(c==="none"||c==="")c="block";b_[a]=c}return b_[a]}function ce(a,b){var c={};d.each(cd.concat.apply([],cd.slice(0,b)),function(){c[this]=a});return c}function b$(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function bZ(){try{return new a.XMLHttpRequest}catch(b){}}function bY(){d(a).unload(function(){for(var a in bW)bW[a](0,1)})}function bS(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var e=a.dataTypes,f={},g,h,i=e.length,j,k=e[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h==="string"&&(f[h.toLowerCase()]=a.converters[h]);l=k,k=e[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=f[m]||f["* "+k];if(!n){p=b;for(o in f){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=f[j[1]+" "+k];if(p){o=f[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&d.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function bR(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function bQ(a,b,c,e){if(d.isArray(b)&&b.length)d.each(b,function(b,f){c||bs.test(a)?e(a,f):bQ(a+"["+(typeof f==="object"||d.isArray(f)?b:"")+"]",f,c,e)});else if(c||b==null||typeof b!=="object")e(a,b);else if(d.isArray(b)||d.isEmptyObject(b))e(a,"");else for(var f in b)bQ(a+"["+f+"]",b[f],c,e)}function bP(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bJ,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l==="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bP(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bP(a,c,d,e,"*",g));return l}function bO(a){return function(b,c){typeof b!=="string"&&(c=b,b="*");if(d.isFunction(c)){var e=b.toLowerCase().split(bD),f=0,g=e.length,h,i,j;for(;f<g;f++)h=e[f],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bq(a,b,c){var e=b==="width"?bk:bl,f=b==="width"?a.offsetWidth:a.offsetHeight;if(c==="border")return f;d.each(e,function(){c||(f-=parseFloat(d.css(a,"padding"+this))||0),c==="margin"?f+=parseFloat(d.css(a,"margin"+this))||0:f-=parseFloat(d.css(a,"border"+this+"Width"))||0});return f}function bc(a,b){b.src?d.ajax({url:b.src,async:!1,dataType:"script"}):d.globalEval(b.text||b.textContent||b.innerHTML||""),b.parentNode&&b.parentNode.removeChild(b)}function bb(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function ba(a,b){if(b.nodeType===1){var c=b.nodeName.toLowerCase();b.clearAttributes(),b.mergeAttributes(a);if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(d.expando)}}function _(a,b){if(b.nodeType===1&&d.hasData(a)){var c=d.expando,e=d.data(a),f=d.data(b,e);if(e=e[c]){var g=e.events;f=f[c]=d.extend({},e);if(g){delete f.handle,f.events={};for(var h in g)for(var i=0,j=g[h].length;i<j;i++)d.event.add(b,h+(g[h][i].namespace?".":"")+g[h][i].namespace,g[h][i],g[h][i].data)}}}}function $(a,b){return d.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function Q(a,b,c){if(d.isFunction(b))return d.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return d.grep(a,function(a,d){return a===b===c});if(typeof b==="string"){var e=d.grep(a,function(a){return a.nodeType===1});if(L.test(b))return d.filter(b,e,!c);b=d.filter(b,e)}return d.grep(a,function(a,e){return d.inArray(a,b)>=0===c})}function P(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function H(a,b){return(a&&a!=="*"?a+".":"")+b.replace(t,"`").replace(u,"&")}function G(a){var b,c,e,f,g,h,i,j,k,l,m,n,o,p=[],q=[],s=d._data(this,"events");if(a.liveFired!==this&&s&&s.live&&!a.target.disabled&&(!a.button||a.type!=="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var t=s.live.slice(0);for(i=0;i<t.length;i++)g=t[i],g.origType.replace(r,"")===a.type?q.push(g.selector):t.splice(i--,1);f=d(a.target).closest(q,a.currentTarget);for(j=0,k=f.length;j<k;j++){m=f[j];for(i=0;i<t.length;i++){g=t[i];if(m.selector===g.selector&&(!n||n.test(g.namespace))&&!m.elem.disabled){h=m.elem,e=null;if(g.preType==="mouseenter"||g.preType==="mouseleave")a.type=g.preType,e=d(a.relatedTarget).closest(g.selector)[0];(!e||e!==h)&&p.push({elem:h,handleObj:g,level:m.level})}}}for(j=0,k=p.length;j<k;j++){f=p[j];if(c&&f.level>c)break;a.currentTarget=f.elem,a.data=f.handleObj.data,a.handleObj=f.handleObj,o=f.handleObj.origHandler.apply(f.elem,arguments);if(o===!1||a.isPropagationStopped()){c=f.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function E(a,c,e){var f=d.extend({},e[0]);f.type=a,f.originalEvent={},f.liveFired=b,d.event.handle.call(c,f),f.isDefaultPrevented()&&e[0].preventDefault()}function y(){return!0}function x(){return!1}function i(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function h(a,c,e){if(e===b&&a.nodeType===1){e=a.getAttribute("data-"+c);if(typeof e==="string"){try{e=e==="true"?!0:e==="false"?!1:e==="null"?null:d.isNaN(e)?g.test(e)?d.parseJSON(e):e:parseFloat(e)}catch(f){}d.data(a,c,e)}else e=b}return e}var c=a.document,d=function(){function G(){if(!d.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(G,1);return}d.ready()}}var d=function(a,b){return new d.fn.init(a,b,g)},e=a.jQuery,f=a.$,g,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,i=/\S/,j=/^\s+/,k=/\s+$/,l=/\d/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=navigator.userAgent,w,x,y,z=Object.prototype.toString,A=Object.prototype.hasOwnProperty,B=Array.prototype.push,C=Array.prototype.slice,D=String.prototype.trim,E=Array.prototype.indexOf,F={};d.fn=d.prototype={constructor:d,init:function(a,e,f){var g,i,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!e&&c.body){this.context=c,this[0]=c.body,this.selector="body",this.length=1;return this}if(typeof a==="string"){g=h.exec(a);if(!g||!g[1]&&e)return!e||e.jquery?(e||f).find(a):this.constructor(e).find(a);if(g[1]){e=e instanceof d?e[0]:e,k=e?e.ownerDocument||e:c,j=m.exec(a),j?d.isPlainObject(e)?(a=[c.createElement(j[1])],d.fn.attr.call(a,e,!0)):a=[k.createElement(j[1])]:(j=d.buildFragment([g[1]],[k]),a=(j.cacheable?d.clone(j.fragment):j.fragment).childNodes);return d.merge(this,a)}i=c.getElementById(g[2]);if(i&&i.parentNode){if(i.id!==g[2])return f.find(a);this.length=1,this[0]=i}this.context=c,this.selector=a;return this}if(d.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return d.makeArray(a,this)},selector:"",jquery:"1.5.2",length:0,size:function(){return this.length},toArray:function(){return C.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var e=this.constructor();d.isArray(a)?B.apply(e,a):d.merge(e,a),e.prevObject=this,e.context=this.context,b==="find"?e.selector=this.selector+(this.selector?" ":"")+c:b&&(e.selector=this.selector+"."+b+"("+c+")");return e},each:function(a,b){return d.each(this,a,b)},ready:function(a){d.bindReady(),x.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(C.apply(this,arguments),"slice",C.call(arguments).join(","))},map:function(a){return this.pushStack(d.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:B,sort:[].sort,splice:[].splice},d.fn.init.prototype=d.fn,d.extend=d.fn.extend=function(){var a,c,e,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i==="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!=="object"&&!d.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){e=i[c],f=a[c];if(i===f)continue;l&&f&&(d.isPlainObject(f)||(g=d.isArray(f)))?(g?(g=!1,h=e&&d.isArray(e)?e:[]):h=e&&d.isPlainObject(e)?e:{},i[c]=d.extend(l,h,f)):f!==b&&(i[c]=f)}return i},d.extend({noConflict:function(b){a.$=f,b&&(a.jQuery=e);return d},isReady:!1,readyWait:1,ready:function(a){a===!0&&d.readyWait--;if(!d.readyWait||a!==!0&&!d.isReady){if(!c.body)return setTimeout(d.ready,1);d.isReady=!0;if(a!==!0&&--d.readyWait>0)return;x.resolveWith(c,[d]),d.fn.trigger&&d(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!x){x=d._Deferred();if(c.readyState==="complete")return setTimeout(d.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",y,!1),a.addEventListener("load",d.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",y),a.attachEvent("onload",d.ready);var b=!1;try{b=a.frameElement==null}catch(e){}c.documentElement.doScroll&&b&&G()}}},isFunction:function(a){return d.type(a)==="function"},isArray:Array.isArray||function(a){return d.type(a)==="array"},isWindow:function(a){return a&&typeof a==="object"&&"setInterval"in a},isNaN:function(a){return a==null||!l.test(a)||isNaN(a)},type:function(a){return a==null?String(a):F[z.call(a)]||"object"},isPlainObject:function(a){if(!a||d.type(a)!=="object"||a.nodeType||d.isWindow(a))return!1;if(a.constructor&&!A.call(a,"constructor")&&!A.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a){}return c===b||A.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!=="string"||!b)return null;b=d.trim(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return a.JSON&&a.JSON.parse?a.JSON.parse(b):(new Function("return "+b))();d.error("Invalid JSON: "+b)},parseXML:function(b,c,e){a.DOMParser?(e=new DOMParser,c=e.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),e=c.documentElement,(!e||!e.nodeName||e.nodeName==="parsererror")&&d.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(a){if(a&&i.test(a)){var b=c.head||c.getElementsByTagName("head")[0]||c.documentElement,e=c.createElement("script");d.support.scriptEval()?e.appendChild(c.createTextNode(a)):e.text=a,b.insertBefore(e,b.firstChild),b.removeChild(e)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,e){var f,g=0,h=a.length,i=h===b||d.isFunction(a);if(e){if(i){for(f in a)if(c.apply(a[f],e)===!1)break}else for(;g<h;)if(c.apply(a[g++],e)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(var j=a[0];g<h&&c.call(j,g,j)!==!1;j=a[++g]){}return a},trim:D?function(a){return a==null?"":D.call(a)}:function(a){return a==null?"":(a+"").replace(j,"").replace(k,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var e=d.type(a);a.length==null||e==="string"||e==="function"||e==="regexp"||d.isWindow(a)?B.call(c,a):d.merge(c,a)}return c},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var c=0,d=b.length;c<d;c++)if(b[c]===a)return c;return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length==="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,b,c){var d=[],e;for(var f=0,g=a.length;f<g;f++)e=b(a[f],f,c),e!=null&&(d[d.length]=e);return d.concat.apply([],d)},guid:1,proxy:function(a,c,e){arguments.length===2&&(typeof c==="string"?(e=a,a=e[c],c=b):c&&!d.isFunction(c)&&(e=c,c=b)),!c&&a&&(c=function(){return a.apply(e||this,arguments)}),a&&(c.guid=a.guid=a.guid||c.guid||d.guid++);return c},access:function(a,c,e,f,g,h){var i=a.length;if(typeof c==="object"){for(var j in c)d.access(a,j,c[j],f,g,e);return a}if(e!==b){f=!h&&f&&d.isFunction(e);for(var k=0;k<i;k++)g(a[k],c,f?e.call(a[k],k,g(a[k],c)):e,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}d.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.subclass=this.subclass,a.fn.init=function b(b,c){c&&c instanceof d&&!(c instanceof a)&&(c=a(c));return d.fn.init.call(this,b,c,e)},a.fn.init.prototype=a.fn;var e=a(c);return a},browser:{}}),d.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){F["[object "+b+"]"]=b.toLowerCase()}),w=d.uaMatch(v),w.browser&&(d.browser[w.browser]=!0,d.browser.version=w.version),d.browser.webkit&&(d.browser.safari=!0),E&&(d.inArray=function(a,b){return E.call(b,a)}),i.test(" ")&&(j=/^[\s\xA0]+/,k=/[\s\xA0]+$/),g=d(c),c.addEventListener?y=function(){c.removeEventListener("DOMContentLoaded",y,!1),d.ready()}:c.attachEvent&&(y=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",y),d.ready())});return d}(),e="then done fail isResolved isRejected promise".split(" "),f=[].slice;d.extend({_Deferred:function(){var a=[],b,c,e,f={done:function(){if(!e){var c=arguments,g,h,i,j,k;b&&(k=b,b=0);for(g=0,h=c.length;g<h;g++)i=c[g],j=d.type(i),j==="array"?f.done.apply(f,i):j==="function"&&a.push(i);k&&f.resolveWith(k[0],k[1])}return this},resolveWith:function(d,f){if(!e&&!b&&!c){f=f||[],c=1;try{while(a[0])a.shift().apply(d,f)}finally{b=[d,f],c=0}}return this},resolve:function(){f.resolveWith(this,arguments);return this},isResolved:function(){return c||b},cancel:function(){e=1,a=[];return this}};return f},Deferred:function(a){var b=d._Deferred(),c=d._Deferred(),f;d.extend(b,{then:function(a,c){b.done(a).fail(c);return this},fail:c.done,rejectWith:c.resolveWith,reject:c.resolve,isRejected:c.isResolved,promise:function(a){if(a==null){if(f)return f;f=a={}}var c=e.length;while(c--)a[e[c]]=b[e[c]];return a}}),b.done(c.cancel).fail(b.cancel),delete b.cancel,a&&a.call(b,b);return b},when:function(a){function i(a){return function(c){b[a]=arguments.length>1?f.call(arguments,0):c,--g||h.resolveWith(h,f.call(b,0))}}var b=arguments,c=0,e=b.length,g=e,h=e<=1&&a&&d.isFunction(a.promise)?a:d.Deferred();if(e>1){for(;c<e;c++)b[c]&&d.isFunction(b[c].promise)?b[c].promise().then(i(c),h.reject):--g;g||h.resolveWith(h,b)}else h!==a&&h.resolveWith(h,e?[a]:[]);return h.promise()}}),function(){d.support={};var b=c.createElement("div");b.style.display="none",b.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var e=b.getElementsByTagName("*"),f=b.getElementsByTagName("a")[0],g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=b.getElementsByTagName("input")[0];if(e&&e.length&&f){d.support={leadingWhitespace:b.firstChild.nodeType===3,tbody:!b.getElementsByTagName("tbody").length,htmlSerialize:!!b.getElementsByTagName("link").length,style:/red/.test(f.getAttribute("style")),hrefNormalized:f.getAttribute("href")==="/a",opacity:/^0.55$/.test(f.style.opacity),cssFloat:!!f.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,deleteExpando:!0,optDisabled:!1,checkClone:!1,noCloneEvent:!0,noCloneChecked:!0,boxModel:null,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableHiddenOffsets:!0,reliableMarginRight:!0},i.checked=!0,d.support.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,d.support.optDisabled=!h.disabled;var j=null;d.support.scriptEval=function(){if(j===null){var b=c.documentElement,e=c.createElement("script"),f="script"+d.now();try{e.appendChild(c.createTextNode("window."+f+"=1;"))}catch(g){}b.insertBefore(e,b.firstChild),a[f]?(j=!0,delete a[f]):j=!1,b.removeChild(e)}return j};try{delete b.test}catch(k){d.support.deleteExpando=!1}!b.addEventListener&&b.attachEvent&&b.fireEvent&&(b.attachEvent("onclick",function l(){d.support.noCloneEvent=!1,b.detachEvent("onclick",l)}),b.cloneNode(!0).fireEvent("onclick")),b=c.createElement("div"),b.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";var m=c.createDocumentFragment();m.appendChild(b.firstChild),d.support.checkClone=m.cloneNode(!0).cloneNode(!0).lastChild.checked,d(function(){var a=c.createElement("div"),b=c.getElementsByTagName("body")[0];if(b){a.style.width=a.style.paddingLeft="1px",b.appendChild(a),d.boxModel=d.support.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,d.support.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="<div style='width:4px;'></div>",d.support.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";var e=a.getElementsByTagName("td");d.support.reliableHiddenOffsets=e[0].offsetHeight===0,e[0].style.display="",e[1].style.display="none",d.support.reliableHiddenOffsets=d.support.reliableHiddenOffsets&&e[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(a.style.width="1px",a.style.marginRight="0",d.support.reliableMarginRight=(parseInt(c.defaultView.getComputedStyle(a,null).marginRight,10)||0)===0),b.removeChild(a).style.display="none",a=e=null}});var n=function(a){var b=c.createElement("div");a="on"+a;if(!b.attachEvent)return!0;var d=a in b;d||(b.setAttribute(a,"return;"),d=typeof b[a]==="function");return d};d.support.submitBubbles=n("submit"),d.support.changeBubbles=n("change"),b=e=f=null}}();var g=/^(?:\{.*\}|\[.*\])$/;d.extend({cache:{},uuid:0,expando:"jQuery"+(d.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?d.cache[a[d.expando]]:a[d.expando];return!!a&&!i(a)},data:function(a,c,e,f){if(d.acceptData(a)){var g=d.expando,h=typeof c==="string",i,j=a.nodeType,k=j?d.cache:a,l=j?a[d.expando]:a[d.expando]&&d.expando;if((!l||f&&l&&!k[l][g])&&h&&e===b)return;l||(j?a[d.expando]=l=++d.uuid:l=d.expando),k[l]||(k[l]={},j||(k[l].toJSON=d.noop));if(typeof c==="object"||typeof c==="function")f?k[l][g]=d.extend(k[l][g],c):k[l]=d.extend(k[l],c);i=k[l],f&&(i[g]||(i[g]={}),i=i[g]),e!==b&&(i[c]=e);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[c]:i}},removeData:function(b,c,e){if(d.acceptData(b)){var f=d.expando,g=b.nodeType,h=g?d.cache:b,j=g?b[d.expando]:d.expando;if(!h[j])return;if(c){var k=e?h[j][f]:h[j];if(k){delete k[c];if(!i(k))return}}if(e){delete h[j][f];if(!i(h[j]))return}var l=h[j][f];d.support.deleteExpando||h!=a?delete h[j]:h[j]=null,l?(h[j]={},g||(h[j].toJSON=d.noop),h[j][f]=l):g&&(d.support.deleteExpando?delete b[d.expando]:b.removeAttribute?b.removeAttribute(d.expando):b[d.expando]=null)}},_data:function(a,b,c){return d.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=d.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),d.fn.extend({data:function(a,c){var e=null;if(typeof a==="undefined"){if(this.length){e=d.data(this[0]);if(this[0].nodeType===1){var f=this[0].attributes,g;for(var i=0,j=f.length;i<j;i++)g=f[i].name,g.indexOf("data-")===0&&(g=g.substr(5),h(this[0],g,e[g]))}}return e}if(typeof a==="object")return this.each(function(){d.data(this,a)});var k=a.split(".");k[1]=k[1]?"."+k[1]:"";if(c===b){e=this.triggerHandler("getData"+k[1]+"!",[k[0]]),e===b&&this.length&&(e=d.data(this[0],a),e=h(this[0],a,e));return e===b&&k[1]?this.data(k[0]):e}return this.each(function(){var b=d(this),e=[k[0],c];b.triggerHandler("setData"+k[1]+"!",e),d.data(this,a,c),b.triggerHandler("changeData"+k[1]+"!",e)})},removeData:function(a){return this.each(function(){d.removeData(this,a)})}}),d.extend({queue:function(a,b,c){if(a){b=(b||"fx")+"queue";var e=d._data(a,b);if(!c)return e||[];!e||d.isArray(c)?e=d._data(a,b,d.makeArray(c)):e.push(c);return e}},dequeue:function(a,b){b=b||"fx";var c=d.queue(a,b),e=c.shift();e==="inprogress"&&(e=c.shift()),e&&(b==="fx"&&c.unshift("inprogress"),e.call(a,function(){d.dequeue(a,b)})),c.length||d.removeData(a,b+"queue",!0)}}),d.fn.extend({queue:function(a,c){typeof a!=="string"&&(c=a,a="fx");if(c===b)return d.queue(this[0],a);return this.each(function(b){var e=d.queue(this,a,c);a==="fx"&&e[0]!=="inprogress"&&d.dequeue(this,a)})},dequeue:function(a){return this.each(function(){d.dequeue(this,a)})},delay:function(a,b){a=d.fx?d.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(){var c=this;setTimeout(function(){d.dequeue(c,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var j=/[\n\t\r]/g,k=/\s+/,l=/\r/g,m=/^(?:href|src|style)$/,n=/^(?:button|input)$/i,o=/^(?:button|input|object|select|textarea)$/i,p=/^a(?:rea)?$/i,q=/^(?:radio|checkbox)$/i;d.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"},d.fn.extend({attr:function(a,b){return d.access(this,a,b,!0,d.attr)},removeAttr:function(a,b){return this.each(function(){d.attr(this,a,""),this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(d.isFunction(a))return this.each(function(b){var c=d(this);c.addClass(a.call(this,b,c.attr("class")))});if(a&&typeof a==="string"){var b=(a||"").split(k);for(var c=0,e=this.length;c<e;c++){var f=this[c];if(f.nodeType===1)if(f.className){var g=" "+f.className+" ",h=f.className;for(var i=0,j=b.length;i<j;i++)g.indexOf(" "+b[i]+" ")<0&&(h+=" "+b[i]);f.className=d.trim(h)}else f.className=a}}return this},removeClass:function(a){if(d.isFunction(a))return this.each(function(b){var c=d(this);c.removeClass(a.call(this,b,c.attr("class")))});if(a&&typeof a==="string"||a===b){var c=(a||"").split(k);for(var e=0,f=this.length;e<f;e++){var g=this[e];if(g.nodeType===1&&g.className)if(a){var h=(" "+g.className+" ").replace(j," ");for(var i=0,l=c.length;i<l;i++)h=h.replace(" "+c[i]+" "," ");g.className=d.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,e=typeof b==="boolean";if(d.isFunction(a))return this.each(function(c){var e=d(this);e.toggleClass(a.call(this,c,e.attr("class"),b),b)});return this.each(function(){if(c==="string"){var f,g=0,h=d(this),i=b,j=a.split(k);while(f=j[g++])i=e?i:!h.hasClass(f),h[i?"addClass":"removeClass"](f)}else if(c==="undefined"||c==="boolean")this.className&&d._data(this,"__className__",this.className),this.className=this.className||a===!1?"":d._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ";for(var c=0,d=this.length;c<d;c++)if((" "+this[c].className+" ").replace(j," ").indexOf(b)>-1)return!0;return!1},val:function(a){if(!arguments.length){var c=this[0];if(c){if(d.nodeName(c,"option")){var e=c.attributes.value;return!e||e.specified?c.value:c.text}if(d.nodeName(c,"select")){var f=c.selectedIndex,g=[],h=c.options,i=c.type==="select-one";if(f<0)return null;for(var j=i?f:0,k=i?f+1:h.length;j<k;j++){var m=h[j];if(m.selected&&(d.support.optDisabled?!m.disabled:m.getAttribute("disabled")===null)&&(!m.parentNode.disabled||!d.nodeName(m.parentNode,"optgroup"))){a=d(m).val();if(i)return a;g.push(a)}}if(i&&!g.length&&h.length)return d(h[f]).val();return g}if(q.test(c.type)&&!d.support.checkOn)return c.getAttribute("value")===null?"on":c.value;return(c.value||"").replace(l,"")}return b}var n=d.isFunction(a);return this.each(function(b){var c=d(this),e=a;if(this.nodeType===1){n&&(e=a.call(this,b,c.val())),e==null?e="":typeof e==="number"?e+="":d.isArray(e)&&(e=d.map(e,function(a){return a==null?"":a+""}));if(d.isArray(e)&&q.test(this.type))this.checked=d.inArray(c.val(),e)>=0;else if(d.nodeName(this,"select")){var f=d.makeArray(e);d("option",this).each(function(){this.selected=d.inArray(d(this).val(),f)>=0}),f.length||(this.selectedIndex=-1)}else this.value=e}})}}),d.extend({attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,e,f){if(!a||a.nodeType===3||a.nodeType===8||a.nodeType===2)return b;if(f&&c in d.attrFn)return d(a)[c](e);var g=a.nodeType!==1||!d.isXMLDoc(a),h=e!==b;c=g&&d.props[c]||c;if(a.nodeType===1){var i=m.test(c);if(c==="selected"&&!d.support.optSelected){var j=a.parentNode;j&&(j.selectedIndex,j.parentNode&&j.parentNode.selectedIndex)}if((c in a||a[c]!==b)&&g&&!i){h&&(c==="type"&&n.test(a.nodeName)&&a.parentNode&&d.error("type property can't be changed"),e===null?a.nodeType===1&&a.removeAttribute(c):a[c]=e);if(d.nodeName(a,"form")&&a.getAttributeNode(c))return a.getAttributeNode(c).nodeValue;if(c==="tabIndex"){var k=a.getAttributeNode("tabIndex");return k&&k.specified?k.value:o.test(a.nodeName)||p.test(a.nodeName)&&a.href?0:b}return a[c]}if(!d.support.style&&g&&c==="style"){h&&(a.style.cssText=""+e);return a.style.cssText}h&&a.setAttribute(c,""+e);if(!a.attributes[c]&&(a.hasAttribute&&!a.hasAttribute(c)))return b;var l=!d.support.hrefNormalized&&g&&i?a.getAttribute(c,2):a.getAttribute(c);return l===null?b:l}h&&(a[c]=e);return a[c]}});var r=/\.(.*)$/,s=/^(?:textarea|input|select)$/i,t=/\./g,u=/ /g,v=/[^\w\s.|`]/g,w=function(a){return a.replace(v,"\\$&")};d.event={add:function(c,e,f,g){if(c.nodeType!==3&&c.nodeType!==8){try{d.isWindow(c)&&(c!==a&&!c.frameElement)&&(c=a)}catch(h){}if(f===!1)f=x;else if(!f)return;var i,j;f.handler&&(i=f,f=i.handler),f.guid||(f.guid=d.guid++);var k=d._data(c);if(!k)return;var l=k.events,m=k.handle;l||(k.events=l={}),m||(k.handle=m=function(a){return typeof d!=="undefined"&&d.event.triggered!==a.type?d.event.handle.apply(m.elem,arguments):b}),m.elem=c,e=e.split(" ");var n,o=0,p;while(n=e[o++]){j=i?d.extend({},i):{handler:f,data:g},n.indexOf(".")>-1?(p=n.split("."),n=p.shift(),j.namespace=p.slice(0).sort().join(".")):(p=[],j.namespace=""),j.type=n,j.guid||(j.guid=f.guid);var q=l[n],r=d.event.special[n]||{};if(!q){q=l[n]=[];if(!r.setup||r.setup.call(c,g,p,m)===!1)c.addEventListener?c.addEventListener(n,m,!1):c.attachEvent&&c.attachEvent("on"+n,m)}r.add&&(r.add.call(c,j),j.handler.guid||(j.handler.guid=f.guid)),q.push(j),d.event.global[n]=!0}c=null}},global:{},remove:function(a,c,e,f){if(a.nodeType!==3&&a.nodeType!==8){e===!1&&(e=x);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=d.hasData(a)&&d._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(e=c.handler,c=c.type);if(!c||typeof c==="string"&&c.charAt(0)==="."){c=c||"";for(h in t)d.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+d.map(m.slice(0).sort(),w).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!e){for(j=0;j<p.length;j++){q=p[j];if(l||n.test(q.namespace))d.event.remove(a,r,q.handler,j),p.splice(j--,1)}continue}o=d.event.special[h]||{};for(j=f||0;j<p.length;j++){q=p[j];if(e.guid===q.guid){if(l||n.test(q.namespace))f==null&&p.splice(j--,1),o.remove&&o.remove.call(a,q);if(f!=null)break}}if(p.length===0||f!=null&&p.length===1)(!o.teardown||o.teardown.call(a,m)===!1)&&d.removeEvent(a,h,s.handle),g=null,delete t[h]}if(d.isEmptyObject(t)){var u=s.handle;u&&(u.elem=null),delete s.events,delete s.handle,d.isEmptyObject(s)&&d.removeData(a,b,!0)}}},trigger:function(a,c,e){var f=a.type||a,g=arguments[3];if(!g){a=typeof a==="object"?a[d.expando]?a:d.extend(d.Event(f),a):d.Event(f),f.indexOf("!")>=0&&(a.type=f=f.slice(0,-1),a.exclusive=!0),e||(a.stopPropagation(),d.event.global[f]&&d.each(d.cache,function(){var b=d.expando,e=this[b];e&&e.events&&e.events[f]&&d.event.trigger(a,c,e.handle.elem)}));if(!e||e.nodeType===3||e.nodeType===8)return b;a.result=b,a.target=e,c=d.makeArray(c),c.unshift(a)}a.currentTarget=e;var h=d._data(e,"handle");h&&h.apply(e,c);var i=e.parentNode||e.ownerDocument;try{e&&e.nodeName&&d.noData[e.nodeName.toLowerCase()]||e["on"+f]&&e["on"+f].apply(e,c)===!1&&(a.result=!1,a.preventDefault())}catch(j){}if(!a.isPropagationStopped()&&i)d.event.trigger(a,c,i,!0);else if(!a.isDefaultPrevented()){var k,l=a.target,m=f.replace(r,""),n=d.nodeName(l,"a")&&m==="click",o=d.event.special[m]||{};if((!o._default||o._default.call(e,a)===!1)&&!n&&!(l&&l.nodeName&&d.noData[l.nodeName.toLowerCase()])){try{l[m]&&(k=l["on"+m],k&&(l["on"+m]=null),d.event.triggered=a.type,l[m]())}catch(p){}k&&(l["on"+m]=k),d.event.triggered=b}}},handle:function(c){var e,f,g,h,i,j=[],k=d.makeArray(arguments);c=k[0]=d.event.fix(c||a.event),c.currentTarget=this,e=c.type.indexOf(".")<0&&!c.exclusive,e||(g=c.type.split("."),c.type=g.shift(),j=g.slice(0).sort(),h=new RegExp("(^|\\.)"+j.join("\\.(?:.*\\.)?")+"(\\.|$)")),c.namespace=c.namespace||j.join("."),i=d._data(this,"events"),f=(i||{})[c.type];if(i&&f){f=f.slice(0);for(var l=0,m=f.length;l<m;l++){var n=f[l];if(e||h.test(n.namespace)){c.handler=n.handler,c.data=n.data,c.handleObj=n;var o=n.handler.apply(this,k);o!==b&&(c.result=o,o===!1&&(c.preventDefault(),c.stopPropagation()));if(c.isImmediatePropagationStopped())break}}}return c.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(a){if(a[d.expando])return a;var e=a;a=d.Event(e);for(var f=this.props.length,g;f;)g=this.props[--f],a[g]=e[g];a.target||(a.target=a.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),!a.relatedTarget&&a.fromElement&&(a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement);if(a.pageX==null&&a.clientX!=null){var h=c.documentElement,i=c.body;a.pageX=a.clientX+(h&&h.scrollLeft||i&&i.scrollLeft||0)-(h&&h.clientLeft||i&&i.clientLeft||0),a.pageY=a.clientY+(h&&h.scrollTop||i&&i.scrollTop||0)-(h&&h.clientTop||i&&i.clientTop||0)}a.which==null&&(a.charCode!=null||a.keyCode!=null)&&(a.which=a.charCode!=null?a.charCode:a.keyCode),!a.metaKey&&a.ctrlKey&&(a.metaKey=a.ctrlKey),!a.which&&a.button!==b&&(a.which=a.button&1?1:a.button&2?3:a.button&4?2:0);return a},guid:1e8,proxy:d.proxy,special:{ready:{setup:d.bindReady,teardown:d.noop},live:{add:function(a){d.event.add(this,H(a.origType,a.selector),d.extend({},a,{handler:G,guid:a.handler.guid}))},remove:function(a){d.event.remove(this,H(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,c){d.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}}},d.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},d.Event=function(a){if(!this.preventDefault)return new d.Event(a);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?y:x):this.type=a,this.timeStamp=d.now(),this[d.expando]=!0},d.Event.prototype={preventDefault:function(){this.isDefaultPrevented=y;var a=this.originalEvent;a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=y;var a=this.originalEvent;a&&(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=y,this.stopPropagation()},isDefaultPrevented:x,isPropagationStopped:x,isImmediatePropagationStopped:x};var z=function(a){var b=a.relatedTarget;try{if(b&&b!==c&&!b.parentNode)return;while(b&&b!==this)b=b.parentNode;b!==this&&(a.type=a.data,d.event.handle.apply(this,arguments))}catch(e){}},A=function(a){a.type=a.data,d.event.handle.apply(this,arguments)};d.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){d.event.special[a]={setup:function(c){d.event.add(this,b,c&&c.selector?A:z,a)},teardown:function(a){d.event.remove(this,b,a&&a.selector?A:z)}}}),d.support.submitBubbles||(d.event.special.submit={setup:function(a,b){if(this.nodeName&&this.nodeName.toLowerCase()!=="form")d.event.add(this,"click.specialSubmit",function(a){var b=a.target,c=b.type;(c==="submit"||c==="image")&&d(b).closest("form").length&&E("submit",this,arguments)}),d.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,c=b.type;(c==="text"||c==="password")&&d(b).closest("form").length&&a.keyCode===13&&E("submit",this,arguments)});else return!1},teardown:function(a){d.event.remove(this,".specialSubmit")}});if(!d.support.changeBubbles){var B,C=function(a){var b=a.type,c=a.value;b==="radio"||b==="checkbox"?c=a.checked:b==="select-multiple"?c=a.selectedIndex>-1?d.map(a.options,function(a){return a.selected}).join("-"):"":a.nodeName.toLowerCase()==="select"&&(c=a.selectedIndex);return c},D=function D(a){var c=a.target,e,f;if(s.test(c.nodeName)&&!c.readOnly){e=d._data(c,"_change_data"),f=C(c),(a.type!=="focusout"||c.type!=="radio")&&d._data(c,"_change_data",f);if(e===b||f===e)return;if(e!=null||f)a.type="change",a.liveFired=b,d.event.trigger(a,arguments[1],c)}};d.event.special.change={filters:{focusout:D,beforedeactivate:D,click:function(a){var b=a.target,c=b.type;(c==="radio"||c==="checkbox"||b.nodeName.toLowerCase()==="select")&&D.call(this,a)},keydown:function(a){var b=a.target,c=b.type;(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&D.call(this,a)},beforeactivate:function(a){var b=a.target;d._data(b,"_change_data",C(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in B)d.event.add(this,c+".specialChange",B[c]);return s.test(this.nodeName)},teardown:function(a){d.event.remove(this,".specialChange");return s.test(this.nodeName)}},B=d.event.special.change.filters,B.focus=B.beforeactivate}c.addEventListener&&d.each({focus:"focusin",blur:"focusout"},function(a,b){function f(a){var c=d.event.fix(a);c.type=b,c.originalEvent={},d.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var e=0;d.event.special[b]={setup:function(){e++===0&&c.addEventListener(a,f,!0)},teardown:function(){--e===0&&c.removeEventListener(a,f,!0)}}}),d.each(["bind","one"],function(a,c){d.fn[c]=function(a,e,f){if(typeof a==="object"){for(var g in a)this[c](g,e,a[g],f);return this}if(d.isFunction(e)||e===!1)f=e,e=b;var h=c==="one"?d.proxy(f,function(a){d(this).unbind(a,h);return f.apply(this,arguments)}):f;if(a==="unload"&&c!=="one")this.one(a,e,f);else for(var i=0,j=this.length;i<j;i++)d.event.add(this[i],a,h,e);return this}}),d.fn.extend({unbind:function(a,b){if(typeof a!=="object"||a.preventDefault)for(var e=0,f=this.length;e<f;e++)d.event.remove(this[e],a,b);else for(var c in a)this.unbind(c,a[c]);return this},delegate:function(a,b,c,d){return this.live(b,c,d,a)},undelegate:function(a,b,c){return arguments.length===0?this.unbind("live"):this.die(b,null,c,a)},trigger:function(a,b){return this.each(function(){d.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){var c=d.Event(a);c.preventDefault(),c.stopPropagation(),d.event.trigger(c,b,this[0]);return c.result}},toggle:function(a){var b=arguments,c=1;while(c<b.length)d.proxy(a,b[c++]);return this.click(d.proxy(a,function(e){var f=(d._data(this,"lastToggle"+a.guid)||0)%c;d._data(this,"lastToggle"+a.guid,f+1),e.preventDefault();return b[f].apply(this,arguments)||!1}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var F={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};d.each(["live","die"],function(a,c){d.fn[c]=function(a,e,f,g){var h,i=0,j,k,l,m=g||this.selector,n=g?this:d(this.context);if(typeof a==="object"&&!a.preventDefault){for(var o in a)n[c](o,e,a[o],m);return this}d.isFunction(e)&&(f=e,e=b),a=(a||"").split(" ");while((h=a[i++])!=null){j=r.exec(h),k="",j&&(k=j[0],h=h.replace(r,""));if(h==="hover"){a.push("mouseenter"+k,"mouseleave"+k);continue}l=h,h==="focus"||h==="blur"?(a.push(F[h]+k),h=h+k):h=(F[h]||h)+k;if(c==="live")for(var p=0,q=n.length;p<q;p++)d.event.add(n[p],"live."+H(h,m),{data:e,selector:m,handler:f,origType:h,origHandler:f,preType:l});else n.unbind("live."+H(h,m),f)}return this}}),d.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){d.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.bind(b,a,c):this.trigger(b)},d.attrFn&&(d.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}if(i.nodeType===1){f||(i.sizcache=c,i.sizset=g);if(typeof b!=="string"){if(i===b){j=!0;break}}else if(k.filter(b,[i]).length>0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}i.nodeType===1&&!f&&(i.sizcache=c,i.sizset=g);if(i.nodeName.toLowerCase()===b){j=i;break}i=i[a]}d[g]=j}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,e,g){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!=="string")return e;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(f.call(n)==="[object Array]")if(u)if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&e.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&e.push(j[t]);else e.push.apply(e,n);else p(n,e);o&&(k(o,h,e,g),k.uniqueSort(e));return e};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},k.matches=function(a,b){return k(a,null,null,b)},k.matchesSelector=function(a,b){return k(b,null,null,[a]).length>0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e<f;e++){var g,h=l.order[e];if(g=l.leftMatch[h].exec(a)){var j=g[1];g.splice(1,1);if(j.substr(j.length-1)!=="\\"){g[1]=(g[1]||"").replace(i,""),d=l.find[h](g,b,c);if(d!=null){a=a.replace(l.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!=="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},k.filter=function(a,c,d,e){var f,g,h=a,i=[],j=c,m=c&&c[0]&&k.isXML(c[0]);while(a&&c.length){for(var n in l.filter)if((f=l.leftMatch[n].exec(a))!=null&&f[2]){var o,p,q=l.filter[n],r=f[1];g=!1,f.splice(1,1);if(r.substr(r.length-1)==="\\")continue;j===i&&(i=[]);if(l.preFilter[n]){f=l.preFilter[n](f,j,d,i,e,m);if(f){if(f===!0)continue}else g=o=!0}if(f)for(var s=0;(p=j[s])!=null;s++)if(p){o=q(p,f,s,j);var t=e^!!o;d&&o!=null?t?g=!0:j[s]=!1:t&&(i.push(p),g=!0)}if(o!==b){d||(j=i),a=a.replace(l.match[n],"");if(!g)return[];break}}if(a===h)if(g==null)k.error(a);else break;h=a}return j},k.error=function(a){throw"Syntax error, unrecognized expression: "+a};var l=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b==="string",d=c&&!j.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1){}a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&k.filter(b,a,!0)},">":function(a,b){var c,d=typeof b==="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&k.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=u;typeof b==="string"&&!j.test(b)&&(b=b.toLowerCase(),d=b,g=t),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=u;typeof b==="string"&&!j.test(b)&&(b=b.toLowerCase(),d=b,g=t),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!=="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!=="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!=="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(i,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return"text"===c&&(b===c||b===null)},radio:function(a){return"radio"===a.type},checkbox:function(a){return"checkbox"===a.type},file:function(a){return"file"===a.type},password:function(a){return"password"===a.type},submit:function(a){return"submit"===a.type},image:function(a){return"image"===a.type},reset:function(a){return"reset"===a.type},button:function(a){return"button"===a.type||a.nodeName.toLowerCase()==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}k.error(e)},CHILD:function(a,b){var c=b[1],d=a;switch(c){case"only":case"first":while(d=d.previousSibling)if(d.nodeType===1)return!1;if(c==="first")return!0;d=a;case"last":while(d=d.nextSibling)if(d.nodeType===1)return!1;return!0;case"nth":var e=b[2],f=b[3];if(e===1&&f===0)return!0;var g=b[0],h=a.parentNode;if(h&&(h.sizcache!==g||!a.nodeIndex)){var i=0;for(d=h.firstChild;d;d=d.nextSibling)d.nodeType===1&&(d.nodeIndex=++i);h.sizcache=g}var j=a.nodeIndex-f;return e===0?j===0:j%e===0&&j/e>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(f.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length==="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var r,s;c.documentElement.compareDocumentPosition?r=function(a,b){if(a===b){g=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(r=function(a,b){var c,d,e=[],f=[],h=a.parentNode,i=b.parentNode,j=h;if(a===b){g=!0;return 0}if(h===i)return s(a,b);if(!h)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return s(e[k],f[k]);return k===c?s(a,f[k],-1):s(e[k],b,1)},s=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),k.getText=function(a){var b="",c;for(var d=0;a[d];d++)c=a[d],c.nodeType===3||c.nodeType===4?b+=c.nodeValue:c.nodeType!==8&&(b+=k.getText(c.childNodes));return b},function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!=="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!=="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!=="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!=="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!=="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g<h;g++)k(a,f[g],d);return k.filter(e,d)};d.find=k,d.expr=k.selectors,d.expr[":"]=d.expr.filters,d.unique=k.uniqueSort,d.text=k.getText,d.isXMLDoc=k.isXML,d.contains=k.contains}();var I=/Until$/,J=/^(?:parents|prevUntil|prevAll)/,K=/,/,L=/^.[^:#\[\.,]*$/,M=Array.prototype.slice,N=d.expr.match.POS,O={children:!0,contents:!0,next:!0,prev:!0};d.fn.extend({find:function(a){var b=this.pushStack("","find",a),c=0;for(var e=0,f=this.length;e<f;e++){c=b.length,d.find(a,this[e],b);if(e>0)for(var g=c;g<b.length;g++)for(var h=0;h<c;h++)if(b[h]===b[g]){b.splice(g--,1);break}}return b},has:function(a){var b=d(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(d.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(Q(this,a,!1),"not",a)},filter:function(a){return this.pushStack(Q(this,a,!0),"filter",a)},is:function(a){return!!a&&d.filter(a,this).length>0},closest:function(a,b){var c=[],e,f,g=this[0];if(d.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(e=0,f=a.length;e<f;e++)i=a[e],j[i]||(j[i]=d.expr.match.POS.test(i)?d(i,b||this.context):i);while(g&&g.ownerDocument&&g!==b){for(i in j)h=j[i],(h.jquery?h.index(g)>-1:d(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=N.test(a)?d(a,b||this.context):null;for(e=0,f=this.length;e<f;e++){g=this[e];while(g){if(l?l.index(g)>-1:d.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b)break}}c=c.length>1?d.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a==="string")return d.inArray(this[0],a?d(a):this.parent().children());return d.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a==="string"?d(a,b):d.makeArray(a),e=d.merge(this.get(),c);return this.pushStack(P(c[0])||P(e[0])?e:d.unique(e))},andSelf:function(){return this.add(this.prevObject)}}),d.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return d.dir(a,"parentNode")},parentsUntil:function(a,b,c){return d.dir(a,"parentNode",c)},next:function(a){return d.nth(a,2,"nextSibling")},prev:function(a){return d.nth(a,2,"previousSibling")},nextAll:function(a){return d.dir(a,"nextSibling")},prevAll:function(a){return d.dir(a,"previousSibling")},nextUntil:function(a,b,c){return d.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return d.dir(a,"previousSibling",c)},siblings:function(a){return d.sibling(a.parentNode.firstChild,a)},children:function(a){return d.sibling(a.firstChild)},contents:function(a){return d.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:d.makeArray(a.childNodes)}},function(a,b){d.fn[a]=function(c,e){var f=d.map(this,b,c),g=M.call(arguments);I.test(a)||(e=c),e&&typeof e==="string"&&(f=d.filter(e,f)),f=this.length>1&&!O[a]?d.unique(f):f,(this.length>1||K.test(e))&&J.test(a)&&(f=f.reverse());return this.pushStack(f,a,g.join(","))}}),d.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?d.find.matchesSelector(b[0],a)?[b[0]]:[]:d.find.matches(a,b)},dir:function(a,c,e){var f=[],g=a[c];while(g&&g.nodeType!==9&&(e===b||g.nodeType!==1||!d(g).is(e)))g.nodeType===1&&f.push(g),g=g[c];return f},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var R=/ jQuery\d+="(?:\d+|null)"/g,S=/^\s+/,T=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,U=/<([\w:]+)/,V=/<tbody/i,W=/<|&#?\w+;/,X=/<(?:script|object|embed|option|style)/i,Y=/checked\s*(?:[^=]|=\s*.checked.)/i,Z={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};Z.optgroup=Z.option,Z.tbody=Z.tfoot=Z.colgroup=Z.caption=Z.thead,Z.th=Z.td,d.support.htmlSerialize||(Z._default=[1,"div<div>","</div>"]),d.fn.extend({text:function(a){if(d.isFunction(a))return this.each(function(b){var c=d(this);c.text(a.call(this,b,c.text()))});if(typeof a!=="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return d.text(this)},wrapAll:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapAll(a.call(this,b))});if(this[0]){var b=d(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapInner(a.call(this,b))});return this.each(function(){var b=d(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){d(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){d.nodeName(this,"body")||d(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=d(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,d(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,e;(e=this[c])!=null;c++)if(!a||d.filter(a,[e]).length)!b&&e.nodeType===1&&(d.cleanData(e.getElementsByTagName("*")),d.cleanData([e])),e.parentNode&&e.parentNode.removeChild(e);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&d.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return d.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(R,""):null;if(typeof a!=="string"||X.test(a)||!d.support.leadingWhitespace&&S.test(a)||Z[(U.exec(a)||["",""])[1].toLowerCase()])d.isFunction(a)?this.each(function(b){var c=d(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);else{a=a.replace(T,"<$1></$2>");try{for(var c=0,e=this.length;c<e;c++)this[c].nodeType===1&&(d.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(f){this.empty().append(a)}}return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(d.isFunction(a))return this.each(function(b){var c=d(this),e=c.html();c.replaceWith(a.call(this,b,e))});typeof a!=="string"&&(a=d(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;d(this).remove(),b?d(b).before(a):d(c).append(a)})}return this.length?this.pushStack(d(d.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,e){var f,g,h,i,j=a[0],k=[];if(!d.support.checkClone&&arguments.length===3&&typeof j==="string"&&Y.test(j))return this.each(function(){d(this).domManip(a,c,e,!0)});if(d.isFunction(j))return this.each(function(f){var g=d(this);a[0]=j.call(this,f,c?g.html():b),g.domManip(a,c,e)});if(this[0]){i=j&&j.parentNode,d.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?f={fragment:i}:f=d.buildFragment(a,this,k),h=f.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&d.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)e.call(c?$(this[l],g):this[l],f.cacheable||m>1&&l<n?d.clone(h,!0,!0):h)}k.length&&d.each(k,bc)}return this}}),d.buildFragment=function(a,b,e){var f,g,h,i=b&&b[0]?b[0].ownerDocument||b[0]:c;a.length===1&&typeof a[0]==="string"&&a[0].length<512&&i===c&&a[0].charAt(0)==="<"&&!X.test(a[0])&&(d.support.checkClone||!Y.test(a[0]))&&(g=!0,h=d.fragments[a[0]],h&&(h!==1&&(f=h))),f||(f=i.createDocumentFragment(),d.clean(a,i,f,e)),g&&(d.fragments[a[0]]=h?f:1);return{fragment:f,cacheable:g}},d.fragments={},d.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){d.fn[a]=function(c){var e=[],f=d(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&f.length===1){f[b](this[0]);return this}for(var h=0,i=f.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();d(f[h])[b](j),e=e.concat(j)}return this.pushStack(e,a,f.selector)}}),d.extend({clone:function(a,b,c){var e=a.cloneNode(!0),f,g,h;if((!d.support.noCloneEvent||!d.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!d.isXMLDoc(a)){ba(a,e),f=bb(a),g=bb(e);for(h=0;f[h];++h)ba(f[h],g[h])}if(b){_(a,e);if(c){f=bb(a),g=bb(e);for(h=0;f[h];++h)_(f[h],g[h])}}return e},clean:function(a,b,e,f){b=b||c,typeof b.createElement==="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var g=[];for(var h=0,i;(i=a[h])!=null;h++){typeof i==="number"&&(i+="");if(!i)continue;if(typeof i!=="string"||W.test(i)){if(typeof i==="string"){i=i.replace(T,"<$1></$2>");var j=(U.exec(i)||["",""])[1].toLowerCase(),k=Z[j]||Z._default,l=k[0],m=b.createElement("div");m.innerHTML=k[1]+i+k[2];while(l--)m=m.lastChild;if(!d.support.tbody){var n=V.test(i),o=j==="table"&&!n?m.firstChild&&m.firstChild.childNodes:k[1]==="<table>"&&!n?m.childNodes:[];for(var p=o.length-1;p>=0;--p)d.nodeName(o[p],"tbody")&&!o[p].childNodes.length&&o[p].parentNode.removeChild(o[p])}!d.support.leadingWhitespace&&S.test(i)&&m.insertBefore(b.createTextNode(S.exec(i)[0]),m.firstChild),i=m.childNodes}}else i=b.createTextNode(i);i.nodeType?g.push(i):g=d.merge(g,i)}if(e)for(h=0;g[h];h++)!f||!d.nodeName(g[h],"script")||g[h].type&&g[h].type.toLowerCase()!=="text/javascript"?(g[h].nodeType===1&&g.splice.apply(g,[h+1,0].concat(d.makeArray(g[h].getElementsByTagName("script")))),e.appendChild(g[h])):f.push(g[h].parentNode?g[h].parentNode.removeChild(g[h]):g[h]);return g},cleanData:function(a){var b,c,e=d.cache,f=d.expando,g=d.event.special,h=d.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&d.noData[j.nodeName.toLowerCase()])continue;c=j[d.expando];if(c){b=e[c]&&e[c][f];if(b&&b.events){for(var k in b.events)g[k]?d.event.remove(j,k):d.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[d.expando]:j.removeAttribute&&j.removeAttribute(d.expando),delete e[c]}}}});var bd=/alpha\([^)]*\)/i,be=/opacity=([^)]*)/,bf=/-([a-z])/ig,bg=/([A-Z]|^ms)/g,bh=/^-?\d+(?:px)?$/i,bi=/^-?\d/,bj={position:"absolute",visibility:"hidden",display:"block"},bk=["Left","Right"],bl=["Top","Bottom"],bm,bn,bo,bp=function(a,b){return b.toUpperCase()};d.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return d.access(this,a,c,!0,function(a,c,e){return e!==b?d.style(a,c,e):d.css(a,c)})},d.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bm(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{zIndex:!0,fontWeight:!0,opacity:!0,zoom:!0,lineHeight:!0},cssProps:{"float":d.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,e,f){if(a&&a.nodeType!==3&&a.nodeType!==8&&a.style){var g,h=d.camelCase(c),i=a.style,j=d.cssHooks[h];c=d.cssProps[h]||h;if(e===b){if(j&&"get"in j&&(g=j.get(a,!1,f))!==b)return g;return i[c]}if(typeof e==="number"&&isNaN(e)||e==null)return;typeof e==="number"&&!d.cssNumber[h]&&(e+="px");if(!j||!("set"in j)||(e=j.set(a,e))!==b)try{i[c]=e}catch(k){}}},css:function(a,c,e){var f,g=d.camelCase(c),h=d.cssHooks[g];c=d.cssProps[g]||g;if(h&&"get"in h&&(f=h.get(a,!0,e))!==b)return f;if(bm)return bm(a,c,g)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]},camelCase:function(a){return a.replace(bf,bp)}}),d.curCSS=d.css,d.each(["height","width"],function(a,b){d.cssHooks[b]={get:function(a,c,e){var f;if(c){a.offsetWidth!==0?f=bq(a,b,e):d.swap(a,bj,function(){f=bq(a,b,e)});if(f<=0){f=bm(a,b,b),f==="0px"&&bo&&(f=bo(a,b,b));if(f!=null)return f===""||f==="auto"?"0px":f}if(f<0||f==null){f=a.style[b];return f===""||f==="auto"?"0px":f}return typeof f==="string"?f:f+"px"}},set:function(a,b){if(!bh.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),d.support.opacity||(d.cssHooks.opacity={get:function(a,b){return be.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style;c.zoom=1;var e=d.isNaN(b)?"":"alpha(opacity="+b*100+")",f=c.filter||"";c.filter=bd.test(f)?f.replace(bd,e):c.filter+" "+e}}),d(function(){d.support.reliableMarginRight||(d.cssHooks.marginRight={get:function(a,b){var c;d.swap(a,{display:"inline-block"},function(){b?c=bm(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bn=function(a,c,e){var f,g,h;e=e.replace(bg,"-$1").toLowerCase();if(!(g=a.ownerDocument.defaultView))return b;if(h=g.getComputedStyle(a,null))f=h.getPropertyValue(e),f===""&&!d.contains(a.ownerDocument.documentElement,a)&&(f=d.style(a,e));return f}),c.documentElement.currentStyle&&(bo=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bh.test(d)&&bi.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bm=bn||bo,d.expr&&d.expr.filters&&(d.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!d.support.reliableHiddenOffsets&&(a.style.display||d.css(a,"display"))==="none"},d.expr.filters.visible=function(a){return!d.expr.filters.hidden(a)});var br=/%20/g,bs=/\[\]$/,bt=/\r?\n/g,bu=/#.*$/,bv=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bw=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bx=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,by=/^(?:GET|HEAD)$/,bz=/^\/\//,bA=/\?/,bB=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bC=/^(?:select|textarea)/i,bD=/\s+/,bE=/([?&])_=[^&]*/,bF=/(^|\-)([a-z])/g,bG=function(a,b,c){return b+c.toUpperCase()},bH=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bI=d.fn.load,bJ={},bK={},bL,bM;try{bL=c.location.href}catch(bN){bL=c.createElement("a"),bL.href="",bL=bL.href}bM=bH.exec(bL.toLowerCase())||[],d.fn.extend({load:function(a,c,e){if(typeof a!=="string"&&bI)return bI.apply(this,arguments);if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var g=a.slice(f,a.length);a=a.slice(0,f)}var h="GET";c&&(d.isFunction(c)?(e=c,c=b):typeof c==="object"&&(c=d.param(c,d.ajaxSettings.traditional),h="POST"));var i=this;d.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?d("<div>").append(c.replace(bB,"")).find(g):c)),e&&i.each(e,[c,b,a])}});return this},serialize:function(){return d.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?d.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bC.test(this.nodeName)||bw.test(this.type))}).map(function(a,b){var c=d(this).val();return c==null?null:d.isArray(c)?d.map(c,function(a,c){return{name:b.name,value:a.replace(bt,"\r\n")}}):{name:b.name,value:c.replace(bt,"\r\n")}}).get()}}),d.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){d.fn[b]=function(a){return this.bind(b,a)}}),d.each(["get","post"],function(a,c){d[c]=function(a,e,f,g){d.isFunction(e)&&(g=g||f,f=e,e=b);return d.ajax({type:c,url:a,data:e,success:f,dataType:g})}}),d.extend({getScript:function(a,c){return d.get(a,b,c,"script")},getJSON:function(a,b,c){return d.get(a,b,c,"json")},ajaxSetup:function(a,b){b?d.extend(!0,a,d.ajaxSettings,b):(b=a,a=d.extend(!0,d.ajaxSettings,b));for(var c in {context:1,url:1})c in b?a[c]=b[c]:c in d.ajaxSettings&&(a[c]=d.ajaxSettings[c]);return a},ajaxSettings:{url:bL,isLocal:bx.test(bM[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":d.parseJSON,"text xml":d.parseXML}},ajaxPrefilter:bO(bJ),ajaxTransport:bO(bK),ajax:function(a,c){function v(a,c,l,n){if(r!==2){r=2,p&&clearTimeout(p),o=b,m=n||"",u.readyState=a?4:0;var q,t,v,w=l?bR(e,u,l):b,x,y;if(a>=200&&a<300||a===304){if(e.ifModified){if(x=u.getResponseHeader("Last-Modified"))d.lastModified[k]=x;if(y=u.getResponseHeader("Etag"))d.etag[k]=y}if(a===304)c="notmodified",q=!0;else try{t=bS(e,w),c="success",q=!0}catch(z){c="parsererror",v=z}}else{v=c;if(!c||a)c="error",a<0&&(a=0)}u.status=a,u.statusText=c,q?h.resolveWith(f,[t,c,u]):h.rejectWith(f,[u,c,v]),u.statusCode(j),j=b,s&&g.trigger("ajax"+(q?"Success":"Error"),[u,e,q?t:v]),i.resolveWith(f,[u,c]),s&&(g.trigger("ajaxComplete",[u,e]),--d.active||d.event.trigger("ajaxStop"))}}typeof a==="object"&&(c=a,a=b),c=c||{};var e=d.ajaxSetup({},c),f=e.context||e,g=f!==e&&(f.nodeType||f instanceof d)?d(f):d.event,h=d.Deferred(),i=d._Deferred(),j=e.statusCode||{},k,l={},m,n,o,p,q,r=0,s,t,u={readyState:0,setRequestHeader:function(a,b){r||(l[a.toLowerCase().replace(bF,bG)]=b);return this},getAllResponseHeaders:function(){return r===2?m:null},getResponseHeader:function(a){var c;if(r===2){if(!n){n={};while(c=bv.exec(m))n[c[1].toLowerCase()]=c[2]}c=n[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){r||(e.mimeType=a);return this},abort:function(a){a=a||"abort",o&&o.abort(a),v(0,a);return this}};h.promise(u),u.success=u.done,u.error=u.fail,u.complete=i.done,u.statusCode=function(a){if(a){var b;if(r<2)for(b in a)j[b]=[j[b],a[b]];else b=a[u.status],u.then(b,b)}return this},e.url=((a||e.url)+"").replace(bu,"").replace(bz,bM[1]+"//"),e.dataTypes=d.trim(e.dataType||"*").toLowerCase().split(bD),e.crossDomain==null&&(q=bH.exec(e.url.toLowerCase()),e.crossDomain=q&&(q[1]!=bM[1]||q[2]!=bM[2]||(q[3]||(q[1]==="http:"?80:443))!=(bM[3]||(bM[1]==="http:"?80:443)))),e.data&&e.processData&&typeof e.data!=="string"&&(e.data=d.param(e.data,e.traditional)),bP(bJ,e,c,u);if(r===2)return!1;s=e.global,e.type=e.type.toUpperCase(),e.hasContent=!by.test(e.type),s&&d.active++===0&&d.event.trigger("ajaxStart");if(!e.hasContent){e.data&&(e.url+=(bA.test(e.url)?"&":"?")+e.data),k=e.url;if(e.cache===!1){var w=d.now(),x=e.url.replace(bE,"$1_="+w);e.url=x+(x===e.url?(bA.test(e.url)?"&":"?")+"_="+w:"")}}if(e.data&&e.hasContent&&e.contentType!==!1||c.contentType)l["Content-Type"]=e.contentType;e.ifModified&&(k=k||e.url,d.lastModified[k]&&(l["If-Modified-Since"]=d.lastModified[k]),d.etag[k]&&(l["If-None-Match"]=d.etag[k])),l.Accept=e.dataTypes[0]&&e.accepts[e.dataTypes[0]]?e.accepts[e.dataTypes[0]]+(e.dataTypes[0]!=="*"?", */*; q=0.01":""):e.accepts["*"];for(t in e.headers)u.setRequestHeader(t,e.headers[t]);if(e.beforeSend&&(e.beforeSend.call(f,u,e)===!1||r===2)){u.abort();return!1}for(t in {success:1,error:1,complete:1})u[t](e[t]);o=bP(bK,e,c,u);if(o){u.readyState=1,s&&g.trigger("ajaxSend",[u,e]),e.async&&e.timeout>0&&(p=setTimeout(function(){u.abort("timeout")},e.timeout));try{r=1,o.send(l,v)}catch(y){status<2?v(-1,y):d.error(y)}}else v(-1,"No Transport");return u},param:function(a,c){var e=[],f=function(a,b){b=d.isFunction(b)?b():b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=d.ajaxSettings.traditional);if(d.isArray(a)||a.jquery&&!d.isPlainObject(a))d.each(a,function(){f(this.name,this.value)});else for(var g in a)bQ(g,a[g],c,f);return e.join("&").replace(br,"+")}}),d.extend({active:0,lastModified:{},etag:{}});var bT=d.now(),bU=/(\=)\?(&|$)|\?\?/i;d.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return d.expando+"_"+bT++}}),d.ajaxPrefilter("json jsonp",function(b,c,e){var f=typeof b.data==="string";if(b.dataTypes[0]==="jsonp"||c.jsonpCallback||c.jsonp!=null||b.jsonp!==!1&&(bU.test(b.url)||f&&bU.test(b.data))){var g,h=b.jsonpCallback=d.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2",m=function(){a[h]=i,g&&d.isFunction(i)&&a[h](g[0])};b.jsonp!==!1&&(j=j.replace(bU,l),b.url===j&&(f&&(k=k.replace(bU,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},e.then(m,m),b.converters["script json"]=function(){g||d.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),d.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){d.globalEval(a);return a}}}),d.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),d.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var bV=d.now(),bW,bX;d.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&bZ()||b$()}:bZ,bX=d.ajaxSettings.xhr(),d.support.ajax=!!bX,d.support.cors=bX&&"withCredentials"in bX,bX=b,d.support.ajax&&d.ajaxTransport(function(a){if(!a.crossDomain||d.support.cors){var c;return{send:function(e,f){var g=a.xhr(),h,i;a.username?g.open(a.type,a.url,a.async,a.username,a.password):g.open(a.type,a.url,a.async);if(a.xhrFields)for(i in a.xhrFields)g[i]=a.xhrFields[i];a.mimeType&&g.overrideMimeType&&g.overrideMimeType(a.mimeType),!a.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(i in e)g.setRequestHeader(i,e[i])}catch(j){}g.send(a.hasContent&&a.data||null),c=function(e,i){var j,k,l,m,n;try{if(c&&(i||g.readyState===4)){c=b,h&&(g.onreadystatechange=d.noop,delete bW[h]);if(i)g.readyState!==4&&g.abort();else{j=g.status,l=g.getAllResponseHeaders(),m={},n=g.responseXML,n&&n.documentElement&&(m.xml=n),m.text=g.responseText;try{k=g.statusText}catch(o){k=""}j||!a.isLocal||a.crossDomain?j===1223&&(j=204):j=m.text?200:404}}}catch(p){i||f(-1,p)}m&&f(j,k,m,l)},a.async&&g.readyState!==4?(bW||(bW={},bY()),h=bV++,g.onreadystatechange=bW[h]=c):c()},abort:function(){c&&c(0,1)}}}});var b_={},ca=/^(?:toggle|show|hide)$/,cb=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cc,cd=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];d.fn.extend({show:function(a,b,c){var e,f;if(a||a===0)return this.animate(ce("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)e=this[g],f=e.style.display,!d._data(e,"olddisplay")&&f==="none"&&(f=e.style.display=""),f===""&&d.css(e,"display")==="none"&&d._data(e,"olddisplay",cf(e.nodeName));for(g=0;g<h;g++){e=this[g],f=e.style.display;if(f===""||f==="none")e.style.display=d._data(e,"olddisplay")||""}return this},hide:function(a,b,c){if(a||a===0)return this.animate(ce("hide",3),a,b,c);for(var e=0,f=this.length;e<f;e++){var g=d.css(this[e],"display");g!=="none"&&!d._data(this[e],"olddisplay")&&d._data(this[e],"olddisplay",g)}for(e=0;e<f;e++)this[e].style.display="none";return this},_toggle:d.fn.toggle,toggle:function(a,b,c){var e=typeof a==="boolean";d.isFunction(a)&&d.isFunction(b)?this._toggle.apply(this,arguments):a==null||e?this.each(function(){var b=e?a:d(this).is(":hidden");d(this)[b?"show":"hide"]()}):this.animate(ce("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,e){var f=d.speed(b,c,e);if(d.isEmptyObject(a))return this.each(f.complete);return this[f.queue===!1?"each":"queue"](function(){var b=d.extend({},f),c,e=this.nodeType===1,g=e&&d(this).is(":hidden"),h=this;for(c in a){var i=d.camelCase(c);c!==i&&(a[i]=a[c],delete a[c],c=i);if(a[c]==="hide"&&g||a[c]==="show"&&!g)return b.complete.call(this);if(e&&(c==="height"||c==="width")){b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY];if(d.css(this,"display")==="inline"&&d.css(this,"float")==="none")if(d.support.inlineBlockNeedsLayout){var j=cf(this.nodeName);j==="inline"?this.style.display="inline-block":(this.style.display="inline",this.style.zoom=1)}else this.style.display="inline-block"}d.isArray(a[c])&&((b.specialEasing=b.specialEasing||{})[c]=a[c][1],a[c]=a[c][0])}b.overflow!=null&&(this.style.overflow="hidden"),b.curAnim=d.extend({},a),d.each(a,function(c,e){var f=new d.fx(h,b,c);if(ca.test(e))f[e==="toggle"?g?"show":"hide":e](a);else{var i=cb.exec(e),j=f.cur();if(i){var k=parseFloat(i[2]),l=i[3]||(d.cssNumber[c]?"":"px");l!=="px"&&(d.style(h,c,(k||1)+l),j=(k||1)/f.cur()*j,d.style(h,c,j+l)),i[1]&&(k=(i[1]==="-="?-1:1)*k+j),f.custom(j,k,l)}else f.custom(j,e,"")}});return!0})},stop:function(a,b){var c=d.timers;a&&this.queue([]),this.each(function(){for(var a=c.length-1;a>=0;a--)c[a].elem===this&&(b&&c[a](!0),c.splice(a,1))}),b||this.dequeue();return this}}),d.each({slideDown:ce("show",1),slideUp:ce("hide",1),slideToggle:ce("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){d.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),d.extend({speed:function(a,b,c){var e=a&&typeof a==="object"?d.extend({},a):{complete:c||!c&&b||d.isFunction(a)&&a,duration:a,easing:c&&b||b&&!d.isFunction(b)&&b};e.duration=d.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in d.fx.speeds?d.fx.speeds[e.duration]:d.fx.speeds._default,e.old=e.complete,e.complete=function(){e.queue!==!1&&d(this).dequeue(),d.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig||(b.orig={})}}),d.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(d.fx.step[this.prop]||d.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=d.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,b,c){function g(a){return e.step(a)}var e=this,f=d.fx;this.startTime=d.now(),this.start=a,this.end=b,this.unit=c||this.unit||(d.cssNumber[this.prop]?"":"px"),this.now=this.start,this.pos=this.state=0,g.elem=this.elem,g()&&d.timers.push(g)&&!cc&&(cc=setInterval(f.tick,f.interval))},show:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),d(this.elem).show()},hide:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=d.now(),c=!0;if(a||b>=this.options.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),this.options.curAnim[this.prop]=!0;for(var e in this.options.curAnim)this.options.curAnim[e]!==!0&&(c=!1);if(c){if(this.options.overflow!=null&&!d.support.shrinkWrapBlocks){var f=this.elem,g=this.options;d.each(["","X","Y"],function(a,b){f.style["overflow"+b]=g.overflow[a]})}this.options.hide&&d(this.elem).hide();if(this.options.hide||this.options.show)for(var h in this.options.curAnim)d.style(this.elem,h,this.options.orig[h]);this.options.complete.call(this.elem)}return!1}var i=b-this.startTime;this.state=i/this.options.duration;var j=this.options.specialEasing&&this.options.specialEasing[this.prop],k=this.options.easing||(d.easing.swing?"swing":"linear");this.pos=d.easing[j||k](this.state,i,0,1,this.options.duration),this.now=this.start+(this.end-this.start)*this.pos,this.update();return!0}},d.extend(d.fx,{tick:function(){var a=d.timers;for(var b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||d.fx.stop()},interval:13,stop:function(){clearInterval(cc),cc=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){d.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit:a.elem[a.prop]=a.now}}}),d.expr&&d.expr.filters&&(d.expr.filters.animated=function(a){return d.grep(d.timers,function(b){return a===b.elem}).length});var cg=/^t(?:able|d|h)$/i,ch=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?d.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){d.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return d.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(e){}var f=b.ownerDocument,g=f.documentElement;if(!c||!d.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=f.body,i=ci(f),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||d.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||d.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:d.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){d.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return d.offset.bodyOffset(b);d.offset.initialize();var c,e=b.offsetParent,f=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(d.offset.supportsFixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===e&&(l+=b.offsetTop,m+=b.offsetLeft,d.offset.doesNotAddBorder&&(!d.offset.doesAddBorderForTableAndCells||!cg.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),f=e,e=b.offsetParent),d.offset.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;d.offset.supportsFixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},d.offset={initialize:function(){var a=c.body,b=c.createElement("div"),e,f,g,h,i=parseFloat(d.css(a,"marginTop"))||0,j="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";d.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),e=b.firstChild,f=e.firstChild,h=e.nextSibling.firstChild.firstChild,this.doesNotAddBorder=f.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,f.style.position="fixed",f.style.top="20px",this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15,f.style.position=f.style.top="",e.style.overflow="hidden",e.style.position="relative",this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),d.offset.initialize=d.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;d.offset.initialize(),d.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(d.css(a,"marginTop"))||0,c+=parseFloat(d.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var e=d.css(a,"position");e==="static"&&(a.style.position="relative");var f=d(a),g=f.offset(),h=d.css(a,"top"),i=d.css(a,"left"),j=(e==="absolute"||e==="fixed")&&d.inArray("auto",[h,i])>-1,k={},l={},m,n;j&&(l=f.position()),m=j?l.top:parseInt(h,10)||0,n=j?l.left:parseInt(i,10)||0,d.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):f.css(k)}},d.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),e=ch.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(d.css(a,"marginTop"))||0,c.left-=parseFloat(d.css(a,"marginLeft"))||0,e.top+=parseFloat(d.css(b[0],"borderTopWidth"))||0,e.left+=parseFloat(d.css(b[0],"borderLeftWidth"))||0;return{top:c.top-e.top,left:c.left-e.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&(!ch.test(a.nodeName)&&d.css(a,"position")==="static"))a=a.offsetParent;return a})}}),d.each(["Left","Top"],function(a,c){var e="scroll"+c;d.fn[e]=function(c){var f=this[0],g;if(!f)return null;if(c!==b)return this.each(function(){g=ci(this),g?g.scrollTo(a?d(g).scrollLeft():c,a?c:d(g).scrollTop()):this[e]=c});g=ci(f);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:d.support.boxModel&&g.document.documentElement[e]||g.document.body[e]:f[e]}}),d.each(["Height","Width"],function(a,c){var e=c.toLowerCase();d.fn["inner"+c]=function(){return this[0]?parseFloat(d.css(this[0],e,"padding")):null},d.fn["outer"+c]=function(a){return this[0]?parseFloat(d.css(this[0],e,a?"margin":"border")):null},d.fn[e]=function(a){var f=this[0];if(!f)return a==null?null:this;if(d.isFunction(a))return this.each(function(b){var c=d(this);c[e](a.call(this,b,c[e]()))});if(d.isWindow(f)){var g=f.document.documentElement["client"+c];return f.document.compatMode==="CSS1Compat"&&g||f.document.body["client"+c]||g}if(f.nodeType===9)return Math.max(f.documentElement["client"+c],f.body["scroll"+c],f.documentElement["scroll"+c],f.body["offset"+c],f.documentElement["offset"+c]);if(a===b){var h=d.css(f,e),i=parseFloat(h);return d.isNaN(i)?h:i}return this.css(e,typeof a==="string"?a:a+"px")}}),a.jQuery=a.$=d})(window);
View
6 lib/bcrypt.js
@@ -1,11 +1,13 @@
const
computecluster = require('compute-cluster'),
logger = require('../lib/logging.js').logger,
-bcrypt = require('bcrypt');
+bcrypt = require('bcrypt'),
+config = require('./configuration.js');
var cc = new computecluster({
module: path.join(__dirname, "bcrypt-compute.js"),
- max_backlog: 100000
+ max_backlog: 100000,
+ max_request_time: config.get('max_compute_duration')
});
cc.on('error', function(e) {
View
15 lib/browserid/views.js
@@ -47,6 +47,10 @@ exports.setup = function(app) {
});
});
+ app.get("/authenticate_with_primary", function(req,res, next) {
+ res.render('authenticate_with_primary.ejs', { layout: false });
+ });
+
app.get('/', function(req,res) {
res.render('index.ejs', {title: 'A Better Way to Sign In', fullpage: true});
});
@@ -55,6 +59,13 @@ exports.setup = function(app) {
res.render('signup.ejs', {title: 'Sign Up', fullpage: false});
});
+ app.get("/idp_auth_complete", function(req, res) {
+ res.render('idp_auth_complete.ejs', {
+ title: 'Sign In Complete',
+ fullpage: false
+ });
+ });
+
app.get("/forgot", function(req, res) {
res.render('forgot.ejs', {title: 'Forgot Password', fullpage: false, email: req.query.email});
});
@@ -76,11 +87,11 @@ exports.setup = function(app) {
});
app.get("/verify_email_address", function(req, res) {
- res.render('verifyuser.ejs', {title: 'Complete Registration', fullpage: true, token: req.query.token});
+ res.render('verify_email_address.ejs', {title: 'Complete Registration', fullpage: true, token: req.query.token});
});
app.get("/add_email_address", function(req,res) {
- res.render('verifyemail.ejs', {title: 'Verify Email Address', fullpage: false});
+ res.render('add_email_address.ejs', {title: 'Verify Email Address', fullpage: false});
});
// REDIRECTS
View
11 lib/configuration.js
@@ -105,8 +105,11 @@ g_configs.production = {
authentication_duration_ms: (2 * 7 * 24 * 60 * 60 * 1000),
certificate_validity_ms: (24 * 60 * 60 * 1000),
min_time_between_emails_ms: (60 * 1000),
- // may be specified to manipulate the maximum number of compute
- max_compute_processes: undefined
+ // may be specified to manipulate the maximum number of compute processes
+ max_compute_processes: undefined,
+ // return a 503 if a compute process would take over 10s to complete
+ max_compute_duration: 10,
+ disable_primary_support: true
};
@@ -124,7 +127,9 @@ g_configs.local = {
authentication_duration_ms: g_configs.production.authentication_duration_ms,
certificate_validity_ms: g_configs.production.certificate_validity_ms,
min_time_between_emails_ms: g_configs.production.min_time_between_emails_ms,
- max_compute_processes: undefined
+ max_compute_processes: undefined,
+ max_compute_duration: 10,
+ disable_primary_support: false
};
// test environments are variations on local
View
9 lib/db.js
@@ -105,14 +105,19 @@ exports.onReady = function(f) {
// these are read only database calls
[
'emailKnown',
+ 'userKnown',
'isStaged',
'emailsBelongToSameAccount',
'emailForVerificationSecret',
+ 'haveVerificationSecret',
'verificationSecretForEmail',
'checkAuth',
'listEmails',
'lastStaged',
- 'ping'
+ 'ping',
+ 'emailType',
+ 'userOwnsEmail',
+ 'emailToUID'
].forEach(function(fn) {
exports[fn] = function() {
checkReady();
@@ -130,6 +135,8 @@ exports.onReady = function(f) {
'removeEmail',
'cancelAccount',
'updatePassword',
+ 'createUserWithPrimaryEmail',
+ 'addPrimaryEmailToAccount'
].forEach(function(fn) {
exports[fn] = function() {
if (!config.get('database').may_write) {
View
253 lib/db/json.js
@@ -55,14 +55,25 @@ var dbPath = path.join(configuration.get('var_path'), "authdb.json");
/* The JSON database. The structure is thus:
* [
* {
+ * id: <numerical user id>
* password: "somepass",
- * emails: [
- * "lloyd@hilaiel.com"
- * ]
+ * emails: {
+ * "lloyd@hilaiel.com": {
+ * type: 'secondary'
+ * }
+ * }
* }
* ]
*/
+function getNextUserID() {
+ var max = 1;
+ jsel.forEach(".id", db.users, function(id) {
+ if (id >= max) max = id + 1;
+ });
+ return max;
+};
+
var db = {
users: [ ],
stagedEmails: { },
@@ -121,10 +132,16 @@ exports.close = function(cb) {
exports.emailKnown = function(email, cb) {
sync();
- var m = jsel.match(".emails :val(" + ESC(email) + ")", db.users);
+ var m = jsel.match(".emails ." + ESC(email), db.users);
setTimeout(function() { cb(m.length > 0) }, 0);
};
+exports.emailType = function(email, cb) {
+ sync();
+ var m = jsel.match(".emails ." + ESC(email), db.users);
+ process.nextTick(function() { cb(m.length ? m[0].type : undefined); });
+};
+
exports.isStaged = function(email, cb) {
if (cb) {
setTimeout(function() {
@@ -147,26 +164,46 @@ exports.lastStaged = function(email, cb) {
exports.emailsBelongToSameAccount = function(lhs, rhs, cb) {
sync();
- emailToUserID(lhs, function(lhs_uid) {
- emailToUserID(rhs, function(rhs_uid) {
- cb(lhs_uid === rhs_uid);
- }, function (error) {
- cb(false);
- });
- }, function (error) {
- cb(false);
+ var m = jsel.match(".emails:has(."+ESC(lhs)+"):has(."+ESC(rhs)+")", db.users);
+ process.nextTick(function() {
+ cb(m && m.length == 1);
});
};
-function addEmailToAccount(existing_email, email, cb) {
- emailToUserID(existing_email, function(userID) {
- if (userID == undefined) {
- cb("no such email: " + existing_email, undefined);
- } else {
- db.users[userID].emails.push(email);
+exports.emailToUID = function(email, cb) {
+ sync();
+ var m = jsel.match(":root > object:has(.emails > ." + ESC(email) + ") > .id", db.users);
+ if (m.length === 0) m = undefined;
+ else m = m[0];
+ process.nextTick(function() {
+ cb(m);
+ });
+};
+
+exports.userOwnsEmail = function(uid, email, cb) {
+ sync();
+ var m = jsel.match(":root > object:has(:root > .id:expr(x=" + ESC(uid) + ")):has(.emails > ." + ESC(email) + ")", db.users);
+ process.nextTick(function() {
+ cb(m && m.length == 1);
+ });
+};
+
+function addEmailToAccount(userID, email, type, cb) {
+ // validate 'type' isn't bogus
+ if ([ 'secondary', 'primary' ].indexOf(type) === -1) {
+ return process.nextTick(function() {
+ cb("invalid type");
+ });
+ }
+
+ process.nextTick(function() {
+ sync();
+ var emails = jsel.match(":has(.id:expr(x="+ ESC(userID) +")) > .emails", db.users);
+ if (emails && emails.length > 0) {
+ emails[0][email] = { type: type };
flush();
- cb();
}
+ cb();
});
}
@@ -185,29 +222,57 @@ exports.stageUser = function(email, cb) {
});
};
-exports.stageEmail = function(existing_email, new_email, cb) {
+exports.stageEmail = function(existing_user, new_email, cb) {
secrets.generate(48, function(secret) {
// overwrite previously staged users
sync();
db.staged[secret] = {
type: "add_email",
- existing_email: existing_email,
+ existing_user: existing_user,
email: new_email,
when: (new Date()).getTime()
};
db.stagedEmails[new_email] = secret;
flush();
-
+
setTimeout(function() { cb(secret); }, 0);
});
};
+exports.createUserWithPrimaryEmail = function(email, cb) {
+ var emailVal = { };
+ emailVal[email] = { type: 'primary' };
+ var uid = getNextUserID();
+ db.users.push({
+ id: uid,
+ password: null,
+ emails: emailVal
+ });
+ flush();
+ process.nextTick(function() {
+ cb(undefined, uid);
+ });
+};
+
+exports.haveVerificationSecret = function(secret, cb) {
+ process.nextTick(function() {
+ sync();
+ cb(!!(db.staged[secret]));
+ });
+};
+
exports.emailForVerificationSecret = function(secret, cb) {
- setTimeout(function() {
+ process.nextTick(function() {
sync();
- cb(db.staged[secret] ? db.staged[secret].email : undefined);
- }, 0);
+ if (!db.staged[secret]) return cb("no such secret");
+ exports.checkAuth(db.staged[secret].existing_user, function (hash) {
+ cb(undefined, {
+ email: db.staged[secret].email,
+ needs_password: !hash
+ });
+ });
+ });
};
exports.verificationSecretForEmail = function(email, cb) {
@@ -229,12 +294,16 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
if (o.type === 'add_account') {
exports.emailKnown(o.email, function(known) {
function createAccount() {
+ var emailVal = {};
+ emailVal[o.email] = { type: 'secondary' };
+ var uid = getNextUserID();
db.users.push({
+ id: uid,
password: hash,
- emails: [ o.email ]
+ emails: emailVal
});
flush();
- cb(undefined, o.email);
+ cb(undefined, o.email, uid);
}
// if this email address is known and a user has completed a re-verification of this email
@@ -243,9 +312,8 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
// NOTE: this might be sub-optimal, but it's a dead simple approach that mitigates many attacks
// and gives us reasonable behavior (without explicitly supporting) in the face of shared email
// addresses.
-
if (known) {
- exports.removeEmail(o.email, o.email, function (err) {
+ removeEmailNoCheck(o.email, function (err) {
if (err) cb(err);
else createAccount();
});
@@ -256,10 +324,12 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
} else if (o.type === 'add_email') {
exports.emailKnown(o.email, function(known) {
function addIt() {
- addEmailToAccount(o.existing_email, o.email, cb);
+ addEmailToAccount(o.existing_user, o.email, 'secondary', function(e) {
+ cb(e, o.email, o.existing_user);
+ });
}
if (known) {
- exports.removeEmail(o.email, o.email, function (err) {
+ removeEmailNoCheck(o.email, function (err) {
if (err) cb(err);
else addIt();
});
@@ -272,87 +342,112 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
}
};
-exports.checkAuth = function(email, cb) {
+exports.addPrimaryEmailToAccount = function(userID, emailToAdd, cb) {
sync();
- var m = jsel.match(":root > object:has(.emails > :val(" + ESC(email) + ")) > .password", db.users);
+ exports.emailKnown(emailToAdd, function(known) {
+ function addIt() {
+ addEmailToAccount(userID, emailToAdd, 'primary', cb);
+ }
+ if (known) {
+ removeEmailNoCheck(emailToAdd, function (err) {
+ if (err) cb(err);
+ else addIt();
+ });
+ } else {
+ addIt();
+ }
+ });
+};
+
+exports.checkAuth = function(userID, cb) {
+ sync();
+ var m = undefined;
+ if (userID) {
+ m = jsel.match(":root > object:has(:root > .id:expr(x=" + ESC(userID) + ")) > .password", db.users);
+ if (m.length === 0) m = undefined;
+ else m = m[0];
+ }
+ process.nextTick(function() { cb(m) });
+};
+
+exports.userKnown = function(userID, cb) {
+ sync();
+ var m = jsel.match(":root > object:has(:root > .id:expr(x=" + ESC(userID) + "))", db.users);
if (m.length === 0) m = undefined;
else m = m[0];
- setTimeout(function() { cb(m) }, 0);
+ process.nextTick(function() { cb(m) });
};
-exports.updatePassword = function(email, hash, cb) {
+exports.updatePassword = function(userID, hash, cb) {
sync();
- var m = jsel.match(":root > object:has(.emails > :val(" + ESC(email) + "))", db.users);
+ var m = jsel.match(":root > object:has(.id:expr(x=" + ESC(userID) + "))", db.users);
var err = undefined;
if (m.length === 0) err = "no such email address";
else m[0].password = hash;
flush();
- setTimeout(function() { cb(err) }, 0);
+ process.nextTick(function() { cb(err) });
};
-function emailToUserID(email, cb) {
- sync();
- var id = undefined;
-
- for (var i = 0; i < db.users.length; i++) {
- if (jsel.match(":val(" + JSON.stringify(email) + ")", db.users[i]).length) {
- id = i;
- break;
- }
- if (id !== undefined) break;
- }
-
- setTimeout(function() { cb(id); }, 0);
-}
-
-exports.listEmails = function(email, cb) {
+exports.listEmails = function(uid, cb) {
sync();
- // get the user id associated with this account
- emailToUserID(email, function(userID) {
- if (userID === undefined) {
- cb("no such email: " + email);
+ var emails = jsel.match(":has(.id:expr(x="+ ESC(uid) +")) > .emails", db.users);
+ process.nextTick(function() {
+ if (!emails || emails.length != 1) {
+ cb("no such user: " + uid);
return;
}
- var email_list = jsel.match(".emails string", db.users[userID]);
- var emails = {};
- for (var i=0; i < email_list.length; i++)
- emails[email_list[i]] = {};
-
- cb(null, emails);
+ cb(null, emails[0]);
});
};
-exports.removeEmail = function(authenticated_email, email, cb) {
+exports.removeEmail = function(authenticated_user, email, cb) {
sync();
- var m = jsel.match(".emails:has(:val("+ESC(authenticated_email)+")):has(:val("+ESC(email)+"))", db.users);
+ var m = jsel.match(":has(.id:expr(x=" + ESC(authenticated_user) + ")) .emails:has(."+ESC(email)+")", db.users);
if (m.length) {
var emails = m[0];
- for (var i = 0; i < emails.length; i++) {
- if (emails[i] === email) {
- emails.splice(i, 1);
- flush();
- break;
- }
- }
+ delete emails[email];
+ flush();
}
setTimeout(function() { cb(); }, 0);
};
-exports.cancelAccount = function(authenticated_email, cb) {
- emailToUserID(authenticated_email, function(user_id) {
- db.users.splice(user_id, 1);
+function removeEmailNoCheck(email, cb) {
+ sync();
+ var m = jsel.match(".emails:has(."+ESC(email)+")", db.users);
+ if (m.length) {
+ var emails = m[0];
+ delete emails[email];
flush();
- cb();
- });
+ }
+ process.nextTick(function() { cb(); });
+};
+
+exports.cancelAccount = function(authenticated_uid, cb) {
+ sync();
+ var id = undefined;
+
+ for (var i = 0; i < db.users.length; i++) {
+ if (db.users[i].id === authenticated_uid) break;
+ }
+
+ if (i < db.users.length) {
+ db.users.splice(i, 1);
+ flush();
+ }
+
+ process.nextTick(function() { cb(); });
};
exports.addTestUser = function(email, hash, cb) {
sync();
- exports.removeEmail(email, email, function() {
+ removeEmailNoCheck(email, function() {
+ var emailVal = {};
+ emailVal[email] = { type: 'secondary' };
db.users.push({
+ id: getNextUserID(),
password: hash,
- emails: [ email ]
+ emails: emailVal
});
flush();
cb();
View
263 lib/db/mysql.js
@@ -41,18 +41,19 @@
/*
* The Schema:
*
- * +--- user ------+ +--- email ----+
- * |*int id | <-\ |*int id |
- * | string passwd | \- |*int user |
- * +---------------+ |*string address
- * +--------------+
+ * +--- user ------+ +--- email -----+
+ * |*int id | <-\ |*int id |
+ * | string passwd | \- |*int user |
+ * +---------------+ |*string address|
+ * | enum type |
+ * +---------------+
*
*
* +------ staged ----------+
* |*int id |
* |*string secret |
* | bool new_acct |
- * | string existing |
+ * | int existing_user |
* |*string email |
* | timestamp ts |
* +------------------------+
@@ -70,13 +71,14 @@ var client = undefined;
const schemas = [
"CREATE TABLE IF NOT EXISTS user (" +
"id BIGINT AUTO_INCREMENT PRIMARY KEY," +
- "passwd CHAR(64) NOT NULL" +
+ "passwd CHAR(64)" +
") ENGINE=InnoDB;",
"CREATE TABLE IF NOT EXISTS email (" +
"id BIGINT AUTO_INCREMENT PRIMARY KEY," +
"user BIGINT NOT NULL," +
"address VARCHAR(255) UNIQUE NOT NULL," +
+ "type ENUM('secondary', 'primary') DEFAULT 'secondary' NOT NULL," +
"FOREIGN KEY user_fkey (user) REFERENCES user(id)" +
") ENGINE=InnoDB;",
@@ -84,9 +86,10 @@ const schemas = [
"id BIGINT AUTO_INCREMENT PRIMARY KEY," +
"secret CHAR(48) UNIQUE NOT NULL," +
"new_acct BOOL NOT NULL," +
- "existing VARCHAR(255)," +
+ "existing_user BIGINT," +
"email VARCHAR(255) UNIQUE NOT NULL," +
- "ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL" +
+ "ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL," +
+ "FOREIGN KEY existing_user_fkey (existing_user) REFERENCES user(id)" +
") ENGINE=InnoDB;",
];
@@ -230,6 +233,26 @@ exports.emailKnown = function(email, cb) {
cb(rows && rows.length > 0 && rows[0].N > 0);
}
);
+};
+
+exports.userKnown = function(uid, cb) {
+ client.query(
+ "SELECT COUNT(*) as N FROM user WHERE id = ?", [ uid ],
+ function(err, rows) {
+ if (err) logUnexpectedError(err);
+ cb(rows && rows.length > 0 && rows[0].N > 0);
+ }
+ );
+};
+
+exports.emailType = function(email, cb) {
+ client.query(
+ "SELECT type FROM email WHERE address = ?", [ email ],
+ function(err, rows) {
+ if (err) logUnexpectedError(err);
+ cb((rows && rows.length > 0) ? rows[0].type : undefined);
+ }
+ );
}
exports.isStaged = function(email, cb) {
@@ -257,7 +280,7 @@ exports.stageUser = function(email, cb) {
secrets.generate(48, function(secret) {
// overwrite previously staged users
client.query('INSERT INTO staged (secret, new_acct, email) VALUES(?,TRUE,?) ' +
- 'ON DUPLICATE KEY UPDATE secret=?, existing="", new_acct=TRUE, ts=NOW()',
+ 'ON DUPLICATE KEY UPDATE secret=?, existing_user=NULL, new_acct=TRUE, ts=NOW()',
[ secret, email, secret],
function(err) {
if (err) {
@@ -268,12 +291,54 @@ exports.stageUser = function(email, cb) {
});
};
+
+exports.haveVerificationSecret = function(secret, cb) {
+ client.query(
+ "SELECT count(*) as n FROM staged WHERE secret = ?", [ secret ],
+ function(err, rows) {
+ if (err) cb(false);
+ else cb(rows.length === 1 && rows[0].n === 1);
+ });
+};
+
exports.emailForVerificationSecret = function(secret, cb) {
client.query(
- "SELECT email FROM staged WHERE secret = ?", [ secret ],
+ "SELECT * FROM staged WHERE secret = ?", [ secret ],
function(err, rows) {
if (err) logUnexpectedError(err);
- cb((rows && rows.length > 0) ? rows[0].email : undefined);
+ // if the record was not found, fail out
+ if (!rows || rows.length != 1) return cb("no such secret");
+
+ var o = rows[0];
+
+ // if the record was found and this is for a new_acct, return the email
+ if (o.new_acct) return cb(undefined, { email: o.email, needs_password: false });
+
+ // we need a userid. the old schema had an 'existing' field which was an email
+ // address. the new schema has an 'existing_user' field which is a userid.
+ // this is transitional code so outstanding verification links continue working
+ // and can be removed in feb 2012 some time. maybe for valentines day?
+ if (typeof o.existing_user === 'number') doCheckAuth(o.existing_user);
+ else if (typeof o.existing === 'string') {
+ exports.emailToUID(o.existing, function(uid) {
+ if (uid === undefined) return cb('acct associated with staged email doesn\'t exist');
+ doCheckAuth(uid);
+ });
+ }
+
+ function doCheckAuth(uid) {
+ // if the account is being added to an existing account, let's find
+ // out if the account has a password set (if only primary email addresses
+ // are associated with the acct at the moment, then there will not be a
+ // password set and the user will need to set one with the addition of
+ // this addresss)
+ exports.checkAuth(uid, function(hash) {
+ cb(undefined, {
+ email: o.email,
+ needs_password: !hash
+ });
+ });
+ }
});
};
@@ -286,6 +351,29 @@ exports.verificationSecretForEmail = function(email, cb) {
});
};
+function addEmailToUser(userID, email, type, cb) {
+ // issue #170 - delete any old records with the same
+ // email address. this is necessary because
+ // gotVerificationSecret is invoked both for
+ // forgotten password flows and for new user signups.
+ client.query(
+ "DELETE FROM email WHERE address = ?",
+ [ email ],
+ function(err, info) {
+ if (err) { logUnexpectedError(err); cb(err); return; }
+ else {
+ client.query(
+ "INSERT INTO email(user, address, type) VALUES(?, ?, ?)",
+ [ userID, email, type ],
+ function(err, info) {
+ if (err) logUnexpectedError(err);
+ cb(err ? err : undefined, email, userID);
+ });
+ }
+ });
+}
+
+
exports.gotVerificationSecret = function(secret, hash, cb) {
client.query(
"SELECT * FROM staged WHERE secret = ?", [ secret ],
@@ -297,30 +385,6 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
else {
var o = rows[0];
- function addEmailToUser(userID) {
- // issue #170 - delete any old records with the same
- // email address. this is necessary because
- // gotVerificationSecret is invoked both for
- // forgotten password flows and for new user signups.
- // We could add an `ON DUPLICATE KEY` clause, however
- // We actually want to invalidate all old public keys.
- client.query(
- "DELETE FROM email WHERE address = ?",
- [ o.email ],
- function(err, info) {
- if (err) { logUnexpectedError(err); cb(err); return; }
- else {
- client.query(
- "INSERT INTO email(user, address) VALUES(?, ?)",
- [ userID, o.email ],
- function(err, info) {
- if (err) logUnexpectedError(err);
- cb(err ? err : undefined, o.email);
- });
- }
- });
- }
-
// delete the record
client.query("DELETE LOW_PRIORITY FROM staged WHERE secret = ?", [ secret ]);
@@ -331,26 +395,54 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
[ hash ],
function(err, info) {
if (err) { logUnexpectedError(err); cb(err); return; }
- addEmailToUser(info.insertId);
+ addEmailToUser(info.insertId, o.email, 'secondary', cb);
});
} else {
- // we're adding an email address to an existing user account. add appropriate entries into
- // email table
- client.query(
- "SELECT user FROM email WHERE address = ?", [ o.existing ],
- function(err, rows) {
- if (err) { logUnexpectedError(err); cb(err); }
- else if (rows.length === 0) cb("cannot find email address: " + o.existing);
- else {
- addEmailToUser(rows[0].user);
- }
+ // we need a userid. the old schema had an 'existing' field which was an email
+ // address. the new schema has an 'existing_user' field which is a userid.
+ // this is transitional code so outstanding verification links continue working
+ // and can be removed in feb 2012 some time. maybe for valentines day?
+ if (typeof o.existing_user === 'number') doAddEmail(o.existing_user);
+ else if (typeof o.existing === 'string') {
+ exports.emailToUID(o.existing, function(uid) {
+ if (uid === undefined) return cb('acct associated with staged email doesn\'t exist');
+ doAddEmail(uid);
});
- }
+ }
+ function doAddEmail(uid) {
+ // we're adding an email address to an existing user account. add appropriate entries into
+ // email table
+ addEmailToUser(uid, o.email, 'secondary', cb);
+ }
+ };
}
}
);
}
+exports.addPrimaryEmailToAccount = function(uid, emailToAdd, cb) {
+ // we're adding an email address to an existing user account. add appropriate entries into
+ // email table
+ addEmailToUser(uid, emailToAdd, 'primary', cb);
+}
+
+exports.createUserWithPrimaryEmail = function(email, cb) {
+ // create a new user acct with no password
+ client.query(
+ "INSERT INTO user() VALUES()",
+ function(err, info) {
+ if (err) { logUnexpectedError(err); cb(err); return; }
+ var uid = info.insertId;
+ client.query(
+ "INSERT INTO email(user, address, type) VALUES(?, ?, ?)",
+ [ uid, email, 'primary' ],
+ function(err, info) {
+ if (err) logUnexpectedError(err);
+ cb(err ? err : undefined, uid);
+ });
+ });
+};
+
exports.emailsBelongToSameAccount = function(lhs, rhs, cb) {
client.query(
'SELECT COUNT(*) AS n FROM email WHERE address = ? AND user = ( SELECT user FROM email WHERE address = ? );',
@@ -361,12 +453,22 @@ exports.emailsBelongToSameAccount = function(lhs, rhs, cb) {
});
}
-exports.stageEmail = function(existing_email, new_email, cb) {
+exports.userOwnsEmail = function(uid, email, cb) {
+ client.query(
+ 'SELECT COUNT(*) AS n FROM email WHERE address = ? AND user = ?',
+ [ email, uid ],
+ function (err, rows) {
+ if (err) cb(false);
+ else cb(rows.length === 1 && rows[0].n === 1);
+ });
+}
+
+exports.stageEmail = function(existing_user, new_email, cb) {
secrets.generate(48, function(secret) {
// overwrite previously staged users
- client.query('INSERT INTO staged (secret, new_acct, existing, email) VALUES(?,FALSE,?,?) ' +
- 'ON DUPLICATE KEY UPDATE secret=?, existing=?, new_acct=FALSE, ts=NOW()',
- [ secret, existing_email, new_email, secret, existing_email],
+ client.query('INSERT INTO staged (secret, new_acct, existing_user, email) VALUES(?,FALSE,?,?) ' +
+ 'ON DUPLICATE KEY UPDATE secret=?, existing_user=?, new_acct=FALSE, ts=NOW()',
+ [ secret, existing_user, new_email, secret, existing_user],
function(err) {
if (err) {
logUnexpectedError(err);
@@ -377,20 +479,30 @@ exports.stageEmail = function(existing_email, new_email, cb) {
});
};
-exports.checkAuth = function(email, cb) {
+exports.emailToUID = function(email, cb) {
client.query(
- 'SELECT passwd FROM user WHERE id = ( SELECT user FROM email WHERE address = ? )',
+ 'SELECT user FROM email WHERE address = ?',
[ email ],
function (err, rows) {
if (err) logUnexpectedError(err);
+ cb((rows && rows.length == 1) ? rows[0].user : undefined);
+ });
+};
+
+exports.checkAuth = function(uid, cb) {
+ client.query(
+ 'SELECT passwd FROM user WHERE id = ?',
+ [ uid ],
+ function (err, rows) {
+ if (err) logUnexpectedError(err);
cb((rows && rows.length == 1) ? rows[0].passwd : undefined);
});
}
-exports.updatePassword = function(email, hash, cb) {
+exports.updatePassword = function(uid, hash, cb) {
client.query(
- 'UPDATE user SET passwd = ? WHERE id = ( SELECT user FROM email WHERE address = ? )',
- [ hash, email ],
+ 'UPDATE user SET passwd = ? WHERE id = ?',
+ [ hash, uid ],
function (err, rows) {
if (err) logUnexpectedError(err);
cb((err || rows.affectedRows !== 1) ? ("no record with email " + email) : undefined);
@@ -402,10 +514,10 @@ exports.updatePassword = function(email, hash, cb) {
*
* returns an object keyed by email address with properties for each email.
*/
-exports.listEmails = function(email, cb) {
+exports.listEmails = function(uid, cb) {
client.query(
- 'SELECT address FROM email WHERE user = ( SELECT user FROM email WHERE address = ? ) ',
- [ email ],
+ 'SELECT address, type FROM email WHERE user = ?',
+ [ uid ],
function (err, rows) {
if (err) cb(err);
else {
@@ -413,17 +525,19 @@ exports.listEmails = function(email, cb) {
// eventually we'll have fields in here
for (var i = 0; i < rows.length; i++)
- emails[rows[i].address] = {};
+ emails[rows[i].address] = {
+ type: rows[i].type
+ };
cb(null,emails);
}
});
};
-exports.removeEmail = function(authenticated_email, email, cb) {
- exports.emailsBelongToSameAccount(authenticated_email, email, function(ok) {
+exports.removeEmail = function(authenticated_user, email, cb) {
+ exports.userOwnsEmail(authenticated_user, email, function(ok) {
if (!ok) {
- logger.warn(authenticated_email + ' attempted to delete an email that doesn\'t belong to her: ' + email);
+ logger.warn(authenticated_user + ' attempted to delete an email that doesn\'t belong to her: ' + email);
cb("authenticated user doesn't have permission to remove specified email " + email);
return;
}
@@ -439,24 +553,11 @@ exports.removeEmail = function(authenticated_email, email, cb) {
});
};
-exports.cancelAccount = function(email, cb) {
+exports.cancelAccount = function(uid, cb) {
function reportErr(err) { if (err) logUnexpectedError(err); }
- client.query(
- "SELECT user FROM email WHERE address = ?", [ email ],
- function (err, rows) {
- if (err) {
- logUnexpectedError(err)
- return cb(err);
- } else if (!rows || !rows.length === 1 || typeof rows[0] !== 'object') {
- var e = "no user with given account: " + email ;
- logUnexpectedError(e)
- return cb(e);
- }
- var uid = rows[0].user;
- client.query("DELETE LOW_PRIORITY FROM email WHERE user = ?", [ uid ], reportErr);
- client.query("DELETE LOW_PRIORITY FROM user WHERE id = ?", [ uid ], reportErr);
- cb();
- });
+ client.query("DELETE LOW_PRIORITY FROM email WHERE user = ?", [ uid ], reportErr);
+ client.query("DELETE LOW_PRIORITY FROM user WHERE id = ?", [ uid ], reportErr);
+ process.nextTick(cb);
};
exports.addTestUser = function(email, hash, cb) {
View
13 lib/http_forward.js
@@ -6,6 +6,14 @@ logger = require('./logging.js').logger,
querystring = require('querystring');
module.exports = function(dest, req, res, cb) {
+ function cleanupReq() {
+ if (preq) {
+ preq.removeAllListeners();
+ preq.destroy();
+ preq = undefined;
+ }
+ }
+
var u = url.parse(dest.toString());
var m = u.protocol === 'http:' ? http : https;
@@ -32,9 +40,14 @@ module.exports = function(dest, req, res, cb) {
res.write(chunk);
}).on('end', function() {
res.end();
+ pres.removeAllListeners();
+ pres.destroy();
+ pres = undefined;
+ cleanupReq();
cb();
});
}).on('error', function(e) {
+ cleanupReq();
cb(e);
});
View
1  lib/load_gen/activities/add_email.js
@@ -67,6 +67,7 @@ exports.startFunc = function(cfg, cb) {
cb = (function() {
var _cb = cb;
return function(x) {
+ if (x) userdb.removeLastEmailFromUser(user);
userdb.releaseUser(user);
_cb(x);
};
View
6 lib/load_gen/user_db.js
@@ -47,7 +47,7 @@ var users = [ ];
exports.num = function() { return users.length; }
function genEmail() {
- return secrets.generate(8) + "@" + secrets.generate(8) + "." + secrets.generate(3);
+ return secrets.generate(16) + "@loadtest.domain";
}
// a little utilitiy function to get any one of an array of items
@@ -138,6 +138,10 @@ exports.addEmailToUser = function(user) {
return email;
};
+exports.removeLastEmailFromUser = function(user) {
+ user.emails.pop();
+};
+
exports.addKeyToUserCtx = function(ctx, email) {
// this is simulated. it will need to be real to apply load to
// the verifier, but that in turn will drastically increase the
View
233 lib/primary.js
@@ -0,0 +1,233 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla BrowserID.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Lloyd Hilaiel <lloyd@hilaiel.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// this file is an abstraction around "primary identity authority" support,
+// specifically checks and a cache to see if a primary supports browserid
+// natively.
+
+const
+https = require('https'),
+logger = require('./logging.js').logger,
+urlparse = require('urlparse'),
+jwk = require('jwcrypto/jwk'),
+jwcert = require("jwcrypto/jwcert"),
+vep = require("jwcrypto/vep"),
+jwt = require("jwcrypto/jwt"),
+config = require("./configuration.js");
+
+const WELL_KNOWN_URL = "/.well-known/vep";
+
+// cache .well-known/vep for six hours
+const MAX_CACHE_MS = (6 * 60 * 60 * 1000);
+
+function parseWellKnownBody(body, domain) {
+ var v = JSON.parse(body);
+
+ const want = [ 'public-key', 'authentication', 'provisioning' ];
+ var got = Object.keys(v);
+
+ want.forEach(function(k) {
+ if (-1 === got.indexOf(k)) throw "missing required key: " + k;
+ });
+
+ var urls = {
+ auth: 'https://' + domain + v.authentication,
+ prov: 'https://' + domain + v.provisioning,
+ };
+
+ // validate the urls
+ urlparse(urls.auth).validate();
+ urlparse(urls.prov).validate();
+
+ // parse the public key
+ return {
+ publicKey: jwk.PublicKey.fromSimpleObject(v['public-key']),
+ urls: urls
+ };
+}
+
+// a cache of network responses. We want to move this into
+// fast and efficient external key/value storage as we scale
+var g_cache = { };
+
+exports.checkSupport = function(domain, cb) {
+ if (!cb) throw "missing required callback function";
+
+ if (config.get('disable_primary_support')) {
+ return process.nextTick(function() { cb(null, false); });
+ }
+
+ if (typeof domain !== 'string' || !domain.length) {
+ return process.nextTick(function() { cb("invalid domain"); });
+ }
+
+ // check cache age
+ if (g_cache[domain]) {
+ if (!g_cache[domain].when || (new Date() - g_cache[domain].when) > MAX_CACHE_MS) {
+ delete g_cache[domain];
+ }
+
+ if (g_cache[domain]) {
+ logger.debug("returning primary support status for '" + domain + "' from cache");
+ return process.nextTick(function() { cb(null, g_cache[domain].status); });
+ }
+ }
+
+ function cacheAndReturn(cacheValue, publicKey) {
+ g_cache[domain] = {
+ when: new Date(),
+ status: cacheValue,
+ publicKey: publicKey
+ };
+ cb(null, cacheValue);
+ }
+
+ // now we need to check to see if domain purports to being a primary for browserid
+ var req = https.get({
+ host: domain,
+ path: WELL_KNOWN_URL,
+ agent: false
+ }, function (res) {
+ if (res.statusCode !== 200) {
+ logger.debug(domain + ' is not a browserid primary - non-200 response code to ' + WELL_KNOWN_URL);
+ return cacheAndReturn(false);
+ }
+ if (res.headers['content-type'].indexOf('application/json') !== 0) {
+ logger.debug(domain + ' is not a browserid primary - non "application/json" response to ' + WELL_KNOWN_URL);
+ return cacheAndReturn(false);
+ }
+
+ var body = "";
+ res.on('data', function(chunk) { body += chunk; });
+ res.on('end', function() {
+ try {
+ var r = parseWellKnownBody(body, domain);
+ logger.info(domain + ' is a valid browserid primary');
+ return cacheAndReturn(r.urls, r.publicKey);
+ } catch(e) {
+ logger.debug(domain + ' is a broken browserid primary, malformed dec of support: ' + e.toString());
+ return cacheAndReturn(false);
+ }
+ });
+ }).on('error', function(e) {
+ logger.debug(domain + ' is not a browserid primary: ' + e.toString());
+ cacheAndReturn(false);
+ });
+};
+
+// Support "shimmed primaries" for local development. That is an environment variable that is any number of
+// CSV values of the form:
+// <domain>|<origin>|<path to .well-known/vep>,
+// where 'domain' is the domain that we would like to shim. 'origin' is the origin to which traffic should
+// be directed, and 'path to .well-known/vep' is a path to the vep file for the domain
+//
+// defining this env var will pre-seed the cache so local testing can take place. example:
+//
+// SHIMMED_PRIMARIES=eyedee.me|http://127.0.0.1:10005|example/primary/.well-known/vep
+
+if (process.env['SHIMMED_PRIMARIES']) {
+ var shims = process.env['SHIMMED_PRIMARIES'].split(',');
+ shims.forEach(function(shim) {
+ var a = shim.split('|');
+ var domain = a[0], origin = a[1], path = a[2];
+ var body = require('fs').readFileSync(path);
+ var r = parseWellKnownBody(body, domain);
+ r.urls.auth = r.urls.auth.replace('https://' + domain, origin);
+ r.urls.prov = r.urls.prov.replace('https://' + domain, origin);
+
+ g_cache[domain] = {
+ when: new Date(),
+ status: r.urls,
+ publicKey: r.publicKey
+ };
+
+ console.log("inserted primary info for '" + domain + "' into cache, pointed at '" + origin + "'");
+ });
+}
+
+exports.getPublicKey = function(domain, cb) {
+ exports.checkSupport(domain, function(err, rv) {
+ if (err) return cb(err);
+ var pubKey;
+ if (rv) pubKey = g_cache[domain].publicKey;
+ if (!pubKey) return cb("can't get public key for " + domain);
+ cb(null, pubKey);
+ });
+};
+
+// verify an assertion generated to authenticate to browserid
+exports.verifyAssertion = function(assertion, cb) {
+ if (config.get('disable_primary_support')) {
+ return process.nextTick(function() { cb("primary support disabled") });
+ }
+
+ try {
+ var bundle = vep.unbundleCertsAndAssertion(assertion);
+ } catch(e) {
+ return process.nextTick(function() { cb("malformed assertion: " + e); });
+ }
+
+ jwcert.JWCert.verifyChain(
+ bundle.certificates,
+ new Date(), function(issuer, next) {
+ // issuer cannot be the browserid
+ if (issuer === config.get('hostname')) {
+ cb("cannot authenticate to browserid with a certificate issued by it.");
+ } else {
+ exports.getPublicKey(issuer, function(err, pubKey) {
+ if (err) return cb(err);
+ next(pubKey);
+ });
+ }
+ }, function(pk, principal) {
+ try {
+ var tok = new jwt.JWT();
+ tok.parse(bundle.assertion);
+
+ // audience must be browserid itself
+ var want = urlparse(config.get('URL')).originOnly();
+ var got = urlparse(tok.audience).originOnly();
+
+ if (want.toString() !== got.toString()) {
+ return cb("can't log in with an assertion for '" + got.toString() + "'");
+ }
+ if (!tok.verify(pk)) throw "verification failure";
+ cb(null, principal.email);
+ } catch(e) {
+ cb("can't verify assertion: " + e.toString());
+ }
+ }, cb);
+};
View
38 lib/verifier/certassertion.js
@@ -47,7 +47,8 @@ jwcert = require("jwcrypto/jwcert"),
vep = require("jwcrypto/vep"),
config = require("../configuration.js"),
logger = require("../logging.js").logger,
-secrets = require('../secrets.js');
+secrets = require('../secrets.js'),
+primary = require('../primary.js');
try {
const publicKey = secrets.loadPublicKey();
@@ -126,12 +127,33 @@ function verify(assertion, audience, successCB, errorCB) {
return errorCB("malformed assertion");
}
+ var ultimateIssuer;
+
jwcert.JWCert.verifyChain(
bundle.certificates,
new Date(), function(issuer, next) {
+ // update issuer with each issuer in the chain, so the
+ // returned issuer will be the last cert in the chain
+ ultimateIssuer = issuer;
+
// allow other retrievers for testing
if (issuer === config.get('hostname')) return next(publicKey);
- return errorCB("this verifier doesn't respect certs issued from domains other than: " + config.get('hostname'));
+ else if (config.get('disable_primary_support')) {
+ return errorCB("this verifier doesn't respect certs issued from domains other than: " +
+ config.get('hostname'));
+ }
+
+ // XXX: this network work happening inside a compute process.
+ // if we have a large number of requests to auth assertions that require
+ // keyfetch, this could theoretically hurt our throughput. We could
+ // move the fetch up into the browserid process and pass it into the
+ // compute process at some point.
+
+ // let's go fetch the public key for this host
+ primary.getPublicKey(issuer, function(err, pubKey) {
+ if (err) return errorCB(err);
+ next(pubKey);
+ });
}, function(pk, principal) {
var tok = new jwt.JWT();
tok.parse(bundle.assertion);
@@ -144,8 +166,18 @@ function verify(assertion, audience, successCB, errorCB) {
return errorCB("audience mismatch: " + err);
}
+ // verify that the issuer is the same as the email domain
+ // NOTE: for "delegation of authority" support we'll need to make this check
+ // more sophisticated
+ var domainFromEmail = principal.email.replace(/^.*@/, '');
+ if (ultimateIssuer != config.get('hostname') && ultimateIssuer !== domainFromEmail)
+ {
+ return errorCB("issuer issue '" + ultimateIssuer + "' may not speak for emails from '"
+ + domainFromEmail + "'");
+ }
+
if (tok.verify(pk)) {
- successCB(principal.email, tok.audience, tok.expires, config.get('hostname'));
+ successCB(principal.email, tok.audience, tok.expires, ultimateIssuer);
} else {
errorCB("verification failure");
}
View
43 lib/wsapi.js
@@ -11,7 +11,7 @@
const
-sessions = require('connect-cookie-session'),
+sessions = require('client-sessions'),
express = require('express');
secrets = require('./secrets'),
config = require('./configuration'),
@@ -29,29 +29,11 @@ const COOKIE_SECRET = secrets.hydrateSecret('browserid_cookie', config.get('var_
const COOKIE_KEY = 'browserid_state';
function clearAuthenticatedUser(session) {
- Object.keys(session).forEach(function(k) {
- if (k !== 'csrf') delete session[k];
- });
+ session.reset(['csrf']);
}
function isAuthed(req) {
- var who;
- try {
- if (req.session.authenticatedUser) {
- if (!Date.parse(req.session.authenticatedAt) > 0) throw "bad timestamp";
- if (new Date() - new Date(req.session.authenticatedAt) >
- config.get('authentication_duration_ms'))
- {
- throw "expired";
- }
- who = req.session.authenticatedUser;
- }
- } catch(e) {
- logger.debug("Session authentication has expired:", e);
- clearAuthenticatedUser(req.session);
- }
-
- return who;
+ return (req.session) ? req.session.userid : undefined;
}
function bcryptPassword(password, cb) {
@@ -63,9 +45,14 @@ function bcryptPassword(password, cb) {
});
};
-function setAuthenticatedUser(session, email) {
- session.authenticatedUser = email;
- session.authenticatedAt = new Date();
+function setAuthenticatedUser(session, uid) {
+ session.userid = uid;
+}
+
+function checkPassword(pass) {
+ if (!pass || pass.length < 8 || pass.length > 80) {
+ return "valid passwords are between 8 and 80 chars";
+ }
}
// common functions exported, for use by different api calls
@@ -73,6 +60,7 @@ exports.clearAuthenticatedUser = clearAuthenticatedUser;
exports.isAuthed = isAuthed;
exports.bcryptPassword = bcryptPassword;
exports.setAuthenticatedUser = setAuthenticatedUser;
+exports.checkPassword = checkPassword;
exports.fowardWritesTo = undefined;
exports.setup = function(options, app) {
@@ -93,7 +81,8 @@ exports.setup = function(options, app) {
var cookieSessionMiddleware = sessions({
secret: COOKIE_SECRET,
- key: COOKIE_KEY,
+ cookieName: COOKIE_KEY,
+ duration: 7 * 24 * 60 * 60 * 1000, // 1 week
cookie: {
path: '/wsapi',
httpOnly: true,
@@ -217,7 +206,9 @@ exports.setup = function(options, app) {
wsapis[operation] = api;
// forward writes if options.forward_writes is defined
- if (options.forward_writes && wsapis[operation].writes_db) {
+ if (options.forward_writes && wsapis[operation].writes_db &&
+ !wsapis[operation].disallow_forward)
+ {
forwardedOperations.push(operation);
var forward_url = options.forward_writes + "wsapi/" + operation;
wsapis[operation].process = function(req, res) {