diff --git a/web_digital_sign/__init__.py b/web_digital_sign/__init__.py index 1656f744..b597d73a 100644 --- a/web_digital_sign/__init__.py +++ b/web_digital_sign/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. import users -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file + diff --git a/web_digital_sign/__openerp__.py b/web_digital_sign/__openerp__.py index a2f39803..1b0cd679 100644 --- a/web_digital_sign/__openerp__.py +++ b/web_digital_sign/__openerp__.py @@ -1,16 +1,27 @@ # -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. +# See LICENSE file for full copyright and licensing details. { "name" : "Web Digital Signature 9.0", - "version" : "1.0", - "author" : "Serpent Consulting Services Pvt. Ltd.", + "version" : "9.0.1.0.0", + 'author': 'Serpent Consulting Services Pvt. Ltd.', "category": '', + 'license': 'AGPL-3', 'complexity': "easy", 'depends': ['web'], - "description": """ - This module provides the functionality to store digital signature image for a record. - The example can be seen into the User's form view where we have added a test field under signature. - """, + 'description': ''' + This module provides the functionality to store digital signature + for a record. + -> This module is helpfull to make your business process a little + bit more faster & makes it more user friendly by providing you + digital signature functionality on your documents. + -> It is touch screen enable so user can add signature with touch + devices. + -> Digital signature can be very usefull for documents such as + sale orders, purchase orders, inovoices, payslips, procurement + receipts, etc. + The example can be seen into the User's form view where we have + added a test field under signature. + ''', 'data': [ 'views/we_digital_sign_view.xml', 'users_view.xml' @@ -21,4 +32,3 @@ 'auto_install': False, } -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/web_digital_sign/static/description/icon.png b/web_digital_sign/static/description/icon.png index 53dd30d9..d15532dd 100644 Binary files a/web_digital_sign/static/description/icon.png and b/web_digital_sign/static/description/icon.png differ diff --git a/web_digital_sign/static/description/img/1.png b/web_digital_sign/static/description/img/1.png new file mode 100644 index 00000000..fa633751 Binary files /dev/null and b/web_digital_sign/static/description/img/1.png differ diff --git a/web_digital_sign/static/description/img/2.png b/web_digital_sign/static/description/img/2.png new file mode 100644 index 00000000..39f2775f Binary files /dev/null and b/web_digital_sign/static/description/img/2.png differ diff --git a/web_digital_sign/static/description/img/3.png b/web_digital_sign/static/description/img/3.png new file mode 100644 index 00000000..4bb84b43 Binary files /dev/null and b/web_digital_sign/static/description/img/3.png differ diff --git a/web_digital_sign/static/description/index.html b/web_digital_sign/static/description/index.html new file mode 100644 index 00000000..d8b7f323 --- /dev/null +++ b/web_digital_sign/static/description/index.html @@ -0,0 +1,68 @@ +
+
+
+

Web Digital Signature

+

This module provides the functionality to store digital signature for a record

+
    +
  • This module is helpfull to make your business process a little bit more faster & makes it more user friendly by providing you digital signature functionality on your documents.
  • +
  • It is touch screen enable so user can add signature with touch devices.
  • +
  • Digital signature can be very usefull for documents such as sale orders, purchase orders, inovoices, payslips, procurement receipts, etc.
  • +
+
+
+

+

Usage

+

To use this module, you need to add widget="signature" to your binary field in your view.

+ +
+
+
+
+
+

+

Digital Signature View

+

User can store their digital signature + in the binary field, as you can see image. +

+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+

Draw Signature

+

As shown in the image, user can add a signature using mouse, pen, or finger.

+
+
+
+
+
+
+

Clear Digital Signature

+

User can clear signature using clear button and it will re-initialize the signature.

+
+
+
+ +
+
+
+
+ + +
+

For any questions, support and development

+

+ +  Contact Us  +

