signature-pad-rails 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,891 @@
1
+ /**
2
+ * Usage for accepting signatures:
3
+ * $('.sigPad').signaturePad()
4
+ *
5
+ * Usage for displaying previous signatures:
6
+ * $('.sigPad').signaturePad({displayOnly:true}).regenerate(sig)
7
+ * or
8
+ * var api = $('.sigPad').signaturePad({displayOnly:true})
9
+ * api.regenerate(sig)
10
+ */
11
+ (function ($) {
12
+
13
+ function SignaturePad (selector, options) {
14
+ /**
15
+ * Reference to the object for use in public methods
16
+ *
17
+ * @private
18
+ *
19
+ * @type {Object}
20
+ */
21
+ var self = this
22
+
23
+ /**
24
+ * Holds the merged default settings and user passed settings
25
+ *
26
+ * @private
27
+ *
28
+ * @type {Object}
29
+ */
30
+ , settings = $.extend({}, $.fn.signaturePad.defaults, options)
31
+
32
+ /**
33
+ * The current context, as passed by jQuery, of selected items
34
+ *
35
+ * @private
36
+ *
37
+ * @type {Object}
38
+ */
39
+ , context = $(selector)
40
+
41
+ /**
42
+ * jQuery reference to the canvas element inside the signature pad
43
+ *
44
+ * @private
45
+ *
46
+ * @type {Object}
47
+ */
48
+ , canvas = $(settings.canvas, context)
49
+
50
+ /**
51
+ * Dom reference to the canvas element inside the signature pad
52
+ *
53
+ * @private
54
+ *
55
+ * @type {Object}
56
+ */
57
+ , element = canvas.get(0)
58
+
59
+ /**
60
+ * The drawing context for the signature canvas
61
+ *
62
+ * @private
63
+ *
64
+ * @type {Object}
65
+ */
66
+ , canvasContext = null
67
+
68
+ /**
69
+ * Holds the previous point of drawing
70
+ * Disallows drawing over the same location to make lines more delicate
71
+ *
72
+ * @private
73
+ *
74
+ * @type {Object}
75
+ */
76
+ , previous = {'x': null, 'y': null}
77
+
78
+ /**
79
+ * An array holding all the points and lines to generate the signature
80
+ * Each item is an object like:
81
+ * {
82
+ * mx: moveTo x coordinate
83
+ * my: moveTo y coordinate
84
+ * lx: lineTo x coordinate
85
+ * lx: lineTo y coordinate
86
+ * }
87
+ *
88
+ * @private
89
+ *
90
+ * @type {Array}
91
+ */
92
+ , output = []
93
+
94
+ /**
95
+ * Stores a timeout for when the mouse leaves the canvas
96
+ * If the mouse has left the canvas for a specific amount of time
97
+ * Stops drawing on the canvas
98
+ *
99
+ * @private
100
+ *
101
+ * @type {Object}
102
+ */
103
+ , mouseLeaveTimeout = false
104
+
105
+ /**
106
+ * Whether the mouse button is currently pressed down or not
107
+ *
108
+ * @private
109
+ *
110
+ * @type {Boolean}
111
+ */
112
+ , mouseButtonDown = false
113
+
114
+ /**
115
+ * Whether the browser is a touch event browser or not
116
+ *
117
+ * @private
118
+ *
119
+ * @type {Boolean}
120
+ */
121
+ , touchable = false
122
+
123
+ /**
124
+ * Whether events have already been bound to the canvas or not
125
+ *
126
+ * @private
127
+ *
128
+ * @type {Boolean}
129
+ */
130
+ , eventsBound = false
131
+
132
+ /**
133
+ * Remembers the default font-size when typing, and will allow it to be scaled for bigger/smaller names
134
+ *
135
+ * @private
136
+ *
137
+ * @type {Number}
138
+ */
139
+ , typeItDefaultFontSize = 30
140
+
141
+ /**
142
+ * Remembers the current font-size when typing
143
+ *
144
+ * @private
145
+ *
146
+ * @type {Number}
147
+ */
148
+ , typeItCurrentFontSize = typeItDefaultFontSize
149
+
150
+ /**
151
+ * Remembers how many characters are in the name field, to help with the scaling feature
152
+ *
153
+ * @private
154
+ *
155
+ * @type {Number}
156
+ */
157
+ , typeItNumChars = 0
158
+
159
+
160
+ /**
161
+ * Clears the mouseLeaveTimeout
162
+ * Resets some other variables that may be active
163
+ *
164
+ * @private
165
+ */
166
+ function clearMouseLeaveTimeout () {
167
+ clearTimeout(mouseLeaveTimeout)
168
+ mouseLeaveTimeout = false
169
+ mouseButtonDown = false
170
+ }
171
+
172
+ /**
173
+ * Draws a line on canvas using the mouse position
174
+ * Checks previous position to not draw over top of previous drawing
175
+ * (makes the line really thick and poorly anti-aliased)
176
+ *
177
+ * @private
178
+ *
179
+ * @param {Object} e The event object
180
+ * @param {Number} newYOffset A pixel value for drawing the newY, used for drawing a single dot on click
181
+ */
182
+ function drawLine (e, newYOffset) {
183
+ var offset, newX, newY
184
+
185
+ e.preventDefault()
186
+
187
+ offset = $(e.target).offset()
188
+
189
+ clearTimeout(mouseLeaveTimeout)
190
+ mouseLeaveTimeout = false
191
+
192
+ if (typeof e.targetTouches !== 'undefined') {
193
+ newX = Math.floor(e.targetTouches[0].pageX - offset.left)
194
+ newY = Math.floor(e.targetTouches[0].pageY - offset.top)
195
+ } else {
196
+ newX = Math.floor(e.pageX - offset.left)
197
+ newY = Math.floor(e.pageY - offset.top)
198
+ }
199
+
200
+ if (previous.x === newX && previous.y === newY)
201
+ return true
202
+
203
+ if (previous.x === null)
204
+ previous.x = newX
205
+
206
+ if (previous.y === null)
207
+ previous.y = newY
208
+
209
+ if (newYOffset)
210
+ newY += newYOffset
211
+
212
+ canvasContext.beginPath()
213
+ canvasContext.moveTo(previous.x, previous.y)
214
+ canvasContext.lineTo(newX, newY)
215
+ canvasContext.lineCap = settings.penCap
216
+ canvasContext.stroke()
217
+ canvasContext.closePath()
218
+
219
+ output.push({
220
+ 'lx' : newX
221
+ , 'ly' : newY
222
+ , 'mx' : previous.x
223
+ , 'my' : previous.y
224
+ })
225
+
226
+ previous.x = newX
227
+ previous.y = newY
228
+
229
+ if (settings.onDraw && typeof settings.onDraw === 'function')
230
+ settings.onDraw.apply(self)
231
+ }
232
+
233
+ /**
234
+ * Callback wrapper for executing stopDrawing without the event
235
+ * Put up here so that it can be removed at a later time
236
+ *
237
+ * @private
238
+ */
239
+ function stopDrawingWrapper () {
240
+ stopDrawing()
241
+ }
242
+
243
+ /**
244
+ * Callback registered to mouse/touch events of the canvas
245
+ * Stops the drawing abilities
246
+ *
247
+ * @private
248
+ *
249
+ * @param {Object} e The event object
250
+ */
251
+ function stopDrawing (e) {
252
+ if (!!e) {
253
+ drawLine(e, 1)
254
+ } else {
255
+ if (touchable) {
256
+ canvas.each(function () {
257
+ this.removeEventListener('touchmove', drawLine)
258
+ // this.removeEventListener('MSPointerMove', drawLine)
259
+ })
260
+ } else {
261
+ canvas.unbind('mousemove.signaturepad')
262
+ }
263
+
264
+ if (output.length > 0 && settings.onDrawEnd && typeof settings.onDrawEnd === 'function')
265
+ settings.onDrawEnd.apply(self)
266
+ }
267
+
268
+ previous.x = null
269
+ previous.y = null
270
+
271
+ if (settings.output && output.length > 0)
272
+ $(settings.output, context).val(JSON.stringify(output))
273
+ }
274
+
275
+ /**
276
+ * Draws the signature line
277
+ *
278
+ * @private
279
+ */
280
+ function drawSigLine () {
281
+ if (!settings.lineWidth)
282
+ return false
283
+
284
+ canvasContext.beginPath()
285
+ canvasContext.lineWidth = settings.lineWidth
286
+ canvasContext.strokeStyle = settings.lineColour
287
+ canvasContext.moveTo(settings.lineMargin, settings.lineTop)
288
+ canvasContext.lineTo(element.width - settings.lineMargin, settings.lineTop)
289
+ canvasContext.stroke()
290
+ canvasContext.closePath()
291
+ }
292
+
293
+ /**
294
+ * Clears all drawings off the canvas and redraws the signature line
295
+ *
296
+ * @private
297
+ */
298
+ function clearCanvas () {
299
+ canvasContext.clearRect(0, 0, element.width, element.height)
300
+ canvasContext.fillStyle = settings.bgColour
301
+ canvasContext.fillRect(0, 0, element.width, element.height)
302
+
303
+ if (!settings.displayOnly)
304
+ drawSigLine()
305
+
306
+ canvasContext.lineWidth = settings.penWidth
307
+ canvasContext.strokeStyle = settings.penColour
308
+
309
+ $(settings.output, context).val('')
310
+ output = []
311
+
312
+ stopDrawing()
313
+ }
314
+
315
+ /**
316
+ * Callback registered to mouse/touch events of the canvas
317
+ * Draws a line at the mouse cursor location, starting a new line if necessary
318
+ *
319
+ * @private
320
+ *
321
+ * @param {Object} e The event object
322
+ * @param {Object} o The object context registered to the event; canvas
323
+ */
324
+ function onMouseMove(e, o) {
325
+ if (previous.x == null) {
326
+ drawLine(e, 1)
327
+ } else {
328
+ drawLine(e, o)
329
+ }
330
+ }
331
+
332
+ /**
333
+ * Callback registered to mouse/touch events of canvas
334
+ * Triggers the drawLine function
335
+ *
336
+ * @private
337
+ *
338
+ * @param {Object} e The event object
339
+ * @param {Object} touchObject The object context registered to the event; canvas
340
+ */
341
+ function startDrawing (e, touchObject) {
342
+ if (touchable) {
343
+ touchObject.addEventListener('touchmove', onMouseMove, false)
344
+ // touchObject.addEventListener('MSPointerMove', onMouseMove, false)
345
+ } else {
346
+ canvas.bind('mousemove.signaturepad', onMouseMove)
347
+ }
348
+
349
+ // Draws a single point on initial mouse down, for people with periods in their name
350
+ drawLine(e, 1)
351
+ }
352
+
353
+ /**
354
+ * Removes all the mouse events from the canvas
355
+ *
356
+ * @private
357
+ */
358
+ function disableCanvas () {
359
+ eventsBound = false
360
+
361
+ canvas.each(function () {
362
+ if (this.removeEventListener) {
363
+ this.removeEventListener('touchend', stopDrawingWrapper)
364
+ this.removeEventListener('touchcancel', stopDrawingWrapper)
365
+ this.removeEventListener('touchmove', drawLine)
366
+ // this.removeEventListener('MSPointerUp', stopDrawingWrapper)
367
+ // this.removeEventListener('MSPointerCancel', stopDrawingWrapper)
368
+ // this.removeEventListener('MSPointerMove', drawLine)
369
+ }
370
+
371
+ if (this.ontouchstart)
372
+ this.ontouchstart = null;
373
+ })
374
+
375
+ $(document).unbind('mouseup.signaturepad')
376
+ canvas.unbind('mousedown.signaturepad')
377
+ canvas.unbind('mousemove.signaturepad')
378
+ canvas.unbind('mouseleave.signaturepad')
379
+
380
+ $(settings.clear, context).unbind('click.signaturepad')
381
+ }
382
+
383
+ /**
384
+ * Lazy touch event detection
385
+ * Uses the first press on the canvas to detect either touch or mouse reliably
386
+ * Will then bind other events as needed
387
+ *
388
+ * @private
389
+ *
390
+ * @param {Object} e The event object
391
+ */
392
+ function initDrawEvents (e) {
393
+ if (eventsBound)
394
+ return false
395
+
396
+ eventsBound = true
397
+
398
+ // Closes open keyboards to free up space
399
+ $('input').blur();
400
+
401
+ if (typeof e.targetTouches !== 'undefined')
402
+ touchable = true
403
+
404
+ if (touchable) {
405
+ canvas.each(function () {
406
+ this.addEventListener('touchend', stopDrawingWrapper, false)
407
+ this.addEventListener('touchcancel', stopDrawingWrapper, false)
408
+ // this.addEventListener('MSPointerUp', stopDrawingWrapper, false)
409
+ // this.addEventListener('MSPointerCancel', stopDrawingWrapper, false)
410
+ })
411
+
412
+ canvas.unbind('mousedown.signaturepad')
413
+ } else {
414
+ $(document).bind('mouseup.signaturepad', function () {
415
+ if (mouseButtonDown) {
416
+ stopDrawing()
417
+ clearMouseLeaveTimeout()
418
+ }
419
+ })
420
+ canvas.bind('mouseleave.signaturepad', function (e) {
421
+ if (mouseButtonDown) stopDrawing(e)
422
+
423
+ if (mouseButtonDown && !mouseLeaveTimeout) {
424
+ mouseLeaveTimeout = setTimeout(function () {
425
+ stopDrawing()
426
+ clearMouseLeaveTimeout()
427
+ }, 500)
428
+ }
429
+ })
430
+
431
+ canvas.each(function () {
432
+ this.ontouchstart = null
433
+ })
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Triggers the abilities to draw on the canvas
439
+ * Sets up mouse/touch events, hides and shows descriptions and sets current classes
440
+ *
441
+ * @private
442
+ */
443
+ function drawIt () {
444
+ $(settings.typed, context).hide()
445
+ clearCanvas()
446
+
447
+ canvas.each(function () {
448
+ this.ontouchstart = function (e) {
449
+ e.preventDefault()
450
+ mouseButtonDown = true
451
+ initDrawEvents(e)
452
+ startDrawing(e, this)
453
+ }
454
+ })
455
+
456
+ canvas.bind('mousedown.signaturepad', function (e) {
457
+ e.preventDefault()
458
+
459
+ // Only allow left mouse clicks to trigger signature drawing
460
+ if (e.which > 1) return false
461
+
462
+ mouseButtonDown = true
463
+ initDrawEvents(e)
464
+ startDrawing(e)
465
+ })
466
+
467
+ $(settings.clear, context).bind('click.signaturepad', function (e) { e.preventDefault(); clearCanvas() })
468
+
469
+ $(settings.typeIt, context).bind('click.signaturepad', function (e) { e.preventDefault(); typeIt() })
470
+ $(settings.drawIt, context).unbind('click.signaturepad')
471
+ $(settings.drawIt, context).bind('click.signaturepad', function (e) { e.preventDefault() })
472
+
473
+ $(settings.typeIt, context).removeClass(settings.currentClass)
474
+ $(settings.drawIt, context).addClass(settings.currentClass)
475
+ $(settings.sig, context).addClass(settings.currentClass)
476
+
477
+ $(settings.typeItDesc, context).hide()
478
+ $(settings.drawItDesc, context).show()
479
+ $(settings.clear, context).show()
480
+ }
481
+
482
+ /**
483
+ * Triggers the abilities to type in the input for generating a signature
484
+ * Sets up mouse events, hides and shows descriptions and sets current classes
485
+ *
486
+ * @private
487
+ */
488
+ function typeIt () {
489
+ clearCanvas()
490
+ disableCanvas()
491
+ $(settings.typed, context).show()
492
+
493
+ $(settings.drawIt, context).bind('click.signaturepad', function (e) { e.preventDefault(); drawIt() })
494
+ $(settings.typeIt, context).unbind('click.signaturepad')
495
+ $(settings.typeIt, context).bind('click.signaturepad', function (e) { e.preventDefault() })
496
+
497
+ $(settings.output, context).val('')
498
+
499
+ $(settings.drawIt, context).removeClass(settings.currentClass)
500
+ $(settings.typeIt, context).addClass(settings.currentClass)
501
+ $(settings.sig, context).removeClass(settings.currentClass)
502
+
503
+ $(settings.drawItDesc, context).hide()
504
+ $(settings.clear, context).hide()
505
+ $(settings.typeItDesc, context).show()
506
+
507
+ typeItCurrentFontSize = typeItDefaultFontSize = $(settings.typed, context).css('font-size').replace(/px/, '')
508
+ }
509
+
510
+ /**
511
+ * Callback registered on key up and blur events for input field
512
+ * Writes the text fields value as Html into an element
513
+ *
514
+ * @private
515
+ *
516
+ * @param {String} val The value of the input field
517
+ */
518
+ function type (val) {
519
+ var typed = $(settings.typed, context)
520
+ , cleanedVal = $.trim(val.replace(/>/g, '&gt;').replace(/</g, '&lt;'))
521
+ , oldLength = typeItNumChars
522
+ , edgeOffset = typeItCurrentFontSize * 0.5
523
+
524
+ typeItNumChars = cleanedVal.length
525
+ typed.html(cleanedVal)
526
+
527
+ if (!cleanedVal) {
528
+ typed.css('font-size', typeItDefaultFontSize + 'px')
529
+ return
530
+ }
531
+
532
+ if (typeItNumChars > oldLength && typed.outerWidth() > element.width) {
533
+ while (typed.outerWidth() > element.width) {
534
+ typeItCurrentFontSize--
535
+ typed.css('font-size', typeItCurrentFontSize + 'px')
536
+ }
537
+ }
538
+
539
+ if (typeItNumChars < oldLength && typed.outerWidth() + edgeOffset < element.width && typeItCurrentFontSize < typeItDefaultFontSize) {
540
+ while (typed.outerWidth() + edgeOffset < element.width && typeItCurrentFontSize < typeItDefaultFontSize) {
541
+ typeItCurrentFontSize++
542
+ typed.css('font-size', typeItCurrentFontSize + 'px')
543
+ }
544
+ }
545
+ }
546
+
547
+ /**
548
+ * Default onBeforeValidate function to clear errors
549
+ *
550
+ * @private
551
+ *
552
+ * @param {Object} context current context object
553
+ * @param {Object} settings provided settings
554
+ */
555
+ function onBeforeValidate (context, settings) {
556
+ $('p.' + settings.errorClass, context).remove()
557
+ context.removeClass(settings.errorClass)
558
+ $('input, label', context).removeClass(settings.errorClass)
559
+ }
560
+
561
+ /**
562
+ * Default onFormError function to show errors
563
+ *
564
+ * @private
565
+ *
566
+ * @param {Object} errors object contains validation errors (e.g. nameInvalid=true)
567
+ * @param {Object} context current context object
568
+ * @param {Object} settings provided settings
569
+ */
570
+ function onFormError (errors, context, settings) {
571
+ if (errors.nameInvalid) {
572
+ context.prepend(['<p class="', settings.errorClass, '">', settings.errorMessage, '</p>'].join(''))
573
+ $(settings.name, context).focus()
574
+ $(settings.name, context).addClass(settings.errorClass)
575
+ $('label[for=' + $(settings.name).attr('id') + ']', context).addClass(settings.errorClass)
576
+ }
577
+
578
+ if (errors.drawInvalid)
579
+ context.prepend(['<p class="', settings.errorClass, '">', settings.errorMessageDraw, '</p>'].join(''))
580
+ }
581
+
582
+ /**
583
+ * Validates the form to confirm a name was typed in the field
584
+ * If drawOnly also confirms that the user drew a signature
585
+ *
586
+ * @private
587
+ *
588
+ * @return {Boolean}
589
+ */
590
+ function validateForm () {
591
+ var valid = true
592
+ , errors = {drawInvalid: false, nameInvalid: false}
593
+ , onBeforeArguments = [context, settings]
594
+ , onErrorArguments = [errors, context, settings]
595
+
596
+ if (settings.onBeforeValidate && typeof settings.onBeforeValidate === 'function') {
597
+ settings.onBeforeValidate.apply(self,onBeforeArguments)
598
+ } else {
599
+ onBeforeValidate.apply(self, onBeforeArguments)
600
+ }
601
+
602
+ if (settings.drawOnly && output.length < 1) {
603
+ errors.drawInvalid = true
604
+ valid = false
605
+ }
606
+
607
+ if ($(settings.name, context).val() === '') {
608
+ errors.nameInvalid = true
609
+ valid = false
610
+ }
611
+
612
+ if (settings.onFormError && typeof settings.onFormError === 'function') {
613
+ settings.onFormError.apply(self,onErrorArguments)
614
+ } else {
615
+ onFormError.apply(self, onErrorArguments)
616
+ }
617
+
618
+ return valid
619
+ }
620
+
621
+ /**
622
+ * Redraws the signature on a specific canvas
623
+ *
624
+ * @private
625
+ *
626
+ * @param {Array} paths the signature JSON
627
+ * @param {Object} context the canvas context to draw on
628
+ * @param {Boolean} saveOutput whether to write the path to the output array or not
629
+ */
630
+ function drawSignature (paths, context, saveOutput) {
631
+ for(var i in paths) {
632
+ if (typeof paths[i] === 'object') {
633
+ context.beginPath()
634
+ context.moveTo(paths[i].mx, paths[i].my)
635
+ context.lineTo(paths[i].lx, paths[i].ly)
636
+ context.lineCap = settings.penCap
637
+ context.stroke()
638
+ context.closePath()
639
+
640
+ if (saveOutput) {
641
+ output.push({
642
+ 'lx' : paths[i].lx
643
+ , 'ly' : paths[i].ly
644
+ , 'mx' : paths[i].mx
645
+ , 'my' : paths[i].my
646
+ })
647
+ }
648
+ }
649
+ }
650
+ }
651
+
652
+ /**
653
+ * Initialisation function, called immediately after all declarations
654
+ * Technically public, but only should be used internally
655
+ *
656
+ * @private
657
+ */
658
+ function init () {
659
+ // Fixes the jQuery.fn.offset() function for Mobile Safari Browsers i.e. iPod Touch, iPad and iPhone
660
+ // https://gist.github.com/661844
661
+ // http://bugs.jquery.com/ticket/6446
662
+ if (parseFloat(((/CPU.+OS ([0-9_]{3}).*AppleWebkit.*Mobile/i.exec(navigator.userAgent)) || [0,'4_2'])[1].replace('_','.')) < 4.1) {
663
+ $.fn.Oldoffset = $.fn.offset;
664
+ $.fn.offset = function () {
665
+ var result = $(this).Oldoffset()
666
+ result.top -= window.scrollY
667
+ result.left -= window.scrollX
668
+
669
+ return result
670
+ }
671
+ }
672
+
673
+ // Disable selection on the typed div and canvas
674
+ $(settings.typed, context).bind('selectstart.signaturepad', function (e) { return $(e.target).is(':input') })
675
+ canvas.bind('selectstart.signaturepad', function (e) { return $(e.target).is(':input') })
676
+
677
+ if (!element.getContext && FlashCanvas)
678
+ FlashCanvas.initElement(element)
679
+
680
+ if (element.getContext) {
681
+ canvasContext = element.getContext('2d')
682
+
683
+ $(settings.sig, context).show()
684
+
685
+ if (!settings.displayOnly) {
686
+ if (!settings.drawOnly) {
687
+ $(settings.name, context).bind('keyup.signaturepad', function () {
688
+ type($(this).val())
689
+ })
690
+
691
+ $(settings.name, context).bind('blur.signaturepad', function () {
692
+ type($(this).val())
693
+ })
694
+
695
+ $(settings.drawIt, context).bind('click.signaturepad', function (e) {
696
+ e.preventDefault()
697
+ drawIt()
698
+ })
699
+ }
700
+
701
+ if (settings.drawOnly || settings.defaultAction === 'drawIt') {
702
+ drawIt()
703
+ } else {
704
+ typeIt()
705
+ }
706
+
707
+ if (settings.validateFields) {
708
+ if ($(selector).is('form')) {
709
+ $(selector).bind('submit.signaturepad', function () { return validateForm() })
710
+ } else {
711
+ $(selector).parents('form').bind('submit.signaturepad', function () { return validateForm() })
712
+ }
713
+ }
714
+
715
+ $(settings.sigNav, context).show()
716
+ }
717
+ }
718
+ }
719
+
720
+ $.extend(self, {
721
+ /**
722
+ * A property to store the current version of Signature Pad
723
+ */
724
+ signaturePad : '{{version}}'
725
+
726
+ /**
727
+ * Initializes SignaturePad
728
+ */
729
+ , init : function () { init() }
730
+
731
+ /**
732
+ * Allows options to be updated after initialization
733
+ *
734
+ * @param {Object} options An object containing the options to be changed
735
+ */
736
+ , updateOptions : function (options) {
737
+ $.extend(settings, options)
738
+ }
739
+
740
+ /**
741
+ * Regenerates a signature on the canvas using an array of objects
742
+ * Follows same format as object property
743
+ * @see var object
744
+ *
745
+ * @param {Array} paths An array of the lines and points
746
+ */
747
+ , regenerate : function (paths) {
748
+ self.clearCanvas()
749
+ $(settings.typed, context).hide()
750
+
751
+ if (typeof paths === 'string')
752
+ paths = JSON.parse(paths)
753
+
754
+ drawSignature(paths, canvasContext, true)
755
+
756
+ if (settings.output && $(settings.output, context).length > 0)
757
+ $(settings.output, context).val(JSON.stringify(output))
758
+ }
759
+
760
+ /**
761
+ * Clears the canvas
762
+ * Redraws the background colour and the signature line
763
+ */
764
+ , clearCanvas : function () { clearCanvas() }
765
+
766
+ /**
767
+ * Returns the signature as a Js array
768
+ *
769
+ * @return {Array}
770
+ */
771
+ , getSignature : function () { return output }
772
+
773
+ /**
774
+ * Returns the signature as a Json string
775
+ *
776
+ * @return {String}
777
+ */
778
+ , getSignatureString : function () { return JSON.stringify(output) }
779
+
780
+ /**
781
+ * Returns the signature as an image
782
+ * Re-draws the signature in a shadow canvas to create a clean version
783
+ *
784
+ * @return {String}
785
+ */
786
+ , getSignatureImage : function () {
787
+ var tmpCanvas = document.createElement('canvas')
788
+ , tmpContext = null
789
+ , data = null
790
+
791
+ tmpCanvas.style.position = 'absolute'
792
+ tmpCanvas.style.top = '-999em'
793
+ tmpCanvas.width = element.width
794
+ tmpCanvas.height = element.height
795
+ document.body.appendChild(tmpCanvas)
796
+
797
+ if (!tmpCanvas.getContext && FlashCanvas)
798
+ FlashCanvas.initElement(tmpCanvas)
799
+
800
+ tmpContext = tmpCanvas.getContext('2d')
801
+
802
+ tmpContext.fillStyle = settings.bgColour
803
+ tmpContext.fillRect(0, 0, element.width, element.height)
804
+ tmpContext.lineWidth = settings.penWidth
805
+ tmpContext.strokeStyle = settings.penColour
806
+
807
+ drawSignature(output, tmpContext)
808
+ data = tmpCanvas.toDataURL.apply(tmpCanvas, arguments)
809
+
810
+ document.body.removeChild(tmpCanvas)
811
+ tmpCanvas = null
812
+
813
+ return data
814
+ }
815
+
816
+ /**
817
+ * The form validation function
818
+ * Validates that the signature has been filled in properly
819
+ * Allows it to be hooked into another validation function and called at a different time
820
+ *
821
+ * @return {Boolean}
822
+ */
823
+ , validateForm : function () { return validateForm() }
824
+ })
825
+ }
826
+
827
+ /**
828
+ * Create the plugin
829
+ * Returns an Api which can be used to call specific methods
830
+ *
831
+ * @param {Object} options The options array
832
+ *
833
+ * @return {Object} The Api for controlling the instance
834
+ */
835
+ $.fn.signaturePad = function (options) {
836
+ var api = null
837
+
838
+ this.each(function () {
839
+ if (!$.data(this, 'plugin-signaturePad')) {
840
+ api = new SignaturePad(this, options)
841
+ api.init()
842
+ $.data(this, 'plugin-signaturePad', api)
843
+ } else {
844
+ api = $.data(this, 'plugin-signaturePad')
845
+ api.updateOptions(options)
846
+ }
847
+ })
848
+
849
+ return api
850
+ }
851
+
852
+ /**
853
+ * Expose the defaults so they can be overwritten for multiple instances
854
+ *
855
+ * @type {Object}
856
+ */
857
+ $.fn.signaturePad.defaults = {
858
+ defaultAction : 'typeIt' // What action should be highlighted first: typeIt or drawIt
859
+ , displayOnly : false // Initialize canvas for signature display only; ignore buttons and inputs
860
+ , drawOnly : false // Whether the to allow a typed signature or not
861
+ , canvas : 'canvas' // Selector for selecting the canvas element
862
+ , sig : '.sig' // Parts of the signature form that require Javascript (hidden by default)
863
+ , sigNav : '.sigNav' // The TypeIt/DrawIt navigation (hidden by default)
864
+ , bgColour : '#ffffff' // The colour fill for the background of the canvas; or transparent
865
+ , penColour : '#145394' // Colour of the drawing ink
866
+ , penWidth : 2 // Thickness of the pen
867
+ , penCap : 'round' // Determines how the end points of each line are drawn (values: 'butt', 'round', 'square')
868
+ , lineColour : '#ccc' // Colour of the signature line
869
+ , lineWidth : 2 // Thickness of the signature line
870
+ , lineMargin : 5 // Margin on right and left of signature line
871
+ , lineTop : 35 // Distance to draw the line from the top
872
+ , name : '.name' // The input field for typing a name
873
+ , typed : '.typed' // The Html element to accept the printed name
874
+ , clear : '.clearButton' // Button for clearing the canvas
875
+ , typeIt : '.typeIt a' // Button to trigger name typing actions (current by default)
876
+ , drawIt : '.drawIt a' // Button to trigger name drawing actions
877
+ , typeItDesc : '.typeItDesc' // The description for TypeIt actions
878
+ , drawItDesc : '.drawItDesc' // The description for DrawIt actions (hidden by default)
879
+ , output : '.output' // The hidden input field for remembering line coordinates
880
+ , currentClass : 'current' // The class used to mark items as being currently active
881
+ , validateFields : true // Whether the name, draw fields should be validated
882
+ , errorClass : 'error' // The class applied to the new error Html element
883
+ , errorMessage : 'Please enter your name' // The error message displayed on invalid submission
884
+ , errorMessageDraw : 'Please sign the document' // The error message displayed when drawOnly and no signature is drawn
885
+ , onBeforeValidate : null // Pass a callback to be used instead of the built-in function
886
+ , onFormError : null // Pass a callback to be used instead of the built-in function
887
+ , onDraw : null // Pass a callback to be used to capture the drawing process
888
+ , onDrawEnd : null // Pass a callback to be exectued after the drawing process
889
+ }
890
+
891
+ }(jQuery))