From 6f1eda559a482964fe1f6cbea9afd2f0c288d063 Mon Sep 17 00:00:00 2001 From: Joshua Gourneau Date: Tue, 13 Mar 2012 23:46:55 -0700 Subject: signature pad include --- index.html | 35 ++ js/flashcanvas.js | 28 ++ js/flashcanvas.swf | Bin 0 -> 21235 bytes js/journal.eot | Bin 0 -> 134296 bytes js/journal.svg | 264 +++++++++++++++ js/journal.ttf | Bin 0 -> 134056 bytes js/journal.woff | Bin 0 -> 84336 bytes js/jquery.signaturepad.css | 217 +++++++++++++ js/jquery.signaturepad.js | 739 ++++++++++++++++++++++++++++++++++++++++++ js/jquery.signaturepad.min.js | 28 ++ js/json2.min.js | 28 ++ js/pen.cur | Bin 0 -> 4286 bytes 12 files changed, 1339 insertions(+) create mode 100755 index.html create mode 100755 js/flashcanvas.js create mode 100755 js/flashcanvas.swf create mode 100755 js/journal.eot create mode 100755 js/journal.svg create mode 100755 js/journal.ttf create mode 100755 js/journal.woff create mode 100755 js/jquery.signaturepad.css create mode 100755 js/jquery.signaturepad.js create mode 100755 js/jquery.signaturepad.min.js create mode 100755 js/json2.min.js create mode 100755 js/pen.cur diff --git a/index.html b/index.html new file mode 100755 index 0000000..3611eec --- /dev/null +++ b/index.html @@ -0,0 +1,35 @@ + + + + Require a Drawn Signature · Signature Pad + + + + + + +
+ + +

Draw your signature

