handle old version of chrome with broken formdata
[roojs1] / Roo / form / BasicForm.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.BasicForm
14  * @extends Roo.util.Observable
15  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
16  * @constructor
17  * @param {String/HTMLElement/Roo.Element} el The form element or its id
18  * @param {Object} config Configuration options
19  */
20 Roo.form.BasicForm = function(el, config){
21     this.allItems = [];
22     this.childForms = [];
23     Roo.apply(this, config);
24     /*
25      * The Roo.form.Field items in this form.
26      * @type MixedCollection
27      */
28      
29      
30     this.items = new Roo.util.MixedCollection(false, function(o){
31         return o.id || (o.id = Roo.id());
32     });
33     this.addEvents({
34         /**
35          * @event beforeaction
36          * Fires before any action is performed. Return false to cancel the action.
37          * @param {Form} this
38          * @param {Action} action The action to be performed
39          */
40         beforeaction: true,
41         /**
42          * @event actionfailed
43          * Fires when an action fails.
44          * @param {Form} this
45          * @param {Action} action The action that failed
46          */
47         actionfailed : true,
48         /**
49          * @event actioncomplete
50          * Fires when an action is completed.
51          * @param {Form} this
52          * @param {Action} action The action that completed
53          */
54         actioncomplete : true
55     });
56     if(el){
57         this.initEl(el);
58     }
59     Roo.form.BasicForm.superclass.constructor.call(this);
60     
61     Roo.form.BasicForm.popover.apply();
62 };
63
64 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
65     /**
66      * @cfg {String} method
67      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
68      */
69     /**
70      * @cfg {DataReader} reader
71      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
72      * This is optional as there is built-in support for processing JSON.
73      */
74     /**
75      * @cfg {DataReader} errorReader
76      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
77      * This is completely optional as there is built-in support for processing JSON.
78      */
79     /**
80      * @cfg {String} url
81      * The URL to use for form actions if one isn't supplied in the action options.
82      */
83     /**
84      * @cfg {Boolean} fileUpload
85      * Set to true if this form is a file upload.
86      */
87      
88     /**
89      * @cfg {Object} baseParams
90      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
91      */
92      /**
93      
94     /**
95      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
96      */
97     timeout: 30,
98
99     // private
100     activeAction : null,
101
102     /**
103      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
104      * or setValues() data instead of when the form was first created.
105      */
106     trackResetOnLoad : false,
107     
108     
109     /**
110      * childForms - used for multi-tab forms
111      * @type {Array}
112      */
113     childForms : false,
114     
115     /**
116      * allItems - full list of fields.
117      * @type {Array}
118      */
119     allItems : false,
120     
121     /**
122      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
123      * element by passing it or its id or mask the form itself by passing in true.
124      * @type Mixed
125      */
126     waitMsgTarget : false,
127     
128     /**
129      * @type Boolean
130      */
131     disableMask : false,
132     
133     /**
134      * @cfg {Boolean} errorMask (true|false) default false
135      */
136     errorMask : false,
137     
138     /**
139      * @cfg {Number} maskOffset Default 100
140      */
141     maskOffset : 100,
142
143     // private
144     initEl : function(el){
145         this.el = Roo.get(el);
146         this.id = this.el.id || Roo.id();
147         this.el.on('submit', this.onSubmit, this);
148         this.el.addClass('x-form');
149     },
150
151     // private
152     onSubmit : function(e){
153         e.stopEvent();
154     },
155
156     /**
157      * Returns true if client-side validation on the form is successful.
158      * @return Boolean
159      */
160     isValid : function(){
161         var valid = true;
162         var target = false;
163         this.items.each(function(f){
164             if(f.validate()){
165                 return;
166             }
167             
168             valid = false;
169                 
170             if(!target && f.el.isVisible(true)){
171                 target = f;
172             }
173         });
174         
175         if(this.errorMask && !valid){
176             Roo.form.BasicForm.popover.mask(this, target);
177         }
178         
179         return valid;
180     },
181
182     /**
183      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
184      * @return Boolean
185      */
186     isDirty : function(){
187         var dirty = false;
188         this.items.each(function(f){
189            if(f.isDirty()){
190                dirty = true;
191                return false;
192            }
193         });
194         return dirty;
195     },
196     
197     /**
198      * Returns true if any fields in this form have changed since their original load. (New version)
199      * @return Boolean
200      */
201     
202     hasChanged : function()
203     {
204         var dirty = false;
205         this.items.each(function(f){
206            if(f.hasChanged()){
207                dirty = true;
208                return false;
209            }
210         });
211         return dirty;
212         
213     },
214     /**
215      * Resets all hasChanged to 'false' -
216      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
217      * So hasChanged storage is only to be used for this purpose
218      * @return Boolean
219      */
220     resetHasChanged : function()
221     {
222         this.items.each(function(f){
223            f.resetHasChanged();
224         });
225         
226     },
227     
228     
229     /**
230      * Performs a predefined action (submit or load) or custom actions you define on this form.
231      * @param {String} actionName The name of the action type
232      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
233      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
234      * accept other config options):
235      * <pre>
236 Property          Type             Description
237 ----------------  ---------------  ----------------------------------------------------------------------------------
238 url               String           The url for the action (defaults to the form's url)
239 method            String           The form method to use (defaults to the form's method, or POST if not defined)
240 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
241 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
242                                    validate the form on the client (defaults to false)
243      * </pre>
244      * @return {BasicForm} this
245      */
246     doAction : function(action, options){
247         if(typeof action == 'string'){
248             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
249         }
250         if(this.fireEvent('beforeaction', this, action) !== false){
251             this.beforeAction(action);
252             action.run.defer(100, action);
253         }
254         return this;
255     },
256
257     /**
258      * Shortcut to do a submit action.
259      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
260      * @return {BasicForm} this
261      */
262     submit : function(options){
263         this.doAction('submit', options);
264         return this;
265     },
266
267     /**
268      * Shortcut to do a load action.
269      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
270      * @return {BasicForm} this
271      */
272     load : function(options){
273         this.doAction('load', options);
274         return this;
275     },
276
277     /**
278      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
279      * @param {Record} record The record to edit
280      * @return {BasicForm} this
281      */
282     updateRecord : function(record){
283         record.beginEdit();
284         var fs = record.fields;
285         fs.each(function(f){
286             var field = this.findField(f.name);
287             if(field){
288                 record.set(f.name, field.getValue());
289             }
290         }, this);
291         record.endEdit();
292         return this;
293     },
294
295     /**
296      * Loads an Roo.data.Record into this form.
297      * @param {Record} record The record to load
298      * @return {BasicForm} this
299      */
300     loadRecord : function(record){
301         this.setValues(record.data);
302         return this;
303     },
304
305     // private
306     beforeAction : function(action){
307         var o = action.options;
308         
309         if(!this.disableMask) {
310             if(this.waitMsgTarget === true){
311                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
312             }else if(this.waitMsgTarget){
313                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
314                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
315             }else {
316                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
317             }
318         }
319         
320          
321     },
322
323     // private
324     afterAction : function(action, success){
325         this.activeAction = null;
326         var o = action.options;
327         
328         if(!this.disableMask) {
329             if(this.waitMsgTarget === true){
330                 this.el.unmask();
331             }else if(this.waitMsgTarget){
332                 this.waitMsgTarget.unmask();
333             }else{
334                 Roo.MessageBox.updateProgress(1);
335                 Roo.MessageBox.hide();
336             }
337         }
338         
339         if(success){
340             if(o.reset){
341                 this.reset();
342             }
343             Roo.callback(o.success, o.scope, [this, action]);
344             this.fireEvent('actioncomplete', this, action);
345             
346         }else{
347             
348             // failure condition..
349             // we have a scenario where updates need confirming.
350             // eg. if a locking scenario exists..
351             // we look for { errors : { needs_confirm : true }} in the response.
352             if (
353                 (typeof(action.result) != 'undefined')  &&
354                 (typeof(action.result.errors) != 'undefined')  &&
355                 (typeof(action.result.errors.needs_confirm) != 'undefined')
356            ){
357                 var _t = this;
358                 Roo.MessageBox.confirm(
359                     "Change requires confirmation",
360                     action.result.errorMsg,
361                     function(r) {
362                         if (r != 'yes') {
363                             return;
364                         }
365                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
366                     }
367                     
368                 );
369                 
370                 
371                 
372                 return;
373             }
374             
375             Roo.callback(o.failure, o.scope, [this, action]);
376             // show an error message if no failed handler is set..
377             if (!this.hasListener('actionfailed')) {
378                 Roo.MessageBox.alert("Error",
379                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
380                         action.result.errorMsg :
381                         "Saving Failed, please check your entries or try again"
382                 );
383             }
384             
385             this.fireEvent('actionfailed', this, action);
386         }
387         
388     },
389
390     /**
391      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
392      * @param {String} id The value to search for
393      * @return Field
394      */
395     findField : function(id){
396         var field = this.items.get(id);
397         if(!field){
398             this.items.each(function(f){
399                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
400                     field = f;
401                     return false;
402                 }
403             });
404         }
405         return field || null;
406     },
407
408     /**
409      * Add a secondary form to this one, 
410      * Used to provide tabbed forms. One form is primary, with hidden values 
411      * which mirror the elements from the other forms.
412      * 
413      * @param {Roo.form.Form} form to add.
414      * 
415      */
416     addForm : function(form)
417     {
418        
419         if (this.childForms.indexOf(form) > -1) {
420             // already added..
421             return;
422         }
423         this.childForms.push(form);
424         var n = '';
425         Roo.each(form.allItems, function (fe) {
426             
427             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
428             if (this.findField(n)) { // already added..
429                 return;
430             }
431             var add = new Roo.form.Hidden({
432                 name : n
433             });
434             add.render(this.el);
435             
436             this.add( add );
437         }, this);
438         
439     },
440     /**
441      * Mark fields in this form invalid in bulk.
442      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
443      * @return {BasicForm} this
444      */
445     markInvalid : function(errors){
446         if(errors instanceof Array){
447             for(var i = 0, len = errors.length; i < len; i++){
448                 var fieldError = errors[i];
449                 var f = this.findField(fieldError.id);
450                 if(f){
451                     f.markInvalid(fieldError.msg);
452                 }
453             }
454         }else{
455             var field, id;
456             for(id in errors){
457                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
458                     field.markInvalid(errors[id]);
459                 }
460             }
461         }
462         Roo.each(this.childForms || [], function (f) {
463             f.markInvalid(errors);
464         });
465         
466         return this;
467     },
468
469     /**
470      * Set values for fields in this form in bulk.
471      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
472      * @return {BasicForm} this
473      */
474     setValues : function(values){
475         if(values instanceof Array){ // array of objects
476             for(var i = 0, len = values.length; i < len; i++){
477                 var v = values[i];
478                 var f = this.findField(v.id);
479                 if(f){
480                     f.setValue(v.value);
481                     if(this.trackResetOnLoad){
482                         f.originalValue = f.getValue();
483                     }
484                 }
485             }
486         }else{ // object hash
487             var field, id;
488             for(id in values){
489                 if(typeof values[id] != 'function' && (field = this.findField(id))){
490                     
491                     if (field.setFromData && 
492                         field.valueField && 
493                         field.displayField &&
494                         // combos' with local stores can 
495                         // be queried via setValue()
496                         // to set their value..
497                         (field.store && !field.store.isLocal)
498                         ) {
499                         // it's a combo
500                         var sd = { };
501                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
502                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
503                         field.setFromData(sd);
504                         
505                     } else {
506                         field.setValue(values[id]);
507                     }
508                     
509                     
510                     if(this.trackResetOnLoad){
511                         field.originalValue = field.getValue();
512                     }
513                 }
514             }
515         }
516         this.resetHasChanged();
517         
518         
519         Roo.each(this.childForms || [], function (f) {
520             f.setValues(values);
521             f.resetHasChanged();
522         });
523                 
524         return this;
525     },
526  
527     /**
528      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
529      * they are returned as an array.
530      * @param {Boolean} asString
531      * @return {Object}
532      */
533     getValues : function(asString){
534         if (this.childForms) {
535             // copy values from the child forms
536             Roo.each(this.childForms, function (f) {
537                 this.setValues(f.getValues());
538             }, this);
539         }
540         
541         // use formdata
542         if (typeof(FormData) != 'undefined' && asString !== true) {
543             // this relies on a 'recent' version of chrome apparently...
544             try {
545                 var fd = (new FormData(this.el.dom)).entries();
546                 var ret = {};
547                 var ent = fd.next();
548                 while (!ent.done) {
549                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
550                     ent = fd.next();
551                 };
552                 return ret;
553             } catch(e) {
554                 
555             }
556             
557         }
558         
559         
560         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
561         if(asString === true){
562             return fs;
563         }
564         return Roo.urlDecode(fs);
565     },
566     
567     /**
568      * Returns the fields in this form as an object with key/value pairs. 
569      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
570      * @return {Object}
571      */
572     getFieldValues : function(with_hidden)
573     {
574         if (this.childForms) {
575             // copy values from the child forms
576             // should this call getFieldValues - probably not as we do not currently copy
577             // hidden fields when we generate..
578             Roo.each(this.childForms, function (f) {
579                 this.setValues(f.getValues());
580             }, this);
581         }
582         
583         var ret = {};
584         this.items.each(function(f){
585             if (!f.getName()) {
586                 return;
587             }
588             var v = f.getValue();
589             if (f.inputType =='radio') {
590                 if (typeof(ret[f.getName()]) == 'undefined') {
591                     ret[f.getName()] = ''; // empty..
592                 }
593                 
594                 if (!f.el.dom.checked) {
595                     return;
596                     
597                 }
598                 v = f.el.dom.value;
599                 
600             }
601             
602             // not sure if this supported any more..
603             if ((typeof(v) == 'object') && f.getRawValue) {
604                 v = f.getRawValue() ; // dates..
605             }
606             // combo boxes where name != hiddenName...
607             if (f.name != f.getName()) {
608                 ret[f.name] = f.getRawValue();
609             }
610             ret[f.getName()] = v;
611         });
612         
613         return ret;
614     },
615
616     /**
617      * Clears all invalid messages in this form.
618      * @return {BasicForm} this
619      */
620     clearInvalid : function(){
621         this.items.each(function(f){
622            f.clearInvalid();
623         });
624         
625         Roo.each(this.childForms || [], function (f) {
626             f.clearInvalid();
627         });
628         
629         
630         return this;
631     },
632
633     /**
634      * Resets this form.
635      * @return {BasicForm} this
636      */
637     reset : function(){
638         this.items.each(function(f){
639             f.reset();
640         });
641         
642         Roo.each(this.childForms || [], function (f) {
643             f.reset();
644         });
645         this.resetHasChanged();
646         
647         return this;
648     },
649
650     /**
651      * Add Roo.form components to this form.
652      * @param {Field} field1
653      * @param {Field} field2 (optional)
654      * @param {Field} etc (optional)
655      * @return {BasicForm} this
656      */
657     add : function(){
658         this.items.addAll(Array.prototype.slice.call(arguments, 0));
659         return this;
660     },
661
662
663     /**
664      * Removes a field from the items collection (does NOT remove its markup).
665      * @param {Field} field
666      * @return {BasicForm} this
667      */
668     remove : function(field){
669         this.items.remove(field);
670         return this;
671     },
672
673     /**
674      * Looks at the fields in this form, checks them for an id attribute,
675      * and calls applyTo on the existing dom element with that id.
676      * @return {BasicForm} this
677      */
678     render : function(){
679         this.items.each(function(f){
680             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
681                 f.applyTo(f.id);
682             }
683         });
684         return this;
685     },
686
687     /**
688      * Calls {@link Ext#apply} for all fields in this form with the passed object.
689      * @param {Object} values
690      * @return {BasicForm} this
691      */
692     applyToFields : function(o){
693         this.items.each(function(f){
694            Roo.apply(f, o);
695         });
696         return this;
697     },
698
699     /**
700      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
701      * @param {Object} values
702      * @return {BasicForm} this
703      */
704     applyIfToFields : function(o){
705         this.items.each(function(f){
706            Roo.applyIf(f, o);
707         });
708         return this;
709     }
710 });
711
712 // back compat
713 Roo.BasicForm = Roo.form.BasicForm;
714
715 Roo.apply(Roo.form.BasicForm, {
716     
717     popover : {
718         
719         padding : 5,
720         
721         isApplied : false,
722         
723         isMasked : false,
724         
725         form : false,
726         
727         target : false,
728         
729         intervalID : false,
730         
731         maskEl : false,
732         
733         apply : function()
734         {
735             if(this.isApplied){
736                 return;
737             }
738             
739             this.maskEl = {
740                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
741                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
742                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
743                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
744             };
745             
746             this.maskEl.top.enableDisplayMode("block");
747             this.maskEl.left.enableDisplayMode("block");
748             this.maskEl.bottom.enableDisplayMode("block");
749             this.maskEl.right.enableDisplayMode("block");
750             
751             Roo.get(document.body).on('click', function(){
752                 this.unmask();
753             }, this);
754             
755             Roo.get(document.body).on('touchstart', function(){
756                 this.unmask();
757             }, this);
758             
759             this.isApplied = true
760         },
761         
762         mask : function(form, target)
763         {
764             this.form = form;
765             
766             this.target = target;
767             
768             if(!this.form.errorMask || !target.el){
769                 return;
770             }
771             
772             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
773             
774             var ot = this.target.el.calcOffsetsTo(scrollable);
775             
776             var scrollTo = ot[1] - this.form.maskOffset;
777             
778             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
779             
780             scrollable.scrollTo('top', scrollTo);
781             
782             var el = this.target.wrap || this.target.el;
783             
784             var box = el.getBox();
785             
786             this.maskEl.top.setStyle('position', 'absolute');
787             this.maskEl.top.setStyle('z-index', 10000);
788             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
789             this.maskEl.top.setLeft(0);
790             this.maskEl.top.setTop(0);
791             this.maskEl.top.show();
792             
793             this.maskEl.left.setStyle('position', 'absolute');
794             this.maskEl.left.setStyle('z-index', 10000);
795             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
796             this.maskEl.left.setLeft(0);
797             this.maskEl.left.setTop(box.y - this.padding);
798             this.maskEl.left.show();
799
800             this.maskEl.bottom.setStyle('position', 'absolute');
801             this.maskEl.bottom.setStyle('z-index', 10000);
802             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
803             this.maskEl.bottom.setLeft(0);
804             this.maskEl.bottom.setTop(box.bottom + this.padding);
805             this.maskEl.bottom.show();
806
807             this.maskEl.right.setStyle('position', 'absolute');
808             this.maskEl.right.setStyle('z-index', 10000);
809             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
810             this.maskEl.right.setLeft(box.right + this.padding);
811             this.maskEl.right.setTop(box.y - this.padding);
812             this.maskEl.right.show();
813
814             this.intervalID = window.setInterval(function() {
815                 Roo.form.BasicForm.popover.unmask();
816             }, 10000);
817
818             window.onwheel = function(){ return false;};
819             
820             (function(){ this.isMasked = true; }).defer(500, this);
821             
822         },
823         
824         unmask : function()
825         {
826             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
827                 return;
828             }
829             
830             this.maskEl.top.setStyle('position', 'absolute');
831             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
832             this.maskEl.top.hide();
833
834             this.maskEl.left.setStyle('position', 'absolute');
835             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
836             this.maskEl.left.hide();
837
838             this.maskEl.bottom.setStyle('position', 'absolute');
839             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
840             this.maskEl.bottom.hide();
841
842             this.maskEl.right.setStyle('position', 'absolute');
843             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
844             this.maskEl.right.hide();
845             
846             window.onwheel = function(){ return true;};
847             
848             if(this.intervalID){
849                 window.clearInterval(this.intervalID);
850                 this.intervalID = false;
851             }
852             
853             this.isMasked = false;
854             
855         }
856         
857     }
858     
859 });