18 Commits
0.9 ... master

Author SHA1 Message Date
88d6118939 Bump to 0.12 2022-04-27 17:00:23 +02:00
MaxJa4
9d3087e78b Updates jSignature.js to newest version 2022-04-27 16:53:44 +02:00
Sebastien Corbin
4883e37c1a Update badges 2022-01-17 14:18:10 +01:00
Sebastien Corbin
ae58c7f725 Bump version to 0.11 2022-01-17 14:07:09 +01:00
Sebastien Corbin
cf9a37a009 Remove depreciations 2022-01-17 14:02:35 +01:00
Sebastien Corbin
382c824cca Flake8 2022-01-17 14:02:35 +01:00
Sebastien Corbin
63c3d7d2dc Move to github actions 2022-01-17 14:02:35 +01:00
David Sauve
ea3800375c Improved _remove_empty so that it doesn't remove 0, just None 2022-01-17 09:12:03 +01:00
David Sauve
a42a9ca543 Filter out null values in signature data
Null values in the signature data will cause the compute box calculation to fail. Filter them out during json.loads
2022-01-17 09:12:03 +01:00
kygoh
9e81795f99 Fix django tarball url 2021-10-10 18:37:33 +02:00
kygoh
7fe2560910 Use environment markers to install pyquery based on python version 2021-10-10 18:37:33 +02:00
kygoh
e92132349c Accept float x,y values 2021-10-10 18:37:33 +02:00
Sebastien Corbin
a740767a06 Release 0.10 2020-07-26 15:58:33 +02:00
Sebastien Corbin
ea249473f1 Add full example of front-office usage 2020-07-26 15:58:12 +02:00
Sebastien Corbin
bea517d746 Add a template filter for displaying signature as base64 2020-07-26 15:55:51 +02:00
Sebastien Corbin
4dd9e5f25f Improve coverage 2020-04-18 21:08:09 +02:00
Sebastien Corbin
72e9f3d6da Fix setup.py classifiers 2020-04-18 21:04:30 +02:00
Sebastien Corbin
7601258ea8 Improve setup.py for next time 2020-04-18 21:01:55 +02:00
26 changed files with 434 additions and 187 deletions

66
.github/workflows/actions.yml vendored Normal file
View File

@@ -0,0 +1,66 @@
name: Run Tests
on: [ push, pull_request ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
django_version:
- '2.2'
- '3.2'
- '4.0'
python-version:
- 3.6
- 3.7
- 3.8
- 3.9
- "3.10"
exclude:
- django_version: '2.2'
python-version: '3.10'
- django_version: '4.0'
python-version: '3.6'
- django_version: '4.0'
python-version: '3.7'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Cache pip
uses: actions/cache@v2
with:
# This path is specific to Ubuntu
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ matrix.django_version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .
pip install -U flake8 coveralls argparse
pip install -U Django~=${{ matrix.django_version }}
- name: Lint with flake8
run: flake8 --ignore=E501,W504 jsignature
- name: Test Django
run: |
python -W error::DeprecationWarning -W error::PendingDeprecationWarning \
-m coverage run ./runtests.py
- name: Coverage
if: ${{ success() }}
run: |
coveralls --service=github

2
.gitignore vendored
View File

@@ -4,3 +4,5 @@ htmlcov
*.egg-info
.tox
example.db
/dist
/build

View File

@@ -1,46 +0,0 @@
language: python
matrix:
fast_finish: true
allow_failures:
- env: DJANGO=master
include:
- python: 2.7
env: DJANGO=1.11
- python: 3.5
env: DJANGO=1.11
- python: 3.5
env: DJANGO=2.2
- python: 3.6
env: DJANGO=1.11
- python: 3.6
env: DJANGO=2.2
- python: 3.6
env: DJANGO=3.0
- python: 3.6
env: DJANGO=master
- python: 3.7
env: DJANGO=2.2
- python: 3.7
env: DJANGO=3.0
- python: 3.7
env: DJANGO=master
- python: 3.8
env: DJANGO=2.2
- python: 3.8
env: DJANGO=3.0
- python: 3.8
env: DJANGO=master
install:
- pip install tox tox-travis
script:
- tox
after_success:
- pip install coveralls
- coveralls

21
CHANGES
View File

@@ -2,6 +2,27 @@
CHANGELOG
=========
0.11 (2022-01-17)
==================
** New **
- Django 4.0 compatibility.
- Moved to Github Actions for testing.
- Added flake8 in CI.
- Dropped support for python < 3.6
- Dropped support for Django 1.11, 2.0, 2.1, 3.0, 3.1.
0.10 (2020-07-26)
==================
** New **
- Add template filter to easily render a signature structure as base64 url.
- Add a full example of front-office usage.
0.9 (2020-04-18)
==================

View File

@@ -5,33 +5,32 @@ It provides:
* A form field and a form widget to handle jquery plugin through a Django form;
* A model field to store a captured signature;
* A mixin adding two fields (signature / signature_date) in any of your Django models.
* A template filter to render signatures as base64 image urls.
.. image:: https://img.shields.io/pypi/v/django-jsignature.svg
:target: https://pypi.python.org/pypi/django-jsignature/
:alt: Latest PyPI version
.. image:: https://travis-ci.org/fle/django-jsignature.png?branch=master
:target: https://travis-ci.org/fle/django-jsignature
.. image:: https://github.com/fle/django-jsignature/actions/workflows/actions.yml/badge.svg
:target: https://github.com/fle/django-jsignature/actions
:alt: Build status
.. image:: https://coveralls.io/repos/fle/django-jsignature/badge.png
:target: https://coveralls.io/r/fle/django-jsignature
.. image:: https://coveralls.io/repos/github/fle/django-jsignature/badge.svg?branch=master
:target: https://coveralls.io/github/fle/django-jsignature?branch=master
:alt: Coverage status
.. image:: https://github.com/fle/django-jsignature/blob/master/screen.png
==================
INSTALL
Installation
==================
For now:
::
pip install django-jsignature
==================
USAGE
Usage
==================
* Add ``jsignature`` to your ``INSTALLED_APPS``:
@@ -44,48 +43,37 @@ USAGE
'jsignature',
)
* Use provided form field and widget:
* Use provided model field (for easy storage):
::
# forms.py
from django import forms
from jsignature.forms import JSignatureField
# models.py
from django.db import models
from jsignature.fields import JSignatureField
class SignatureForm(forms.Form):
class SignatureModel(models.Model):
signature = JSignatureField()
* In your template
* In your form template
::
{{ form.media }}
<form action="." method="POST">
{% for field in form %}
{{ field.label_tag }}
{{ field }}
{% endfor %}
<input type="submit" value="Save"/>
<form action="" method="post">
{{ form }}
<input type="submit" value="Save" />
{% csrf_token %}
</form>
* Render image after form validation:
* Render image from db value in your display template:
::
# views.py
from jsignature.utils import draw_signature
from myapp.forms import SignatureForm
{# yourtemplate.html #}
{% load jsignature_filters %}
<img src="{{ obj.signature|signature_base64 }}" alt="{{ obj }}" />
def my_view(request):
form = SignatureForm(request.POST or None)
if form.is_valid():
signature = form.cleaned_data.get('signature')
if signature:
# as an image
signature_picture = draw_signature(signature)
# or as a file
signature_file_path = draw_signature(signature, as_file=True)
* By default, jSignature is made to work outside of admin, requiring that
you include the jQuery library in your ``<head>``.
@@ -94,8 +82,11 @@ USAGE
``JSIGNATURE_JQUERY`` setting to ``admin``. Otherwise if set to any url
pointing to jQuery, it will be automatically included.
It is strongly suggested to take example from ``example_project``, which is
`located in this repo <https://github.com/fle/django-jsignature/tree/master/example_project>`_
==================
CUSTOMIZATION
Customization
==================
JSignature plugin options are available in python:
@@ -130,10 +121,11 @@ Available settings are:
* ``JSIGNATURE_RESET_BUTTON`` (ResetButton)
==================
IN YOUR MODELS
In your models
==================
If you want to store signatures, provided mixin gives a ``signature`` and a ``signature_date`` that update themselves:
If you want to store signatures easily, a provided mixin gives a ``signature``
and a ``signature_date`` that update themselves:
::
@@ -144,6 +136,41 @@ If you want to store signatures, provided mixin gives a ``signature`` and a ``si
name = models.CharField()
==================
In your forms
==================
* If you need more precise handling of the form field, you can use it directly:
::
# forms.py
from django import forms
from jsignature.forms import JSignatureField
class SignatureForm(forms.Form):
signature = JSignatureField()
* And upon saving, have direct access to the image with ``draw_signature()``
::
# views.py
from jsignature.utils import draw_signature
from myapp.forms import SignatureForm
def my_view(request):
form = SignatureForm(request.POST or None)
if form.is_valid():
signature = form.cleaned_data.get('signature')
if signature:
# as an image
signature_picture = draw_signature(signature)
# or as a file
signature_file_path = draw_signature(signature, as_file=True)
==================
Example project
==================
@@ -162,16 +189,17 @@ If you want to have a demo of this package, just use the example project:
./manage.py migrate
./manage.py createsuperuser
Fill the user info, launch django with ``./manage.py runserver`` and head over to
`http://127.0.0.1:8000/ <http://127.0.0.1:8000/>`_ and login with the
credentials your provided.
Fill the user info, launch django with ``./manage.py runserver`` and head over
to `http://127.0.0.1:8000/ <http://127.0.0.1:8000/>`_, you can also
`login to the admin <http://127.0.0.1:8000/admin>`_ with the credentials your
provided.
==================
AUTHORS
Authors
==================
* Florent Lebreton <florent.lebreton@makina-corpus.com>
* Sébastien Corbin <sebastien.corbin@makina-corpus.com>
* Florent Lebreton <florent.lebreton@makina-corpus.com> (original author)
* Sébastien Corbin <sebastien.corbin@makina-corpus.com> (maintainer)
|makinacom|_

View File

@@ -0,0 +1,39 @@
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<title>Example project</title>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{% url 'admin:index' %}">Admnistration</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'list' %}">List</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'create' %}">Create</a>
</li>
</ul>
</div>
</nav>
<div class="container">
{% block content %}{% endblock %}
</div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
{% block extra_media %}{% endblock %}
</body>
</html>

View File

@@ -0,0 +1,14 @@
{% extends "base.html" %}
{% block extra_media %}
{{ form.media }}
{% endblock %}
{% block content %}
<form action="" method="post">
{{ form }}
<input type="submit" value="Save" class="btn btn-success" />
<a href="{% url 'list' %}" class="btn btn-danger">Cancel</a>
{% csrf_token %}
</form>
{% endblock %}

