Skip to content

Commit

Permalink
HTML5 Invoice
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathantneal committed Jul 31, 2012
1 parent aa4be79 commit 55f77ae
Show file tree
Hide file tree
Showing 4 changed files with 467 additions and 0 deletions.
82 changes: 82 additions & 0 deletions index.html
@@ -0,0 +1,82 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Invoice</title>
<link rel="stylesheet" href="style.css">
<link rel="license" href="http://www.opensource.org/licenses/mit-license/">
<script src="script.js"></script>
</head>
<body>
<header>
<h1>Invoice</h1>
<address contenteditable>
<p>Jonathan Neal</p>
<p>101 E. Chapman Ave<br>Orange, CA 92866</p>
<p>(800) 555-1234</p>
</address>
<span><img alt="" src="logo.png"><input type="file" accept="image/*"></span>
</header>
<article>
<h1>Recipient</h1>
<address contenteditable>
<p>Some Company<br>c/o Some Guy</p>
</address>
<table class="meta">
<tr>
<th><span contenteditable>Invoice #</span></th>
<td><span contenteditable>101138</span></td>
</tr>
<tr>
<th><span contenteditable>Date</span></th>
<td><span contenteditable>January 1, 2012</span></td>
</tr>
<tr>
<th><span contenteditable>Amount Due</span></th>
<td><span id="prefix" contenteditable>$</span><span>600.00</span></td>
</tr>
</table>
<table class="inventory">
<thead>
<tr>
<th><span contenteditable>Item</span></th>
<th><span contenteditable>Description</span></th>
<th><span contenteditable>Rate</span></th>
<th><span contenteditable>Quantity</span></th>
<th><span contenteditable>Price</span></th>
</tr>
</thead>
<tbody>
<tr>
<td><a class="cut">-</a><span contenteditable>Front End Consultation</span></td>
<td><span contenteditable>Experience Review</span></td>
<td><span data-prefix>$</span><span contenteditable>150.00</span></td>
<td><span contenteditable>4</span></td>
<td><span data-prefix>$</span><span>600.00</span></td>
</tr>
</tbody>
</table>
<a class="add">+</a>
<table class="balance">
<tr>
<th><span contenteditable>Total</span></th>
<td><span data-prefix>$</span><span>600.00</span></td>
</tr>
<tr>
<th><span contenteditable>Amount Paid</span></th>
<td><span data-prefix>$</span><span contenteditable>0.00</span></td>
</tr>
<tr>
<th><span contenteditable>Balance Due</span></th>
<td><span data-prefix>$</span><span>600.00</span></td>
</tr>
</table>
</article>
<aside>
<h1><span contenteditable>Additional Notes</span></h1>
<div contenteditable>
<p>A finance charge of 1.5% will be made on unpaid balances after 30 days.</p>
</div>
</aside>
</body>
</html>
Binary file added logo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
238 changes: 238 additions & 0 deletions script.js
@@ -0,0 +1,238 @@
/* Shivving (IE8 is not supported, but at least it won't look as awful)
/* ========================================================================== */

(function (document) {
var
head = document.head = document.getElementsByTagName('head')[0] || document.documentElement,
elements = 'article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output picture progress section summary time video x'.split(' '),
elementsLength = elements.length,
elementsIndex = 0,
element;

while (elementsIndex < elementsLength) {
element = document.createElement(elements[++elementsIndex]);
}

element.innerHTML = 'x<style>' +
'article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}' +
'audio[controls],canvas,video{display:inline-block}' +
'[hidden],audio{display:none}' +
'mark{background:#FF0;color:#000}' +
'</style>';

return head.insertBefore(element.lastChild, head.firstChild);
})(document);

/* Prototyping
/* ========================================================================== */

(function (window, ElementPrototype, ArrayPrototype, polyfill) {
function NodeList() { [polyfill] }
NodeList.prototype.length = ArrayPrototype.length;

ElementPrototype.matchesSelector = ElementPrototype.matchesSelector ||
ElementPrototype.mozMatchesSelector ||
ElementPrototype.msMatchesSelector ||
ElementPrototype.oMatchesSelector ||
ElementPrototype.webkitMatchesSelector ||
function matchesSelector(selector) {
return ArrayPrototype.indexOf.call(this.parentNode.querySelectorAll(selector), this) > -1;
};

ElementPrototype.ancestorQuerySelectorAll = ElementPrototype.ancestorQuerySelectorAll ||
ElementPrototype.mozAncestorQuerySelectorAll ||
ElementPrototype.msAncestorQuerySelectorAll ||
ElementPrototype.oAncestorQuerySelectorAll ||
ElementPrototype.webkitAncestorQuerySelectorAll ||
function ancestorQuerySelectorAll(selector) {
for (var cite = this, newNodeList = new NodeList; cite = cite.parentElement;) {
if (cite.matchesSelector(selector)) ArrayPrototype.push.call(newNodeList, cite);
}

return newNodeList;
};

ElementPrototype.ancestorQuerySelector = ElementPrototype.ancestorQuerySelector ||
ElementPrototype.mozAncestorQuerySelector ||
ElementPrototype.msAncestorQuerySelector ||
ElementPrototype.oAncestorQuerySelector ||
ElementPrototype.webkitAncestorQuerySelector ||
function ancestorQuerySelector(selector) {
return this.ancestorQuerySelectorAll(selector)[0] || null;
};
})(this, Element.prototype, Array.prototype);

