From b6bf777ef977436472651727da4565e1ce04b0da Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 5 Sep 2013 18:34:46 -0500 Subject: Switched registration form to no longer be modal, but instead it's own page Added validation of form using parsley.js Added password strength indicator using zxcvbn --- wqflask/wqflask/static/new/css/parsley.css | 20 +++++++++++ .../static/new/javascript/password_strength.coffee | 33 +++++++++++++++++ .../static/new/javascript/password_strength.js | 42 ++++++++++++++++++++++ .../wqflask/static/new/js_external/parsley.min.js | 35 ++++++++++++++++++ .../static/new/js_external/zxcvbn/.gitignore | 2 ++ 5 files changed, 132 insertions(+) create mode 100644 wqflask/wqflask/static/new/css/parsley.css create mode 100644 wqflask/wqflask/static/new/javascript/password_strength.coffee create mode 100644 wqflask/wqflask/static/new/javascript/password_strength.js create mode 100644 wqflask/wqflask/static/new/js_external/parsley.min.js create mode 100644 wqflask/wqflask/static/new/js_external/zxcvbn/.gitignore (limited to 'wqflask') diff --git a/wqflask/wqflask/static/new/css/parsley.css b/wqflask/wqflask/static/new/css/parsley.css new file mode 100644 index 00000000..7d244579 --- /dev/null +++ b/wqflask/wqflask/static/new/css/parsley.css @@ -0,0 +1,20 @@ +/* Adapted from parsleyjs.org/documentation.html#parsleyclasses */ + +input.parsley-success, textarea.parsley-success { + color: #468847 !important; + background-color: #DFF0D8 !important; + border: 1px solid #D6E9C6 !important; +} +input.parsley-error, textarea.parsley-error { + color: #B94A48 !important; + background-color: #F2DEDE !important; + border: 1px solid #EED3D7 !important; +} +ul.parsley-error-list { + font-size: 11px; + margin: 2px; + list-style-type:none; +} +ul.parsley-error-list li { + line-height: 11px; +} \ No newline at end of file diff --git a/wqflask/wqflask/static/new/javascript/password_strength.coffee b/wqflask/wqflask/static/new/javascript/password_strength.coffee new file mode 100644 index 00000000..0bee5836 --- /dev/null +++ b/wqflask/wqflask/static/new/javascript/password_strength.coffee @@ -0,0 +1,33 @@ +$ -> + + + $("#password").keyup -> + passtext = $(this).val() + result = zxcvbn(passtext) + if passtext.length < 6 + $("#password_strength").html('') + $("#password_alert").fadeOut() + else + word = word_score(result.score) + crack_time = result.crack_time_display + if crack_time == "instant" + crack_time = "a second" + display = "This is #{word} password. It can be cracked in #{crack_time}." + $("#password_strength").html(display) + $("#password_alert").fadeIn() + + + + word_score = (num_score) -> + num_score = parseInt(num_score) + console.log("num_score is:", num_score) + mapping = + 0: "a terrible" + 1: "a bad" + 2: "a mediocre" + 3: "a good" + 4: "an excellent" + console.log("mapping is:", mapping) + result = mapping[num_score] + console.log("result is:", result) + return result \ No newline at end of file diff --git a/wqflask/wqflask/static/new/javascript/password_strength.js b/wqflask/wqflask/static/new/javascript/password_strength.js new file mode 100644 index 00000000..166e1125 --- /dev/null +++ b/wqflask/wqflask/static/new/javascript/password_strength.js @@ -0,0 +1,42 @@ +// Generated by CoffeeScript 1.6.1 +(function() { + + $(function() { + var word_score; + $("#password").keyup(function() { + var crack_time, display, passtext, result, word; + passtext = $(this).val(); + result = zxcvbn(passtext); + if (passtext.length < 6) { + $("#password_strength").html(''); + return $("#password_alert").fadeOut(); + } else { + word = word_score(result.score); + crack_time = result.crack_time_display; + if (crack_time === "instant") { + crack_time = "a second"; + } + display = "This is " + word + " password. It can be cracked in " + crack_time + "."; + $("#password_strength").html(display); + return $("#password_alert").fadeIn(); + } + }); + return word_score = function(num_score) { + var mapping, result; + num_score = parseInt(num_score); + console.log("num_score is:", num_score); + mapping = { + 0: "a terrible", + 1: "a bad", + 2: "a mediocre", + 3: "a good", + 4: "an excellent" + }; + console.log("mapping is:", mapping); + result = mapping[num_score]; + console.log("result is:", result); + return result; + }; + }); + +}).call(this); diff --git a/wqflask/wqflask/static/new/js_external/parsley.min.js b/wqflask/wqflask/static/new/js_external/parsley.min.js new file mode 100644 index 00000000..ab85c683 --- /dev/null +++ b/wqflask/wqflask/static/new/js_external/parsley.min.js @@ -0,0 +1,35 @@ +/* Parsley dist/parsley.min.js build version 1.1.18 http://parsleyjs.org */ +!function(d){var h=function(a){this.messages={defaultMessage:"This value seems to be invalid.",type:{email:"This value should be a valid email.",url:"This value should be a valid url.",urlstrict:"This value should be a valid url.",number:"This value should be a valid number.",digits:"This value should be digits.",dateIso:"This value should be a valid date (YYYY-MM-DD).",alphanum:"This value should be alphanumeric.",phone:"This value should be a valid phone number."},notnull:"This value should not be null.", +notblank:"This value should not be blank.",required:"This value is required.",regexp:"This value seems to be invalid.",min:"This value should be greater than or equal to %s.",max:"This value should be lower than or equal to %s.",range:"This value should be between %s and %s.",minlength:"This value is too short. It should have %s characters or more.",maxlength:"This value is too long. It should have %s characters or less.",rangelength:"This value length is invalid. It should be between %s and %s characters long.", +mincheck:"You must select at least %s choices.",maxcheck:"You must select %s choices or less.",rangecheck:"You must select between %s and %s choices.",equalto:"This value should be the same."};this.init(a)};h.prototype={constructor:h,validators:{notnull:function(a){return 0=b},maxlength:function(a,b){return a.length<=b},rangelength:function(a,b){return this.minlength(a,b[0])&&this.maxlength(a,b[1])}, +min:function(a,b){return Number(a)>=b},max:function(a,b){return Number(a)<=b},range:function(a,b){return a>=b[0]&&a<=b[1]},equalto:function(a,b,c){c.options.validateIfUnchanged=!0;return a===d(b).val()},remote:function(a,b,c){var f={},g={};f[c.$element.attr("name")]=a;"undefined"!==typeof c.options.remoteDatatype&&(g={dataType:c.options.remoteDatatype});var m=function(a,b){"undefined"!==typeof b&&("undefined"!==typeof c.Validator.messages.remote&&b!==c.Validator.messages.remote)&&d(c.ulError+" .remote").remove(); +c.updtConstraint({name:"remote",valid:a},b);c.manageValidationResult()},n=function(a){if("object"===typeof a)return a;try{a=d.parseJSON(a)}catch(b){}return a},e=function(a){return"object"===typeof a&&null!==a?"undefined"!==typeof a.error?a.error:"undefined"!==typeof a.message?a.message:null:null};d.ajax(d.extend({},{url:b,data:f,type:c.options.remoteMethod||"GET",success:function(a){a=n(a);m(1===a||!0===a||"object"===typeof a&&null!==a&&"undefined"!==typeof a.success,e(a))},error:function(a){a=n(a); +m(!1,e(a))}},g));return null},mincheck:function(a,b){return this.minlength(a,b)},maxcheck:function(a,b){return this.maxlength(a,b)},rangecheck:function(a,b){return this.rangelength(a,b)}},init:function(a){var b=a.validators;a=a.messages;for(var c in b)this.addValidator(c,b[c]);for(c in a)this.addMessage(c,a[c])},formatMesssage:function(a,b){if("object"===typeof b){for(var c in b)a=this.formatMesssage(a,b[c]);return a}return"string"===typeof a?a.replace(/%s/i,b):""},addValidator:function(a,b){this.validators[a]= +b},addMessage:function(a,b,c){if("undefined"!==typeof c&&!0===c)this.messages.type[a]=b;else if("type"===a)for(var d in b)this.messages.type[d]=b[d];else this.messages[a]=b}};var j=function(a,b,c){this.options=b;this.Validator=new h(b);if("ParsleyFieldMultiple"===c)return this;this.init(a,c||"ParsleyField")};j.prototype={constructor:j,init:function(a,b){this.type=b;this.valid=!0;this.element=a;this.validatedOnce=!1;this.$element=d(a);this.val=this.$element.val();this.isRequired=!1;this.constraints= +{};"undefined"===typeof this.isRadioOrCheckbox&&(this.isRadioOrCheckbox=!1,this.hash=this.generateHash(),this.errorClassHandler=this.options.errors.classHandler(a,this.isRadioOrCheckbox)||this.$element);this.ulErrorManagement();this.bindHtml5Constraints();this.addConstraints();this.hasConstraints()&&this.bindValidationEvents()},setParent:function(a){this.$parent=d(a)},getParent:function(){return this.$parent},bindHtml5Constraints:function(){if(this.$element.hasClass("required")||this.$element.prop("required"))this.options.required= +!0;"undefined"!==typeof this.$element.attr("type")&&RegExp(this.$element.attr("type"),"i").test("email url number range")&&(this.options.type=this.$element.attr("type"),RegExp(this.options.type,"i").test("number range")&&(this.options.type="number","undefined"!==typeof this.$element.attr("min")&&this.$element.attr("min").length&&(this.options.min=this.$element.attr("min")),"undefined"!==typeof this.$element.attr("max")&&this.$element.attr("max").length&&(this.options.max=this.$element.attr("max")))); +"string"===typeof this.$element.attr("pattern")&&this.$element.attr("pattern").length&&(this.options.regexp=this.$element.attr("pattern"))},addConstraints:function(){for(var a in this.options){var b={};b[a]=this.options[a];this.addConstraint(b,!0)}},addConstraint:function(a,b){for(var c in a)c=c.toLowerCase(),"function"===typeof this.Validator.validators[c]&&(this.constraints[c]={name:c,requirements:a[c],valid:null},"required"===c&&(this.isRequired=!0),this.addCustomConstraintMessage(c));"undefined"=== +typeof b&&this.bindValidationEvents()},updateConstraint:function(a,b){for(var c in a)this.updtConstraint({name:c,requirements:a[c],valid:null},b)},updtConstraint:function(a,b){this.constraints[a.name]=d.extend(!0,this.constraints[a.name],a);"string"===typeof b&&(this.Validator.messages[a.name]=b);this.bindValidationEvents()},removeConstraint:function(a){a=a.toLowerCase();delete this.constraints[a];"required"===a&&(this.isRequired=!1);this.hasConstraints()?this.bindValidationEvents():"ParsleyForm"=== +typeof this.getParent()?this.getParent().removeItem(this.$element):this.destroy()},addCustomConstraintMessage:function(a){var b=a+("type"===a&&"undefined"!==typeof this.options[a]?this.options[a].charAt(0).toUpperCase()+this.options[a].substr(1):"")+"Message";"undefined"!==typeof this.options[b]&&this.Validator.addMessage("type"===a?this.options[a]:a,this.options[b],"type"===a)},bindValidationEvents:function(){this.valid=null;this.$element.addClass("parsley-validated");this.$element.off("."+this.type); +this.options.remote&&!/change/i.test(this.options.trigger)&&(this.options.trigger=!this.options.trigger?"change":" change");var a=(!this.options.trigger?"":this.options.trigger)+(/key/i.test(this.options.trigger)?"":" keyup");this.$element.is("select")&&(a+=/change/i.test(a)?"":" change");a=a.replace(/^\s+/g,"").replace(/\s+$/g,"");this.$element.on((a+" ").split(" ").join("."+this.type+" "),!1,d.proxy(this.eventValidation,this))},generateHash:function(){return"parsley-"+(Math.random()+"").substring(2)}, +getHash:function(){return this.hash},getVal:function(){return this.$element.data("value")||this.$element.val()},eventValidation:function(a){var b=this.getVal();if("keyup"===a.type&&!/keyup/i.test(this.options.trigger)&&!this.validatedOnce||"change"===a.type&&!/change/i.test(this.options.trigger)&&!this.validatedOnce||!this.isRadioOrCheckbox&&this.getLength(b)",errorElem:"
  • "},listeners:{onFieldValidate:function(){return!1},onFormSubmit:function(){},onFieldError:function(){},onFieldSuccess:function(){}}}; +d(window).on("load",function(){d('[data-validate="parsley"]').each(function(){d(this).parsley()})})}(window.jQuery||window.Zepto); diff --git a/wqflask/wqflask/static/new/js_external/zxcvbn/.gitignore b/wqflask/wqflask/static/new/js_external/zxcvbn/.gitignore new file mode 100644 index 00000000..af1b4bc3 --- /dev/null +++ b/wqflask/wqflask/static/new/js_external/zxcvbn/.gitignore @@ -0,0 +1,2 @@ +*~ +*.js -- cgit v1.2.3