+
diff --git a/web_digital_sign/static/lib/excanvas.js b/web_digital_sign/static/lib/excanvas.js deleted file mode 100644 index 367764b4..00000000 --- a/web_digital_sign/static/lib/excanvas.js +++ /dev/null @@ -1,924 +0,0 @@ -// Copyright 2006 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - - -// Known Issues: -// -// * Patterns are not implemented. -// * Radial gradient are not implemented. The VML version of these look very -// different from the canvas one. -// * Clipping paths are not implemented. -// * Coordsize. The width and height attribute have higher priority than the -// width and height style values which isn't correct. -// * Painting mode isn't implemented. -// * Canvas width/height should is using content-box by default. IE in -// Quirks mode will draw the canvas using border-box. Either change your -// doctype to HTML5 -// (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype) -// or use Box Sizing Behavior from WebFX -// (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html) -// * Non uniform scaling does not correctly scale strokes. -// * Optimize. There is always room for speed improvements. - -// Only add this code if we do not already have a canvas implementation -if (!document.createElement('canvas').getContext) { - -(function() { - - // alias some functions to make (compiled) code shorter - var m = Math; - var mr = m.round; - var ms = m.sin; - var mc = m.cos; - var abs = m.abs; - var sqrt = m.sqrt; - - // this is used for sub pixel precision - var Z = 10; - var Z2 = Z / 2; - - /** - * This funtion is assigned to the elements as element.getContext(). - * @this {HTMLElement} - * @return {CanvasRenderingContext2D_} - */ - function getContext() { - return this.context_ || - (this.context_ = new CanvasRenderingContext2D_(this)); - } - - var slice = Array.prototype.slice; - - /** - * Binds a function to an object. The returned function will always use the - * passed in {@code obj} as {@code this}. - * - * Example: - * - * g = bind(f, obj, a, b) - * g(c, d) // will do f.call(obj, a, b, c, d) - * - * @param {Function} f The function to bind the object to - * @param {Object} obj The object that should act as this when the function - * is called - * @param {*} var_args Rest arguments that will be used as the initial - * arguments when the function is called - * @return {Function} A new function that has bound this - */ - function bind(f, obj, var_args) { - var a = slice.call(arguments, 2); - return function() { - return f.apply(obj, a.concat(slice.call(arguments))); - }; - } - - var G_vmlCanvasManager_ = { - init: function(opt_doc) { - if (/MSIE/.test(navigator.userAgent) && !window.opera) { - var doc = opt_doc || document; - // Create a dummy element so that IE will allow canvas elements to be - // recognized. - doc.createElement('canvas'); - doc.attachEvent('onreadystatechange', bind(this.init_, this, doc)); - } - }, - - init_: function(doc) { - // create xmlns - if (!doc.namespaces['g_vml_']) { - doc.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml', - '#default#VML'); - - } - if (!doc.namespaces['g_o_']) { - doc.namespaces.add('g_o_', 'urn:schemas-microsoft-com:office:office', - '#default#VML'); - } - - // Setup default CSS. Only add one style sheet per document - if (!doc.styleSheets['ex_canvas_']) { - var ss = doc.createStyleSheet(); - ss.owningElement.id = 'ex_canvas_'; - ss.cssText = 'canvas{display:inline-block;overflow:hidden;' + - // default size is 300x150 in Gecko and Opera - 'text-align:left;width:300px;height:150px}' + - 'g_vml_\\:*{behavior:url(#default#VML)}' + - 'g_o_\\:*{behavior:url(#default#VML)}'; - - } - - // find all canvas elements - var els = doc.getElementsByTagName('canvas'); - for (var i = 0; i < els.length; i++) { - this.initElement(els[i]); - } - }, - - /** - * Public initializes a canvas element so that it can be used as canvas - * element from now on. This is called automatically before the page is - * loaded but if you are creating elements using createElement you need to - * make sure this is called on the element. - * @param {HTMLElement} el The canvas element to initialize. - * @return {HTMLElement} the element that was created. - */ - initElement: function(el) { - if (!el.getContext) { - - el.getContext = getContext; - - // Remove fallback content. There is no way to hide text nodes so we - // just remove all childNodes. We could hide all elements and remove - // text nodes but who really cares about the fallback content. - el.innerHTML = ''; - - // do not use inline function because that will leak memory - el.attachEvent('onpropertychange', onPropertyChange); - el.attachEvent('onresize', onResize); - - var attrs = el.attributes; - if (attrs.width && attrs.width.specified) { - // TODO: use runtimeStyle and coordsize - // el.getContext().setWidth_(attrs.width.nodeValue); - el.style.width = attrs.width.nodeValue + 'px'; - } else { - el.width = el.clientWidth; - } - if (attrs.height && attrs.height.specified) { - // TODO: use runtimeStyle and coordsize - // el.getContext().setHeight_(attrs.height.nodeValue); - el.style.height = attrs.height.nodeValue + 'px'; - } else { - el.height = el.clientHeight; - } - //el.getContext().setCoordsize_() - } - return el; - } - }; - - function onPropertyChange(e) { - var el = e.srcElement; - - switch (e.propertyName) { - case 'width': - el.style.width = el.attributes.width.nodeValue + 'px'; - el.getContext().clearRect(); - break; - case 'height': - el.style.height = el.attributes.height.nodeValue + 'px'; - el.getContext().clearRect(); - break; - } - } - - function onResize(e) { - var el = e.srcElement; - if (el.firstChild) { - el.firstChild.style.width = el.clientWidth + 'px'; - el.firstChild.style.height = el.clientHeight + 'px'; - } - } - - G_vmlCanvasManager_.init(); - - // precompute "00" to "FF" - var dec2hex = []; - for (var i = 0; i < 16; i++) { - for (var j = 0; j < 16; j++) { - dec2hex[i * 16 + j] = i.toString(16) + j.toString(16); - } - } - - function createMatrixIdentity() { - return [ - [1, 0, 0], - [0, 1, 0], - [0, 0, 1] - ]; - } - - function matrixMultiply(m1, m2) { - var result = createMatrixIdentity(); - - for (var x = 0; x < 3; x++) { - for (var y = 0; y < 3; y++) { - var sum = 0; - - for (var z = 0; z < 3; z++) { - sum += m1[x][z] * m2[z][y]; - } - - result[x][y] = sum; - } - } - return result; - } - - function copyState(o1, o2) { - o2.fillStyle = o1.fillStyle; - o2.lineCap = o1.lineCap; - o2.lineJoin = o1.lineJoin; - o2.lineWidth = o1.lineWidth; - o2.miterLimit = o1.miterLimit; - o2.shadowBlur = o1.shadowBlur; - o2.shadowColor = o1.shadowColor; - o2.shadowOffsetX = o1.shadowOffsetX; - o2.shadowOffsetY = o1.shadowOffsetY; - o2.strokeStyle = o1.strokeStyle; - o2.globalAlpha = o1.globalAlpha; - o2.arcScaleX_ = o1.arcScaleX_; - o2.arcScaleY_ = o1.arcScaleY_; - o2.lineScale_ = o1.lineScale_; - } - - function processStyle(styleString) { - var str, alpha = 1; - - styleString = String(styleString); - if (styleString.substring(0, 3) == 'rgb') { - var start = styleString.indexOf('(', 3); - var end = styleString.indexOf(')', start + 1); - var guts = styleString.substring(start + 1, end).split(','); - - str = '#'; - for (var i = 0; i < 3; i++) { - str += dec2hex[Number(guts[i])]; - } - - if (guts.length == 4 && styleString.substr(3, 1) == 'a') { - alpha = guts[3]; - } - } else { - str = styleString; - } - - return {color: str, alpha: alpha}; - } - - function processLineCap(lineCap) { - switch (lineCap) { - case 'butt': - return 'flat'; - case 'round': - return 'round'; - case 'square': - default: - return 'square'; - } - } - - /** - * This class implements CanvasRenderingContext2D interface as described by - * the WHATWG. - * @param {HTMLElement} surfaceElement The element that the 2D context should - * be associated with - */ - function CanvasRenderingContext2D_(surfaceElement) { - this.m_ = createMatrixIdentity(); - - this.mStack_ = []; - this.aStack_ = []; - this.currentPath_ = []; - - // Canvas context properties - this.strokeStyle = '#000'; - this.fillStyle = '#000'; - - this.lineWidth = 1; - this.lineJoin = 'miter'; - this.lineCap = 'butt'; - this.miterLimit = Z * 1; - this.globalAlpha = 1; - this.canvas = surfaceElement; - - var el = surfaceElement.ownerDocument.createElement('div'); - el.style.width = surfaceElement.clientWidth + 'px'; - el.style.height = surfaceElement.clientHeight + 'px'; - el.style.overflow = 'hidden'; - el.style.position = 'absolute'; - surfaceElement.appendChild(el); - - this.element_ = el; - this.arcScaleX_ = 1; - this.arcScaleY_ = 1; - this.lineScale_ = 1; - } - - var contextPrototype = CanvasRenderingContext2D_.prototype; - contextPrototype.clearRect = function() { - this.element_.innerHTML = ''; - }; - - contextPrototype.beginPath = function() { - // TODO: Branch current matrix so that save/restore has no effect - // as per safari docs. - this.currentPath_ = []; - }; - - contextPrototype.moveTo = function(aX, aY) { - var p = this.getCoords_(aX, aY); - this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y}); - this.currentX_ = p.x; - this.currentY_ = p.y; - }; - - contextPrototype.lineTo = function(aX, aY) { - var p = this.getCoords_(aX, aY); - this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y}); - - this.currentX_ = p.x; - this.currentY_ = p.y; - }; - - contextPrototype.bezierCurveTo = function(aCP1x, aCP1y, - aCP2x, aCP2y, - aX, aY) { - var p = this.getCoords_(aX, aY); - var cp1 = this.getCoords_(aCP1x, aCP1y); - var cp2 = this.getCoords_(aCP2x, aCP2y); - bezierCurveTo(this, cp1, cp2, p); - }; - - // Helper function that takes the already fixed cordinates. - function bezierCurveTo(self, cp1, cp2, p) { - self.currentPath_.push({ - type: 'bezierCurveTo', - cp1x: cp1.x, - cp1y: cp1.y, - cp2x: cp2.x, - cp2y: cp2.y, - x: p.x, - y: p.y - }); - self.currentX_ = p.x; - self.currentY_ = p.y; - } - - contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) { - // the following is lifted almost directly from - // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes - - var cp = this.getCoords_(aCPx, aCPy); - var p = this.getCoords_(aX, aY); - - var cp1 = { - x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_), - y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_) - }; - var cp2 = { - x: cp1.x + (p.x - this.currentX_) / 3.0, - y: cp1.y + (p.y - this.currentY_) / 3.0 - }; - - bezierCurveTo(this, cp1, cp2, p); - }; - - contextPrototype.arc = function(aX, aY, aRadius, - aStartAngle, aEndAngle, aClockwise) { - aRadius *= Z; - var arcType = aClockwise ? 'at' : 'wa'; - - var xStart = aX + mc(aStartAngle) * aRadius - Z2; - var yStart = aY + ms(aStartAngle) * aRadius - Z2; - - var xEnd = aX + mc(aEndAngle) * aRadius - Z2; - var yEnd = aY + ms(aEndAngle) * aRadius - Z2; - - // IE won't render arches drawn counter clockwise if xStart == xEnd. - if (xStart == xEnd && !aClockwise) { - xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something - // that can be represented in binary - } - - var p = this.getCoords_(aX, aY); - var pStart = this.getCoords_(xStart, yStart); - var pEnd = this.getCoords_(xEnd, yEnd); - - this.currentPath_.push({type: arcType, - x: p.x, - y: p.y, - radius: aRadius, - xStart: pStart.x, - yStart: pStart.y, - xEnd: pEnd.x, - yEnd: pEnd.y}); - - }; - - contextPrototype.rect = function(aX, aY, aWidth, aHeight) { - this.moveTo(aX, aY); - this.lineTo(aX + aWidth, aY); - this.lineTo(aX + aWidth, aY + aHeight); - this.lineTo(aX, aY + aHeight); - this.closePath(); - }; - - contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) { - var oldPath = this.currentPath_; - this.beginPath(); - - this.moveTo(aX, aY); - this.lineTo(aX + aWidth, aY); - this.lineTo(aX + aWidth, aY + aHeight); - this.lineTo(aX, aY + aHeight); - this.closePath(); - this.stroke(); - - this.currentPath_ = oldPath; - }; - - contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) { - var oldPath = this.currentPath_; - this.beginPath(); - - this.moveTo(aX, aY); - this.lineTo(aX + aWidth, aY); - this.lineTo(aX + aWidth, aY + aHeight); - this.lineTo(aX, aY + aHeight); - this.closePath(); - this.fill(); - - this.currentPath_ = oldPath; - }; - - contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) { - var gradient = new CanvasGradient_('gradient'); - gradient.x0_ = aX0; - gradient.y0_ = aY0; - gradient.x1_ = aX1; - gradient.y1_ = aY1; - return gradient; - }; - - contextPrototype.createRadialGradient = function(aX0, aY0, aR0, - aX1, aY1, aR1) { - var gradient = new CanvasGradient_('gradientradial'); - gradient.x0_ = aX0; - gradient.y0_ = aY0; - gradient.r0_ = aR0; - gradient.x1_ = aX1; - gradient.y1_ = aY1; - gradient.r1_ = aR1; - return gradient; - }; - - contextPrototype.drawImage = function(image, var_args) { - var dx, dy, dw, dh, sx, sy, sw, sh; - - // to find the original width we overide the width and height - var oldRuntimeWidth = image.runtimeStyle.width; - var oldRuntimeHeight = image.runtimeStyle.height; - image.runtimeStyle.width = 'auto'; - image.runtimeStyle.height = 'auto'; - - // get the original size - var w = image.width; - var h = image.height; - - // and remove overides - image.runtimeStyle.width = oldRuntimeWidth; - image.runtimeStyle.height = oldRuntimeHeight; - - if (arguments.length == 3) { - dx = arguments[1]; - dy = arguments[2]; - sx = sy = 0; - sw = dw = w; - sh = dh = h; - } else if (arguments.length == 5) { - dx = arguments[1]; - dy = arguments[2]; - dw = arguments[3]; - dh = arguments[4]; - sx = sy = 0; - sw = w; - sh = h; - } else if (arguments.length == 9) { - sx = arguments[1]; - sy = arguments[2]; - sw = arguments[3]; - sh = arguments[4]; - dx = arguments[5]; - dy = arguments[6]; - dw = arguments[7]; - dh = arguments[8]; - } else { - throw Error('Invalid number of arguments'); - } - - var d = this.getCoords_(dx, dy); - - var w2 = sw / 2; - var h2 = sh / 2; - - var vmlStr = []; - - var W = 10; - var H = 10; - - // For some reason that I've now forgotten, using divs didn't work - vmlStr.push(' ' , - '', - ''); - - this.element_.insertAdjacentHTML('BeforeEnd', - vmlStr.join('')); - }; - - contextPrototype.stroke = function(aFill) { - var lineStr = []; - var lineOpen = false; - var a = processStyle(aFill ? this.fillStyle : this.strokeStyle); - var color = a.color; - var opacity = a.alpha * this.globalAlpha; - - var W = 10; - var H = 10; - - lineStr.push(''); - - if (!aFill) { - var lineWidth = this.lineScale_ * this.lineWidth; - - // VML cannot correctly render a line if the width is less than 1px. - // In that case, we dilute the color to make the line look thinner. - if (lineWidth < 1) { - opacity *= lineWidth; - } - - lineStr.push( - '' - ); - } else if (typeof this.fillStyle == 'object') { - var fillStyle = this.fillStyle; - var angle = 0; - var focus = {x: 0, y: 0}; - - // additional offset - var shift = 0; - // scale factor for offset - var expansion = 1; - - if (fillStyle.type_ == 'gradient') { - var x0 = fillStyle.x0_ / this.arcScaleX_; - var y0 = fillStyle.y0_ / this.arcScaleY_; - var x1 = fillStyle.x1_ / this.arcScaleX_; - var y1 = fillStyle.y1_ / this.arcScaleY_; - var p0 = this.getCoords_(x0, y0); - var p1 = this.getCoords_(x1, y1); - var dx = p1.x - p0.x; - var dy = p1.y - p0.y; - angle = Math.atan2(dx, dy) * 180 / Math.PI; - - // The angle should be a non-negative number. - if (angle < 0) { - angle += 360; - } - - // Very small angles produce an unexpected result because they are - // converted to a scientific notation string. - if (angle < 1e-6) { - angle = 0; - } - } else { - var p0 = this.getCoords_(fillStyle.x0_, fillStyle.y0_); - var width = max.x - min.x; - var height = max.y - min.y; - focus = { - x: (p0.x - min.x) / width, - y: (p0.y - min.y) / height - }; - - width /= this.arcScaleX_ * Z; - height /= this.arcScaleY_ * Z; - var dimension = m.max(width, height); - shift = 2 * fillStyle.r0_ / dimension; - expansion = 2 * fillStyle.r1_ / dimension - shift; - } - - // We need to sort the color stops in ascending order by offset, - // otherwise IE won't interpret it correctly. - var stops = fillStyle.colors_; - stops.sort(function(cs1, cs2) { - return cs1.offset - cs2.offset; - }); - - var length = stops.length; - var color1 = stops[0].color; - var color2 = stops[length - 1].color; - var opacity1 = stops[0].alpha * this.globalAlpha; - var opacity2 = stops[length - 1].alpha * this.globalAlpha; - - var colors = []; - for (var i = 0; i < length; i++) { - var stop = stops[i]; - colors.push(stop.offset * expansion + shift + ' ' + stop.color); - } - - // When colors attribute is used, the meanings of opacity and o:opacity2 - // are reversed. - lineStr.push(''); - } else { - lineStr.push(''); - } - - lineStr.push(''); - - this.element_.insertAdjacentHTML('beforeEnd', lineStr.join('')); - }; - - contextPrototype.fill = function() { - this.stroke(true); - } - - contextPrototype.closePath = function() { - this.currentPath_.push({type: 'close'}); - }; - - /** - * @private - */ - contextPrototype.getCoords_ = function(aX, aY) { - var m = this.m_; - return { - x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2, - y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2 - } - }; - - contextPrototype.save = function() { - var o = {}; - copyState(this, o); - this.aStack_.push(o); - this.mStack_.push(this.m_); - this.m_ = matrixMultiply(createMatrixIdentity(), this.m_); - }; - - contextPrototype.restore = function() { - copyState(this.aStack_.pop(), this); - this.m_ = this.mStack_.pop(); - }; - - function matrixIsFinite(m) { - for (var j = 0; j < 3; j++) { - for (var k = 0; k < 2; k++) { - if (!isFinite(m[j][k]) || isNaN(m[j][k])) { - return false; - } - } - } - return true; - } - - function setM(ctx, m, updateLineScale) { - if (!matrixIsFinite(m)) { - return; - } - ctx.m_ = m; - - if (updateLineScale) { - // Get the line scale. - // Determinant of this.m_ means how much the area is enlarged by the - // transformation. So its square root can be used as a scale factor - // for width. - var det = m[0][0] * m[1][1] - m[0][1] * m[1][0]; - ctx.lineScale_ = sqrt(abs(det)); - } - } - - contextPrototype.translate = function(aX, aY) { - var m1 = [ - [1, 0, 0], - [0, 1, 0], - [aX, aY, 1] - ]; - - setM(this, matrixMultiply(m1, this.m_), false); - }; - - contextPrototype.rotate = function(aRot) { - var c = mc(aRot); - var s = ms(aRot); - - var m1 = [ - [c, s, 0], - [-s, c, 0], - [0, 0, 1] - ]; - - setM(this, matrixMultiply(m1, this.m_), false); - }; - - contextPrototype.scale = function(aX, aY) { - this.arcScaleX_ *= aX; - this.arcScaleY_ *= aY; - var m1 = [ - [aX, 0, 0], - [0, aY, 0], - [0, 0, 1] - ]; - - setM(this, matrixMultiply(m1, this.m_), true); - }; - - contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) { - var m1 = [ - [m11, m12, 0], - [m21, m22, 0], - [dx, dy, 1] - ]; - - setM(this, matrixMultiply(m1, this.m_), true); - }; - - contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) { - var m = [ - [m11, m12, 0], - [m21, m22, 0], - [dx, dy, 1] - ]; - - setM(this, m, true); - }; - - /******** STUBS ********/ - contextPrototype.clip = function() { - // TODO: Implement - }; - - contextPrototype.arcTo = function() { - // TODO: Implement - }; - - contextPrototype.createPattern = function() { - return new CanvasPattern_; - }; - - // Gradient / Pattern Stubs - function CanvasGradient_(aType) { - this.type_ = aType; - this.x0_ = 0; - this.y0_ = 0; - this.r0_ = 0; - this.x1_ = 0; - this.y1_ = 0; - this.r1_ = 0; - this.colors_ = []; - } - - CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) { - aColor = processStyle(aColor); - this.colors_.push({offset: aOffset, - color: aColor.color, - alpha: aColor.alpha}); - }; - - function CanvasPattern_() {} - - // set up externs - G_vmlCanvasManager = G_vmlCanvasManager_; - CanvasRenderingContext2D = CanvasRenderingContext2D_; - CanvasGradient = CanvasGradient_; - CanvasPattern = CanvasPattern_; - -})(); - -} // if diff --git a/web_digital_sign/static/lib/jSignature/jSignatureCustom.js b/web_digital_sign/static/lib/jSignature/jSignatureCustom.js new file mode 100644 index 00000000..bb99cac8 --- /dev/null +++ b/web_digital_sign/static/lib/jSignature/jSignatureCustom.js @@ -0,0 +1,1392 @@ +/** @preserve +jSignature v2 "${buildDate}" "${commitID}" +Copyright (c) 2012 Willow Systems Corp http://willow-systems.com +Copyright (c) 2010 Brinley Ang http://www.unbolt.net +MIT License + +*/ +;(function() { + +var apinamespace = 'jSignature' + +/** +Allows one to delay certain eventual action by setting up a timer for it and allowing one to delay it +by "kick"ing it. Sorta like "kick the can down the road" + +@public +@class +@param +@returns {Type} +*/ +var KickTimerClass = function(time, callback) { + var timer + this.kick = function() { + clearTimeout(timer) + timer = setTimeout( + callback + , time + ) + } + this.clear = function() { + clearTimeout(timer) + } + return this +} + +var PubSubClass = function(context){ + 'use strict' + /* @preserve + ----------------------------------------------------------------------------------------------- + JavaScript PubSub library + 2012 (c) Willow Systems Corp (www.willow-systems.com) + based on Peter Higgins (dante@dojotoolkit.org) + Loosely based on Dojo publish/subscribe API, limited in scope. Rewritten blindly. + Original is (c) Dojo Foundation 2004-2010. Released under either AFL or new BSD, see: + http://dojofoundation.org/license for more information. + ----------------------------------------------------------------------------------------------- + */ + this.topics = {} + // here we choose what will be "this" for the called events. + // if context is defined, it's context. Else, 'this' is this instance of PubSub + this.context = context ? context : this + /** + * Allows caller to emit an event and pass arguments to event listeners. + * @public + * @function + * @param topic {String} Name of the channel on which to voice this event + * @param **arguments Any number of arguments you want to pass to the listeners of this event. + */ + this.publish = function(topic, arg1, arg2, etc) { + 'use strict' + if (this.topics[topic]) { + var currentTopic = this.topics[topic] + , args = Array.prototype.slice.call(arguments, 1) + , toremove = [] + , fn + , i, l + , pair + + for (i = 0, l = currentTopic.length; i < l; i++) { + pair = currentTopic[i] // this is a [function, once_flag] array + fn = pair[0] + if (pair[1] /* 'run once' flag set */){ + pair[0] = function(){} + toremove.push(i) + } + fn.apply(this.context, args) + } + for (i = 0, l = toremove.length; i < l; i++) { + currentTopic.splice(toremove[i], 1) + } + } + } + /** + * Allows listener code to subscribe to channel and be called when data is available + * @public + * @function + * @param topic {String} Name of the channel on which to voice this event + * @param callback {Function} Executable (function pointer) that will be ran when event is voiced on this channel. + * @param once {Boolean} (optional. False by default) Flag indicating if the function is to be triggered only once. + * @returns {Object} A token object that cen be used for unsubscribing. + */ + this.subscribe = function(topic, callback, once) { + 'use strict' + if (!this.topics[topic]) { + this.topics[topic] = [[callback, once]]; + } else { + this.topics[topic].push([callback,once]); + } + return { + "topic": topic, + "callback": callback + }; + }; + /** + * Allows listener code to unsubscribe from a channel + * @public + * @function + * @param token {Object} A token object that was returned by `subscribe` method + */ + this.unsubscribe = function(token) { + if (this.topics[token.topic]) { + var currentTopic = this.topics[token.topic] + + for (var i = 0, l = currentTopic.length; i < l; i++) { + if (currentTopic[i][0] === token.callback) { + currentTopic.splice(i, 1) + } + } + } + } +} + +/// Returns front, back and "decor" colors derived from element (as jQuery obj) +function getColors($e){ + var tmp + , undef + , frontcolor = $e.css('color') + , backcolor + , e = $e[0] + + var toOfDOM = false + while(e && !backcolor && !toOfDOM){ + try{ + tmp = $(e).css('background-color') + } catch (ex) { + tmp = 'transparent' + } + if (tmp !== 'transparent' && tmp !== 'rgba(0, 0, 0, 0)'){ + backcolor = tmp + } + toOfDOM = e.body + e = e.parentNode + } + + var rgbaregex = /rgb[a]*\((\d+),\s*(\d+),\s*(\d+)/ // modern browsers + , hexregex = /#([AaBbCcDdEeFf\d]{2})([AaBbCcDdEeFf\d]{2})([AaBbCcDdEeFf\d]{2})/ // IE 8 and less. + , frontcolorcomponents + + // Decomposing Front color into R, G, B ints + tmp = undef + tmp = frontcolor.match(rgbaregex) + if (tmp){ + frontcolorcomponents = {'r':parseInt(tmp[1],10),'g':parseInt(tmp[2],10),'b':parseInt(tmp[3],10)} + } else { + tmp = frontcolor.match(hexregex) + if (tmp) { + frontcolorcomponents = {'r':parseInt(tmp[1],16),'g':parseInt(tmp[2],16),'b':parseInt(tmp[3],16)} + } + } +// if(!frontcolorcomponents){ +// frontcolorcomponents = {'r':255,'g':255,'b':255} +// } + + var backcolorcomponents + // Decomposing back color into R, G, B ints + if(!backcolor){ + // HIghly unlikely since this means that no background styling was applied to any element from here to top of dom. + // we'll pick up back color from front color + if(frontcolorcomponents){ + if (Math.max.apply(null, [frontcolorcomponents.r, frontcolorcomponents.g, frontcolorcomponents.b]) > 127){ + backcolorcomponents = {'r':0,'g':0,'b':0} + } else { + backcolorcomponents = {'r':255,'g':255,'b':255} + } + } else { + // arg!!! front color is in format we don't understand (hsl, named colors) + // Let's just go with white background. + backcolorcomponents = {'r':255,'g':255,'b':255} + } + } else { + tmp = undef + tmp = backcolor.match(rgbaregex) + if (tmp){ + backcolorcomponents = {'r':parseInt(tmp[1],10),'g':parseInt(tmp[2],10),'b':parseInt(tmp[3],10)} + } else { + tmp = backcolor.match(hexregex) + if (tmp) { + backcolorcomponents = {'r':parseInt(tmp[1],16),'g':parseInt(tmp[2],16),'b':parseInt(tmp[3],16)} + } + } +// if(!backcolorcomponents){ +// backcolorcomponents = {'r':0,'g':0,'b':0} +// } + } + + // Deriving Decor color + // THis is LAZY!!!! Better way would be to use HSL and adjust luminocity. However, that could be an overkill. + + var toRGBfn = function(o){return 'rgb(' + [o.r, o.g, o.b].join(', ') + ')'} + , decorcolorcomponents + , frontcolorbrightness + , adjusted + + if (frontcolorcomponents && backcolorcomponents){ + var backcolorbrightness = Math.max.apply(null, [frontcolorcomponents.r, frontcolorcomponents.g, frontcolorcomponents.b]) + + frontcolorbrightness = Math.max.apply(null, [backcolorcomponents.r, backcolorcomponents.g, backcolorcomponents.b]) + adjusted = Math.round(frontcolorbrightness + (-1 * (frontcolorbrightness - backcolorbrightness) * 0.75)) // "dimming" the difference between pen and back. + decorcolorcomponents = {'r':adjusted,'g':adjusted,'b':adjusted} // always shade of gray + } else if (frontcolorcomponents) { + frontcolorbrightness = Math.max.apply(null, [frontcolorcomponents.r, frontcolorcomponents.g, frontcolorcomponents.b]) + var polarity = +1 + if (frontcolorbrightness > 127){ + polarity = -1 + } + // shifting by 25% (64 points on RGB scale) + adjusted = Math.round(frontcolorbrightness + (polarity * 96)) // "dimming" the pen's color by 75% to get decor color. + decorcolorcomponents = {'r':adjusted,'g':adjusted,'b':adjusted} // always shade of gray + } else { + decorcolorcomponents = {'r':191,'g':191,'b':191} // always shade of gray + } + + return { + 'color': frontcolor + , 'background-color': backcolorcomponents? toRGBfn(backcolorcomponents) : backcolor + , 'decor-color': toRGBfn(decorcolorcomponents) + } +} + +function Vector(x,y){ + this.x = x + this.y = y + this.reverse = function(){ + return new this.constructor( + this.x * -1 + , this.y * -1 + ) + } + this._length = null + this.getLength = function(){ + if (!this._length){ + this._length = Math.sqrt( Math.pow(this.x, 2) + Math.pow(this.y, 2) ) + } + return this._length + } + + var polarity = function (e){ + return Math.round(e / Math.abs(e)) + } + this.resizeTo = function(length){ + // proportionally changes x,y such that the hypotenuse (vector length) is = new length + if (this.x === 0 && this.y === 0){ + this._length = 0 + } else if (this.x === 0){ + this._length = length + this.y = length * polarity(this.y) + } else if(this.y === 0){ + this._length = length + this.x = length * polarity(this.x) + } else { + var proportion = Math.abs(this.y / this.x) + , x = Math.sqrt(Math.pow(length, 2) / (1 + Math.pow(proportion, 2))) + , y = proportion * x + this._length = length + this.x = x * polarity(this.x) + this.y = y * polarity(this.y) + } + return this + } + + /** + * Calculates the angle between 'this' vector and another. + * @public + * @function + * @returns {Number} The angle between the two vectors as measured in PI. + */ + this.angleTo = function(vectorB) { + var divisor = this.getLength() * vectorB.getLength() + if (divisor === 0) { + return 0 + } else { + // JavaScript floating point math is screwed up. + // because of it, the core of the formula can, on occasion, have values + // over 1.0 and below -1.0. + return Math.acos( + Math.min( + Math.max( + ( this.x * vectorB.x + this.y * vectorB.y ) / divisor + , -1.0 + ) + , 1.0 + ) + ) / Math.PI + } + } +} + +function Point(x,y){ + this.x = x + this.y = y + + this.getVectorToCoordinates = function (x, y) { + return new Vector(x - this.x, y - this.y) + } + this.getVectorFromCoordinates = function (x, y) { + return this.getVectorToCoordinates(x, y).reverse() + } + this.getVectorToPoint = function (point) { + return new Vector(point.x - this.x, point.y - this.y) + } + this.getVectorFromPoint = function (point) { + return this.getVectorToPoint(point).reverse() + } +} + +/* + * About data structure: + * We don't store / deal with "pictures" this signature capture code captures "vectors" + * + * We don't store bitmaps. We store "strokes" as arrays of arrays. (Actually, arrays of objects containing arrays of coordinates. + * + * Stroke = mousedown + mousemoved * n (+ mouseup but we don't record that as that was the "end / lack of movement" indicator) + * + * Vectors = not classical vectors where numbers indicated shift relative last position. Our vectors are actually coordinates against top left of canvas. + * we could calc the classical vectors, but keeping the the actual coordinates allows us (through Math.max / min) + * to calc the size of resulting drawing very quickly. If we want classical vectors later, we can always get them in backend code. + * + * So, the data structure: + * + * var data = [ + * { // stroke starts + * x : [101, 98, 57, 43] // x points + * , y : [1, 23, 65, 87] // y points + * } // stroke ends + * , { // stroke starts + * x : [55, 56, 57, 58] // x points + * , y : [101, 97, 54, 4] // y points + * } // stroke ends + * , { // stroke consisting of just a dot + * x : [53] // x points + * , y : [151] // y points + * } // stroke ends + * ] + * + * we don't care or store stroke width (it's canvas-size-relative), color, shadow values. These can be added / changed on whim post-capture. + * + */ +function DataEngine(storageObject, context, startStrokeFn, addToStrokeFn, endStrokeFn){ + this.data = storageObject // we expect this to be an instance of Array + this.context = context + + if (storageObject.length){ + // we have data to render + var numofstrokes = storageObject.length + , stroke + , numofpoints + + for (var i = 0; i < numofstrokes; i++){ + stroke = storageObject[i] + numofpoints = stroke.x.length + startStrokeFn.call(context, stroke) + for(var j = 1; j < numofpoints; j++){ + addToStrokeFn.call(context, stroke, j) + } + endStrokeFn.call(context, stroke) + } + } + + this.changed = function(){} + + this.startStrokeFn = startStrokeFn + this.addToStrokeFn = addToStrokeFn + this.endStrokeFn = endStrokeFn + + this.inStroke = false + + this._lastPoint = null + this._stroke = null + this.startStroke = function(point){ + if(point && typeof(point.x) == "number" && typeof(point.y) == "number"){ + this._stroke = {'x':[point.x], 'y':[point.y]} + this.data.push(this._stroke) + this._lastPoint = point + this.inStroke = true + // 'this' does not work same inside setTimeout( + var stroke = this._stroke + , fn = this.startStrokeFn + , context = this.context + setTimeout( + // some IE's don't support passing args per setTimeout API. Have to create closure every time instead. + function() {fn.call(context, stroke)} + , 3 + ) + return point + } else { + return null + } + } + // that "5" at the very end of this if is important to explain. + // we do NOT render links between two captured points (in the middle of the stroke) if the distance is shorter than that number. + // not only do we NOT render it, we also do NOT capture (add) these intermediate points to storage. + // when clustering of these is too tight, it produces noise on the line, which, because of smoothing, makes lines too curvy. + // maybe, later, we can expose this as a configurable setting of some sort. + this.addToStroke = function(point){ + if (this.inStroke && + typeof(point.x) === "number" && + typeof(point.y) === "number" && + // calculates absolute shift in diagonal pixels away from original point + (Math.abs(point.x - this._lastPoint.x) + Math.abs(point.y - this._lastPoint.y)) > 4 + ){ + var positionInStroke = this._stroke.x.length + this._stroke.x.push(point.x) + this._stroke.y.push(point.y) + this._lastPoint = point + + var stroke = this._stroke + , fn = this.addToStrokeFn + , context = this.context + setTimeout( + // some IE's don't support passing args per setTimeout API. Have to create closure every time instead. + function() {fn.call(context, stroke, positionInStroke)} + , 3 + ) + return point + } else { + return null + } + } + this.endStroke = function(){ + var c = this.inStroke + this.inStroke = false + this._lastPoint = null + if (c){ + var stroke = this._stroke + , fn = this.endStrokeFn // 'this' does not work same inside setTimeout( + , context = this.context + , changedfn = this.changed + setTimeout( + // some IE's don't support passing args per setTimeout API. Have to create closure every time instead. + function(){ + fn.call(context, stroke) + changedfn.call(context) + } + , 3 + ) + return true + } else { + return null + } + } +} + +var basicDot = function(ctx, x, y, size){ + var fillStyle = ctx.fillStyle + ctx.fillStyle = ctx.strokeStyle + ctx.fillRect(x + size / -2 , y + size / -2, size, size) + ctx.fillStyle = fillStyle +} +, basicLine = function(ctx, startx, starty, endx, endy){ + ctx.beginPath() + ctx.moveTo(startx, starty) + ctx.lineTo(endx, endy) + ctx.stroke() +} +, basicCurve = function(ctx, startx, starty, endx, endy, cp1x, cp1y, cp2x, cp2y){ + ctx.beginPath() + ctx.moveTo(startx, starty) + ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, endx, endy) + ctx.stroke() +} +, strokeStartCallback = function(stroke) { + // this = jSignatureClass instance + basicDot(this.canvasContext, stroke.x[0], stroke.y[0], this.settings.lineWidth) +} +, strokeAddCallback = function(stroke, positionInStroke){ + // this = jSignatureClass instance + + // Because we are funky this way, here we draw TWO curves. + // 1. POSSIBLY "this line" - spanning from point right before us, to this latest point. + // 2. POSSIBLY "prior curve" - spanning from "latest point" to the one before it. + + // Why you ask? + // long lines (ones with many pixels between them) do not look good when they are part of a large curvy stroke. + // You know, the jaggedy crocodile spine instead of a pretty, smooth curve. Yuck! + // We want to approximate pretty curves in-place of those ugly lines. + // To approximate a very nice curve we need to know the direction of line before and after. + // Hence, on long lines we actually wait for another point beyond it to come back from + // mousemoved before we draw this curve. + + // So for "prior curve" to be calc'ed we need 4 points + // A, B, C, D (we are on D now, A is 3 points in the past.) + // and 3 lines: + // pre-line (from points A to B), + // this line (from points B to C), (we call it "this" because if it was not yet, it's the only one we can draw for sure.) + // post-line (from points C to D) (even through D point is 'current' we don't know how we can draw it yet) + // + // Well, actually, we don't need to *know* the point A, just the vector A->B + var Cpoint = new Point(stroke.x[positionInStroke-1], stroke.y[positionInStroke-1]) + , Dpoint = new Point(stroke.x[positionInStroke], stroke.y[positionInStroke]) + , CDvector = Cpoint.getVectorToPoint(Dpoint) + + // Again, we have a chance here to draw TWO things: + // BC Curve (only if it's long, because if it was short, it was drawn by previous callback) and + // CD Line (only if it's short) + + // So, let's start with BC curve. + // if there is only 2 points in stroke array, we don't have "history" long enough to have point B, let alone point A. + // Falling through to drawing line CD is proper, as that's the only line we have points for. + if(positionInStroke > 1) { + // we are here when there are at least 3 points in stroke array. + var Bpoint = new Point(stroke.x[positionInStroke-2], stroke.y[positionInStroke-2]) + , BCvector = Bpoint.getVectorToPoint(Cpoint) + , ABvector + if(BCvector.getLength() > this.lineCurveThreshold){ + // Yey! Pretty curves, here we come! + if(positionInStroke > 2) { + // we are here when at least 4 points in stroke array. + ABvector = (new Point(stroke.x[positionInStroke-3], stroke.y[positionInStroke-3])).getVectorToPoint(Bpoint) + } else { + ABvector = new Vector(0,0) + } + + var minlenfraction = 0.05 + , maxlen = BCvector.getLength() * 0.35 + , ABCangle = BCvector.angleTo(ABvector.reverse()) + , BCDangle = CDvector.angleTo(BCvector.reverse()) + , BCP1vector = new Vector(ABvector.x + BCvector.x, ABvector.y + BCvector.y).resizeTo( + Math.max(minlenfraction, ABCangle) * maxlen + ) + , CCP2vector = (new Vector(BCvector.x + CDvector.x, BCvector.y + CDvector.y)).reverse().resizeTo( + Math.max(minlenfraction, BCDangle) * maxlen + ) + + basicCurve( + this.canvasContext + , Bpoint.x + , Bpoint.y + , Cpoint.x + , Cpoint.y + , Bpoint.x + BCP1vector.x + , Bpoint.y + BCP1vector.y + , Cpoint.x + CCP2vector.x + , Cpoint.y + CCP2vector.y + ) + } + } + if(CDvector.getLength() <= this.lineCurveThreshold){ + basicLine( + this.canvasContext + , Cpoint.x + , Cpoint.y + , Dpoint.x + , Dpoint.y + ) + } +} +, strokeEndCallback = function(stroke){ + // this = jSignatureClass instance + + // Here we tidy up things left unfinished in last strokeAddCallback run. + + // What's POTENTIALLY left unfinished there is the curve between the last points + // in the stroke, if the len of that line is more than lineCurveThreshold + // If the last line was shorter than lineCurveThreshold, it was drawn there, and there + // is nothing for us here to do. + // We can also be called when there is only one point in the stroke (meaning, the + // stroke was just a dot), in which case, again, there is nothing for us to do. + + // So for "this curve" to be calc'ed we need 3 points + // A, B, C + // and 2 lines: + // pre-line (from points A to B), + // this line (from points B to C) + // Well, actually, we don't need to *know* the point A, just the vector A->B + // so, we really need points B, C and AB vector. + var positionInStroke = stroke.x.length - 1 + + if (positionInStroke > 0){ + // there are at least 2 points in the stroke.we are in business. + var Cpoint = new Point(stroke.x[positionInStroke], stroke.y[positionInStroke]) + , Bpoint = new Point(stroke.x[positionInStroke-1], stroke.y[positionInStroke-1]) + , BCvector = Bpoint.getVectorToPoint(Cpoint) + , ABvector + if (BCvector.getLength() > this.lineCurveThreshold){ + // yep. This one was left undrawn in prior callback. Have to draw it now. + if (positionInStroke > 1){ + // we have at least 3 elems in stroke + ABvector = (new Point(stroke.x[positionInStroke-2], stroke.y[positionInStroke-2])).getVectorToPoint(Bpoint) + var BCP1vector = new Vector(ABvector.x + BCvector.x, ABvector.y + BCvector.y).resizeTo(BCvector.getLength() / 2) + basicCurve( + this.canvasContext + , Bpoint.x + , Bpoint.y + , Cpoint.x + , Cpoint.y + , Bpoint.x + BCP1vector.x + , Bpoint.y + BCP1vector.y + , Cpoint.x + , Cpoint.y + ) + } else { + // Since there is no AB leg, there is no curve to draw. This line is still "long" but no curve. + basicLine( + this.canvasContext + , Bpoint.x + , Bpoint.y + , Cpoint.x + , Cpoint.y + ) + } + } + } +} + + +/* +var getDataStats = function(){ + var strokecnt = strokes.length + , stroke + , pointid + , pointcnt + , x, y + , maxX = Number.NEGATIVE_INFINITY + , maxY = Number.NEGATIVE_INFINITY + , minX = Number.POSITIVE_INFINITY + , minY = Number.POSITIVE_INFINITY + for(strokeid = 0; strokeid < strokecnt; strokeid++){ + stroke = strokes[strokeid] + pointcnt = stroke.length + for(pointid = 0; pointid < pointcnt; pointid++){ + x = stroke.x[pointid] + y = stroke.y[pointid] + if (x > maxX){ + maxX = x + } else if (x < minX) { + minX = x + } + if (y > maxY){ + maxY = y + } else if (y < minY) { + minY = y + } + } + } + return {'maxX': maxX, 'minX': minX, 'maxY': maxY, 'minY': minY} +} +*/ + +function conditionallyLinkCanvasResizeToWindowResize(jSignatureInstance, settingsWidth, apinamespace, globalEvents){ + 'use strict' + if ( settingsWidth === 'ratio' || settingsWidth.split('')[settingsWidth.length - 1] === '%' ) { + + this.eventTokens[apinamespace + '.parentresized'] = globalEvents.subscribe( + apinamespace + '.parentresized' + , (function(eventTokens, $parent, originalParentWidth, sizeRatio){ + 'use strict' + + return function(){ + 'use strict' + + var w = $parent.width() + if (w !== originalParentWidth) { + + // UNsubscribing this particular instance of signature pad only. + // there is a separate `eventTokens` per each instance of signature pad + for (var key in eventTokens){ + if (eventTokens.hasOwnProperty(key)) { + globalEvents.unsubscribe(eventTokens[key]) + delete eventTokens[key] + } + } + + var settings = jSignatureInstance.settings + jSignatureInstance.$parent.children().remove() + for (var key in jSignatureInstance){ + if (jSignatureInstance.hasOwnProperty(key)) { + delete jSignatureInstance[key] + } + } + + // scale data to new signature pad size + settings.data = (function(data, scale){ + var newData = [] + var o, i, l, j, m, stroke + for ( i = 0, l = data.length; i < l; i++) { + stroke = data[i] + + o = {'x':[],'y':[]} + + for ( j = 0, m = stroke.x.length; j < m; j++) { + o.x.push(stroke.x[j] * scale) + o.y.push(stroke.y[j] * scale) + } + + newData.push(o) + } + return newData + })( + settings.data + , w * 1.0 / originalParentWidth + ) + + $parent[apinamespace](settings) + } + } + })( + this.eventTokens + , this.$parent + , this.$parent.width() + , this.canvas.width * 1.0 / this.canvas.height + ) + ) + } +} + + +function jSignatureClass(parent, options, instanceExtensions) { + + var $parent = this.$parent = $(parent) + , eventTokens = this.eventTokens = {} + , events = this.events = new PubSubClass(this) + , globalEvents = $.fn[apinamespace]('globalEvents') + , settings = { + 'width' : 'ratio' + ,'height' : 'ratio' + ,'sizeRatio': 4 // only used when height = 'ratio' + ,'color' : '#000' + ,'background-color': '#fff' + ,'decor-color': '#eee' + ,'lineWidth' : 0 + ,'minFatFingerCompensation' : -10 + ,'showUndoButton': false + ,'data': [] + } + $.extend(settings, getColors($parent)) + if (options) { + $.extend(settings, options) + } + this.settings = settings + + for (var extensionName in instanceExtensions){ + if (instanceExtensions.hasOwnProperty(extensionName)) { + instanceExtensions[extensionName].call(this, extensionName) + } + } + + this.events.publish(apinamespace+'.initializing') + + // these, when enabled, will hover above the sig area. Hence we append them to DOM before canvas. + this.$controlbarUpper = (function(){ + var controlbarstyle = 'padding:0 !important;margin:0 !important;'+ + 'width: 100% !important; height: 0 !important;'+ + 'margin-top:-1em !important;margin-bottom:1em !important;' + return $('
').appendTo($parent) + })(); + + this.isCanvasEmulator = false // will be flipped by initializer when needed. + var canvas = this.canvas = this.initializeCanvas(settings) + , $canvas = $(canvas) + + this.$controlbarLower = (function(){ + var controlbarstyle = 'padding:0 !important;margin:0 !important;'+ + 'width: 100% !important; height: 0 !important;'+ + 'margin-top:-1.5em !important;margin-bottom:1.5em !important;' + return $('
').appendTo($parent) + })(); + + this.canvasContext = canvas.getContext("2d") + + // Most of our exposed API will be looking for this: + $canvas.data(apinamespace + '.this', this) + + + settings.lineWidth = (function(defaultLineWidth, canvasWidth){ + if (!defaultLineWidth){ + return Math.max( + Math.round(canvasWidth / 400) /*+1 pixel for every extra 300px of width.*/ + , 2 /* minimum line width */ + ) + } else { + return defaultLineWidth + } + })(settings.lineWidth, canvas.width); + + this.lineCurveThreshold = settings.lineWidth * 3 + + // Add custom class if defined + if(settings.cssclass && $.trim(settings.cssclass) != "") { + $canvas.addClass(settings.cssclass) + } + + // used for shifting the drawing point up on touch devices, so one can see the drawing above the finger. + this.fatFingerCompensation = 0 + + var movementHandlers = (function(jSignatureInstance) { + + //================================ + // mouse down, move, up handlers: + + // shifts - adjustment values in viewport pixels drived from position of canvas on the page + var shiftX + , shiftY + , setStartValues = function(){ + var tos = $(jSignatureInstance.canvas).offset() + shiftX = tos.left * -1 + shiftY = tos.top * -1 + } + , getPointFromEvent = function(e) { + var firstEvent = (e.changedTouches && e.changedTouches.length > 0 ? e.changedTouches[0] : e) + // All devices i tried report correct coordinates in pageX,Y + // Android Chrome 2.3.x, 3.1, 3.2., Opera Mobile, safari iOS 4.x, + // Windows: Chrome, FF, IE9, Safari + // None of that scroll shift calc vs screenXY other sigs do is needed. + // ... oh, yeah, the "fatFinger.." is for tablets so that people see what they draw. + return new Point( + Math.round(firstEvent.pageX + shiftX) + , Math.round(firstEvent.pageY + shiftY) + jSignatureInstance.fatFingerCompensation + ) + } + , timer = new KickTimerClass( + 750 + , function() { jSignatureInstance.dataEngine.endStroke() } + ) + + this.drawEndHandler = function(e) { + try { e.preventDefault() } catch (ex) {} + timer.clear() + jSignatureInstance.dataEngine.endStroke() + } + this.drawStartHandler = function(e) { + e.preventDefault() + // for performance we cache the offsets + // we recalc these only at the beginning the stroke + setStartValues() + jSignatureInstance.dataEngine.startStroke( getPointFromEvent(e) ) + timer.kick() + } + this.drawMoveHandler = function(e) { + e.preventDefault() + if (!jSignatureInstance.dataEngine.inStroke){ + return + } + jSignatureInstance.dataEngine.addToStroke( getPointFromEvent(e) ) + timer.kick() + } + + return this + + }).call( {}, this ) + + // + //================================ + + ;(function(drawEndHandler, drawStartHandler, drawMoveHandler) { + var canvas = this.canvas + , $canvas = $(canvas) + , undef + if (this.isCanvasEmulator){ + $canvas.bind('mousemove.'+apinamespace, drawMoveHandler) + $canvas.bind('mouseup.'+apinamespace, drawEndHandler) + $canvas.bind('mousedown.'+apinamespace, drawStartHandler) + } else { + canvas.ontouchstart = function(e) { + canvas.onmousedown = undef + canvas.onmouseup = undef + canvas.onmousemove = undef + + this.fatFingerCompensation = ( + settings.minFatFingerCompensation && + settings.lineWidth * -3 > settings.minFatFingerCompensation + ) ? settings.lineWidth * -3 : settings.minFatFingerCompensation + + drawStartHandler(e) + + canvas.ontouchend = drawEndHandler + canvas.ontouchstart = drawStartHandler + canvas.ontouchmove = drawMoveHandler + } + canvas.onmousedown = function(e) { + canvas.ontouchstart = undef + canvas.ontouchend = undef + canvas.ontouchmove = undef + + drawStartHandler(e) + + canvas.onmousedown = drawStartHandler + canvas.onmouseup = drawEndHandler + canvas.onmousemove = drawMoveHandler + } + } + }).call( + this + , movementHandlers.drawEndHandler + , movementHandlers.drawStartHandler + , movementHandlers.drawMoveHandler + ) + + //========================================= + // various event handlers + + // on mouseout + mouseup canvas did not know that mouseUP fired. Continued to draw despite mouse UP. + // it is bettr than + // $canvas.bind('mouseout', drawEndHandler) + // because we don't want to break the stroke where user accidentally gets ouside and wants to get back in quickly. + eventTokens[apinamespace + '.windowmouseup'] = globalEvents.subscribe( + apinamespace + '.windowmouseup' + , movementHandlers.drawEndHandler + ) + + this.events.publish(apinamespace+'.attachingEventHandlers') + + // If we have proportional width, we sign up to events broadcasting "window resized" and checking if + // parent's width changed. If so, we (1) extract settings + data from current signature pad, + // (2) remove signature pad from parent, and (3) reinit new signature pad at new size with same settings, (rescaled) data. + conditionallyLinkCanvasResizeToWindowResize.call( + this + , this + , settings.width.toString(10) + , apinamespace, globalEvents + ) + + // end of event handlers. + // =============================== + + this.resetCanvas(settings.data) + + // resetCanvas renders the data on the screen and fires ONE "change" event + // if there is data. If you have controls that rely on "change" firing + // attach them to something that runs before this.resetCanvas, like + // apinamespace+'.attachingEventHandlers' that fires a bit higher. + this.events.publish(apinamespace+'.initialized') + + return this +} // end of initBase + +//========================================================================= +// jSignatureClass's methods and supporting fn's + +jSignatureClass.prototype.resetCanvas = function(data){ + var canvas = this.canvas + , settings = this.settings + , ctx = this.canvasContext + , isCanvasEmulator = this.isCanvasEmulator + + , cw = canvas.width + , ch = canvas.height + + // preparing colors, drawing area + + ctx.clearRect(0, 0, cw + 30, ch + 30) + + ctx.shadowColor = ctx.fillStyle = settings['background-color'] + if (isCanvasEmulator){ + // FLashCanvas fills with Black by default, covering up the parent div's background + // hence we refill + ctx.fillRect(0,0,cw + 30, ch + 30) + } + + ctx.lineWidth = Math.ceil(parseInt(settings.lineWidth, 10)) + ctx.lineCap = ctx.lineJoin = "round" + + // signature line + ctx.strokeStyle = settings['decor-color'] + ctx.shadowOffsetX = 0 + ctx.shadowOffsetY = 0 + var lineoffset = Math.round( ch / 5 ) + basicLine(ctx, lineoffset * 1.5, ch - lineoffset, cw - (lineoffset * 1.5), ch - lineoffset) + ctx.strokeStyle = settings.color + + if (!isCanvasEmulator){ + ctx.shadowColor = ctx.strokeStyle + ctx.shadowOffsetX = ctx.lineWidth * 0.5 + ctx.shadowOffsetY = ctx.lineWidth * -0.6 + ctx.shadowBlur = 0 + } + + // setting up new dataEngine + + if (!data) { data = [] } + + var dataEngine = this.dataEngine = new DataEngine( + data + , this + , strokeStartCallback + , strokeAddCallback + , strokeEndCallback + ) + + settings.data = data // onwindowresize handler uses it, i think. + $(canvas).data(apinamespace+'.data', data) + .data(apinamespace+'.settings', settings) + + // we fire "change" event on every change in data. + // setting this up: + dataEngine.changed = (function(target, events, apinamespace) { + 'use strict' + return function() { + events.publish(apinamespace+'.change') + target.trigger('change') + } + })(this.$parent, this.events, apinamespace) + // let's trigger change on all data reloads + dataEngine.changed() + + // import filters will be passing this back as indication of "we rendered" + return true +} + +function initializeCanvasEmulator(canvas){ + if (canvas.getContext){ + return false + } else { + // for cases when jSignature, FlashCanvas is inserted + // from one window into another (child iframe) + // 'window' and 'FlashCanvas' may be stuck behind + // in that other parent window. + // we need to find it + var window = canvas.ownerDocument.parentWindow + var FC = window.FlashCanvas ? + canvas.ownerDocument.parentWindow.FlashCanvas : + ( + typeof FlashCanvas === "undefined" ? + undefined : + FlashCanvas + ) + + if (FC) { + canvas = FC.initElement(canvas) + + var zoom = 1 + // FlashCanvas uses flash which has this annoying habit of NOT scaling with page zoom. + // It matches pixel-to-pixel to screen instead. + // Since we are targeting ONLY IE 7, 8 with FlashCanvas, we will test the zoom only the IE8, IE7 way + if (window && window.screen && window.screen.deviceXDPI && window.screen.logicalXDPI){ + zoom = window.screen.deviceXDPI * 1.0 / window.screen.logicalXDPI + } + if (zoom !== 1){ + try { + // We effectively abuse the brokenness of FlashCanvas and force the flash rendering surface to + // occupy larger pixel dimensions than the wrapping, scaled up DIV and Canvas elems. + $(canvas).children('object').get(0).resize(Math.ceil(canvas.width * zoom), Math.ceil(canvas.height * zoom)) + // And by applying "scale" transformation we can talk "browser pixels" to FlashCanvas + // and have it translate the "browser pixels" to "screen pixels" + canvas.getContext('2d').scale(zoom, zoom) + // Note to self: don't reuse Canvas element. Repeated "scale" are cumulative. + } catch (ex) {} + } + return true + } else { + throw new Error("Canvas element does not support 2d context. jSignature cannot proceed.") + } + } + +} + +jSignatureClass.prototype.initializeCanvas = function(settings) { + // =========== + // Init + Sizing code + + var canvas = document.createElement('canvas') + , $canvas = $(canvas) + + // We cannot work with circular dependency + if (settings.width === settings.height && settings.height === 'ratio') { + settings.width = '100%' + } + + $canvas.css( + 'margin' + , 0 + ).css( + 'padding' + , 0 + ).css( + 'border' + , 'none' + ).css( + 'height' + , settings.height === 'ratio' || !settings.height ? 1 : settings.height.toString(10) + ).css( + 'width' + , settings.width === 'ratio' || !settings.width ? 1 : settings.width.toString(10) + ) + + $canvas.appendTo(this.$parent) + + // we could not do this until canvas is rendered (appended to DOM) + if (settings.height === 'ratio') { + $canvas.css( + 'height' + , Math.round( $canvas.width() / settings.sizeRatio ) + ) + } else if (settings.width === 'ratio') { + $canvas.css( + 'width' + , Math.round( $canvas.height() * settings.sizeRatio ) + ) + } + + $canvas.addClass(apinamespace) + + // canvas's drawing area resolution is independent from canvas's size. + // pixels are just scaled up or down when internal resolution does not + // match external size. So... + + canvas.width = $canvas.width() + canvas.height = $canvas.height() + + // Special case Sizing code + + this.isCanvasEmulator = initializeCanvasEmulator(canvas) + + // End of Sizing Code + // =========== + + // normally select preventer would be short, but + // Canvas emulator on IE does NOT provide value for Event. Hence this convoluted line. + canvas.onselectstart = function(e){if(e && e.preventDefault){e.preventDefault()}; if(e && e.stopPropagation){e.stopPropagation()}; return false;} + + return canvas +} + + +var GlobalJSignatureObjectInitializer = function(window){ + + var globalEvents = new PubSubClass() + + // common "window resized" event listener. + // jSignature instances will subscribe to this chanel. + // to resize themselves when needed. + ;(function(globalEvents, apinamespace, $, window){ + 'use strict' + + var resizetimer + , runner = function(){ + globalEvents.publish( + apinamespace + '.parentresized' + ) + } + + // jSignature knows how to resize its content when its parent is resized + // window resize is the only way we can catch resize events though... + $(window).bind('resize.'+apinamespace, function(){ + if (resizetimer) { + clearTimeout(resizetimer) + } + resizetimer = setTimeout( + runner + , 500 + ) + }) + // when mouse exists canvas element and "up"s outside, we cannot catch it with + // callbacks attached to canvas. This catches it outside. + .bind('mouseup.'+apinamespace, function(e){ + globalEvents.publish( + apinamespace + '.windowmouseup' + ) + }) + + })(globalEvents, apinamespace, $, window) + + var jSignatureInstanceExtensions = { + + 'exampleExtension':function(extensionName){ + // we are called very early in instance's life. + // right after the settings are resolved and + // jSignatureInstance.events is created + // and right before first ("jSignature.initializing") event is called. + // You don't really need to manupilate + // jSignatureInstance directly, just attach + // a bunch of events to jSignatureInstance.events + // (look at the source of jSignatureClass to see when these fire) + // and your special pieces of code will attach by themselves. + + // this function runs every time a new instance is set up. + // this means every var you create will live only for one instance + // unless you attach it to something outside, like "window." + // and pick it up later from there. + + // when globalEvents' events fire, 'this' is globalEvents object + // when jSignatureInstance's events fire, 'this' is jSignatureInstance + + // Here, + // this = is new jSignatureClass's instance. + + // The way you COULD approch setting this up is: + // if you have multistep set up, attach event to "jSignature.initializing" + // that attaches other events to be fired further lower the init stream. + // Or, if you know for sure you rely on only one jSignatureInstance's event, + // just attach to it directly + + this.events.subscribe( + // name of the event + apinamespace + '.initializing' + // event handlers, can pass args too, but in majority of cases, + // 'this' which is jSignatureClass object instance pointer is enough to get by. + , function(){ + if (this.settings.hasOwnProperty('non-existent setting category?')) { + console.log(extensionName + ' is here') + } + } + ) + } + + } + + var exportplugins = { + 'default':function(data){return this.toDataURL()} + , 'native':function(data){return data} + , 'image':function(data){ + /*this = canvas elem */ + var imagestring = this.toDataURL() + + if (typeof imagestring === 'string' && + imagestring.length > 4 && + imagestring.slice(0,5) === 'data:' && + imagestring.indexOf(',') !== -1){ + + var splitterpos = imagestring.indexOf(',') + + return [ + imagestring.slice(5, splitterpos) + , imagestring.substr(splitterpos + 1) + ] + } + return [] + } + } + + // will be part of "importplugins" + function _renderImageOnCanvas( data, formattype, rerendercallable ) { + 'use strict' + // #1. Do NOT rely on this. No worky on IE + // (url max len + lack of base64 decoder + possibly other issues) + // #2. This does NOT affect what is captured as "signature" as far as vector data is + // concerned. This is treated same as "signature line" - i.e. completely ignored + // the only time you see imported image data exported is if you export as image. + + // we do NOT call rerendercallable here (unlike in other import plugins) + // because importing image does absolutely nothing to the underlying vector data storage + // This could be a way to "import" old signatures stored as images + // This could also be a way to import extra decor into signature area. + + var img = new Image() + // this = Canvas DOM elem. Not jQuery object. Not Canvas's parent div. + , c = this + + img.onload = function() { + var ctx = c.getContext("2d").drawImage( + img, 0, 0 + , ( img.width < c.width) ? img.width : c.width + , ( img.height < c.height) ? img.height : c.height + ) + } + + img.src = 'data:' + formattype + ',' + data + } + + var importplugins = { + 'native':function(data, formattype, rerendercallable){ + // we expect data as Array of objects of arrays here - whatever 'default' EXPORT plugin spits out. + // returning Truthy to indicate we are good, all updated. + rerendercallable( data ) + } + , 'image': _renderImageOnCanvas + , 'image/png;base64': _renderImageOnCanvas + , 'image/jpeg;base64': _renderImageOnCanvas + , 'image/jpg;base64': _renderImageOnCanvas + } + + function _clearDrawingArea( data ) { + this.find('canvas.'+apinamespace) + .add(this.filter('canvas.'+apinamespace)) + .data(apinamespace+'.this').resetCanvas( data ) + return this + } + + function _setDrawingData( data, formattype ) { + var undef + + if (formattype === undef && typeof data === 'string' && data.substr(0,5) === 'data:') { + formattype = data.slice(5).split(',')[0] + // 5 chars of "data:" + mimetype len + 1 "," char = all skipped. + data = data.slice(6 + formattype.length) + if (formattype === data) return + } + + var $canvas = this.find('canvas.'+apinamespace).add(this.filter('canvas.'+apinamespace)) + + if (!importplugins.hasOwnProperty(formattype)){ + throw new Error(apinamespace + " is unable to find import plugin with for format '"+ String(formattype) +"'") + } else if ($canvas.length !== 0){ + importplugins[formattype].call( + $canvas[0] + , data + , formattype + , (function(jSignatureInstance){ + return function(){ return jSignatureInstance.resetCanvas.apply(jSignatureInstance, arguments) } + })($canvas.data(apinamespace+'.this')) + ) + } + + return this + } + + var elementIsOrphan = function(e){ + var topOfDOM = false + e = e.parentNode + while (e && !topOfDOM){ + topOfDOM = $(e).find(".oe_form") + e = e.parentNode + } + return !topOfDOM + } + + //These are exposed as methods under $obj.jSignature('methodname', *args) + var plugins = {'export':exportplugins, 'import':importplugins, 'instance': jSignatureInstanceExtensions} + , methods = { + 'init' : function( options ) { + return this.each( function() { + if (!elementIsOrphan(this)) { + new jSignatureClass(this, options, jSignatureInstanceExtensions) + } + }) + } + , 'getSettings' : function() { + return this.find('canvas.'+apinamespace) + .add(this.filter('canvas.'+apinamespace)) + .data(apinamespace+'.this').settings + } + // around since v1 + , 'clear' : _clearDrawingArea + // was mistakenly introduced instead of 'clear' in v2 + , 'reset' : _clearDrawingArea + , 'addPlugin' : function(pluginType, pluginName, callable){ + if (plugins.hasOwnProperty(pluginType)){ + plugins[pluginType][pluginName] = callable + } + return this + } + , 'listPlugins' : function(pluginType){ + var answer = [] + if (plugins.hasOwnProperty(pluginType)){ + var o = plugins[pluginType] + for (var k in o){ + if (o.hasOwnProperty(k)){ + answer.push(k) + } + } + } + return answer + } + , 'getData' : function( formattype ) { + var undef, $canvas=this.find('canvas.'+apinamespace).add(this.filter('canvas.'+apinamespace)) + if (formattype === undef) formattype = 'default' + if ($canvas.length !== 0 && exportplugins.hasOwnProperty(formattype)){ + return exportplugins[formattype].call( + $canvas.get(0) // canvas dom elem + , $canvas.data(apinamespace+'.data') // raw signature data as array of objects of arrays + ) + } + } + // around since v1. Took only one arg - data-url-formatted string with (likely png of) signature image + , 'importData' : _setDrawingData + // was mistakenly introduced instead of 'importData' in v2 + , 'setData' : _setDrawingData + // this is one and same instance for all jSignature. + , 'globalEvents' : function(){return globalEvents} + // there will be a separate one for each jSignature instance. + , 'events' : function() { + return this.find('canvas.'+apinamespace) + .add(this.filter('canvas.'+apinamespace)) + .data(apinamespace+'.this').events + } + } // end of methods declaration. + + $.fn[apinamespace] = function(method) { + 'use strict' + if ( !method || typeof method === 'object' ) { + return methods.init.apply( this, arguments ) + } else if ( typeof method === 'string' && methods[method] ) { + return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 )) + } else { + $.error( 'Method ' + String(method) + ' does not exist on jQuery.' + apinamespace ) + } + } + +} // end of GlobalJSignatureObjectInitializer + +GlobalJSignatureObjectInitializer(window) + +})(); \ No newline at end of file diff --git a/web_digital_sign/static/lib/jquery.signature.js b/web_digital_sign/static/lib/jquery.signature.js deleted file mode 100644 index fecbf999..00000000 --- a/web_digital_sign/static/lib/jquery.signature.js +++ /dev/null @@ -1,244 +0,0 @@ - -(function($) { // Hide scope, no $ conflict - -var signatureOverrides = { - - // Global defaults for signature - options: { - background: '#ffffff', // Colour of the background - color: '#000000', // Colour of the signature - thickness: 2, // Thickness of the lines - guideline: false, // Add a guide line or not? - guidelineColor: '#a0a0a0', // Guide line colour - guidelineOffset: 50, // Guide line offset from the bottom - guidelineIndent: 10, // Guide line indent from the edges - notAvailable: 'Your browser doesn\'t support signing', // Error message when no canvas - syncField: null, // Selector for synchronised text field - change: null // Callback when signature changed - }, - - /* Initialise a new signature area. */ - _create: function() { - - - this.element.addClass(this.widgetFullName || this.widgetBaseClass); - try { - this.canvas = $('' + '' + '')[0]; - this.element.prepend(this.canvas); - this.element.find('img').remove(); - this.ctx = this.canvas.getContext('2d'); - } - catch (e) { - $(this.canvas).remove(); - this.resize = true; - this.canvas = document.createElement('canvas'); - this.canvas.setAttribute('width', this.element.width()); - this.canvas.setAttribute('height', this.element.height()); - this.canvas.innerHTML = this.options.notAvailable; - this.element.append(this.canvas); - if (G_vmlCanvasManager) { // Requires excanvas.js - G_vmlCanvasManager.initElement(this.canvas); - } - this.ctx = this.canvas.getContext('2d'); - } - this._refresh(true); - this._mouseInit(); - }, - - /* Refresh the appearance of the signature area. - @param init (boolean, internal) true if initialising */ - _refresh: function(init) { - if (this.resize) { - var parent = $(this.canvas); - $('div', this.canvas).css({width: parent.width(), height: parent.height()}); - } - this.ctx.fillStyle = this.options.background; - this.ctx.strokeStyle = this.options.color; - this.ctx.lineWidth = this.options.thickness; - this.ctx.lineCap = 'round'; - this.ctx.lineJoin = 'round'; - this.clear(init); - }, - - /* Clear the signature area. - @param init (boolean, internal) true if initialising */ - clear: function(init) { - this.ctx.fillRect(0, 0, this.element.width(), this.element.height()); - if (this.options.guideline) { - this.ctx.save(); - this.ctx.strokeStyle = this.options.guidelineColor; - this.ctx.lineWidth = 1; - this.ctx.beginPath(); - this.ctx.moveTo(this.options.guidelineIndent, - this.element.height() - this.options.guidelineOffset); - this.ctx.lineTo(this.element.width() - this.options.guidelineIndent, - this.element.height() - this.options.guidelineOffset); - this.ctx.stroke(); - this.ctx.restore(); - } - this.lines = []; - if (!init) { - this._changed(); - } - }, - - /* Synchronise changes and trigger change event. - @param event (Event) the triggering event */ - _changed: function(event) { - if (this.options.syncField) { - $(this.options.syncField).val(this.toJSON()); - } - this._trigger('change', event, {}); - }, - - /* Custom options handling. - @param options (object) the new option values */ - _setOptions: function(options) { - if (this._superApply) { - this._superApply(arguments); // Base widget handling - } - else { - $.Widget.prototype._setOptions.apply(this, arguments); // Base widget handling - } - this._refresh(); - }, - - /* Determine if dragging can start. - @param event (Event) the triggering mouse event - @return (boolean) true if allowed, false if not */ - _mouseCapture: function(event) { - return !this.options.disabled; - }, - - /* Start a new line. - @param event (Event) the triggering mouse event */ - _mouseStart: function(event) { - this.offset = this.element.offset(); - this.offset.left -= document.documentElement.scrollLeft || document.body.scrollLeft; - this.offset.top -= document.documentElement.scrollTop || document.body.scrollTop; - this.lastPoint = [this._round(event.clientX - this.offset.left), - this._round(event.clientY - this.offset.top)]; - this.curLine = [this.lastPoint]; - this.lines.push(this.curLine); - }, - - /* Track the mouse. - @param event (Event) the triggering mouse event */ - _mouseDrag: function(event) { - var point = [this._round(event.clientX - this.offset.left), - this._round(event.clientY - this.offset.top)]; - this.curLine.push(point); - this.ctx.beginPath(); - this.ctx.moveTo(this.lastPoint[0], this.lastPoint[1]); - this.ctx.lineTo(point[0], point[1]); - this.ctx.stroke(); - this.lastPoint = point; - }, - - /* End a line. - @param event (Event) the triggering mouse event */ - _mouseStop: function(event) { - this.lastPoint = null; - this.curLine = null; - this._changed(event); - }, - - /* Round to two decimal points. - @param value (number) the value to round - @return (number) the rounded value */ - _round: function(value) { - return Math.round(value * 100) / 100; - }, - - /* Convert the captured lines to JSON text. - @return (string) the JSON text version of the lines */ - toJSON: function() { - return '{"lines":[' + $.map(this.lines, function(line) { - return '[' + $.map(line, function(point) { - return '[' + point + ']'; - }) + ']'; - }) + ']}'; - }, - - /* Convert the captured lines to SVG text. - @return (string) the SVG text version of the lines */ - toSVG: function() { - return '\n\n' + - '\n' + - ' \n' + - ' \n' + - ' \n'+ - $.map(this.lines, function(line) { - return ' \n'; - }).join('') + - ' \n \n\n'; - }, - - /* Draw a signature from its JSON description. - @param sigJSON (object) object with attribute lines - being an array of arrays of points or - (string) text version of the JSON */ - draw: function(sigJSON) { - this.clear(true); - if (typeof sigJSON === 'string') { - sigJSON = $.parseJSON(sigJSON); - } - this.lines = sigJSON.lines || []; - var ctx = this.ctx; - $.each(this.lines, function() { - ctx.beginPath(); - $.each(this, function(i) { - ctx[i === 0 ? 'moveTo' : 'lineTo'](this[0], this[1]); - }); - ctx.stroke(); - }); - this._changed(); - }, - - /* Determine whether or not any drawing has occurred. - @return (boolean) true if not signed, false if signed */ - isEmpty: function() { - return this.lines.length === 0; - }, - - /* Remove the signature functionality. */ - _destroy: function() { - this.element.removeClass(this.widgetFullName || this.widgetBaseClass); - $(this.canvas).remove(); - this.canvas = this.ctx = this.lines = null; - this._mouseDestroy(); - } -}; - -if (!$.Widget.prototype._destroy) { - $.extend(signatureOverrides, { - /* Remove the signature functionality. */ - destroy: function() { - this._destroy(); - $.Widget.prototype.destroy.call(this); // Base widget handling - } - }); -} - -if($.Widget.prototype._getCreateOptions === $.noop) { - $.extend(signatureOverrides, { - /* Restore the metadata functionality. */ - _getCreateOptions: function() { - return $.metadata && $.metadata.get(this.element[0])[this.widgetName]; - } - }); -} - -/* Signature capture and display. - Depends on jquery.ui.widget, jquery.ui.mouse. */ -$.widget('kbw.signature', $.ui.mouse, signatureOverrides); - -// Make some things more accessible -$.kbw.signature.options = $.kbw.signature.prototype.options; - -})(jQuery); \ No newline at end of file diff --git a/web_digital_sign/static/src/css/digital.css b/web_digital_sign/static/src/css/digital.css deleted file mode 100644 index b0a44871..00000000 --- a/web_digital_sign/static/src/css/digital.css +++ /dev/null @@ -1,10 +0,0 @@ -/* Styles for signature plugin v1.1.0. */ -/* .kbw-signature { - height : "100px"; - width : "150px"; -} */ - - -.openerp .oe_form_readonly .oe_edit_only, .openerp .oe_form_readonly .oe_form_field:empty { - display: none !important; -} \ No newline at end of file diff --git a/web_digital_sign/static/src/css/jquery-ui.css b/web_digital_sign/static/src/css/jquery-ui.css deleted file mode 100644 index ac8782f8..00000000 --- a/web_digital_sign/static/src/css/jquery-ui.css +++ /dev/null @@ -1,1225 +0,0 @@ -/*! jQuery UI - v1.11.2 - 2014-10-16 -* http://jqueryui.com -* Includes: core.css, accordion.css, autocomplete.css, button.css, datepicker.css, dialog.css, draggable.css, menu.css, progressbar.css, resizable.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css -* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=segoe%20ui%2CArial%2Csans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=6px&bgColorHeader=ece8da&bgTextureHeader=gloss_wave&bgImgOpacityHeader=100&borderColorHeader=d4ccb0&fcHeader=433f38&iconColorHeader=847e71&bgColorContent=f5f3e5&bgTextureContent=highlight_hard&bgImgOpacityContent=100&borderColorContent=dfd9c3&fcContent=312e25&iconColorContent=808080&bgColorDefault=459e00&bgTextureDefault=highlight_hard&bgImgOpacityDefault=15&borderColorDefault=327E04&fcDefault=ffffff&iconColorDefault=eeeeee&bgColorHover=67b021&bgTextureHover=highlight_soft&bgImgOpacityHover=25&borderColorHover=327E04&fcHover=ffffff&iconColorHover=ffffff&bgColorActive=fafaf4&bgTextureActive=highlight_hard&bgImgOpacityActive=100&borderColorActive=d4ccb0&fcActive=459e00&iconColorActive=8DC262&bgColorHighlight=fcf0ba&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=e8e1b5&fcHighlight=363636&iconColorHighlight=8DC262&bgColorError=ffedad&bgTextureError=highlight_soft&bgImgOpacityError=95&borderColorError=e3a345&fcError=cd5c0a&iconColorError=cd0a0a&bgColorOverlay=2b2922&bgTextureOverlay=inset_soft&bgImgOpacityOverlay=15&opacityOverlay=90&bgColorShadow=cccccc&bgTextureShadow=highlight_hard&bgImgOpacityShadow=95&opacityShadow=20&thicknessShadow=12px&offsetTopShadow=-12px&offsetLeftShadow=-12px&cornerRadiusShadow=10px -* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ - -/* Layout helpers -----------------------------------*/ -.ui-helper-hidden { - display: none; -} -.ui-helper-hidden-accessible { - border: 0; - clip: rect(0 0 0 0); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; -} -.ui-helper-reset { - margin: 0; - padding: 0; - border: 0; - outline: 0; - line-height: 1.3; - text-decoration: none; - font-size: 100%; - list-style: none; -} -.ui-helper-clearfix:before, -.ui-helper-clearfix:after { - content: ""; - display: table; - border-collapse: collapse; -} -.ui-helper-clearfix:after { - clear: both; -} -.ui-helper-clearfix { - min-height: 0; /* support: IE7 */ -} -.ui-helper-zfix { - width: 100%; - height: 100%; - top: 0; - left: 0; - position: absolute; - opacity: 0; - filter:Alpha(Opacity=0); /* support: IE8 */ -} - -.ui-front { - z-index: 100; -} - - -/* Interaction Cues -----------------------------------*/ -.ui-state-disabled { - cursor: default !important; -} - - -/* Icons -----------------------------------*/ - -/* states and images */ -.ui-icon { - display: block; - text-indent: -99999px; - overflow: hidden; - background-repeat: no-repeat; -} - - -/* Misc visuals -----------------------------------*/ - -/* Overlays */ -.ui-widget-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; -} -.ui-accordion .ui-accordion-header { - display: block; - cursor: pointer; - position: relative; - margin: 2px 0 0 0; - padding: .5em .5em .5em .7em; - min-height: 0; /* support: IE7 */ - font-size: 100%; -} -.ui-accordion .ui-accordion-icons { - padding-left: 2.2em; -} -.ui-accordion .ui-accordion-icons .ui-accordion-icons { - padding-left: 2.2em; -} -.ui-accordion .ui-accordion-header .ui-accordion-header-icon { - position: absolute; - left: .5em; - top: 50%; - margin-top: -8px; -} -.ui-accordion .ui-accordion-content { - padding: 1em 2.2em; - border-top: 0; - overflow: auto; -} -.ui-autocomplete { - position: absolute; - top: 0; - left: 0; - cursor: default; -} -.ui-button { - display: inline-block; - position: relative; - padding: 0; - line-height: normal; - margin-right: .1em; - cursor: pointer; - vertical-align: middle; - text-align: center; - overflow: visible; /* removes extra width in IE */ -} -.ui-button, -.ui-button:link, -.ui-button:visited, -.ui-button:hover, -.ui-button:active { - text-decoration: none; -} -/* to make room for the icon, a width needs to be set here */ -.ui-button-icon-only { - width: 2.2em; -} -/* button elements seem to need a little more width */ -button.ui-button-icon-only { - width: 2.4em; -} -.ui-button-icons-only { - width: 3.4em; -} -button.ui-button-icons-only { - width: 3.7em; -} - -/* button text element */ -.ui-button .ui-button-text { - display: block; - line-height: normal; -} -.ui-button-text-only .ui-button-text { - padding: .4em 1em; -} -.ui-button-icon-only .ui-button-text, -.ui-button-icons-only .ui-button-text { - padding: .4em; - text-indent: -9999999px; -} -.ui-button-text-icon-primary .ui-button-text, -.ui-button-text-icons .ui-button-text { - padding: .4em 1em .4em 2.1em; -} -.ui-button-text-icon-secondary .ui-button-text, -.ui-button-text-icons .ui-button-text { - padding: .4em 2.1em .4em 1em; -} -.ui-button-text-icons .ui-button-text { - padding-left: 2.1em; - padding-right: 2.1em; -} -/* no icon support for input elements, provide padding by default */ -input.ui-button { - padding: .4em 1em; -} - -/* button icon element(s) */ -.ui-button-icon-only .ui-icon, -.ui-button-text-icon-primary .ui-icon, -.ui-button-text-icon-secondary .ui-icon, -.ui-button-text-icons .ui-icon, -.ui-button-icons-only .ui-icon { - position: absolute; - top: 50%; - margin-top: -8px; -} -.ui-button-icon-only .ui-icon { - left: 50%; - margin-left: -8px; -} -.ui-button-text-icon-primary .ui-button-icon-primary, -.ui-button-text-icons .ui-button-icon-primary, -.ui-button-icons-only .ui-button-icon-primary { - left: .5em; -} -.ui-button-text-icon-secondary .ui-button-icon-secondary, -.ui-button-text-icons .ui-button-icon-secondary, -.ui-button-icons-only .ui-button-icon-secondary { - right: .5em; -} - -/* button sets */ -.ui-buttonset { - margin-right: 7px; -} -.ui-buttonset .ui-button { - margin-left: 0; - margin-right: -.3em; -} - -/* workarounds */ -/* reset extra padding in Firefox, see h5bp.com/l */ -input.ui-button::-moz-focus-inner, -button.ui-button::-moz-focus-inner { - border: 0; - padding: 0; -} -.ui-datepicker { - width: 17em; - padding: .2em .2em 0; - display: none; -} -.ui-datepicker .ui-datepicker-header { - position: relative; - padding: .2em 0; -} -.ui-datepicker .ui-datepicker-prev, -.ui-datepicker .ui-datepicker-next { - position: absolute; - top: 2px; - width: 1.8em; - height: 1.8em; -} -.ui-datepicker .ui-datepicker-prev-hover, -.ui-datepicker .ui-datepicker-next-hover { - top: 1px; -} -.ui-datepicker .ui-datepicker-prev { - left: 2px; -} -.ui-datepicker .ui-datepicker-next { - right: 2px; -} -.ui-datepicker .ui-datepicker-prev-hover { - left: 1px; -} -.ui-datepicker .ui-datepicker-next-hover { - right: 1px; -} -.ui-datepicker .ui-datepicker-prev span, -.ui-datepicker .ui-datepicker-next span { - display: block; - position: absolute; - left: 50%; - margin-left: -8px; - top: 50%; - margin-top: -8px; -} -.ui-datepicker .ui-datepicker-title { - margin: 0 2.3em; - line-height: 1.8em; - text-align: center; -} -.ui-datepicker .ui-datepicker-title select { - font-size: 1em; - margin: 1px 0; -} -.ui-datepicker select.ui-datepicker-month, -.ui-datepicker select.ui-datepicker-year { - width: 45%; -} -.ui-datepicker table { - width: 100%; - font-size: .9em; - border-collapse: collapse; - margin: 0 0 .4em; -} -.ui-datepicker th { - padding: .7em .3em; - text-align: center; - font-weight: bold; - border: 0; -} -.ui-datepicker td { - border: 0; - padding: 1px; -} -.ui-datepicker td span, -.ui-datepicker td a { - display: block; - padding: .2em; - text-align: right; - text-decoration: none; -} -.ui-datepicker .ui-datepicker-buttonpane { - background-image: none; - margin: .7em 0 0 0; - padding: 0 .2em; - border-left: 0; - border-right: 0; - border-bottom: 0; -} -.ui-datepicker .ui-datepicker-buttonpane button { - float: right; - margin: .5em .2em .4em; - cursor: pointer; - padding: .2em .6em .3em .6em; - width: auto; - overflow: visible; -} -.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { - float: left; -} - -/* with multiple calendars */ -.ui-datepicker.ui-datepicker-multi { - width: auto; -} -.ui-datepicker-multi .ui-datepicker-group { - float: left; -} -.ui-datepicker-multi .ui-datepicker-group table { - width: 95%; - margin: 0 auto .4em; -} -.ui-datepicker-multi-2 .ui-datepicker-group { - width: 50%; -} -.ui-datepicker-multi-3 .ui-datepicker-group { - width: 33.3%; -} -.ui-datepicker-multi-4 .ui-datepicker-group { - width: 25%; -} -.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, -.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { - border-left-width: 0; -} -.ui-datepicker-multi .ui-datepicker-buttonpane { - clear: left; -} -.ui-datepicker-row-break { - clear: both; - width: 100%; - font-size: 0; -} - -/* RTL support */ -.ui-datepicker-rtl { - direction: rtl; -} -.ui-datepicker-rtl .ui-datepicker-prev { - right: 2px; - left: auto; -} -.ui-datepicker-rtl .ui-datepicker-next { - left: 2px; - right: auto; -} -.ui-datepicker-rtl .ui-datepicker-prev:hover { - right: 1px; - left: auto; -} -.ui-datepicker-rtl .ui-datepicker-next:hover { - left: 1px; - right: auto; -} -.ui-datepicker-rtl .ui-datepicker-buttonpane { - clear: right; -} -.ui-datepicker-rtl .ui-datepicker-buttonpane button { - float: left; -} -.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, -.ui-datepicker-rtl .ui-datepicker-group { - float: right; -} -.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, -.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { - border-right-width: 0; - border-left-width: 1px; -} -.ui-dialog { - overflow: hidden; - position: absolute; - top: 0; - left: 0; - padding: .2em; - outline: 0; -} -.ui-dialog .ui-dialog-titlebar { - padding: .4em 1em; - position: relative; -} -.ui-dialog .ui-dialog-title { - float: left; - margin: .1em 0; - white-space: nowrap; - width: 90%; - overflow: hidden; - text-overflow: ellipsis; -} -.ui-dialog .ui-dialog-titlebar-close { - position: absolute; - right: .3em; - top: 50%; - width: 20px; - margin: -10px 0 0 0; - padding: 1px; - height: 20px; -} -.ui-dialog .ui-dialog-content { - position: relative; - border: 0; - padding: .5em 1em; - background: none; - overflow: auto; -} -.ui-dialog .ui-dialog-buttonpane { - text-align: left; - border-width: 1px 0 0 0; - background-image: none; - margin-top: .5em; - padding: .3em 1em .5em .4em; -} -.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { - float: right; -} -.ui-dialog .ui-dialog-buttonpane button { - margin: .5em .4em .5em 0; - cursor: pointer; -} -.ui-dialog .ui-resizable-se { - width: 12px; - height: 12px; - right: -5px; - bottom: -5px; - background-position: 16px 16px; -} -.ui-draggable .ui-dialog-titlebar { - cursor: move; -} -.ui-draggable-handle { - -ms-touch-action: none; - touch-action: none; -} -.ui-menu { - list-style: none; - padding: 0; - margin: 0; - display: block; - outline: none; -} -.ui-menu .ui-menu { - position: absolute; -} -.ui-menu .ui-menu-item { - position: relative; - margin: 0; - padding: 3px 1em 3px .4em; - cursor: pointer; - min-height: 0; /* support: IE7 */ - /* support: IE10, see #8844 */ - list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"); -} -.ui-menu .ui-menu-divider { - margin: 5px 0; - height: 0; - font-size: 0; - line-height: 0; - border-width: 1px 0 0 0; -} -.ui-menu .ui-state-focus, -.ui-menu .ui-state-active { - margin: -1px; -} - -/* icon support */ -.ui-menu-icons { - position: relative; -} -.ui-menu-icons .ui-menu-item { - padding-left: 2em; -} - -/* left-aligned */ -.ui-menu .ui-icon { - position: absolute; - top: 0; - bottom: 0; - left: .2em; - margin: auto 0; -} - -/* right-aligned */ -.ui-menu .ui-menu-icon { - left: auto; - right: 0; -} -.ui-progressbar { - height: 2em; - text-align: left; - overflow: hidden; -} -.ui-progressbar .ui-progressbar-value { - margin: -1px; - height: 100%; -} -.ui-progressbar .ui-progressbar-overlay { - background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw=="); - height: 100%; - filter: alpha(opacity=25); /* support: IE8 */ - opacity: 0.25; -} -.ui-progressbar-indeterminate .ui-progressbar-value { - background-image: none; -} -.ui-resizable { - position: relative; -} -.ui-resizable-handle { - position: absolute; - font-size: 0.1px; - display: block; - -ms-touch-action: none; - touch-action: none; -} -.ui-resizable-disabled .ui-resizable-handle, -.ui-resizable-autohide .ui-resizable-handle { - display: none; -} -.ui-resizable-n { - cursor: n-resize; - height: 7px; - width: 100%; - top: -5px; - left: 0; -} -.ui-resizable-s { - cursor: s-resize; - height: 7px; - width: 100%; - bottom: -5px; - left: 0; -} -.ui-resizable-e { - cursor: e-resize; - width: 7px; - right: -5px; - top: 0; - height: 100%; -} -.ui-resizable-w { - cursor: w-resize; - width: 7px; - left: -5px; - top: 0; - height: 100%; -} -.ui-resizable-se { - cursor: se-resize; - width: 12px; - height: 12px; - right: 1px; - bottom: 1px; -} -.ui-resizable-sw { - cursor: sw-resize; - width: 9px; - height: 9px; - left: -5px; - bottom: -5px; -} -.ui-resizable-nw { - cursor: nw-resize; - width: 9px; - height: 9px; - left: -5px; - top: -5px; -} -.ui-resizable-ne { - cursor: ne-resize; - width: 9px; - height: 9px; - right: -5px; - top: -5px; -} -.ui-selectable { - -ms-touch-action: none; - touch-action: none; -} -.ui-selectable-helper { - position: absolute; - z-index: 100; - border: 1px dotted black; -} -.ui-selectmenu-menu { - padding: 0; - margin: 0; - position: absolute; - top: 0; - left: 0; - display: none; -} -.ui-selectmenu-menu .ui-menu { - overflow: auto; - /* Support: IE7 */ - overflow-x: hidden; - padding-bottom: 1px; -} -.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup { - font-size: 1em; - font-weight: bold; - line-height: 1.5; - padding: 2px 0.4em; - margin: 0.5em 0 0 0; - height: auto; - border: 0; -} -.ui-selectmenu-open { - display: block; -} -.ui-selectmenu-button { - display: inline-block; - overflow: hidden; - position: relative; - text-decoration: none; - cursor: pointer; -} -.ui-selectmenu-button span.ui-icon { - right: 0.5em; - left: auto; - margin-top: -8px; - position: absolute; - top: 50%; -} -.ui-selectmenu-button span.ui-selectmenu-text { - text-align: left; - padding: 0.4em 2.1em 0.4em 1em; - display: block; - line-height: 1.4; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.ui-slider { - position: relative; - text-align: left; -} -.ui-slider .ui-slider-handle { - position: absolute; - z-index: 2; - width: 1.2em; - height: 1.2em; - cursor: default; - -ms-touch-action: none; - touch-action: none; -} -.ui-slider .ui-slider-range { - position: absolute; - z-index: 1; - font-size: .7em; - display: block; - border: 0; - background-position: 0 0; -} - -/* support: IE8 - See #6727 */ -.ui-slider.ui-state-disabled .ui-slider-handle, -.ui-slider.ui-state-disabled .ui-slider-range { - filter: inherit; -} - -.ui-slider-horizontal { - height: .8em; -} -.ui-slider-horizontal .ui-slider-handle { - top: -.3em; - margin-left: -.6em; -} -.ui-slider-horizontal .ui-slider-range { - top: 0; - height: 100%; -} -.ui-slider-horizontal .ui-slider-range-min { - left: 0; -} -.ui-slider-horizontal .ui-slider-range-max { - right: 0; -} - -.ui-slider-vertical { - width: .8em; - height: 100px; -} -.ui-slider-vertical .ui-slider-handle { - left: -.3em; - margin-left: 0; - margin-bottom: -.6em; -} -.ui-slider-vertical .ui-slider-range { - left: 0; - width: 100%; -} -.ui-slider-vertical .ui-slider-range-min { - bottom: 0; -} -.ui-slider-vertical .ui-slider-range-max { - top: 0; -} -.ui-sortable-handle { - -ms-touch-action: none; - touch-action: none; -} -.ui-spinner { - position: relative; - display: inline-block; - overflow: hidden; - padding: 0; - vertical-align: middle; -} -.ui-spinner-input { - border: none; - background: none; - color: inherit; - padding: 0; - margin: .2em 0; - vertical-align: middle; - margin-left: .4em; - margin-right: 22px; -} -.ui-spinner-button { - width: 16px; - height: 50%; - font-size: .5em; - padding: 0; - margin: 0; - text-align: center; - position: absolute; - cursor: default; - display: block; - overflow: hidden; - right: 0; -} -/* more specificity required here to override default borders */ -.ui-spinner a.ui-spinner-button { - border-top: none; - border-bottom: none; - border-right: none; -} -/* vertically center icon */ -.ui-spinner .ui-icon { - position: absolute; - margin-top: -8px; - top: 50%; - left: 0; -} -.ui-spinner-up { - top: 0; -} -.ui-spinner-down { - bottom: 0; -} - -/* TR overrides */ -.ui-spinner .ui-icon-triangle-1-s { - /* need to fix icons sprite */ - background-position: -65px -16px; -} -.ui-tabs { - position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ - padding: .2em; -} -.ui-tabs .ui-tabs-nav { - margin: 0; - padding: .2em .2em 0; -} -.ui-tabs .ui-tabs-nav li { - list-style: none; - float: left; - position: relative; - top: 0; - margin: 1px .2em 0 0; - border-bottom-width: 0; - padding: 0; - white-space: nowrap; -} -.ui-tabs .ui-tabs-nav .ui-tabs-anchor { - float: left; - padding: .5em 1em; - text-decoration: none; -} -.ui-tabs .ui-tabs-nav li.ui-tabs-active { - margin-bottom: -1px; - padding-bottom: 1px; -} -.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor, -.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor, -.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor { - cursor: text; -} -.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor { - cursor: pointer; -} -.ui-tabs .ui-tabs-panel { - display: block; - border-width: 0; - padding: 1em 1.4em; - background: none; -} -.ui-tooltip { - padding: 8px; - position: absolute; - z-index: 9999; - max-width: 300px; - -webkit-box-shadow: 0 0 5px #aaa; - box-shadow: 0 0 5px #aaa; -} -body .ui-tooltip { - border-width: 2px; -} - -/* Component containers -----------------------------------*/ -.ui-widget { - font-family: segoe ui,Arial,sans-serif; - font-size: 1.1em; -} -.ui-widget .ui-widget { - font-size: 1em; -} -.ui-widget input, -.ui-widget select, -.ui-widget textarea, -.ui-widget button { - font-family: segoe ui,Arial,sans-serif; - font-size: 1em; -} -.ui-widget-content { - border: 1px solid #dfd9c3; - background: #f5f3e5 url("images/ui-bg_highlight-hard_100_f5f3e5_1x100.png") 50% top repeat-x; - color: #312e25; -} -.ui-widget-content a { - color: #312e25; -} -.ui-widget-header { - border: 1px solid #d4ccb0; - background: #ece8da url("images/ui-bg_gloss-wave_100_ece8da_500x100.png") 50% 50% repeat-x; - color: #433f38; - font-weight: bold; -} -.ui-widget-header a { - color: #433f38; -} - -/* Interaction states -----------------------------------*/ -.ui-state-default, -.ui-widget-content .ui-state-default, -.ui-widget-header .ui-state-default { - border: 1px solid #327E04; - background: #459e00 url("images/ui-bg_highlight-hard_15_459e00_1x100.png") 50% 50% repeat-x; - font-weight: bold; - color: #ffffff; -} -.ui-state-default a, -.ui-state-default a:link, -.ui-state-default a:visited { - color: #ffffff; - text-decoration: none; -} -.ui-state-hover, -.ui-widget-content .ui-state-hover, -.ui-widget-header .ui-state-hover, -.ui-state-focus, -.ui-widget-content .ui-state-focus, -.ui-widget-header .ui-state-focus { - border: 1px solid #327E04; - background: #67b021 url("images/ui-bg_highlight-soft_25_67b021_1x100.png") 50% 50% repeat-x; - font-weight: bold; - color: #ffffff; -} -.ui-state-hover a, -.ui-state-hover a:hover, -.ui-state-hover a:link, -.ui-state-hover a:visited, -.ui-state-focus a, -.ui-state-focus a:hover, -.ui-state-focus a:link, -.ui-state-focus a:visited { - color: #ffffff; - text-decoration: none; -} -.ui-state-active, -.ui-widget-content .ui-state-active, -.ui-widget-header .ui-state-active { - border: 1px solid #d4ccb0; - background: #fafaf4 url("images/ui-bg_highlight-hard_100_fafaf4_1x100.png") 50% 50% repeat-x; - font-weight: bold; - color: #459e00; -} -.ui-state-active a, -.ui-state-active a:link, -.ui-state-active a:visited { - color: #459e00; - text-decoration: none; -} - -/* Interaction Cues -----------------------------------*/ -.ui-state-highlight, -.ui-widget-content .ui-state-highlight, -.ui-widget-header .ui-state-highlight { - border: 1px solid #e8e1b5; - background: #fcf0ba url("images/ui-bg_glass_55_fcf0ba_1x400.png") 50% 50% repeat-x; - color: #363636; -} -.ui-state-highlight a, -.ui-widget-content .ui-state-highlight a, -.ui-widget-header .ui-state-highlight a { - color: #363636; -} -.ui-state-error, -.ui-widget-content .ui-state-error, -.ui-widget-header .ui-state-error { - border: 1px solid #e3a345; - background: #ffedad url("images/ui-bg_highlight-soft_95_ffedad_1x100.png") 50% top repeat-x; - color: #cd5c0a; -} -.ui-state-error a, -.ui-widget-content .ui-state-error a, -.ui-widget-header .ui-state-error a { - color: #cd5c0a; -} -.ui-state-error-text, -.ui-widget-content .ui-state-error-text, -.ui-widget-header .ui-state-error-text { - color: #cd5c0a; -} -.ui-priority-primary, -.ui-widget-content .ui-priority-primary, -.ui-widget-header .ui-priority-primary { - font-weight: bold; -} -.ui-priority-secondary, -.ui-widget-content .ui-priority-secondary, -.ui-widget-header .ui-priority-secondary { - opacity: .7; - filter:Alpha(Opacity=70); /* support: IE8 */ - font-weight: normal; -} -.ui-state-disabled, -.ui-widget-content .ui-state-disabled, -.ui-widget-header .ui-state-disabled { - opacity: .35; - filter:Alpha(Opacity=35); /* support: IE8 */ - background-image: none; -} -.ui-state-disabled .ui-icon { - filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */ -} - -/* Icons -----------------------------------*/ - -/* states and images */ -.ui-icon { - width: 16px; - height: 16px; -} -.ui-icon, -.ui-widget-content .ui-icon { - background-image: url("images/ui-icons_808080_256x240.png"); -} -.ui-widget-header .ui-icon { - background-image: url("images/ui-icons_847e71_256x240.png"); -} -.ui-state-default .ui-icon { - background-image: url("images/ui-icons_eeeeee_256x240.png"); -} -.ui-state-hover .ui-icon, -.ui-state-focus .ui-icon { - background-image: url("images/ui-icons_ffffff_256x240.png"); -} -.ui-state-active .ui-icon { - background-image: url("images/ui-icons_8DC262_256x240.png"); -} -.ui-state-highlight .ui-icon { - background-image: url("images/ui-icons_8DC262_256x240.png"); -} -.ui-state-error .ui-icon, -.ui-state-error-text .ui-icon { - background-image: url("images/ui-icons_cd0a0a_256x240.png"); -} - -/* positioning */ -.ui-icon-blank { background-position: 16px 16px; } -.ui-icon-carat-1-n { background-position: 0 0; } -.ui-icon-carat-1-ne { background-position: -16px 0; } -.ui-icon-carat-1-e { background-position: -32px 0; } -.ui-icon-carat-1-se { background-position: -48px 0; } -.ui-icon-carat-1-s { background-position: -64px 0; } -.ui-icon-carat-1-sw { background-position: -80px 0; } -.ui-icon-carat-1-w { background-position: -96px 0; } -.ui-icon-carat-1-nw { background-position: -112px 0; } -.ui-icon-carat-2-n-s { background-position: -128px 0; } -.ui-icon-carat-2-e-w { background-position: -144px 0; } -.ui-icon-triangle-1-n { background-position: 0 -16px; } -.ui-icon-triangle-1-ne { background-position: -16px -16px; } -.ui-icon-triangle-1-e { background-position: -32px -16px; } -.ui-icon-triangle-1-se { background-position: -48px -16px; } -.ui-icon-triangle-1-s { background-position: -64px -16px; } -.ui-icon-triangle-1-sw { background-position: -80px -16px; } -.ui-icon-triangle-1-w { background-position: -96px -16px; } -.ui-icon-triangle-1-nw { background-position: -112px -16px; } -.ui-icon-triangle-2-n-s { background-position: -128px -16px; } -.ui-icon-triangle-2-e-w { background-position: -144px -16px; } -.ui-icon-arrow-1-n { background-position: 0 -32px; } -.ui-icon-arrow-1-ne { background-position: -16px -32px; } -.ui-icon-arrow-1-e { background-position: -32px -32px; } -.ui-icon-arrow-1-se { background-position: -48px -32px; } -.ui-icon-arrow-1-s { background-position: -64px -32px; } -.ui-icon-arrow-1-sw { background-position: -80px -32px; } -.ui-icon-arrow-1-w { background-position: -96px -32px; } -.ui-icon-arrow-1-nw { background-position: -112px -32px; } -.ui-icon-arrow-2-n-s { background-position: -128px -32px; } -.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } -.ui-icon-arrow-2-e-w { background-position: -160px -32px; } -.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } -.ui-icon-arrowstop-1-n { background-position: -192px -32px; } -.ui-icon-arrowstop-1-e { background-position: -208px -32px; } -.ui-icon-arrowstop-1-s { background-position: -224px -32px; } -.ui-icon-arrowstop-1-w { background-position: -240px -32px; } -.ui-icon-arrowthick-1-n { background-position: 0 -48px; } -.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } -.ui-icon-arrowthick-1-e { background-position: -32px -48px; } -.ui-icon-arrowthick-1-se { background-position: -48px -48px; } -.ui-icon-arrowthick-1-s { background-position: -64px -48px; } -.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } -.ui-icon-arrowthick-1-w { background-position: -96px -48px; } -.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } -.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } -.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } -.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } -.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } -.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } -.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } -.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } -.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } -.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } -.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } -.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } -.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } -.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } -.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } -.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } -.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } -.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } -.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } -.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } -.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } -.ui-icon-arrow-4 { background-position: 0 -80px; } -.ui-icon-arrow-4-diag { background-position: -16px -80px; } -.ui-icon-extlink { background-position: -32px -80px; } -.ui-icon-newwin { background-position: -48px -80px; } -.ui-icon-refresh { background-position: -64px -80px; } -.ui-icon-shuffle { background-position: -80px -80px; } -.ui-icon-transfer-e-w { background-position: -96px -80px; } -.ui-icon-transferthick-e-w { background-position: -112px -80px; } -.ui-icon-folder-collapsed { background-position: 0 -96px; } -.ui-icon-folder-open { background-position: -16px -96px; } -.ui-icon-document { background-position: -32px -96px; } -.ui-icon-document-b { background-position: -48px -96px; } -.ui-icon-note { background-position: -64px -96px; } -.ui-icon-mail-closed { background-position: -80px -96px; } -.ui-icon-mail-open { background-position: -96px -96px; } -.ui-icon-suitcase { background-position: -112px -96px; } -.ui-icon-comment { background-position: -128px -96px; } -.ui-icon-person { background-position: -144px -96px; } -.ui-icon-print { background-position: -160px -96px; } -.ui-icon-trash { background-position: -176px -96px; } -.ui-icon-locked { background-position: -192px -96px; } -.ui-icon-unlocked { background-position: -208px -96px; } -.ui-icon-bookmark { background-position: -224px -96px; } -.ui-icon-tag { background-position: -240px -96px; } -.ui-icon-home { background-position: 0 -112px; } -.ui-icon-flag { background-position: -16px -112px; } -.ui-icon-calendar { background-position: -32px -112px; } -.ui-icon-cart { background-position: -48px -112px; } -.ui-icon-pencil { background-position: -64px -112px; } -.ui-icon-clock { background-position: -80px -112px; } -.ui-icon-disk { background-position: -96px -112px; } -.ui-icon-calculator { background-position: -112px -112px; } -.ui-icon-zoomin { background-position: -128px -112px; } -.ui-icon-zoomout { background-position: -144px -112px; } -.ui-icon-search { background-position: -160px -112px; } -.ui-icon-wrench { background-position: -176px -112px; } -.ui-icon-gear { background-position: -192px -112px; } -.ui-icon-heart { background-position: -208px -112px; } -.ui-icon-star { background-position: -224px -112px; } -.ui-icon-link { background-position: -240px -112px; } -.ui-icon-cancel { background-position: 0 -128px; } -.ui-icon-plus { background-position: -16px -128px; } -.ui-icon-plusthick { background-position: -32px -128px; } -.ui-icon-minus { background-position: -48px -128px; } -.ui-icon-minusthick { background-position: -64px -128px; } -.ui-icon-close { background-position: -80px -128px; } -.ui-icon-closethick { background-position: -96px -128px; } -.ui-icon-key { background-position: -112px -128px; } -.ui-icon-lightbulb { background-position: -128px -128px; } -.ui-icon-scissors { background-position: -144px -128px; } -.ui-icon-clipboard { background-position: -160px -128px; } -.ui-icon-copy { background-position: -176px -128px; } -.ui-icon-contact { background-position: -192px -128px; } -.ui-icon-image { background-position: -208px -128px; } -.ui-icon-video { background-position: -224px -128px; } -.ui-icon-script { background-position: -240px -128px; } -.ui-icon-alert { background-position: 0 -144px; } -.ui-icon-info { background-position: -16px -144px; } -.ui-icon-notice { background-position: -32px -144px; } -.ui-icon-help { background-position: -48px -144px; } -.ui-icon-check { background-position: -64px -144px; } -.ui-icon-bullet { background-position: -80px -144px; } -.ui-icon-radio-on { background-position: -96px -144px; } -.ui-icon-radio-off { background-position: -112px -144px; } -.ui-icon-pin-w { background-position: -128px -144px; } -.ui-icon-pin-s { background-position: -144px -144px; } -.ui-icon-play { background-position: 0 -160px; } -.ui-icon-pause { background-position: -16px -160px; } -.ui-icon-seek-next { background-position: -32px -160px; } -.ui-icon-seek-prev { background-position: -48px -160px; } -.ui-icon-seek-end { background-position: -64px -160px; } -.ui-icon-seek-start { background-position: -80px -160px; } -/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ -.ui-icon-seek-first { background-position: -80px -160px; } -.ui-icon-stop { background-position: -96px -160px; } -.ui-icon-eject { background-position: -112px -160px; } -.ui-icon-volume-off { background-position: -128px -160px; } -.ui-icon-volume-on { background-position: -144px -160px; } -.ui-icon-power { background-position: 0 -176px; } -.ui-icon-signal-diag { background-position: -16px -176px; } -.ui-icon-signal { background-position: -32px -176px; } -.ui-icon-battery-0 { background-position: -48px -176px; } -.ui-icon-battery-1 { background-position: -64px -176px; } -.ui-icon-battery-2 { background-position: -80px -176px; } -.ui-icon-battery-3 { background-position: -96px -176px; } -.ui-icon-circle-plus { background-position: 0 -192px; } -.ui-icon-circle-minus { background-position: -16px -192px; } -.ui-icon-circle-close { background-position: -32px -192px; } -.ui-icon-circle-triangle-e { background-position: -48px -192px; } -.ui-icon-circle-triangle-s { background-position: -64px -192px; } -.ui-icon-circle-triangle-w { background-position: -80px -192px; } -.ui-icon-circle-triangle-n { background-position: -96px -192px; } -.ui-icon-circle-arrow-e { background-position: -112px -192px; } -.ui-icon-circle-arrow-s { background-position: -128px -192px; } -.ui-icon-circle-arrow-w { background-position: -144px -192px; } -.ui-icon-circle-arrow-n { background-position: -160px -192px; } -.ui-icon-circle-zoomin { background-position: -176px -192px; } -.ui-icon-circle-zoomout { background-position: -192px -192px; } -.ui-icon-circle-check { background-position: -208px -192px; } -.ui-icon-circlesmall-plus { background-position: 0 -208px; } -.ui-icon-circlesmall-minus { background-position: -16px -208px; } -.ui-icon-circlesmall-close { background-position: -32px -208px; } -.ui-icon-squaresmall-plus { background-position: -48px -208px; } -.ui-icon-squaresmall-minus { background-position: -64px -208px; } -.ui-icon-squaresmall-close { background-position: -80px -208px; } -.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } -.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } -.ui-icon-grip-solid-vertical { background-position: -32px -224px; } -.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } -.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } -.ui-icon-grip-diagonal-se { background-position: -80px -224px; } - - -/* Misc visuals -----------------------------------*/ - -/* Corner radius */ -.ui-corner-all, -.ui-corner-top, -.ui-corner-left, -.ui-corner-tl { - border-top-left-radius: 6px; -} -.ui-corner-all, -.ui-corner-top, -.ui-corner-right, -.ui-corner-tr { - border-top-right-radius: 6px; -} -.ui-corner-all, -.ui-corner-bottom, -.ui-corner-left, -.ui-corner-bl { - border-bottom-left-radius: 6px; -} -.ui-corner-all, -.ui-corner-bottom, -.ui-corner-right, -.ui-corner-br { - border-bottom-right-radius: 6px; -} - -/* Overlays */ -.ui-widget-overlay { - background: #2b2922 url("images/ui-bg_inset-soft_15_2b2922_1x100.png") 50% bottom repeat-x; - opacity: .9; - filter: Alpha(Opacity=90); /* support: IE8 */ -} -.ui-widget-shadow { - margin: -12px 0 0 -12px; - padding: 12px; - background: #cccccc url("images/ui-bg_highlight-hard_95_cccccc_1x100.png") 50% top repeat-x; - opacity: .2; - filter: Alpha(Opacity=20); /* support: IE8 */ - border-radius: 10px; -} diff --git a/web_digital_sign/static/src/css/jquery.signature.css b/web_digital_sign/static/src/css/jquery.signature.css deleted file mode 100644 index 5952cf50..00000000 --- a/web_digital_sign/static/src/css/jquery.signature.css +++ /dev/null @@ -1,5 +0,0 @@ -/* Styles for signature plugin v1.1.0. */ -.kbw-signature { - display: inline-block; - border: 1px solid #a0a0a0; -} diff --git a/web_digital_sign/static/src/img/icon.png b/web_digital_sign/static/src/img/icon.png deleted file mode 100644 index 53dd30d9..00000000 Binary files a/web_digital_sign/static/src/img/icon.png and /dev/null differ diff --git a/web_digital_sign/static/src/js/digital_sign.js b/web_digital_sign/static/src/js/digital_sign.js index 1363b93d..f40c68d7 100644 --- a/web_digital_sign/static/src/js/digital_sign.js +++ b/web_digital_sign/static/src/js/digital_sign.js @@ -1,143 +1,122 @@ -odoo.define('web_digital_sign.web_digital_sign',function(require){ +odoo.define('web_digital_sign.web_digital_sign', function(require) { "use strict"; var core = require('web.core'); - var data = require('web.data'); var FormView = require('web.FormView'); var utils = require('web.utils'); + var session = require('web.session'); + var Model = require('web.Model'); var _t = core._t; var QWeb = core.qweb; - var images = {}; var FieldSignature = core.form_widget_registry.map.image.extend({ template: 'FieldSignature', + placeholder: "/web/static/src/img/placeholder.png", + initialize_content: function() { + var self = this; + this.$el.find('> img').remove(); + this.$el.find('.signature > canvas').remove(); + var sign_options = {'decor-color' : '#D1D0CE', 'color': '#000', 'background-color': '#fff','height':'150','width':'550'}; + this.$el.find(".signature").jSignature("init",sign_options); + this.$el.find(".signature").attr({"tabindex": "0",'height':"100"}); + this.empty_sign = this.$el.find(".signature").jSignature("getData",'image'); + this.$el.find('#sign_clean').click(this.on_clear_sign); + this.$el.find('.save_sign').click(this.on_save_sign); + }, + on_clear_sign: function() { + var self = this; + this.$el.find(".signature > canvas").remove(); + this.$el.find('> img').remove(); + this.$el.find(".signature").attr("tabindex", "0"); + var sign_options = {'decor-color' : '#D1D0CE', 'color': '#000', 'background-color': '#fff','height':'150','width':'550','clear': true}; + this.$el.find(".signature").jSignature(sign_options); + this.$el.find(".signature").focus(); + self.set('value', false); + }, + on_save_sign: function(value_) { + var self = this; + this.$el.find('> img').remove(); + var signature = self.$el.find(".signature").jSignature("getData",'image'); + var is_empty = signature + ? self.empty_sign[1] === signature[1] + : false; + if (! is_empty && typeof signature !== "undefined" && signature[1]) { + self.set('value',signature[1]); + } + }, render_value: function() { var self = this; - var url; - if (this.get('value') && ! utils.is_bin_size(this.get('value'))) { + var url = this.placeholder; + if (this.get('value') && !utils.is_bin_size(this.get('value'))) { url = 'data:image/png;base64,' + this.get('value'); - }else if (this.get('value')) { - var id = JSON.stringify(this.view.datarecord.id || null); - self.digita_dataset = new data.DataSetSearch(self, self.view.model, {}, []); - self.digita_dataset.read_slice(['id', self.name], {'domain': [['id', '=', id]]}).then(function(records){ - _.each(records,function(record){ - if(record[self.name]){ - images[self.name] = record[self.name] - }else{ - images[self.name] = "" - } - }) - }) - var field = this.name; - if (this.options.preview_image) - field = this.options.preview_image; + } else if (this.get('value')) { url = this.session.url('/web/binary/image', { - model: this.view.dataset.model, - id: id, - field: field, - t: (new Date().getTime()), + model: this.view.dataset.model, + id: JSON.stringify(this.view.datarecord.id || null), + field: this.options.preview_image + ? this.options.preview_image + : this.name, + t: new Date().getTime() }); - }else { - images[self.name] = "" + } else { url = this.placeholder; - self.set('value',images[self.name]) } - var $img = $(QWeb.render("FieldBinaryImage-img", { widget: this, url: url })); - this.$el.find('img').remove(); - if(this.view.get("actual_mode") !== 'edit' && this.view.get("actual_mode") !== 'create'){ - this.$el.prepend($img); - }else if(this.view.get("actual_mode") == 'edit' ){ + if (this.view.get("actual_mode") === 'view') { + var $img = $(QWeb.render("FieldBinaryImage-extend", { widget: this, url: url })); this.$el.find('> img').remove(); - this.$el.find('> canvas').remove(); - if(! this.get('value')){ - this.$el.find('> img').remove(); - $(this.$el[0]).find(".signature").signature(); - }else if(this.get('value')){ - this.$el.prepend($img); - } - }else if( this.view.get("actual_mode") == 'create'){ - images = {} + this.$el.find(".signature").hide(); + this.$el.prepend($img); + $img.load(function() { + if (! self.options.size) { + return; + } + $img.css("max-width", "" + self.options.size[0] + "px"); + $img.css("max-height", "" + self.options.size[1] + "px"); + $img.css("margin-left", "" + (self.options.size[0] - $img.width()) / 2 + "px"); + $img.css("margin-top", "" + (self.options.size[1] - $img.height()) / 2 + "px"); + }); + $img.on('error', function() { + $img.attr('src', self.placeholder); + self.do_warn(_t("Image"), _t("Could not display the selected image.")); + }); + } else if (this.view.get("actual_mode") === 'edit') { this.$el.find('> img').remove(); - this.$el.find('> canvas').remove(); - if(! this.get('value')){ - this.$el.find('> img').remove(); - $(this.$el[0]).find(".signature").signature(); - }else if(this.get('value')){ - this.$el.prepend($img); - } - } - $(this.$el[0]).find('.clear_sign').click(function(){ - self.$el.find('> img').remove(); - images[self.name] = "" - $(self.$el[0]).find(".signature").show(); - $(self.$el[0]).find(".signature").signature('clear'); - }); - $(this.$el[0]).find('.save_sign').on('click',function(){ - var val - if($(self.$el[0]).find(".signature").hasClass( "kbw-signature" ) && ! $(self.$el[0]).find(".signature").signature('isEmpty')){ - $(self.$el[0]).find(".signature").hide(); - val = $(self.$el[0]).find(".signature > canvas")[0].toDataURL(); - images[self.name] = val.split(',')[1] - var $img = $(QWeb.render("FieldBinaryImage-extend", { widget: self, url: val })); - self.$el.find('> img').remove(); - self.$el.prepend($img); - self.set('value',val.split(',')[1]) - var id = JSON.stringify(self.view.datarecord.id || null); - var field = self.name; - url = self.session.url('/web/binary/image', { - model: self.view.dataset.model, - id: id, - field: field, - t: (new Date().getTime()), - }); - }else{ - var id = JSON.stringify(self.view.datarecord.id || null); - var field = self.name; - if (self.options.preview_image) - field = self.options.preview_image; - url = self.session.url('/web/binary/image', { - model: self.view.dataset.model, - id: id, - field: field, - t: (new Date().getTime()), + if (this.get('value')) { + var field_name = this.options.preview_image + ? this.options.preview_image + : this.name; + new Model(this.view.dataset.model).call("read", [this.view.datarecord.id, [field_name]]).done(function(data) { + if (data) { + var field_desc = _.values(_.pick(data, field_name)); + self.$el.find(".signature").jSignature("reset"); + self.$el.find(".signature").jSignature("setData",'data:image/png;base64,'+field_desc[0]); + } }); - var $img = $(QWeb.render("FieldBinaryImage-extend", { widget: self, url: url })); - self.$el.find('> img').remove(); + } else { + this.$el.find('> img').remove(); + this.$el.find('.signature > canvas').remove(); + var sign_options = {'decor-color' : '#D1D0CE', 'color': '#000','background-color': '#fff','height':'150','width':'550'}; + this.$el.find(".signature").jSignature("init",sign_options); } - }); - $img.load(function() { - if (! self.options.size) - return; - $img.css("max-width", "" + self.options.size[0] + "px"); - $img.css("max-height", "" + self.options.size[1] + "px"); - $img.css("margin-left", "" + (self.options.size[0] - $img.width()) / 2 + "px"); - $img.css("margin-top", "" + (self.options.size[1] - $img.height()) / 2 + "px"); - }); - $img.on('error', function() { - console.log("eroor") - $img.attr('src', self.placeholder); - self.do_warn(_t("Image"), _t("Could not display the selected image.")); - }); - }, + } else if (this.view.get("actual_mode") === 'create') { + this.$el.find('> img').remove(); + this.$el.find('> canvas').remove(); + if (!this.get('value')) { + this.$el.find(".signature").empty().jSignature("init",{'decor-color' : '#D1D0CE', 'color': '#000','background-color': '#fff','height':'150','width':'550'}); + } + } + } }); - core.form_widget_registry.add('signature', FieldSignature) + core.form_widget_registry.add('signature', FieldSignature); FormView.include({ - save: function(prepend_on_create) { - var self = this; - $('.save_sign').click() - var save_obj = {prepend_on_create: prepend_on_create, ret: null}; - this.save_list.push(save_obj); - return self._process_operations().then(function() { - if (save_obj.error) - return $.Deferred().reject(); - return $.when.apply($, save_obj.ret); - }).done(function(result) { - self.$el.removeClass('oe_form_dirty'); - }); - }, + save: function() { + this.$el.find('.save_sign').click(); + return this._super.apply(this, arguments); + } }); }); + diff --git a/web_digital_sign/static/src/xml/digital_sign.xml b/web_digital_sign/static/src/xml/digital_sign.xml index 68cf2d64..0fe6c0be 100644 --- a/web_digital_sign/static/src/xml/digital_sign.xml +++ b/web_digital_sign/static/src/xml/digital_sign.xml @@ -1,18 +1,24 @@ - + - -
-

- - + +
+
+
+ Clear + +
+ Draw your signature +
+
- + - \ No newline at end of file + diff --git a/web_digital_sign/users.py b/web_digital_sign/users.py index 92e6e1ef..3e18fa92 100644 --- a/web_digital_sign/users.py +++ b/web_digital_sign/users.py @@ -9,4 +9,3 @@ class Users(models.Model): signature= fields.Binary(string='Signature') -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/web_digital_sign/users_view.xml b/web_digital_sign/users_view.xml index 8c7cc427..40bd5c5d 100644 --- a/web_digital_sign/users_view.xml +++ b/web_digital_sign/users_view.xml @@ -13,4 +13,5 @@ - \ No newline at end of file + + diff --git a/web_digital_sign/views/we_digital_sign_view.xml b/web_digital_sign/views/we_digital_sign_view.xml index b366fd51..ef98dd96 100644 --- a/web_digital_sign/views/we_digital_sign_view.xml +++ b/web_digital_sign/views/we_digital_sign_view.xml @@ -1,13 +1,13 @@ -