A Pen by mhakes on CodePen. This Constructor takes a jQuery form element, and allows for: beautifying, formatting, interacting, validating, populating and cultivating of the form. Purposely did not use $.fn to extend jQuery. Bootstrap used in this example
Last active
August 29, 2015 14:17
-
-
Save mhakes/d612b6c31c32b61cbc9a to your computer and use it in GitHub Desktop.
Denton
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Denton($el) { | |
var levi = this; | |
this.current = {}; | |
if (!(this instanceof Denton)) { | |
return new Denton($el); | |
} | |
if (!($el instanceof jQuery)) { | |
alert('Please pass a form'); | |
return; | |
} | |
this.form = $el; | |
this.populated = false; | |
this.opt = { | |
// use bootstrap-select | |
selectpicker: true, | |
capLabels: false, | |
checked: 'btn-default', | |
notChecked: 'btn-success', | |
icon: '<span class="glyphicon glyphicon-check"></span> ', | |
err: '<span class="errSpan"><br>Required Please</span>', | |
notRight: '<span class="notRight errSpan" style="color:red;"><br>Something is not right with this one <span class="glyphicon glyphicon-hand-down"></span></span>', | |
req: ' <sup><span class="glyphicon glyphicon-star" style="color:red;"></span></sup>' | |
}; | |
// Use the browser's built-in functionality to quickly and safely escape the string | |
// http://shebang.brandonmintern.com/foolproof-html-escaping-in-javascript/ | |
function _escapeHtml(str) { | |
var div = document.createElement('div'); | |
div.appendChild(document.createTextNode(str)); | |
return div.innerHTML; | |
} | |
// UNSAFE with unsafe strings; only use on previously-escaped ones! | |
function _unescapeHtml(escapedStr) { | |
var div = document.createElement('div'); | |
div.innerHTML = escapedStr; | |
var child = div.childNodes[0]; | |
return child ? child.nodeValue : ''; | |
} | |
// | |
function _getLabel($item) { | |
return levi.form.find('[for="' + $item.attr('id') + '"]'); | |
} | |
function _makePretty() { | |
$('.denton-row').each(function() { | |
var highestBox = 0; | |
$('.denton-group', this).each(function() { | |
if ($(this).height() > highestBox) { | |
highestBox = $(this).height(); | |
} | |
}); | |
highestBox += 30; | |
$('.denton-group', this).height(highestBox); | |
}); | |
} | |
function _checkValidValue($el) { | |
var isZip = function(zip) { | |
var regex = /^\d{5}$/; | |
if (regex.test(zip)) { | |
return true; | |
} | |
return false; | |
}; | |
var formatPhone = function(pn) { | |
// will return null if not a valid phone number | |
// http://stackoverflow.com/users/244128/maerics | |
var pn2 = ("" + pn).replace(/\D/g, ''); | |
var m = pn2.match(/^(\d{3})(\d{3})(\d{4})$/); | |
return (!m) ? null : "(" + m[1] + ") " + m[2] + "-" + m[3]; | |
}; | |
// http://stackoverflow.com/questions/46155/validate-email-address-in-javascript | |
var validateEmail = function(email) { | |
var re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; | |
return re.test(email); | |
}; | |
var y; | |
var val = $.trim($el.val()); | |
switch ($el.attr('data-vtype')) { | |
case 'zip': | |
y = isZip(val); | |
break; | |
case 'phone': | |
y = formatPhone(val); | |
if (_.isNull(y)) { | |
y = false; | |
} else { | |
$el.val(y); | |
y = true; | |
} | |
break; | |
case 'email': | |
y = validateEmail(val); | |
break; | |
case 'int': | |
val = Number(val); | |
if (!isNaN(parseFloat(val)) && isFinite(val)) { | |
y = true; | |
} else { | |
y = false; | |
} | |
break; | |
} | |
return y; | |
} | |
function _setUpLabel($item) { | |
var $lab = _getLabel($item); | |
$item.addClass('denton-formatted'); | |
if (levi.opt.capLabels) { | |
$lab.text($lab.text().toUpperCase()); | |
} | |
if ($item.hasClass('denton-req')) { | |
$lab.append(levi.opt.req); | |
} | |
} | |
function _formatButtons($item) { | |
$item.children('button').each(function(i, b) { | |
$(b).addClass('btn').addClass(levi.opt.notChecked).attr('role', 'button').attr('data-text', $(b).text()); | |
if ($item.hasClass('btn-group-vertical')) { | |
$(b).addClass('btn-block'); | |
} | |
}); | |
} | |
function _formatLabel($item, valid) { | |
var $lab = _getLabel($item); | |
if (valid) { | |
$item.addClass('denton-chosen'); | |
if ($lab.hasClass('denton-valid')) { | |
return; | |
} | |
$lab.find('.errSpan').remove(); | |
$lab.removeClass("denton-error denton-label").addClass("denton-valid"); | |
return; | |
} | |
if ($lab.hasClass('denton-error')) { | |
return; | |
} | |
$item.removeClass('denton-chosen'); | |
$lab.removeClass("denton-valid"); | |
if (!$item.hasClass('denton-req')) { | |
$lab.addClass("denton-label"); | |
return; | |
} | |
$lab.removeClass("denton-label").addClass("denton-error").append(levi.opt.err); | |
} | |
function _uncheck($i) { | |
$i.removeClass(levi.opt.checked).removeClass('denton-choice').addClass(levi.opt.notChecked).html($i.attr('data-text')); | |
} | |
function _check($i) { | |
$i.removeClass(levi.opt.notChecked).addClass('denton-choice').addClass(levi.opt.checked).html(levi.opt.icon + $i.attr('data-text')); | |
} | |
function _formatRadio() { | |
var $radio = levi.form.find('.denton-radio').not('.denton-formatted'); | |
$radio.each(function(i, r) { | |
var $r = $(r); | |
_setUpLabel($r); | |
_formatButtons($r); | |
$r.on("click", function(e) { | |
var $b = $(e.target); | |
e.preventDefault(); | |
if ($b.hasClass('glyphicon')) { | |
$b = $b.parent(); | |
} | |
if ($b.hasClass('denton-choice')) { | |
return; | |
} | |
if (!$b.hasClass('btn')) { | |
return; | |
} | |
_uncheck($r.find('.denton-choice')); | |
_check($b); | |
_formatLabel($r, true); | |
levi.current[$r.attr('id')] = $b.attr('value'); | |
levi.cb($r, { | |
id: $r.attr('id'), | |
value: $b.attr('value') | |
}); | |
}); | |
}); | |
} | |
function _formatCheck() { | |
var $check = levi.form.find('.denton-check').not('.denton-formatted'); | |
$check.each(function(i, c) { | |
var $c = $(c); | |
_setUpLabel($c); | |
_formatButtons($c); | |
$c.on("click", function(e) { | |
var $b = $(e.target); | |
var arr = []; | |
levi.current[$c.attr('id')] = arr; | |
e.preventDefault(); | |
if ($b.hasClass('glyphicon')) { | |
$b = $b.parent(); | |
} | |
if (!$b.hasClass('btn')) { | |
return; | |
} | |
if ($b.hasClass('denton-choice')) { | |
_uncheck($b); | |
} else { | |
_check($b); | |
} | |
if ($c.find('.denton-choice').length) { | |
$c.find('.denton-choice').each(function(inx, y) { | |
arr.push($(y).attr('value')); | |
}); | |
_formatLabel($c, true); | |
levi.current[$c.attr('id')] = arr; | |
levi.cb($c, { | |
id: $c.attr('id'), | |
value: arr | |
}); | |
return; | |
} | |
_formatLabel($c, false); | |
}); | |
}); | |
} | |
function _formatInput() { | |
var $input = levi.form.find('.denton-input').not('.denton-formatted'); | |
$input.each(function(x, i) { | |
var $i = $(i), | |
$lab = _getLabel($i); | |
_setUpLabel($i); | |
$i.on("blur", function(e) { | |
$i.val(_escapeHtml($i.val())); | |
levi.current[$i.attr('id')] = $i.val(); | |
if ($i.val().length) { | |
$i.valid = true; | |
if ($i.hasClass('denton-vcheck')) { | |
$i.valid = _checkValidValue($i); | |
} | |
if ($i.valid) { | |
$lab.find('.notRight').remove(); | |
_formatLabel($i, true); | |
levi.cb($i, { | |
id: $i.attr('id'), | |
value: $i.val() | |
}); | |
return; | |
} | |
$lab.removeClass("denton-valid").addClass('denton-label').append(levi.opt.notRight); | |
} else { | |
_formatLabel($i, false); | |
} | |
}).on("focus", function(e) { | |
if (e.which === 9) { | |
return; | |
} | |
if ($i.val().length) { | |
if ($lab.find('.notRight')) { | |
return; | |
} | |
if (!$lab.hasClass('denton-valid')) { | |
_formatLabel($i, true); | |
} | |
} | |
}).on("keyup", function() { | |
if ($i.val().length) { | |
if (!$lab.hasClass('denton-valid')) { | |
_formatLabel($i, true); | |
} | |
} else { | |
_formatLabel($i, false); | |
} | |
}); | |
}); | |
} | |
function _formatSelect() { | |
levi.form.find('.denton-select').not('.denton-formatted').each(function(i, s) { | |
var $s = $(s); | |
_setUpLabel($s); | |
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent)) { | |
$s.selectpicker('mobile'); | |
$s.selectpicker({ | |
style: levi.opt.notChecked | |
}); | |
} | |
$s.selectpicker({ | |
style: levi.opt.notChecked | |
}); | |
$s.on("change", function() { | |
levi.current[$s.attr('id')] = $s.val(); | |
if ($.isEmptyObject($s.val())) { | |
$s.selectpicker('setStyle', levi.opt.notChecked); | |
_formatLabel($s, false); | |
} else { | |
levi.cb($s, { | |
id: $s.attr('id'), | |
value: $s.val() | |
}); | |
_formatLabel($s, true); | |
$s.selectpicker('setStyle', levi.opt.checked); | |
} | |
$s.selectpicker('render'); | |
}); | |
}); | |
} | |
function _formatSelectPlain() { | |
levi.form.find('.denton-select').not('.denton-formatted').each(function(i, s) { | |
var $s = $(s); | |
_setUpLabel($s); | |
$s.on("change", function() { | |
levi.current[$s.attr('id')] = $s.val(); | |
if ($.isEmptyObject($s.val())) { | |
_formatLabel($s, false); | |
} else { | |
levi.cb($s, { | |
id: $s.attr('id'), | |
value: $s.val() | |
}); | |
_formatLabel($s, true); | |
} | |
}); | |
}); | |
} | |
this.format = function(obj) { | |
if ($.isPlainObject(obj)) { | |
levi.opt = $.extend(true, levi.opt, obj); | |
} | |
levi.form.find('.denton-value').not('.denton-formatted').each(function(i, b) { | |
$(b).text($(b).attr('value')).addClass('denton-formatted'); | |
}); | |
levi.form.find('label').addClass('denton-label denton-80'); | |
_formatRadio(); | |
_formatCheck(); | |
_formatInput(); | |
if (levi.opt.selectpicker) { | |
_formatSelect(); | |
} else { | |
_formatSelectPlain(); | |
} | |
_makePretty(); | |
return levi; | |
}; | |
this.setup = function(cb) { | |
levi.cb = cb; | |
return levi; | |
}; | |
this.populate = function(data, cb) { | |
$.each(data, function(k, v) { | |
var $f = $('#' + k); | |
levi.current[k] = _escapeHtml(v); | |
if ($f) { | |
if ($f.hasClass('denton-radio')) { | |
$f.find('[value="' + v + '"]').trigger('click'); | |
} else if ($f.hasClass('denton-check')) { | |
$.each(v, function(b, p) { | |
$f.find('[value="' + p + '"]').trigger('click'); | |
}); | |
} else if ($f.hasClass('denton-input')) { | |
$f.val(v).trigger('blur'); | |
} else if ($f.hasClass('denton-select')) { | |
$f.selectpicker('val', v); | |
$f.trigger('change'); | |
$f.selectpicker('render'); | |
} | |
} | |
}); | |
}; | |
this.validate = function(func) { | |
if ($(".errSpan").is(':visible')) { | |
func(false); | |
return; | |
} | |
levi.form.find('.denton-req').not('.denton-chosen').not(':hidden').each(function(j, k) { | |
_formatLabel($(k), false); | |
}); | |
// selectpicker selects are hidden | |
// add the class denton-hidden if you don't want to validate the element | |
levi.form.find('select.denton-req').not('.denton-chosen').not(".denton-hidden").not(':visible').each(function(j, k) { | |
_formatLabel($(k), false); | |
}); | |
if ($(".errSpan").is(':visible')) { | |
func(false); | |
return; | |
} | |
func(true); | |
}; | |
this.cultivate = function(func) { | |
var send = {}; | |
levi.form.find('.denton-chosen').each(function(i, z) { | |
var $z = $(z), | |
id = $z.attr('id'); | |
if (id) { | |
if ($z.hasClass('denton-radio')) { | |
send[id] = $z.find('.denton-choice').attr('value'); | |
} else if ($z.hasClass('denton-check')) { | |
send[id] = []; | |
$z.find('.denton-choice').each(function(inx, n) { | |
send[id].push($(n).attr('value')); | |
}); | |
} else if ($z.hasClass('denton-input')) { | |
send[id] = he.encode($z.val()); | |
} else if ($z.hasClass('denton-select')) { | |
send[id] = $z.val(); | |
} else { | |
console.log($z); | |
} | |
} | |
}); | |
func(send); | |
}; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
example use | |
*/ | |
var Levi = new Denton($('#testForm')); | |
var x = { | |
radio1: 'C', | |
check1: ['2', '3'], | |
select1: ['Brown', 'Blue'] | |
}; | |
// method to work with user interactions | |
// @params | |
// $obj jQuery element interacted with | |
// d object d.id, (the id of the $obj) d.value the value being passed | |
Levi.process = function($obj, d) { | |
if (!Levi.populated) { | |
return; | |
} | |
// work with the form elements (hide show etc.) | |
if ($obj.is('[data-next]')) { | |
$("#" + $obj.data('next')).focus(); | |
} | |
}; | |
Levi.setup(Levi.process).format({ | |
capLabels: true, | |
checked: 'btn-success', | |
notChecked: 'btn-black' | |
}).populate(x, function() { | |
Levi.populated = true; | |
}); | |
$('#validateButton').on("click", function(e) { | |
e.preventDefault(); | |
if ($(this).hasClass('usedUp')) { | |
return; | |
} | |
$(this).attr('disabled', 'disabled'); | |
Levi.validate(function(valid) { | |
if (valid) { | |
$('#validateButton').removeClass('btn-primary').addClass('btn-success usedUp').html('Valid! Thanks.').removeAttr("disabled"); | |
Levi.cultivate(function(send) { | |
console.log(send); | |
}); | |
} | |
$('.btn-primary').removeAttr('disabled'); | |
}); | |
}); | |
$('.btn-danger').on("click", function(e) { | |
e.preventDefault(); | |
console.log(Levi.current); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="container-fluid"> | |
<form id="testForm"> | |
<div class="row denton-row"> | |
<div class="col-md-4"> | |
<div class="denton-group"> | |
<p><label for="radio1">Radio 1</label></p> | |
<p class="btn-group denton-radio denton-req" id="radio1"> | |
<button class="denton-value" value="A"></button> | |
<button class="denton-value" value="B"></button> | |
<button class="denton-value" value="C"></button> | |
</p> | |
</div> | |
</div> | |
<div class="col-md-4"> | |
<div class="denton-group"> | |
<p><label for="check1">Check 1</label></p> | |
<p class="btn-group denton-check denton-req" id="check1" data-next="input1"> | |
<button value="1" class="denton-value"></button> | |
<button value="2" class="denton-value"></button> | |
<button value="3" class="denton-value"></button> | |
</p> | |
</div> | |
</div> | |
<div class="col-md-4"> | |
<div class="denton-group"> | |
<label id="wtf" for="input1">Input 1</label> | |
<input data-vtype="zip" type="text" id="input1" class="denton-input denton-vcheck denton-80 denton-req" /> | |
</div> | |
</div> | |
</div> | |
<div class="row denton-row"> | |
<div class="col-md-6"> | |
<div class="denton-group"> | |
<label for="select1">Select 1</label> | |
<select name="select1[]" multiple id="select1" class="denton-select denton-req"> | |
<option value="Red">Red</option> | |
<option value="Brown">Brown</option> | |
<option value="Green">Green</option> | |
<option value="Blue">Blue</option> | |
</select> | |
</div> | |
</div> | |
<div class="col-md-6"> | |
<div class="denton-group"> | |
<p><label for="vertCheck">Vertical Check</label></p> | |
<p class="btn-group btn-group-vertical denton-check denton-req denton-40" id="vertCheck"> | |
<button value="Betty" class="denton-value"></button> | |
<button value="Abby" class="denton-value"></button> | |
<button value="Alec" class="denton-value"></button> | |
<button value="Steve" class="denton-value"></button> | |
<button value="Levi" class="denton-value"></button> | |
</p> | |
</div> | |
</div> | |
</div> | |
</form> | |
<div class="row"> | |
<div class="col-md-offset-3 col-md-6"> | |
<button id="validateButton" class="btn btn-primary">Validate</button> | |
<br /> | |
<button class="btn btn-danger">Log Current</button> | |
</div> | |
</div> | |
</div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.denton-label, .denton-valid, .denton-error { | |
width: 80%; | |
} | |
.denton-label { | |
color: black; | |
border-bottom: 2px solid black; | |
} | |
.denton-valid { | |
color: green; | |
border-bottom: 2px solid green; | |
} | |
.denton-error { | |
color: red; | |
border-bottom: 2px solid red; | |
} | |
.denton-20 { | |
width: 20%; | |
} | |
.denton-40 { | |
width: 40%; | |
} | |
.denton-80 { | |
width: 80%; | |
} | |
.denton-group { | |
border: 2px solid DarkSlateGrey; | |
border-radius: 4px; | |
text-align: center; | |
margin-top 10px; | |
margin-bottom: 10px; | |
padding: 10px; | |
} | |
.denton-input, .denton-radio, .denton-check, .denton-select { | |
padding-top: 8px; | |
} | |
.denton-input { | |
border-radius: 6px; | |
padding: 12px; | |
color: black; | |
margin: 8px auto 30px auto; | |
} | |
.denton-choice { | |
font-weight: bold; | |
} | |
/* | |
http://charliepark.org/bootstrap_buttons/ | |
*/ | |
.btn-black { | |
background-color: hsl(0, 0%, 1%) !important; | |
background-repeat: repeat-x; | |
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#353535", endColorstr="#020202"); | |
background-image: -khtml-gradient(linear, left top, left bottom, from(#353535), to(#020202)); | |
background-image: -moz-linear-gradient(top, #353535, #020202); | |
background-image: -ms-linear-gradient(top, #353535, #020202); | |
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #353535), color-stop(100%, #020202)); | |
background-image: -webkit-linear-gradient(top, #353535, #020202); | |
background-image: -o-linear-gradient(top, #353535, #020202); | |
background-image: linear-gradient(#353535, #020202); | |
border-color: #020202 #020202 hsl(0, 0%, -4%); | |
color: #fff !important; | |
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.33); | |
-webkit-font-smoothing: antialiased; | |
} | |
.btn-gold { | |
background-color: hsl(42, 67%, 14%) !important; | |
background-repeat: repeat-x; | |
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#d6a532", endColorstr="#3b2d0b"); | |
background-image: -khtml-gradient(linear, left top, left bottom, from(#d6a532), to(#3b2d0b)); | |
background-image: -moz-linear-gradient(top, #d6a532, #3b2d0b); | |
background-image: -ms-linear-gradient(top, #d6a532, #3b2d0b); | |
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #d6a532), color-stop(100%, #3b2d0b)); | |
background-image: -webkit-linear-gradient(top, #d6a532, #3b2d0b); | |
background-image: -o-linear-gradient(top, #d6a532, #3b2d0b); | |
background-image: linear-gradient(#d6a532, #3b2d0b); | |
border-color: #3b2d0b #3b2d0b hsl(42, 67%, 4.5%); | |
color: #fff !important; | |
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.62); | |
-webkit-font-smoothing: antialiased; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment