@technical-team/jsignature
Version:
jQuery plugin for handling digital signatures
234 lines (219 loc) • 7.84 kB
JavaScript
/** @preserve
jSignature v2 jSignature's custom "base30" format export and import plugins.
*/
/**
Copyright (c) 2011 Willow Systems Corp http://willow-systems.com
MIT License <http://www.opensource.org/licenses/mit-license.php>
*/
;(function(){
var chunkSeparator = '_'
, charmap = {} // {'1':'g','2':'h','3':'i','4':'j','5':'k','6':'l','7':'m','8':'n','9':'o','a':'p','b':'q','c':'r','d':'s','e':'t','f':'u','0':'v'}
, charmap_reverse = {} // will be filled by 'uncompress*" function
// need to split below for IE7 (possibly others), which does not understand string[position] it seems (returns undefined)
, allchars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX'.split('')
, bitness = allchars.length / 2
, minus = 'Z'
, plus = 'Y'
for(var i = bitness-1; i > -1; i--){
charmap[allchars[i]] = allchars[i+bitness]
charmap_reverse[allchars[i+bitness]] = allchars[i]
}
var remapTailChars = function(number){
// for any given number as string, returning string with trailing chars remapped something like so:
// '345' -> '3de'
var chars = number.split('')
, l = chars.length
// we are skipping first char. standard hex number char = delimiter
for (var i = 1; i < l; i++ ){
chars[i] = charmap[chars[i]]
}
return chars.join('')
}
, compressstrokeleg = function(data){
// we convert half-stroke (only 'x' series or only 'y' series of numbers)
// data is like this:
// [517,516,514,513,513,513,514,516,519,524,529,537,541,543,544,544,539,536]
// that is converted into this:
// "5agm12100p1235584210m53"
// each number in the chain is converted such:
// - find diff from previous number
// - first significant digit is kept as digit char. digit char = start of new number.
// - consecutive numbers are mapped to letters, where 1 to 9 are A to I, 0 is O
// Sign changes are denoted by "P" - plus, "M" for minus.
var answer = []
, lastwhole = 0
, last = 0
, lastpolarity = 1
, l = data.length
, nwhole, n, absn
for(var i = 0; i < l; i++){
// we start with whole coordinates for each point
// coords are converted into series of vectors:
// [512, 514, 520]
// [512, +2, +6]
nwhole = Math.round(data[i])
n = nwhole - lastwhole
lastwhole = nwhole
// inserting sign change when needed.
if (n < 0 && lastpolarity > 0) {
lastpolarity = -1
answer.push(minus)
}
else if (n > 0 && lastpolarity < 0) {
lastpolarity = 1
answer.push(plus)
}
// since we have dealt with sign. let's absolute the value.
absn = Math.abs(n)
// adding number to list We convert these to Hex before storing on the string.
if (absn >= bitness) {
answer.push(remapTailChars(absn.toString(bitness)))
} else {
answer.push(absn.toString(bitness))
}
}
return answer.join('')
}
, uncompressstrokeleg = function(datastring){
// we convert half-stroke (only 'x' series or only 'y' series of numbers)
// datastring like this:
// "5agm12100p1235584210m53"
// is converted into this:
// [517,516,514,513,513,513,514,516,519,524,529,537,541,543,544,544,539,536]
// each number in the chain is converted such:
// - digit char = start of new whole number. Alpha chars except "p","m" are numbers in hiding.
// These consecutive digist expressed as alphas mapped back to digit char.
// resurrected number is the diff between this point and prior coord.
// - running polaritiy is attached to the number.
// - we undiff (signed number + prior coord) the number.
// - if char 'm','p', flip running polarity
var answer = []
, chars = datastring.split('')
, l = chars.length
, ch
, polarity = 1
, partial = []
, preprewhole = 0
, prewhole
for(var i = 0; i < l; i++){
ch = chars[i]
if (ch in charmap || ch === minus || ch === plus){
// this is new number - start of a new whole number.
// before we can deal with it, we need to flush out what we already
// parsed out from string, but keep in limbo, waiting for this sign
// that prior number is done.
// we deal with 3 numbers here:
// 1. start of this number - a diff from previous number to
// whole, new number, which we cannot do anything with cause
// we don't know its ending yet.
// 2. number that we now realize have just finished parsing = prewhole
// 3. number we keep around that came before prewhole = preprewhole
if (partial.length !== 0) {
// yep, we have some number parts in there.
prewhole = parseInt( partial.join(''), bitness) * polarity + preprewhole
answer.push( prewhole )
preprewhole = prewhole
}
if (ch === minus){
polarity = -1
partial = []
} else if (ch === plus){
polarity = 1
partial = []
} else {
// now, let's start collecting parts for the new number:
partial = [ch]
}
} else /* alphas replacing digits */ {
// more parts for the new number
partial.push(charmap_reverse[ch])
}
}
// we always will have something stuck in partial
// because we don't have closing delimiter
answer.push( parseInt( partial.join(''), bitness ) * polarity + preprewhole )
return answer
}
, compressstrokes = function(data){
var answer = []
, l = data.length
, stroke
for(var i = 0; i < l; i++){
stroke = data[i]
answer.push(compressstrokeleg(stroke.x))
answer.push(compressstrokeleg(stroke.y))
}
return answer.join(chunkSeparator)
}
, uncompressstrokes = function(datastring){
var data = []
, chunks = datastring.split(chunkSeparator)
, l = chunks.length / 2
for (var i = 0; i < l; i++){
data.push({
'x':uncompressstrokeleg(chunks[i*2])
, 'y':uncompressstrokeleg(chunks[i*2+1])
})
}
return data
}
, acceptedformat = 'image/jsignature;base30'
, pluginCompressor = function(data){
return [acceptedformat , compressstrokes(data)]
}
, pluginDecompressor = function(data, formattype, importcallable){
if (typeof data !== 'string') return
if (data.substring(0, acceptedformat.length).toLowerCase() === acceptedformat) {
data = data.substring(acceptedformat.length + 1) // chopping off "," there
}
importcallable( uncompressstrokes(data) )
}
, Initializer = function($){
var mothership = $.fn['jSignature']
mothership(
'addPlugin'
,'export'
,'base30' // alias
,pluginCompressor
)
mothership(
'addPlugin'
,'export'
,acceptedformat // full name
,pluginCompressor
)
mothership(
'addPlugin'
,'import'
,'base30' // alias
,pluginDecompressor
)
mothership(
'addPlugin'
,'import'
,acceptedformat // full name
,pluginDecompressor
)
}
// //Because plugins are minified together with jSignature, multiple defines per (minified) file blow up and dont make sense
// //Need to revisit this later.
// if ( typeof define === "function" && define.amd != null) {
// // AMD-loader compatible resource declaration
// // you need to call this one with jQuery as argument.
// define(function(){return Initializer} )
// } else {
// global-polluting outcome.
if(this.jQuery == null) {throw new Error("We need jQuery for some of the functionality. jQuery is not detected. Failing to initialize...")}
Initializer(this.jQuery)
// }
if (this.jSignatureDebug) {
this.jSignatureDebug['base30'] = {
'remapTailChars':remapTailChars
, 'compressstrokeleg':compressstrokeleg
, 'uncompressstrokeleg':uncompressstrokeleg
, 'compressstrokes':compressstrokes
, 'uncompressstrokes':uncompressstrokes
, 'charmap': charmap
}
}
}).call(typeof window !== 'undefined'? window : this);