View File

@@ -0,0 +1,24 @@
{% extends "base.html" %}{% load jsignature_filters %}
{% block content %}
<h1>List</h1>
<ul>
{% for obj in object_list %}
<li>
<dl>
<dt>Raw data (from db)</dt>
<dd>{{ obj.signature }}</dd>
<dt>As image</dt>
<dd>
<img src="{{ obj.signature|signature_base64 }}" alt="{{ obj }}" />
<a href="{% url 'update' obj.pk %}" class="btn btn-primary">
Update
</a>
</dd>
</dl>
</li>
{% endfor %}
</ul>
<a href="{% url 'create' %}" class="btn btn-primary">Create</a>
{% endblock %}

View File

@@ -3,6 +3,11 @@ from __future__ import unicode_literals
from django.contrib import admin
from django.urls import path
from example_project import views
urlpatterns = [
path('', admin.site.urls),
path('', views.ExampleListView.as_view(), name='list'),
path('create', views.ExampleCreateView.as_view(), name='create'),
path('update/<int:pk>', views.ExampleUpdateView.as_view(), name='update'),
path('admin', admin.site.urls),
]

20
example_project/views.py Normal file
View File

@@ -0,0 +1,20 @@
from django.urls import reverse_lazy
from django.views import generic
from example_project.models import ExampleModel
class ExampleCreateView(generic.CreateView):
model = ExampleModel
fields = '__all__'
success_url = reverse_lazy('list')
class ExampleUpdateView(generic.UpdateView):
model = ExampleModel
fields = '__all__'
success_url = reverse_lazy('list')
class ExampleListView(generic.ListView):
model = ExampleModel

View File

@@ -18,13 +18,13 @@ class JSignatureField(Field):
widget = JSignatureWidget()
def to_python(self, value):
"""
Validates that the input can be red as a JSON object.
Returns a Python list (JSON object unserialized).
"""
if value in JSIGNATURE_EMPTY_VALUES:
return None
try:
return json.loads(value)
except ValueError:
raise ValidationError('Invalid JSON format.')
"""
Validates that the input can be red as a JSON object.
Returns a Python list (JSON object unserialized).
"""
if value in JSIGNATURE_EMPTY_VALUES:
return None
try:
return json.loads(value)
except ValueError:
raise ValidationError('Invalid JSON format.')

View File

@@ -5,7 +5,7 @@
import json
from datetime import datetime
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from .fields import JSignatureField

View File