/* Helper Functions
/* ========================================================================== */

function generateTableRow() {
var emptyColumn = document.createElement('tr');

emptyColumn.innerHTML = '<td><a class="cut">-</a><span contenteditable></span></td>' +
'<td><span contenteditable></span></td>' +
'<td><span data-prefix>$</span><span contenteditable>0.00</span></td>' +
'<td><span contenteditable>0</span></td>' +
'<td><span data-prefix>$</span><span>0.00</span></td>';

return emptyColumn;
}

function parseFloatHTML(element) {
return parseFloat(element.innerHTML.replace(/[^\d\.\-]+/g, '')) || 0;
}

function parsePrice(number) {
return number.toFixed(2).replace(/(\d)(?=(\d\d\d)+([^\d]|$))/g, '$1,');
}

/* Update Number
/* ========================================================================== */

function updateNumber(e) {
var
activeElement = document.activeElement,
value = parseFloat(activeElement.innerHTML),
wasPrice = activeElement.innerHTML == parsePrice(parseFloatHTML(activeElement));

if (!isNaN(value) && (e.keyCode == 38 || e.keyCode == 40 || e.wheelDeltaY)) {
e.preventDefault();

value += e.keyCode == 38 ? 1 : e.keyCode == 40 ? -1 : Math.round(e.wheelDelta * 0.025);
value = Math.max(value, 0);

activeElement.innerHTML = wasPrice ? parsePrice(value) : value;
}

updateInvoice();
}

/* Update Invoice
/* ========================================================================== */

function updateInvoice() {
var total = 0;
var cells, price, total, a, i;

// update inventory cells
// ======================

for (var a = document.querySelectorAll('table.inventory tbody tr'), i = 0; a[i]; ++i) {
// get inventory row cells
cells = a[i].querySelectorAll('span:last-child');

// set price as cell[2] * cell[3]
price = parseFloatHTML(cells[2]) * parseFloatHTML(cells[3]);

// add price to total
total += price;

// set row total
cells[4].innerHTML = price;
}

// update balance cells
// ====================

// get balance cells
cells = document.querySelectorAll('table.balance td:last-child span:last-child');

// set total
cells[0].innerHTML = total;

// set balance and meta balance
cells[2].innerHTML = document.querySelector('table.meta tr:last-child td:last-child span:last-child').innerHTML = parsePrice(total - parseFloatHTML(cells[1]));

// update prefix formatting
// ========================

var prefix = document.querySelector('#prefix').innerHTML;
for (a = document.querySelectorAll('[data-prefix]'), i = 0; a[i]; ++i) a[i].innerHTML = prefix;

// update price formatting
// =======================

for (a = document.querySelectorAll('span[data-prefix] + span'), i = 0; a[i]; ++i) if (document.activeElement != a[i]) a[i].innerHTML = parsePrice(parseFloatHTML(a[i]));
}

/* On Content Load
/* ========================================================================== */

function onContentLoad() {
updateInvoice();

var
input = document.querySelector('input'),
image = document.querySelector('img');

function onClick(e) {
var element = e.target.querySelector('[contenteditable]'), row;

element && e.target != document.documentElement && e.target != document.body && element.focus();

if (e.target.matchesSelector('.add')) {
document.querySelector('table.inventory tbody').appendChild(generateTableRow());
}
else if (e.target.className == 'cut') {
row = e.target.ancestorQuerySelector('tr');

row.parentNode.removeChild(row);
}

updateInvoice();
}

function onEnterCancel(e) {
e.preventDefault();

image.classList.add('hover');
}

function onLeaveCancel(e) {
e.preventDefault();

image.classList.remove('hover');
}

function onFileInput(e) {
image.classList.remove('hover');

var
reader = new FileReader(),
files = e.dataTransfer ? e.dataTransfer.files : e.target.files,
i = 0;

reader.onload = onFileLoad;

while (files[i]) reader.readAsDataURL(files[i++]);
}

function onFileLoad(e) {
var data = e.target.result;

image.src = data;
}

if (window.addEventListener) {
document.addEventListener('click', onClick);

document.addEventListener('mousewheel', updateNumber);
document.addEventListener('keydown', updateNumber);

document.addEventListener('keydown', updateInvoice);
document.addEventListener('keyup', updateInvoice);

input.addEventListener('focus', onEnterCancel);
input.addEventListener('mouseover', onEnterCancel);
input.addEventListener('dragover', onEnterCancel);
input.addEventListener('dragenter', onEnterCancel);

input.addEventListener('blur', onLeaveCancel);
input.addEventListener('dragleave', onLeaveCancel);
input.addEventListener('mouseout', onLeaveCancel);

input.addEventListener('drop', onFileInput);
input.addEventListener('change', onFileInput);
}
}

window.addEventListener && document.addEventListener('DOMContentLoaded', onContentLoad);

0 comments on commit 55f77ae

Please sign in to comment.