diff --git a/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.js b/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.js index 0939b884..91c7575e 100644 --- a/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.js +++ b/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.js @@ -168,6 +168,25 @@ jQuery(document).ready(function($) { } if($on) $on.addClass('on').siblings('.on').removeClass('on'); }); + + var $passMask = $inputfield.find('.pass-mask'); + if($passMask.length) { + var $passMaskShow = $passMask.find('.pass-mask-show'); + var $passMaskHide = $passMask.find('.pass-mask-hide'); + $passMaskHide.hide(); + $passMaskShow.on('click', function(e) { + $(this).hide(); + $passMaskHide.show(); + $inputfield.find('input[type=password]').prop('type', 'text').addClass('pass-unmask'); + return false; + }); + $passMaskHide.hide().on('click', function(e) { + $(this).hide(); + $passMaskShow.show(); + $inputfield.find('input.pass-unmask').prop('type', 'password').removeClass('pass-unmask'); + return false; + }); + } }); // accommodate issue where Firefox auto-populates remembered password when it shouldn't diff --git a/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.min.js b/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.min.js index 99d34185..52c0e2b8 100644 --- a/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.min.js +++ b/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.min.js @@ -1 +1 @@ -jQuery(document).ready(function($){var $inputs=$("input.InputfieldPasswordComplexify");$inputs.each(function(){var $input=$(this);var $inputfield=$input.closest(".Inputfield");var $confirm=$inputfield.find(".InputfieldPasswordConfirm");var $confirms=$confirm.next(".pass-confirm");var $inputOld=$inputfield.find("input.InputfieldPasswordOld");var $wrapScores=$input.siblings(".pass-scores");var $percent=$input.siblings(".pass-percent");var $scores=$wrapScores.children();var requirements=$wrapScores.attr("data-requirements").split(" ");var minlength=parseInt($input.attr("data-minlength"));var inputOldEvent;var options={banMode:$input.attr("data-banMode"),strengthScaleFactor:parseFloat($input.attr("data-factor")),minimumChars:minlength};if($inputOld.length){$input.attr("disabled","disabled");inputOldEvent=function(){if($(this).val().length>0){$inputOld.off("keyup input change blur",inputOldEvent);$input.removeAttr("disabled")}};$inputOld.on("keyup input change blur",inputOldEvent)}if($confirm.length)$confirm.attr("disabled","disabled");$input.complexify(options,function(valid,complexity){var $on=null;var val=$input.val();var len=val.length;var numGood=0;if(len>0){for(var n=0;n("+Math.floor(complexity)+"%)")}if($confirm.val().length){$confirm.change()}});$input.on("change",function(){var val=$(this).val();if(val.length>0){$input.attr("required","required");$confirm.attr("required","required")}else if(!$(this).closest(".InputfieldStateRequired").length){$input.removeAttr("required");$confirm.removeAttr("required")}});$confirm.on("keyup change",function(){var val1=$input.val();var val2=$(this).val();var $on=null;var $p=$input.closest("p").removeClass("pass-matches");if(val2.length==0){$on=$confirms.children(".confirm-pending")}else if(val1==val2){$on=$confirms.children(".confirm-yes");$p.addClass("pass-matches")}else if(val1.indexOf(val2)===0){$on=$confirms.children(".confirm-qty");$on.children("span").html(val2.length+"/"+val1.length)}else{$on=$confirms.children(".confirm-no")}if($on)$on.addClass("on").siblings(".on").removeClass("on")})});var $ffinputs=$(".InputfieldPassword").find("input[autocomplete='new-password']");if($ffinputs.length){setTimeout(function(){$ffinputs.each(function(){if($(this).val().length<1||$(this).attr("value").length>0)return;$(this).val("").trigger("keyup").change().closest(".Inputfield").removeClass("InputfieldStateChanged")})},1e3)}}); \ No newline at end of file +jQuery(document).ready(function($){var $inputs=$("input.InputfieldPasswordComplexify");$inputs.each(function(){var $input=$(this);var $inputfield=$input.closest(".Inputfield");var $confirm=$inputfield.find(".InputfieldPasswordConfirm");var $confirms=$confirm.next(".pass-confirm");var $inputOld=$inputfield.find("input.InputfieldPasswordOld");var $wrapScores=$input.siblings(".pass-scores");var $percent=$input.siblings(".pass-percent");var $scores=$wrapScores.children();var requirements=$wrapScores.attr("data-requirements").split(" ");var minlength=parseInt($input.attr("data-minlength"));var inputOldEvent;var options={banMode:$input.attr("data-banMode"),strengthScaleFactor:parseFloat($input.attr("data-factor")),minimumChars:minlength};if($inputOld.length){$input.attr("disabled","disabled");inputOldEvent=function(){if($(this).val().length>0){$inputOld.off("keyup input change blur",inputOldEvent);$input.removeAttr("disabled")}};$inputOld.on("keyup input change blur",inputOldEvent)}if($confirm.length)$confirm.attr("disabled","disabled");$input.complexify(options,function(valid,complexity){var $on=null;var val=$input.val();var len=val.length;var numGood=0;if(len>0){for(var n=0;n("+Math.floor(complexity)+"%)")}if($confirm.val().length){$confirm.change()}});$input.on("change",function(){var val=$(this).val();if(val.length>0){$input.attr("required","required");$confirm.attr("required","required")}else if(!$(this).closest(".InputfieldStateRequired").length){$input.removeAttr("required");$confirm.removeAttr("required")}});$confirm.on("keyup change",function(){var val1=$input.val();var val2=$(this).val();var $on=null;var $p=$input.closest("p").removeClass("pass-matches");if(val2.length==0){$on=$confirms.children(".confirm-pending")}else if(val1==val2){$on=$confirms.children(".confirm-yes");$p.addClass("pass-matches")}else if(val1.indexOf(val2)===0){$on=$confirms.children(".confirm-qty");$on.children("span").html(val2.length+"/"+val1.length)}else{$on=$confirms.children(".confirm-no")}if($on)$on.addClass("on").siblings(".on").removeClass("on")});var $passMask=$inputfield.find(".pass-mask");if($passMask.length){var $passMaskShow=$passMask.find(".pass-mask-show");var $passMaskHide=$passMask.find(".pass-mask-hide");$passMaskHide.hide();$passMaskShow.on("click",function(e){$(this).hide();$passMaskHide.show();$inputfield.find("input[type=password]").prop("type","text").addClass("pass-unmask");return false});$passMaskHide.hide().on("click",function(e){$(this).hide();$passMaskShow.show();$inputfield.find("input.pass-unmask").prop("type","password").removeClass("pass-unmask");return false})}});var $ffinputs=$(".InputfieldPassword").find("input[autocomplete='new-password']");if($ffinputs.length){setTimeout(function(){$ffinputs.each(function(){if($(this).val().length<1||$(this).attr("value").length>0)return;$(this).val("").trigger("keyup").change().closest(".Inputfield").removeClass("InputfieldStateChanged")})},1e3)}}); \ No newline at end of file diff --git a/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.module b/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.module index 4a7d9753..c8fc80a9 100644 --- a/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.module +++ b/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.module @@ -3,7 +3,7 @@ /** * An Inputfield for handling a password * - * ProcessWire 3.x, Copyright 2020 by Ryan Cramer + * ProcessWire 3.x, Copyright 2021 by Ryan Cramer * https://processwire.com * * @property array $requirements Array of requirements (See require* constants) @@ -12,6 +12,7 @@ * @property float $complexifyFactor Complexify factor, lower numbers enable simpler passwords (default=0.7) * @property int $requireOld Require previous password? 0=Auto, 1=Yes, -1=No, Default=0 (Auto) * @property bool $showPass Allow password to be rendered in renderValue and/or re-populated in form? + * @property bool $unmask Allow passwords to be unmasked? (3.0.173+) * @property string $defaultLabel Default label for field (default='Set Password'). Used when no 'label' has been set. * @property string $oldPassLabel Label for old/current password placeholder. * @property string $newPassLabel Label for new password placeholder. @@ -114,6 +115,7 @@ class InputfieldPassword extends InputfieldText { self::requireNone => $this->_('none (disable all above)'), )); $this->set('showPass', false); // allow password to be rendered in renderValue and/or re-populated in form? + $this->set('unmask', false); $this->set('oldPassLabel', $this->_('Current password')); $this->set('newPassLabel', $this->_('New password')); $this->set('confirmLabel', $this->_('Confirm')); @@ -262,6 +264,14 @@ class InputfieldPassword extends InputfieldText { "$okIcon" . "" . "