@@ -1,9 +1,9 @@
/*
jSignature v2 "2012-11-01T22:48" "commit ID 1c15dfafecc75925c3b7d529356a558b59220edb"
jSignature v2 "2018-11-06T13:56" "commit ID 89c22b348ab2e1d92a928d8fd992f175e8bc5cbd"
Copyright (c) 2012 Willow Systems Corp http://willow-systems.com
Copyright (c) 2010 Brinley Ang http://www.unbolt.net
MIT License <http://www.opensource.org/licenses/mit-license.php>
MIT License <http://www.opensource.org/licenses/mit-license.php>
Simplify.js BSD
@@ -32,46 +32,50 @@ jSignature v2 jSignature's custom "base30" format export and import plugins.
jSignature v2 SVG export plugin.
*/
(function(){function q(b){for(var j,s=b.css("color"),g,b=b[0],c=!1;b&&!g&&!c;){try{j=$(b).css("background-color")}catch(d){j="transparent"}"transparent"!==j&&"rgba(0, 0, 0, 0)"!==j&&(g=j);c=b.body;b=b.parentNode}var b=/rgb[a]*\((\d+),\s*(\d+),\s*(\d+)/,c=/#([AaBbCcDdEeFf\d]{2})([AaBbCcDdEeFf\d]{2})([AaBbCcDdEeFf\d]{2})/,a;j=void 0;(j=s.match(b))?a={r:parseInt(j[1],10),g:parseInt(j[2],10),b:parseInt(j[3],10)}:(j=s.match(c))&&(a={r:parseInt(j[1],16),g:parseInt(j[2],16),b:parseInt(j[3],16)});var f;g?
(j=void 0,(j=g.match(b))?f={r:parseInt(j[1],10),g:parseInt(j[2],10),b:parseInt(j[3],10)}:(j=g.match(c))&&(f={r:parseInt(j[1],16),g:parseInt(j[2],16),b:parseInt(j[3],16)})):f=a?127<Math.max.apply(null,[a.r,a.g,a.b])?{r:0,g:0,b:0}:{r:255,g:255,b:255}:{r:255,g:255,b:255};j=function(b){return"rgb("+[b.r,b.g,b.b].join(", ")+")"};a&&f?(b=Math.max.apply(null,[a.r,a.g,a.b]),a=Math.max.apply(null,[f.r,f.g,f.b]),a=Math.round(a+-0.75*(a-b)),a={r:a,g:a,b:a}):a?(a=Math.max.apply(null,[a.r,a.g,a.b]),b=1,127<a&&
(b=-1),a=Math.round(a+96*b),a={r:a,g:a,b:a}):a={r:191,g:191,b:191};return{color:s,"background-color":f?j(f):g,"decor-color":j(a)}}function m(b,j){this.x=b;this.y=j;this.reverse=function(){return new this.constructor(-1*this.x,-1*this.y)};this._length=null;this.getLength=function(){this._length||(this._length=Math.sqrt(Math.pow(this.x,2)+Math.pow(this.y,2)));return this._length};var s=function(b){return Math.round(b/Math.abs(b))};this.resizeTo=function(b){if(0===this.x&&0===this.y)this._length=0;else if(0===
this.x)this._length=b,this.y=b*s(this.y);else if(0===this.y)this._length=b,this.x=b*s(this.x);else{var j=Math.abs(this.y/this.x),a=Math.sqrt(Math.pow(b,2)/(1+Math.pow(j,2))),j=j*a;this._length=b;this.x=a*s(this.x);this.y=j*s(this.y)}return this};this.angleTo=function(b){var j=this.getLength()*b.getLength();return 0===j?0:Math.acos(Math.min(Math.max((this.x*b.x+this.y*b.y)/j,-1),1))/Math.PI}}function k(b,j){this.x=b;this.y=j;this.getVectorToCoordinates=function(b,j){return new m(b-this.x,j-this.y)};
this.getVectorFromCoordinates=function(b,j){return this.getVectorToCoordinates(b,j).reverse()};this.getVectorToPoint=function(b){return new m(b.x-this.x,b.y-this.y)};this.getVectorFromPoint=function(b){return this.getVectorToPoint(b).reverse()}}function t(b,j,a,g,c){this.data=b;this.context=j;if(b.length)for(var d=b.length,f,l,i=0;i<d;i++){f=b[i];l=f.x.length;a.call(j,f);for(var e=1;e<l;e++)g.call(j,f,e);c.call(j,f)}this.changed=function(){};this.startStrokeFn=a;this.addToStrokeFn=g;this.endStrokeFn=
c;this.inStroke=!1;this._stroke=this._lastPoint=null;this.startStroke=function(b){if(b&&"number"==typeof b.x&&"number"==typeof b.y){this._stroke={x:[b.x],y:[b.y]};this.data.push(this._stroke);this._lastPoint=b;this.inStroke=!0;var j=this._stroke,a=this.startStrokeFn,g=this.context;setTimeout(function(){a.call(g,j)},3);return b}return null};this.addToStroke=function(b){if(this.inStroke&&"number"===typeof b.x&&"number"===typeof b.y&&4<Math.abs(b.x-this._lastPoint.x)+Math.abs(b.y-this._lastPoint.y)){var j=
this._stroke.x.length;this._stroke.x.push(b.x);this._stroke.y.push(b.y);this._lastPoint=b;var a=this._stroke,g=this.addToStrokeFn,s=this.context;setTimeout(function(){g.call(s,a,j)},3);return b}return null};this.endStroke=function(){var b=this.inStroke;this.inStroke=!1;this._lastPoint=null;if(b){var j=this._stroke,a=this.endStrokeFn,g=this.context,s=this.changed;setTimeout(function(){a.call(g,j);s.call(g)},3);return!0}return null}}function r(b,j,a){var g=this.$parent=$(b),b=this.eventTokens={};this.events=
new v(this);var c=$.fn[e]("globalEvents"),d={width:"ratio",height:"ratio",sizeRatio:4,color:"#000","background-color":"#fff","decor-color":"#eee",lineWidth:0,minFatFingerCompensation:-10,showUndoButton:!1,data:[]};$.extend(d,q(g));j&&$.extend(d,j);this.settings=d;for(var f in a)a.hasOwnProperty(f)&&a[f].call(this,f);this.events.publish(e+".initializing");this.$controlbarUpper=$('<div style="padding:0 !important;margin:0 !important;width: 100% !important; height: 0 !important;margin-top:-1em !important;margin-bottom:1em !important;"></div>').appendTo(g);
this.isCanvasEmulator=!1;a=this.canvas=this.initializeCanvas(d);j=$(a);this.$controlbarLower=$('<div style="padding:0 !important;margin:0 !important;width: 100% !important; height: 0 !important;margin-top:-1.5em !important;margin-bottom:1.5em !important;"></div>').appendTo(g);this.canvasContext=a.getContext("2d");j.data(e+".this",this);g=(g=d.lineWidth)?g:Math.max(Math.round(a.width/400),2);d.lineWidth=g;this.lineCurveThreshold=3*d.lineWidth;d.cssclass&&""!=$.trim(d.cssclass)&&j.addClass(d.cssclass);
this.fatFingerCompensation=0;var g=function(b){var j,a,g=function(g){g=g.changedTouches&&0<g.changedTouches.length?g.changedTouches[0]:g;return new k(Math.round(g.pageX+j),Math.round(g.pageY+a)+b.fatFingerCompensation)},d=new y(750,function(){b.dataEngine.endStroke()});this.drawEndHandler=function(j){try{j.preventDefault()}catch(a){}d.clear();b.dataEngine.endStroke()};this.drawStartHandler=function(c){c.preventDefault();var s=$(b.canvas).offset();j=-1*s.left;a=-1*s.top;b.dataEngine.startStroke(g(c));
d.kick()};this.drawMoveHandler=function(j){j.preventDefault();b.dataEngine.inStroke&&(b.dataEngine.addToStroke(g(j)),d.kick())};return this}.call({},this),l=g.drawEndHandler,i=g.drawStartHandler,p=g.drawMoveHandler,h=this.canvas,j=$(h);this.isCanvasEmulator?(j.bind("mousemove."+e,p),j.bind("mouseup."+e,l),j.bind("mousedown."+e,i)):(h.ontouchstart=function(b){h.onmousedown=void 0;h.onmouseup=void 0;h.onmousemove=void 0;this.fatFingerCompensation=d.minFatFingerCompensation&&-3*d.lineWidth>d.minFatFingerCompensation?
-3*d.lineWidth:d.minFatFingerCompensation;i(b);h.ontouchend=l;h.ontouchstart=i;h.ontouchmove=p},h.onmousedown=function(b){h.ontouchstart=void 0;h.ontouchend=void 0;h.ontouchmove=void 0;i(b);h.onmousedown=i;h.onmouseup=l;h.onmousemove=p});b[e+".windowmouseup"]=c.subscribe(e+".windowmouseup",g.drawEndHandler);this.events.publish(e+".attachingEventHandlers");var n=this,b=d.width.toString(10),w=e;if("ratio"===b||"%"===b.split("")[b.length-1])this.eventTokens[w+".parentresized"]=c.subscribe(w+".parentresized",
function(b,j,a){return function(){var g=j.width();if(g!==a){for(var d in b)b.hasOwnProperty(d)&&(c.unsubscribe(b[d]),delete b[d]);var s=n.settings;n.$parent.children().remove();for(d in n)n.hasOwnProperty(d)&&delete n[d];d=s.data;var g=1*g/a,f=[],l,i,e,h,k,p;i=0;for(e=d.length;i<e;i++){p=d[i];l={x:[],y:[]};h=0;for(k=p.x.length;h<k;h++)l.x.push(p.x[h]*g),l.y.push(p.y[h]*g);f.push(l)}s.data=f;j[w](s)}}}(this.eventTokens,this.$parent,this.$parent.width(),1*this.canvas.width/this.canvas.height));this.resetCanvas(d.data);
this.events.publish(e+".initialized");return this}var e="jSignature",y=function(b,j){var a;this.kick=function(){clearTimeout(a);a=setTimeout(j,b)};this.clear=function(){clearTimeout(a)};return this},v=function(b){this.topics={};this.context=b?b:this;this.publish=function(b,a,g,d){if(this.topics[b]){var c=this.topics[b],f=Array.prototype.slice.call(arguments,1),l=[],i,e,h,p;e=0;for(h=c.length;e<h;e++)p=c[e],i=p[0],p[1]&&(p[0]=function(){},l.push(e)),i.apply(this.context,f);e=0;for(h=l.length;e<h;e++)c.splice(l[e],
1)}};this.subscribe=function(b,a,g){this.topics[b]?this.topics[b].push([a,g]):this.topics[b]=[[a,g]];return{topic:b,callback:a}};this.unsubscribe=function(b){if(this.topics[b.topic])for(var a=this.topics[b.topic],g=0,d=a.length;g<d;g++)a[g][0]===b.callback&&a.splice(g,1)}},z=function(b){var a=this.canvasContext,d=b.x[0],b=b.y[0],g=this.settings.lineWidth,c=a.fillStyle;a.fillStyle=a.strokeStyle;a.fillRect(d+g/-2,b+g/-2,g,g);a.fillStyle=c},u=function(b,a){var d=new k(b.x[a-1],b.y[a-1]),g=new k(b.x[a],
b.y[a]),c=d.getVectorToPoint(g);if(1<a){var f=new k(b.x[a-2],b.y[a-2]),l=f.getVectorToPoint(d),i;if(l.getLength()>this.lineCurveThreshold){i=2<a?(new k(b.x[a-3],b.y[a-3])).getVectorToPoint(f):new m(0,0);var e=0.35*l.getLength(),h=l.angleTo(i.reverse()),p=c.angleTo(l.reverse());i=(new m(i.x+l.x,i.y+l.y)).resizeTo(Math.max(0.05,h)*e);var n=(new m(l.x+c.x,l.y+c.y)).reverse().resizeTo(Math.max(0.05,p)*e),l=this.canvasContext,e=f.x,p=f.y,h=d.x,w=d.y,A=f.x+i.x,f=f.y+i.y;i=d.x+n.x;n=d.y+n.y;l.beginPath();
l.moveTo(e,p);l.bezierCurveTo(A,f,i,n,h,w);l.stroke()}}c.getLength()<=this.lineCurveThreshold&&(c=this.canvasContext,f=d.x,d=d.y,i=g.x,g=g.y,c.beginPath(),c.moveTo(f,d),c.lineTo(i,g),c.stroke())},x=function(b){var a=b.x.length-1;if(0<a){var d=new k(b.x[a],b.y[a]),g=new k(b.x[a-1],b.y[a-1]),c=g.getVectorToPoint(d);if(c.getLength()>this.lineCurveThreshold){if(1<a){var b=(new k(b.x[a-2],b.y[a-2])).getVectorToPoint(g),f=(new m(b.x+c.x,b.y+c.y)).resizeTo(c.getLength()/2),c=this.canvasContext,b=g.x,a=g.y,
l=d.x,i=d.y,e=g.x+f.x,g=g.y+f.y,f=d.x,d=d.y;c.beginPath();c.moveTo(b,a);c.bezierCurveTo(e,g,f,d,l,i)}else c=this.canvasContext,b=g.x,g=g.y,a=d.x,d=d.y,c.beginPath(),c.moveTo(b,g),c.lineTo(a,d);c.stroke()}}};r.prototype.resetCanvas=function(b){var a=this.canvas,d=this.settings,c=this.canvasContext,f=this.isCanvasEmulator,l=a.width,i=a.height;c.clearRect(0,0,l+30,i+30);c.shadowColor=c.fillStyle=d["background-color"];f&&c.fillRect(0,0,l+30,i+30);c.lineWidth=Math.ceil(parseInt(d.lineWidth,10));c.lineCap=
c.lineJoin="round";c.strokeStyle=d["decor-color"];c.shadowOffsetX=0;c.shadowOffsetY=0;var h=Math.round(i/5),p=1.5*h,k=i-h,l=l-1.5*h,i=i-h;c.beginPath();c.moveTo(p,k);c.lineTo(l,i);c.stroke();c.strokeStyle=d.color;f||(c.shadowColor=c.strokeStyle,c.shadowOffsetX=0.5*c.lineWidth,c.shadowOffsetY=-0.6*c.lineWidth,c.shadowBlur=0);b||(b=[]);c=this.dataEngine=new t(b,this,z,u,x);d.data=b;$(a).data(e+".data",b).data(e+".settings",d);var n=this.$parent,w=this.events,A=e;c.changed=function(){w.publish(A+".change");
n.trigger("change")};c.changed();return!0};r.prototype.initializeCanvas=function(b){var a=document.createElement("canvas"),c=$(a);b.width===b.height&&"ratio"===b.height&&(b.width="100%");c.css("margin",0).css("padding",0).css("border","none").css("height","ratio"===b.height||!b.height?1:b.height.toString(10)).css("width","ratio"===b.width||!b.width?1:b.width.toString(10));c.appendTo(this.$parent);"ratio"===b.height?c.css("height",Math.round(c.width()/b.sizeRatio)):"ratio"===b.width&&c.css("width",
Math.round(c.height()*b.sizeRatio));c.addClass(e);a.width=c.width();a.height=c.height();b=a;if(b.getContext)b=!1;else{var c=b.ownerDocument.parentWindow,d=c.FlashCanvas?b.ownerDocument.parentWindow.FlashCanvas:"undefined"===typeof FlashCanvas?void 0:FlashCanvas;if(d){b=d.initElement(b);d=1;c&&(c.screen&&c.screen.deviceXDPI&&c.screen.logicalXDPI)&&(d=1*c.screen.deviceXDPI/c.screen.logicalXDPI);if(1!==d)try{$(b).children("object").get(0).resize(Math.ceil(b.width*d),Math.ceil(b.height*d)),b.getContext("2d").scale(d,
d)}catch(f){}b=!0}else throw Error("Canvas element does not support 2d context. jSignature cannot proceed.");}this.isCanvasEmulator=b;a.onselectstart=function(b){b&&b.preventDefault&&b.preventDefault();b&&b.stopPropagation&&b.stopPropagation();return!1};return a};var p=window,h=function(b,a){var c=new Image,d=this;c.onload=function(){d.getContext("2d").drawImage(c,0,0,c.width<d.width?c.width:d.width,c.height<d.height?c.height:d.height)};c.src="data:"+a+","+b},a=function(b){this.find("canvas."+e).add(this.filter("canvas."+
e)).data(e+".this").resetCanvas(b);return this},f=function(b,a){if(void 0===a&&("string"===typeof b&&"data:"===b.substr(0,5))&&(a=b.slice(5).split(",")[0],b=b.slice(6+a.length),a===b))return;var c=this.find("canvas."+e).add(this.filter("canvas."+e));if(n.hasOwnProperty(a))0!==c.length&&n[a].call(c[0],b,a,function(b){return function(){return b.resetCanvas.apply(b,arguments)}}(c.data(e+".this")));else throw Error(e+" is unable to find import plugin with for format '"+String(a)+"'");return this},d=new v,
c=e,l,i=function(){d.publish(c+".parentresized")};$(p).bind("resize."+c,function(){l&&clearTimeout(l);l=setTimeout(i,500)}).bind("mouseup."+c,function(){d.publish(c+".windowmouseup")});var w={},A={"default":function(){return this.toDataURL()},"native":function(b){return b},image:function(){var b=this.toDataURL();if("string"===typeof b&&4<b.length&&"data:"===b.slice(0,5)&&-1!==b.indexOf(",")){var a=b.indexOf(",");return[b.slice(5,a),b.substr(a+1)]}return[]}},n={"native":function(b,a,c){c(b)},image:h,
"image/png;base64":h,"image/jpeg;base64":h,"image/jpg;base64":h},B={"export":A,"import":n,instance:w},C={init:function(b){return this.each(function(){var a,c=!1;for(a=this.parentNode;a&&!c;)c=a.body,a=a.parentNode;!c||new r(this,b,w)})},getSettings:function(){return this.find("canvas."+e).add(this.filter("canvas."+e)).data(e+".this").settings},clear:a,reset:a,addPlugin:function(b,a,c){B.hasOwnProperty(b)&&(B[b][a]=c);return this},listPlugins:function(b){var a=[];if(B.hasOwnProperty(b)){var b=B[b],
c;for(c in b)b.hasOwnProperty(c)&&a.push(c)}return a},getData:function(b){var a=this.find("canvas."+e).add(this.filter("canvas."+e));void 0===b&&(b="default");if(0!==a.length&&A.hasOwnProperty(b))return A[b].call(a.get(0),a.data(e+".data"))},importData:f,setData:f,globalEvents:function(){return d},events:function(){return this.find("canvas."+e).add(this.filter("canvas."+e)).data(e+".this").events}};$.fn[e]=function(b){if(!b||"object"===typeof b)return C.init.apply(this,arguments);if("string"===typeof b&&
C[b])return C[b].apply(this,Array.prototype.slice.call(arguments,1));$.error("Method "+String(b)+" does not exist on jQuery."+e)}})();
(function(){$.fn.jSignature("addPlugin","instance","UndoButton",function(q){this.events.subscribe("jSignature.attachingEventHandlers",function(){if(this.settings[q]){var m=this.settings[q];"function"!==typeof m&&(m=function(){var e=$('<input type="button" value="Undo last stroke" style="position:absolute;display:none;margin:0 !important;top:auto" />').appendTo(this.$controlbarLower),k=e.width();e.css("left",Math.round((this.canvas.width-k)/2));k!==e.width()&&e.width(k);return e});var k=m.call(this),
t=this;t.events.subscribe("jSignature.change",function(){t.dataEngine.data.length?k.show():k.hide()});var r=this,e=(this.events.topics.hasOwnProperty("jSignature.undo")?q:"jSignature")+".undo";k.bind("click",function(){r.events.publish(e)});r.events.subscribe(e,function(){var e=r.dataEngine.data;e.length&&(e.pop(),r.resetCanvas(e))})}})})})();
(function(){for(var q={},m={},k="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX".split(""),t=k.length/2,r=t-1;-1<r;r--)q[k[r]]=k[r+t],m[k[r+t]]=k[r];var e=function(e){for(var e=e.split(""),h=e.length,a=1;a<h;a++)e[a]=q[e[a]];return e.join("")},y=function(k){for(var h=[],a=0,f=1,d=k.length,c,l,i=0;i<d;i++)c=Math.round(k[i]),l=c-a,a=c,0>l&&0<f?(f=-1,h.push("Z")):0<l&&0>f&&(f=1,h.push("Y")),c=Math.abs(l),c>=t?h.push(e(c.toString(t))):h.push(c.toString(t));return h.join("")},v=function(e){for(var h=
[],e=e.split(""),a=e.length,f,d=1,c=[],l=0,i=0;i<a;i++)f=e[i],f in q||"Z"===f||"Y"===f?(0!==c.length&&(c=parseInt(c.join(""),t)*d+l,h.push(c),l=c),"Z"===f?(d=-1,c=[]):"Y"===f?(d=1,c=[]):c=[f]):c.push(m[f]);h.push(parseInt(c.join(""),t)*d+l);return h},z=function(e){for(var h=[],a=e.length,f,d=0;d<a;d++)f=e[d],h.push(y(f.x)),h.push(y(f.y));return h.join("_")},u=function(e){for(var h=[],e=e.split("_"),a=e.length/2,f=0;f<a;f++)h.push({x:v(e[2*f]),y:v(e[2*f+1])});return h},k=function(e){return["image/jsignature;base30",
z(e)]},r=function(e,h,a){"string"===typeof e&&("image/jsignature;base30"===e.substring(0,23).toLowerCase()&&(e=e.substring(24)),a(u(e)))};if(null==this.jQuery)throw Error("We need jQuery for some of the functionality. jQuery is not detected. Failing to initialize...");var x=this.jQuery.fn.jSignature;x("addPlugin","export","base30",k);x("addPlugin","export","image/jsignature;base30",k);x("addPlugin","import","base30",r);x("addPlugin","import","image/jsignature;base30",r);this.jSignatureDebug&&(this.jSignatureDebug.base30=
{remapTailChars:e,compressstrokeleg:y,uncompressstrokeleg:v,compressstrokes:z,uncompressstrokes:u,charmap:q})}).call("undefined"!==typeof window?window:this);
(function(){function q(a,f){this.x=a;this.y=f;this.reverse=function(){return new this.constructor(-1*this.x,-1*this.y)};this._length=null;this.getLength=function(){this._length||(this._length=Math.sqrt(Math.pow(this.x,2)+Math.pow(this.y,2)));return this._length};var d=function(a){return Math.round(a/Math.abs(a))};this.resizeTo=function(a){if(0===this.x&&0===this.y)this._length=0;else if(0===this.x)this._length=a,this.y=a*d(this.y);else if(0===this.y)this._length=a,this.x=a*d(this.x);else{var f=Math.abs(this.y/
this.x),e=Math.sqrt(Math.pow(a,2)/(1+Math.pow(f,2))),f=f*e;this._length=a;this.x=e*d(this.x);this.y=f*d(this.y)}return this};this.angleTo=function(a){var d=this.getLength()*a.getLength();return 0===d?0:Math.acos(Math.min(Math.max((this.x*a.x+this.y*a.y)/d,-1),1))/Math.PI}}function m(a,f){this.x=a;this.y=f;this.getVectorToCoordinates=function(a,c){return new q(a-this.x,c-this.y)};this.getVectorFromCoordinates=function(a,c){return this.getVectorToCoordinates(a,c).reverse()};this.getVectorToPoint=function(a){return new q(a.x-
this.x,a.y-this.y)};this.getVectorFromPoint=function(a){return this.getVectorToPoint(a).reverse()}}function k(a,f){var d=Math.pow(10,f);return Math.round(a*d)/d}function t(a,f,d){var f=f+1,c=new m(a.x[f-1],a.y[f-1]),e=new m(a.x[f],a.y[f]),e=c.getVectorToPoint(e),i=new m(a.x[f-2],a.y[f-2]),c=i.getVectorToPoint(c);return c.getLength()>d?(d=2<f?(new m(a.x[f-3],a.y[f-3])).getVectorToPoint(i):new q(0,0),a=0.35*c.getLength(),i=c.angleTo(d.reverse()),f=e.angleTo(c.reverse()),d=(new q(d.x+c.x,d.y+c.y)).resizeTo(Math.max(0.05,
i)*a),e=(new q(c.x+e.x,c.y+e.y)).reverse().resizeTo(Math.max(0.05,f)*a),e=new q(c.x+e.x,c.y+e.y),["c",k(d.x,2),k(d.y,2),k(e.x,2),k(e.y,2),k(c.x,2),k(c.y,2)]):["l",k(c.x,2),k(c.y,2)]}function r(a,f){var d=a.x.length-1,c=new m(a.x[d],a.y[d]),e=new m(a.x[d-1],a.y[d-1]),c=e.getVectorToPoint(c);if(1<d&&c.getLength()>f){var d=(new m(a.x[d-2],a.y[d-2])).getVectorToPoint(e),e=c.angleTo(d.reverse()),i=0.35*c.getLength(),d=(new q(d.x+c.x,d.y+c.y)).resizeTo(Math.max(0.05,e)*i);return["c",k(d.x,2),k(d.y,2),k(c.x,
2),k(c.y,2),k(c.x,2),k(c.y,2)]}return["l",k(c.x,2),k(c.y,2)]}function e(a,e,d){for(var e=["M",k(a.x[0]-e,2),k(a.y[0]-d,2)],d=1,c=a.x.length-1;d<c;d++)e.push.apply(e,t(a,d,1));0<c?e.push.apply(e,r(a,d,1)):0===c&&e.push.apply(e,["l",1,1]);return e.join(" ")}function y(a){var f=['<?xml version="1.0" encoding="UTF-8" standalone="no"?>','<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'],d,c=a.length,l,i=[],h=[],k=l=d=0,n=0,p=[];if(0!==c){for(d=0;d<c;d++){k=
a[d];n=[];l={x:[],y:[]};for(var m=void 0,b=void 0,m=0,b=k.x.length;m<b;m++)n.push({x:k.x[m],y:k.y[m]});n=simplify(n,0.7,!0);m=0;for(b=n.length;m<b;m++)l.x.push(n[m].x),l.y.push(n[m].y);p.push(l);i=i.concat(l.x);h=h.concat(l.y)}a=Math.min.apply(null,i)-1;c=Math.max.apply(null,i)+1;i=Math.min.apply(null,h)-1;h=Math.max.apply(null,h)+1;k=0>a?0:a;n=0>i?0:i;d=c-a;l=h-i}f.push('<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="'+d.toString()+'" height="'+l.toString()+'">');d=0;for(c=p.length;d<
c;d++)l=p[d],f.push('<path fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="'+e(l,k,n)+'"/>');f.push("</svg>");return f.join("")}function v(a){return[p,y(a)]}function z(a){return[h,x(y(a))]}var u=window;"use strict";("undefined"!=typeof exports?exports:u).simplify=function(a,e,d){e=void 0!==e?e*e:1;if(!d){for(var c=a.length,l,i=a[0],h=[i],d=1;d<c;d++){l=a[d];var k=l.x-i.x,n=l.y-i.y;k*k+n*n>e&&(h.push(l),i=l)}a=(i!==l&&h.push(l),h)}l=a;var d=l.length,
c=new ("undefined"!=typeof Uint8Array?Uint8Array:Array)(d),i=0,h=d-1,m,p,b=[],j=[],s=[];for(c[i]=c[h]=1;h;){n=0;for(k=i+1;k<h;k++){m=l[k];var g=l[i],r=l[h],q=g.x,t=g.y,g=r.x-q,u=r.y-t,v=void 0;if(0!==g||0!==u)v=((m.x-q)*g+(m.y-t)*u)/(g*g+u*u),1<v?(q=r.x,t=r.y):0<v&&(q+=g*v,t+=u*v);m=(g=m.x-q,u=m.y-t,g*g+u*u);m>n&&(p=k,n=m)}n>e&&(c[p]=1,b.push(i),j.push(p),b.push(p),j.push(h));i=b.pop();h=j.pop()}for(k=0;k<d;k++)c[k]&&s.push(l[k]);return a=s,a};if("function"!==typeof x)var x=function(a){var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".split(""),
d,c,h,i,k=0,m=0,n="",n=[];do d=a.charCodeAt(k++),c=a.charCodeAt(k++),h=a.charCodeAt(k++),i=d<<16|c<<8|h,d=i>>18&63,c=i>>12&63,h=i>>6&63,i&=63,n[m++]=e[d]+e[c]+e[h]+e[i];while(k<a.length);n=n.join("");a=a.length%3;return(a?n.slice(0,a-3):n)+"===".slice(a||3)};var p="image/svg+xml",h="image/svg+xml;base64";if("undefined"===typeof $)throw Error("We need jQuery for some of the functionality. jQuery is not detected. Failing to initialize...");u=$.fn.jSignature;u("addPlugin","export","svg",v);u("addPlugin",
"export",p,v);u("addPlugin","export","svgbase64",z);u("addPlugin","export",h,z)})();
(function(){function q(a){var b=a.css("color"),c;a=a[0];for(var g=!1;a&&!c&&!g;){try{var d=$(a).css("background-color")}catch(l){d="transparent"}"transparent"!==d&&"rgba(0, 0, 0, 0)"!==d&&(c=d);g=a.body;a=a.parentNode}a=/rgb[a]*\((\d+),\s*(\d+),\s*(\d+)/;g=/#([AaBbCcDdEeFf\d]{2})([AaBbCcDdEeFf\d]{2})([AaBbCcDdEeFf\d]{2})/;d=void 0;if(d=b.match(a))var m={r:parseInt(d[1],10),g:parseInt(d[2],10),b:parseInt(d[3],10)};else(d=b.match(g))&&(m={r:parseInt(d[1],16),g:parseInt(d[2],16),b:parseInt(d[3],16)});
if(c)if(d=void 0,d=c.match(a))var e={r:parseInt(d[1],10),g:parseInt(d[2],10),b:parseInt(d[3],10)};else(d=c.match(g))&&(e={r:parseInt(d[1],16),g:parseInt(d[2],16),b:parseInt(d[3],16)});else e=m?127<Math.max.apply(null,[m.r,m.g,m.b])?{r:0,g:0,b:0}:{r:255,g:255,b:255}:{r:255,g:255,b:255};d=function(a){return"rgb("+[a.r,a.g,a.b].join(", ")+")"};m&&e?(a=Math.max.apply(null,[m.r,m.g,m.b]),m=Math.max.apply(null,[e.r,e.g,e.b]),m=Math.round(m+-.75*(m-a)),m={r:m,g:m,b:m}):m?(m=Math.max.apply(null,[m.r,m.g,
m.b]),a=1,127<m&&(a=-1),m=Math.round(m+96*a),m={r:m,g:m,b:m}):m={r:191,g:191,b:191};return{color:b,"background-color":e?d(e):c,"decor-color":d(m)}}function k(a,b){this.x=a;this.y=b;this.reverse=function(){return new this.constructor(-1*this.x,-1*this.y)};this._length=null;this.getLength=function(){this._length||(this._length=Math.sqrt(Math.pow(this.x,2)+Math.pow(this.y,2)));return this._length};var c=function(a){return Math.round(a/Math.abs(a))};this.resizeTo=function(a){if(0===this.x&&0===this.y)this._length=
0;else if(0===this.x)this._length=a,this.y=a*c(this.y);else if(0===this.y)this._length=a,this.x=a*c(this.x);else{var b=Math.abs(this.y/this.x),g=Math.sqrt(Math.pow(a,2)/(1+Math.pow(b,2)));b*=g;this._length=a;this.x=g*c(this.x);this.y=b*c(this.y)}return this};this.angleTo=function(a){var b=this.getLength()*a.getLength();return 0===b?0:Math.acos(Math.min(Math.max((this.x*a.x+this.y*a.y)/b,-1),1))/Math.PI}}function h(a,b){this.x=a;this.y=b;this.getVectorToCoordinates=function(a,b){return new k(a-this.x,
b-this.y)};this.getVectorFromCoordinates=function(a,b){return this.getVectorToCoordinates(a,b).reverse()};this.getVectorToPoint=function(a){return new k(a.x-this.x,a.y-this.y)};this.getVectorFromPoint=function(a){return this.getVectorToPoint(a).reverse()}}function p(a,b,c,g,d){this.data=a;this.context=b;if(a.length)for(var m=a.length,e,l,f=0;f<m;f++){e=a[f];l=e.x.length;c.call(b,e);for(var t=1;t<l;t++)g.call(b,e,t);d.call(b,e)}this.changed=function(){};this.startStrokeFn=c;this.addToStrokeFn=g;this.endStrokeFn=
d;this.inStroke=!1;this._stroke=this._lastPoint=null;this.startStroke=function(a){if(a&&"number"==typeof a.x&&"number"==typeof a.y){this._stroke={x:[a.x],y:[a.y]};this.data.push(this._stroke);this._lastPoint=a;this.inStroke=!0;var b=this._stroke,c=this.startStrokeFn,d=this.context;setTimeout(function(){c.call(d,b)},3);return a}return null};this.addToStroke=function(a){if(this.inStroke&&"number"===typeof a.x&&"number"===typeof a.y&&4<Math.abs(a.x-this._lastPoint.x)+Math.abs(a.y-this._lastPoint.y)){var b=
this._stroke.x.length;this._stroke.x.push(a.x);this._stroke.y.push(a.y);this._lastPoint=a;var c=this._stroke,d=this.addToStrokeFn,g=this.context;setTimeout(function(){d.call(g,c,b)},3);return a}return null};this.endStroke=function(){var a=this.inStroke;this.inStroke=!1;this._lastPoint=null;if(a){var b=this._stroke,c=this.endStrokeFn,d=this.context,g=this.changed;setTimeout(function(){c.call(d,b);g.call(d)},3);return!0}return null}}function n(a,b,c,g){if("ratio"===b||"%"===b.split("")[b.length-1])this.eventTokens[c+
".parentresized"]=g.subscribe(c+".parentresized",function(b,m,e,l){return function(){var d=m.width();if(d!==e){for(var l in b)b.hasOwnProperty(l)&&(g.unsubscribe(b[l]),delete b[l]);var f=a.settings;a.$parent.children().remove();for(l in a)a.hasOwnProperty(l)&&delete a[l];l=f.data;d=1*d/e;var r=[],D,E;var h=0;for(D=l.length;h<D;h++){var k=l[h];var n={x:[],y:[]};var p=0;for(E=k.x.length;p<E;p++)n.x.push(k.x[p]*d),n.y.push(k.y[p]*d);r.push(n)}f.data=r;m[c](f)}}}(this.eventTokens,this.$parent,this.$parent.width(),
1*this.canvas.width/this.canvas.height))}function w(a,b,c){var g=this.$parent=$(a);a=this.eventTokens={};this.events=new u(this);var d=$.fn.jSignature("globalEvents"),e={width:"ratio",height:"ratio",sizeRatio:4,color:"#000","background-color":"#fff","decor-color":"#eee",lineWidth:0,minFatFingerCompensation:-10,showUndoButton:!1,readOnly:!1,data:[],signatureLine:!1};$.extend(e,q(g));b&&$.extend(e,b);this.settings=e;for(var f in c)c.hasOwnProperty(f)&&c[f].call(this,f);this.events.publish("jSignature.initializing");
this.$controlbarUpper=$('<div style="padding:0 !important; margin:0 !important;width: 100% !important; height: 0 !important; -ms-touch-action: none; touch-action: none;margin-top:-1em !important; margin-bottom:1em !important;"></div>').appendTo(g);this.isCanvasEmulator=!1;b=this.canvas=this.initializeCanvas(e);c=$(b);this.$controlbarLower=$('<div style="padding:0 !important; margin:0 !important;width: 100% !important; height: 0 !important; -ms-touch-action: none; touch-action: none;margin-top:-1.5em !important; margin-bottom:1.5em !important; position: relative;"></div>').appendTo(g);
this.canvasContext=b.getContext("2d");c.data("jSignature.this",this);e.lineWidth=function(a,b){return a?a:Math.max(Math.round(b/400),2)}(e.lineWidth,b.width);this.lineCurveThreshold=3*e.lineWidth;e.cssclass&&""!=$.trim(e.cssclass)&&c.addClass(e.cssclass);this.fatFingerCompensation=0;g=function(a){var b,c,d=function(d){d=d.changedTouches&&0<d.changedTouches.length?d.changedTouches[0]:d;return new h(Math.round(d.pageX+b),Math.round(d.pageY+c)+a.fatFingerCompensation)},g=new v(750,function(){a.dataEngine.endStroke()});
this.drawEndHandler=function(b){if(!a.settings.readOnly){try{b.preventDefault()}catch(A){}g.clear();a.dataEngine.endStroke()}};this.drawStartHandler=function(e){if(!a.settings.readOnly){e.preventDefault();var m=$(a.canvas).offset();b=-1*m.left;c=-1*m.top;a.dataEngine.startStroke(d(e));g.kick()}};this.drawMoveHandler=function(b){a.settings.readOnly||(b.preventDefault(),a.dataEngine.inStroke&&(a.dataEngine.addToStroke(d(b)),g.kick()))};return this}.call({},this);(function(a,b,c){var d=this.canvas,g=
$(d);if(this.isCanvasEmulator)g.bind("mousemove.jSignature",c),g.bind("mouseup.jSignature",a),g.bind("mousedown.jSignature",b);else{var m="function"===typeof d.addEventListener;this.ontouchstart=function(g){d.onmousedown=d.onmouseup=d.onmousemove=void 0;this.fatFingerCompensation=e.minFatFingerCompensation&&-3*e.lineWidth>e.minFatFingerCompensation?-3*e.lineWidth:e.minFatFingerCompensation;b(g);m?(d.addEventListener("touchend",a),d.addEventListener("touchstart",b),d.addEventListener("touchmove",c)):
(d.ontouchend=a,d.ontouchstart=b,d.ontouchmove=c)};m?d.addEventListener("touchstart",this.ontouchstart):d.ontouchstart=ontouchstart;d.onmousedown=function(g){m?d.removeEventListener("touchstart",this.ontouchstart):d.ontouchstart=d.ontouchend=d.ontouchmove=void 0;b(g);d.onmousedown=b;d.onmouseup=a;d.onmousemove=c};window.navigator.msPointerEnabled&&(d.onmspointerdown=b,d.onmspointerup=a,d.onmspointermove=c)}}).call(this,g.drawEndHandler,g.drawStartHandler,g.drawMoveHandler);a["jSignature.windowmouseup"]=
d.subscribe("jSignature.windowmouseup",g.drawEndHandler);this.events.publish("jSignature.attachingEventHandlers");n.call(this,this,e.width.toString(10),"jSignature",d);this.resetCanvas(e.data);this.events.publish("jSignature.initialized");return this}function x(a){if(a.getContext)return!1;var b=a.ownerDocument.parentWindow,c=b.FlashCanvas?a.ownerDocument.parentWindow.FlashCanvas:"undefined"===typeof FlashCanvas?void 0:FlashCanvas;if(c){a=c.initElement(a);c=1;b&&b.screen&&b.screen.deviceXDPI&&b.screen.logicalXDPI&&
(c=1*b.screen.deviceXDPI/b.screen.logicalXDPI);if(1!==c)try{$(a).children("object").get(0).resize(Math.ceil(a.width*c),Math.ceil(a.height*c)),a.getContext("2d").scale(c,c)}catch(g){}return!0}throw Error("Canvas element does not support 2d context. jSignature cannot proceed.");}var v=function(a,b){var c;this.kick=function(){clearTimeout(c);c=setTimeout(b,a)};this.clear=function(){clearTimeout(c)};return this},u=function(a){this.topics={};this.context=a?a:this;this.publish=function(a,c,g,d){if(this.topics[a]){var b=
this.topics[a],e=Array.prototype.slice.call(arguments,1),f=[],h=[],t;var k=0;for(t=b.length;k<t;k++){var r=b[k];var D=r[0];r[1]&&(r[0]=function(){},f.push(k));h.push(D)}k=0;for(t=f.length;k<t;k++)b.splice(f[k],1);k=0;for(t=h.length;k<t;k++)h[k].apply(this.context,e)}};this.subscribe=function(a,c,g){this.topics[a]?this.topics[a].push([c,g]):this.topics[a]=[[c,g]];return{topic:a,callback:c}};this.unsubscribe=function(a){if(this.topics[a.topic])for(var b=this.topics[a.topic],g=0,d=b.length;g<d;g++)b[g]&&
b[g][0]===a.callback&&b.splice(g,1)}},y=function(a,b,c,g,d){a.beginPath();a.moveTo(b,c);a.lineTo(g,d);a.closePath();a.stroke()},C=function(a){var b=this.canvasContext,c=a.x[0];a=a.y[0];var g=this.settings.lineWidth,d=b.fillStyle;b.fillStyle=b.strokeStyle;b.fillRect(c+g/-2,a+g/-2,g,g);b.fillStyle=d},f=function(a,b){var c=new h(a.x[b-1],a.y[b-1]),g=new h(a.x[b],a.y[b]),d=c.getVectorToPoint(g);if(1<b){var e=new h(a.x[b-2],a.y[b-2]),f=e.getVectorToPoint(c);if(f.getLength()>this.lineCurveThreshold){var l=
2<b?(new h(a.x[b-3],a.y[b-3])).getVectorToPoint(e):new k(0,0);var n=.35*f.getLength(),t=f.angleTo(l.reverse()),p=d.angleTo(f.reverse());l=(new k(l.x+f.x,l.y+f.y)).resizeTo(Math.max(.05,t)*n);var r=(new k(f.x+d.x,f.y+d.y)).reverse().resizeTo(Math.max(.05,p)*n);f=this.canvasContext;n=e.x;p=e.y;t=c.x;var D=c.y,A=e.x+l.x;e=e.y+l.y;l=c.x+r.x;r=c.y+r.y;f.beginPath();f.moveTo(n,p);f.bezierCurveTo(A,e,l,r,t,D);f.closePath();f.stroke()}}d.getLength()<=this.lineCurveThreshold&&y(this.canvasContext,c.x,c.y,
g.x,g.y)},e=function(a){var b=a.x.length-1;if(0<b){var c=new h(a.x[b],a.y[b]),e=new h(a.x[b-1],a.y[b-1]),d=e.getVectorToPoint(c);if(d.getLength()>this.lineCurveThreshold)if(1<b){a=(new h(a.x[b-2],a.y[b-2])).getVectorToPoint(e);var f=(new k(a.x+d.x,a.y+d.y)).resizeTo(d.getLength()/2);d=this.canvasContext;a=e.x;b=e.y;var E=c.x,l=c.y,n=e.x+f.x;e=e.y+f.y;f=c.x;c=c.y;d.beginPath();d.moveTo(a,b);d.bezierCurveTo(n,e,f,c,E,l);d.closePath();d.stroke()}else y(this.canvasContext,e.x,e.y,c.x,c.y)}};w.prototype.resetCanvas=
function(a,b){var c=this.canvas,g=this.settings,d=this.canvasContext,m=this.isCanvasEmulator,h=c.width,l=c.height;b||d.clearRect(0,0,h+30,l+30);d.shadowColor=d.fillStyle=g["background-color"];m&&d.fillRect(0,0,h+30,l+30);d.lineWidth=Math.ceil(parseInt(g.lineWidth,10));d.lineCap=d.lineJoin="round";if(g.signatureLine){if(null!=g["decor-color"]){d.strokeStyle=g["decor-color"];d.shadowOffsetX=0;d.shadowOffsetY=0;var k=Math.round(l/5);y(d,1.5*k,l-k,h-1.5*k,l-k)}m||(d.shadowColor=d.strokeStyle,d.shadowOffsetX=
.5*d.lineWidth,d.shadowOffsetY=-.6*d.lineWidth,d.shadowBlur=0)}d.strokeStyle=g.color;a||(a=[]);d=this.dataEngine=new p(a,this,C,f,e);g.data=a;$(c).data("jSignature.data",a).data("jSignature.settings",g);d.changed=function(a,b,d){return function(){b.publish(d+".change");a.trigger("change")}}(this.$parent,this.events,"jSignature");d.changed();return!0};w.prototype.initializeCanvas=function(a){var b=document.createElement("canvas"),c=$(b);a.width===a.height&&"ratio"===a.height&&(a.width="100%");c.css({margin:0,
padding:0,border:"none",height:"ratio"!==a.height&&a.height?a.height.toString(10):1,width:"ratio"!==a.width&&a.width?a.width.toString(10):1,"-ms-touch-action":"none","touch-action":"none","background-color":a["background-color"]});c.appendTo(this.$parent);"ratio"===a.height?c.css("height",Math.round(c.width()/a.sizeRatio)):"ratio"===a.width&&c.css("width",Math.round(c.height()*a.sizeRatio));c.addClass("jSignature");b.width=c.width();b.height=c.height();this.isCanvasEmulator=x(b);b.onselectstart=function(a){a&&
a.preventDefault&&a.preventDefault();a&&a.stopPropagation&&a.stopPropagation();return!1};return b};(function(a){function b(a,b,d){var c=new Image,e=this;c.onload=function(){var a=e.getContext("2d"),b=a.shadowColor;a.shadowColor="transparent";a.drawImage(c,0,0,c.width<e.width?c.width:e.width,c.height<e.height?c.height:e.height);a.shadowColor=b};c.src="data:"+b+","+a}function c(a,b){this.find("canvas.jSignature").add(this.filter("canvas.jSignature")).data("jSignature.this").resetCanvas(a,b);return this}
function e(a,b){if(void 0===b&&"string"===typeof a&&"data:"===a.substr(0,5)&&(b=a.slice(5).split(",")[0],a=a.slice(6+b.length),b===a))return;var c=this.find("canvas.jSignature").add(this.filter("canvas.jSignature"));if(l.hasOwnProperty(b))0!==c.length&&l[b].call(c[0],a,b,function(a){return function(){return a.resetCanvas.apply(a,arguments)}}(c.data("jSignature.this")));else throw Error("jSignature is unable to find import plugin with for format '"+String(b)+"'");return this}var d=new u;(function(a,
b,c,d){var e,g=function(){a.publish(b+".parentresized")};c(d).bind("resize."+b,function(){e&&clearTimeout(e);e=setTimeout(g,500)}).bind("mouseup."+b,function(c){a.publish(b+".windowmouseup")})})(d,"jSignature",$,a);var f={},h={"default":function(a){return this.toDataURL()},"native":function(a){return a},image:function(a){a=this.toDataURL();if("string"===typeof a&&4<a.length&&"data:"===a.slice(0,5)&&-1!==a.indexOf(",")){var b=a.indexOf(",");return[a.slice(5,b),a.substr(b+1)]}return[]}},l={"native":function(a,
b,c){c(a)},image:b,"image/png;base64":b,"image/jpeg;base64":b,"image/jpg;base64":b},k=function(a){var b=!1;for(a=a.parentNode;a&&!b;)b=a.body,a=a.parentNode;return!b},n={"export":h,"import":l,instance:f},p={init:function(a){return this.each(function(){k(this)||new w(this,a,f)})},destroy:function(){return this.each(function(){if(!k(this)){var a=$(this).find("canvas").data("jSignature.this");if(a){a.$controlbarLower.remove();a.$controlbarUpper.remove();$(a.canvas).remove();for(var b in a.eventTokens)a.eventTokens.hasOwnProperty(b)&&
d.unsubscribe(a.eventTokens[b])}}})},getSettings:function(){return this.find("canvas.jSignature").add(this.filter("canvas.jSignature")).data("jSignature.this").settings},isModified:function(){return null!==this.find("canvas.jSignature").add(this.filter("canvas.jSignature")).data("jSignature.this").dataEngine._stroke},updateSetting:function(a,b,c){var d=this.find("canvas.jSignature").add(this.filter("canvas.jSignature")).data("jSignature.this");d.settings[a]=b;d.resetCanvas(c?null:d.settings.data,
!0);return d.settings[a]},clear:c,reset:c,addPlugin:function(a,b,c){n.hasOwnProperty(a)&&(n[a][b]=c);return this},listPlugins:function(a){var b=[];if(n.hasOwnProperty(a)){a=n[a];for(var c in a)a.hasOwnProperty(c)&&b.push(c)}return b},getData:function(a){var b=this.find("canvas.jSignature").add(this.filter("canvas.jSignature"));void 0===a&&(a="default");if(0!==b.length&&h.hasOwnProperty(a))return h[a].call(b.get(0),b.data("jSignature.data"),b.data("jSignature.settings"))},importData:e,setData:e,globalEvents:function(){return d},
disable:function(){this.find("input").attr("disabled",1);this.find("canvas.jSignature").addClass("disabled").data("jSignature.this").settings.readOnly=!0},enable:function(){this.find("input").removeAttr("disabled");this.find("canvas.jSignature").removeClass("disabled").data("jSignature.this").settings.readOnly=!1},events:function(){return this.find("canvas.jSignature").add(this.filter("canvas.jSignature")).data("jSignature.this").events}};$.fn.jSignature=function(a){if(a&&"object"!==typeof a){if("string"===
typeof a&&p[a])return p[a].apply(this,Array.prototype.slice.call(arguments,1));$.error("Method "+String(a)+" does not exist on jQuery.jSignature")}else return p.init.apply(this,arguments)}})(window)})();
(function(){function q(k,h,p){k=k.call(this);(function(h,k,p){h.events.subscribe(p+".change",function(){h.dataEngine.data.length?k.show():k.hide()})})(this,k,h);(function(h,k,p){var n=p+".undo";k.bind("click",function(){h.events.publish(n)});h.events.subscribe(n,function(){var k=h.dataEngine.data;k.length&&(k.pop(),h.resetCanvas(k))})})(this,k,this.events.topics.hasOwnProperty(h+".undo")?p:h)}$.fn.jSignature("addPlugin","instance","UndoButton",function(k){this.events.subscribe("jSignature.attachingEventHandlers",
function(){if(this.settings[k]){var h=this.settings[k];"function"!==typeof h&&(h=function(){var h=$('<input type="button" value="Undo last stroke" style="position:absolute;display:none;margin:0 !important;top:auto" />').appendTo(this.$controlbarLower),k=h.width();h.css("left",Math.round((this.canvas.width-k)/2));k!==h.width()&&h.width(k);return h});q.call(this,h,"jSignature",k)}})})})();
(function(){for(var q={},k={},h="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX".split(""),p=h.length/2,n=p-1;-1<n;n--)q[h[n]]=h[n+p],k[h[n+p]]=h[n];var w=function(e){e=e.split("");for(var a=e.length,b=1;b<a;b++)e[b]=q[e[b]];return e.join("")},x=function(e){for(var a=[],b=0,c=1,g=e.length,d,f,h=0;h<g;h++)d=Math.round(e[h]),f=d-b,b=d,0>f&&0<c?(c=-1,a.push("Z")):0<f&&0>c&&(c=1,a.push("Y")),d=Math.abs(f),d>=p?a.push(w(d.toString(p))):a.push(d.toString(p));return a.join("")},v=function(e){var a=
[];e=e.split("");for(var b=e.length,c,g=1,d=[],f=0,h=0;h<b;h++)c=e[h],c in q||"Z"===c||"Y"===c?(0!==d.length&&(d=parseInt(d.join(""),p)*g+f,a.push(d),f=d),"Z"===c?(g=-1,d=[]):"Y"===c?(g=1,d=[]):d=[c]):d.push(k[c]);a.push(parseInt(d.join(""),p)*g+f);return a},u=function(e){for(var a=[],b=e.length,c,g=0;g<b;g++)c=e[g],a.push(x(c.x)),a.push(x(c.y));return a.join("_")},y=function(e){var a=[];e=e.split("_");for(var b=e.length/2,c=0;c<b;c++)a.push({x:v(e[2*c]),y:v(e[2*c+1])});return a},C=function(e){return["image/jsignature;base30",
u(e)]},f=function(e,a,b){"string"===typeof e&&("image/jsignature;base30"===e.substring(0,23).toLowerCase()&&(e=e.substring(24)),b(y(e)))};if(null==this.jQuery)throw Error("We need jQuery for some of the functionality. jQuery is not detected. Failing to initialize...");(function(e){e=e.fn.jSignature;e("addPlugin","export","base30",C);e("addPlugin","export","image/jsignature;base30",C);e("addPlugin","import","base30",f);e("addPlugin","import","image/jsignature;base30",f)})(this.jQuery);this.jSignatureDebug&&
(this.jSignatureDebug.base30={remapTailChars:w,compressstrokeleg:x,uncompressstrokeleg:v,compressstrokes:u,uncompressstrokes:y,charmap:q})}).call("undefined"!==typeof window?window:this);
(function(){function q(f,e){this.x=f;this.y=e;this.reverse=function(){return new this.constructor(-1*this.x,-1*this.y)};this._length=null;this.getLength=function(){this._length||(this._length=Math.sqrt(Math.pow(this.x,2)+Math.pow(this.y,2)));return this._length};var a=function(a){return Math.round(a/Math.abs(a))};this.resizeTo=function(b){if(0===this.x&&0===this.y)this._length=0;else if(0===this.x)this._length=b,this.y=b*a(this.y);else if(0===this.y)this._length=b,this.x=b*a(this.x);else{var c=Math.abs(this.y/
this.x),e=Math.sqrt(Math.pow(b,2)/(1+Math.pow(c,2)));c*=e;this._length=b;this.x=e*a(this.x);this.y=c*a(this.y)}return this};this.angleTo=function(a){var b=this.getLength()*a.getLength();return 0===b?0:Math.acos(Math.min(Math.max((this.x*a.x+this.y*a.y)/b,-1),1))/Math.PI}}function k(f,e){this.x=f;this.y=e;this.getVectorToCoordinates=function(a,b){return new q(a-this.x,b-this.y)};this.getVectorFromCoordinates=function(a,b){return this.getVectorToCoordinates(a,b).reverse()};this.getVectorToPoint=function(a){return new q(a.x-
this.x,a.y-this.y)};this.getVectorFromPoint=function(a){return this.getVectorToPoint(a).reverse()}}function h(f,e){var a=Math.pow(10,e);return Math.round(f*a)/a}function p(f,e,a){e+=1;var b=new k(f.x[e-1],f.y[e-1]),c=new k(f.x[e],f.y[e]);c=b.getVectorToPoint(c);var g=new k(f.x[e-2],f.y[e-2]);b=g.getVectorToPoint(b);return b.getLength()>a?(a=2<e?(new k(f.x[e-3],f.y[e-3])).getVectorToPoint(g):new q(0,0),f=.35*b.getLength(),g=b.angleTo(a.reverse()),e=c.angleTo(b.reverse()),a=(new q(a.x+b.x,a.y+b.y)).resizeTo(Math.max(.05,
g)*f),c=(new q(b.x+c.x,b.y+c.y)).reverse().resizeTo(Math.max(.05,e)*f),c=new q(b.x+c.x,b.y+c.y),["c",h(a.x,2),h(a.y,2),h(c.x,2),h(c.y,2),h(b.x,2),h(b.y,2)]):["l",h(b.x,2),h(b.y,2)]}function n(f,e){var a=f.x.length-1,b=new k(f.x[a],f.y[a]),c=new k(f.x[a-1],f.y[a-1]);b=c.getVectorToPoint(b);if(1<a&&b.getLength()>e){a=(new k(f.x[a-2],f.y[a-2])).getVectorToPoint(c);c=b.angleTo(a.reverse());var g=.35*b.getLength();a=(new q(a.x+b.x,a.y+b.y)).resizeTo(Math.max(.05,c)*g);return["c",h(a.x,2),h(a.y,2),h(b.x,
2),h(b.y,2),h(b.x,2),h(b.y,2)]}return["l",h(b.x,2),h(b.y,2)]}function w(f,e,a){e=["M",h(f.x[0]-e,2),h(f.y[0]-a,2)];a=1;for(var b=f.x.length-1;a<b;a++)e.push.apply(e,p(f,a,1));0<b?e.push.apply(e,n(f,a,1)):0===b&&e.push.apply(e,["l",1,1]);return e.join(" ")}function x(f){for(var e=[],a=[["fill",void 0,"none"],["stroke","color","#000000"],["stroke-width","lineWidth",2],["stroke-linecap",void 0,"round"],["stroke-linejoin",void 0,"round"]],b=a.length-1;0<=b;b--){var c=a[b][1],g=a[b][2];e.push(a[b][0]+
'="'+(c in f&&f[c]?f[c]:g)+'"')}return e.join(" ")}function v(f,e){var a=['<?xml version="1.0" encoding="UTF-8" standalone="no"?>','<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'],b,c=f.length,g,d=[],h=[],k=g=b=0,l=0,p=[];if(0!==c){for(b=0;b<c;b++){g=f[b];var n=[],q={x:[],y:[]};l=0;for(k=g.x.length;l<k;l++)n.push({x:g.x[l],y:g.y[l]});n=simplify(n,.7,!0);l=0;for(k=n.length;l<k;l++)q.x.push(n[l].x),q.y.push(n[l].y);g=q;p.push(g);d=d.concat(g.x);h=
h.concat(g.y)}c=Math.min.apply(null,d)-1;b=Math.max.apply(null,d)+1;d=Math.min.apply(null,h)-1;h=Math.max.apply(null,h)+1;k=0>c?0:c;l=0>d?0:d;b-=c;g=h-d}a.push('<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="'+b.toString()+'" height="'+g.toString()+'">');b=0;for(c=p.length;b<c;b++)g=p[b],a.push("<path "+x(e)+' d="'+w(g,k,l)+'"/>');a.push("</svg>");return a.join("")}function u(f,e){return["image/svg+xml",v(f,e)]}function y(f,e){return["image/svg+xml;base64",C(v(f,e))]}(function(f,e){"use strict";
f.simplify=function(a,b,c){b=b!==e?b*b:1;if(!c){var g=a.length,d=a[0],f=[d];for(c=1;c<g;c++){var h=a[c];var k=h.x-d.x,n=h.y-d.y;k*k+n*n>b&&(f.push(h),d=h)}a=(d!==h&&f.push(h),f)}h=a;c=h.length;g=new (typeof Uint8Array!=e+""?Uint8Array:Array)(c);d=0;f=c-1;var p,q=[],r=[],y=[];for(g[d]=g[f]=1;f;){n=0;for(k=d+1;k<f;k++){var A=h[k];var z=h[d],w=h[f],u=z.x,v=z.y;z=w.x-u;var B=w.y-v;if(0!==z||0!==B){var x=((A.x-u)*z+(A.y-v)*B)/(z*z+B*B);1<x?(u=w.x,v=w.y):0<x&&(u+=z*x,v+=B*x)}A=(z=A.x-u,B=A.y-v,z*z+B*B);
A>n&&(p=k,n=A)}n>b&&(g[p]=1,q.push(d),r.push(p),q.push(p),r.push(f));d=q.pop();f=r.pop()}for(k=0;k<c;k++)g[k]&&y.push(h[k]);return a=y,a}})(window);if("function"!==typeof C)var C=function(f){var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".split(""),a=0,b=0,c=[];do{var g=f.charCodeAt(a++);var d=f.charCodeAt(a++);var h=f.charCodeAt(a++);var k=g<<16|d<<8|h;g=k>>18&63;d=k>>12&63;h=k>>6&63;k&=63;c[b++]=e[g]+e[d]+e[h]+e[k]}while(a<f.length);e=c.join("");f=f.length%3;return(f?e.slice(0,
f-3):e)+"===".slice(f||3)};if("undefined"===typeof $)throw Error("We need jQuery for some of the functionality. jQuery is not detected. Failing to initialize...");(function(f){f=f.fn.jSignature;f("addPlugin","export","svg",u);f("addPlugin","export","image/svg+xml",u);f("addPlugin","export","svgbase64",y);f("addPlugin","export","image/svg+xml;base64",y)})($)})();

