Skip to content

Instantly share code, notes, and snippets.

@mhakes
Last active August 29, 2015 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mhakes/d612b6c31c32b61cbc9a to your computer and use it in GitHub Desktop.
Save mhakes/d612b6c31c32b61cbc9a to your computer and use it in GitHub Desktop.
Denton
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>&nbsp;',
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: '&nbsp;<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);
};
}

Denton

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

License.

/*
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);
});
<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>
.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