+ + clear + +
+
+ + +
+ +
+ + + + + diff --git a/js/flashcanvas.js b/js/flashcanvas.js new file mode 100755 index 0000000..dc0eee0 --- /dev/null +++ b/js/flashcanvas.js @@ -0,0 +1,28 @@ +/* + * FlashCanvas + * + * Copyright (c) 2009 Tim Cameron Ryan + * Copyright (c) 2009-2011 FlashCanvas Project + * Released under the MIT/X License + */ +window.ActiveXObject&&!window.CanvasRenderingContext2D&&function(h,j){function D(a){this.code=a;this.message=T[a]}function U(a){this.width=a}function E(a){this.id=a.C++}function t(a){this.G=a;this.id=a.C++}function u(a,b){this.canvas=a;this.B=b;this.d=a.uniqueID;this.D();this.C=0;this.t="";var c=this;setInterval(function(){n[c.d]===0&&c.e()},30)}function A(){if(j.readyState==="complete"){j.detachEvent(F,A);for(var a=j.getElementsByTagName(r),b=0,c=a.length;b=8?a.src:a.getAttribute("src",4)}function v(a){return(""+a).replace(/&/g,"&").replace(/0)return eval(this.B.CallFunction(''+a.join("�")+""))},I:function(a,b){this.e();this.D();if(a>0)this.B.width=a;if(b>0)this.B.height=b;this.a.push(e.resize,a,b)}};t.prototype={addColorStop:function(a,b){if(isNaN(a)||a<0||a>1)i(1);this.G.a.push(e.addColorStop,this.id,a,b)}};D.prototype=Error();var T={1:"INDEX_SIZE_ERR",9:"NOT_SUPPORTED_ERR",11:"INVALID_STATE_ERR", +12:"SYNTAX_ERR",17:"TYPE_MISMATCH_ERR",18:"SECURITY_ERR"},B={initElement:function(a){if(a.getContext)return a;var b=a.uniqueID,c="external"+b;x[b]=false;n[b]=1;Q(a);a.innerHTML=''; +s[b]=a;var d=a.firstChild;y[b]=a.lastChild;var f=j.body.contains;if(f(a))d.movie=w;else var g=setInterval(function(){if(f(a)){clearInterval(g);d.movie=w}},0);if(j.compatMode==="BackCompat"||!h.XMLHttpRequest)y[b].style.overflow="hidden";var o=new u(a,d);a.getContext=function(l){return l==="2d"?o:k};a.toDataURL=function(l,z){(""+l).replace(/[A-Z]+/g,W)==="image/jpeg"?o.a.push(e.toDataURL,l,typeof z==="number"?z:""):o.a.push(e.toDataURL,l);return o.e()};d.attachEvent(K,G);return a},saveImage:function(a){a.firstChild.saveImage()}, +setOptions:function(){},trigger:function(a,b){s[a].fireEvent("on"+b)},unlock:function(a,b){n[a]&&--n[a];if(b){var c=s[a],d=c.firstChild,f,g;Q(c);f=c.width;g=c.height;c.style.width=f+"px";c.style.height=g+"px";if(f>0)d.width=f;if(g>0)d.height=g;d.resize(f,g);c.attachEvent(L,H);x[a]=true}}};j.createElement(r);j.createStyleSheet().cssText=r+"{display:inline-block;overflow:hidden;width:300px;height:150px}";j.readyState==="complete"?A():j.attachEvent(F,A);h.attachEvent(J,I);if(w.indexOf(location.protocol+ +"//"+location.host+"/")===0){var S=new ActiveXObject("Microsoft.XMLHTTP");S.open("GET",w,false);S.send(k)}h[M]=u;h[N]=t;h[O]=E;h[C]=B;h[P]={init:function(){},init_:function(){},initElement:B.initElement};keep=u.measureText}(window,document); diff --git a/js/flashcanvas.swf b/js/flashcanvas.swf new file mode 100755 index 0000000..66ff213 Binary files /dev/null and b/js/flashcanvas.swf differ diff --git a/js/journal.eot b/js/journal.eot new file mode 100755 index 0000000..00c934d Binary files /dev/null and b/js/journal.eot differ diff --git a/js/journal.svg b/js/journal.svg new file mode 100755 index 0000000..b42901d --- /dev/null +++ b/js/journal.svg @@ -0,0 +1,264 @@ + + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Thank you for travelling with Fontourist com RWE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/js/journal.ttf b/js/journal.ttf new file mode 100755 index 0000000..3c967de Binary files /dev/null and b/js/journal.ttf differ diff --git a/js/journal.woff b/js/journal.woff new file mode 100755 index 0000000..03f8fc4 Binary files /dev/null and b/js/journal.woff differ diff --git a/js/jquery.signaturepad.css b/js/jquery.signaturepad.css new file mode 100755 index 0000000..ff5e348 --- /dev/null +++ b/js/jquery.signaturepad.css @@ -0,0 +1,217 @@ +/** + * SignaturePad: A jQuery plugin for assisting in the creation of an HTML5 canvas + * based signature pad. Records the drawn signature in JSON for later regeneration. + * + * @project ca.thomasjbradley.applications.signaturepad + * @author Thomas J Bradley + * @link http://thomasjbradley.ca/lab/signature-pad + * @link http://github.com/thomasjbradley/signature-pad + * @copyright Copyright MMXI, Thomas J Bradley + * @license New BSD License + */ + +@font-face { + font-family: 'Journal'; + src: url('journal.eot'); + src: url('journal.eot?#iefix') format('embedded-opentype'), + url('journal.woff') format('woff'), + url('journal.ttf') format('truetype'), + url('journal.svg#JournalRegular') format('svg'); + font-weight: normal; + font-style: normal; +} + +.sigPad { + margin: 0; + padding: 0; + width: 200px; +} + +.sigPad label { + display: block; + margin: 0 0 0.515em; + padding: 0; + + color: #000; + font: italic normal 1em/1.375 Georgia,Times,serif; +} + +.sigPad label.error { + color: #f33; +} + +.sigPad input { + margin: 0; + padding: 0.2em 0; + width: 198px; + + border: 1px solid #666; + + font-size: 1em; +} + +.sigPad input.error { + border-color: #f33; +} + +.sigPad button { + margin: 1em 0 0 0; + padding: 0.6em 0.6em 0.7em; + + background-color: #ccc; + border: 0; + -moz-border-radius: 8px; + -webkit-border-radius: 8px; + border-radius: 8px; + + cursor: pointer; + + color: #555; + font: bold 1em/1.375 sans-serif; + text-align: left; +} + +.sigPad button:hover { + background-color: #333; + + color: #fff; +} + +.sig { + display: none; +} + +.sigNav { + display: none; + height: 2.25em; + margin: 0; + padding: 0; + position: relative; + + list-style-type: none; +} + +.sigNav li { + display: inline; + float: left; + margin: 0; + padding: 0; +} + +.sigNav a, +.sigNav a:link, +.sigNav a:visited { + display: block; + margin: 0; + padding: 0 0.6em; + + border: 0; + + color: #333; + font-weight: bold; + line-height: 2.25em; + text-decoration: underline; +} + +.sigNav a.current, +.sigNav a.current:link, +.sigNav a.current:visited { + background-color: #666; + -moz-border-radius-topleft: 8px; + -moz-border-radius-topright: 8px; + -webkit-border-top-left-radius: 8px; + -webkit-border-top-right-radius: 8px; + border-radius: 8px 8px 0 0; + + color: #fff; + text-decoration: none; +} + +.sigNav .typeIt a.current, +.sigNav .typeIt a.current:link, +.sigNav .typeIt a.current:visited { + background-color: #ccc; + + color: #555; +} + +.sigNav .clearButton { + bottom: 0.2em; + display: none; + position: absolute; + right: 0; + + font-size: 0.75em; + line-height: 1.375; +} + +.sigWrapper { + clear: both; + height: 55px; + + border: 1px solid #ccc; +} + +.sigWrapper.current { + border-color: #666; +} + +.signed .sigWrapper { + border: 0; +} + +.pad { + position: relative; +} + +.current .pad { + /** + * For cross browser compatibility, this should be an absolute URL + * In IE the cursor is relative to the HTML document + * In all other browsers the cursor is relative to the CSS file + * + * http://www.useragentman.com/blog/2011/12/21/cross-browser-css-cursor-images-in-depth/ + */ + cursor: url("../build/pen.cur"), crosshair; + /** + * IE will ignore this line because of the hotspot position + * Unfortunately we need this twice, because some browsers ignore the hotspot inside the .cur + */ + cursor: url("pen.cur") 16 16, crosshair; +} + +.typed { + height: 55px; + margin: 0; + padding: 0 5px; + position: absolute; + z-index: 90; + + cursor: default; + + color: #145394; + font: normal 1.875em/50px "Journal",Georgia,Times,serif; +} + +.typeItDesc, +.drawItDesc { + display: none; + margin: 0.75em 0 0.515em; + padding: 0.515em 0 0; + + border-top: 3px solid #ccc; + + color: #000; + font: italic normal 1em/1.375 Georgia,Times,serif; +} + +p.error { + display: block; + margin: 0.5em 0; + padding: 0.4em; + + background-color: #f33; + + color: #fff; + font-weight: bold; +} diff --git a/js/jquery.signaturepad.js b/js/jquery.signaturepad.js new file mode 100755 index 0000000..31b661b --- /dev/null +++ b/js/jquery.signaturepad.js @@ -0,0 +1,739 @@ +/** + * @preserve SignaturePad: A jQuery plugin for assisting in the creation of an HTML5 canvas + * based signature pad. Records the drawn signature in JSON for later regeneration. + * + * Dependencies: FlashCanvas/1.5, json2.js, jQuery/1.3.2+ + * + * @project ca.thomasjbradley.applications.signaturepad + * @author Thomas J Bradley + * @link http://thomasjbradley.ca/lab/signature-pad + * @link http://github.com/thomasjbradley/signature-pad + * @copyright Copyright MMXI, Thomas J Bradley + * @license New BSD License + * @version {{version}} + */ + +/** + * Usage for accepting signatures: + * $('.sigPad').signaturePad() + * + * Usage for displaying previous signatures: + * $('.sigPad').signaturePad({displayOnly:true}).regenerate(sig) + * or + * var api = $('.sigPad').signaturePad({displayOnly:true}) + * api.regenerate(sig) + */ +(function ($) { + +function SignaturePad (selector, options) { + /** + * Reference to the object for use in public methods + * + * @private + * + * @type {Object} + */ + var self = this + + /** + * Holds the merged default settings and user passed settings + * + * @private + * + * @type {Object} + */ + , settings = $.extend({}, $.fn.signaturePad.defaults, options) + + /** + * The current context, as passed by jQuery, of selected items + * + * @private + * + * @type {Object} + */ + , context = $(selector) + + /** + * jQuery reference to the canvas element inside the signature pad + * + * @private + * + * @type {Object} + */ + , canvas = $(settings.canvas, context) + + /** + * Dom reference to the canvas element inside the signature pad + * + * @private + * + * @type {Object} + */ + , element = canvas.get(0) + + /** + * The drawing context for the signature canvas + * + * @private + * + * @type {Object} + */ + , canvasContext = null + + /** + * Holds the previous point of drawing + * Disallows drawing over the same location to make lines more delicate + * + * @private + * + * @type {Object} + */ + , previous = {'x': null, 'y': null} + + /** + * An array holding all the points and lines to generate the signature + * Each item is an object like: + * { + * mx: moveTo x coordinate + * my: moveTo y coordinate + * lx: lineTo x coordinate + * lx: lineTo y coordinate + * } + * + * @private + * + * @type {Array} + */ + , output = [] + + /** + * Stores a timeout for when the mouse leaves the canvas + * If the mouse has left the canvas for a specific amount of time + * Stops drawing on the canvas + * + * @private + * + * @type {Object} + */ + , mouseLeaveTimeout = false + + /** + * Whether the browser is a touch event browser or not + * + * @private + * + * @type {Boolean} + */ + , touchable = false + + /** + * Whether events have already been bound to the canvas or not + * + * @private + * + * @type {Boolean} + */ + , eventsBound = false + + + /** + * Draws a line on canvas using the mouse position + * Checks previous position to not draw over top of previous drawing + * (makes the line really thick and poorly anti-aliased) + * + * @private + * + * @param {Object} e The event object + * @param {Number} newYOffset A pixel value for drawing the newY, used for drawing a single dot on click + */ + function drawLine (e, newYOffset) { + var offset = $(e.target).offset(), newX, newY + + clearTimeout(mouseLeaveTimeout) + mouseLeaveTimeout = false + + if (typeof e.changedTouches !== 'undefined') { + newX = Math.floor(e.changedTouches[0].pageX - offset.left) + newY = Math.floor(e.changedTouches[0].pageY - offset.top) + } else { + newX = Math.floor(e.pageX - offset.left) + newY = Math.floor(e.pageY - offset.top) + } + + if (previous.x === newX && previous.y === newY) + return true + + if (previous.x === null) + previous.x = newX + + if (previous.y === null) + previous.y = newY + + if (newYOffset) + newY += newYOffset + + canvasContext.beginPath() + canvasContext.moveTo(previous.x, previous.y) + canvasContext.lineTo(newX, newY) + canvasContext.lineCap = settings.penCap + canvasContext.stroke() + canvasContext.closePath() + + output.push({ + 'lx': newX + ,'ly': newY + ,'mx': previous.x + ,'my': previous.y + }) + + previous.x = newX + previous.y = newY + } + + /** + * Callback registered to mouse/touch events of the canvas + * Stops the drawing abilities + * + * @private + * + * @param {Object} e The event object + */ + function stopDrawing () { + if (touchable) { + canvas.each(function () { + this.ontouchmove = null + }) + } else { + canvas.unbind('mousemove.signaturepad') + } + + previous.x = null + previous.y = null + + if (output.length > 0) + $(settings.output, context).val(JSON.stringify(output)) + } + + /** + * Draws the signature line + * + * @private + */ + function drawSigLine () { + if (!settings.lineWidth) + return false + + canvasContext.beginPath() + canvasContext.lineWidth = settings.lineWidth + canvasContext.strokeStyle = settings.lineColour + canvasContext.moveTo(settings.lineMargin, settings.lineTop) + canvasContext.lineTo(element.width - settings.lineMargin, settings.lineTop) + canvasContext.stroke() + canvasContext.closePath() + } + + /** + * Clears all drawings off the canvas and redraws the signature line + * + * @private + */ + function clearCanvas () { + stopDrawing() + + canvasContext.clearRect(0, 0, element.width, element.height) + canvasContext.fillStyle = settings.bgColour + canvasContext.fillRect(0, 0, element.width, element.height) + + if (!settings.displayOnly) + drawSigLine() + + canvasContext.lineWidth = settings.penWidth + canvasContext.strokeStyle = settings.penColour + + $(settings.output, context).val('') + output = [] + } + + /** + * Callback registered to mouse/touch events of canvas + * Triggers the drawLine function + * + * @private + * + * @param {Object} e The event object + * @param {Object} o The object context registered to the event; canvas + */ + function startDrawing (e, o) { + if (touchable) { + canvas.each(function () { + this.addEventListener('touchmove', drawLine, false) + }) + } else { + canvas.bind('mousemove.signaturepad', drawLine) + } + + // Draws a single point on initial mouse down, for people with periods in their name + drawLine(e, 1) + } + + /** + * Removes all the mouse events from the canvas + * + * @private + */ + function disableCanvas () { + eventsBound = false + + if (touchable) { + canvas.each(function () { + this.removeEventListener('touchstart', stopDrawing) + this.removeEventListener('touchend', stopDrawing) + this.removeEventListener('touchmove', drawLine) + }) + } else { + canvas.unbind('mousedown.signaturepad') + canvas.unbind('mouseup.signaturepad') + canvas.unbind('mousemove.signaturepad') + canvas.unbind('mouseleave.signaturepad') + } + + $(settings.clear, context).unbind('click.signaturepad') + } + + /** + * Lazy touch event detection + * Uses the first press on the canvas to detect either touch or mouse reliably + * Will then bind other events as needed + * + * @private + * + * @param {Object} e The event object + */ + function initDrawEvents (e) { + if (eventsBound) + return false + + eventsBound = true + + if (typeof e.changedTouches !== 'undefined') + touchable = true + + if (touchable) { + canvas.each(function () { + this.addEventListener('touchend', stopDrawing, false) + this.addEventListener('touchcancel', stopDrawing, false) + }) + + canvas.unbind('mousedown.signaturepad') + } else { + canvas.bind('mouseup.signaturepad', function (e) { stopDrawing() }) + canvas.bind('mouseleave.signaturepad', function (e) { + if (!mouseLeaveTimeout) { + mouseLeaveTimeout = setTimeout(function () { + stopDrawing() + clearTimeout(mouseLeaveTimeout) + mouseLeaveTimeout = false + }, 500) + } + }) + + canvas.each(function () { + this.ontouchstart = null + }) + } + } + + /** + * Triggers the abilities to draw on the canvas + * Sets up mouse/touch events, hides and shows descriptions and sets current classes + * + * @private + */ + function drawIt () { + $(settings.typed, context).hide() + clearCanvas() + + canvas.each(function () { + this.ontouchstart = function (e) { + e.preventDefault() + initDrawEvents(e) + startDrawing(e, this) + } + }) + + canvas.bind('mousedown.signaturepad', function (e) { + initDrawEvents(e) + startDrawing(e, this) + }) + + $(settings.clear, context).bind('click.signaturepad', function (e) { e.preventDefault(); clearCanvas() }) + + $(settings.typeIt, context).bind('click.signaturepad', function (e) { e.preventDefault(); typeIt() }) + $(settings.drawIt, context).unbind('click.signaturepad') + $(settings.drawIt, context).bind('click.signaturepad', function (e) { e.preventDefault() }) + + $(settings.typeIt, context).removeClass(settings.currentClass) + $(settings.drawIt, context).addClass(settings.currentClass) + $(settings.sig, context).addClass(settings.currentClass) + + $(settings.typeItDesc, context).hide() + $(settings.drawItDesc, context).show() + $(settings.clear, context).show() + } + + /** + * Triggers the abilities to type in the input for generating a signature + * Sets up mouse events, hides and shows descriptions and sets current classes + * + * @private + */ + function typeIt () { + clearCanvas() + disableCanvas() + $(settings.typed, context).show() + + $(settings.drawIt, context).bind('click.signaturepad', function (e) { e.preventDefault(); drawIt() }) + $(settings.typeIt, context).unbind('click.signaturepad') + $(settings.typeIt, context).bind('click.signaturepad', function (e) { e.preventDefault() }) + + $(settings.output, context).val('') + + $(settings.drawIt, context).removeClass(settings.currentClass) + $(settings.typeIt, context).addClass(settings.currentClass) + $(settings.sig, context).removeClass(settings.currentClass) + + $(settings.drawItDesc, context).hide() + $(settings.clear, context).hide() + $(settings.typeItDesc, context).show() + } + + /** + * Callback registered on key up and blur events for input field + * Writes the text fields value as Html into an element + * + * @private + * + * @param {String} val The value of the input field + */ + function type (val) { + $(settings.typed, context).html(val.replace(/>/g, '>').replace(/ element.width) { + var oldSize = $(settings.typed, context).css('font-size').replace(/px/, '') + $(settings.typed, context).css('font-size', oldSize-1+'px') + } + } + + /** + * Default onBeforeValidate function to clear errors + * + * @private + * + * @param {Object} context current context object + * @param {Object} settings provided settings + */ + function onBeforeValidate (context, settings) { + $('p.' + settings.errorClass, context).remove() + context.removeClass(settings.errorClass) + $('input, label', context).removeClass(settings.errorClass) + } + + /** + * Default onFormError function to show errors + * + * @private + * + * @param {Object} errors object contains validation errors (e.g. nameInvalid=true) + * @param {Object} context current context object + * @param {Object} settings provided settings + */ + function onFormError (errors, context, settings) { + if (errors.nameInvalid) { + context.prepend(['

', settings.errorMessage, '

'].join('')) + $(settings.name, context).focus() + $(settings.name, context).addClass(settings.errorClass) + $('label[for=' + $(settings.name).attr('id') + ']', context).addClass(settings.errorClass) + } + + if (errors.drawInvalid) + context.prepend(['

', settings.errorMessageDraw, '

'].join('')) + } + + /** + * Validates the form to confirm a name was typed in the field + * If drawOnly also confirms that the user drew a signature + * + * @private + * + * @return {Boolean} + */ + function validateForm () { + var valid = true + , errors = {drawInvalid: false, nameInvalid: false} + , onBeforeArguments = [context, settings] + , onErrorArguments = [errors, context, settings] + + if (settings.onBeforeValidate && typeof settings.onBeforeValidate === 'function') { + settings.onBeforeValidate.apply(self,onBeforeArguments) + } else { + onBeforeValidate.apply(self, onBeforeArguments) + } + + if (settings.drawOnly && output.length < 1) { + errors.drawInvalid = true + valid = false + } + + if ($(settings.name, context).val() === '') { + errors.nameInvalid = true + valid = false + } + + if (settings.onFormError && typeof settings.onFormError === 'function') { + settings.onFormError.apply(self,onErrorArguments) + } else { + onFormError.apply(self, onErrorArguments) + } + + return valid + } + + /** + * Redraws the signature on a specific canvas + * + * @private + * + * @param {Array} paths the signature JSON + * @param {Object} context the canvas context to draw on + * @param {Boolean} saveOutput whether to write the path to the output array or not + */ + function drawSignature (paths, context, saveOutput) { + for(var i in paths) { + if (typeof paths[i] === 'object') { + context.beginPath() + context.moveTo(paths[i].mx, paths[i].my) + context.lineTo(paths[i].lx, paths[i].ly) + context.lineCap = settings.penCap + context.stroke() + context.closePath() + + if (saveOutput) { + output.push({ + 'lx': paths[i].lx + ,'ly': paths[i].ly + ,'mx': paths[i].mx + ,'my': paths[i].my + }) + } + } + } + } + + /** + * Initialisation function, called immediately after all declarations + * Technically public, but only should be used internally + * + * @private + */ + function init () { + // Fixes the jQuery.fn.offset() function for Mobile Safari Browsers i.e. iPod Touch, iPad and iPhone + // https://gist.github.com/661844 + // http://bugs.jquery.com/ticket/6446 + if (parseFloat(((/CPU.+OS ([0-9_]{3}).*AppleWebkit.*Mobile/i.exec(navigator.userAgent)) || [0,'4_2'])[1].replace('_','.')) < 4.1) { + $.fn.Oldoffset = $.fn.offset; + $.fn.offset = function () { + var result = $(this).Oldoffset() + result.top -= window.scrollY + result.left -= window.scrollX + + return result + } + } + + // Disable selection on the typed div and canvas + $(settings.typed, context).bind('selectstart.signaturepad', function (e) { return $(e.target).is(':input') }) + canvas.bind('selectstart.signaturepad', function (e) { return $(e.target).is(':input') }) + + if (!element.getContext && FlashCanvas) + FlashCanvas.initElement(element) + + if (element.getContext) { + canvasContext = element.getContext('2d') + + $(settings.sig, context).show() + + if (!settings.displayOnly) { + if (!settings.drawOnly) { + $(settings.name, context).bind('keyup.signaturepad', function () { + type($(this).val()) + }) + + $(settings.name, context).bind('blur.signaturepad', function () { + type($(this).val()) + }) + + $(settings.drawIt, context).bind('click.signaturepad', function (e) { + e.preventDefault() + drawIt() + }) + } + + if (settings.drawOnly || settings.defaultAction === 'drawIt') { + drawIt() + } else { + typeIt() + } + + if (settings.validateFields) { + if ($(selector).is('form')) { + $(selector).bind('submit.signaturepad', function () { return validateForm() }) + } else { + $(selector).parents('form').bind('submit.signaturepad', function () { return validateForm() }) + } + } + + $(settings.sigNav, context).show() + } + } + } + + $.extend(self, { + /** + * Initializes SignaturePad + */ + init: function () { init() } + + /** + * Regenerates a signature on the canvas using an array of objects + * Follows same format as object property + * @see var object + * + * @param {Array} paths An array of the lines and points + */ + , regenerate: function (paths) { + self.clearCanvas() + $(settings.typed, context).hide() + + if (typeof paths === 'string') + paths = JSON.parse(paths) + + drawSignature(paths, canvasContext, true) + + if ($(settings.output, context).length > 0) + $(settings.output, context).val(JSON.stringify(output)) + } + + /** + * Clears the canvas + * Redraws the background colour and the signature line + */ + , clearCanvas: function () { clearCanvas() } + + /** + * Returns the signature as a Js array + * + * @return {Array} + */ + , getSignature: function () { return output } + + /** + * Returns the signature as a Json string + * + * @return {String} + */ + , getSignatureString: function () { return JSON.stringify(output) } + + /** + * Returns the signature as an image + * Re-draws the signature in a shadow canvas to create a clean version + * + * @return {String} + */ + , getSignatureImage: function () { + var tmpCanvas = document.createElement('canvas') + , tmpContext = null + , data = null + + tmpCanvas.style.position = 'absolute' + tmpCanvas.style.top = '-999em' + tmpCanvas.width = element.width + tmpCanvas.height = element.height + document.body.appendChild(tmpCanvas) + + if (!tmpCanvas.getContext && FlashCanvas) + FlashCanvas.initElement(tmpCanvas) + + tmpContext = tmpCanvas.getContext('2d') + + tmpContext.fillStyle = settings.bgColour + tmpContext.fillRect(0, 0, element.width, element.height) + tmpContext.lineWidth = settings.penWidth + tmpContext.strokeStyle = settings.penColour + + drawSignature(output, tmpContext) + data = tmpCanvas.toDataURL.apply(tmpCanvas, arguments) + + document.body.removeChild(tmpCanvas) + tmpCanvas = null + + return data + } + }) +} + +/** + * Create the plugin + * Returns an Api which can be used to call specific methods + * + * @param {Object} options The options array + * + * @return {Object} The Api for controlling the instance + */ +$.fn.signaturePad = function (options) { + var api = null + + this.each(function () { + api = new SignaturePad(this, options) + api.init() + }) + + return api +} + +/** + * Expose the defaults so they can be overwritten for multiple instances + * + * @type {Object} + */ +$.fn.signaturePad.defaults = { + defaultAction: 'typeIt' // What action should be highlighted first: typeIt or drawIt + , displayOnly: false // Initialize canvas for signature display only; ignore buttons and inputs + , drawOnly: false // Whether the to allow a typed signature or not + , canvas: 'canvas' // Selector for selecting the canvas element + , sig: '.sig' // Parts of the signature form that require Javascript (hidden by default) + , sigNav: '.sigNav' // The TypeIt/DrawIt navigation (hidden by default) + , bgColour: '#ffffff' // The colour fill for the background of the canvas + , penColour: '#145394' // Colour of the drawing ink + , penWidth: 2 // Thickness of the pen + , penCap: 'round' // Determines how the end points of each line are drawn (values: 'butt', 'round', 'square') + , lineColour: '#ccc' // Colour of the signature line + , lineWidth: 2 // Thickness of the signature line + , lineMargin: 5 // Margin on right and left of signature line + , lineTop: 35 // Distance to draw the line from the top + , name: '.name' // The input field for typing a name + , typed: '.typed' // The Html element to accept the printed name + , clear: '.clearButton' // Button for clearing the canvas + , typeIt: '.typeIt a' // Button to trigger name typing actions (current by default) + , drawIt: '.drawIt a' // Button to trigger name drawing actions + , typeItDesc: '.typeItDesc' // The description for TypeIt actions + , drawItDesc: '.drawItDesc' // The description for DrawIt actions (hidden by default) + , output: '.output' // The hidden input field for remembering line coordinates + , currentClass: 'current' // The class used to mark items as being currently active + , validateFields: true // Whether the name, draw fields should be validated + , errorClass: 'error' // The class applied to the new error Html element + , errorMessage: 'Please enter your name' // The error message displayed on invalid submission + , errorMessageDraw: 'Please sign the document' // The error message displayed when drawOnly and no signature is drawn + , onBeforeValidate: null // Pass a callback to be used instead of the built-in function + , onFormError: null // Pass a callback to be used instead of the built-in function +} +}(jQuery)) diff --git a/js/jquery.signaturepad.min.js b/js/jquery.signaturepad.min.js new file mode 100755 index 0000000..db34e19 --- /dev/null +++ b/js/jquery.signaturepad.min.js @@ -0,0 +1,28 @@ +/* + SignaturePad: A jQuery plugin for assisting in the creation of an HTML5 canvas + based signature pad. Records the drawn signature in JSON for later regeneration. + + Dependencies: FlashCanvas/1.5, json2.js, jQuery/1.3.2+ + + @project ca.thomasjbradley.applications.signaturepad + @author Thomas J Bradley + @link http://thomasjbradley.ca/lab/signature-pad + @link http://github.com/thomasjbradley/signature-pad + @copyright Copyright MMXI, Thomas J Bradley + @license New BSD License + @version 2.2.0 +*/ +(function(c){function C(n,q){function s(a,e){var f=c(a.target).offset(),g;clearTimeout(o);o=false;if(typeof a.changedTouches!=="undefined"){g=Math.floor(a.changedTouches[0].pageX-f.left);f=Math.floor(a.changedTouches[0].pageY-f.top)}else{g=Math.floor(a.pageX-f.left);f=Math.floor(a.pageY-f.top)}if(j.x===g&&j.y===f)return true;if(j.x===null)j.x=g;if(j.y===null)j.y=f;if(e)f+=e;h.beginPath();h.moveTo(j.x,j.y);h.lineTo(g,f);h.lineCap=b.penCap;h.stroke();h.closePath();l.push({lx:g,ly:f,mx:j.x,my:j.y}); +j.x=g;j.y=f}function m(){r?i.each(function(){this.ontouchmove=null}):i.unbind("mousemove.signaturepad");j.x=null;j.y=null;l.length>0&&c(b.output,d).val(JSON.stringify(l))}function t(){m();h.clearRect(0,0,k.width,k.height);h.fillStyle=b.bgColour;h.fillRect(0,0,k.width,k.height);if(!b.displayOnly)if(b.lineWidth){h.beginPath();h.lineWidth=b.lineWidth;h.strokeStyle=b.lineColour;h.moveTo(b.lineMargin,b.lineTop);h.lineTo(k.width-b.lineMargin,b.lineTop);h.stroke();h.closePath()}h.lineWidth=b.penWidth;h.strokeStyle= +b.penColour;c(b.output,d).val("");l=[]}function w(a){r?i.each(function(){this.addEventListener("touchmove",s,false)}):i.bind("mousemove.signaturepad",s);s(a,1)}function D(){u=false;if(r)i.each(function(){this.removeEventListener("touchstart",m);this.removeEventListener("touchend",m);this.removeEventListener("touchmove",s)});else{i.unbind("mousedown.signaturepad");i.unbind("mouseup.signaturepad");i.unbind("mousemove.signaturepad");i.unbind("mouseleave.signaturepad")}c(b.clear,d).unbind("click.signaturepad")} +function x(a){if(u)return false;u=true;if(typeof a.changedTouches!=="undefined")r=true;if(r){i.each(function(){this.addEventListener("touchend",m,false);this.addEventListener("touchcancel",m,false)});i.unbind("mousedown.signaturepad")}else{i.bind("mouseup.signaturepad",function(){m()});i.bind("mouseleave.signaturepad",function(){o||(o=setTimeout(function(){m();clearTimeout(o);o=false},500))});i.each(function(){this.ontouchstart=null})}}function v(){c(b.typed,d).hide();t();i.each(function(){this.ontouchstart= +function(a){a.preventDefault();x(a);w(a,this)}});i.bind("mousedown.signaturepad",function(a){x(a);w(a,this)});c(b.clear,d).bind("click.signaturepad",function(a){a.preventDefault();t()});c(b.typeIt,d).bind("click.signaturepad",function(a){a.preventDefault();y()});c(b.drawIt,d).unbind("click.signaturepad");c(b.drawIt,d).bind("click.signaturepad",function(a){a.preventDefault()});c(b.typeIt,d).removeClass(b.currentClass);c(b.drawIt,d).addClass(b.currentClass);c(b.sig,d).addClass(b.currentClass);c(b.typeItDesc, +d).hide();c(b.drawItDesc,d).show();c(b.clear,d).show()}function y(){t();D();c(b.typed,d).show();c(b.drawIt,d).bind("click.signaturepad",function(a){a.preventDefault();v()});c(b.typeIt,d).unbind("click.signaturepad");c(b.typeIt,d).bind("click.signaturepad",function(a){a.preventDefault()});c(b.output,d).val("");c(b.drawIt,d).removeClass(b.currentClass);c(b.typeIt,d).addClass(b.currentClass);c(b.sig,d).removeClass(b.currentClass);c(b.drawItDesc,d).hide();c(b.clear,d).hide();c(b.typeItDesc,d).show()} +function z(a){for(c(b.typed,d).html(a.replace(/>/g,">").replace(/k.width;){a=c(b.typed,d).css("font-size").replace(/px/,"");c(b.typed,d).css("font-size",a-1+"px")}}function E(a,e){c("p."+e.errorClass,a).remove();a.removeClass(e.errorClass);c("input, label",a).removeClass(e.errorClass)}function F(a,e,f){if(a.nameInvalid){e.prepend(['

',f.errorMessage,"

"].join(""));c(f.name,e).focus();c(f.name,e).addClass(f.errorClass);c("label[for="+ +c(f.name).attr("id")+"]",e).addClass(f.errorClass)}a.drawInvalid&&e.prepend(['

',f.errorMessageDraw,"

"].join(""))}function A(){var a=true,e={drawInvalid:false,nameInvalid:false},f=[d,b],g=[e,d,b];b.onBeforeValidate&&typeof b.onBeforeValidate==="function"?b.onBeforeValidate.apply(p,f):E.apply(p,f);if(b.drawOnly&&l.length<1){e.drawInvalid=true;a=false}if(c(b.name,d).val()===""){e.nameInvalid=true;a=false}b.onFormError&&typeof b.onFormError==="function"?b.onFormError.apply(p, +g):F.apply(p,g);return a}function B(a,e,f){for(var g in a)if(typeof a[g]==="object"){e.beginPath();e.moveTo(a[g].mx,a[g].my);e.lineTo(a[g].lx,a[g].ly);e.lineCap=b.penCap;e.stroke();e.closePath();f&&l.push({lx:a[g].lx,ly:a[g].ly,mx:a[g].mx,my:a[g].my})}}function G(){if(parseFloat((/CPU.+OS ([0-9_]{3}).*AppleWebkit.*Mobile/i.exec(navigator.userAgent)||[0,"4_2"])[1].replace("_","."))<4.1){c.fn.Oldoffset=c.fn.offset;c.fn.offset=function(){var a=c(this).Oldoffset();a.top-=window.scrollY;a.left-=window.scrollX; +return a}}c(b.typed,d).bind("selectstart.signaturepad",function(a){return c(a.target).is(":input")});i.bind("selectstart.signaturepad",function(a){return c(a.target).is(":input")});!k.getContext&&FlashCanvas&&FlashCanvas.initElement(k);if(k.getContext){h=k.getContext("2d");c(b.sig,d).show();if(!b.displayOnly){if(!b.drawOnly){c(b.name,d).bind("keyup.signaturepad",function(){z(c(this).val())});c(b.name,d).bind("blur.signaturepad",function(){z(c(this).val())});c(b.drawIt,d).bind("click.signaturepad", +function(a){a.preventDefault();v()})}b.drawOnly||b.defaultAction==="drawIt"?v():y();if(b.validateFields)c(n).is("form")?c(n).bind("submit.signaturepad",function(){return A()}):c(n).parents("form").bind("submit.signaturepad",function(){return A()});c(b.sigNav,d).show()}}}var p=this,b=c.extend({},c.fn.signaturePad.defaults,q),d=c(n),i=c(b.canvas,d),k=i.get(0),h=null,j={x:null,y:null},l=[],o=false,r=false,u=false;c.extend(p,{init:function(){G()},regenerate:function(a){p.clearCanvas();c(b.typed,d).hide(); +if(typeof a==="string")a=JSON.parse(a);B(a,h,true);c(b.output,d).length>0&&c(b.output,d).val(JSON.stringify(l))},clearCanvas:function(){t()},getSignature:function(){return l},getSignatureString:function(){return JSON.stringify(l)},getSignatureImage:function(){var a=document.createElement("canvas"),e=null;e=null;a.style.position="absolute";a.style.top="-999em";a.width=k.width;a.height=k.height;document.body.appendChild(a);!a.getContext&&FlashCanvas&&FlashCanvas.initElement(a);e=a.getContext("2d"); +e.fillStyle=b.bgColour;e.fillRect(0,0,k.width,k.height);e.lineWidth=b.penWidth;e.strokeStyle=b.penColour;B(l,e);e=a.toDataURL.apply(a,arguments);document.body.removeChild(a);return e}})}c.fn.signaturePad=function(n){var q=null;this.each(function(){q=new C(this,n);q.init()});return q};c.fn.signaturePad.defaults={defaultAction:"typeIt",displayOnly:false,drawOnly:false,canvas:"canvas",sig:".sig",sigNav:".sigNav",bgColour:"#ffffff",penColour:"#145394",penWidth:2,penCap:"round",lineColour:"#ccc",lineWidth:2, +lineMargin:5,lineTop:35,name:".name",typed:".typed",clear:".clearButton",typeIt:".typeIt a",drawIt:".drawIt a",typeItDesc:".typeItDesc",drawItDesc:".drawItDesc",output:".output",currentClass:"current",validateFields:true,errorClass:"error",errorMessage:"Please enter your name",errorMessageDraw:"Please sign the document",onBeforeValidate:null,onFormError:null}})(jQuery); diff --git a/js/json2.min.js b/js/json2.min.js new file mode 100755 index 0000000..7672430 --- /dev/null +++ b/js/json2.min.js @@ -0,0 +1,28 @@ +if(!this.JSON){this.JSON={};} +(function(){function f(n){return n<10?'0'+n:n;} +if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+ +f(this.getUTCMonth()+1)+'-'+ +f(this.getUTCDate())+'T'+ +f(this.getUTCHours())+':'+ +f(this.getUTCMinutes())+':'+ +f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};} +var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';} +function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);} +if(typeof rep==='function'){value=rep.call(holder,key,value);} +switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';} +gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i