Roo/form/Signature.js
[roojs1] / Roo / form / Signature.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12 /**
13  * @class Roo.form.Signature
14  * @extends Roo.form.Field
15  * Signature field.  
16  * @constructor
17  * 
18  * @param {Object} config Configuration options
19  */
20
21 Roo.form.Signature = function(config){
22     Roo.form.Signature.superclass.constructor.call(this, config);
23     
24     this.addEvents({// not in used??
25          /**
26          * @event confirm
27          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
28              * @param {Roo.form.ComboBox} combo This combo box
29              */
30         'confirm' : true,
31         /**
32          * @event reset
33          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
34              * @param {Roo.form.ComboBox} combo This combo box
35              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
36              */
37         'reset' : true
38     });
39 };
40
41 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
42     /**
43      * @cfg {Object} labels Label to use when rendering a form.
44      * defaults to 
45      * labels : { 
46      *      clear : "Clear",
47      *      confirm : "Confirm"
48      *  }
49      */
50     labels : { 
51         clear : "Clear",
52         confirm : "Confirm"
53     },
54     /**
55      * @cfg {Number} width The signature panel width (defaults to 300)
56      */
57     width: 300,
58     /**
59      * @cfg {Number} height The signature panel height (defaults to 100)
60      */
61     height : 100,
62     /**
63      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
64      */
65     allowBlank : false,
66     
67     //private
68     // {Object} signPanel The signature SVG panel element (defaults to {})
69     signPanel : {},
70     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
71     isMouseDown : false,
72     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
73     isConfirmed : false,
74     // {String} signatureTmp SVG mapping string (defaults to empty string)
75     signatureTmp : '',
76     
77     
78     defaultAutoCreate : { // modified by initCompnoent..
79         tag: "input",
80         type:"hidden"
81     },
82
83     // private
84     onRender : function(ct, position){
85         
86         Roo.form.Signature.superclass.onRender.call(this, ct, position);
87         
88         this.wrap = this.el.wrap({
89             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
90         });
91         
92         this.createToolbar(this);
93         this.signPanel = this.wrap.createChild({
94                 tag: 'div',
95                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
96             }, this.el
97         );
98             
99         this.svgID = Roo.id();
100         this.svgEl = this.signPanel.createChild({
101               xmlns : 'http://www.w3.org/2000/svg',
102               tag : 'svg',
103               width: this.width,
104               height: this.height,
105               viewBox: '0 0 '+this.width+' '+this.height,
106               cn : [
107                 {
108                     tag: "rect",
109                     id: this.svgID + "-svg-r",
110                     width: this.width,
111                     height: this.height,
112                     fill: "#ffa"
113                 },
114                 {
115                     tag: "line",
116                     x1: "0", // start
117                     y1: (this.height*0.8), // start set the line in 80% of height
118                     x2: this.width, // end
119                     y2: (this.height*0.8), // end set the line in 80% of height
120                     'stroke': "#666",
121                     'stroke-width': "1",
122                     'stroke-dasharray': "3",
123                     'shape-rendering': "crispEdges",
124                     'pointer-events': "none"
125                 },
126                 {
127                     tag: "path",
128                     id: this.svgID + "-svg-p",
129                     'stroke': "navy",
130                     'stroke-width': "3",
131                     'fill': "none",
132                     'pointer-events': 'none'
133                 }
134               ]
135         });
136         this.createSVG();
137         this.svgBox = this.svgEl.dom.getScreenCTM();
138     },
139     createSVG : function(){ 
140         var svg = this.signPanel;
141         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
142         var t = this;
143
144         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
145         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
146         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
147         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
148         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
149         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
150         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
151         
152     },
153     isTouchEvent : function(e){
154         return e.type.match(/^touch/);
155     },
156     getCoords : function (e) {
157         var pt    = this.svgEl.dom.createSVGPoint();
158         pt.x = e.clientX; 
159         pt.y = e.clientY;
160         if (this.isTouchEvent(e)) {
161             pt.x =  e.targetTouches[0].clientX 
162             pt.y = e.targetTouches[0].clientY;
163         }
164         var a = this.svgEl.dom.getScreenCTM();
165         var b = a.inverse();
166         var mx = pt.matrixTransform(b);
167         return mx.x + ',' + mx.y;
168     },
169     //mouse event headler 
170     down : function (e) {
171         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
172         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
173         
174         this.isMouseDown = true;
175         
176         e.preventDefault();
177     },
178     move : function (e) {
179         if (this.isMouseDown) {
180             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
181             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
182         }
183         
184         e.preventDefault();
185     },
186     up : function (e) {
187         this.isMouseDown = false;
188         var sp = this.signatureTmp.split(' ');
189         
190         if(sp.length > 1){
191             if(!sp[sp.length-2].match(/^L/)){
192                 sp.pop();
193                 sp.pop();
194                 sp.push("");
195                 this.signatureTmp = sp.join(" ");
196             }
197         }
198         if(this.getValue() != this.signatureTmp){
199             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
200             this.isConfirmed = false;
201         }
202         e.preventDefault();
203     },
204     
205     /**
206      * Protected method that will not generally be called directly. It
207      * is called when the editor creates its toolbar. Override this method if you need to
208      * add custom toolbar buttons.
209      * @param {HtmlEditor} editor
210      */
211     createToolbar : function(editor){
212          function btn(id, toggle, handler){
213             var xid = fid + '-'+ id ;
214             return {
215                 id : xid,
216                 cmd : id,
217                 cls : 'x-btn-icon x-edit-'+id,
218                 enableToggle:toggle !== false,
219                 scope: editor, // was editor...
220                 handler:handler||editor.relayBtnCmd,
221                 clickEvent:'mousedown',
222                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
223                 tabIndex:-1
224             };
225         }
226         
227         
228         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
229         this.tb = tb;
230         this.tb.add(
231            {
232                 cls : ' x-signature-btn x-signature-'+id,
233                 scope: editor, // was editor...
234                 handler: this.reset,
235                 clickEvent:'mousedown',
236                 text: this.labels.clear
237             },
238             {
239                  xtype : 'Fill',
240                  xns: Roo.Toolbar
241             }, 
242             {
243                 cls : '  x-signature-btn x-signature-'+id,
244                 scope: editor, // was editor...
245                 handler: this.setConfirmed,
246                 clickEvent:'mousedown',
247                 text: this.labels.confirm
248             }
249         );
250     
251     },
252     //public
253     /**
254      * 
255      * @return {String} Image Data URI
256      */
257     getImageDataURI : function(){
258         var svg = this.svgEl.dom.outerHTML;
259         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
260         return src;
261     },
262     /**
263      * 
264      * @return {Boolean} this.isConfirmed
265      */
266     getConfirmed : function(){
267         return this.isConfirmed;
268     },
269     /**
270      * 
271      * @return {Boolean} this.isConfirmed
272      */
273     getWidth : function(){
274         return this.width;
275     },
276     /**
277      * 
278      * @return {Boolean} this.isConfirmed
279      */
280     getHeight : function(){
281         return this.height;
282     },
283     // private
284     getSignature : function(){
285         return this.signatureTmp;
286     },
287     // private
288     reset : function(){
289         this.signatureTmp = '';
290         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
291         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
292         this.isConfirmed = false;
293         Roo.form.Signature.superclass.reset.call(this);
294     },
295     test : function(){
296 //        Roo.log(this.signPanel.dom.contentWindow.up())
297     },
298     //private
299     setConfirmed : function(){
300         if(!this.getSignature()){
301             return;
302         }
303         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
304         this.setValue(this.getSignature());
305         this.isConfirmed = true;
306 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
307     },
308     // private
309     // Subclasses should provide the validation implementation by overriding this
310     validateValue : function(value){
311         if(this.allowBlank){
312             return true;
313         }
314         
315         if(this.isConfirmed){
316             return true;
317         }
318         return false;
319     }
320 });