"; + + if($this->unmask) { + $out .= + "

" . + "" . $this->_('Show Password') . "" . + "" . $this->_('Hide Password') . "" . + "

"; + } $this->attr('value', $value); if($trackChanges) $this->setTrackChanges(true); @@ -449,13 +459,14 @@ class InputfieldPassword extends InputfieldText { 'maxlength', 'showCount', 'size', - ); + ); foreach($skips as $name) { $f = $inputfields->get($name); if($f) $inputfields->remove($f); } - + + /** @var InputfieldCheckboxes $f */ $f = $this->wire('modules')->get('InputfieldCheckboxes'); $f->attr('name', 'requirements'); $f->label = $this->_('Password requirements'); @@ -467,7 +478,8 @@ class InputfieldPassword extends InputfieldText { $f->attr('value', $value); $f->columnWidth = 50; $inputfields->add($f); - + + /** @var InputfieldRadios $f */ $f = $this->wire('modules')->get('InputfieldRadios'); $f->attr('name', 'complexifyBanMode'); $f->label = $this->_('Word ban mode'); @@ -477,7 +489,8 @@ class InputfieldPassword extends InputfieldText { $f->attr('value', $this->complexifyBanMode); $f->columnWidth = 50; $inputfields->add($f); - + + /** @var InputfieldFloat $f */ $f = $this->wire('modules')->get('InputfieldFloat'); $f->attr('name', 'complexifyFactor'); $f->label = $this->_('Complexify factor'); @@ -488,6 +501,7 @@ class InputfieldPassword extends InputfieldText { $f->columnWidth = 50; $inputfields->add($f); + /** @var InputfieldInteger $f */ $f = $this->wire('modules')->get('InputfieldInteger'); $f->attr('name', 'minlength'); $f->label = $this->_('Minimum password length'); @@ -497,13 +511,15 @@ class InputfieldPassword extends InputfieldText { $inputfields->add($f); if(!$this->getSetting('hasFieldtype')) { + /** @var InputfieldCheckbox $f */ $f = $this->wire('modules')->get('InputfieldCheckbox'); $f->attr('name', 'showPass'); $f->label = $this->_('Allow existing passwords to be shown and/or rendered in form?'); if($this->getSetting("showPass")) $f->attr('checked', 'checked'); $inputfields->add($f); } - + + /** @var InputfieldRadios $f */ $f = $this->wire('modules')->get('InputfieldRadios'); $f->attr('name', 'requireOld'); $f->label = $this->_('Require old password before allowing changes?'); @@ -515,6 +531,16 @@ class InputfieldPassword extends InputfieldText { $f->attr('value', (int) $this->requireOld); $inputfields->add($f); + $f = $this->wire()->modules->get('InputfieldRadios'); + $f->attr('name', 'unmask'); + $f->label = $this->_('Allow user to show/unmask password during changes?'); + $f->description = $this->_('Provides a show/hide password control so users can see what they type when in an appropriate environment.'); + $f->addOption(1, $this->_('Yes')); + $f->addOption(0, $this->_('No')); + $f->optionColumns = 1; + $f->attr('value', (int) $this->unmask); + $inputfields->add($f); + return $inputfields; } }