View File

View File

@@ -0,0 +1,21 @@
import base64
import io
from django import template
from django.utils.encoding import iri_to_uri
from jsignature.utils import draw_signature
register = template.Library()
@register.filter
def signature_base64(value):
if value is None or not isinstance(value, str):
return ""
in_mem_file = io.BytesIO()
draw_signature(value).save(in_mem_file, format="PNG")
in_mem_file.seek(0)
return "data:image/png;base64,{}".format(
iri_to_uri(base64.b64encode(in_mem_file.read()).decode('utf8'))
)

View File

@@ -15,25 +15,31 @@ def draw_signature(data, as_file=False):
if `as_file` is True, a temp file is returned instead of Image instance
"""
def _remove_empty_pts(pt):
return {
'x': list(filter(lambda n: n is not None, pt['x'])),
'y': list(filter(lambda n: n is not None, pt['y']))
}
if type(data) is str:
drawing = json.loads(data)
drawing = json.loads(data, object_hook=_remove_empty_pts)
elif type(data) is list:
drawing = data
else:
raise ValueError
# Compute box
width = max(chain(*[d['x'] for d in drawing])) + 10
height = max(chain(*[d['y'] for d in drawing])) + 10
width = int(round(max(chain(*[d['x'] for d in drawing])))) + 10
height = int(round(max(chain(*[d['y'] for d in drawing])))) + 10
# Draw image
im = Image.new("RGBA", (width*AA, height*AA))
im = Image.new("RGBA", (width * AA, height * AA))
draw = ImageDraw.Draw(im)
for line in drawing:
len_line = len(line['x'])
points = [(line['x'][i]*AA, line['y'][i]*AA)
points = [(line['x'][i] * AA, line['y'][i] * AA)
for i in range(0, len_line)]
draw.line(points, fill="#000", width=2*AA)
draw.line(points, fill="#000", width=2 * AA)
im = ImageOps.expand(im)
# Smart crop
bbox = im.getbbox()

View File

@@ -11,7 +11,7 @@ from django.core import validators
from django.core.exceptions import ValidationError
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from jsignature.settings import JSIGNATURE_DEFAULT_CONFIG
JSIGNATURE_EMPTY_VALUES = validators.EMPTY_VALUES + ('[]', )

View File

@@ -1,2 +1,3 @@
pillow
pyquery
pyquery<1.4.2; python_version<="2.7"
pyquery>=1.4.2; python_version>"2.7"

View File

@@ -5,7 +5,7 @@ here = os.path.abspath(os.path.dirname(__file__))
setup(
name='django-jsignature',
version='0.9',
version='0.12',
author='Florent Lebreton',
author_email='florent.lebreton@makina-corpus.com',
url='https://github.com/fle/django-jsignature',
@@ -14,8 +14,8 @@ setup(
long_description=open(os.path.join(here, 'README.rst')).read() + '\n\n' +
open(os.path.join(here, 'CHANGES')).read(),
license='LPGL, see LICENSE file.',
install_requires=['Django', 'pillow'],
packages=find_packages(),
install_requires=['Django>=1.11', 'pillow', 'pyquery>=1.4.2'],
packages=find_packages(exclude=['example_project*', 'tests']),
include_package_data=True,
zip_safe=False,
classifiers=[
@@ -29,13 +29,13 @@ setup(
'Framework :: Django :: 2.2',
'Framework :: Django :: 3.0',
'Development Status :: 5 - Production/Stable',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5'
'Programming Language :: Python :: 3.6'
'Programming Language :: Python :: 3.7'
'Programming Language :: Python :: 3.8'
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
],
)

View File

@@ -2,6 +2,8 @@ import os
DEBUG = True
USE_TZ = True
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
SECRET_KEY = 'thisisntactuallysecretatall'

View File

@@ -0,0 +1,12 @@
import json
from django.test import SimpleTestCase
from jsignature.templatetags.jsignature_filters import signature_base64
FAULTY_SIGNATURE = [{"x": [120.00675156801722, 116.11464070635179, 114.16858527551909], "y": [83.0316983821957, 76.54484694608666, 224.44505968937275]}, {"x": [161.52260075911508, 165.4147116207805, 173.84761848772226, 184.2265807854967, 201.0923945193802], "y": [84.97775381302841, 79.78827266414118, 72.00405094081033, 62.922458930257676, 56.43560749414864]}]
DUMMY_STR_VALUE = json.dumps(FAULTY_SIGNATURE)
class TemplateFilterFailedTest(SimpleTestCase):
def test_throw_error(self):
output = signature_base64(DUMMY_STR_VALUE)

View File

@@ -24,13 +24,13 @@ class JSignatureFieldTest(SimpleTestCase):
def test_to_python_correct_value_python(self):
f = JSignatureField()
val = [{"x": [1, 2], "y": [3, 4]}]
self.assertEquals(val, f.to_python(val))
self.assertEqual(val, f.to_python(val))
def test_to_python_correct_value_json(self):
f = JSignatureField()
val = [{"x": [1, 2], "y": [3, 4]}]
val_str = '[{"x":[1,2], "y":[3,4]}]'
self.assertEquals(val, f.to_python(val_str))
self.assertEqual(val, f.to_python(val_str))
def test_to_python_incorrect_value(self):
f = JSignatureField()
@@ -47,7 +47,7 @@ class JSignatureFieldTest(SimpleTestCase):
val = [{"x": [1, 2], "y": [3, 4]}]
val_prep = f.get_prep_value(val)
self.assertIsInstance(val_prep, string_types)
self.assertEquals(val, json.loads(val_prep))
self.assertEqual(val, json.loads(val_prep))
def test_get_prep_value_correct_values_json(self):
f = JSignatureField()
@@ -55,7 +55,7 @@ class JSignatureFieldTest(SimpleTestCase):
val_str = '[{"x":[1,2], "y":[3,4]}]'
val_prep = f.get_prep_value(val_str)
self.assertIsInstance(val_prep, string_types)
self.assertEquals(val, json.loads(val_prep))
self.assertEqual(val, json.loads(val_prep))
def test_get_prep_value_incorrect_values(self):
f = JSignatureField()

19
tests/test_filter.py Normal file
View File

@@ -0,0 +1,19 @@
import json
from django.test import SimpleTestCase
from jsignature.templatetags.jsignature_filters import signature_base64
DUMMY_VALUE = [{"x": [205, 210], "y": [59, 63]},
{"x": [205, 207], "y": [67, 64]}]
DUMMY_STR_VALUE = json.dumps(DUMMY_VALUE)
class TemplateFilterTest(SimpleTestCase):
def test_inputs_bad_type_value(self):
self.assertEqual(signature_base64(object()), '')
self.assertEqual(signature_base64(None), '')
def test_outputs_as_base64(self):
output = signature_base64(DUMMY_STR_VALUE)
self.assertEqual(output, "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANwAAABNCAYAAADNYApnAAABIklEQVR4nO3ZP0odURjG4d/ce5dgE3EHgXTp07iD6B7cga3YpUoh7iFrCti4BkGw0OLMoEVIEW4yxPs81ZxzGPial+/8KQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPh3ttW0dhFwCN4GbbNaFXAAlrCdVh/m791KtcC7tmkE7mP1XP2sPs1rQgd7NjVCd1TdNkL3UJ2tWRQciovqqRG8q0aXc6aDPZsat5RVX6q7Rui+zXPbX/wD/KFla7mc306qH9XneazLwZ4s3eu8eqwuV6wF3rXllvK4um9sI7/Oc7s8hMNeLVf/3xthu5nHzmzwF0y9drjrxvPAMgcA/7/lWUBnAwAAAAAAfucFwt4TZmdXW+AAAAAASUVORK5CYII=")

View File

@@ -19,7 +19,7 @@ class JSignatureFormFieldTest(SimpleTestCase):
def test_to_python_correct_values(self):
f = JSignatureField()
val = '[{"x":[1,2], "y":[3,4]}]'
self.assertEquals([{'x': [1, 2], 'y': [3, 4]}], f.to_python(val))
self.assertEqual([{'x': [1, 2], 'y': [3, 4]}], f.to_python(val))
def test_to_python_incorrect_values(self):
f = JSignatureField()

View File

@@ -25,7 +25,7 @@ class JSignatureWidgetTest(SimpleTestCase):
self.assertIn('jSignature.min.js', media_js_str)
self.assertIn('django_jsignature.js', media_js_str)
media_css = list(media.render_css())
self.assertEquals([], media_css)
self.assertEqual([], media_css)
@override_settings(JSIGNATURE_JQUERY='admin')
def test_media_in_admin(self):
@@ -34,12 +34,19 @@ class JSignatureWidgetTest(SimpleTestCase):
media_js = list(media.render_js())
self.assertEqual(4, len(media_js))
@override_settings(JSIGNATURE_JQUERY='https://code.jquery.com/jquery-3.5.0.min.js')
def test_media_custom_jquery(self):
widget = JSignatureWidget()
media = widget.media
media_js = list(media.render_js())
self.assertEqual(3, len(media_js))
def test_init(self):
w = JSignatureWidget()
self.assertEquals({}, w.jsignature_attrs)
self.assertEqual({}, w.jsignature_attrs)
given_attrs = {'width': 300, 'height': 100}
w = JSignatureWidget(jsignature_attrs=given_attrs)
self.assertEquals(given_attrs, w.jsignature_attrs)
self.assertEqual(given_attrs, w.jsignature_attrs)
def test_build_jsignature_id(self):
w = JSignatureWidget()
@@ -62,7 +69,7 @@ class JSignatureWidgetTest(SimpleTestCase):
val = [{"x": [1, 2], "y": [3, 4]}]
val_prep = w.prep_value(val)
self.assertIsInstance(val_prep, string_types)
self.assertEquals(val, json.loads(val_prep))
self.assertEqual(val, json.loads(val_prep))
def test_prep_value_correct_values_json(self):
w = JSignatureWidget()
@@ -70,7 +77,7 @@ class JSignatureWidgetTest(SimpleTestCase):
val_str = '[{"x":[1,2], "y":[3,4]}]'
val_prep = w.prep_value(val_str)
self.assertIsInstance(val_prep, string_types)
self.assertEquals(val, json.loads(val_prep))
self.assertEqual(val, json.loads(val_prep))
def test_prep_value_incorrect_values(self):
w = JSignatureWidget()

26
tox.ini
View File

@@ -1,23 +1,25 @@
[tox]
envlist =
{py27,py35,py36}-django111,
{py35,py36,py37,py38}-django22,
{py36,py37,py38}-django{30,master}
{py36,py37,py38,py39,py310}-django22,
{py36,py37,py38,py39,py310}-django32,
{py38,py39,py310}-django{40,master},
py310-djangomaster
[testenv]
deps=
django111: Django>=1.11,<2.0
django22: Django>=2.2,<3.0
django30: Django>=3.0,<4.0
djangomaster: https://github.com/django/django/archive/master.tar.gz
django32: Django>=3.2,<4.0
django40: Django>=4.0,<4.1
djangomaster: https://github.com/django/django/archive/main.tar.gz
-r requirements.txt
coverage
commands= coverage run ./runtests.py
[travis:env]
DJANGO =
1.11: django111
2.2: django22
3.0: django30
master: djangomaster
[gh-actions]
python =
3.6: py36-django{22,32}
3.7: py37-django{22,32}
3.8: py38-django{22,32,40}
3.9: py39-django{22,32,40}
3.10: py310-django{22,32,40,master}