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} fieldLabel 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 {Object} signPanel The signature SVG panel element (defaults to {})
64      */
65     signPanel : {},
66     /**
67      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
68      */
69     allowBlank : false,
70     /**
71      * @cfg {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
72      */
73     isMouseDown : false,
74     /**
75      * @cfg {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
76      */
77     isConfirmed : false,
78     /**
79      * @cfg {String} signatureTmp SVG mapping string (defaults to empty string)
80      */
81     signatureTmp : '',
82     
83     defaultAutoCreate : { // modified by initCompnoent..
84         tag: "input",
85         type:"hidden"
86     },
87
88     // private
89     onRender : function(ct, position){
90         
91         Roo.form.Signature.superclass.onRender.call(this, ct, position);
92         
93         this.wrap = this.el.wrap({
94             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
95         });
96         
97         this.createToolbar(this);
98         this.signPanel = this.wrap.createChild({
99                 tag: 'div',
100                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
101             }, this.el
102         );
103             
104         this.svgEl = this.signPanel.createChild({
105               xmlns : 'http://www.w3.org/2000/svg',
106               tag : 'svg',
107               width: this.width,
108               height: this.height,
109               viewBox: '0 0 '+this.width+' '+this.height,
110               cn : [
111                 {
112                     tag: "rect",
113                     id: "svg-r",
114                     width: this.width,
115                     height: this.height,
116                     fill: "#ffa"
117                 },
118                 {
119                     tag: "line",
120                     x1: "0",
121                     y1: "80",
122                     x2: "300",
123                     y2: "80",
124                     'stroke': "#666",
125                     'stroke-width': "1",
126                     'stroke-dasharray': "3",
127                     'shape-rendering': "crispEdges",
128                     'pointer-events': "none"
129                 },
130                 {
131                     tag: "path",
132                     id: "svg-p",
133                     'stroke': "navy",
134                     'stroke-width': "3",
135                     'fill': "none",
136                     'pointer-events': 'none'
137                 },
138               ]
139         });
140         this.createSVG();
141         this.svgBox = this.svgEl.dom.getScreenCTM();
142     },
143     createSVG : function(){ 
144         var svg = this.signPanel;
145         var r = svg.select('#svg-r', true).first().dom;
146         var t = this;
147
148         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
149         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
150         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
151         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
152         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
153         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
154         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
155         
156     },
157     isTouchEvent : function(e){
158         return e.type.match(/^touch/);
159     },
160     getCoords : function (e) {
161         var pt    = this.svgEl.dom.createSVGPoint();
162         pt.x = e.clientX; 
163         pt.y = e.clientY;
164         if (this.isTouchEvent(e)) {
165             pt.x =  e.targetTouches[0].clientX 
166             pt.y = e.targetTouches[0].clientY;
167         }
168         var a = this.svgEl.dom.getScreenCTM();
169         var b = a.inverse();
170         var mx = pt.matrixTransform(b);
171         return mx.x + ',' + mx.y;
172     },
173     //mouse event headler 
174     down : function (e) {
175         var sp = this.signatureTmp.split(' ');
176         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
177         this.signPanel.select('#svg-p', true).first().attr('d', this.signatureTmp);
178         
179         this.isMouseDown = true;
180         
181         e.preventDefault();
182     },
183     move : function (e) {
184         if (this.isMouseDown) {
185             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
186             this.signPanel.select('#svg-p', true).first().attr('d', this.signatureTmp);
187         }
188         
189         e.preventDefault();
190     },
191     up : function (e) {
192         this.isMouseDown = false;
193         var sp = this.signatureTmp.split(' ');
194         
195         if(sp.length > 1){
196             if(!sp[sp.length-2].match(/^L/)){
197                 sp.pop();
198                 sp.pop();
199                 sp.push("");
200                 this.signatureTmp = sp.join(" ");
201             }
202         }
203         if(this.getValue() != this.signatureTmp){
204             this.signPanel.select('#svg-r', true).first().attr('fill', '#ffa');
205             this.isConfirmed = false;
206         }
207         
208         e.preventDefault();
209     },
210     
211     /**
212      * Protected method that will not generally be called directly. It
213      * is called when the editor creates its toolbar. Override this method if you need to
214      * add custom toolbar buttons.
215      * @param {HtmlEditor} editor
216      */
217     createToolbar : function(editor){
218          function btn(id, toggle, handler){
219             var xid = fid + '-'+ id ;
220             return {
221                 id : xid,
222                 cmd : id,
223                 cls : 'x-btn-icon x-edit-'+id,
224                 enableToggle:toggle !== false,
225                 scope: editor, // was editor...
226                 handler:handler||editor.relayBtnCmd,
227                 clickEvent:'mousedown',
228                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
229                 tabIndex:-1
230             };
231         }
232         
233         
234         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
235         this.tb = tb;
236         this.tb.add(
237            {
238                 cls : ' x-signature-btn x-signature-'+id,
239                 scope: editor, // was editor...
240                 handler: this.reset,
241                 clickEvent:'mousedown',
242                 text: this.labels.clear
243             } ,
244             {
245                  xtype : 'Fill',
246                  xns: Roo.Toolbar
247             }, 
248             {
249                 cls : '  x-signature-btn x-signature-'+id,
250                 scope: editor, // was editor...
251                 handler: this.setConfirmed,
252                 clickEvent:'mousedown',
253                 text: this.labels.confirm
254             }
255            
256         );
257     
258     },
259     // private
260     getSignature : function(){
261         return this.signatureTmp;
262     },
263     // private
264     reset : function(){
265         this.signatureTmp = '';
266         this.signPanel.select('#svg-r', true).first().attr('fill', '#ffa');
267         this.signPanel.select('#svg-p', true).first().attr('d', '');
268         this.isConfirmed = false;
269         Roo.form.Signature.superclass.reset.call(this);
270     },
271     test : function(){
272 //        Roo.log(this.signPanel.dom.contentWindow.up())
273     },
274     //public
275     setConfirmed : function(){
276         if(!this.getSignature()){
277             return;
278         }
279         this.signPanel.select('#svg-r', true).first().attr('fill', '#cfc');
280         this.setValue(this.getSignature());
281         this.isConfirmed = true;
282 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
283     },
284     // private
285     // Subclasses should provide the validation implementation by overriding this
286     validateValue : function(value){
287         if(this.allowBlank){
288             return true;
289         }
290         
291         if(this.isConfirmed){
292             return true;
293         }
294         return false;
295     }
296 });