docs/default.css
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         };
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             };
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             Roo.log(settings[size]);
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa (ban|check|...) font awesome icon
989  * @cfg {String} icon (info-sign|check|...) glyphicon name
990  * @cfg {Boolean} hidden (true|false) hide the element
991  * @cfg {Boolean} expandable (true|false) default false
992  * @cfg {Boolean} expanded (true|false) default true
993  * @cfg {String} rheader contet on the right of header
994
995  *     
996  * @constructor
997  * Create a new Container
998  * @param {Object} config The config object
999  */
1000
1001 Roo.bootstrap.Container = function(config){
1002     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1003     
1004     this.addEvents({
1005         // raw events
1006          /**
1007          * @event expand
1008          * After the panel has been expand
1009          * 
1010          * @param {Roo.bootstrap.Container} this
1011          */
1012         "expand" : true,
1013         /**
1014          * @event collapse
1015          * After the panel has been collapsed
1016          * 
1017          * @param {Roo.bootstrap.Container} this
1018          */
1019         "collapse" : true
1020     });
1021 };
1022
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1024     
1025     jumbotron : false,
1026     well: '',
1027     panel : '',
1028     header: '',
1029     footer : '',
1030     sticky: '',
1031     tag : false,
1032     alert : false,
1033     fa: false,
1034     icon : false,
1035     expandable : false,
1036     rheader : '',
1037     expanded : true,
1038   
1039      
1040     getChildContainer : function() {
1041         
1042         if(!this.el){
1043             return false;
1044         }
1045         
1046         if (this.panel.length) {
1047             return this.el.select('.panel-body',true).first();
1048         }
1049         
1050         return this.el;
1051     },
1052     
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : this.tag || 'div',
1058             html : '',
1059             cls : ''
1060         };
1061         if (this.jumbotron) {
1062             cfg.cls = 'jumbotron';
1063         }
1064         
1065         
1066         
1067         // - this is applied by the parent..
1068         //if (this.cls) {
1069         //    cfg.cls = this.cls + '';
1070         //}
1071         
1072         if (this.sticky.length) {
1073             
1074             var bd = Roo.get(document.body);
1075             if (!bd.hasClass('bootstrap-sticky')) {
1076                 bd.addClass('bootstrap-sticky');
1077                 Roo.select('html',true).setStyle('height', '100%');
1078             }
1079              
1080             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1081         }
1082         
1083         
1084         if (this.well.length) {
1085             switch (this.well) {
1086                 case 'lg':
1087                 case 'sm':
1088                     cfg.cls +=' well well-' +this.well;
1089                     break;
1090                 default:
1091                     cfg.cls +=' well';
1092                     break;
1093             }
1094         }
1095         
1096         if (this.hidden) {
1097             cfg.cls += ' hidden';
1098         }
1099         
1100         
1101         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102             cfg.cls +=' alert alert-' + this.alert;
1103         }
1104         
1105         var body = cfg;
1106         
1107         if (this.panel.length) {
1108             cfg.cls += ' panel panel-' + this.panel;
1109             cfg.cn = [];
1110             if (this.header.length) {
1111                 
1112                 var h = [];
1113                 
1114                 if(this.expandable){
1115                     
1116                     cfg.cls = cfg.cls + ' expandable';
1117                     
1118                     h.push({
1119                         tag: 'i',
1120                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1121                     });
1122                     
1123                 }
1124                 
1125                 h.push(
1126                     {
1127                         tag: 'span',
1128                         cls : 'panel-title',
1129                         html : (this.expandable ? '&nbsp;' : '') + this.header
1130                     },
1131                     {
1132                         tag: 'span',
1133                         cls: 'panel-header-right',
1134                         html: this.rheader
1135                     }
1136                 );
1137                 
1138                 cfg.cn.push({
1139                     cls : 'panel-heading',
1140                     style : this.expandable ? 'cursor: pointer' : '',
1141                     cn : h
1142                 });
1143                 
1144             }
1145             
1146             body = false;
1147             cfg.cn.push({
1148                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1149                 html : this.html
1150             });
1151             
1152             
1153             if (this.footer.length) {
1154                 cfg.cn.push({
1155                     cls : 'panel-footer',
1156                     html : this.footer
1157                     
1158                 });
1159             }
1160             
1161         }
1162         
1163         if (body) {
1164             body.html = this.html || cfg.html;
1165             // prefix with the icons..
1166             if (this.fa) {
1167                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1168             }
1169             if (this.icon) {
1170                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1171             }
1172             
1173             
1174         }
1175         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176             cfg.cls =  'container';
1177         }
1178         
1179         return cfg;
1180     },
1181     
1182     initEvents: function() 
1183     {
1184         if(!this.expandable){
1185             return;
1186         }
1187         
1188         var headerEl = this.headerEl();
1189         
1190         if(!headerEl){
1191             return;
1192         }
1193         
1194         headerEl.on('click', this.onToggleClick, this);
1195         
1196     },
1197     
1198     onToggleClick : function()
1199     {
1200         var headerEl = this.headerEl();
1201         
1202         if(!headerEl){
1203             return;
1204         }
1205         
1206         if(this.expanded){
1207             this.collapse();
1208             return;
1209         }
1210         
1211         this.expand();
1212     },
1213     
1214     expand : function()
1215     {
1216         if(this.fireEvent('expand', this)) {
1217             
1218             this.expanded = true;
1219             
1220             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1221             
1222             this.el.select('.panel-body',true).first().removeClass('hide');
1223             
1224             var toggleEl = this.toggleEl();
1225
1226             if(!toggleEl){
1227                 return;
1228             }
1229
1230             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1231         }
1232         
1233     },
1234     
1235     collapse : function()
1236     {
1237         if(this.fireEvent('collapse', this)) {
1238             
1239             this.expanded = false;
1240             
1241             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242             this.el.select('.panel-body',true).first().addClass('hide');
1243         
1244             var toggleEl = this.toggleEl();
1245
1246             if(!toggleEl){
1247                 return;
1248             }
1249
1250             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1251         }
1252     },
1253     
1254     toggleEl : function()
1255     {
1256         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1257             return;
1258         }
1259         
1260         return this.el.select('.panel-heading .fa',true).first();
1261     },
1262     
1263     headerEl : function()
1264     {
1265         if(!this.el || !this.panel.length || !this.header.length){
1266             return;
1267         }
1268         
1269         return this.el.select('.panel-heading',true).first()
1270     },
1271     
1272     titleEl : function()
1273     {
1274         if(!this.el || !this.panel.length || !this.header.length){
1275             return;
1276         }
1277         
1278         return this.el.select('.panel-title',true).first();
1279     },
1280     
1281     setTitle : function(v)
1282     {
1283         var titleEl = this.titleEl();
1284         
1285         if(!titleEl){
1286             return;
1287         }
1288         
1289         titleEl.dom.innerHTML = v;
1290     },
1291     
1292     getTitle : function()
1293     {
1294         
1295         var titleEl = this.titleEl();
1296         
1297         if(!titleEl){
1298             return '';
1299         }
1300         
1301         return titleEl.dom.innerHTML;
1302     },
1303     
1304     setRightTitle : function(v)
1305     {
1306         var t = this.el.select('.panel-header-right',true).first();
1307         
1308         if(!t){
1309             return;
1310         }
1311         
1312         t.dom.innerHTML = v;
1313     }
1314    
1315 });
1316
1317  /*
1318  * - LGPL
1319  *
1320  * image
1321  * 
1322  */
1323
1324
1325 /**
1326  * @class Roo.bootstrap.Img
1327  * @extends Roo.bootstrap.Component
1328  * Bootstrap Img class
1329  * @cfg {Boolean} imgResponsive false | true
1330  * @cfg {String} border rounded | circle | thumbnail
1331  * @cfg {String} src image source
1332  * @cfg {String} alt image alternative text
1333  * @cfg {String} href a tag href
1334  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335  * @cfg {String} xsUrl xs image source
1336  * @cfg {String} smUrl sm image source
1337  * @cfg {String} mdUrl md image source
1338  * @cfg {String} lgUrl lg image source
1339  * 
1340  * @constructor
1341  * Create a new Input
1342  * @param {Object} config The config object
1343  */
1344
1345 Roo.bootstrap.Img = function(config){
1346     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1347     
1348     this.addEvents({
1349         // img events
1350         /**
1351          * @event click
1352          * The img click event for the img.
1353          * @param {Roo.EventObject} e
1354          */
1355         "click" : true
1356     });
1357 };
1358
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1360     
1361     imgResponsive: true,
1362     border: '',
1363     src: '',
1364     href: false,
1365     target: false,
1366     xsUrl: '',
1367     smUrl: '',
1368     mdUrl: '',
1369     lgUrl: '',
1370
1371     getAutoCreate : function()
1372     {   
1373         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374             return this.createSingleImg();
1375         }
1376         
1377         var cfg = {
1378             tag: 'div',
1379             cls: 'roo-image-responsive-group',
1380             cn: []
1381         };
1382         var _this = this;
1383         
1384         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1385             
1386             if(!_this[size + 'Url']){
1387                 return;
1388             }
1389             
1390             var img = {
1391                 tag: 'img',
1392                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393                 html: _this.html || cfg.html,
1394                 src: _this[size + 'Url']
1395             };
1396             
1397             img.cls += ' roo-image-responsive-' + size;
1398             
1399             var s = ['xs', 'sm', 'md', 'lg'];
1400             
1401             s.splice(s.indexOf(size), 1);
1402             
1403             Roo.each(s, function(ss){
1404                 img.cls += ' hidden-' + ss;
1405             });
1406             
1407             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408                 cfg.cls += ' img-' + _this.border;
1409             }
1410             
1411             if(_this.alt){
1412                 cfg.alt = _this.alt;
1413             }
1414             
1415             if(_this.href){
1416                 var a = {
1417                     tag: 'a',
1418                     href: _this.href,
1419                     cn: [
1420                         img
1421                     ]
1422                 };
1423
1424                 if(this.target){
1425                     a.target = _this.target;
1426                 }
1427             }
1428             
1429             cfg.cn.push((_this.href) ? a : img);
1430             
1431         });
1432         
1433         return cfg;
1434     },
1435     
1436     createSingleImg : function()
1437     {
1438         var cfg = {
1439             tag: 'img',
1440             cls: (this.imgResponsive) ? 'img-responsive' : '',
1441             html : null
1442         };
1443         
1444         cfg.html = this.html || cfg.html;
1445         
1446         cfg.src = this.src || cfg.src;
1447         
1448         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449             cfg.cls += ' img-' + this.border;
1450         }
1451         
1452         if(this.alt){
1453             cfg.alt = this.alt;
1454         }
1455         
1456         if(this.href){
1457             var a = {
1458                 tag: 'a',
1459                 href: this.href,
1460                 cn: [
1461                     cfg
1462                 ]
1463             };
1464             
1465             if(this.target){
1466                 a.target = this.target;
1467             }
1468             
1469         }
1470         
1471         return (this.href) ? a : cfg;
1472     },
1473     
1474     initEvents: function() 
1475     {
1476         if(!this.href){
1477             this.el.on('click', this.onClick, this);
1478         }
1479         
1480     },
1481     
1482     onClick : function(e)
1483     {
1484         Roo.log('img onclick');
1485         this.fireEvent('click', this, e);
1486     }
1487    
1488 });
1489
1490  /*
1491  * - LGPL
1492  *
1493  * image
1494  * 
1495  */
1496
1497
1498 /**
1499  * @class Roo.bootstrap.Link
1500  * @extends Roo.bootstrap.Component
1501  * Bootstrap Link Class
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505  * @cfg {String} html the content of the link.
1506  * @cfg {String} anchor name for the anchor link
1507
1508  * @cfg {Boolean} preventDefault (true | false) default false
1509
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Link = function(config){
1517     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1531     
1532     href: false,
1533     target: false,
1534     preventDefault: false,
1535     anchor : false,
1536     alt : false,
1537
1538     getAutoCreate : function()
1539     {
1540         
1541         var cfg = {
1542             tag: 'a'
1543         };
1544         // anchor's do not require html/href...
1545         if (this.anchor === false) {
1546             cfg.html = this.html || '';
1547             cfg.href = this.href || '#';
1548         } else {
1549             cfg.name = this.anchor;
1550             if (this.html !== false) {
1551                 cfg.html = this.html;
1552             }
1553             if (this.href !== false) {
1554                 cfg.href = this.href;
1555             }
1556         }
1557         
1558         if(this.alt !== false){
1559             cfg.alt = this.alt;
1560         }
1561         
1562         
1563         if(this.target !== false) {
1564             cfg.target = this.target;
1565         }
1566         
1567         return cfg;
1568     },
1569     
1570     initEvents: function() {
1571         
1572         if(!this.href || this.preventDefault){
1573             this.el.on('click', this.onClick, this);
1574         }
1575     },
1576     
1577     onClick : function(e)
1578     {
1579         if(this.preventDefault){
1580             e.preventDefault();
1581         }
1582         //Roo.log('img onclick');
1583         this.fireEvent('click', this, e);
1584     }
1585    
1586 });
1587
1588  /*
1589  * - LGPL
1590  *
1591  * header
1592  * 
1593  */
1594
1595 /**
1596  * @class Roo.bootstrap.Header
1597  * @extends Roo.bootstrap.Component
1598  * Bootstrap Header class
1599  * @cfg {String} html content of header
1600  * @cfg {Number} level (1|2|3|4|5|6) default 1
1601  * 
1602  * @constructor
1603  * Create a new Header
1604  * @param {Object} config The config object
1605  */
1606
1607
1608 Roo.bootstrap.Header  = function(config){
1609     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1610 };
1611
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1613     
1614     //href : false,
1615     html : false,
1616     level : 1,
1617     
1618     
1619     
1620     getAutoCreate : function(){
1621         
1622         
1623         
1624         var cfg = {
1625             tag: 'h' + (1 *this.level),
1626             html: this.html || ''
1627         } ;
1628         
1629         return cfg;
1630     }
1631    
1632 });
1633
1634  
1635
1636  /*
1637  * Based on:
1638  * Ext JS Library 1.1.1
1639  * Copyright(c) 2006-2007, Ext JS, LLC.
1640  *
1641  * Originally Released Under LGPL - original licence link has changed is not relivant.
1642  *
1643  * Fork - LGPL
1644  * <script type="text/javascript">
1645  */
1646  
1647 /**
1648  * @class Roo.bootstrap.MenuMgr
1649  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1650  * @singleton
1651  */
1652 Roo.bootstrap.MenuMgr = function(){
1653    var menus, active, groups = {}, attached = false, lastShow = new Date();
1654
1655    // private - called when first menu is created
1656    function init(){
1657        menus = {};
1658        active = new Roo.util.MixedCollection();
1659        Roo.get(document).addKeyListener(27, function(){
1660            if(active.length > 0){
1661                hideAll();
1662            }
1663        });
1664    }
1665
1666    // private
1667    function hideAll(){
1668        if(active && active.length > 0){
1669            var c = active.clone();
1670            c.each(function(m){
1671                m.hide();
1672            });
1673        }
1674    }
1675
1676    // private
1677    function onHide(m){
1678        active.remove(m);
1679        if(active.length < 1){
1680            Roo.get(document).un("mouseup", onMouseDown);
1681             
1682            attached = false;
1683        }
1684    }
1685
1686    // private
1687    function onShow(m){
1688        var last = active.last();
1689        lastShow = new Date();
1690        active.add(m);
1691        if(!attached){
1692           Roo.get(document).on("mouseup", onMouseDown);
1693            
1694            attached = true;
1695        }
1696        if(m.parentMenu){
1697           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698           m.parentMenu.activeChild = m;
1699        }else if(last && last.isVisible()){
1700           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1701        }
1702    }
1703
1704    // private
1705    function onBeforeHide(m){
1706        if(m.activeChild){
1707            m.activeChild.hide();
1708        }
1709        if(m.autoHideTimer){
1710            clearTimeout(m.autoHideTimer);
1711            delete m.autoHideTimer;
1712        }
1713    }
1714
1715    // private
1716    function onBeforeShow(m){
1717        var pm = m.parentMenu;
1718        if(!pm && !m.allowOtherMenus){
1719            hideAll();
1720        }else if(pm && pm.activeChild && active != m){
1721            pm.activeChild.hide();
1722        }
1723    }
1724
1725    // private this should really trigger on mouseup..
1726    function onMouseDown(e){
1727         Roo.log("on Mouse Up");
1728         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1729             Roo.log("hideAll");
1730             hideAll();
1731             e.stopEvent();
1732         }
1733         
1734         
1735    }
1736
1737    // private
1738    function onBeforeCheck(mi, state){
1739        if(state){
1740            var g = groups[mi.group];
1741            for(var i = 0, l = g.length; i < l; i++){
1742                if(g[i] != mi){
1743                    g[i].setChecked(false);
1744                }
1745            }
1746        }
1747    }
1748
1749    return {
1750
1751        /**
1752         * Hides all menus that are currently visible
1753         */
1754        hideAll : function(){
1755             hideAll();  
1756        },
1757
1758        // private
1759        register : function(menu){
1760            if(!menus){
1761                init();
1762            }
1763            menus[menu.id] = menu;
1764            menu.on("beforehide", onBeforeHide);
1765            menu.on("hide", onHide);
1766            menu.on("beforeshow", onBeforeShow);
1767            menu.on("show", onShow);
1768            var g = menu.group;
1769            if(g && menu.events["checkchange"]){
1770                if(!groups[g]){
1771                    groups[g] = [];
1772                }
1773                groups[g].push(menu);
1774                menu.on("checkchange", onCheck);
1775            }
1776        },
1777
1778         /**
1779          * Returns a {@link Roo.menu.Menu} object
1780          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781          * be used to generate and return a new Menu instance.
1782          */
1783        get : function(menu){
1784            if(typeof menu == "string"){ // menu id
1785                return menus[menu];
1786            }else if(menu.events){  // menu instance
1787                return menu;
1788            }
1789            /*else if(typeof menu.length == 'number'){ // array of menu items?
1790                return new Roo.bootstrap.Menu({items:menu});
1791            }else{ // otherwise, must be a config
1792                return new Roo.bootstrap.Menu(menu);
1793            }
1794            */
1795            return false;
1796        },
1797
1798        // private
1799        unregister : function(menu){
1800            delete menus[menu.id];
1801            menu.un("beforehide", onBeforeHide);
1802            menu.un("hide", onHide);
1803            menu.un("beforeshow", onBeforeShow);
1804            menu.un("show", onShow);
1805            var g = menu.group;
1806            if(g && menu.events["checkchange"]){
1807                groups[g].remove(menu);
1808                menu.un("checkchange", onCheck);
1809            }
1810        },
1811
1812        // private
1813        registerCheckable : function(menuItem){
1814            var g = menuItem.group;
1815            if(g){
1816                if(!groups[g]){
1817                    groups[g] = [];
1818                }
1819                groups[g].push(menuItem);
1820                menuItem.on("beforecheckchange", onBeforeCheck);
1821            }
1822        },
1823
1824        // private
1825        unregisterCheckable : function(menuItem){
1826            var g = menuItem.group;
1827            if(g){
1828                groups[g].remove(menuItem);
1829                menuItem.un("beforecheckchange", onBeforeCheck);
1830            }
1831        }
1832    };
1833 }();/*
1834  * - LGPL
1835  *
1836  * menu
1837  * 
1838  */
1839
1840 /**
1841  * @class Roo.bootstrap.Menu
1842  * @extends Roo.bootstrap.Component
1843  * Bootstrap Menu class - container for MenuItems
1844  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1845  * 
1846  * @constructor
1847  * Create a new Menu
1848  * @param {Object} config The config object
1849  */
1850
1851
1852 Roo.bootstrap.Menu = function(config){
1853     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854     if (this.registerMenu) {
1855         Roo.bootstrap.MenuMgr.register(this);
1856     }
1857     this.addEvents({
1858         /**
1859          * @event beforeshow
1860          * Fires before this menu is displayed
1861          * @param {Roo.menu.Menu} this
1862          */
1863         beforeshow : true,
1864         /**
1865          * @event beforehide
1866          * Fires before this menu is hidden
1867          * @param {Roo.menu.Menu} this
1868          */
1869         beforehide : true,
1870         /**
1871          * @event show
1872          * Fires after this menu is displayed
1873          * @param {Roo.menu.Menu} this
1874          */
1875         show : true,
1876         /**
1877          * @event hide
1878          * Fires after this menu is hidden
1879          * @param {Roo.menu.Menu} this
1880          */
1881         hide : true,
1882         /**
1883          * @event click
1884          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885          * @param {Roo.menu.Menu} this
1886          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887          * @param {Roo.EventObject} e
1888          */
1889         click : true,
1890         /**
1891          * @event mouseover
1892          * Fires when the mouse is hovering over this menu
1893          * @param {Roo.menu.Menu} this
1894          * @param {Roo.EventObject} e
1895          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1896          */
1897         mouseover : true,
1898         /**
1899          * @event mouseout
1900          * Fires when the mouse exits this menu
1901          * @param {Roo.menu.Menu} this
1902          * @param {Roo.EventObject} e
1903          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1904          */
1905         mouseout : true,
1906         /**
1907          * @event itemclick
1908          * Fires when a menu item contained in this menu is clicked
1909          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910          * @param {Roo.EventObject} e
1911          */
1912         itemclick: true
1913     });
1914     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1915 };
1916
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1918     
1919    /// html : false,
1920     //align : '',
1921     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1922     type: false,
1923     /**
1924      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1925      */
1926     registerMenu : true,
1927     
1928     menuItems :false, // stores the menu items..
1929     
1930     hidden:true,
1931     
1932     parentMenu : false,
1933     
1934     getChildContainer : function() {
1935         return this.el;  
1936     },
1937     
1938     getAutoCreate : function(){
1939          
1940         //if (['right'].indexOf(this.align)!==-1) {
1941         //    cfg.cn[1].cls += ' pull-right'
1942         //}
1943         
1944         
1945         var cfg = {
1946             tag : 'ul',
1947             cls : 'dropdown-menu' ,
1948             style : 'z-index:1000'
1949             
1950         };
1951         
1952         if (this.type === 'submenu') {
1953             cfg.cls = 'submenu active';
1954         }
1955         if (this.type === 'treeview') {
1956             cfg.cls = 'treeview-menu';
1957         }
1958         
1959         return cfg;
1960     },
1961     initEvents : function() {
1962         
1963        // Roo.log("ADD event");
1964        // Roo.log(this.triggerEl.dom);
1965         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1966         
1967         this.triggerEl.addClass('dropdown-toggle');
1968         
1969         
1970         
1971         
1972         if (Roo.isTouch) {
1973             this.el.on('touchstart'  , this.onTouch, this);
1974         }
1975         this.el.on('click' , this.onClick, this);
1976
1977         this.el.on("mouseover", this.onMouseOver, this);
1978         this.el.on("mouseout", this.onMouseOut, this);
1979         
1980         
1981     },
1982     findTargetItem : function(e){
1983         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1984         if(!t){
1985             return false;
1986         }
1987         //Roo.log(t);         Roo.log(t.id);
1988         if(t && t.id){
1989             //Roo.log(this.menuitems);
1990             return this.menuitems.get(t.id);
1991             
1992             //return this.items.get(t.menuItemId);
1993         }
1994         
1995         return false;
1996     },
1997     
1998     onTouch : function(e) {
1999         e.stopEvent();
2000         this.onClick(e);
2001     },
2002     
2003     onClick : function(e){
2004         Roo.log("menu.onClick");
2005         var t = this.findTargetItem(e);
2006         if(!t || t.isContainer){
2007             return;
2008         }
2009         Roo.log(e);
2010         /*
2011         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2012             if(t == this.activeItem && t.shouldDeactivate(e)){
2013                 this.activeItem.deactivate();
2014                 delete this.activeItem;
2015                 return;
2016             }
2017             if(t.canActivate){
2018                 this.setActiveItem(t, true);
2019             }
2020             return;
2021             
2022             
2023         }
2024         */
2025        
2026         Roo.log('pass click event');
2027         
2028         t.onClick(e);
2029         
2030         this.fireEvent("click", this, t, e);
2031         
2032         this.hide();
2033     },
2034      onMouseOver : function(e){
2035         var t  = this.findTargetItem(e);
2036         //Roo.log(t);
2037         //if(t){
2038         //    if(t.canActivate && !t.disabled){
2039         //        this.setActiveItem(t, true);
2040         //    }
2041         //}
2042         
2043         this.fireEvent("mouseover", this, e, t);
2044     },
2045     isVisible : function(){
2046         return !this.hidden;
2047     },
2048      onMouseOut : function(e){
2049         var t  = this.findTargetItem(e);
2050         
2051         //if(t ){
2052         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2053         //        this.activeItem.deactivate();
2054         //        delete this.activeItem;
2055         //    }
2056         //}
2057         this.fireEvent("mouseout", this, e, t);
2058     },
2059     
2060     
2061     /**
2062      * Displays this menu relative to another element
2063      * @param {String/HTMLElement/Roo.Element} element The element to align to
2064      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2065      * the element (defaults to this.defaultAlign)
2066      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2067      */
2068     show : function(el, pos, parentMenu){
2069         this.parentMenu = parentMenu;
2070         if(!this.el){
2071             this.render();
2072         }
2073         this.fireEvent("beforeshow", this);
2074         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2075     },
2076      /**
2077      * Displays this menu at a specific xy position
2078      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2079      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2080      */
2081     showAt : function(xy, parentMenu, /* private: */_e){
2082         this.parentMenu = parentMenu;
2083         if(!this.el){
2084             this.render();
2085         }
2086         if(_e !== false){
2087             this.fireEvent("beforeshow", this);
2088             //xy = this.el.adjustForConstraints(xy);
2089         }
2090         
2091         //this.el.show();
2092         this.hideMenuItems();
2093         this.hidden = false;
2094         this.triggerEl.addClass('open');
2095         
2096         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2097             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2098         }
2099         
2100         this.el.setXY(xy);
2101         this.focus();
2102         this.fireEvent("show", this);
2103     },
2104     
2105     focus : function(){
2106         return;
2107         if(!this.hidden){
2108             this.doFocus.defer(50, this);
2109         }
2110     },
2111
2112     doFocus : function(){
2113         if(!this.hidden){
2114             this.focusEl.focus();
2115         }
2116     },
2117
2118     /**
2119      * Hides this menu and optionally all parent menus
2120      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2121      */
2122     hide : function(deep){
2123         
2124         this.hideMenuItems();
2125         if(this.el && this.isVisible()){
2126             this.fireEvent("beforehide", this);
2127             if(this.activeItem){
2128                 this.activeItem.deactivate();
2129                 this.activeItem = null;
2130             }
2131             this.triggerEl.removeClass('open');;
2132             this.hidden = true;
2133             this.fireEvent("hide", this);
2134         }
2135         if(deep === true && this.parentMenu){
2136             this.parentMenu.hide(true);
2137         }
2138     },
2139     
2140     onTriggerPress  : function(e)
2141     {
2142         
2143         Roo.log('trigger press');
2144         //Roo.log(e.getTarget());
2145        // Roo.log(this.triggerEl.dom);
2146         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2147             return;
2148         }
2149         
2150         if (this.isVisible()) {
2151             Roo.log('hide');
2152             this.hide();
2153         } else {
2154             Roo.log('show');
2155             this.show(this.triggerEl, false, false);
2156         }
2157         
2158         e.stopEvent();
2159     },
2160     
2161          
2162        
2163     
2164     hideMenuItems : function()
2165     {
2166         //$(backdrop).remove()
2167         Roo.select('.open',true).each(function(aa) {
2168             
2169             aa.removeClass('open');
2170           //var parent = getParent($(this))
2171           //var relatedTarget = { relatedTarget: this }
2172           
2173            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2174           //if (e.isDefaultPrevented()) return
2175            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2176         })
2177     },
2178     addxtypeChild : function (tree, cntr) {
2179         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2180           
2181         this.menuitems.add(comp);
2182         return comp;
2183
2184     },
2185     getEl : function()
2186     {
2187         Roo.log(this.el);
2188         return this.el;
2189     }
2190 });
2191
2192  
2193  /*
2194  * - LGPL
2195  *
2196  * menu item
2197  * 
2198  */
2199
2200
2201 /**
2202  * @class Roo.bootstrap.MenuItem
2203  * @extends Roo.bootstrap.Component
2204  * Bootstrap MenuItem class
2205  * @cfg {String} html the menu label
2206  * @cfg {String} href the link
2207  * @cfg {Boolean} preventDefault (true | false) default true
2208  * @cfg {Boolean} isContainer (true | false) default false
2209  * 
2210  * 
2211  * @constructor
2212  * Create a new MenuItem
2213  * @param {Object} config The config object
2214  */
2215
2216
2217 Roo.bootstrap.MenuItem = function(config){
2218     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2219     this.addEvents({
2220         // raw events
2221         /**
2222          * @event click
2223          * The raw click event for the entire grid.
2224          * @param {Roo.bootstrap.MenuItem} this
2225          * @param {Roo.EventObject} e
2226          */
2227         "click" : true
2228     });
2229 };
2230
2231 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2232     
2233     href : false,
2234     html : false,
2235     preventDefault: true,
2236     isContainer : false,
2237     
2238     getAutoCreate : function(){
2239         
2240         if(this.isContainer){
2241             return {
2242                 tag: 'li',
2243                 cls: 'dropdown-menu-item'
2244             };
2245         }
2246         
2247         var cfg= {
2248             tag: 'li',
2249             cls: 'dropdown-menu-item',
2250             cn: [
2251                     {
2252                         tag : 'a',
2253                         href : '#',
2254                         html : 'Link'
2255                     }
2256                 ]
2257         };
2258         if (this.parent().type == 'treeview') {
2259             cfg.cls = 'treeview-menu';
2260         }
2261         
2262         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2263         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2264         return cfg;
2265     },
2266     
2267     initEvents: function() {
2268         
2269         //this.el.select('a').on('click', this.onClick, this);
2270         
2271     },
2272     onClick : function(e)
2273     {
2274         Roo.log('item on click ');
2275         //if(this.preventDefault){
2276         //    e.preventDefault();
2277         //}
2278         //this.parent().hideMenuItems();
2279         
2280         this.fireEvent('click', this, e);
2281     },
2282     getEl : function()
2283     {
2284         return this.el;
2285     }
2286 });
2287
2288  
2289
2290  /*
2291  * - LGPL
2292  *
2293  * menu separator
2294  * 
2295  */
2296
2297
2298 /**
2299  * @class Roo.bootstrap.MenuSeparator
2300  * @extends Roo.bootstrap.Component
2301  * Bootstrap MenuSeparator class
2302  * 
2303  * @constructor
2304  * Create a new MenuItem
2305  * @param {Object} config The config object
2306  */
2307
2308
2309 Roo.bootstrap.MenuSeparator = function(config){
2310     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2311 };
2312
2313 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2314     
2315     getAutoCreate : function(){
2316         var cfg = {
2317             cls: 'divider',
2318             tag : 'li'
2319         };
2320         
2321         return cfg;
2322     }
2323    
2324 });
2325
2326  
2327
2328  
2329 /*
2330 * Licence: LGPL
2331 */
2332
2333 /**
2334  * @class Roo.bootstrap.Modal
2335  * @extends Roo.bootstrap.Component
2336  * Bootstrap Modal class
2337  * @cfg {String} title Title of dialog
2338  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2339  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2340  * @cfg {Boolean} specificTitle default false
2341  * @cfg {Array} buttons Array of buttons or standard button set..
2342  * @cfg {String} buttonPosition (left|right|center) default right
2343  * @cfg {Boolean} animate default true
2344  * @cfg {Boolean} allow_close default true
2345  * 
2346  * @constructor
2347  * Create a new Modal Dialog
2348  * @param {Object} config The config object
2349  */
2350
2351 Roo.bootstrap.Modal = function(config){
2352     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2353     this.addEvents({
2354         // raw events
2355         /**
2356          * @event btnclick
2357          * The raw btnclick event for the button
2358          * @param {Roo.EventObject} e
2359          */
2360         "btnclick" : true
2361     });
2362     this.buttons = this.buttons || [];
2363      
2364     if (this.tmpl) {
2365         this.tmpl = Roo.factory(this.tmpl);
2366     }
2367     
2368 };
2369
2370 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2371     
2372     title : 'test dialog',
2373    
2374     buttons : false,
2375     
2376     // set on load...
2377      
2378     html: false,
2379     
2380     tmp: false,
2381     
2382     specificTitle: false,
2383     
2384     buttonPosition: 'right',
2385     
2386     allow_close : true,
2387     
2388     animate : true,
2389     
2390     
2391      // private
2392     bodyEl:  false,
2393     footerEl:  false,
2394     titleEl:  false,
2395     closeEl:  false,
2396     
2397     
2398     onRender : function(ct, position)
2399     {
2400         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2401      
2402         if(!this.el){
2403             var cfg = Roo.apply({},  this.getAutoCreate());
2404             cfg.id = Roo.id();
2405             //if(!cfg.name){
2406             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2407             //}
2408             //if (!cfg.name.length) {
2409             //    delete cfg.name;
2410            // }
2411             if (this.cls) {
2412                 cfg.cls += ' ' + this.cls;
2413             }
2414             if (this.style) {
2415                 cfg.style = this.style;
2416             }
2417             this.el = Roo.get(document.body).createChild(cfg, position);
2418         }
2419         //var type = this.el.dom.type;
2420         
2421         
2422         
2423         
2424         if(this.tabIndex !== undefined){
2425             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2426         }
2427         
2428         
2429         this.bodyEl = this.el.select('.modal-body',true).first();
2430         this.closeEl = this.el.select('.modal-header .close', true).first();
2431         this.footerEl = this.el.select('.modal-footer',true).first();
2432         this.titleEl = this.el.select('.modal-title',true).first();
2433         
2434         
2435          
2436         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2437         this.maskEl.enableDisplayMode("block");
2438         this.maskEl.hide();
2439         //this.el.addClass("x-dlg-modal");
2440     
2441         if (this.buttons.length) {
2442             Roo.each(this.buttons, function(bb) {
2443                 var b = Roo.apply({}, bb);
2444                 b.xns = b.xns || Roo.bootstrap;
2445                 b.xtype = b.xtype || 'Button';
2446                 if (typeof(b.listeners) == 'undefined') {
2447                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2448                 }
2449                 
2450                 var btn = Roo.factory(b);
2451                 
2452                 btn.onRender(this.el.select('.modal-footer div').first());
2453                 
2454             },this);
2455         }
2456         // render the children.
2457         var nitems = [];
2458         
2459         if(typeof(this.items) != 'undefined'){
2460             var items = this.items;
2461             delete this.items;
2462
2463             for(var i =0;i < items.length;i++) {
2464                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2465             }
2466         }
2467         
2468         this.items = nitems;
2469         
2470         // where are these used - they used to be body/close/footer
2471         
2472        
2473         this.initEvents();
2474         //this.el.addClass([this.fieldClass, this.cls]);
2475         
2476     },
2477     
2478     getAutoCreate : function(){
2479         
2480         
2481         var bdy = {
2482                 cls : 'modal-body',
2483                 html : this.html || ''
2484         };
2485         
2486         var title = {
2487             tag: 'h4',
2488             cls : 'modal-title',
2489             html : this.title
2490         };
2491         
2492         if(this.specificTitle){
2493             title = this.title;
2494             
2495         };
2496         
2497         var header = [];
2498         if (this.allow_close) {
2499             header.push({
2500                 tag: 'button',
2501                 cls : 'close',
2502                 html : '&times'
2503             });
2504         }
2505         header.push(title);
2506         
2507         var modal = {
2508             cls: "modal",
2509             style : 'display: none',
2510             cn : [
2511                 {
2512                     cls: "modal-dialog",
2513                     cn : [
2514                         {
2515                             cls : "modal-content",
2516                             cn : [
2517                                 {
2518                                     cls : 'modal-header',
2519                                     cn : header
2520                                 },
2521                                 bdy,
2522                                 {
2523                                     cls : 'modal-footer',
2524                                     cn : [
2525                                         {
2526                                             tag: 'div',
2527                                             cls: 'btn-' + this.buttonPosition
2528                                         }
2529                                     ]
2530                                     
2531                                 }
2532                                 
2533                                 
2534                             ]
2535                             
2536                         }
2537                     ]
2538                         
2539                 }
2540             ]
2541         };
2542         
2543         if(this.animate){
2544             modal.cls += ' fade';
2545         }
2546         
2547         return modal;
2548           
2549     },
2550     getChildContainer : function() {
2551          
2552          return this.bodyEl;
2553         
2554     },
2555     getButtonContainer : function() {
2556          return this.el.select('.modal-footer div',true).first();
2557         
2558     },
2559     initEvents : function()
2560     {
2561         if (this.allow_close) {
2562             this.closeEl.on('click', this.hide, this);
2563         }
2564         
2565         var _this = this;
2566         
2567         window.addEventListener("resize", function() { _this.resize(); } );
2568
2569     },
2570     
2571     resize : function()
2572     {
2573         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2574     },
2575     
2576     show : function() {
2577         
2578         if (!this.rendered) {
2579             this.render();
2580         }
2581         
2582         this.el.setStyle('display', 'block');
2583         
2584         if(this.animate){
2585             var _this = this;
2586             (function(){ _this.el.addClass('in'); }).defer(50);
2587         }else{
2588             this.el.addClass('in');
2589         }
2590         
2591         // not sure how we can show data in here.. 
2592         //if (this.tmpl) {
2593         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2594         //}
2595         
2596         Roo.get(document.body).addClass("x-body-masked");
2597         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2598         this.maskEl.show();
2599         this.el.setStyle('zIndex', '10001');
2600        
2601         this.fireEvent('show', this);
2602         
2603         
2604     },
2605     hide : function()
2606     {
2607         this.maskEl.hide();
2608         Roo.get(document.body).removeClass("x-body-masked");
2609         this.el.removeClass('in');
2610         
2611         if(this.animate){
2612             var _this = this;
2613             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2614         }else{
2615             this.el.setStyle('display', 'none');
2616         }
2617         
2618         this.fireEvent('hide', this);
2619     },
2620     
2621     addButton : function(str, cb)
2622     {
2623          
2624         
2625         var b = Roo.apply({}, { html : str } );
2626         b.xns = b.xns || Roo.bootstrap;
2627         b.xtype = b.xtype || 'Button';
2628         if (typeof(b.listeners) == 'undefined') {
2629             b.listeners = { click : cb.createDelegate(this)  };
2630         }
2631         
2632         var btn = Roo.factory(b);
2633            
2634         btn.onRender(this.el.select('.modal-footer div').first());
2635         
2636         return btn;   
2637        
2638     },
2639     
2640     setDefaultButton : function(btn)
2641     {
2642         //this.el.select('.modal-footer').()
2643     },
2644     resizeTo: function(w,h)
2645     {
2646         // skip..
2647     },
2648     setContentSize  : function(w, h)
2649     {
2650         
2651     },
2652     onButtonClick: function(btn,e)
2653     {
2654         //Roo.log([a,b,c]);
2655         this.fireEvent('btnclick', btn.name, e);
2656     },
2657      /**
2658      * Set the title of the Dialog
2659      * @param {String} str new Title
2660      */
2661     setTitle: function(str) {
2662         this.titleEl.dom.innerHTML = str;    
2663     },
2664     /**
2665      * Set the body of the Dialog
2666      * @param {String} str new Title
2667      */
2668     setBody: function(str) {
2669         this.bodyEl.dom.innerHTML = str;    
2670     },
2671     /**
2672      * Set the body of the Dialog using the template
2673      * @param {Obj} data - apply this data to the template and replace the body contents.
2674      */
2675     applyBody: function(obj)
2676     {
2677         if (!this.tmpl) {
2678             Roo.log("Error - using apply Body without a template");
2679             //code
2680         }
2681         this.tmpl.overwrite(this.bodyEl, obj);
2682     }
2683     
2684 });
2685
2686
2687 Roo.apply(Roo.bootstrap.Modal,  {
2688     /**
2689          * Button config that displays a single OK button
2690          * @type Object
2691          */
2692         OK :  [{
2693             name : 'ok',
2694             weight : 'primary',
2695             html : 'OK'
2696         }], 
2697         /**
2698          * Button config that displays Yes and No buttons
2699          * @type Object
2700          */
2701         YESNO : [
2702             {
2703                 name  : 'no',
2704                 html : 'No'
2705             },
2706             {
2707                 name  :'yes',
2708                 weight : 'primary',
2709                 html : 'Yes'
2710             }
2711         ],
2712         
2713         /**
2714          * Button config that displays OK and Cancel buttons
2715          * @type Object
2716          */
2717         OKCANCEL : [
2718             {
2719                name : 'cancel',
2720                 html : 'Cancel'
2721             },
2722             {
2723                 name : 'ok',
2724                 weight : 'primary',
2725                 html : 'OK'
2726             }
2727         ],
2728         /**
2729          * Button config that displays Yes, No and Cancel buttons
2730          * @type Object
2731          */
2732         YESNOCANCEL : [
2733             {
2734                 name : 'yes',
2735                 weight : 'primary',
2736                 html : 'Yes'
2737             },
2738             {
2739                 name : 'no',
2740                 html : 'No'
2741             },
2742             {
2743                 name : 'cancel',
2744                 html : 'Cancel'
2745             }
2746         ]
2747 });
2748  
2749  /*
2750  * - LGPL
2751  *
2752  * messagebox - can be used as a replace
2753  * 
2754  */
2755 /**
2756  * @class Roo.MessageBox
2757  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2758  * Example usage:
2759  *<pre><code>
2760 // Basic alert:
2761 Roo.Msg.alert('Status', 'Changes saved successfully.');
2762
2763 // Prompt for user data:
2764 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2765     if (btn == 'ok'){
2766         // process text value...
2767     }
2768 });
2769
2770 // Show a dialog using config options:
2771 Roo.Msg.show({
2772    title:'Save Changes?',
2773    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2774    buttons: Roo.Msg.YESNOCANCEL,
2775    fn: processResult,
2776    animEl: 'elId'
2777 });
2778 </code></pre>
2779  * @singleton
2780  */
2781 Roo.bootstrap.MessageBox = function(){
2782     var dlg, opt, mask, waitTimer;
2783     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2784     var buttons, activeTextEl, bwidth;
2785
2786     
2787     // private
2788     var handleButton = function(button){
2789         dlg.hide();
2790         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2791     };
2792
2793     // private
2794     var handleHide = function(){
2795         if(opt && opt.cls){
2796             dlg.el.removeClass(opt.cls);
2797         }
2798         //if(waitTimer){
2799         //    Roo.TaskMgr.stop(waitTimer);
2800         //    waitTimer = null;
2801         //}
2802     };
2803
2804     // private
2805     var updateButtons = function(b){
2806         var width = 0;
2807         if(!b){
2808             buttons["ok"].hide();
2809             buttons["cancel"].hide();
2810             buttons["yes"].hide();
2811             buttons["no"].hide();
2812             //dlg.footer.dom.style.display = 'none';
2813             return width;
2814         }
2815         dlg.footerEl.dom.style.display = '';
2816         for(var k in buttons){
2817             if(typeof buttons[k] != "function"){
2818                 if(b[k]){
2819                     buttons[k].show();
2820                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2821                     width += buttons[k].el.getWidth()+15;
2822                 }else{
2823                     buttons[k].hide();
2824                 }
2825             }
2826         }
2827         return width;
2828     };
2829
2830     // private
2831     var handleEsc = function(d, k, e){
2832         if(opt && opt.closable !== false){
2833             dlg.hide();
2834         }
2835         if(e){
2836             e.stopEvent();
2837         }
2838     };
2839
2840     return {
2841         /**
2842          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2843          * @return {Roo.BasicDialog} The BasicDialog element
2844          */
2845         getDialog : function(){
2846            if(!dlg){
2847                 dlg = new Roo.bootstrap.Modal( {
2848                     //draggable: true,
2849                     //resizable:false,
2850                     //constraintoviewport:false,
2851                     //fixedcenter:true,
2852                     //collapsible : false,
2853                     //shim:true,
2854                     //modal: true,
2855                   //  width:400,
2856                   //  height:100,
2857                     //buttonAlign:"center",
2858                     closeClick : function(){
2859                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2860                             handleButton("no");
2861                         }else{
2862                             handleButton("cancel");
2863                         }
2864                     }
2865                 });
2866                 dlg.render();
2867                 dlg.on("hide", handleHide);
2868                 mask = dlg.mask;
2869                 //dlg.addKeyListener(27, handleEsc);
2870                 buttons = {};
2871                 this.buttons = buttons;
2872                 var bt = this.buttonText;
2873                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2874                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2875                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2876                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2877                 Roo.log(buttons)
2878                 bodyEl = dlg.bodyEl.createChild({
2879
2880                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2881                         '<textarea class="roo-mb-textarea"></textarea>' +
2882                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2883                 });
2884                 msgEl = bodyEl.dom.firstChild;
2885                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2886                 textboxEl.enableDisplayMode();
2887                 textboxEl.addKeyListener([10,13], function(){
2888                     if(dlg.isVisible() && opt && opt.buttons){
2889                         if(opt.buttons.ok){
2890                             handleButton("ok");
2891                         }else if(opt.buttons.yes){
2892                             handleButton("yes");
2893                         }
2894                     }
2895                 });
2896                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2897                 textareaEl.enableDisplayMode();
2898                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2899                 progressEl.enableDisplayMode();
2900                 var pf = progressEl.dom.firstChild;
2901                 if (pf) {
2902                     pp = Roo.get(pf.firstChild);
2903                     pp.setHeight(pf.offsetHeight);
2904                 }
2905                 
2906             }
2907             return dlg;
2908         },
2909
2910         /**
2911          * Updates the message box body text
2912          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2913          * the XHTML-compliant non-breaking space character '&amp;#160;')
2914          * @return {Roo.MessageBox} This message box
2915          */
2916         updateText : function(text){
2917             if(!dlg.isVisible() && !opt.width){
2918                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2919             }
2920             msgEl.innerHTML = text || '&#160;';
2921       
2922             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2923             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2924             var w = Math.max(
2925                     Math.min(opt.width || cw , this.maxWidth), 
2926                     Math.max(opt.minWidth || this.minWidth, bwidth)
2927             );
2928             if(opt.prompt){
2929                 activeTextEl.setWidth(w);
2930             }
2931             if(dlg.isVisible()){
2932                 dlg.fixedcenter = false;
2933             }
2934             // to big, make it scroll. = But as usual stupid IE does not support
2935             // !important..
2936             
2937             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2938                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2939                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2940             } else {
2941                 bodyEl.dom.style.height = '';
2942                 bodyEl.dom.style.overflowY = '';
2943             }
2944             if (cw > w) {
2945                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2946             } else {
2947                 bodyEl.dom.style.overflowX = '';
2948             }
2949             
2950             dlg.setContentSize(w, bodyEl.getHeight());
2951             if(dlg.isVisible()){
2952                 dlg.fixedcenter = true;
2953             }
2954             return this;
2955         },
2956
2957         /**
2958          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2959          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2960          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2961          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2962          * @return {Roo.MessageBox} This message box
2963          */
2964         updateProgress : function(value, text){
2965             if(text){
2966                 this.updateText(text);
2967             }
2968             if (pp) { // weird bug on my firefox - for some reason this is not defined
2969                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2970             }
2971             return this;
2972         },        
2973
2974         /**
2975          * Returns true if the message box is currently displayed
2976          * @return {Boolean} True if the message box is visible, else false
2977          */
2978         isVisible : function(){
2979             return dlg && dlg.isVisible();  
2980         },
2981
2982         /**
2983          * Hides the message box if it is displayed
2984          */
2985         hide : function(){
2986             if(this.isVisible()){
2987                 dlg.hide();
2988             }  
2989         },
2990
2991         /**
2992          * Displays a new message box, or reinitializes an existing message box, based on the config options
2993          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2994          * The following config object properties are supported:
2995          * <pre>
2996 Property    Type             Description
2997 ----------  ---------------  ------------------------------------------------------------------------------------
2998 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2999                                    closes (defaults to undefined)
3000 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3001                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3002 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3003                                    progress and wait dialogs will ignore this property and always hide the
3004                                    close button as they can only be closed programmatically.
3005 cls               String           A custom CSS class to apply to the message box element
3006 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3007                                    displayed (defaults to 75)
3008 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3009                                    function will be btn (the name of the button that was clicked, if applicable,
3010                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3011                                    Progress and wait dialogs will ignore this option since they do not respond to
3012                                    user actions and can only be closed programmatically, so any required function
3013                                    should be called by the same code after it closes the dialog.
3014 icon              String           A CSS class that provides a background image to be used as an icon for
3015                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3016 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3017 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3018 modal             Boolean          False to allow user interaction with the page while the message box is
3019                                    displayed (defaults to true)
3020 msg               String           A string that will replace the existing message box body text (defaults
3021                                    to the XHTML-compliant non-breaking space character '&#160;')
3022 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3023 progress          Boolean          True to display a progress bar (defaults to false)
3024 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3025 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3026 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3027 title             String           The title text
3028 value             String           The string value to set into the active textbox element if displayed
3029 wait              Boolean          True to display a progress bar (defaults to false)
3030 width             Number           The width of the dialog in pixels
3031 </pre>
3032          *
3033          * Example usage:
3034          * <pre><code>
3035 Roo.Msg.show({
3036    title: 'Address',
3037    msg: 'Please enter your address:',
3038    width: 300,
3039    buttons: Roo.MessageBox.OKCANCEL,
3040    multiline: true,
3041    fn: saveAddress,
3042    animEl: 'addAddressBtn'
3043 });
3044 </code></pre>
3045          * @param {Object} config Configuration options
3046          * @return {Roo.MessageBox} This message box
3047          */
3048         show : function(options)
3049         {
3050             
3051             // this causes nightmares if you show one dialog after another
3052             // especially on callbacks..
3053              
3054             if(this.isVisible()){
3055                 
3056                 this.hide();
3057                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3058                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3059                 Roo.log("New Dialog Message:" +  options.msg )
3060                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3061                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3062                 
3063             }
3064             var d = this.getDialog();
3065             opt = options;
3066             d.setTitle(opt.title || "&#160;");
3067             d.closeEl.setDisplayed(opt.closable !== false);
3068             activeTextEl = textboxEl;
3069             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3070             if(opt.prompt){
3071                 if(opt.multiline){
3072                     textboxEl.hide();
3073                     textareaEl.show();
3074                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3075                         opt.multiline : this.defaultTextHeight);
3076                     activeTextEl = textareaEl;
3077                 }else{
3078                     textboxEl.show();
3079                     textareaEl.hide();
3080                 }
3081             }else{
3082                 textboxEl.hide();
3083                 textareaEl.hide();
3084             }
3085             progressEl.setDisplayed(opt.progress === true);
3086             this.updateProgress(0);
3087             activeTextEl.dom.value = opt.value || "";
3088             if(opt.prompt){
3089                 dlg.setDefaultButton(activeTextEl);
3090             }else{
3091                 var bs = opt.buttons;
3092                 var db = null;
3093                 if(bs && bs.ok){
3094                     db = buttons["ok"];
3095                 }else if(bs && bs.yes){
3096                     db = buttons["yes"];
3097                 }
3098                 dlg.setDefaultButton(db);
3099             }
3100             bwidth = updateButtons(opt.buttons);
3101             this.updateText(opt.msg);
3102             if(opt.cls){
3103                 d.el.addClass(opt.cls);
3104             }
3105             d.proxyDrag = opt.proxyDrag === true;
3106             d.modal = opt.modal !== false;
3107             d.mask = opt.modal !== false ? mask : false;
3108             if(!d.isVisible()){
3109                 // force it to the end of the z-index stack so it gets a cursor in FF
3110                 document.body.appendChild(dlg.el.dom);
3111                 d.animateTarget = null;
3112                 d.show(options.animEl);
3113             }
3114             return this;
3115         },
3116
3117         /**
3118          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3119          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3120          * and closing the message box when the process is complete.
3121          * @param {String} title The title bar text
3122          * @param {String} msg The message box body text
3123          * @return {Roo.MessageBox} This message box
3124          */
3125         progress : function(title, msg){
3126             this.show({
3127                 title : title,
3128                 msg : msg,
3129                 buttons: false,
3130                 progress:true,
3131                 closable:false,
3132                 minWidth: this.minProgressWidth,
3133                 modal : true
3134             });
3135             return this;
3136         },
3137
3138         /**
3139          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3140          * If a callback function is passed it will be called after the user clicks the button, and the
3141          * id of the button that was clicked will be passed as the only parameter to the callback
3142          * (could also be the top-right close button).
3143          * @param {String} title The title bar text
3144          * @param {String} msg The message box body text
3145          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3146          * @param {Object} scope (optional) The scope of the callback function
3147          * @return {Roo.MessageBox} This message box
3148          */
3149         alert : function(title, msg, fn, scope){
3150             this.show({
3151                 title : title,
3152                 msg : msg,
3153                 buttons: this.OK,
3154                 fn: fn,
3155                 scope : scope,
3156                 modal : true
3157             });
3158             return this;
3159         },
3160
3161         /**
3162          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3163          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3164          * You are responsible for closing the message box when the process is complete.
3165          * @param {String} msg The message box body text
3166          * @param {String} title (optional) The title bar text
3167          * @return {Roo.MessageBox} This message box
3168          */
3169         wait : function(msg, title){
3170             this.show({
3171                 title : title,
3172                 msg : msg,
3173                 buttons: false,
3174                 closable:false,
3175                 progress:true,
3176                 modal:true,
3177                 width:300,
3178                 wait:true
3179             });
3180             waitTimer = Roo.TaskMgr.start({
3181                 run: function(i){
3182                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3183                 },
3184                 interval: 1000
3185             });
3186             return this;
3187         },
3188
3189         /**
3190          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3191          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3192          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3193          * @param {String} title The title bar text
3194          * @param {String} msg The message box body text
3195          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3196          * @param {Object} scope (optional) The scope of the callback function
3197          * @return {Roo.MessageBox} This message box
3198          */
3199         confirm : function(title, msg, fn, scope){
3200             this.show({
3201                 title : title,
3202                 msg : msg,
3203                 buttons: this.YESNO,
3204                 fn: fn,
3205                 scope : scope,
3206                 modal : true
3207             });
3208             return this;
3209         },
3210
3211         /**
3212          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3213          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3214          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3215          * (could also be the top-right close button) and the text that was entered will be passed as the two
3216          * parameters to the callback.
3217          * @param {String} title The title bar text
3218          * @param {String} msg The message box body text
3219          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3220          * @param {Object} scope (optional) The scope of the callback function
3221          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3222          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3223          * @return {Roo.MessageBox} This message box
3224          */
3225         prompt : function(title, msg, fn, scope, multiline){
3226             this.show({
3227                 title : title,
3228                 msg : msg,
3229                 buttons: this.OKCANCEL,
3230                 fn: fn,
3231                 minWidth:250,
3232                 scope : scope,
3233                 prompt:true,
3234                 multiline: multiline,
3235                 modal : true
3236             });
3237             return this;
3238         },
3239
3240         /**
3241          * Button config that displays a single OK button
3242          * @type Object
3243          */
3244         OK : {ok:true},
3245         /**
3246          * Button config that displays Yes and No buttons
3247          * @type Object
3248          */
3249         YESNO : {yes:true, no:true},
3250         /**
3251          * Button config that displays OK and Cancel buttons
3252          * @type Object
3253          */
3254         OKCANCEL : {ok:true, cancel:true},
3255         /**
3256          * Button config that displays Yes, No and Cancel buttons
3257          * @type Object
3258          */
3259         YESNOCANCEL : {yes:true, no:true, cancel:true},
3260
3261         /**
3262          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3263          * @type Number
3264          */
3265         defaultTextHeight : 75,
3266         /**
3267          * The maximum width in pixels of the message box (defaults to 600)
3268          * @type Number
3269          */
3270         maxWidth : 600,
3271         /**
3272          * The minimum width in pixels of the message box (defaults to 100)
3273          * @type Number
3274          */
3275         minWidth : 100,
3276         /**
3277          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3278          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3279          * @type Number
3280          */
3281         minProgressWidth : 250,
3282         /**
3283          * An object containing the default button text strings that can be overriden for localized language support.
3284          * Supported properties are: ok, cancel, yes and no.
3285          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3286          * @type Object
3287          */
3288         buttonText : {
3289             ok : "OK",
3290             cancel : "Cancel",
3291             yes : "Yes",
3292             no : "No"
3293         }
3294     };
3295 }();
3296
3297 /**
3298  * Shorthand for {@link Roo.MessageBox}
3299  */
3300 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3301 Roo.Msg = Roo.Msg || Roo.MessageBox;
3302 /*
3303  * - LGPL
3304  *
3305  * navbar
3306  * 
3307  */
3308
3309 /**
3310  * @class Roo.bootstrap.Navbar
3311  * @extends Roo.bootstrap.Component
3312  * Bootstrap Navbar class
3313
3314  * @constructor
3315  * Create a new Navbar
3316  * @param {Object} config The config object
3317  */
3318
3319
3320 Roo.bootstrap.Navbar = function(config){
3321     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3322     
3323 };
3324
3325 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3326     
3327     
3328    
3329     // private
3330     navItems : false,
3331     loadMask : false,
3332     
3333     
3334     getAutoCreate : function(){
3335         
3336         
3337         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3338         
3339     },
3340     
3341     initEvents :function ()
3342     {
3343         //Roo.log(this.el.select('.navbar-toggle',true));
3344         this.el.select('.navbar-toggle',true).on('click', function() {
3345            // Roo.log('click');
3346             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3347         }, this);
3348         
3349         var mark = {
3350             tag: "div",
3351             cls:"x-dlg-mask"
3352         };
3353         
3354         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3355         
3356         var size = this.el.getSize();
3357         this.maskEl.setSize(size.width, size.height);
3358         this.maskEl.enableDisplayMode("block");
3359         this.maskEl.hide();
3360         
3361         if(this.loadMask){
3362             this.maskEl.show();
3363         }
3364     },
3365     
3366     
3367     getChildContainer : function()
3368     {
3369         if (this.el.select('.collapse').getCount()) {
3370             return this.el.select('.collapse',true).first();
3371         }
3372         
3373         return this.el;
3374     },
3375     
3376     mask : function()
3377     {
3378         this.maskEl.show();
3379     },
3380     
3381     unmask : function()
3382     {
3383         this.maskEl.hide();
3384     } 
3385     
3386     
3387     
3388     
3389 });
3390
3391
3392
3393  
3394
3395  /*
3396  * - LGPL
3397  *
3398  * navbar
3399  * 
3400  */
3401
3402 /**
3403  * @class Roo.bootstrap.NavSimplebar
3404  * @extends Roo.bootstrap.Navbar
3405  * Bootstrap Sidebar class
3406  *
3407  * @cfg {Boolean} inverse is inverted color
3408  * 
3409  * @cfg {String} type (nav | pills | tabs)
3410  * @cfg {Boolean} arrangement stacked | justified
3411  * @cfg {String} align (left | right) alignment
3412  * 
3413  * @cfg {Boolean} main (true|false) main nav bar? default false
3414  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3415  * 
3416  * @cfg {String} tag (header|footer|nav|div) default is nav 
3417
3418  * 
3419  * 
3420  * 
3421  * @constructor
3422  * Create a new Sidebar
3423  * @param {Object} config The config object
3424  */
3425
3426
3427 Roo.bootstrap.NavSimplebar = function(config){
3428     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3429 };
3430
3431 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3432     
3433     inverse: false,
3434     
3435     type: false,
3436     arrangement: '',
3437     align : false,
3438     
3439     
3440     
3441     main : false,
3442     
3443     
3444     tag : false,
3445     
3446     
3447     getAutoCreate : function(){
3448         
3449         
3450         var cfg = {
3451             tag : this.tag || 'div',
3452             cls : 'navbar'
3453         };
3454           
3455         
3456         cfg.cn = [
3457             {
3458                 cls: 'nav',
3459                 tag : 'ul'
3460             }
3461         ];
3462         
3463          
3464         this.type = this.type || 'nav';
3465         if (['tabs','pills'].indexOf(this.type)!==-1) {
3466             cfg.cn[0].cls += ' nav-' + this.type
3467         
3468         
3469         } else {
3470             if (this.type!=='nav') {
3471                 Roo.log('nav type must be nav/tabs/pills')
3472             }
3473             cfg.cn[0].cls += ' navbar-nav'
3474         }
3475         
3476         
3477         
3478         
3479         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3480             cfg.cn[0].cls += ' nav-' + this.arrangement;
3481         }
3482         
3483         
3484         if (this.align === 'right') {
3485             cfg.cn[0].cls += ' navbar-right';
3486         }
3487         
3488         if (this.inverse) {
3489             cfg.cls += ' navbar-inverse';
3490             
3491         }
3492         
3493         
3494         return cfg;
3495     
3496         
3497     }
3498     
3499     
3500     
3501 });
3502
3503
3504
3505  
3506
3507  
3508        /*
3509  * - LGPL
3510  *
3511  * navbar
3512  * 
3513  */
3514
3515 /**
3516  * @class Roo.bootstrap.NavHeaderbar
3517  * @extends Roo.bootstrap.NavSimplebar
3518  * Bootstrap Sidebar class
3519  *
3520  * @cfg {String} brand what is brand
3521  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3522  * @cfg {String} brand_href href of the brand
3523  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3524  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3525  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3526  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3527  * 
3528  * @constructor
3529  * Create a new Sidebar
3530  * @param {Object} config The config object
3531  */
3532
3533
3534 Roo.bootstrap.NavHeaderbar = function(config){
3535     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3536       
3537 };
3538
3539 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3540     
3541     position: '',
3542     brand: '',
3543     brand_href: false,
3544     srButton : true,
3545     autohide : false,
3546     desktopCenter : false,
3547    
3548     
3549     getAutoCreate : function(){
3550         
3551         var   cfg = {
3552             tag: this.nav || 'nav',
3553             cls: 'navbar',
3554             role: 'navigation',
3555             cn: []
3556         };
3557         
3558         var cn = cfg.cn;
3559         if (this.desktopCenter) {
3560             cn.push({cls : 'container', cn : []});
3561             cn = cn[0].cn;
3562         }
3563         
3564         if(this.srButton){
3565             cn.push({
3566                 tag: 'div',
3567                 cls: 'navbar-header',
3568                 cn: [
3569                     {
3570                         tag: 'button',
3571                         type: 'button',
3572                         cls: 'navbar-toggle',
3573                         'data-toggle': 'collapse',
3574                         cn: [
3575                             {
3576                                 tag: 'span',
3577                                 cls: 'sr-only',
3578                                 html: 'Toggle navigation'
3579                             },
3580                             {
3581                                 tag: 'span',
3582                                 cls: 'icon-bar'
3583                             },
3584                             {
3585                                 tag: 'span',
3586                                 cls: 'icon-bar'
3587                             },
3588                             {
3589                                 tag: 'span',
3590                                 cls: 'icon-bar'
3591                             }
3592                         ]
3593                     }
3594                 ]
3595             });
3596         }
3597         
3598         cn.push({
3599             tag: 'div',
3600             cls: 'collapse navbar-collapse',
3601             cn : []
3602         });
3603         
3604         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3605         
3606         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3607             cfg.cls += ' navbar-' + this.position;
3608             
3609             // tag can override this..
3610             
3611             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3612         }
3613         
3614         if (this.brand !== '') {
3615             cn[0].cn.push({
3616                 tag: 'a',
3617                 href: this.brand_href ? this.brand_href : '#',
3618                 cls: 'navbar-brand',
3619                 cn: [
3620                 this.brand
3621                 ]
3622             });
3623         }
3624         
3625         if(this.main){
3626             cfg.cls += ' main-nav';
3627         }
3628         
3629         
3630         return cfg;
3631
3632         
3633     },
3634     getHeaderChildContainer : function()
3635     {
3636         if (this.el.select('.navbar-header').getCount()) {
3637             return this.el.select('.navbar-header',true).first();
3638         }
3639         
3640         return this.getChildContainer();
3641     },
3642     
3643     
3644     initEvents : function()
3645     {
3646         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3647         
3648         if (this.autohide) {
3649             
3650             var prevScroll = 0;
3651             var ft = this.el;
3652             
3653             Roo.get(document).on('scroll',function(e) {
3654                 var ns = Roo.get(document).getScroll().top;
3655                 var os = prevScroll;
3656                 prevScroll = ns;
3657                 
3658                 if(ns > os){
3659                     ft.removeClass('slideDown');
3660                     ft.addClass('slideUp');
3661                     return;
3662                 }
3663                 ft.removeClass('slideUp');
3664                 ft.addClass('slideDown');
3665                  
3666               
3667           },this);
3668         }
3669     }    
3670     
3671 });
3672
3673
3674
3675  
3676
3677  /*
3678  * - LGPL
3679  *
3680  * navbar
3681  * 
3682  */
3683
3684 /**
3685  * @class Roo.bootstrap.NavSidebar
3686  * @extends Roo.bootstrap.Navbar
3687  * Bootstrap Sidebar class
3688  * 
3689  * @constructor
3690  * Create a new Sidebar
3691  * @param {Object} config The config object
3692  */
3693
3694
3695 Roo.bootstrap.NavSidebar = function(config){
3696     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3697 };
3698
3699 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3700     
3701     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3702     
3703     getAutoCreate : function(){
3704         
3705         
3706         return  {
3707             tag: 'div',
3708             cls: 'sidebar sidebar-nav'
3709         };
3710     
3711         
3712     }
3713     
3714     
3715     
3716 });
3717
3718
3719
3720  
3721
3722  /*
3723  * - LGPL
3724  *
3725  * nav group
3726  * 
3727  */
3728
3729 /**
3730  * @class Roo.bootstrap.NavGroup
3731  * @extends Roo.bootstrap.Component
3732  * Bootstrap NavGroup class
3733  * @cfg {String} align (left|right)
3734  * @cfg {Boolean} inverse
3735  * @cfg {String} type (nav|pills|tab) default nav
3736  * @cfg {String} navId - reference Id for navbar.
3737
3738  * 
3739  * @constructor
3740  * Create a new nav group
3741  * @param {Object} config The config object
3742  */
3743
3744 Roo.bootstrap.NavGroup = function(config){
3745     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3746     this.navItems = [];
3747    
3748     Roo.bootstrap.NavGroup.register(this);
3749      this.addEvents({
3750         /**
3751              * @event changed
3752              * Fires when the active item changes
3753              * @param {Roo.bootstrap.NavGroup} this
3754              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3755              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3756          */
3757         'changed': true
3758      });
3759     
3760 };
3761
3762 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3763     
3764     align: '',
3765     inverse: false,
3766     form: false,
3767     type: 'nav',
3768     navId : '',
3769     // private
3770     
3771     navItems : false, 
3772     
3773     getAutoCreate : function()
3774     {
3775         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3776         
3777         cfg = {
3778             tag : 'ul',
3779             cls: 'nav' 
3780         };
3781         
3782         if (['tabs','pills'].indexOf(this.type)!==-1) {
3783             cfg.cls += ' nav-' + this.type
3784         } else {
3785             if (this.type!=='nav') {
3786                 Roo.log('nav type must be nav/tabs/pills')
3787             }
3788             cfg.cls += ' navbar-nav'
3789         }
3790         
3791         if (this.parent().sidebar) {
3792             cfg = {
3793                 tag: 'ul',
3794                 cls: 'dashboard-menu sidebar-menu'
3795             };
3796             
3797             return cfg;
3798         }
3799         
3800         if (this.form === true) {
3801             cfg = {
3802                 tag: 'form',
3803                 cls: 'navbar-form'
3804             };
3805             
3806             if (this.align === 'right') {
3807                 cfg.cls += ' navbar-right';
3808             } else {
3809                 cfg.cls += ' navbar-left';
3810             }
3811         }
3812         
3813         if (this.align === 'right') {
3814             cfg.cls += ' navbar-right';
3815         }
3816         
3817         if (this.inverse) {
3818             cfg.cls += ' navbar-inverse';
3819             
3820         }
3821         
3822         
3823         return cfg;
3824     },
3825     /**
3826     * sets the active Navigation item
3827     * @param {Roo.bootstrap.NavItem} the new current navitem
3828     */
3829     setActiveItem : function(item)
3830     {
3831         var prev = false;
3832         Roo.each(this.navItems, function(v){
3833             if (v == item) {
3834                 return ;
3835             }
3836             if (v.isActive()) {
3837                 v.setActive(false, true);
3838                 prev = v;
3839                 
3840             }
3841             
3842         });
3843
3844         item.setActive(true, true);
3845         this.fireEvent('changed', this, item, prev);
3846         
3847         
3848     },
3849     /**
3850     * gets the active Navigation item
3851     * @return {Roo.bootstrap.NavItem} the current navitem
3852     */
3853     getActive : function()
3854     {
3855         
3856         var prev = false;
3857         Roo.each(this.navItems, function(v){
3858             
3859             if (v.isActive()) {
3860                 prev = v;
3861                 
3862             }
3863             
3864         });
3865         return prev;
3866     },
3867     
3868     indexOfNav : function()
3869     {
3870         
3871         var prev = false;
3872         Roo.each(this.navItems, function(v,i){
3873             
3874             if (v.isActive()) {
3875                 prev = i;
3876                 
3877             }
3878             
3879         });
3880         return prev;
3881     },
3882     /**
3883     * adds a Navigation item
3884     * @param {Roo.bootstrap.NavItem} the navitem to add
3885     */
3886     addItem : function(cfg)
3887     {
3888         var cn = new Roo.bootstrap.NavItem(cfg);
3889         this.register(cn);
3890         cn.parentId = this.id;
3891         cn.onRender(this.el, null);
3892         return cn;
3893     },
3894     /**
3895     * register a Navigation item
3896     * @param {Roo.bootstrap.NavItem} the navitem to add
3897     */
3898     register : function(item)
3899     {
3900         this.navItems.push( item);
3901         item.navId = this.navId;
3902     
3903     },
3904     
3905     /**
3906     * clear all the Navigation item
3907     */
3908    
3909     clearAll : function()
3910     {
3911         this.navItems = [];
3912         this.el.dom.innerHTML = '';
3913     },
3914     
3915     getNavItem: function(tabId)
3916     {
3917         var ret = false;
3918         Roo.each(this.navItems, function(e) {
3919             if (e.tabId == tabId) {
3920                ret =  e;
3921                return false;
3922             }
3923             return true;
3924             
3925         });
3926         return ret;
3927     },
3928     
3929     setActiveNext : function()
3930     {
3931         var i = this.indexOfNav(this.getActive());
3932         if (i > this.navItems.length) {
3933             return;
3934         }
3935         this.setActiveItem(this.navItems[i+1]);
3936     },
3937     setActivePrev : function()
3938     {
3939         var i = this.indexOfNav(this.getActive());
3940         if (i  < 1) {
3941             return;
3942         }
3943         this.setActiveItem(this.navItems[i-1]);
3944     },
3945     clearWasActive : function(except) {
3946         Roo.each(this.navItems, function(e) {
3947             if (e.tabId != except.tabId && e.was_active) {
3948                e.was_active = false;
3949                return false;
3950             }
3951             return true;
3952             
3953         });
3954     },
3955     getWasActive : function ()
3956     {
3957         var r = false;
3958         Roo.each(this.navItems, function(e) {
3959             if (e.was_active) {
3960                r = e;
3961                return false;
3962             }
3963             return true;
3964             
3965         });
3966         return r;
3967     }
3968     
3969     
3970 });
3971
3972  
3973 Roo.apply(Roo.bootstrap.NavGroup, {
3974     
3975     groups: {},
3976      /**
3977     * register a Navigation Group
3978     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3979     */
3980     register : function(navgrp)
3981     {
3982         this.groups[navgrp.navId] = navgrp;
3983         
3984     },
3985     /**
3986     * fetch a Navigation Group based on the navigation ID
3987     * @param {string} the navgroup to add
3988     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3989     */
3990     get: function(navId) {
3991         if (typeof(this.groups[navId]) == 'undefined') {
3992             return false;
3993             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3994         }
3995         return this.groups[navId] ;
3996     }
3997     
3998     
3999     
4000 });
4001
4002  /*
4003  * - LGPL
4004  *
4005  * row
4006  * 
4007  */
4008
4009 /**
4010  * @class Roo.bootstrap.NavItem
4011  * @extends Roo.bootstrap.Component
4012  * Bootstrap Navbar.NavItem class
4013  * @cfg {String} href  link to
4014  * @cfg {String} html content of button
4015  * @cfg {String} badge text inside badge
4016  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4017  * @cfg {String} glyphicon name of glyphicon
4018  * @cfg {String} icon name of font awesome icon
4019  * @cfg {Boolean} active Is item active
4020  * @cfg {Boolean} disabled Is item disabled
4021  
4022  * @cfg {Boolean} preventDefault (true | false) default false
4023  * @cfg {String} tabId the tab that this item activates.
4024  * @cfg {String} tagtype (a|span) render as a href or span?
4025  * @cfg {Boolean} animateRef (true|false) link to element default false  
4026   
4027  * @constructor
4028  * Create a new Navbar Item
4029  * @param {Object} config The config object
4030  */
4031 Roo.bootstrap.NavItem = function(config){
4032     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4033     this.addEvents({
4034         // raw events
4035         /**
4036          * @event click
4037          * The raw click event for the entire grid.
4038          * @param {Roo.EventObject} e
4039          */
4040         "click" : true,
4041          /**
4042             * @event changed
4043             * Fires when the active item active state changes
4044             * @param {Roo.bootstrap.NavItem} this
4045             * @param {boolean} state the new state
4046              
4047          */
4048         'changed': true,
4049         /**
4050             * @event scrollto
4051             * Fires when scroll to element
4052             * @param {Roo.bootstrap.NavItem} this
4053             * @param {Object} options
4054             * @param {Roo.EventObject} e
4055              
4056          */
4057         'scrollto': true
4058     });
4059    
4060 };
4061
4062 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4063     
4064     href: false,
4065     html: '',
4066     badge: '',
4067     icon: false,
4068     glyphicon: false,
4069     active: false,
4070     preventDefault : false,
4071     tabId : false,
4072     tagtype : 'a',
4073     disabled : false,
4074     animateRef : false,
4075     was_active : false,
4076     
4077     getAutoCreate : function(){
4078          
4079         var cfg = {
4080             tag: 'li',
4081             cls: 'nav-item'
4082             
4083         };
4084         
4085         if (this.active) {
4086             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4087         }
4088         if (this.disabled) {
4089             cfg.cls += ' disabled';
4090         }
4091         
4092         if (this.href || this.html || this.glyphicon || this.icon) {
4093             cfg.cn = [
4094                 {
4095                     tag: this.tagtype,
4096                     href : this.href || "#",
4097                     html: this.html || ''
4098                 }
4099             ];
4100             
4101             if (this.icon) {
4102                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4103             }
4104
4105             if(this.glyphicon) {
4106                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4107             }
4108             
4109             if (this.menu) {
4110                 
4111                 cfg.cn[0].html += " <span class='caret'></span>";
4112              
4113             }
4114             
4115             if (this.badge !== '') {
4116                  
4117                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4118             }
4119         }
4120         
4121         
4122         
4123         return cfg;
4124     },
4125     initEvents: function() 
4126     {
4127         if (typeof (this.menu) != 'undefined') {
4128             this.menu.parentType = this.xtype;
4129             this.menu.triggerEl = this.el;
4130             this.menu = this.addxtype(Roo.apply({}, this.menu));
4131         }
4132         
4133         this.el.select('a',true).on('click', this.onClick, this);
4134         
4135         if(this.tagtype == 'span'){
4136             this.el.select('span',true).on('click', this.onClick, this);
4137         }
4138        
4139         // at this point parent should be available..
4140         this.parent().register(this);
4141     },
4142     
4143     onClick : function(e)
4144     {
4145         if(
4146                 this.preventDefault || 
4147                 this.href == '#' 
4148         ){
4149             
4150             e.preventDefault();
4151         }
4152         
4153         if (this.disabled) {
4154             return;
4155         }
4156         
4157         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4158         if (tg && tg.transition) {
4159             Roo.log("waiting for the transitionend");
4160             return;
4161         }
4162         
4163         
4164         
4165         //Roo.log("fire event clicked");
4166         if(this.fireEvent('click', this, e) === false){
4167             return;
4168         };
4169         
4170         if(this.tagtype == 'span'){
4171             return;
4172         }
4173         
4174         //Roo.log(this.href);
4175         var ael = this.el.select('a',true).first();
4176         //Roo.log(ael);
4177         
4178         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4179             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4180             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4181                 return; // ignore... - it's a 'hash' to another page.
4182             }
4183             
4184             e.preventDefault();
4185             this.scrollToElement(e);
4186         }
4187         
4188         
4189         var p =  this.parent();
4190    
4191         if (['tabs','pills'].indexOf(p.type)!==-1) {
4192             if (typeof(p.setActiveItem) !== 'undefined') {
4193                 p.setActiveItem(this);
4194             }
4195         }
4196         
4197         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4198         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4199             // remove the collapsed menu expand...
4200             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4201         }
4202     },
4203     
4204     isActive: function () {
4205         return this.active
4206     },
4207     setActive : function(state, fire, is_was_active)
4208     {
4209         if (this.active && !state && this.navId) {
4210             this.was_active = true;
4211             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4212             if (nv) {
4213                 nv.clearWasActive(this);
4214             }
4215             
4216         }
4217         this.active = state;
4218         
4219         if (!state ) {
4220             this.el.removeClass('active');
4221         } else if (!this.el.hasClass('active')) {
4222             this.el.addClass('active');
4223         }
4224         if (fire) {
4225             this.fireEvent('changed', this, state);
4226         }
4227         
4228         // show a panel if it's registered and related..
4229         
4230         if (!this.navId || !this.tabId || !state || is_was_active) {
4231             return;
4232         }
4233         
4234         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4235         if (!tg) {
4236             return;
4237         }
4238         var pan = tg.getPanelByName(this.tabId);
4239         if (!pan) {
4240             return;
4241         }
4242         // if we can not flip to new panel - go back to old nav highlight..
4243         if (false == tg.showPanel(pan)) {
4244             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4245             if (nv) {
4246                 var onav = nv.getWasActive();
4247                 if (onav) {
4248                     onav.setActive(true, false, true);
4249                 }
4250             }
4251             
4252         }
4253         
4254         
4255         
4256     },
4257      // this should not be here...
4258     setDisabled : function(state)
4259     {
4260         this.disabled = state;
4261         if (!state ) {
4262             this.el.removeClass('disabled');
4263         } else if (!this.el.hasClass('disabled')) {
4264             this.el.addClass('disabled');
4265         }
4266         
4267     },
4268     
4269     /**
4270      * Fetch the element to display the tooltip on.
4271      * @return {Roo.Element} defaults to this.el
4272      */
4273     tooltipEl : function()
4274     {
4275         return this.el.select('' + this.tagtype + '', true).first();
4276     },
4277     
4278     scrollToElement : function(e)
4279     {
4280         var c = document.body;
4281         
4282         /*
4283          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4284          */
4285         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4286             c = document.documentElement;
4287         }
4288         
4289         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4290         
4291         if(!target){
4292             return;
4293         }
4294
4295         var o = target.calcOffsetsTo(c);
4296         
4297         var options = {
4298             target : target,
4299             value : o[1]
4300         };
4301         
4302         this.fireEvent('scrollto', this, options, e);
4303         
4304         Roo.get(c).scrollTo('top', options.value, true);
4305         
4306         return;
4307     }
4308 });
4309  
4310
4311  /*
4312  * - LGPL
4313  *
4314  * sidebar item
4315  *
4316  *  li
4317  *    <span> icon </span>
4318  *    <span> text </span>
4319  *    <span>badge </span>
4320  */
4321
4322 /**
4323  * @class Roo.bootstrap.NavSidebarItem
4324  * @extends Roo.bootstrap.NavItem
4325  * Bootstrap Navbar.NavSidebarItem class
4326  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4327  * @constructor
4328  * Create a new Navbar Button
4329  * @param {Object} config The config object
4330  */
4331 Roo.bootstrap.NavSidebarItem = function(config){
4332     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4333     this.addEvents({
4334         // raw events
4335         /**
4336          * @event click
4337          * The raw click event for the entire grid.
4338          * @param {Roo.EventObject} e
4339          */
4340         "click" : true,
4341          /**
4342             * @event changed
4343             * Fires when the active item active state changes
4344             * @param {Roo.bootstrap.NavSidebarItem} this
4345             * @param {boolean} state the new state
4346              
4347          */
4348         'changed': true
4349     });
4350    
4351 };
4352
4353 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4354     
4355     badgeWeight : 'default',
4356     
4357     getAutoCreate : function(){
4358         
4359         
4360         var a = {
4361                 tag: 'a',
4362                 href : this.href || '#',
4363                 cls: '',
4364                 html : '',
4365                 cn : []
4366         };
4367         var cfg = {
4368             tag: 'li',
4369             cls: '',
4370             cn: [ a ]
4371         };
4372         var span = {
4373             tag: 'span',
4374             html : this.html || ''
4375         };
4376         
4377         
4378         if (this.active) {
4379             cfg.cls += ' active';
4380         }
4381         
4382         if (this.disabled) {
4383             cfg.cls += ' disabled';
4384         }
4385         
4386         // left icon..
4387         if (this.glyphicon || this.icon) {
4388             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4389             a.cn.push({ tag : 'i', cls : c }) ;
4390         }
4391         // html..
4392         a.cn.push(span);
4393         // then badge..
4394         if (this.badge !== '') {
4395             
4396             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4397         }
4398         // fi
4399         if (this.menu) {
4400             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4401             a.cls += 'dropdown-toggle treeview' ;
4402             
4403         }
4404         
4405         
4406         
4407         return cfg;
4408          
4409            
4410     },
4411     
4412     initEvents : function()
4413     { 
4414         this.el.on('click', this.onClick, this);
4415        
4416     
4417         if(this.badge !== ''){
4418  
4419             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4420         }
4421         
4422     },
4423     
4424     onClick : function(e)
4425     {
4426         if(this.disabled){
4427             e.preventDefault();
4428             return;
4429         }
4430         
4431         if(this.preventDefault){
4432             e.preventDefault();
4433         }
4434         
4435         this.fireEvent('click', this);
4436     },
4437     
4438     disable : function()
4439     {
4440         this.setDisabled(true);
4441     },
4442     
4443     enable : function()
4444     {
4445         this.setDisabled(false);
4446     },
4447     
4448     setDisabled : function(state)
4449     {
4450         if(this.disabled == state){
4451             return;
4452         }
4453         
4454         this.disabled = state;
4455         
4456         if (state) {
4457             this.el.addClass('disabled');
4458             return;
4459         }
4460         
4461         this.el.removeClass('disabled');
4462         
4463         return;
4464     },
4465     
4466     setActive : function(state)
4467     {
4468         if(this.active == state){
4469             return;
4470         }
4471         
4472         this.active = state;
4473         
4474         if (state) {
4475             this.el.addClass('active');
4476             return;
4477         }
4478         
4479         this.el.removeClass('active');
4480         
4481         return;
4482     },
4483     
4484     isActive: function () 
4485     {
4486         return this.active;
4487     },
4488     
4489     setBadge : function(str)
4490     {
4491         if(!this.badgeEl){
4492             return;
4493         }
4494         
4495         this.badgeEl.dom.innerHTML = str;
4496     }
4497     
4498    
4499      
4500  
4501 });
4502  
4503
4504  /*
4505  * - LGPL
4506  *
4507  * row
4508  * 
4509  */
4510
4511 /**
4512  * @class Roo.bootstrap.Row
4513  * @extends Roo.bootstrap.Component
4514  * Bootstrap Row class (contains columns...)
4515  * 
4516  * @constructor
4517  * Create a new Row
4518  * @param {Object} config The config object
4519  */
4520
4521 Roo.bootstrap.Row = function(config){
4522     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4523 };
4524
4525 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4526     
4527     getAutoCreate : function(){
4528        return {
4529             cls: 'row clearfix'
4530        };
4531     }
4532     
4533     
4534 });
4535
4536  
4537
4538  /*
4539  * - LGPL
4540  *
4541  * element
4542  * 
4543  */
4544
4545 /**
4546  * @class Roo.bootstrap.Element
4547  * @extends Roo.bootstrap.Component
4548  * Bootstrap Element class
4549  * @cfg {String} html contents of the element
4550  * @cfg {String} tag tag of the element
4551  * @cfg {String} cls class of the element
4552  * @cfg {Boolean} preventDefault (true|false) default false
4553  * @cfg {Boolean} clickable (true|false) default false
4554  * 
4555  * @constructor
4556  * Create a new Element
4557  * @param {Object} config The config object
4558  */
4559
4560 Roo.bootstrap.Element = function(config){
4561     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4562     
4563     this.addEvents({
4564         // raw events
4565         /**
4566          * @event click
4567          * When a element is chick
4568          * @param {Roo.bootstrap.Element} this
4569          * @param {Roo.EventObject} e
4570          */
4571         "click" : true
4572     });
4573 };
4574
4575 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4576     
4577     tag: 'div',
4578     cls: '',
4579     html: '',
4580     preventDefault: false, 
4581     clickable: false,
4582     
4583     getAutoCreate : function(){
4584         
4585         var cfg = {
4586             tag: this.tag,
4587             cls: this.cls,
4588             html: this.html
4589         };
4590         
4591         return cfg;
4592     },
4593     
4594     initEvents: function() 
4595     {
4596         Roo.bootstrap.Element.superclass.initEvents.call(this);
4597         
4598         if(this.clickable){
4599             this.el.on('click', this.onClick, this);
4600         }
4601         
4602     },
4603     
4604     onClick : function(e)
4605     {
4606         if(this.preventDefault){
4607             e.preventDefault();
4608         }
4609         
4610         this.fireEvent('click', this, e);
4611     },
4612     
4613     getValue : function()
4614     {
4615         return this.el.dom.innerHTML;
4616     },
4617     
4618     setValue : function(value)
4619     {
4620         this.el.dom.innerHTML = value;
4621     }
4622    
4623 });
4624
4625  
4626
4627  /*
4628  * - LGPL
4629  *
4630  * pagination
4631  * 
4632  */
4633
4634 /**
4635  * @class Roo.bootstrap.Pagination
4636  * @extends Roo.bootstrap.Component
4637  * Bootstrap Pagination class
4638  * @cfg {String} size xs | sm | md | lg
4639  * @cfg {Boolean} inverse false | true
4640  * 
4641  * @constructor
4642  * Create a new Pagination
4643  * @param {Object} config The config object
4644  */
4645
4646 Roo.bootstrap.Pagination = function(config){
4647     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4648 };
4649
4650 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4651     
4652     cls: false,
4653     size: false,
4654     inverse: false,
4655     
4656     getAutoCreate : function(){
4657         var cfg = {
4658             tag: 'ul',
4659                 cls: 'pagination'
4660         };
4661         if (this.inverse) {
4662             cfg.cls += ' inverse';
4663         }
4664         if (this.html) {
4665             cfg.html=this.html;
4666         }
4667         if (this.cls) {
4668             cfg.cls += " " + this.cls;
4669         }
4670         return cfg;
4671     }
4672    
4673 });
4674
4675  
4676
4677  /*
4678  * - LGPL
4679  *
4680  * Pagination item
4681  * 
4682  */
4683
4684
4685 /**
4686  * @class Roo.bootstrap.PaginationItem
4687  * @extends Roo.bootstrap.Component
4688  * Bootstrap PaginationItem class
4689  * @cfg {String} html text
4690  * @cfg {String} href the link
4691  * @cfg {Boolean} preventDefault (true | false) default true
4692  * @cfg {Boolean} active (true | false) default false
4693  * @cfg {Boolean} disabled default false
4694  * 
4695  * 
4696  * @constructor
4697  * Create a new PaginationItem
4698  * @param {Object} config The config object
4699  */
4700
4701
4702 Roo.bootstrap.PaginationItem = function(config){
4703     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4704     this.addEvents({
4705         // raw events
4706         /**
4707          * @event click
4708          * The raw click event for the entire grid.
4709          * @param {Roo.EventObject} e
4710          */
4711         "click" : true
4712     });
4713 };
4714
4715 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4716     
4717     href : false,
4718     html : false,
4719     preventDefault: true,
4720     active : false,
4721     cls : false,
4722     disabled: false,
4723     
4724     getAutoCreate : function(){
4725         var cfg= {
4726             tag: 'li',
4727             cn: [
4728                 {
4729                     tag : 'a',
4730                     href : this.href ? this.href : '#',
4731                     html : this.html ? this.html : ''
4732                 }
4733             ]
4734         };
4735         
4736         if(this.cls){
4737             cfg.cls = this.cls;
4738         }
4739         
4740         if(this.disabled){
4741             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4742         }
4743         
4744         if(this.active){
4745             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4746         }
4747         
4748         return cfg;
4749     },
4750     
4751     initEvents: function() {
4752         
4753         this.el.on('click', this.onClick, this);
4754         
4755     },
4756     onClick : function(e)
4757     {
4758         Roo.log('PaginationItem on click ');
4759         if(this.preventDefault){
4760             e.preventDefault();
4761         }
4762         
4763         if(this.disabled){
4764             return;
4765         }
4766         
4767         this.fireEvent('click', this, e);
4768     }
4769    
4770 });
4771
4772  
4773
4774  /*
4775  * - LGPL
4776  *
4777  * slider
4778  * 
4779  */
4780
4781
4782 /**
4783  * @class Roo.bootstrap.Slider
4784  * @extends Roo.bootstrap.Component
4785  * Bootstrap Slider class
4786  *    
4787  * @constructor
4788  * Create a new Slider
4789  * @param {Object} config The config object
4790  */
4791
4792 Roo.bootstrap.Slider = function(config){
4793     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4794 };
4795
4796 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4797     
4798     getAutoCreate : function(){
4799         
4800         var cfg = {
4801             tag: 'div',
4802             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4803             cn: [
4804                 {
4805                     tag: 'a',
4806                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4807                 }
4808             ]
4809         };
4810         
4811         return cfg;
4812     }
4813    
4814 });
4815
4816  /*
4817  * Based on:
4818  * Ext JS Library 1.1.1
4819  * Copyright(c) 2006-2007, Ext JS, LLC.
4820  *
4821  * Originally Released Under LGPL - original licence link has changed is not relivant.
4822  *
4823  * Fork - LGPL
4824  * <script type="text/javascript">
4825  */
4826  
4827
4828 /**
4829  * @class Roo.grid.ColumnModel
4830  * @extends Roo.util.Observable
4831  * This is the default implementation of a ColumnModel used by the Grid. It defines
4832  * the columns in the grid.
4833  * <br>Usage:<br>
4834  <pre><code>
4835  var colModel = new Roo.grid.ColumnModel([
4836         {header: "Ticker", width: 60, sortable: true, locked: true},
4837         {header: "Company Name", width: 150, sortable: true},
4838         {header: "Market Cap.", width: 100, sortable: true},
4839         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4840         {header: "Employees", width: 100, sortable: true, resizable: false}
4841  ]);
4842  </code></pre>
4843  * <p>
4844  
4845  * The config options listed for this class are options which may appear in each
4846  * individual column definition.
4847  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4848  * @constructor
4849  * @param {Object} config An Array of column config objects. See this class's
4850  * config objects for details.
4851 */
4852 Roo.grid.ColumnModel = function(config){
4853         /**
4854      * The config passed into the constructor
4855      */
4856     this.config = config;
4857     this.lookup = {};
4858
4859     // if no id, create one
4860     // if the column does not have a dataIndex mapping,
4861     // map it to the order it is in the config
4862     for(var i = 0, len = config.length; i < len; i++){
4863         var c = config[i];
4864         if(typeof c.dataIndex == "undefined"){
4865             c.dataIndex = i;
4866         }
4867         if(typeof c.renderer == "string"){
4868             c.renderer = Roo.util.Format[c.renderer];
4869         }
4870         if(typeof c.id == "undefined"){
4871             c.id = Roo.id();
4872         }
4873         if(c.editor && c.editor.xtype){
4874             c.editor  = Roo.factory(c.editor, Roo.grid);
4875         }
4876         if(c.editor && c.editor.isFormField){
4877             c.editor = new Roo.grid.GridEditor(c.editor);
4878         }
4879         this.lookup[c.id] = c;
4880     }
4881
4882     /**
4883      * The width of columns which have no width specified (defaults to 100)
4884      * @type Number
4885      */
4886     this.defaultWidth = 100;
4887
4888     /**
4889      * Default sortable of columns which have no sortable specified (defaults to false)
4890      * @type Boolean
4891      */
4892     this.defaultSortable = false;
4893
4894     this.addEvents({
4895         /**
4896              * @event widthchange
4897              * Fires when the width of a column changes.
4898              * @param {ColumnModel} this
4899              * @param {Number} columnIndex The column index
4900              * @param {Number} newWidth The new width
4901              */
4902             "widthchange": true,
4903         /**
4904              * @event headerchange
4905              * Fires when the text of a header changes.
4906              * @param {ColumnModel} this
4907              * @param {Number} columnIndex The column index
4908              * @param {Number} newText The new header text
4909              */
4910             "headerchange": true,
4911         /**
4912              * @event hiddenchange
4913              * Fires when a column is hidden or "unhidden".
4914              * @param {ColumnModel} this
4915              * @param {Number} columnIndex The column index
4916              * @param {Boolean} hidden true if hidden, false otherwise
4917              */
4918             "hiddenchange": true,
4919             /**
4920          * @event columnmoved
4921          * Fires when a column is moved.
4922          * @param {ColumnModel} this
4923          * @param {Number} oldIndex
4924          * @param {Number} newIndex
4925          */
4926         "columnmoved" : true,
4927         /**
4928          * @event columlockchange
4929          * Fires when a column's locked state is changed
4930          * @param {ColumnModel} this
4931          * @param {Number} colIndex
4932          * @param {Boolean} locked true if locked
4933          */
4934         "columnlockchange" : true
4935     });
4936     Roo.grid.ColumnModel.superclass.constructor.call(this);
4937 };
4938 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4939     /**
4940      * @cfg {String} header The header text to display in the Grid view.
4941      */
4942     /**
4943      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4944      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4945      * specified, the column's index is used as an index into the Record's data Array.
4946      */
4947     /**
4948      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4949      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4950      */
4951     /**
4952      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4953      * Defaults to the value of the {@link #defaultSortable} property.
4954      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4955      */
4956     /**
4957      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4958      */
4959     /**
4960      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4961      */
4962     /**
4963      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4964      */
4965     /**
4966      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4967      */
4968     /**
4969      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4970      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4971      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4972      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4973      */
4974        /**
4975      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4976      */
4977     /**
4978      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4979      */
4980     /**
4981      * @cfg {String} cursor (Optional)
4982      */
4983     /**
4984      * @cfg {String} tooltip (Optional)
4985      */
4986     /**
4987      * @cfg {Number} xs (Optional)
4988      */
4989     /**
4990      * @cfg {Number} sm (Optional)
4991      */
4992     /**
4993      * @cfg {Number} md (Optional)
4994      */
4995     /**
4996      * @cfg {Number} lg (Optional)
4997      */
4998     /**
4999      * Returns the id of the column at the specified index.
5000      * @param {Number} index The column index
5001      * @return {String} the id
5002      */
5003     getColumnId : function(index){
5004         return this.config[index].id;
5005     },
5006
5007     /**
5008      * Returns the column for a specified id.
5009      * @param {String} id The column id
5010      * @return {Object} the column
5011      */
5012     getColumnById : function(id){
5013         return this.lookup[id];
5014     },
5015
5016     
5017     /**
5018      * Returns the column for a specified dataIndex.
5019      * @param {String} dataIndex The column dataIndex
5020      * @return {Object|Boolean} the column or false if not found
5021      */
5022     getColumnByDataIndex: function(dataIndex){
5023         var index = this.findColumnIndex(dataIndex);
5024         return index > -1 ? this.config[index] : false;
5025     },
5026     
5027     /**
5028      * Returns the index for a specified column id.
5029      * @param {String} id The column id
5030      * @return {Number} the index, or -1 if not found
5031      */
5032     getIndexById : function(id){
5033         for(var i = 0, len = this.config.length; i < len; i++){
5034             if(this.config[i].id == id){
5035                 return i;
5036             }
5037         }
5038         return -1;
5039     },
5040     
5041     /**
5042      * Returns the index for a specified column dataIndex.
5043      * @param {String} dataIndex The column dataIndex
5044      * @return {Number} the index, or -1 if not found
5045      */
5046     
5047     findColumnIndex : function(dataIndex){
5048         for(var i = 0, len = this.config.length; i < len; i++){
5049             if(this.config[i].dataIndex == dataIndex){
5050                 return i;
5051             }
5052         }
5053         return -1;
5054     },
5055     
5056     
5057     moveColumn : function(oldIndex, newIndex){
5058         var c = this.config[oldIndex];
5059         this.config.splice(oldIndex, 1);
5060         this.config.splice(newIndex, 0, c);
5061         this.dataMap = null;
5062         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5063     },
5064
5065     isLocked : function(colIndex){
5066         return this.config[colIndex].locked === true;
5067     },
5068
5069     setLocked : function(colIndex, value, suppressEvent){
5070         if(this.isLocked(colIndex) == value){
5071             return;
5072         }
5073         this.config[colIndex].locked = value;
5074         if(!suppressEvent){
5075             this.fireEvent("columnlockchange", this, colIndex, value);
5076         }
5077     },
5078
5079     getTotalLockedWidth : function(){
5080         var totalWidth = 0;
5081         for(var i = 0; i < this.config.length; i++){
5082             if(this.isLocked(i) && !this.isHidden(i)){
5083                 this.totalWidth += this.getColumnWidth(i);
5084             }
5085         }
5086         return totalWidth;
5087     },
5088
5089     getLockedCount : function(){
5090         for(var i = 0, len = this.config.length; i < len; i++){
5091             if(!this.isLocked(i)){
5092                 return i;
5093             }
5094         }
5095     },
5096
5097     /**
5098      * Returns the number of columns.
5099      * @return {Number}
5100      */
5101     getColumnCount : function(visibleOnly){
5102         if(visibleOnly === true){
5103             var c = 0;
5104             for(var i = 0, len = this.config.length; i < len; i++){
5105                 if(!this.isHidden(i)){
5106                     c++;
5107                 }
5108             }
5109             return c;
5110         }
5111         return this.config.length;
5112     },
5113
5114     /**
5115      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5116      * @param {Function} fn
5117      * @param {Object} scope (optional)
5118      * @return {Array} result
5119      */
5120     getColumnsBy : function(fn, scope){
5121         var r = [];
5122         for(var i = 0, len = this.config.length; i < len; i++){
5123             var c = this.config[i];
5124             if(fn.call(scope||this, c, i) === true){
5125                 r[r.length] = c;
5126             }
5127         }
5128         return r;
5129     },
5130
5131     /**
5132      * Returns true if the specified column is sortable.
5133      * @param {Number} col The column index
5134      * @return {Boolean}
5135      */
5136     isSortable : function(col){
5137         if(typeof this.config[col].sortable == "undefined"){
5138             return this.defaultSortable;
5139         }
5140         return this.config[col].sortable;
5141     },
5142
5143     /**
5144      * Returns the rendering (formatting) function defined for the column.
5145      * @param {Number} col The column index.
5146      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5147      */
5148     getRenderer : function(col){
5149         if(!this.config[col].renderer){
5150             return Roo.grid.ColumnModel.defaultRenderer;
5151         }
5152         return this.config[col].renderer;
5153     },
5154
5155     /**
5156      * Sets the rendering (formatting) function for a column.
5157      * @param {Number} col The column index
5158      * @param {Function} fn The function to use to process the cell's raw data
5159      * to return HTML markup for the grid view. The render function is called with
5160      * the following parameters:<ul>
5161      * <li>Data value.</li>
5162      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5163      * <li>css A CSS style string to apply to the table cell.</li>
5164      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5165      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5166      * <li>Row index</li>
5167      * <li>Column index</li>
5168      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5169      */
5170     setRenderer : function(col, fn){
5171         this.config[col].renderer = fn;
5172     },
5173
5174     /**
5175      * Returns the width for the specified column.
5176      * @param {Number} col The column index
5177      * @return {Number}
5178      */
5179     getColumnWidth : function(col){
5180         return this.config[col].width * 1 || this.defaultWidth;
5181     },
5182
5183     /**
5184      * Sets the width for a column.
5185      * @param {Number} col The column index
5186      * @param {Number} width The new width
5187      */
5188     setColumnWidth : function(col, width, suppressEvent){
5189         this.config[col].width = width;
5190         this.totalWidth = null;
5191         if(!suppressEvent){
5192              this.fireEvent("widthchange", this, col, width);
5193         }
5194     },
5195
5196     /**
5197      * Returns the total width of all columns.
5198      * @param {Boolean} includeHidden True to include hidden column widths
5199      * @return {Number}
5200      */
5201     getTotalWidth : function(includeHidden){
5202         if(!this.totalWidth){
5203             this.totalWidth = 0;
5204             for(var i = 0, len = this.config.length; i < len; i++){
5205                 if(includeHidden || !this.isHidden(i)){
5206                     this.totalWidth += this.getColumnWidth(i);
5207                 }
5208             }
5209         }
5210         return this.totalWidth;
5211     },
5212
5213     /**
5214      * Returns the header for the specified column.
5215      * @param {Number} col The column index
5216      * @return {String}
5217      */
5218     getColumnHeader : function(col){
5219         return this.config[col].header;
5220     },
5221
5222     /**
5223      * Sets the header for a column.
5224      * @param {Number} col The column index
5225      * @param {String} header The new header
5226      */
5227     setColumnHeader : function(col, header){
5228         this.config[col].header = header;
5229         this.fireEvent("headerchange", this, col, header);
5230     },
5231
5232     /**
5233      * Returns the tooltip for the specified column.
5234      * @param {Number} col The column index
5235      * @return {String}
5236      */
5237     getColumnTooltip : function(col){
5238             return this.config[col].tooltip;
5239     },
5240     /**
5241      * Sets the tooltip for a column.
5242      * @param {Number} col The column index
5243      * @param {String} tooltip The new tooltip
5244      */
5245     setColumnTooltip : function(col, tooltip){
5246             this.config[col].tooltip = tooltip;
5247     },
5248
5249     /**
5250      * Returns the dataIndex for the specified column.
5251      * @param {Number} col The column index
5252      * @return {Number}
5253      */
5254     getDataIndex : function(col){
5255         return this.config[col].dataIndex;
5256     },
5257
5258     /**
5259      * Sets the dataIndex for a column.
5260      * @param {Number} col The column index
5261      * @param {Number} dataIndex The new dataIndex
5262      */
5263     setDataIndex : function(col, dataIndex){
5264         this.config[col].dataIndex = dataIndex;
5265     },
5266
5267     
5268     
5269     /**
5270      * Returns true if the cell is editable.
5271      * @param {Number} colIndex The column index
5272      * @param {Number} rowIndex The row index
5273      * @return {Boolean}
5274      */
5275     isCellEditable : function(colIndex, rowIndex){
5276         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5277     },
5278
5279     /**
5280      * Returns the editor defined for the cell/column.
5281      * return false or null to disable editing.
5282      * @param {Number} colIndex The column index
5283      * @param {Number} rowIndex The row index
5284      * @return {Object}
5285      */
5286     getCellEditor : function(colIndex, rowIndex){
5287         return this.config[colIndex].editor;
5288     },
5289
5290     /**
5291      * Sets if a column is editable.
5292      * @param {Number} col The column index
5293      * @param {Boolean} editable True if the column is editable
5294      */
5295     setEditable : function(col, editable){
5296         this.config[col].editable = editable;
5297     },
5298
5299
5300     /**
5301      * Returns true if the column is hidden.
5302      * @param {Number} colIndex The column index
5303      * @return {Boolean}
5304      */
5305     isHidden : function(colIndex){
5306         return this.config[colIndex].hidden;
5307     },
5308
5309
5310     /**
5311      * Returns true if the column width cannot be changed
5312      */
5313     isFixed : function(colIndex){
5314         return this.config[colIndex].fixed;
5315     },
5316
5317     /**
5318      * Returns true if the column can be resized
5319      * @return {Boolean}
5320      */
5321     isResizable : function(colIndex){
5322         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5323     },
5324     /**
5325      * Sets if a column is hidden.
5326      * @param {Number} colIndex The column index
5327      * @param {Boolean} hidden True if the column is hidden
5328      */
5329     setHidden : function(colIndex, hidden){
5330         this.config[colIndex].hidden = hidden;
5331         this.totalWidth = null;
5332         this.fireEvent("hiddenchange", this, colIndex, hidden);
5333     },
5334
5335     /**
5336      * Sets the editor for a column.
5337      * @param {Number} col The column index
5338      * @param {Object} editor The editor object
5339      */
5340     setEditor : function(col, editor){
5341         this.config[col].editor = editor;
5342     }
5343 });
5344
5345 Roo.grid.ColumnModel.defaultRenderer = function(value){
5346         if(typeof value == "string" && value.length < 1){
5347             return "&#160;";
5348         }
5349         return value;
5350 };
5351
5352 // Alias for backwards compatibility
5353 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5354 /*
5355  * Based on:
5356  * Ext JS Library 1.1.1
5357  * Copyright(c) 2006-2007, Ext JS, LLC.
5358  *
5359  * Originally Released Under LGPL - original licence link has changed is not relivant.
5360  *
5361  * Fork - LGPL
5362  * <script type="text/javascript">
5363  */
5364  
5365 /**
5366  * @class Roo.LoadMask
5367  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5368  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5369  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5370  * element's UpdateManager load indicator and will be destroyed after the initial load.
5371  * @constructor
5372  * Create a new LoadMask
5373  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5374  * @param {Object} config The config object
5375  */
5376 Roo.LoadMask = function(el, config){
5377     this.el = Roo.get(el);
5378     Roo.apply(this, config);
5379     if(this.store){
5380         this.store.on('beforeload', this.onBeforeLoad, this);
5381         this.store.on('load', this.onLoad, this);
5382         this.store.on('loadexception', this.onLoadException, this);
5383         this.removeMask = false;
5384     }else{
5385         var um = this.el.getUpdateManager();
5386         um.showLoadIndicator = false; // disable the default indicator
5387         um.on('beforeupdate', this.onBeforeLoad, this);
5388         um.on('update', this.onLoad, this);
5389         um.on('failure', this.onLoad, this);
5390         this.removeMask = true;
5391     }
5392 };
5393
5394 Roo.LoadMask.prototype = {
5395     /**
5396      * @cfg {Boolean} removeMask
5397      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5398      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5399      */
5400     /**
5401      * @cfg {String} msg
5402      * The text to display in a centered loading message box (defaults to 'Loading...')
5403      */
5404     msg : 'Loading...',
5405     /**
5406      * @cfg {String} msgCls
5407      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5408      */
5409     msgCls : 'x-mask-loading',
5410
5411     /**
5412      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5413      * @type Boolean
5414      */
5415     disabled: false,
5416
5417     /**
5418      * Disables the mask to prevent it from being displayed
5419      */
5420     disable : function(){
5421        this.disabled = true;
5422     },
5423
5424     /**
5425      * Enables the mask so that it can be displayed
5426      */
5427     enable : function(){
5428         this.disabled = false;
5429     },
5430     
5431     onLoadException : function()
5432     {
5433         Roo.log(arguments);
5434         
5435         if (typeof(arguments[3]) != 'undefined') {
5436             Roo.MessageBox.alert("Error loading",arguments[3]);
5437         } 
5438         /*
5439         try {
5440             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5441                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5442             }   
5443         } catch(e) {
5444             
5445         }
5446         */
5447     
5448         
5449         
5450         this.el.unmask(this.removeMask);
5451     },
5452     // private
5453     onLoad : function()
5454     {
5455         this.el.unmask(this.removeMask);
5456     },
5457
5458     // private
5459     onBeforeLoad : function(){
5460         if(!this.disabled){
5461             this.el.mask(this.msg, this.msgCls);
5462         }
5463     },
5464
5465     // private
5466     destroy : function(){
5467         if(this.store){
5468             this.store.un('beforeload', this.onBeforeLoad, this);
5469             this.store.un('load', this.onLoad, this);
5470             this.store.un('loadexception', this.onLoadException, this);
5471         }else{
5472             var um = this.el.getUpdateManager();
5473             um.un('beforeupdate', this.onBeforeLoad, this);
5474             um.un('update', this.onLoad, this);
5475             um.un('failure', this.onLoad, this);
5476         }
5477     }
5478 };/*
5479  * - LGPL
5480  *
5481  * table
5482  * 
5483  */
5484
5485 /**
5486  * @class Roo.bootstrap.Table
5487  * @extends Roo.bootstrap.Component
5488  * Bootstrap Table class
5489  * @cfg {String} cls table class
5490  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5491  * @cfg {String} bgcolor Specifies the background color for a table
5492  * @cfg {Number} border Specifies whether the table cells should have borders or not
5493  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5494  * @cfg {Number} cellspacing Specifies the space between cells
5495  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5496  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5497  * @cfg {String} sortable Specifies that the table should be sortable
5498  * @cfg {String} summary Specifies a summary of the content of a table
5499  * @cfg {Number} width Specifies the width of a table
5500  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5501  * 
5502  * @cfg {boolean} striped Should the rows be alternative striped
5503  * @cfg {boolean} bordered Add borders to the table
5504  * @cfg {boolean} hover Add hover highlighting
5505  * @cfg {boolean} condensed Format condensed
5506  * @cfg {boolean} responsive Format condensed
5507  * @cfg {Boolean} loadMask (true|false) default false
5508  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5509  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5510  * @cfg {Boolean} rowSelection (true|false) default false
5511  * @cfg {Boolean} cellSelection (true|false) default false
5512  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5513  
5514  * 
5515  * @constructor
5516  * Create a new Table
5517  * @param {Object} config The config object
5518  */
5519
5520 Roo.bootstrap.Table = function(config){
5521     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5522     
5523     // BC...
5524     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5525     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5526     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5527     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5528     
5529     
5530     if (this.sm) {
5531         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5532         this.sm = this.selModel;
5533         this.sm.xmodule = this.xmodule || false;
5534     }
5535     if (this.cm && typeof(this.cm.config) == 'undefined') {
5536         this.colModel = new Roo.grid.ColumnModel(this.cm);
5537         this.cm = this.colModel;
5538         this.cm.xmodule = this.xmodule || false;
5539     }
5540     if (this.store) {
5541         this.store= Roo.factory(this.store, Roo.data);
5542         this.ds = this.store;
5543         this.ds.xmodule = this.xmodule || false;
5544          
5545     }
5546     if (this.footer && this.store) {
5547         this.footer.dataSource = this.ds;
5548         this.footer = Roo.factory(this.footer);
5549     }
5550     
5551     /** @private */
5552     this.addEvents({
5553         /**
5554          * @event cellclick
5555          * Fires when a cell is clicked
5556          * @param {Roo.bootstrap.Table} this
5557          * @param {Roo.Element} el
5558          * @param {Number} rowIndex
5559          * @param {Number} columnIndex
5560          * @param {Roo.EventObject} e
5561          */
5562         "cellclick" : true,
5563         /**
5564          * @event celldblclick
5565          * Fires when a cell is double clicked
5566          * @param {Roo.bootstrap.Table} this
5567          * @param {Roo.Element} el
5568          * @param {Number} rowIndex
5569          * @param {Number} columnIndex
5570          * @param {Roo.EventObject} e
5571          */
5572         "celldblclick" : true,
5573         /**
5574          * @event rowclick
5575          * Fires when a row is clicked
5576          * @param {Roo.bootstrap.Table} this
5577          * @param {Roo.Element} el
5578          * @param {Number} rowIndex
5579          * @param {Roo.EventObject} e
5580          */
5581         "rowclick" : true,
5582         /**
5583          * @event rowdblclick
5584          * Fires when a row is double clicked
5585          * @param {Roo.bootstrap.Table} this
5586          * @param {Roo.Element} el
5587          * @param {Number} rowIndex
5588          * @param {Roo.EventObject} e
5589          */
5590         "rowdblclick" : true,
5591         /**
5592          * @event mouseover
5593          * Fires when a mouseover occur
5594          * @param {Roo.bootstrap.Table} this
5595          * @param {Roo.Element} el
5596          * @param {Number} rowIndex
5597          * @param {Number} columnIndex
5598          * @param {Roo.EventObject} e
5599          */
5600         "mouseover" : true,
5601         /**
5602          * @event mouseout
5603          * Fires when a mouseout occur
5604          * @param {Roo.bootstrap.Table} this
5605          * @param {Roo.Element} el
5606          * @param {Number} rowIndex
5607          * @param {Number} columnIndex
5608          * @param {Roo.EventObject} e
5609          */
5610         "mouseout" : true,
5611         /**
5612          * @event rowclass
5613          * Fires when a row is rendered, so you can change add a style to it.
5614          * @param {Roo.bootstrap.Table} this
5615          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5616          */
5617         'rowclass' : true,
5618           /**
5619          * @event rowsrendered
5620          * Fires when all the  rows have been rendered
5621          * @param {Roo.bootstrap.Table} this
5622          */
5623         'rowsrendered' : true
5624         
5625     });
5626 };
5627
5628 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5629     
5630     cls: false,
5631     align: false,
5632     bgcolor: false,
5633     border: false,
5634     cellpadding: false,
5635     cellspacing: false,
5636     frame: false,
5637     rules: false,
5638     sortable: false,
5639     summary: false,
5640     width: false,
5641     striped : false,
5642     bordered: false,
5643     hover:  false,
5644     condensed : false,
5645     responsive : false,
5646     sm : false,
5647     cm : false,
5648     store : false,
5649     loadMask : false,
5650     footerShow : true,
5651     headerShow : true,
5652   
5653     rowSelection : false,
5654     cellSelection : false,
5655     layout : false,
5656     
5657     // Roo.Element - the tbody
5658     mainBody: false, 
5659     
5660     getAutoCreate : function(){
5661         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5662         
5663         cfg = {
5664             tag: 'table',
5665             cls : 'table',
5666             cn : []
5667         };
5668             
5669         if (this.striped) {
5670             cfg.cls += ' table-striped';
5671         }
5672         
5673         if (this.hover) {
5674             cfg.cls += ' table-hover';
5675         }
5676         if (this.bordered) {
5677             cfg.cls += ' table-bordered';
5678         }
5679         if (this.condensed) {
5680             cfg.cls += ' table-condensed';
5681         }
5682         if (this.responsive) {
5683             cfg.cls += ' table-responsive';
5684         }
5685         
5686         if (this.cls) {
5687             cfg.cls+=  ' ' +this.cls;
5688         }
5689         
5690         // this lot should be simplifed...
5691         
5692         if (this.align) {
5693             cfg.align=this.align;
5694         }
5695         if (this.bgcolor) {
5696             cfg.bgcolor=this.bgcolor;
5697         }
5698         if (this.border) {
5699             cfg.border=this.border;
5700         }
5701         if (this.cellpadding) {
5702             cfg.cellpadding=this.cellpadding;
5703         }
5704         if (this.cellspacing) {
5705             cfg.cellspacing=this.cellspacing;
5706         }
5707         if (this.frame) {
5708             cfg.frame=this.frame;
5709         }
5710         if (this.rules) {
5711             cfg.rules=this.rules;
5712         }
5713         if (this.sortable) {
5714             cfg.sortable=this.sortable;
5715         }
5716         if (this.summary) {
5717             cfg.summary=this.summary;
5718         }
5719         if (this.width) {
5720             cfg.width=this.width;
5721         }
5722         if (this.layout) {
5723             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5724         }
5725         
5726         if(this.store || this.cm){
5727             if(this.headerShow){
5728                 cfg.cn.push(this.renderHeader());
5729             }
5730             
5731             cfg.cn.push(this.renderBody());
5732             
5733             if(this.footerShow){
5734                 cfg.cn.push(this.renderFooter());
5735             }
5736             
5737             cfg.cls+=  ' TableGrid';
5738         }
5739         
5740         return { cn : [ cfg ] };
5741     },
5742     
5743     initEvents : function()
5744     {   
5745         if(!this.store || !this.cm){
5746             return;
5747         }
5748         
5749         //Roo.log('initEvents with ds!!!!');
5750         
5751         this.mainBody = this.el.select('tbody', true).first();
5752         
5753         
5754         var _this = this;
5755         
5756         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5757             e.on('click', _this.sort, _this);
5758         });
5759         
5760         this.el.on("click", this.onClick, this);
5761         this.el.on("dblclick", this.onDblClick, this);
5762         
5763         // why is this done????? = it breaks dialogs??
5764         //this.parent().el.setStyle('position', 'relative');
5765         
5766         
5767         if (this.footer) {
5768             this.footer.parentId = this.id;
5769             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5770         }
5771         
5772         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5773         
5774         this.store.on('load', this.onLoad, this);
5775         this.store.on('beforeload', this.onBeforeLoad, this);
5776         this.store.on('update', this.onUpdate, this);
5777         this.store.on('add', this.onAdd, this);
5778         
5779     },
5780     
5781     onMouseover : function(e, el)
5782     {
5783         var cell = Roo.get(el);
5784         
5785         if(!cell){
5786             return;
5787         }
5788         
5789         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5790             cell = cell.findParent('td', false, true);
5791         }
5792         
5793         var row = cell.findParent('tr', false, true);
5794         var cellIndex = cell.dom.cellIndex;
5795         var rowIndex = row.dom.rowIndex - 1; // start from 0
5796         
5797         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5798         
5799     },
5800     
5801     onMouseout : function(e, el)
5802     {
5803         var cell = Roo.get(el);
5804         
5805         if(!cell){
5806             return;
5807         }
5808         
5809         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5810             cell = cell.findParent('td', false, true);
5811         }
5812         
5813         var row = cell.findParent('tr', false, true);
5814         var cellIndex = cell.dom.cellIndex;
5815         var rowIndex = row.dom.rowIndex - 1; // start from 0
5816         
5817         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5818         
5819     },
5820     
5821     onClick : function(e, el)
5822     {
5823         var cell = Roo.get(el);
5824         
5825         if(!cell || (!this.cellSelection && !this.rowSelection)){
5826             return;
5827         }
5828         
5829         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5830             cell = cell.findParent('td', false, true);
5831         }
5832         
5833         if(!cell || typeof(cell) == 'undefined'){
5834             return;
5835         }
5836         
5837         var row = cell.findParent('tr', false, true);
5838         
5839         if(!row || typeof(row) == 'undefined'){
5840             return;
5841         }
5842         
5843         var cellIndex = cell.dom.cellIndex;
5844         var rowIndex = this.getRowIndex(row);
5845         
5846         // why??? - should these not be based on SelectionModel?
5847         if(this.cellSelection){
5848             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5849         }
5850         
5851         if(this.rowSelection){
5852             this.fireEvent('rowclick', this, row, rowIndex, e);
5853         }
5854         
5855         
5856     },
5857     
5858     onDblClick : function(e,el)
5859     {
5860         var cell = Roo.get(el);
5861         
5862         if(!cell || (!this.CellSelection && !this.RowSelection)){
5863             return;
5864         }
5865         
5866         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5867             cell = cell.findParent('td', false, true);
5868         }
5869         
5870         if(!cell || typeof(cell) == 'undefined'){
5871             return;
5872         }
5873         
5874         var row = cell.findParent('tr', false, true);
5875         
5876         if(!row || typeof(row) == 'undefined'){
5877             return;
5878         }
5879         
5880         var cellIndex = cell.dom.cellIndex;
5881         var rowIndex = this.getRowIndex(row);
5882         
5883         if(this.CellSelection){
5884             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5885         }
5886         
5887         if(this.RowSelection){
5888             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5889         }
5890     },
5891     
5892     sort : function(e,el)
5893     {
5894         var col = Roo.get(el);
5895         
5896         if(!col.hasClass('sortable')){
5897             return;
5898         }
5899         
5900         var sort = col.attr('sort');
5901         var dir = 'ASC';
5902         
5903         if(col.hasClass('glyphicon-arrow-up')){
5904             dir = 'DESC';
5905         }
5906         
5907         this.store.sortInfo = {field : sort, direction : dir};
5908         
5909         if (this.footer) {
5910             Roo.log("calling footer first");
5911             this.footer.onClick('first');
5912         } else {
5913         
5914             this.store.load({ params : { start : 0 } });
5915         }
5916     },
5917     
5918     renderHeader : function()
5919     {
5920         var header = {
5921             tag: 'thead',
5922             cn : []
5923         };
5924         
5925         var cm = this.cm;
5926         
5927         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5928             
5929             var config = cm.config[i];
5930             
5931             var c = {
5932                 tag: 'th',
5933                 style : '',
5934                 html: cm.getColumnHeader(i)
5935             };
5936             
5937             var hh = '';
5938             
5939             if(typeof(config.lgHeader) != 'undefined'){
5940                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5941             }
5942             
5943             if(typeof(config.mdHeader) != 'undefined'){
5944                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5945             }
5946             
5947             if(typeof(config.smHeader) != 'undefined'){
5948                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5949             }
5950             
5951             if(typeof(config.xsHeader) != 'undefined'){
5952                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5953             }
5954             
5955             if(hh.length){
5956                 c.html = hh;
5957             }
5958             
5959             if(typeof(config.tooltip) != 'undefined'){
5960                 c.tooltip = config.tooltip;
5961             }
5962             
5963             if(typeof(config.colspan) != 'undefined'){
5964                 c.colspan = config.colspan;
5965             }
5966             
5967             if(typeof(config.hidden) != 'undefined' && config.hidden){
5968                 c.style += ' display:none;';
5969             }
5970             
5971             if(typeof(config.dataIndex) != 'undefined'){
5972                 c.sort = config.dataIndex;
5973             }
5974             
5975             if(typeof(config.sortable) != 'undefined' && config.sortable){
5976                 c.cls = 'sortable';
5977             }
5978             
5979             if(typeof(config.align) != 'undefined' && config.align.length){
5980                 c.style += ' text-align:' + config.align + ';';
5981             }
5982             
5983             if(typeof(config.width) != 'undefined'){
5984                 c.style += ' width:' + config.width + 'px;';
5985             }
5986             
5987             if(typeof(config.cls) != 'undefined'){
5988                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5989             }
5990             
5991             ['xs','sm','md','lg'].map(function(size){
5992                 
5993                 if(typeof(config[size]) == 'undefined'){
5994                     return;
5995                 }
5996                 
5997                 if (!config[size]) { // 0 = hidden
5998                     cfg.cls += ' hidden-' + size;
5999                     return;
6000                 }
6001                 
6002                 cfg.cls += ' col-' + size + '-' + config[size];
6003
6004             });
6005             
6006             header.cn.push(c)
6007         }
6008         
6009         return header;
6010     },
6011     
6012     renderBody : function()
6013     {
6014         var body = {
6015             tag: 'tbody',
6016             cn : [
6017                 {
6018                     tag: 'tr',
6019                     cn : [
6020                         {
6021                             tag : 'td',
6022                             colspan :  this.cm.getColumnCount()
6023                         }
6024                     ]
6025                 }
6026             ]
6027         };
6028         
6029         return body;
6030     },
6031     
6032     renderFooter : function()
6033     {
6034         var footer = {
6035             tag: 'tfoot',
6036             cn : [
6037                 {
6038                     tag: 'tr',
6039                     cn : [
6040                         {
6041                             tag : 'td',
6042                             colspan :  this.cm.getColumnCount()
6043                         }
6044                     ]
6045                 }
6046             ]
6047         };
6048         
6049         return footer;
6050     },
6051     
6052     
6053     
6054     onLoad : function()
6055     {
6056         Roo.log('ds onload');
6057         this.clear();
6058         
6059         var _this = this;
6060         var cm = this.cm;
6061         var ds = this.store;
6062         
6063         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6064             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6065             
6066             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6067                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6068             }
6069             
6070             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6071                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6072             }
6073         });
6074         
6075         var tbody =  this.mainBody;
6076               
6077         if(ds.getCount() > 0){
6078             ds.data.each(function(d,rowIndex){
6079                 var row =  this.renderRow(cm, ds, rowIndex);
6080                 
6081                 tbody.createChild(row);
6082                 
6083                 var _this = this;
6084                 
6085                 if(row.cellObjects.length){
6086                     Roo.each(row.cellObjects, function(r){
6087                         _this.renderCellObject(r);
6088                     })
6089                 }
6090                 
6091             }, this);
6092         }
6093         
6094         Roo.each(this.el.select('tbody td', true).elements, function(e){
6095             e.on('mouseover', _this.onMouseover, _this);
6096         });
6097         
6098         Roo.each(this.el.select('tbody td', true).elements, function(e){
6099             e.on('mouseout', _this.onMouseout, _this);
6100         });
6101         this.fireEvent('rowsrendered', this);
6102         //if(this.loadMask){
6103         //    this.maskEl.hide();
6104         //}
6105     },
6106     
6107     
6108     onUpdate : function(ds,record)
6109     {
6110         this.refreshRow(record);
6111     },
6112     
6113     onRemove : function(ds, record, index, isUpdate){
6114         if(isUpdate !== true){
6115             this.fireEvent("beforerowremoved", this, index, record);
6116         }
6117         var bt = this.mainBody.dom;
6118         
6119         var rows = this.el.select('tbody > tr', true).elements;
6120         
6121         if(typeof(rows[index]) != 'undefined'){
6122             bt.removeChild(rows[index].dom);
6123         }
6124         
6125 //        if(bt.rows[index]){
6126 //            bt.removeChild(bt.rows[index]);
6127 //        }
6128         
6129         if(isUpdate !== true){
6130             //this.stripeRows(index);
6131             //this.syncRowHeights(index, index);
6132             //this.layout();
6133             this.fireEvent("rowremoved", this, index, record);
6134         }
6135     },
6136     
6137     onAdd : function(ds, records, rowIndex)
6138     {
6139         //Roo.log('on Add called');
6140         // - note this does not handle multiple adding very well..
6141         var bt = this.mainBody.dom;
6142         for (var i =0 ; i < records.length;i++) {
6143             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6144             //Roo.log(records[i]);
6145             //Roo.log(this.store.getAt(rowIndex+i));
6146             this.insertRow(this.store, rowIndex + i, false);
6147             return;
6148         }
6149         
6150     },
6151     
6152     
6153     refreshRow : function(record){
6154         var ds = this.store, index;
6155         if(typeof record == 'number'){
6156             index = record;
6157             record = ds.getAt(index);
6158         }else{
6159             index = ds.indexOf(record);
6160         }
6161         this.insertRow(ds, index, true);
6162         this.onRemove(ds, record, index+1, true);
6163         //this.syncRowHeights(index, index);
6164         //this.layout();
6165         this.fireEvent("rowupdated", this, index, record);
6166     },
6167     
6168     insertRow : function(dm, rowIndex, isUpdate){
6169         
6170         if(!isUpdate){
6171             this.fireEvent("beforerowsinserted", this, rowIndex);
6172         }
6173             //var s = this.getScrollState();
6174         var row = this.renderRow(this.cm, this.store, rowIndex);
6175         // insert before rowIndex..
6176         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6177         
6178         var _this = this;
6179                 
6180         if(row.cellObjects.length){
6181             Roo.each(row.cellObjects, function(r){
6182                 _this.renderCellObject(r);
6183             })
6184         }
6185             
6186         if(!isUpdate){
6187             this.fireEvent("rowsinserted", this, rowIndex);
6188             //this.syncRowHeights(firstRow, lastRow);
6189             //this.stripeRows(firstRow);
6190             //this.layout();
6191         }
6192         
6193     },
6194     
6195     
6196     getRowDom : function(rowIndex)
6197     {
6198         var rows = this.el.select('tbody > tr', true).elements;
6199         
6200         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6201         
6202     },
6203     // returns the object tree for a tr..
6204   
6205     
6206     renderRow : function(cm, ds, rowIndex) 
6207     {
6208         
6209         var d = ds.getAt(rowIndex);
6210         
6211         var row = {
6212             tag : 'tr',
6213             cn : []
6214         };
6215             
6216         var cellObjects = [];
6217         
6218         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6219             var config = cm.config[i];
6220             
6221             var renderer = cm.getRenderer(i);
6222             var value = '';
6223             var id = false;
6224             
6225             if(typeof(renderer) !== 'undefined'){
6226                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6227             }
6228             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6229             // and are rendered into the cells after the row is rendered - using the id for the element.
6230             
6231             if(typeof(value) === 'object'){
6232                 id = Roo.id();
6233                 cellObjects.push({
6234                     container : id,
6235                     cfg : value 
6236                 })
6237             }
6238             
6239             var rowcfg = {
6240                 record: d,
6241                 rowIndex : rowIndex,
6242                 colIndex : i,
6243                 rowClass : ''
6244             };
6245
6246             this.fireEvent('rowclass', this, rowcfg);
6247             
6248             var td = {
6249                 tag: 'td',
6250                 cls : rowcfg.rowClass,
6251                 style: '',
6252                 html: (typeof(value) === 'object') ? '' : value
6253             };
6254             
6255             if (id) {
6256                 td.id = id;
6257             }
6258             
6259             if(typeof(config.colspan) != 'undefined'){
6260                 td.colspan = config.colspan;
6261             }
6262             
6263             if(typeof(config.hidden) != 'undefined' && config.hidden){
6264                 td.style += ' display:none;';
6265             }
6266             
6267             if(typeof(config.align) != 'undefined' && config.align.length){
6268                 td.style += ' text-align:' + config.align + ';';
6269             }
6270             
6271             if(typeof(config.width) != 'undefined'){
6272                 td.style += ' width:' +  config.width + 'px;';
6273             }
6274             
6275             if(typeof(config.cursor) != 'undefined'){
6276                 td.style += ' cursor:' +  config.cursor + ';';
6277             }
6278             
6279             if(typeof(config.cls) != 'undefined'){
6280                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6281             }
6282             
6283             ['xs','sm','md','lg'].map(function(size){
6284                 
6285                 if(typeof(config[size]) == 'undefined'){
6286                     return;
6287                 }
6288                 
6289                 if (!config[size]) { // 0 = hidden
6290                     td.cls += ' hidden-' + size;
6291                     return;
6292                 }
6293                 
6294                 td.cls += ' col-' + size + '-' + config[size];
6295
6296             });
6297              
6298             row.cn.push(td);
6299            
6300         }
6301         
6302         row.cellObjects = cellObjects;
6303         
6304         return row;
6305           
6306     },
6307     
6308     
6309     
6310     onBeforeLoad : function()
6311     {
6312         //Roo.log('ds onBeforeLoad');
6313         
6314         //this.clear();
6315         
6316         //if(this.loadMask){
6317         //    this.maskEl.show();
6318         //}
6319     },
6320      /**
6321      * Remove all rows
6322      */
6323     clear : function()
6324     {
6325         this.el.select('tbody', true).first().dom.innerHTML = '';
6326     },
6327     /**
6328      * Show or hide a row.
6329      * @param {Number} rowIndex to show or hide
6330      * @param {Boolean} state hide
6331      */
6332     setRowVisibility : function(rowIndex, state)
6333     {
6334         var bt = this.mainBody.dom;
6335         
6336         var rows = this.el.select('tbody > tr', true).elements;
6337         
6338         if(typeof(rows[rowIndex]) == 'undefined'){
6339             return;
6340         }
6341         rows[rowIndex].dom.style.display = state ? '' : 'none';
6342     },
6343     
6344     
6345     getSelectionModel : function(){
6346         if(!this.selModel){
6347             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6348         }
6349         return this.selModel;
6350     },
6351     /*
6352      * Render the Roo.bootstrap object from renderder
6353      */
6354     renderCellObject : function(r)
6355     {
6356         var _this = this;
6357         
6358         var t = r.cfg.render(r.container);
6359         
6360         if(r.cfg.cn){
6361             Roo.each(r.cfg.cn, function(c){
6362                 var child = {
6363                     container: t.getChildContainer(),
6364                     cfg: c
6365                 };
6366                 _this.renderCellObject(child);
6367             })
6368         }
6369     },
6370     
6371     getRowIndex : function(row)
6372     {
6373         var rowIndex = -1;
6374         
6375         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6376             if(el != row){
6377                 return;
6378             }
6379             
6380             rowIndex = index;
6381         });
6382         
6383         return rowIndex;
6384     }
6385    
6386 });
6387
6388  
6389
6390  /*
6391  * - LGPL
6392  *
6393  * table cell
6394  * 
6395  */
6396
6397 /**
6398  * @class Roo.bootstrap.TableCell
6399  * @extends Roo.bootstrap.Component
6400  * Bootstrap TableCell class
6401  * @cfg {String} html cell contain text
6402  * @cfg {String} cls cell class
6403  * @cfg {String} tag cell tag (td|th) default td
6404  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6405  * @cfg {String} align Aligns the content in a cell
6406  * @cfg {String} axis Categorizes cells
6407  * @cfg {String} bgcolor Specifies the background color of a cell
6408  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6409  * @cfg {Number} colspan Specifies the number of columns a cell should span
6410  * @cfg {String} headers Specifies one or more header cells a cell is related to
6411  * @cfg {Number} height Sets the height of a cell
6412  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6413  * @cfg {Number} rowspan Sets the number of rows a cell should span
6414  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6415  * @cfg {String} valign Vertical aligns the content in a cell
6416  * @cfg {Number} width Specifies the width of a cell
6417  * 
6418  * @constructor
6419  * Create a new TableCell
6420  * @param {Object} config The config object
6421  */
6422
6423 Roo.bootstrap.TableCell = function(config){
6424     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6425 };
6426
6427 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6428     
6429     html: false,
6430     cls: false,
6431     tag: false,
6432     abbr: false,
6433     align: false,
6434     axis: false,
6435     bgcolor: false,
6436     charoff: false,
6437     colspan: false,
6438     headers: false,
6439     height: false,
6440     nowrap: false,
6441     rowspan: false,
6442     scope: false,
6443     valign: false,
6444     width: false,
6445     
6446     
6447     getAutoCreate : function(){
6448         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6449         
6450         cfg = {
6451             tag: 'td'
6452         };
6453         
6454         if(this.tag){
6455             cfg.tag = this.tag;
6456         }
6457         
6458         if (this.html) {
6459             cfg.html=this.html
6460         }
6461         if (this.cls) {
6462             cfg.cls=this.cls
6463         }
6464         if (this.abbr) {
6465             cfg.abbr=this.abbr
6466         }
6467         if (this.align) {
6468             cfg.align=this.align
6469         }
6470         if (this.axis) {
6471             cfg.axis=this.axis
6472         }
6473         if (this.bgcolor) {
6474             cfg.bgcolor=this.bgcolor
6475         }
6476         if (this.charoff) {
6477             cfg.charoff=this.charoff
6478         }
6479         if (this.colspan) {
6480             cfg.colspan=this.colspan
6481         }
6482         if (this.headers) {
6483             cfg.headers=this.headers
6484         }
6485         if (this.height) {
6486             cfg.height=this.height
6487         }
6488         if (this.nowrap) {
6489             cfg.nowrap=this.nowrap
6490         }
6491         if (this.rowspan) {
6492             cfg.rowspan=this.rowspan
6493         }
6494         if (this.scope) {
6495             cfg.scope=this.scope
6496         }
6497         if (this.valign) {
6498             cfg.valign=this.valign
6499         }
6500         if (this.width) {
6501             cfg.width=this.width
6502         }
6503         
6504         
6505         return cfg;
6506     }
6507    
6508 });
6509
6510  
6511
6512  /*
6513  * - LGPL
6514  *
6515  * table row
6516  * 
6517  */
6518
6519 /**
6520  * @class Roo.bootstrap.TableRow
6521  * @extends Roo.bootstrap.Component
6522  * Bootstrap TableRow class
6523  * @cfg {String} cls row class
6524  * @cfg {String} align Aligns the content in a table row
6525  * @cfg {String} bgcolor Specifies a background color for a table row
6526  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6527  * @cfg {String} valign Vertical aligns the content in a table row
6528  * 
6529  * @constructor
6530  * Create a new TableRow
6531  * @param {Object} config The config object
6532  */
6533
6534 Roo.bootstrap.TableRow = function(config){
6535     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6536 };
6537
6538 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6539     
6540     cls: false,
6541     align: false,
6542     bgcolor: false,
6543     charoff: false,
6544     valign: false,
6545     
6546     getAutoCreate : function(){
6547         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6548         
6549         cfg = {
6550             tag: 'tr'
6551         };
6552             
6553         if(this.cls){
6554             cfg.cls = this.cls;
6555         }
6556         if(this.align){
6557             cfg.align = this.align;
6558         }
6559         if(this.bgcolor){
6560             cfg.bgcolor = this.bgcolor;
6561         }
6562         if(this.charoff){
6563             cfg.charoff = this.charoff;
6564         }
6565         if(this.valign){
6566             cfg.valign = this.valign;
6567         }
6568         
6569         return cfg;
6570     }
6571    
6572 });
6573
6574  
6575
6576  /*
6577  * - LGPL
6578  *
6579  * table body
6580  * 
6581  */
6582
6583 /**
6584  * @class Roo.bootstrap.TableBody
6585  * @extends Roo.bootstrap.Component
6586  * Bootstrap TableBody class
6587  * @cfg {String} cls element class
6588  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6589  * @cfg {String} align Aligns the content inside the element
6590  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6591  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6592  * 
6593  * @constructor
6594  * Create a new TableBody
6595  * @param {Object} config The config object
6596  */
6597
6598 Roo.bootstrap.TableBody = function(config){
6599     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6600 };
6601
6602 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6603     
6604     cls: false,
6605     tag: false,
6606     align: false,
6607     charoff: false,
6608     valign: false,
6609     
6610     getAutoCreate : function(){
6611         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6612         
6613         cfg = {
6614             tag: 'tbody'
6615         };
6616             
6617         if (this.cls) {
6618             cfg.cls=this.cls
6619         }
6620         if(this.tag){
6621             cfg.tag = this.tag;
6622         }
6623         
6624         if(this.align){
6625             cfg.align = this.align;
6626         }
6627         if(this.charoff){
6628             cfg.charoff = this.charoff;
6629         }
6630         if(this.valign){
6631             cfg.valign = this.valign;
6632         }
6633         
6634         return cfg;
6635     }
6636     
6637     
6638 //    initEvents : function()
6639 //    {
6640 //        
6641 //        if(!this.store){
6642 //            return;
6643 //        }
6644 //        
6645 //        this.store = Roo.factory(this.store, Roo.data);
6646 //        this.store.on('load', this.onLoad, this);
6647 //        
6648 //        this.store.load();
6649 //        
6650 //    },
6651 //    
6652 //    onLoad: function () 
6653 //    {   
6654 //        this.fireEvent('load', this);
6655 //    }
6656 //    
6657 //   
6658 });
6659
6660  
6661
6662  /*
6663  * Based on:
6664  * Ext JS Library 1.1.1
6665  * Copyright(c) 2006-2007, Ext JS, LLC.
6666  *
6667  * Originally Released Under LGPL - original licence link has changed is not relivant.
6668  *
6669  * Fork - LGPL
6670  * <script type="text/javascript">
6671  */
6672
6673 // as we use this in bootstrap.
6674 Roo.namespace('Roo.form');
6675  /**
6676  * @class Roo.form.Action
6677  * Internal Class used to handle form actions
6678  * @constructor
6679  * @param {Roo.form.BasicForm} el The form element or its id
6680  * @param {Object} config Configuration options
6681  */
6682
6683  
6684  
6685 // define the action interface
6686 Roo.form.Action = function(form, options){
6687     this.form = form;
6688     this.options = options || {};
6689 };
6690 /**
6691  * Client Validation Failed
6692  * @const 
6693  */
6694 Roo.form.Action.CLIENT_INVALID = 'client';
6695 /**
6696  * Server Validation Failed
6697  * @const 
6698  */
6699 Roo.form.Action.SERVER_INVALID = 'server';
6700  /**
6701  * Connect to Server Failed
6702  * @const 
6703  */
6704 Roo.form.Action.CONNECT_FAILURE = 'connect';
6705 /**
6706  * Reading Data from Server Failed
6707  * @const 
6708  */
6709 Roo.form.Action.LOAD_FAILURE = 'load';
6710
6711 Roo.form.Action.prototype = {
6712     type : 'default',
6713     failureType : undefined,
6714     response : undefined,
6715     result : undefined,
6716
6717     // interface method
6718     run : function(options){
6719
6720     },
6721
6722     // interface method
6723     success : function(response){
6724
6725     },
6726
6727     // interface method
6728     handleResponse : function(response){
6729
6730     },
6731
6732     // default connection failure
6733     failure : function(response){
6734         
6735         this.response = response;
6736         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6737         this.form.afterAction(this, false);
6738     },
6739
6740     processResponse : function(response){
6741         this.response = response;
6742         if(!response.responseText){
6743             return true;
6744         }
6745         this.result = this.handleResponse(response);
6746         return this.result;
6747     },
6748
6749     // utility functions used internally
6750     getUrl : function(appendParams){
6751         var url = this.options.url || this.form.url || this.form.el.dom.action;
6752         if(appendParams){
6753             var p = this.getParams();
6754             if(p){
6755                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6756             }
6757         }
6758         return url;
6759     },
6760
6761     getMethod : function(){
6762         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6763     },
6764
6765     getParams : function(){
6766         var bp = this.form.baseParams;
6767         var p = this.options.params;
6768         if(p){
6769             if(typeof p == "object"){
6770                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6771             }else if(typeof p == 'string' && bp){
6772                 p += '&' + Roo.urlEncode(bp);
6773             }
6774         }else if(bp){
6775             p = Roo.urlEncode(bp);
6776         }
6777         return p;
6778     },
6779
6780     createCallback : function(){
6781         return {
6782             success: this.success,
6783             failure: this.failure,
6784             scope: this,
6785             timeout: (this.form.timeout*1000),
6786             upload: this.form.fileUpload ? this.success : undefined
6787         };
6788     }
6789 };
6790
6791 Roo.form.Action.Submit = function(form, options){
6792     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6793 };
6794
6795 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6796     type : 'submit',
6797
6798     haveProgress : false,
6799     uploadComplete : false,
6800     
6801     // uploadProgress indicator.
6802     uploadProgress : function()
6803     {
6804         if (!this.form.progressUrl) {
6805             return;
6806         }
6807         
6808         if (!this.haveProgress) {
6809             Roo.MessageBox.progress("Uploading", "Uploading");
6810         }
6811         if (this.uploadComplete) {
6812            Roo.MessageBox.hide();
6813            return;
6814         }
6815         
6816         this.haveProgress = true;
6817    
6818         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6819         
6820         var c = new Roo.data.Connection();
6821         c.request({
6822             url : this.form.progressUrl,
6823             params: {
6824                 id : uid
6825             },
6826             method: 'GET',
6827             success : function(req){
6828                //console.log(data);
6829                 var rdata = false;
6830                 var edata;
6831                 try  {
6832                    rdata = Roo.decode(req.responseText)
6833                 } catch (e) {
6834                     Roo.log("Invalid data from server..");
6835                     Roo.log(edata);
6836                     return;
6837                 }
6838                 if (!rdata || !rdata.success) {
6839                     Roo.log(rdata);
6840                     Roo.MessageBox.alert(Roo.encode(rdata));
6841                     return;
6842                 }
6843                 var data = rdata.data;
6844                 
6845                 if (this.uploadComplete) {
6846                    Roo.MessageBox.hide();
6847                    return;
6848                 }
6849                    
6850                 if (data){
6851                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6852                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6853                     );
6854                 }
6855                 this.uploadProgress.defer(2000,this);
6856             },
6857        
6858             failure: function(data) {
6859                 Roo.log('progress url failed ');
6860                 Roo.log(data);
6861             },
6862             scope : this
6863         });
6864            
6865     },
6866     
6867     
6868     run : function()
6869     {
6870         // run get Values on the form, so it syncs any secondary forms.
6871         this.form.getValues();
6872         
6873         var o = this.options;
6874         var method = this.getMethod();
6875         var isPost = method == 'POST';
6876         if(o.clientValidation === false || this.form.isValid()){
6877             
6878             if (this.form.progressUrl) {
6879                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6880                     (new Date() * 1) + '' + Math.random());
6881                     
6882             } 
6883             
6884             
6885             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6886                 form:this.form.el.dom,
6887                 url:this.getUrl(!isPost),
6888                 method: method,
6889                 params:isPost ? this.getParams() : null,
6890                 isUpload: this.form.fileUpload
6891             }));
6892             
6893             this.uploadProgress();
6894
6895         }else if (o.clientValidation !== false){ // client validation failed
6896             this.failureType = Roo.form.Action.CLIENT_INVALID;
6897             this.form.afterAction(this, false);
6898         }
6899     },
6900
6901     success : function(response)
6902     {
6903         this.uploadComplete= true;
6904         if (this.haveProgress) {
6905             Roo.MessageBox.hide();
6906         }
6907         
6908         
6909         var result = this.processResponse(response);
6910         if(result === true || result.success){
6911             this.form.afterAction(this, true);
6912             return;
6913         }
6914         if(result.errors){
6915             this.form.markInvalid(result.errors);
6916             this.failureType = Roo.form.Action.SERVER_INVALID;
6917         }
6918         this.form.afterAction(this, false);
6919     },
6920     failure : function(response)
6921     {
6922         this.uploadComplete= true;
6923         if (this.haveProgress) {
6924             Roo.MessageBox.hide();
6925         }
6926         
6927         this.response = response;
6928         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6929         this.form.afterAction(this, false);
6930     },
6931     
6932     handleResponse : function(response){
6933         if(this.form.errorReader){
6934             var rs = this.form.errorReader.read(response);
6935             var errors = [];
6936             if(rs.records){
6937                 for(var i = 0, len = rs.records.length; i < len; i++) {
6938                     var r = rs.records[i];
6939                     errors[i] = r.data;
6940                 }
6941             }
6942             if(errors.length < 1){
6943                 errors = null;
6944             }
6945             return {
6946                 success : rs.success,
6947                 errors : errors
6948             };
6949         }
6950         var ret = false;
6951         try {
6952             ret = Roo.decode(response.responseText);
6953         } catch (e) {
6954             ret = {
6955                 success: false,
6956                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6957                 errors : []
6958             };
6959         }
6960         return ret;
6961         
6962     }
6963 });
6964
6965
6966 Roo.form.Action.Load = function(form, options){
6967     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6968     this.reader = this.form.reader;
6969 };
6970
6971 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6972     type : 'load',
6973
6974     run : function(){
6975         
6976         Roo.Ajax.request(Roo.apply(
6977                 this.createCallback(), {
6978                     method:this.getMethod(),
6979                     url:this.getUrl(false),
6980                     params:this.getParams()
6981         }));
6982     },
6983
6984     success : function(response){
6985         
6986         var result = this.processResponse(response);
6987         if(result === true || !result.success || !result.data){
6988             this.failureType = Roo.form.Action.LOAD_FAILURE;
6989             this.form.afterAction(this, false);
6990             return;
6991         }
6992         this.form.clearInvalid();
6993         this.form.setValues(result.data);
6994         this.form.afterAction(this, true);
6995     },
6996
6997     handleResponse : function(response){
6998         if(this.form.reader){
6999             var rs = this.form.reader.read(response);
7000             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7001             return {
7002                 success : rs.success,
7003                 data : data
7004             };
7005         }
7006         return Roo.decode(response.responseText);
7007     }
7008 });
7009
7010 Roo.form.Action.ACTION_TYPES = {
7011     'load' : Roo.form.Action.Load,
7012     'submit' : Roo.form.Action.Submit
7013 };/*
7014  * - LGPL
7015  *
7016  * form
7017  * 
7018  */
7019
7020 /**
7021  * @class Roo.bootstrap.Form
7022  * @extends Roo.bootstrap.Component
7023  * Bootstrap Form class
7024  * @cfg {String} method  GET | POST (default POST)
7025  * @cfg {String} labelAlign top | left (default top)
7026  * @cfg {String} align left  | right - for navbars
7027  * @cfg {Boolean} loadMask load mask when submit (default true)
7028
7029  * 
7030  * @constructor
7031  * Create a new Form
7032  * @param {Object} config The config object
7033  */
7034
7035
7036 Roo.bootstrap.Form = function(config){
7037     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7038     this.addEvents({
7039         /**
7040          * @event clientvalidation
7041          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7042          * @param {Form} this
7043          * @param {Boolean} valid true if the form has passed client-side validation
7044          */
7045         clientvalidation: true,
7046         /**
7047          * @event beforeaction
7048          * Fires before any action is performed. Return false to cancel the action.
7049          * @param {Form} this
7050          * @param {Action} action The action to be performed
7051          */
7052         beforeaction: true,
7053         /**
7054          * @event actionfailed
7055          * Fires when an action fails.
7056          * @param {Form} this
7057          * @param {Action} action The action that failed
7058          */
7059         actionfailed : true,
7060         /**
7061          * @event actioncomplete
7062          * Fires when an action is completed.
7063          * @param {Form} this
7064          * @param {Action} action The action that completed
7065          */
7066         actioncomplete : true
7067     });
7068     
7069 };
7070
7071 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7072       
7073      /**
7074      * @cfg {String} method
7075      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7076      */
7077     method : 'POST',
7078     /**
7079      * @cfg {String} url
7080      * The URL to use for form actions if one isn't supplied in the action options.
7081      */
7082     /**
7083      * @cfg {Boolean} fileUpload
7084      * Set to true if this form is a file upload.
7085      */
7086      
7087     /**
7088      * @cfg {Object} baseParams
7089      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7090      */
7091       
7092     /**
7093      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7094      */
7095     timeout: 30,
7096     /**
7097      * @cfg {Sting} align (left|right) for navbar forms
7098      */
7099     align : 'left',
7100
7101     // private
7102     activeAction : null,
7103  
7104     /**
7105      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7106      * element by passing it or its id or mask the form itself by passing in true.
7107      * @type Mixed
7108      */
7109     waitMsgTarget : false,
7110     
7111     loadMask : true,
7112     
7113     getAutoCreate : function(){
7114         
7115         var cfg = {
7116             tag: 'form',
7117             method : this.method || 'POST',
7118             id : this.id || Roo.id(),
7119             cls : ''
7120         };
7121         if (this.parent().xtype.match(/^Nav/)) {
7122             cfg.cls = 'navbar-form navbar-' + this.align;
7123             
7124         }
7125         
7126         if (this.labelAlign == 'left' ) {
7127             cfg.cls += ' form-horizontal';
7128         }
7129         
7130         
7131         return cfg;
7132     },
7133     initEvents : function()
7134     {
7135         this.el.on('submit', this.onSubmit, this);
7136         // this was added as random key presses on the form where triggering form submit.
7137         this.el.on('keypress', function(e) {
7138             if (e.getCharCode() != 13) {
7139                 return true;
7140             }
7141             // we might need to allow it for textareas.. and some other items.
7142             // check e.getTarget().
7143             
7144             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7145                 return true;
7146             }
7147         
7148             Roo.log("keypress blocked");
7149             
7150             e.preventDefault();
7151             return false;
7152         });
7153         
7154     },
7155     // private
7156     onSubmit : function(e){
7157         e.stopEvent();
7158     },
7159     
7160      /**
7161      * Returns true if client-side validation on the form is successful.
7162      * @return Boolean
7163      */
7164     isValid : function(){
7165         var items = this.getItems();
7166         var valid = true;
7167         items.each(function(f){
7168            if(!f.validate()){
7169                valid = false;
7170                
7171            }
7172         });
7173         return valid;
7174     },
7175     /**
7176      * Returns true if any fields in this form have changed since their original load.
7177      * @return Boolean
7178      */
7179     isDirty : function(){
7180         var dirty = false;
7181         var items = this.getItems();
7182         items.each(function(f){
7183            if(f.isDirty()){
7184                dirty = true;
7185                return false;
7186            }
7187            return true;
7188         });
7189         return dirty;
7190     },
7191      /**
7192      * Performs a predefined action (submit or load) or custom actions you define on this form.
7193      * @param {String} actionName The name of the action type
7194      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7195      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7196      * accept other config options):
7197      * <pre>
7198 Property          Type             Description
7199 ----------------  ---------------  ----------------------------------------------------------------------------------
7200 url               String           The url for the action (defaults to the form's url)
7201 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7202 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7203 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7204                                    validate the form on the client (defaults to false)
7205      * </pre>
7206      * @return {BasicForm} this
7207      */
7208     doAction : function(action, options){
7209         if(typeof action == 'string'){
7210             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7211         }
7212         if(this.fireEvent('beforeaction', this, action) !== false){
7213             this.beforeAction(action);
7214             action.run.defer(100, action);
7215         }
7216         return this;
7217     },
7218     
7219     // private
7220     beforeAction : function(action){
7221         var o = action.options;
7222         
7223         if(this.loadMask){
7224             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7225         }
7226         // not really supported yet.. ??
7227         
7228         //if(this.waitMsgTarget === true){
7229         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7230         //}else if(this.waitMsgTarget){
7231         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7232         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7233         //}else {
7234         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7235        // }
7236          
7237     },
7238
7239     // private
7240     afterAction : function(action, success){
7241         this.activeAction = null;
7242         var o = action.options;
7243         
7244         //if(this.waitMsgTarget === true){
7245             this.el.unmask();
7246         //}else if(this.waitMsgTarget){
7247         //    this.waitMsgTarget.unmask();
7248         //}else{
7249         //    Roo.MessageBox.updateProgress(1);
7250         //    Roo.MessageBox.hide();
7251        // }
7252         // 
7253         if(success){
7254             if(o.reset){
7255                 this.reset();
7256             }
7257             Roo.callback(o.success, o.scope, [this, action]);
7258             this.fireEvent('actioncomplete', this, action);
7259             
7260         }else{
7261             
7262             // failure condition..
7263             // we have a scenario where updates need confirming.
7264             // eg. if a locking scenario exists..
7265             // we look for { errors : { needs_confirm : true }} in the response.
7266             if (
7267                 (typeof(action.result) != 'undefined')  &&
7268                 (typeof(action.result.errors) != 'undefined')  &&
7269                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7270            ){
7271                 var _t = this;
7272                 Roo.log("not supported yet");
7273                  /*
7274                 
7275                 Roo.MessageBox.confirm(
7276                     "Change requires confirmation",
7277                     action.result.errorMsg,
7278                     function(r) {
7279                         if (r != 'yes') {
7280                             return;
7281                         }
7282                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7283                     }
7284                     
7285                 );
7286                 */
7287                 
7288                 
7289                 return;
7290             }
7291             
7292             Roo.callback(o.failure, o.scope, [this, action]);
7293             // show an error message if no failed handler is set..
7294             if (!this.hasListener('actionfailed')) {
7295                 Roo.log("need to add dialog support");
7296                 /*
7297                 Roo.MessageBox.alert("Error",
7298                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7299                         action.result.errorMsg :
7300                         "Saving Failed, please check your entries or try again"
7301                 );
7302                 */
7303             }
7304             
7305             this.fireEvent('actionfailed', this, action);
7306         }
7307         
7308     },
7309     /**
7310      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7311      * @param {String} id The value to search for
7312      * @return Field
7313      */
7314     findField : function(id){
7315         var items = this.getItems();
7316         var field = items.get(id);
7317         if(!field){
7318              items.each(function(f){
7319                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7320                     field = f;
7321                     return false;
7322                 }
7323                 return true;
7324             });
7325         }
7326         return field || null;
7327     },
7328      /**
7329      * Mark fields in this form invalid in bulk.
7330      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7331      * @return {BasicForm} this
7332      */
7333     markInvalid : function(errors){
7334         if(errors instanceof Array){
7335             for(var i = 0, len = errors.length; i < len; i++){
7336                 var fieldError = errors[i];
7337                 var f = this.findField(fieldError.id);
7338                 if(f){
7339                     f.markInvalid(fieldError.msg);
7340                 }
7341             }
7342         }else{
7343             var field, id;
7344             for(id in errors){
7345                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7346                     field.markInvalid(errors[id]);
7347                 }
7348             }
7349         }
7350         //Roo.each(this.childForms || [], function (f) {
7351         //    f.markInvalid(errors);
7352         //});
7353         
7354         return this;
7355     },
7356
7357     /**
7358      * Set values for fields in this form in bulk.
7359      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7360      * @return {BasicForm} this
7361      */
7362     setValues : function(values){
7363         if(values instanceof Array){ // array of objects
7364             for(var i = 0, len = values.length; i < len; i++){
7365                 var v = values[i];
7366                 var f = this.findField(v.id);
7367                 if(f){
7368                     f.setValue(v.value);
7369                     if(this.trackResetOnLoad){
7370                         f.originalValue = f.getValue();
7371                     }
7372                 }
7373             }
7374         }else{ // object hash
7375             var field, id;
7376             for(id in values){
7377                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7378                     
7379                     if (field.setFromData && 
7380                         field.valueField && 
7381                         field.displayField &&
7382                         // combos' with local stores can 
7383                         // be queried via setValue()
7384                         // to set their value..
7385                         (field.store && !field.store.isLocal)
7386                         ) {
7387                         // it's a combo
7388                         var sd = { };
7389                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7390                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7391                         field.setFromData(sd);
7392                         
7393                     } else {
7394                         field.setValue(values[id]);
7395                     }
7396                     
7397                     
7398                     if(this.trackResetOnLoad){
7399                         field.originalValue = field.getValue();
7400                     }
7401                 }
7402             }
7403         }
7404          
7405         //Roo.each(this.childForms || [], function (f) {
7406         //    f.setValues(values);
7407         //});
7408                 
7409         return this;
7410     },
7411
7412     /**
7413      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7414      * they are returned as an array.
7415      * @param {Boolean} asString
7416      * @return {Object}
7417      */
7418     getValues : function(asString){
7419         //if (this.childForms) {
7420             // copy values from the child forms
7421         //    Roo.each(this.childForms, function (f) {
7422         //        this.setValues(f.getValues());
7423         //    }, this);
7424         //}
7425         
7426         
7427         
7428         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7429         if(asString === true){
7430             return fs;
7431         }
7432         return Roo.urlDecode(fs);
7433     },
7434     
7435     /**
7436      * Returns the fields in this form as an object with key/value pairs. 
7437      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7438      * @return {Object}
7439      */
7440     getFieldValues : function(with_hidden)
7441     {
7442         var items = this.getItems();
7443         var ret = {};
7444         items.each(function(f){
7445             if (!f.getName()) {
7446                 return;
7447             }
7448             var v = f.getValue();
7449             if (f.inputType =='radio') {
7450                 if (typeof(ret[f.getName()]) == 'undefined') {
7451                     ret[f.getName()] = ''; // empty..
7452                 }
7453                 
7454                 if (!f.el.dom.checked) {
7455                     return;
7456                     
7457                 }
7458                 v = f.el.dom.value;
7459                 
7460             }
7461             
7462             // not sure if this supported any more..
7463             if ((typeof(v) == 'object') && f.getRawValue) {
7464                 v = f.getRawValue() ; // dates..
7465             }
7466             // combo boxes where name != hiddenName...
7467             if (f.name != f.getName()) {
7468                 ret[f.name] = f.getRawValue();
7469             }
7470             ret[f.getName()] = v;
7471         });
7472         
7473         return ret;
7474     },
7475
7476     /**
7477      * Clears all invalid messages in this form.
7478      * @return {BasicForm} this
7479      */
7480     clearInvalid : function(){
7481         var items = this.getItems();
7482         
7483         items.each(function(f){
7484            f.clearInvalid();
7485         });
7486         
7487         
7488         
7489         return this;
7490     },
7491
7492     /**
7493      * Resets this form.
7494      * @return {BasicForm} this
7495      */
7496     reset : function(){
7497         var items = this.getItems();
7498         items.each(function(f){
7499             f.reset();
7500         });
7501         
7502         Roo.each(this.childForms || [], function (f) {
7503             f.reset();
7504         });
7505        
7506         
7507         return this;
7508     },
7509     getItems : function()
7510     {
7511         var r=new Roo.util.MixedCollection(false, function(o){
7512             return o.id || (o.id = Roo.id());
7513         });
7514         var iter = function(el) {
7515             if (el.inputEl) {
7516                 r.add(el);
7517             }
7518             if (!el.items) {
7519                 return;
7520             }
7521             Roo.each(el.items,function(e) {
7522                 iter(e);
7523             });
7524             
7525             
7526         };
7527         
7528         iter(this);
7529         return r;
7530         
7531         
7532         
7533         
7534     }
7535     
7536 });
7537
7538  
7539 /*
7540  * Based on:
7541  * Ext JS Library 1.1.1
7542  * Copyright(c) 2006-2007, Ext JS, LLC.
7543  *
7544  * Originally Released Under LGPL - original licence link has changed is not relivant.
7545  *
7546  * Fork - LGPL
7547  * <script type="text/javascript">
7548  */
7549 /**
7550  * @class Roo.form.VTypes
7551  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7552  * @singleton
7553  */
7554 Roo.form.VTypes = function(){
7555     // closure these in so they are only created once.
7556     var alpha = /^[a-zA-Z_]+$/;
7557     var alphanum = /^[a-zA-Z0-9_]+$/;
7558     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7559     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7560
7561     // All these messages and functions are configurable
7562     return {
7563         /**
7564          * The function used to validate email addresses
7565          * @param {String} value The email address
7566          */
7567         'email' : function(v){
7568             return email.test(v);
7569         },
7570         /**
7571          * The error text to display when the email validation function returns false
7572          * @type String
7573          */
7574         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7575         /**
7576          * The keystroke filter mask to be applied on email input
7577          * @type RegExp
7578          */
7579         'emailMask' : /[a-z0-9_\.\-@]/i,
7580
7581         /**
7582          * The function used to validate URLs
7583          * @param {String} value The URL
7584          */
7585         'url' : function(v){
7586             return url.test(v);
7587         },
7588         /**
7589          * The error text to display when the url validation function returns false
7590          * @type String
7591          */
7592         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7593         
7594         /**
7595          * The function used to validate alpha values
7596          * @param {String} value The value
7597          */
7598         'alpha' : function(v){
7599             return alpha.test(v);
7600         },
7601         /**
7602          * The error text to display when the alpha validation function returns false
7603          * @type String
7604          */
7605         'alphaText' : 'This field should only contain letters and _',
7606         /**
7607          * The keystroke filter mask to be applied on alpha input
7608          * @type RegExp
7609          */
7610         'alphaMask' : /[a-z_]/i,
7611
7612         /**
7613          * The function used to validate alphanumeric values
7614          * @param {String} value The value
7615          */
7616         'alphanum' : function(v){
7617             return alphanum.test(v);
7618         },
7619         /**
7620          * The error text to display when the alphanumeric validation function returns false
7621          * @type String
7622          */
7623         'alphanumText' : 'This field should only contain letters, numbers and _',
7624         /**
7625          * The keystroke filter mask to be applied on alphanumeric input
7626          * @type RegExp
7627          */
7628         'alphanumMask' : /[a-z0-9_]/i
7629     };
7630 }();/*
7631  * - LGPL
7632  *
7633  * Input
7634  * 
7635  */
7636
7637 /**
7638  * @class Roo.bootstrap.Input
7639  * @extends Roo.bootstrap.Component
7640  * Bootstrap Input class
7641  * @cfg {Boolean} disabled is it disabled
7642  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7643  * @cfg {String} name name of the input
7644  * @cfg {string} fieldLabel - the label associated
7645  * @cfg {string} placeholder - placeholder to put in text.
7646  * @cfg {string}  before - input group add on before
7647  * @cfg {string} after - input group add on after
7648  * @cfg {string} size - (lg|sm) or leave empty..
7649  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7650  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7651  * @cfg {Number} md colspan out of 12 for computer-sized screens
7652  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7653  * @cfg {string} value default value of the input
7654  * @cfg {Number} labelWidth set the width of label (0-12)
7655  * @cfg {String} labelAlign (top|left)
7656  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7657  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7658
7659  * @cfg {String} align (left|center|right) Default left
7660  * @cfg {Boolean} forceFeedback (true|false) Default false
7661  * 
7662  * 
7663  * 
7664  * 
7665  * @constructor
7666  * Create a new Input
7667  * @param {Object} config The config object
7668  */
7669
7670 Roo.bootstrap.Input = function(config){
7671     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7672    
7673         this.addEvents({
7674             /**
7675              * @event focus
7676              * Fires when this field receives input focus.
7677              * @param {Roo.form.Field} this
7678              */
7679             focus : true,
7680             /**
7681              * @event blur
7682              * Fires when this field loses input focus.
7683              * @param {Roo.form.Field} this
7684              */
7685             blur : true,
7686             /**
7687              * @event specialkey
7688              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7689              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7690              * @param {Roo.form.Field} this
7691              * @param {Roo.EventObject} e The event object
7692              */
7693             specialkey : true,
7694             /**
7695              * @event change
7696              * Fires just before the field blurs if the field value has changed.
7697              * @param {Roo.form.Field} this
7698              * @param {Mixed} newValue The new value
7699              * @param {Mixed} oldValue The original value
7700              */
7701             change : true,
7702             /**
7703              * @event invalid
7704              * Fires after the field has been marked as invalid.
7705              * @param {Roo.form.Field} this
7706              * @param {String} msg The validation message
7707              */
7708             invalid : true,
7709             /**
7710              * @event valid
7711              * Fires after the field has been validated with no errors.
7712              * @param {Roo.form.Field} this
7713              */
7714             valid : true,
7715              /**
7716              * @event keyup
7717              * Fires after the key up
7718              * @param {Roo.form.Field} this
7719              * @param {Roo.EventObject}  e The event Object
7720              */
7721             keyup : true
7722         });
7723 };
7724
7725 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7726      /**
7727      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7728       automatic validation (defaults to "keyup").
7729      */
7730     validationEvent : "keyup",
7731      /**
7732      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7733      */
7734     validateOnBlur : true,
7735     /**
7736      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7737      */
7738     validationDelay : 250,
7739      /**
7740      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7741      */
7742     focusClass : "x-form-focus",  // not needed???
7743     
7744        
7745     /**
7746      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7747      */
7748     invalidClass : "has-warning",
7749     
7750     /**
7751      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7752      */
7753     validClass : "has-success",
7754     
7755     /**
7756      * @cfg {Boolean} hasFeedback (true|false) default true
7757      */
7758     hasFeedback : true,
7759     
7760     /**
7761      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7762      */
7763     invalidFeedbackClass : "glyphicon-warning-sign",
7764     
7765     /**
7766      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7767      */
7768     validFeedbackClass : "glyphicon-ok",
7769     
7770     /**
7771      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7772      */
7773     selectOnFocus : false,
7774     
7775      /**
7776      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7777      */
7778     maskRe : null,
7779        /**
7780      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7781      */
7782     vtype : null,
7783     
7784       /**
7785      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7786      */
7787     disableKeyFilter : false,
7788     
7789        /**
7790      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7791      */
7792     disabled : false,
7793      /**
7794      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7795      */
7796     allowBlank : true,
7797     /**
7798      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7799      */
7800     blankText : "This field is required",
7801     
7802      /**
7803      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7804      */
7805     minLength : 0,
7806     /**
7807      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7808      */
7809     maxLength : Number.MAX_VALUE,
7810     /**
7811      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7812      */
7813     minLengthText : "The minimum length for this field is {0}",
7814     /**
7815      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7816      */
7817     maxLengthText : "The maximum length for this field is {0}",
7818   
7819     
7820     /**
7821      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7822      * If available, this function will be called only after the basic validators all return true, and will be passed the
7823      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7824      */
7825     validator : null,
7826     /**
7827      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7828      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7829      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7830      */
7831     regex : null,
7832     /**
7833      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7834      */
7835     regexText : "",
7836     
7837     autocomplete: false,
7838     
7839     
7840     fieldLabel : '',
7841     inputType : 'text',
7842     
7843     name : false,
7844     placeholder: false,
7845     before : false,
7846     after : false,
7847     size : false,
7848     hasFocus : false,
7849     preventMark: false,
7850     isFormField : true,
7851     value : '',
7852     labelWidth : 2,
7853     labelAlign : false,
7854     readOnly : false,
7855     align : false,
7856     formatedValue : false,
7857     forceFeedback : false,
7858     
7859     parentLabelAlign : function()
7860     {
7861         var parent = this;
7862         while (parent.parent()) {
7863             parent = parent.parent();
7864             if (typeof(parent.labelAlign) !='undefined') {
7865                 return parent.labelAlign;
7866             }
7867         }
7868         return 'left';
7869         
7870     },
7871     
7872     getAutoCreate : function(){
7873         
7874         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7875         
7876         var id = Roo.id();
7877         
7878         var cfg = {};
7879         
7880         if(this.inputType != 'hidden'){
7881             cfg.cls = 'form-group' //input-group
7882         }
7883         
7884         var input =  {
7885             tag: 'input',
7886             id : id,
7887             type : this.inputType,
7888             value : this.value,
7889             cls : 'form-control',
7890             placeholder : this.placeholder || '',
7891             autocomplete : this.autocomplete || 'new-password'
7892         };
7893         
7894         
7895         if(this.align){
7896             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7897         }
7898         
7899         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7900             input.maxLength = this.maxLength;
7901         }
7902         
7903         if (this.disabled) {
7904             input.disabled=true;
7905         }
7906         
7907         if (this.readOnly) {
7908             input.readonly=true;
7909         }
7910         
7911         if (this.name) {
7912             input.name = this.name;
7913         }
7914         if (this.size) {
7915             input.cls += ' input-' + this.size;
7916         }
7917         var settings=this;
7918         ['xs','sm','md','lg'].map(function(size){
7919             if (settings[size]) {
7920                 cfg.cls += ' col-' + size + '-' + settings[size];
7921             }
7922         });
7923         
7924         var inputblock = input;
7925         
7926         var feedback = {
7927             tag: 'span',
7928             cls: 'glyphicon form-control-feedback'
7929         };
7930             
7931         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7932             
7933             inputblock = {
7934                 cls : 'has-feedback',
7935                 cn :  [
7936                     input,
7937                     feedback
7938                 ] 
7939             };  
7940         }
7941         
7942         if (this.before || this.after) {
7943             
7944             inputblock = {
7945                 cls : 'input-group',
7946                 cn :  [] 
7947             };
7948             
7949             if (this.before && typeof(this.before) == 'string') {
7950                 
7951                 inputblock.cn.push({
7952                     tag :'span',
7953                     cls : 'roo-input-before input-group-addon',
7954                     html : this.before
7955                 });
7956             }
7957             if (this.before && typeof(this.before) == 'object') {
7958                 this.before = Roo.factory(this.before);
7959                 Roo.log(this.before);
7960                 inputblock.cn.push({
7961                     tag :'span',
7962                     cls : 'roo-input-before input-group-' +
7963                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7964                 });
7965             }
7966             
7967             inputblock.cn.push(input);
7968             
7969             if (this.after && typeof(this.after) == 'string') {
7970                 inputblock.cn.push({
7971                     tag :'span',
7972                     cls : 'roo-input-after input-group-addon',
7973                     html : this.after
7974                 });
7975             }
7976             if (this.after && typeof(this.after) == 'object') {
7977                 this.after = Roo.factory(this.after);
7978                 Roo.log(this.after);
7979                 inputblock.cn.push({
7980                     tag :'span',
7981                     cls : 'roo-input-after input-group-' +
7982                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7983                 });
7984             }
7985             
7986             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7987                 inputblock.cls += ' has-feedback';
7988                 inputblock.cn.push(feedback);
7989             }
7990         };
7991         
7992         if (align ==='left' && this.fieldLabel.length) {
7993                 Roo.log("left and has label");
7994                 cfg.cn = [
7995                     
7996                     {
7997                         tag: 'label',
7998                         'for' :  id,
7999                         cls : 'control-label col-sm-' + this.labelWidth,
8000                         html : this.fieldLabel
8001                         
8002                     },
8003                     {
8004                         cls : "col-sm-" + (12 - this.labelWidth), 
8005                         cn: [
8006                             inputblock
8007                         ]
8008                     }
8009                     
8010                 ];
8011         } else if ( this.fieldLabel.length) {
8012                 Roo.log(" label");
8013                  cfg.cn = [
8014                    
8015                     {
8016                         tag: 'label',
8017                         //cls : 'input-group-addon',
8018                         html : this.fieldLabel
8019                         
8020                     },
8021                     
8022                     inputblock
8023                     
8024                 ];
8025
8026         } else {
8027             
8028                 Roo.log(" no label && no align");
8029                 cfg.cn = [
8030                     
8031                         inputblock
8032                     
8033                 ];
8034                 
8035                 
8036         };
8037         Roo.log('input-parentType: ' + this.parentType);
8038         
8039         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8040            cfg.cls += ' navbar-form';
8041            Roo.log(cfg);
8042         }
8043         
8044         return cfg;
8045         
8046     },
8047     /**
8048      * return the real input element.
8049      */
8050     inputEl: function ()
8051     {
8052         return this.el.select('input.form-control',true).first();
8053     },
8054     
8055     tooltipEl : function()
8056     {
8057         return this.inputEl();
8058     },
8059     
8060     setDisabled : function(v)
8061     {
8062         var i  = this.inputEl().dom;
8063         if (!v) {
8064             i.removeAttribute('disabled');
8065             return;
8066             
8067         }
8068         i.setAttribute('disabled','true');
8069     },
8070     initEvents : function()
8071     {
8072           
8073         this.inputEl().on("keydown" , this.fireKey,  this);
8074         this.inputEl().on("focus", this.onFocus,  this);
8075         this.inputEl().on("blur", this.onBlur,  this);
8076         
8077         this.inputEl().relayEvent('keyup', this);
8078  
8079         // reference to original value for reset
8080         this.originalValue = this.getValue();
8081         //Roo.form.TextField.superclass.initEvents.call(this);
8082         if(this.validationEvent == 'keyup'){
8083             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8084             this.inputEl().on('keyup', this.filterValidation, this);
8085         }
8086         else if(this.validationEvent !== false){
8087             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8088         }
8089         
8090         if(this.selectOnFocus){
8091             this.on("focus", this.preFocus, this);
8092             
8093         }
8094         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8095             this.inputEl().on("keypress", this.filterKeys, this);
8096         }
8097        /* if(this.grow){
8098             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8099             this.el.on("click", this.autoSize,  this);
8100         }
8101         */
8102         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8103             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8104         }
8105         
8106         if (typeof(this.before) == 'object') {
8107             this.before.render(this.el.select('.roo-input-before',true).first());
8108         }
8109         if (typeof(this.after) == 'object') {
8110             this.after.render(this.el.select('.roo-input-after',true).first());
8111         }
8112         
8113         
8114     },
8115     filterValidation : function(e){
8116         if(!e.isNavKeyPress()){
8117             this.validationTask.delay(this.validationDelay);
8118         }
8119     },
8120      /**
8121      * Validates the field value
8122      * @return {Boolean} True if the value is valid, else false
8123      */
8124     validate : function(){
8125         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8126         if(this.disabled || this.validateValue(this.getRawValue())){
8127             this.markValid();
8128             return true;
8129         }
8130         
8131         this.markInvalid();
8132         return false;
8133     },
8134     
8135     
8136     /**
8137      * Validates a value according to the field's validation rules and marks the field as invalid
8138      * if the validation fails
8139      * @param {Mixed} value The value to validate
8140      * @return {Boolean} True if the value is valid, else false
8141      */
8142     validateValue : function(value){
8143         if(value.length < 1)  { // if it's blank
8144             if(this.allowBlank){
8145                 return true;
8146             }
8147             return false;
8148         }
8149         
8150         if(value.length < this.minLength){
8151             return false;
8152         }
8153         if(value.length > this.maxLength){
8154             return false;
8155         }
8156         if(this.vtype){
8157             var vt = Roo.form.VTypes;
8158             if(!vt[this.vtype](value, this)){
8159                 return false;
8160             }
8161         }
8162         if(typeof this.validator == "function"){
8163             var msg = this.validator(value);
8164             if(msg !== true){
8165                 return false;
8166             }
8167         }
8168         
8169         if(this.regex && !this.regex.test(value)){
8170             return false;
8171         }
8172         
8173         return true;
8174     },
8175
8176     
8177     
8178      // private
8179     fireKey : function(e){
8180         //Roo.log('field ' + e.getKey());
8181         if(e.isNavKeyPress()){
8182             this.fireEvent("specialkey", this, e);
8183         }
8184     },
8185     focus : function (selectText){
8186         if(this.rendered){
8187             this.inputEl().focus();
8188             if(selectText === true){
8189                 this.inputEl().dom.select();
8190             }
8191         }
8192         return this;
8193     } ,
8194     
8195     onFocus : function(){
8196         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8197            // this.el.addClass(this.focusClass);
8198         }
8199         if(!this.hasFocus){
8200             this.hasFocus = true;
8201             this.startValue = this.getValue();
8202             this.fireEvent("focus", this);
8203         }
8204     },
8205     
8206     beforeBlur : Roo.emptyFn,
8207
8208     
8209     // private
8210     onBlur : function(){
8211         this.beforeBlur();
8212         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8213             //this.el.removeClass(this.focusClass);
8214         }
8215         this.hasFocus = false;
8216         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8217             this.validate();
8218         }
8219         var v = this.getValue();
8220         if(String(v) !== String(this.startValue)){
8221             this.fireEvent('change', this, v, this.startValue);
8222         }
8223         this.fireEvent("blur", this);
8224     },
8225     
8226     /**
8227      * Resets the current field value to the originally loaded value and clears any validation messages
8228      */
8229     reset : function(){
8230         this.setValue(this.originalValue);
8231         this.validate();
8232     },
8233      /**
8234      * Returns the name of the field
8235      * @return {Mixed} name The name field
8236      */
8237     getName: function(){
8238         return this.name;
8239     },
8240      /**
8241      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8242      * @return {Mixed} value The field value
8243      */
8244     getValue : function(){
8245         
8246         var v = this.inputEl().getValue();
8247         
8248         return v;
8249     },
8250     /**
8251      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8252      * @return {Mixed} value The field value
8253      */
8254     getRawValue : function(){
8255         var v = this.inputEl().getValue();
8256         
8257         return v;
8258     },
8259     
8260     /**
8261      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8262      * @param {Mixed} value The value to set
8263      */
8264     setRawValue : function(v){
8265         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8266     },
8267     
8268     selectText : function(start, end){
8269         var v = this.getRawValue();
8270         if(v.length > 0){
8271             start = start === undefined ? 0 : start;
8272             end = end === undefined ? v.length : end;
8273             var d = this.inputEl().dom;
8274             if(d.setSelectionRange){
8275                 d.setSelectionRange(start, end);
8276             }else if(d.createTextRange){
8277                 var range = d.createTextRange();
8278                 range.moveStart("character", start);
8279                 range.moveEnd("character", v.length-end);
8280                 range.select();
8281             }
8282         }
8283     },
8284     
8285     /**
8286      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8287      * @param {Mixed} value The value to set
8288      */
8289     setValue : function(v){
8290         this.value = v;
8291         if(this.rendered){
8292             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8293             this.validate();
8294         }
8295     },
8296     
8297     /*
8298     processValue : function(value){
8299         if(this.stripCharsRe){
8300             var newValue = value.replace(this.stripCharsRe, '');
8301             if(newValue !== value){
8302                 this.setRawValue(newValue);
8303                 return newValue;
8304             }
8305         }
8306         return value;
8307     },
8308   */
8309     preFocus : function(){
8310         
8311         if(this.selectOnFocus){
8312             this.inputEl().dom.select();
8313         }
8314     },
8315     filterKeys : function(e){
8316         var k = e.getKey();
8317         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8318             return;
8319         }
8320         var c = e.getCharCode(), cc = String.fromCharCode(c);
8321         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8322             return;
8323         }
8324         if(!this.maskRe.test(cc)){
8325             e.stopEvent();
8326         }
8327     },
8328      /**
8329      * Clear any invalid styles/messages for this field
8330      */
8331     clearInvalid : function(){
8332         
8333         if(!this.el || this.preventMark){ // not rendered
8334             return;
8335         }
8336         this.el.removeClass(this.invalidClass);
8337         
8338         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8339             
8340             var feedback = this.el.select('.form-control-feedback', true).first();
8341             
8342             if(feedback){
8343                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8344             }
8345             
8346         }
8347         
8348         this.fireEvent('valid', this);
8349     },
8350     
8351      /**
8352      * Mark this field as valid
8353      */
8354     markValid : function()
8355     {
8356         if(!this.el  || this.preventMark){ // not rendered
8357             return;
8358         }
8359         
8360         this.el.removeClass([this.invalidClass, this.validClass]);
8361         
8362         var feedback = this.el.select('.form-control-feedback', true).first();
8363             
8364         if(feedback){
8365             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8366         }
8367
8368         if(this.disabled || this.allowBlank){
8369             return;
8370         }
8371         
8372         var formGroup = this.el.findParent('.form-group', false, true);
8373         
8374         if(formGroup){
8375             
8376             var label = formGroup.select('label', true).first();
8377             var icon = formGroup.select('i.fa-star', true).first();
8378             
8379             if(label && icon){
8380                 icon.remove();
8381             }
8382         }
8383         
8384         this.el.addClass(this.validClass);
8385         
8386         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8387             
8388             var feedback = this.el.select('.form-control-feedback', true).first();
8389             
8390             if(feedback){
8391                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8392                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8393             }
8394             
8395         }
8396         
8397         this.fireEvent('valid', this);
8398     },
8399     
8400      /**
8401      * Mark this field as invalid
8402      * @param {String} msg The validation message
8403      */
8404     markInvalid : function(msg)
8405     {
8406         if(!this.el  || this.preventMark){ // not rendered
8407             return;
8408         }
8409         
8410         this.el.removeClass([this.invalidClass, this.validClass]);
8411         
8412         var feedback = this.el.select('.form-control-feedback', true).first();
8413             
8414         if(feedback){
8415             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8416         }
8417
8418         if(this.disabled || this.allowBlank){
8419             return;
8420         }
8421         
8422         var formGroup = this.el.findParent('.form-group', false, true);
8423         
8424         if(formGroup){
8425             var label = formGroup.select('label', true).first();
8426             var icon = formGroup.select('i.fa-star', true).first();
8427
8428             if(!this.getValue().length && label && !icon){
8429                 this.el.findParent('.form-group', false, true).createChild({
8430                     tag : 'i',
8431                     cls : 'text-danger fa fa-lg fa-star',
8432                     tooltip : 'This field is required',
8433                     style : 'margin-right:5px;'
8434                 }, label, true);
8435             }
8436         }
8437         
8438         
8439         this.el.addClass(this.invalidClass);
8440         
8441         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8442             
8443             var feedback = this.el.select('.form-control-feedback', true).first();
8444             
8445             if(feedback){
8446                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8447                 
8448                 if(this.getValue().length || this.forceFeedback){
8449                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8450                 }
8451                 
8452             }
8453             
8454         }
8455         
8456         this.fireEvent('invalid', this, msg);
8457     },
8458     // private
8459     SafariOnKeyDown : function(event)
8460     {
8461         // this is a workaround for a password hang bug on chrome/ webkit.
8462         
8463         var isSelectAll = false;
8464         
8465         if(this.inputEl().dom.selectionEnd > 0){
8466             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8467         }
8468         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8469             event.preventDefault();
8470             this.setValue('');
8471             return;
8472         }
8473         
8474         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8475             
8476             event.preventDefault();
8477             // this is very hacky as keydown always get's upper case.
8478             //
8479             var cc = String.fromCharCode(event.getCharCode());
8480             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8481             
8482         }
8483     },
8484     adjustWidth : function(tag, w){
8485         tag = tag.toLowerCase();
8486         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8487             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8488                 if(tag == 'input'){
8489                     return w + 2;
8490                 }
8491                 if(tag == 'textarea'){
8492                     return w-2;
8493                 }
8494             }else if(Roo.isOpera){
8495                 if(tag == 'input'){
8496                     return w + 2;
8497                 }
8498                 if(tag == 'textarea'){
8499                     return w-2;
8500                 }
8501             }
8502         }
8503         return w;
8504     }
8505     
8506 });
8507
8508  
8509 /*
8510  * - LGPL
8511  *
8512  * Input
8513  * 
8514  */
8515
8516 /**
8517  * @class Roo.bootstrap.TextArea
8518  * @extends Roo.bootstrap.Input
8519  * Bootstrap TextArea class
8520  * @cfg {Number} cols Specifies the visible width of a text area
8521  * @cfg {Number} rows Specifies the visible number of lines in a text area
8522  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8523  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8524  * @cfg {string} html text
8525  * 
8526  * @constructor
8527  * Create a new TextArea
8528  * @param {Object} config The config object
8529  */
8530
8531 Roo.bootstrap.TextArea = function(config){
8532     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8533    
8534 };
8535
8536 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8537      
8538     cols : false,
8539     rows : 5,
8540     readOnly : false,
8541     warp : 'soft',
8542     resize : false,
8543     value: false,
8544     html: false,
8545     
8546     getAutoCreate : function(){
8547         
8548         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8549         
8550         var id = Roo.id();
8551         
8552         var cfg = {};
8553         
8554         var input =  {
8555             tag: 'textarea',
8556             id : id,
8557             warp : this.warp,
8558             rows : this.rows,
8559             value : this.value || '',
8560             html: this.html || '',
8561             cls : 'form-control',
8562             placeholder : this.placeholder || '' 
8563             
8564         };
8565         
8566         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8567             input.maxLength = this.maxLength;
8568         }
8569         
8570         if(this.resize){
8571             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8572         }
8573         
8574         if(this.cols){
8575             input.cols = this.cols;
8576         }
8577         
8578         if (this.readOnly) {
8579             input.readonly = true;
8580         }
8581         
8582         if (this.name) {
8583             input.name = this.name;
8584         }
8585         
8586         if (this.size) {
8587             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8588         }
8589         
8590         var settings=this;
8591         ['xs','sm','md','lg'].map(function(size){
8592             if (settings[size]) {
8593                 cfg.cls += ' col-' + size + '-' + settings[size];
8594             }
8595         });
8596         
8597         var inputblock = input;
8598         
8599         if(this.hasFeedback && !this.allowBlank){
8600             
8601             var feedback = {
8602                 tag: 'span',
8603                 cls: 'glyphicon form-control-feedback'
8604             };
8605
8606             inputblock = {
8607                 cls : 'has-feedback',
8608                 cn :  [
8609                     input,
8610                     feedback
8611                 ] 
8612             };  
8613         }
8614         
8615         
8616         if (this.before || this.after) {
8617             
8618             inputblock = {
8619                 cls : 'input-group',
8620                 cn :  [] 
8621             };
8622             if (this.before) {
8623                 inputblock.cn.push({
8624                     tag :'span',
8625                     cls : 'input-group-addon',
8626                     html : this.before
8627                 });
8628             }
8629             
8630             inputblock.cn.push(input);
8631             
8632             if(this.hasFeedback && !this.allowBlank){
8633                 inputblock.cls += ' has-feedback';
8634                 inputblock.cn.push(feedback);
8635             }
8636             
8637             if (this.after) {
8638                 inputblock.cn.push({
8639                     tag :'span',
8640                     cls : 'input-group-addon',
8641                     html : this.after
8642                 });
8643             }
8644             
8645         }
8646         
8647         if (align ==='left' && this.fieldLabel.length) {
8648                 Roo.log("left and has label");
8649                 cfg.cn = [
8650                     
8651                     {
8652                         tag: 'label',
8653                         'for' :  id,
8654                         cls : 'control-label col-sm-' + this.labelWidth,
8655                         html : this.fieldLabel
8656                         
8657                     },
8658                     {
8659                         cls : "col-sm-" + (12 - this.labelWidth), 
8660                         cn: [
8661                             inputblock
8662                         ]
8663                     }
8664                     
8665                 ];
8666         } else if ( this.fieldLabel.length) {
8667                 Roo.log(" label");
8668                  cfg.cn = [
8669                    
8670                     {
8671                         tag: 'label',
8672                         //cls : 'input-group-addon',
8673                         html : this.fieldLabel
8674                         
8675                     },
8676                     
8677                     inputblock
8678                     
8679                 ];
8680
8681         } else {
8682             
8683                    Roo.log(" no label && no align");
8684                 cfg.cn = [
8685                     
8686                         inputblock
8687                     
8688                 ];
8689                 
8690                 
8691         }
8692         
8693         if (this.disabled) {
8694             input.disabled=true;
8695         }
8696         
8697         return cfg;
8698         
8699     },
8700     /**
8701      * return the real textarea element.
8702      */
8703     inputEl: function ()
8704     {
8705         return this.el.select('textarea.form-control',true).first();
8706     }
8707 });
8708
8709  
8710 /*
8711  * - LGPL
8712  *
8713  * trigger field - base class for combo..
8714  * 
8715  */
8716  
8717 /**
8718  * @class Roo.bootstrap.TriggerField
8719  * @extends Roo.bootstrap.Input
8720  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8721  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8722  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8723  * for which you can provide a custom implementation.  For example:
8724  * <pre><code>
8725 var trigger = new Roo.bootstrap.TriggerField();
8726 trigger.onTriggerClick = myTriggerFn;
8727 trigger.applyTo('my-field');
8728 </code></pre>
8729  *
8730  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8731  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8732  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8733  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8734  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8735
8736  * @constructor
8737  * Create a new TriggerField.
8738  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8739  * to the base TextField)
8740  */
8741 Roo.bootstrap.TriggerField = function(config){
8742     this.mimicing = false;
8743     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8744 };
8745
8746 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8747     /**
8748      * @cfg {String} triggerClass A CSS class to apply to the trigger
8749      */
8750      /**
8751      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8752      */
8753     hideTrigger:false,
8754
8755     /**
8756      * @cfg {Boolean} removable (true|false) special filter default false
8757      */
8758     removable : false,
8759     
8760     /** @cfg {Boolean} grow @hide */
8761     /** @cfg {Number} growMin @hide */
8762     /** @cfg {Number} growMax @hide */
8763
8764     /**
8765      * @hide 
8766      * @method
8767      */
8768     autoSize: Roo.emptyFn,
8769     // private
8770     monitorTab : true,
8771     // private
8772     deferHeight : true,
8773
8774     
8775     actionMode : 'wrap',
8776     
8777     caret : false,
8778     
8779     
8780     getAutoCreate : function(){
8781        
8782         var align = this.labelAlign || this.parentLabelAlign();
8783         
8784         var id = Roo.id();
8785         
8786         var cfg = {
8787             cls: 'form-group' //input-group
8788         };
8789         
8790         
8791         var input =  {
8792             tag: 'input',
8793             id : id,
8794             type : this.inputType,
8795             cls : 'form-control',
8796             autocomplete: 'new-password',
8797             placeholder : this.placeholder || '' 
8798             
8799         };
8800         if (this.name) {
8801             input.name = this.name;
8802         }
8803         if (this.size) {
8804             input.cls += ' input-' + this.size;
8805         }
8806         
8807         if (this.disabled) {
8808             input.disabled=true;
8809         }
8810         
8811         var inputblock = input;
8812         
8813         if(this.hasFeedback && !this.allowBlank){
8814             
8815             var feedback = {
8816                 tag: 'span',
8817                 cls: 'glyphicon form-control-feedback'
8818             };
8819             
8820             if(this.removable && !this.editable && !this.tickable){
8821                 inputblock = {
8822                     cls : 'has-feedback',
8823                     cn :  [
8824                         inputblock,
8825                         {
8826                             tag: 'button',
8827                             html : 'x',
8828                             cls : 'roo-combo-removable-btn close'
8829                         },
8830                         feedback
8831                     ] 
8832                 };
8833             } else {
8834                 inputblock = {
8835                     cls : 'has-feedback',
8836                     cn :  [
8837                         inputblock,
8838                         feedback
8839                     ] 
8840                 };
8841             }
8842
8843         } else {
8844             if(this.removable && !this.editable && !this.tickable){
8845                 inputblock = {
8846                     cls : 'roo-removable',
8847                     cn :  [
8848                         inputblock,
8849                         {
8850                             tag: 'button',
8851                             html : 'x',
8852                             cls : 'roo-combo-removable-btn close'
8853                         }
8854                     ] 
8855                 };
8856             }
8857         }
8858         
8859         if (this.before || this.after) {
8860             
8861             inputblock = {
8862                 cls : 'input-group',
8863                 cn :  [] 
8864             };
8865             if (this.before) {
8866                 inputblock.cn.push({
8867                     tag :'span',
8868                     cls : 'input-group-addon',
8869                     html : this.before
8870                 });
8871             }
8872             
8873             inputblock.cn.push(input);
8874             
8875             if(this.hasFeedback && !this.allowBlank){
8876                 inputblock.cls += ' has-feedback';
8877                 inputblock.cn.push(feedback);
8878             }
8879             
8880             if (this.after) {
8881                 inputblock.cn.push({
8882                     tag :'span',
8883                     cls : 'input-group-addon',
8884                     html : this.after
8885                 });
8886             }
8887             
8888         };
8889         
8890         var box = {
8891             tag: 'div',
8892             cn: [
8893                 {
8894                     tag: 'input',
8895                     type : 'hidden',
8896                     cls: 'form-hidden-field'
8897                 },
8898                 inputblock
8899             ]
8900             
8901         };
8902         
8903         if(this.multiple){
8904             Roo.log('multiple');
8905             
8906             box = {
8907                 tag: 'div',
8908                 cn: [
8909                     {
8910                         tag: 'input',
8911                         type : 'hidden',
8912                         cls: 'form-hidden-field'
8913                     },
8914                     {
8915                         tag: 'ul',
8916                         cls: 'select2-choices',
8917                         cn:[
8918                             {
8919                                 tag: 'li',
8920                                 cls: 'select2-search-field',
8921                                 cn: [
8922
8923                                     inputblock
8924                                 ]
8925                             }
8926                         ]
8927                     }
8928                 ]
8929             }
8930         };
8931         
8932         var combobox = {
8933             cls: 'select2-container input-group',
8934             cn: [
8935                 box
8936 //                {
8937 //                    tag: 'ul',
8938 //                    cls: 'typeahead typeahead-long dropdown-menu',
8939 //                    style: 'display:none'
8940 //                }
8941             ]
8942         };
8943         
8944         if(!this.multiple && this.showToggleBtn){
8945             
8946             var caret = {
8947                         tag: 'span',
8948                         cls: 'caret'
8949              };
8950             if (this.caret != false) {
8951                 caret = {
8952                      tag: 'i',
8953                      cls: 'fa fa-' + this.caret
8954                 };
8955                 
8956             }
8957             
8958             combobox.cn.push({
8959                 tag :'span',
8960                 cls : 'input-group-addon btn dropdown-toggle',
8961                 cn : [
8962                     caret,
8963                     {
8964                         tag: 'span',
8965                         cls: 'combobox-clear',
8966                         cn  : [
8967                             {
8968                                 tag : 'i',
8969                                 cls: 'icon-remove'
8970                             }
8971                         ]
8972                     }
8973                 ]
8974
8975             })
8976         }
8977         
8978         if(this.multiple){
8979             combobox.cls += ' select2-container-multi';
8980         }
8981         
8982         if (align ==='left' && this.fieldLabel.length) {
8983             
8984                 Roo.log("left and has label");
8985                 cfg.cn = [
8986                     
8987                     {
8988                         tag: 'label',
8989                         'for' :  id,
8990                         cls : 'control-label col-sm-' + this.labelWidth,
8991                         html : this.fieldLabel
8992                         
8993                     },
8994                     {
8995                         cls : "col-sm-" + (12 - this.labelWidth), 
8996                         cn: [
8997                             combobox
8998                         ]
8999                     }
9000                     
9001                 ];
9002         } else if ( this.fieldLabel.length) {
9003                 Roo.log(" label");
9004                  cfg.cn = [
9005                    
9006                     {
9007                         tag: 'label',
9008                         //cls : 'input-group-addon',
9009                         html : this.fieldLabel
9010                         
9011                     },
9012                     
9013                     combobox
9014                     
9015                 ];
9016
9017         } else {
9018             
9019                 Roo.log(" no label && no align");
9020                 cfg = combobox
9021                      
9022                 
9023         }
9024          
9025         var settings=this;
9026         ['xs','sm','md','lg'].map(function(size){
9027             if (settings[size]) {
9028                 cfg.cls += ' col-' + size + '-' + settings[size];
9029             }
9030         });
9031         Roo.log(cfg);
9032         return cfg;
9033         
9034     },
9035     
9036     
9037     
9038     // private
9039     onResize : function(w, h){
9040 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9041 //        if(typeof w == 'number'){
9042 //            var x = w - this.trigger.getWidth();
9043 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9044 //            this.trigger.setStyle('left', x+'px');
9045 //        }
9046     },
9047
9048     // private
9049     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9050
9051     // private
9052     getResizeEl : function(){
9053         return this.inputEl();
9054     },
9055
9056     // private
9057     getPositionEl : function(){
9058         return this.inputEl();
9059     },
9060
9061     // private
9062     alignErrorIcon : function(){
9063         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9064     },
9065
9066     // private
9067     initEvents : function(){
9068         
9069         this.createList();
9070         
9071         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9072         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9073         if(!this.multiple && this.showToggleBtn){
9074             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9075             if(this.hideTrigger){
9076                 this.trigger.setDisplayed(false);
9077             }
9078             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9079         }
9080         
9081         if(this.multiple){
9082             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9083         }
9084         
9085         if(this.removable && !this.editable && !this.tickable){
9086             var close = this.closeTriggerEl();
9087             
9088             if(close){
9089                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9090                 close.on('click', this.removeBtnClick, this, close);
9091             }
9092         }
9093         
9094         //this.trigger.addClassOnOver('x-form-trigger-over');
9095         //this.trigger.addClassOnClick('x-form-trigger-click');
9096         
9097         //if(!this.width){
9098         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9099         //}
9100     },
9101     
9102     closeTriggerEl : function()
9103     {
9104         var close = this.el.select('.roo-combo-removable-btn', true).first();
9105         return close ? close : false;
9106     },
9107     
9108     removeBtnClick : function(e, h, el)
9109     {
9110         e.preventDefault();
9111         
9112         if(this.fireEvent("remove", this) !== false){
9113             this.reset();
9114         }
9115     },
9116     
9117     createList : function()
9118     {
9119         this.list = Roo.get(document.body).createChild({
9120             tag: 'ul',
9121             cls: 'typeahead typeahead-long dropdown-menu',
9122             style: 'display:none'
9123         });
9124         
9125         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9126         
9127     },
9128
9129     // private
9130     initTrigger : function(){
9131        
9132     },
9133
9134     // private
9135     onDestroy : function(){
9136         if(this.trigger){
9137             this.trigger.removeAllListeners();
9138           //  this.trigger.remove();
9139         }
9140         //if(this.wrap){
9141         //    this.wrap.remove();
9142         //}
9143         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9144     },
9145
9146     // private
9147     onFocus : function(){
9148         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9149         /*
9150         if(!this.mimicing){
9151             this.wrap.addClass('x-trigger-wrap-focus');
9152             this.mimicing = true;
9153             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9154             if(this.monitorTab){
9155                 this.el.on("keydown", this.checkTab, this);
9156             }
9157         }
9158         */
9159     },
9160
9161     // private
9162     checkTab : function(e){
9163         if(e.getKey() == e.TAB){
9164             this.triggerBlur();
9165         }
9166     },
9167
9168     // private
9169     onBlur : function(){
9170         // do nothing
9171     },
9172
9173     // private
9174     mimicBlur : function(e, t){
9175         /*
9176         if(!this.wrap.contains(t) && this.validateBlur()){
9177             this.triggerBlur();
9178         }
9179         */
9180     },
9181
9182     // private
9183     triggerBlur : function(){
9184         this.mimicing = false;
9185         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9186         if(this.monitorTab){
9187             this.el.un("keydown", this.checkTab, this);
9188         }
9189         //this.wrap.removeClass('x-trigger-wrap-focus');
9190         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9191     },
9192
9193     // private
9194     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9195     validateBlur : function(e, t){
9196         return true;
9197     },
9198
9199     // private
9200     onDisable : function(){
9201         this.inputEl().dom.disabled = true;
9202         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9203         //if(this.wrap){
9204         //    this.wrap.addClass('x-item-disabled');
9205         //}
9206     },
9207
9208     // private
9209     onEnable : function(){
9210         this.inputEl().dom.disabled = false;
9211         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9212         //if(this.wrap){
9213         //    this.el.removeClass('x-item-disabled');
9214         //}
9215     },
9216
9217     // private
9218     onShow : function(){
9219         var ae = this.getActionEl();
9220         
9221         if(ae){
9222             ae.dom.style.display = '';
9223             ae.dom.style.visibility = 'visible';
9224         }
9225     },
9226
9227     // private
9228     
9229     onHide : function(){
9230         var ae = this.getActionEl();
9231         ae.dom.style.display = 'none';
9232     },
9233
9234     /**
9235      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9236      * by an implementing function.
9237      * @method
9238      * @param {EventObject} e
9239      */
9240     onTriggerClick : Roo.emptyFn
9241 });
9242  /*
9243  * Based on:
9244  * Ext JS Library 1.1.1
9245  * Copyright(c) 2006-2007, Ext JS, LLC.
9246  *
9247  * Originally Released Under LGPL - original licence link has changed is not relivant.
9248  *
9249  * Fork - LGPL
9250  * <script type="text/javascript">
9251  */
9252
9253
9254 /**
9255  * @class Roo.data.SortTypes
9256  * @singleton
9257  * Defines the default sorting (casting?) comparison functions used when sorting data.
9258  */
9259 Roo.data.SortTypes = {
9260     /**
9261      * Default sort that does nothing
9262      * @param {Mixed} s The value being converted
9263      * @return {Mixed} The comparison value
9264      */
9265     none : function(s){
9266         return s;
9267     },
9268     
9269     /**
9270      * The regular expression used to strip tags
9271      * @type {RegExp}
9272      * @property
9273      */
9274     stripTagsRE : /<\/?[^>]+>/gi,
9275     
9276     /**
9277      * Strips all HTML tags to sort on text only
9278      * @param {Mixed} s The value being converted
9279      * @return {String} The comparison value
9280      */
9281     asText : function(s){
9282         return String(s).replace(this.stripTagsRE, "");
9283     },
9284     
9285     /**
9286      * Strips all HTML tags to sort on text only - Case insensitive
9287      * @param {Mixed} s The value being converted
9288      * @return {String} The comparison value
9289      */
9290     asUCText : function(s){
9291         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9292     },
9293     
9294     /**
9295      * Case insensitive string
9296      * @param {Mixed} s The value being converted
9297      * @return {String} The comparison value
9298      */
9299     asUCString : function(s) {
9300         return String(s).toUpperCase();
9301     },
9302     
9303     /**
9304      * Date sorting
9305      * @param {Mixed} s The value being converted
9306      * @return {Number} The comparison value
9307      */
9308     asDate : function(s) {
9309         if(!s){
9310             return 0;
9311         }
9312         if(s instanceof Date){
9313             return s.getTime();
9314         }
9315         return Date.parse(String(s));
9316     },
9317     
9318     /**
9319      * Float sorting
9320      * @param {Mixed} s The value being converted
9321      * @return {Float} The comparison value
9322      */
9323     asFloat : function(s) {
9324         var val = parseFloat(String(s).replace(/,/g, ""));
9325         if(isNaN(val)) val = 0;
9326         return val;
9327     },
9328     
9329     /**
9330      * Integer sorting
9331      * @param {Mixed} s The value being converted
9332      * @return {Number} The comparison value
9333      */
9334     asInt : function(s) {
9335         var val = parseInt(String(s).replace(/,/g, ""));
9336         if(isNaN(val)) val = 0;
9337         return val;
9338     }
9339 };/*
9340  * Based on:
9341  * Ext JS Library 1.1.1
9342  * Copyright(c) 2006-2007, Ext JS, LLC.
9343  *
9344  * Originally Released Under LGPL - original licence link has changed is not relivant.
9345  *
9346  * Fork - LGPL
9347  * <script type="text/javascript">
9348  */
9349
9350 /**
9351 * @class Roo.data.Record
9352  * Instances of this class encapsulate both record <em>definition</em> information, and record
9353  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9354  * to access Records cached in an {@link Roo.data.Store} object.<br>
9355  * <p>
9356  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9357  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9358  * objects.<br>
9359  * <p>
9360  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9361  * @constructor
9362  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9363  * {@link #create}. The parameters are the same.
9364  * @param {Array} data An associative Array of data values keyed by the field name.
9365  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9366  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9367  * not specified an integer id is generated.
9368  */
9369 Roo.data.Record = function(data, id){
9370     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9371     this.data = data;
9372 };
9373
9374 /**
9375  * Generate a constructor for a specific record layout.
9376  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9377  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9378  * Each field definition object may contain the following properties: <ul>
9379  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
9380  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9381  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9382  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9383  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9384  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9385  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9386  * this may be omitted.</p></li>
9387  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9388  * <ul><li>auto (Default, implies no conversion)</li>
9389  * <li>string</li>
9390  * <li>int</li>
9391  * <li>float</li>
9392  * <li>boolean</li>
9393  * <li>date</li></ul></p></li>
9394  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9395  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9396  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9397  * by the Reader into an object that will be stored in the Record. It is passed the
9398  * following parameters:<ul>
9399  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9400  * </ul></p></li>
9401  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9402  * </ul>
9403  * <br>usage:<br><pre><code>
9404 var TopicRecord = Roo.data.Record.create(
9405     {name: 'title', mapping: 'topic_title'},
9406     {name: 'author', mapping: 'username'},
9407     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9408     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9409     {name: 'lastPoster', mapping: 'user2'},
9410     {name: 'excerpt', mapping: 'post_text'}
9411 );
9412
9413 var myNewRecord = new TopicRecord({
9414     title: 'Do my job please',
9415     author: 'noobie',
9416     totalPosts: 1,
9417     lastPost: new Date(),
9418     lastPoster: 'Animal',
9419     excerpt: 'No way dude!'
9420 });
9421 myStore.add(myNewRecord);
9422 </code></pre>
9423  * @method create
9424  * @static
9425  */
9426 Roo.data.Record.create = function(o){
9427     var f = function(){
9428         f.superclass.constructor.apply(this, arguments);
9429     };
9430     Roo.extend(f, Roo.data.Record);
9431     var p = f.prototype;
9432     p.fields = new Roo.util.MixedCollection(false, function(field){
9433         return field.name;
9434     });
9435     for(var i = 0, len = o.length; i < len; i++){
9436         p.fields.add(new Roo.data.Field(o[i]));
9437     }
9438     f.getField = function(name){
9439         return p.fields.get(name);  
9440     };
9441     return f;
9442 };
9443
9444 Roo.data.Record.AUTO_ID = 1000;
9445 Roo.data.Record.EDIT = 'edit';
9446 Roo.data.Record.REJECT = 'reject';
9447 Roo.data.Record.COMMIT = 'commit';
9448
9449 Roo.data.Record.prototype = {
9450     /**
9451      * Readonly flag - true if this record has been modified.
9452      * @type Boolean
9453      */
9454     dirty : false,
9455     editing : false,
9456     error: null,
9457     modified: null,
9458
9459     // private
9460     join : function(store){
9461         this.store = store;
9462     },
9463
9464     /**
9465      * Set the named field to the specified value.
9466      * @param {String} name The name of the field to set.
9467      * @param {Object} value The value to set the field to.
9468      */
9469     set : function(name, value){
9470         if(this.data[name] == value){
9471             return;
9472         }
9473         this.dirty = true;
9474         if(!this.modified){
9475             this.modified = {};
9476         }
9477         if(typeof this.modified[name] == 'undefined'){
9478             this.modified[name] = this.data[name];
9479         }
9480         this.data[name] = value;
9481         if(!this.editing && this.store){
9482             this.store.afterEdit(this);
9483         }       
9484     },
9485
9486     /**
9487      * Get the value of the named field.
9488      * @param {String} name The name of the field to get the value of.
9489      * @return {Object} The value of the field.
9490      */
9491     get : function(name){
9492         return this.data[name]; 
9493     },
9494
9495     // private
9496     beginEdit : function(){
9497         this.editing = true;
9498         this.modified = {}; 
9499     },
9500
9501     // private
9502     cancelEdit : function(){
9503         this.editing = false;
9504         delete this.modified;
9505     },
9506
9507     // private
9508     endEdit : function(){
9509         this.editing = false;
9510         if(this.dirty && this.store){
9511             this.store.afterEdit(this);
9512         }
9513     },
9514
9515     /**
9516      * Usually called by the {@link Roo.data.Store} which owns the Record.
9517      * Rejects all changes made to the Record since either creation, or the last commit operation.
9518      * Modified fields are reverted to their original values.
9519      * <p>
9520      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9521      * of reject operations.
9522      */
9523     reject : function(){
9524         var m = this.modified;
9525         for(var n in m){
9526             if(typeof m[n] != "function"){
9527                 this.data[n] = m[n];
9528             }
9529         }
9530         this.dirty = false;
9531         delete this.modified;
9532         this.editing = false;
9533         if(this.store){
9534             this.store.afterReject(this);
9535         }
9536     },
9537
9538     /**
9539      * Usually called by the {@link Roo.data.Store} which owns the Record.
9540      * Commits all changes made to the Record since either creation, or the last commit operation.
9541      * <p>
9542      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9543      * of commit operations.
9544      */
9545     commit : function(){
9546         this.dirty = false;
9547         delete this.modified;
9548         this.editing = false;
9549         if(this.store){
9550             this.store.afterCommit(this);
9551         }
9552     },
9553
9554     // private
9555     hasError : function(){
9556         return this.error != null;
9557     },
9558
9559     // private
9560     clearError : function(){
9561         this.error = null;
9562     },
9563
9564     /**
9565      * Creates a copy of this record.
9566      * @param {String} id (optional) A new record id if you don't want to use this record's id
9567      * @return {Record}
9568      */
9569     copy : function(newId) {
9570         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9571     }
9572 };/*
9573  * Based on:
9574  * Ext JS Library 1.1.1
9575  * Copyright(c) 2006-2007, Ext JS, LLC.
9576  *
9577  * Originally Released Under LGPL - original licence link has changed is not relivant.
9578  *
9579  * Fork - LGPL
9580  * <script type="text/javascript">
9581  */
9582
9583
9584
9585 /**
9586  * @class Roo.data.Store
9587  * @extends Roo.util.Observable
9588  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9589  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9590  * <p>
9591  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
9592  * has no knowledge of the format of the data returned by the Proxy.<br>
9593  * <p>
9594  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9595  * instances from the data object. These records are cached and made available through accessor functions.
9596  * @constructor
9597  * Creates a new Store.
9598  * @param {Object} config A config object containing the objects needed for the Store to access data,
9599  * and read the data into Records.
9600  */
9601 Roo.data.Store = function(config){
9602     this.data = new Roo.util.MixedCollection(false);
9603     this.data.getKey = function(o){
9604         return o.id;
9605     };
9606     this.baseParams = {};
9607     // private
9608     this.paramNames = {
9609         "start" : "start",
9610         "limit" : "limit",
9611         "sort" : "sort",
9612         "dir" : "dir",
9613         "multisort" : "_multisort"
9614     };
9615
9616     if(config && config.data){
9617         this.inlineData = config.data;
9618         delete config.data;
9619     }
9620
9621     Roo.apply(this, config);
9622     
9623     if(this.reader){ // reader passed
9624         this.reader = Roo.factory(this.reader, Roo.data);
9625         this.reader.xmodule = this.xmodule || false;
9626         if(!this.recordType){
9627             this.recordType = this.reader.recordType;
9628         }
9629         if(this.reader.onMetaChange){
9630             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9631         }
9632     }
9633
9634     if(this.recordType){
9635         this.fields = this.recordType.prototype.fields;
9636     }
9637     this.modified = [];
9638
9639     this.addEvents({
9640         /**
9641          * @event datachanged
9642          * Fires when the data cache has changed, and a widget which is using this Store
9643          * as a Record cache should refresh its view.
9644          * @param {Store} this
9645          */
9646         datachanged : true,
9647         /**
9648          * @event metachange
9649          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9650          * @param {Store} this
9651          * @param {Object} meta The JSON metadata
9652          */
9653         metachange : true,
9654         /**
9655          * @event add
9656          * Fires when Records have been added to the Store
9657          * @param {Store} this
9658          * @param {Roo.data.Record[]} records The array of Records added
9659          * @param {Number} index The index at which the record(s) were added
9660          */
9661         add : true,
9662         /**
9663          * @event remove
9664          * Fires when a Record has been removed from the Store
9665          * @param {Store} this
9666          * @param {Roo.data.Record} record The Record that was removed
9667          * @param {Number} index The index at which the record was removed
9668          */
9669         remove : true,
9670         /**
9671          * @event update
9672          * Fires when a Record has been updated
9673          * @param {Store} this
9674          * @param {Roo.data.Record} record The Record that was updated
9675          * @param {String} operation The update operation being performed.  Value may be one of:
9676          * <pre><code>
9677  Roo.data.Record.EDIT
9678  Roo.data.Record.REJECT
9679  Roo.data.Record.COMMIT
9680          * </code></pre>
9681          */
9682         update : true,
9683         /**
9684          * @event clear
9685          * Fires when the data cache has been cleared.
9686          * @param {Store} this
9687          */
9688         clear : true,
9689         /**
9690          * @event beforeload
9691          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9692          * the load action will be canceled.
9693          * @param {Store} this
9694          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9695          */
9696         beforeload : true,
9697         /**
9698          * @event beforeloadadd
9699          * Fires after a new set of Records has been loaded.
9700          * @param {Store} this
9701          * @param {Roo.data.Record[]} records The Records that were loaded
9702          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9703          */
9704         beforeloadadd : true,
9705         /**
9706          * @event load
9707          * Fires after a new set of Records has been loaded, before they are added to the store.
9708          * @param {Store} this
9709          * @param {Roo.data.Record[]} records The Records that were loaded
9710          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9711          * @params {Object} return from reader
9712          */
9713         load : true,
9714         /**
9715          * @event loadexception
9716          * Fires if an exception occurs in the Proxy during loading.
9717          * Called with the signature of the Proxy's "loadexception" event.
9718          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9719          * 
9720          * @param {Proxy} 
9721          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9722          * @param {Object} load options 
9723          * @param {Object} jsonData from your request (normally this contains the Exception)
9724          */
9725         loadexception : true
9726     });
9727     
9728     if(this.proxy){
9729         this.proxy = Roo.factory(this.proxy, Roo.data);
9730         this.proxy.xmodule = this.xmodule || false;
9731         this.relayEvents(this.proxy,  ["loadexception"]);
9732     }
9733     this.sortToggle = {};
9734     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9735
9736     Roo.data.Store.superclass.constructor.call(this);
9737
9738     if(this.inlineData){
9739         this.loadData(this.inlineData);
9740         delete this.inlineData;
9741     }
9742 };
9743
9744 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9745      /**
9746     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9747     * without a remote query - used by combo/forms at present.
9748     */
9749     
9750     /**
9751     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9752     */
9753     /**
9754     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9755     */
9756     /**
9757     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9758     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9759     */
9760     /**
9761     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9762     * on any HTTP request
9763     */
9764     /**
9765     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9766     */
9767     /**
9768     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9769     */
9770     multiSort: false,
9771     /**
9772     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9773     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9774     */
9775     remoteSort : false,
9776
9777     /**
9778     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9779      * loaded or when a record is removed. (defaults to false).
9780     */
9781     pruneModifiedRecords : false,
9782
9783     // private
9784     lastOptions : null,
9785
9786     /**
9787      * Add Records to the Store and fires the add event.
9788      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9789      */
9790     add : function(records){
9791         records = [].concat(records);
9792         for(var i = 0, len = records.length; i < len; i++){
9793             records[i].join(this);
9794         }
9795         var index = this.data.length;
9796         this.data.addAll(records);
9797         this.fireEvent("add", this, records, index);
9798     },
9799
9800     /**
9801      * Remove a Record from the Store and fires the remove event.
9802      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9803      */
9804     remove : function(record){
9805         var index = this.data.indexOf(record);
9806         this.data.removeAt(index);
9807         if(this.pruneModifiedRecords){
9808             this.modified.remove(record);
9809         }
9810         this.fireEvent("remove", this, record, index);
9811     },
9812
9813     /**
9814      * Remove all Records from the Store and fires the clear event.
9815      */
9816     removeAll : function(){
9817         this.data.clear();
9818         if(this.pruneModifiedRecords){
9819             this.modified = [];
9820         }
9821         this.fireEvent("clear", this);
9822     },
9823
9824     /**
9825      * Inserts Records to the Store at the given index and fires the add event.
9826      * @param {Number} index The start index at which to insert the passed Records.
9827      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9828      */
9829     insert : function(index, records){
9830         records = [].concat(records);
9831         for(var i = 0, len = records.length; i < len; i++){
9832             this.data.insert(index, records[i]);
9833             records[i].join(this);
9834         }
9835         this.fireEvent("add", this, records, index);
9836     },
9837
9838     /**
9839      * Get the index within the cache of the passed Record.
9840      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9841      * @return {Number} The index of the passed Record. Returns -1 if not found.
9842      */
9843     indexOf : function(record){
9844         return this.data.indexOf(record);
9845     },
9846
9847     /**
9848      * Get the index within the cache of the Record with the passed id.
9849      * @param {String} id The id of the Record to find.
9850      * @return {Number} The index of the Record. Returns -1 if not found.
9851      */
9852     indexOfId : function(id){
9853         return this.data.indexOfKey(id);
9854     },
9855
9856     /**
9857      * Get the Record with the specified id.
9858      * @param {String} id The id of the Record to find.
9859      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9860      */
9861     getById : function(id){
9862         return this.data.key(id);
9863     },
9864
9865     /**
9866      * Get the Record at the specified index.
9867      * @param {Number} index The index of the Record to find.
9868      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9869      */
9870     getAt : function(index){
9871         return this.data.itemAt(index);
9872     },
9873
9874     /**
9875      * Returns a range of Records between specified indices.
9876      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9877      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9878      * @return {Roo.data.Record[]} An array of Records
9879      */
9880     getRange : function(start, end){
9881         return this.data.getRange(start, end);
9882     },
9883
9884     // private
9885     storeOptions : function(o){
9886         o = Roo.apply({}, o);
9887         delete o.callback;
9888         delete o.scope;
9889         this.lastOptions = o;
9890     },
9891
9892     /**
9893      * Loads the Record cache from the configured Proxy using the configured Reader.
9894      * <p>
9895      * If using remote paging, then the first load call must specify the <em>start</em>
9896      * and <em>limit</em> properties in the options.params property to establish the initial
9897      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9898      * <p>
9899      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9900      * and this call will return before the new data has been loaded. Perform any post-processing
9901      * in a callback function, or in a "load" event handler.</strong>
9902      * <p>
9903      * @param {Object} options An object containing properties which control loading options:<ul>
9904      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9905      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9906      * passed the following arguments:<ul>
9907      * <li>r : Roo.data.Record[]</li>
9908      * <li>options: Options object from the load call</li>
9909      * <li>success: Boolean success indicator</li></ul></li>
9910      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9911      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9912      * </ul>
9913      */
9914     load : function(options){
9915         options = options || {};
9916         if(this.fireEvent("beforeload", this, options) !== false){
9917             this.storeOptions(options);
9918             var p = Roo.apply(options.params || {}, this.baseParams);
9919             // if meta was not loaded from remote source.. try requesting it.
9920             if (!this.reader.metaFromRemote) {
9921                 p._requestMeta = 1;
9922             }
9923             if(this.sortInfo && this.remoteSort){
9924                 var pn = this.paramNames;
9925                 p[pn["sort"]] = this.sortInfo.field;
9926                 p[pn["dir"]] = this.sortInfo.direction;
9927             }
9928             if (this.multiSort) {
9929                 var pn = this.paramNames;
9930                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9931             }
9932             
9933             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9934         }
9935     },
9936
9937     /**
9938      * Reloads the Record cache from the configured Proxy using the configured Reader and
9939      * the options from the last load operation performed.
9940      * @param {Object} options (optional) An object containing properties which may override the options
9941      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9942      * the most recently used options are reused).
9943      */
9944     reload : function(options){
9945         this.load(Roo.applyIf(options||{}, this.lastOptions));
9946     },
9947
9948     // private
9949     // Called as a callback by the Reader during a load operation.
9950     loadRecords : function(o, options, success){
9951         if(!o || success === false){
9952             if(success !== false){
9953                 this.fireEvent("load", this, [], options, o);
9954             }
9955             if(options.callback){
9956                 options.callback.call(options.scope || this, [], options, false);
9957             }
9958             return;
9959         }
9960         // if data returned failure - throw an exception.
9961         if (o.success === false) {
9962             // show a message if no listener is registered.
9963             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9964                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9965             }
9966             // loadmask wil be hooked into this..
9967             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9968             return;
9969         }
9970         var r = o.records, t = o.totalRecords || r.length;
9971         
9972         this.fireEvent("beforeloadadd", this, r, options, o);
9973         
9974         if(!options || options.add !== true){
9975             if(this.pruneModifiedRecords){
9976                 this.modified = [];
9977             }
9978             for(var i = 0, len = r.length; i < len; i++){
9979                 r[i].join(this);
9980             }
9981             if(this.snapshot){
9982                 this.data = this.snapshot;
9983                 delete this.snapshot;
9984             }
9985             this.data.clear();
9986             this.data.addAll(r);
9987             this.totalLength = t;
9988             this.applySort();
9989             this.fireEvent("datachanged", this);
9990         }else{
9991             this.totalLength = Math.max(t, this.data.length+r.length);
9992             this.add(r);
9993         }
9994         this.fireEvent("load", this, r, options, o);
9995         if(options.callback){
9996             options.callback.call(options.scope || this, r, options, true);
9997         }
9998     },
9999
10000
10001     /**
10002      * Loads data from a passed data block. A Reader which understands the format of the data
10003      * must have been configured in the constructor.
10004      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10005      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10006      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10007      */
10008     loadData : function(o, append){
10009         var r = this.reader.readRecords(o);
10010         this.loadRecords(r, {add: append}, true);
10011     },
10012
10013     /**
10014      * Gets the number of cached records.
10015      * <p>
10016      * <em>If using paging, this may not be the total size of the dataset. If the data object
10017      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10018      * the data set size</em>
10019      */
10020     getCount : function(){
10021         return this.data.length || 0;
10022     },
10023
10024     /**
10025      * Gets the total number of records in the dataset as returned by the server.
10026      * <p>
10027      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10028      * the dataset size</em>
10029      */
10030     getTotalCount : function(){
10031         return this.totalLength || 0;
10032     },
10033
10034     /**
10035      * Returns the sort state of the Store as an object with two properties:
10036      * <pre><code>
10037  field {String} The name of the field by which the Records are sorted
10038  direction {String} The sort order, "ASC" or "DESC"
10039      * </code></pre>
10040      */
10041     getSortState : function(){
10042         return this.sortInfo;
10043     },
10044
10045     // private
10046     applySort : function(){
10047         if(this.sortInfo && !this.remoteSort){
10048             var s = this.sortInfo, f = s.field;
10049             var st = this.fields.get(f).sortType;
10050             var fn = function(r1, r2){
10051                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10052                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10053             };
10054             this.data.sort(s.direction, fn);
10055             if(this.snapshot && this.snapshot != this.data){
10056                 this.snapshot.sort(s.direction, fn);
10057             }
10058         }
10059     },
10060
10061     /**
10062      * Sets the default sort column and order to be used by the next load operation.
10063      * @param {String} fieldName The name of the field to sort by.
10064      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10065      */
10066     setDefaultSort : function(field, dir){
10067         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10068     },
10069
10070     /**
10071      * Sort the Records.
10072      * If remote sorting is used, the sort is performed on the server, and the cache is
10073      * reloaded. If local sorting is used, the cache is sorted internally.
10074      * @param {String} fieldName The name of the field to sort by.
10075      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10076      */
10077     sort : function(fieldName, dir){
10078         var f = this.fields.get(fieldName);
10079         if(!dir){
10080             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10081             
10082             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10083                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10084             }else{
10085                 dir = f.sortDir;
10086             }
10087         }
10088         this.sortToggle[f.name] = dir;
10089         this.sortInfo = {field: f.name, direction: dir};
10090         if(!this.remoteSort){
10091             this.applySort();
10092             this.fireEvent("datachanged", this);
10093         }else{
10094             this.load(this.lastOptions);
10095         }
10096     },
10097
10098     /**
10099      * Calls the specified function for each of the Records in the cache.
10100      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10101      * Returning <em>false</em> aborts and exits the iteration.
10102      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10103      */
10104     each : function(fn, scope){
10105         this.data.each(fn, scope);
10106     },
10107
10108     /**
10109      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10110      * (e.g., during paging).
10111      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10112      */
10113     getModifiedRecords : function(){
10114         return this.modified;
10115     },
10116
10117     // private
10118     createFilterFn : function(property, value, anyMatch){
10119         if(!value.exec){ // not a regex
10120             value = String(value);
10121             if(value.length == 0){
10122                 return false;
10123             }
10124             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10125         }
10126         return function(r){
10127             return value.test(r.data[property]);
10128         };
10129     },
10130
10131     /**
10132      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10133      * @param {String} property A field on your records
10134      * @param {Number} start The record index to start at (defaults to 0)
10135      * @param {Number} end The last record index to include (defaults to length - 1)
10136      * @return {Number} The sum
10137      */
10138     sum : function(property, start, end){
10139         var rs = this.data.items, v = 0;
10140         start = start || 0;
10141         end = (end || end === 0) ? end : rs.length-1;
10142
10143         for(var i = start; i <= end; i++){
10144             v += (rs[i].data[property] || 0);
10145         }
10146         return v;
10147     },
10148
10149     /**
10150      * Filter the records by a specified property.
10151      * @param {String} field A field on your records
10152      * @param {String/RegExp} value Either a string that the field
10153      * should start with or a RegExp to test against the field
10154      * @param {Boolean} anyMatch True to match any part not just the beginning
10155      */
10156     filter : function(property, value, anyMatch){
10157         var fn = this.createFilterFn(property, value, anyMatch);
10158         return fn ? this.filterBy(fn) : this.clearFilter();
10159     },
10160
10161     /**
10162      * Filter by a function. The specified function will be called with each
10163      * record in this data source. If the function returns true the record is included,
10164      * otherwise it is filtered.
10165      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10166      * @param {Object} scope (optional) The scope of the function (defaults to this)
10167      */
10168     filterBy : function(fn, scope){
10169         this.snapshot = this.snapshot || this.data;
10170         this.data = this.queryBy(fn, scope||this);
10171         this.fireEvent("datachanged", this);
10172     },
10173
10174     /**
10175      * Query the records by a specified property.
10176      * @param {String} field A field on your records
10177      * @param {String/RegExp} value Either a string that the field
10178      * should start with or a RegExp to test against the field
10179      * @param {Boolean} anyMatch True to match any part not just the beginning
10180      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10181      */
10182     query : function(property, value, anyMatch){
10183         var fn = this.createFilterFn(property, value, anyMatch);
10184         return fn ? this.queryBy(fn) : this.data.clone();
10185     },
10186
10187     /**
10188      * Query by a function. The specified function will be called with each
10189      * record in this data source. If the function returns true the record is included
10190      * in the results.
10191      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10192      * @param {Object} scope (optional) The scope of the function (defaults to this)
10193       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10194      **/
10195     queryBy : function(fn, scope){
10196         var data = this.snapshot || this.data;
10197         return data.filterBy(fn, scope||this);
10198     },
10199
10200     /**
10201      * Collects unique values for a particular dataIndex from this store.
10202      * @param {String} dataIndex The property to collect
10203      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10204      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10205      * @return {Array} An array of the unique values
10206      **/
10207     collect : function(dataIndex, allowNull, bypassFilter){
10208         var d = (bypassFilter === true && this.snapshot) ?
10209                 this.snapshot.items : this.data.items;
10210         var v, sv, r = [], l = {};
10211         for(var i = 0, len = d.length; i < len; i++){
10212             v = d[i].data[dataIndex];
10213             sv = String(v);
10214             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10215                 l[sv] = true;
10216                 r[r.length] = v;
10217             }
10218         }
10219         return r;
10220     },
10221
10222     /**
10223      * Revert to a view of the Record cache with no filtering applied.
10224      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10225      */
10226     clearFilter : function(suppressEvent){
10227         if(this.snapshot && this.snapshot != this.data){
10228             this.data = this.snapshot;
10229             delete this.snapshot;
10230             if(suppressEvent !== true){
10231                 this.fireEvent("datachanged", this);
10232             }
10233         }
10234     },
10235
10236     // private
10237     afterEdit : function(record){
10238         if(this.modified.indexOf(record) == -1){
10239             this.modified.push(record);
10240         }
10241         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10242     },
10243     
10244     // private
10245     afterReject : function(record){
10246         this.modified.remove(record);
10247         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10248     },
10249
10250     // private
10251     afterCommit : function(record){
10252         this.modified.remove(record);
10253         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10254     },
10255
10256     /**
10257      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10258      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10259      */
10260     commitChanges : function(){
10261         var m = this.modified.slice(0);
10262         this.modified = [];
10263         for(var i = 0, len = m.length; i < len; i++){
10264             m[i].commit();
10265         }
10266     },
10267
10268     /**
10269      * Cancel outstanding changes on all changed records.
10270      */
10271     rejectChanges : function(){
10272         var m = this.modified.slice(0);
10273         this.modified = [];
10274         for(var i = 0, len = m.length; i < len; i++){
10275             m[i].reject();
10276         }
10277     },
10278
10279     onMetaChange : function(meta, rtype, o){
10280         this.recordType = rtype;
10281         this.fields = rtype.prototype.fields;
10282         delete this.snapshot;
10283         this.sortInfo = meta.sortInfo || this.sortInfo;
10284         this.modified = [];
10285         this.fireEvent('metachange', this, this.reader.meta);
10286     },
10287     
10288     moveIndex : function(data, type)
10289     {
10290         var index = this.indexOf(data);
10291         
10292         var newIndex = index + type;
10293         
10294         this.remove(data);
10295         
10296         this.insert(newIndex, data);
10297         
10298     }
10299 });/*
10300  * Based on:
10301  * Ext JS Library 1.1.1
10302  * Copyright(c) 2006-2007, Ext JS, LLC.
10303  *
10304  * Originally Released Under LGPL - original licence link has changed is not relivant.
10305  *
10306  * Fork - LGPL
10307  * <script type="text/javascript">
10308  */
10309
10310 /**
10311  * @class Roo.data.SimpleStore
10312  * @extends Roo.data.Store
10313  * Small helper class to make creating Stores from Array data easier.
10314  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10315  * @cfg {Array} fields An array of field definition objects, or field name strings.
10316  * @cfg {Array} data The multi-dimensional array of data
10317  * @constructor
10318  * @param {Object} config
10319  */
10320 Roo.data.SimpleStore = function(config){
10321     Roo.data.SimpleStore.superclass.constructor.call(this, {
10322         isLocal : true,
10323         reader: new Roo.data.ArrayReader({
10324                 id: config.id
10325             },
10326             Roo.data.Record.create(config.fields)
10327         ),
10328         proxy : new Roo.data.MemoryProxy(config.data)
10329     });
10330     this.load();
10331 };
10332 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10333  * Based on:
10334  * Ext JS Library 1.1.1
10335  * Copyright(c) 2006-2007, Ext JS, LLC.
10336  *
10337  * Originally Released Under LGPL - original licence link has changed is not relivant.
10338  *
10339  * Fork - LGPL
10340  * <script type="text/javascript">
10341  */
10342
10343 /**
10344 /**
10345  * @extends Roo.data.Store
10346  * @class Roo.data.JsonStore
10347  * Small helper class to make creating Stores for JSON data easier. <br/>
10348 <pre><code>
10349 var store = new Roo.data.JsonStore({
10350     url: 'get-images.php',
10351     root: 'images',
10352     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10353 });
10354 </code></pre>
10355  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10356  * JsonReader and HttpProxy (unless inline data is provided).</b>
10357  * @cfg {Array} fields An array of field definition objects, or field name strings.
10358  * @constructor
10359  * @param {Object} config
10360  */
10361 Roo.data.JsonStore = function(c){
10362     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10363         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10364         reader: new Roo.data.JsonReader(c, c.fields)
10365     }));
10366 };
10367 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10368  * Based on:
10369  * Ext JS Library 1.1.1
10370  * Copyright(c) 2006-2007, Ext JS, LLC.
10371  *
10372  * Originally Released Under LGPL - original licence link has changed is not relivant.
10373  *
10374  * Fork - LGPL
10375  * <script type="text/javascript">
10376  */
10377
10378  
10379 Roo.data.Field = function(config){
10380     if(typeof config == "string"){
10381         config = {name: config};
10382     }
10383     Roo.apply(this, config);
10384     
10385     if(!this.type){
10386         this.type = "auto";
10387     }
10388     
10389     var st = Roo.data.SortTypes;
10390     // named sortTypes are supported, here we look them up
10391     if(typeof this.sortType == "string"){
10392         this.sortType = st[this.sortType];
10393     }
10394     
10395     // set default sortType for strings and dates
10396     if(!this.sortType){
10397         switch(this.type){
10398             case "string":
10399                 this.sortType = st.asUCString;
10400                 break;
10401             case "date":
10402                 this.sortType = st.asDate;
10403                 break;
10404             default:
10405                 this.sortType = st.none;
10406         }
10407     }
10408
10409     // define once
10410     var stripRe = /[\$,%]/g;
10411
10412     // prebuilt conversion function for this field, instead of
10413     // switching every time we're reading a value
10414     if(!this.convert){
10415         var cv, dateFormat = this.dateFormat;
10416         switch(this.type){
10417             case "":
10418             case "auto":
10419             case undefined:
10420                 cv = function(v){ return v; };
10421                 break;
10422             case "string":
10423                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10424                 break;
10425             case "int":
10426                 cv = function(v){
10427                     return v !== undefined && v !== null && v !== '' ?
10428                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10429                     };
10430                 break;
10431             case "float":
10432                 cv = function(v){
10433                     return v !== undefined && v !== null && v !== '' ?
10434                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10435                     };
10436                 break;
10437             case "bool":
10438             case "boolean":
10439                 cv = function(v){ return v === true || v === "true" || v == 1; };
10440                 break;
10441             case "date":
10442                 cv = function(v){
10443                     if(!v){
10444                         return '';
10445                     }
10446                     if(v instanceof Date){
10447                         return v;
10448                     }
10449                     if(dateFormat){
10450                         if(dateFormat == "timestamp"){
10451                             return new Date(v*1000);
10452                         }
10453                         return Date.parseDate(v, dateFormat);
10454                     }
10455                     var parsed = Date.parse(v);
10456                     return parsed ? new Date(parsed) : null;
10457                 };
10458              break;
10459             
10460         }
10461         this.convert = cv;
10462     }
10463 };
10464
10465 Roo.data.Field.prototype = {
10466     dateFormat: null,
10467     defaultValue: "",
10468     mapping: null,
10469     sortType : null,
10470     sortDir : "ASC"
10471 };/*
10472  * Based on:
10473  * Ext JS Library 1.1.1
10474  * Copyright(c) 2006-2007, Ext JS, LLC.
10475  *
10476  * Originally Released Under LGPL - original licence link has changed is not relivant.
10477  *
10478  * Fork - LGPL
10479  * <script type="text/javascript">
10480  */
10481  
10482 // Base class for reading structured data from a data source.  This class is intended to be
10483 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10484
10485 /**
10486  * @class Roo.data.DataReader
10487  * Base class for reading structured data from a data source.  This class is intended to be
10488  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10489  */
10490
10491 Roo.data.DataReader = function(meta, recordType){
10492     
10493     this.meta = meta;
10494     
10495     this.recordType = recordType instanceof Array ? 
10496         Roo.data.Record.create(recordType) : recordType;
10497 };
10498
10499 Roo.data.DataReader.prototype = {
10500      /**
10501      * Create an empty record
10502      * @param {Object} data (optional) - overlay some values
10503      * @return {Roo.data.Record} record created.
10504      */
10505     newRow :  function(d) {
10506         var da =  {};
10507         this.recordType.prototype.fields.each(function(c) {
10508             switch( c.type) {
10509                 case 'int' : da[c.name] = 0; break;
10510                 case 'date' : da[c.name] = new Date(); break;
10511                 case 'float' : da[c.name] = 0.0; break;
10512                 case 'boolean' : da[c.name] = false; break;
10513                 default : da[c.name] = ""; break;
10514             }
10515             
10516         });
10517         return new this.recordType(Roo.apply(da, d));
10518     }
10519     
10520 };/*
10521  * Based on:
10522  * Ext JS Library 1.1.1
10523  * Copyright(c) 2006-2007, Ext JS, LLC.
10524  *
10525  * Originally Released Under LGPL - original licence link has changed is not relivant.
10526  *
10527  * Fork - LGPL
10528  * <script type="text/javascript">
10529  */
10530
10531 /**
10532  * @class Roo.data.DataProxy
10533  * @extends Roo.data.Observable
10534  * This class is an abstract base class for implementations which provide retrieval of
10535  * unformatted data objects.<br>
10536  * <p>
10537  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10538  * (of the appropriate type which knows how to parse the data object) to provide a block of
10539  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10540  * <p>
10541  * Custom implementations must implement the load method as described in
10542  * {@link Roo.data.HttpProxy#load}.
10543  */
10544 Roo.data.DataProxy = function(){
10545     this.addEvents({
10546         /**
10547          * @event beforeload
10548          * Fires before a network request is made to retrieve a data object.
10549          * @param {Object} This DataProxy object.
10550          * @param {Object} params The params parameter to the load function.
10551          */
10552         beforeload : true,
10553         /**
10554          * @event load
10555          * Fires before the load method's callback is called.
10556          * @param {Object} This DataProxy object.
10557          * @param {Object} o The data object.
10558          * @param {Object} arg The callback argument object passed to the load function.
10559          */
10560         load : true,
10561         /**
10562          * @event loadexception
10563          * Fires if an Exception occurs during data retrieval.
10564          * @param {Object} This DataProxy object.
10565          * @param {Object} o The data object.
10566          * @param {Object} arg The callback argument object passed to the load function.
10567          * @param {Object} e The Exception.
10568          */
10569         loadexception : true
10570     });
10571     Roo.data.DataProxy.superclass.constructor.call(this);
10572 };
10573
10574 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10575
10576     /**
10577      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10578      */
10579 /*
10580  * Based on:
10581  * Ext JS Library 1.1.1
10582  * Copyright(c) 2006-2007, Ext JS, LLC.
10583  *
10584  * Originally Released Under LGPL - original licence link has changed is not relivant.
10585  *
10586  * Fork - LGPL
10587  * <script type="text/javascript">
10588  */
10589 /**
10590  * @class Roo.data.MemoryProxy
10591  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10592  * to the Reader when its load method is called.
10593  * @constructor
10594  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10595  */
10596 Roo.data.MemoryProxy = function(data){
10597     if (data.data) {
10598         data = data.data;
10599     }
10600     Roo.data.MemoryProxy.superclass.constructor.call(this);
10601     this.data = data;
10602 };
10603
10604 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10605     /**
10606      * Load data from the requested source (in this case an in-memory
10607      * data object passed to the constructor), read the data object into
10608      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10609      * process that block using the passed callback.
10610      * @param {Object} params This parameter is not used by the MemoryProxy class.
10611      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10612      * object into a block of Roo.data.Records.
10613      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10614      * The function must be passed <ul>
10615      * <li>The Record block object</li>
10616      * <li>The "arg" argument from the load function</li>
10617      * <li>A boolean success indicator</li>
10618      * </ul>
10619      * @param {Object} scope The scope in which to call the callback
10620      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10621      */
10622     load : function(params, reader, callback, scope, arg){
10623         params = params || {};
10624         var result;
10625         try {
10626             result = reader.readRecords(this.data);
10627         }catch(e){
10628             this.fireEvent("loadexception", this, arg, null, e);
10629             callback.call(scope, null, arg, false);
10630             return;
10631         }
10632         callback.call(scope, result, arg, true);
10633     },
10634     
10635     // private
10636     update : function(params, records){
10637         
10638     }
10639 });/*
10640  * Based on:
10641  * Ext JS Library 1.1.1
10642  * Copyright(c) 2006-2007, Ext JS, LLC.
10643  *
10644  * Originally Released Under LGPL - original licence link has changed is not relivant.
10645  *
10646  * Fork - LGPL
10647  * <script type="text/javascript">
10648  */
10649 /**
10650  * @class Roo.data.HttpProxy
10651  * @extends Roo.data.DataProxy
10652  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10653  * configured to reference a certain URL.<br><br>
10654  * <p>
10655  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10656  * from which the running page was served.<br><br>
10657  * <p>
10658  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10659  * <p>
10660  * Be aware that to enable the browser to parse an XML document, the server must set
10661  * the Content-Type header in the HTTP response to "text/xml".
10662  * @constructor
10663  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10664  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10665  * will be used to make the request.
10666  */
10667 Roo.data.HttpProxy = function(conn){
10668     Roo.data.HttpProxy.superclass.constructor.call(this);
10669     // is conn a conn config or a real conn?
10670     this.conn = conn;
10671     this.useAjax = !conn || !conn.events;
10672   
10673 };
10674
10675 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10676     // thse are take from connection...
10677     
10678     /**
10679      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10680      */
10681     /**
10682      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10683      * extra parameters to each request made by this object. (defaults to undefined)
10684      */
10685     /**
10686      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10687      *  to each request made by this object. (defaults to undefined)
10688      */
10689     /**
10690      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
10691      */
10692     /**
10693      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10694      */
10695      /**
10696      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10697      * @type Boolean
10698      */
10699   
10700
10701     /**
10702      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10703      * @type Boolean
10704      */
10705     /**
10706      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10707      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10708      * a finer-grained basis than the DataProxy events.
10709      */
10710     getConnection : function(){
10711         return this.useAjax ? Roo.Ajax : this.conn;
10712     },
10713
10714     /**
10715      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10716      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10717      * process that block using the passed callback.
10718      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10719      * for the request to the remote server.
10720      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10721      * object into a block of Roo.data.Records.
10722      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10723      * The function must be passed <ul>
10724      * <li>The Record block object</li>
10725      * <li>The "arg" argument from the load function</li>
10726      * <li>A boolean success indicator</li>
10727      * </ul>
10728      * @param {Object} scope The scope in which to call the callback
10729      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10730      */
10731     load : function(params, reader, callback, scope, arg){
10732         if(this.fireEvent("beforeload", this, params) !== false){
10733             var  o = {
10734                 params : params || {},
10735                 request: {
10736                     callback : callback,
10737                     scope : scope,
10738                     arg : arg
10739                 },
10740                 reader: reader,
10741                 callback : this.loadResponse,
10742                 scope: this
10743             };
10744             if(this.useAjax){
10745                 Roo.applyIf(o, this.conn);
10746                 if(this.activeRequest){
10747                     Roo.Ajax.abort(this.activeRequest);
10748                 }
10749                 this.activeRequest = Roo.Ajax.request(o);
10750             }else{
10751                 this.conn.request(o);
10752             }
10753         }else{
10754             callback.call(scope||this, null, arg, false);
10755         }
10756     },
10757
10758     // private
10759     loadResponse : function(o, success, response){
10760         delete this.activeRequest;
10761         if(!success){
10762             this.fireEvent("loadexception", this, o, response);
10763             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10764             return;
10765         }
10766         var result;
10767         try {
10768             result = o.reader.read(response);
10769         }catch(e){
10770             this.fireEvent("loadexception", this, o, response, e);
10771             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10772             return;
10773         }
10774         
10775         this.fireEvent("load", this, o, o.request.arg);
10776         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10777     },
10778
10779     // private
10780     update : function(dataSet){
10781
10782     },
10783
10784     // private
10785     updateResponse : function(dataSet){
10786
10787     }
10788 });/*
10789  * Based on:
10790  * Ext JS Library 1.1.1
10791  * Copyright(c) 2006-2007, Ext JS, LLC.
10792  *
10793  * Originally Released Under LGPL - original licence link has changed is not relivant.
10794  *
10795  * Fork - LGPL
10796  * <script type="text/javascript">
10797  */
10798
10799 /**
10800  * @class Roo.data.ScriptTagProxy
10801  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10802  * other than the originating domain of the running page.<br><br>
10803  * <p>
10804  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
10805  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10806  * <p>
10807  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10808  * source code that is used as the source inside a &lt;script> tag.<br><br>
10809  * <p>
10810  * In order for the browser to process the returned data, the server must wrap the data object
10811  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10812  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10813  * depending on whether the callback name was passed:
10814  * <p>
10815  * <pre><code>
10816 boolean scriptTag = false;
10817 String cb = request.getParameter("callback");
10818 if (cb != null) {
10819     scriptTag = true;
10820     response.setContentType("text/javascript");
10821 } else {
10822     response.setContentType("application/x-json");
10823 }
10824 Writer out = response.getWriter();
10825 if (scriptTag) {
10826     out.write(cb + "(");
10827 }
10828 out.print(dataBlock.toJsonString());
10829 if (scriptTag) {
10830     out.write(");");
10831 }
10832 </pre></code>
10833  *
10834  * @constructor
10835  * @param {Object} config A configuration object.
10836  */
10837 Roo.data.ScriptTagProxy = function(config){
10838     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10839     Roo.apply(this, config);
10840     this.head = document.getElementsByTagName("head")[0];
10841 };
10842
10843 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10844
10845 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10846     /**
10847      * @cfg {String} url The URL from which to request the data object.
10848      */
10849     /**
10850      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10851      */
10852     timeout : 30000,
10853     /**
10854      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10855      * the server the name of the callback function set up by the load call to process the returned data object.
10856      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10857      * javascript output which calls this named function passing the data object as its only parameter.
10858      */
10859     callbackParam : "callback",
10860     /**
10861      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10862      * name to the request.
10863      */
10864     nocache : true,
10865
10866     /**
10867      * Load data from the configured URL, read the data object into
10868      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10869      * process that block using the passed callback.
10870      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10871      * for the request to the remote server.
10872      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10873      * object into a block of Roo.data.Records.
10874      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10875      * The function must be passed <ul>
10876      * <li>The Record block object</li>
10877      * <li>The "arg" argument from the load function</li>
10878      * <li>A boolean success indicator</li>
10879      * </ul>
10880      * @param {Object} scope The scope in which to call the callback
10881      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10882      */
10883     load : function(params, reader, callback, scope, arg){
10884         if(this.fireEvent("beforeload", this, params) !== false){
10885
10886             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10887
10888             var url = this.url;
10889             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10890             if(this.nocache){
10891                 url += "&_dc=" + (new Date().getTime());
10892             }
10893             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10894             var trans = {
10895                 id : transId,
10896                 cb : "stcCallback"+transId,
10897                 scriptId : "stcScript"+transId,
10898                 params : params,
10899                 arg : arg,
10900                 url : url,
10901                 callback : callback,
10902                 scope : scope,
10903                 reader : reader
10904             };
10905             var conn = this;
10906
10907             window[trans.cb] = function(o){
10908                 conn.handleResponse(o, trans);
10909             };
10910
10911             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10912
10913             if(this.autoAbort !== false){
10914                 this.abort();
10915             }
10916
10917             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10918
10919             var script = document.createElement("script");
10920             script.setAttribute("src", url);
10921             script.setAttribute("type", "text/javascript");
10922             script.setAttribute("id", trans.scriptId);
10923             this.head.appendChild(script);
10924
10925             this.trans = trans;
10926         }else{
10927             callback.call(scope||this, null, arg, false);
10928         }
10929     },
10930
10931     // private
10932     isLoading : function(){
10933         return this.trans ? true : false;
10934     },
10935
10936     /**
10937      * Abort the current server request.
10938      */
10939     abort : function(){
10940         if(this.isLoading()){
10941             this.destroyTrans(this.trans);
10942         }
10943     },
10944
10945     // private
10946     destroyTrans : function(trans, isLoaded){
10947         this.head.removeChild(document.getElementById(trans.scriptId));
10948         clearTimeout(trans.timeoutId);
10949         if(isLoaded){
10950             window[trans.cb] = undefined;
10951             try{
10952                 delete window[trans.cb];
10953             }catch(e){}
10954         }else{
10955             // if hasn't been loaded, wait for load to remove it to prevent script error
10956             window[trans.cb] = function(){
10957                 window[trans.cb] = undefined;
10958                 try{
10959                     delete window[trans.cb];
10960                 }catch(e){}
10961             };
10962         }
10963     },
10964
10965     // private
10966     handleResponse : function(o, trans){
10967         this.trans = false;
10968         this.destroyTrans(trans, true);
10969         var result;
10970         try {
10971             result = trans.reader.readRecords(o);
10972         }catch(e){
10973             this.fireEvent("loadexception", this, o, trans.arg, e);
10974             trans.callback.call(trans.scope||window, null, trans.arg, false);
10975             return;
10976         }
10977         this.fireEvent("load", this, o, trans.arg);
10978         trans.callback.call(trans.scope||window, result, trans.arg, true);
10979     },
10980
10981     // private
10982     handleFailure : function(trans){
10983         this.trans = false;
10984         this.destroyTrans(trans, false);
10985         this.fireEvent("loadexception", this, null, trans.arg);
10986         trans.callback.call(trans.scope||window, null, trans.arg, false);
10987     }
10988 });/*
10989  * Based on:
10990  * Ext JS Library 1.1.1
10991  * Copyright(c) 2006-2007, Ext JS, LLC.
10992  *
10993  * Originally Released Under LGPL - original licence link has changed is not relivant.
10994  *
10995  * Fork - LGPL
10996  * <script type="text/javascript">
10997  */
10998
10999 /**
11000  * @class Roo.data.JsonReader
11001  * @extends Roo.data.DataReader
11002  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11003  * based on mappings in a provided Roo.data.Record constructor.
11004  * 
11005  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11006  * in the reply previously. 
11007  * 
11008  * <p>
11009  * Example code:
11010  * <pre><code>
11011 var RecordDef = Roo.data.Record.create([
11012     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11013     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11014 ]);
11015 var myReader = new Roo.data.JsonReader({
11016     totalProperty: "results",    // The property which contains the total dataset size (optional)
11017     root: "rows",                // The property which contains an Array of row objects
11018     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11019 }, RecordDef);
11020 </code></pre>
11021  * <p>
11022  * This would consume a JSON file like this:
11023  * <pre><code>
11024 { 'results': 2, 'rows': [
11025     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11026     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11027 }
11028 </code></pre>
11029  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11030  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11031  * paged from the remote server.
11032  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11033  * @cfg {String} root name of the property which contains the Array of row objects.
11034  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11035  * @cfg {Array} fields Array of field definition objects
11036  * @constructor
11037  * Create a new JsonReader
11038  * @param {Object} meta Metadata configuration options
11039  * @param {Object} recordType Either an Array of field definition objects,
11040  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11041  */
11042 Roo.data.JsonReader = function(meta, recordType){
11043     
11044     meta = meta || {};
11045     // set some defaults:
11046     Roo.applyIf(meta, {
11047         totalProperty: 'total',
11048         successProperty : 'success',
11049         root : 'data',
11050         id : 'id'
11051     });
11052     
11053     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11054 };
11055 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11056     
11057     /**
11058      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11059      * Used by Store query builder to append _requestMeta to params.
11060      * 
11061      */
11062     metaFromRemote : false,
11063     /**
11064      * This method is only used by a DataProxy which has retrieved data from a remote server.
11065      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11066      * @return {Object} data A data block which is used by an Roo.data.Store object as
11067      * a cache of Roo.data.Records.
11068      */
11069     read : function(response){
11070         var json = response.responseText;
11071        
11072         var o = /* eval:var:o */ eval("("+json+")");
11073         if(!o) {
11074             throw {message: "JsonReader.read: Json object not found"};
11075         }
11076         
11077         if(o.metaData){
11078             
11079             delete this.ef;
11080             this.metaFromRemote = true;
11081             this.meta = o.metaData;
11082             this.recordType = Roo.data.Record.create(o.metaData.fields);
11083             this.onMetaChange(this.meta, this.recordType, o);
11084         }
11085         return this.readRecords(o);
11086     },
11087
11088     // private function a store will implement
11089     onMetaChange : function(meta, recordType, o){
11090
11091     },
11092
11093     /**
11094          * @ignore
11095          */
11096     simpleAccess: function(obj, subsc) {
11097         return obj[subsc];
11098     },
11099
11100         /**
11101          * @ignore
11102          */
11103     getJsonAccessor: function(){
11104         var re = /[\[\.]/;
11105         return function(expr) {
11106             try {
11107                 return(re.test(expr))
11108                     ? new Function("obj", "return obj." + expr)
11109                     : function(obj){
11110                         return obj[expr];
11111                     };
11112             } catch(e){}
11113             return Roo.emptyFn;
11114         };
11115     }(),
11116
11117     /**
11118      * Create a data block containing Roo.data.Records from an XML document.
11119      * @param {Object} o An object which contains an Array of row objects in the property specified
11120      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11121      * which contains the total size of the dataset.
11122      * @return {Object} data A data block which is used by an Roo.data.Store object as
11123      * a cache of Roo.data.Records.
11124      */
11125     readRecords : function(o){
11126         /**
11127          * After any data loads, the raw JSON data is available for further custom processing.
11128          * @type Object
11129          */
11130         this.o = o;
11131         var s = this.meta, Record = this.recordType,
11132             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11133
11134 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11135         if (!this.ef) {
11136             if(s.totalProperty) {
11137                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11138                 }
11139                 if(s.successProperty) {
11140                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11141                 }
11142                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11143                 if (s.id) {
11144                         var g = this.getJsonAccessor(s.id);
11145                         this.getId = function(rec) {
11146                                 var r = g(rec);  
11147                                 return (r === undefined || r === "") ? null : r;
11148                         };
11149                 } else {
11150                         this.getId = function(){return null;};
11151                 }
11152             this.ef = [];
11153             for(var jj = 0; jj < fl; jj++){
11154                 f = fi[jj];
11155                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11156                 this.ef[jj] = this.getJsonAccessor(map);
11157             }
11158         }
11159
11160         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11161         if(s.totalProperty){
11162             var vt = parseInt(this.getTotal(o), 10);
11163             if(!isNaN(vt)){
11164                 totalRecords = vt;
11165             }
11166         }
11167         if(s.successProperty){
11168             var vs = this.getSuccess(o);
11169             if(vs === false || vs === 'false'){
11170                 success = false;
11171             }
11172         }
11173         var records = [];
11174         for(var i = 0; i < c; i++){
11175                 var n = root[i];
11176             var values = {};
11177             var id = this.getId(n);
11178             for(var j = 0; j < fl; j++){
11179                 f = fi[j];
11180             var v = this.ef[j](n);
11181             if (!f.convert) {
11182                 Roo.log('missing convert for ' + f.name);
11183                 Roo.log(f);
11184                 continue;
11185             }
11186             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11187             }
11188             var record = new Record(values, id);
11189             record.json = n;
11190             records[i] = record;
11191         }
11192         return {
11193             raw : o,
11194             success : success,
11195             records : records,
11196             totalRecords : totalRecords
11197         };
11198     }
11199 });/*
11200  * Based on:
11201  * Ext JS Library 1.1.1
11202  * Copyright(c) 2006-2007, Ext JS, LLC.
11203  *
11204  * Originally Released Under LGPL - original licence link has changed is not relivant.
11205  *
11206  * Fork - LGPL
11207  * <script type="text/javascript">
11208  */
11209
11210 /**
11211  * @class Roo.data.ArrayReader
11212  * @extends Roo.data.DataReader
11213  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11214  * Each element of that Array represents a row of data fields. The
11215  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11216  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11217  * <p>
11218  * Example code:.
11219  * <pre><code>
11220 var RecordDef = Roo.data.Record.create([
11221     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11222     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11223 ]);
11224 var myReader = new Roo.data.ArrayReader({
11225     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11226 }, RecordDef);
11227 </code></pre>
11228  * <p>
11229  * This would consume an Array like this:
11230  * <pre><code>
11231 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11232   </code></pre>
11233  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11234  * @constructor
11235  * Create a new JsonReader
11236  * @param {Object} meta Metadata configuration options.
11237  * @param {Object} recordType Either an Array of field definition objects
11238  * as specified to {@link Roo.data.Record#create},
11239  * or an {@link Roo.data.Record} object
11240  * created using {@link Roo.data.Record#create}.
11241  */
11242 Roo.data.ArrayReader = function(meta, recordType){
11243     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11244 };
11245
11246 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11247     /**
11248      * Create a data block containing Roo.data.Records from an XML document.
11249      * @param {Object} o An Array of row objects which represents the dataset.
11250      * @return {Object} data A data block which is used by an Roo.data.Store object as
11251      * a cache of Roo.data.Records.
11252      */
11253     readRecords : function(o){
11254         var sid = this.meta ? this.meta.id : null;
11255         var recordType = this.recordType, fields = recordType.prototype.fields;
11256         var records = [];
11257         var root = o;
11258             for(var i = 0; i < root.length; i++){
11259                     var n = root[i];
11260                 var values = {};
11261                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11262                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11263                 var f = fields.items[j];
11264                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11265                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11266                 v = f.convert(v);
11267                 values[f.name] = v;
11268             }
11269                 var record = new recordType(values, id);
11270                 record.json = n;
11271                 records[records.length] = record;
11272             }
11273             return {
11274                 records : records,
11275                 totalRecords : records.length
11276             };
11277     }
11278 });/*
11279  * - LGPL
11280  * * 
11281  */
11282
11283 /**
11284  * @class Roo.bootstrap.ComboBox
11285  * @extends Roo.bootstrap.TriggerField
11286  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11287  * @cfg {Boolean} append (true|false) default false
11288  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11289  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11290  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11291  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11292  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11293  * @cfg {Boolean} animate default true
11294  * @cfg {Boolean} emptyResultText only for touch device
11295  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11296  * @constructor
11297  * Create a new ComboBox.
11298  * @param {Object} config Configuration options
11299  */
11300 Roo.bootstrap.ComboBox = function(config){
11301     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11302     this.addEvents({
11303         /**
11304          * @event expand
11305          * Fires when the dropdown list is expanded
11306              * @param {Roo.bootstrap.ComboBox} combo This combo box
11307              */
11308         'expand' : true,
11309         /**
11310          * @event collapse
11311          * Fires when the dropdown list is collapsed
11312              * @param {Roo.bootstrap.ComboBox} combo This combo box
11313              */
11314         'collapse' : true,
11315         /**
11316          * @event beforeselect
11317          * Fires before a list item is selected. Return false to cancel the selection.
11318              * @param {Roo.bootstrap.ComboBox} combo This combo box
11319              * @param {Roo.data.Record} record The data record returned from the underlying store
11320              * @param {Number} index The index of the selected item in the dropdown list
11321              */
11322         'beforeselect' : true,
11323         /**
11324          * @event select
11325          * Fires when a list item is selected
11326              * @param {Roo.bootstrap.ComboBox} combo This combo box
11327              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11328              * @param {Number} index The index of the selected item in the dropdown list
11329              */
11330         'select' : true,
11331         /**
11332          * @event beforequery
11333          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11334          * The event object passed has these properties:
11335              * @param {Roo.bootstrap.ComboBox} combo This combo box
11336              * @param {String} query The query
11337              * @param {Boolean} forceAll true to force "all" query
11338              * @param {Boolean} cancel true to cancel the query
11339              * @param {Object} e The query event object
11340              */
11341         'beforequery': true,
11342          /**
11343          * @event add
11344          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11345              * @param {Roo.bootstrap.ComboBox} combo This combo box
11346              */
11347         'add' : true,
11348         /**
11349          * @event edit
11350          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11351              * @param {Roo.bootstrap.ComboBox} combo This combo box
11352              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11353              */
11354         'edit' : true,
11355         /**
11356          * @event remove
11357          * Fires when the remove value from the combobox array
11358              * @param {Roo.bootstrap.ComboBox} combo This combo box
11359              */
11360         'remove' : true,
11361         /**
11362          * @event specialfilter
11363          * Fires when specialfilter
11364             * @param {Roo.bootstrap.ComboBox} combo This combo box
11365             */
11366         'specialfilter' : true
11367         
11368     });
11369     
11370     this.item = [];
11371     this.tickItems = [];
11372     
11373     this.selectedIndex = -1;
11374     if(this.mode == 'local'){
11375         if(config.queryDelay === undefined){
11376             this.queryDelay = 10;
11377         }
11378         if(config.minChars === undefined){
11379             this.minChars = 0;
11380         }
11381     }
11382 };
11383
11384 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11385      
11386     /**
11387      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11388      * rendering into an Roo.Editor, defaults to false)
11389      */
11390     /**
11391      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11392      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11393      */
11394     /**
11395      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11396      */
11397     /**
11398      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11399      * the dropdown list (defaults to undefined, with no header element)
11400      */
11401
11402      /**
11403      * @cfg {String/Roo.Template} tpl The template to use to render the output
11404      */
11405      
11406      /**
11407      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11408      */
11409     listWidth: undefined,
11410     /**
11411      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11412      * mode = 'remote' or 'text' if mode = 'local')
11413      */
11414     displayField: undefined,
11415     
11416     /**
11417      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11418      * mode = 'remote' or 'value' if mode = 'local'). 
11419      * Note: use of a valueField requires the user make a selection
11420      * in order for a value to be mapped.
11421      */
11422     valueField: undefined,
11423     
11424     
11425     /**
11426      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11427      * field's data value (defaults to the underlying DOM element's name)
11428      */
11429     hiddenName: undefined,
11430     /**
11431      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11432      */
11433     listClass: '',
11434     /**
11435      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11436      */
11437     selectedClass: 'active',
11438     
11439     /**
11440      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11441      */
11442     shadow:'sides',
11443     /**
11444      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11445      * anchor positions (defaults to 'tl-bl')
11446      */
11447     listAlign: 'tl-bl?',
11448     /**
11449      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11450      */
11451     maxHeight: 300,
11452     /**
11453      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11454      * query specified by the allQuery config option (defaults to 'query')
11455      */
11456     triggerAction: 'query',
11457     /**
11458      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11459      * (defaults to 4, does not apply if editable = false)
11460      */
11461     minChars : 4,
11462     /**
11463      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11464      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11465      */
11466     typeAhead: false,
11467     /**
11468      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11469      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11470      */
11471     queryDelay: 500,
11472     /**
11473      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11474      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11475      */
11476     pageSize: 0,
11477     /**
11478      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11479      * when editable = true (defaults to false)
11480      */
11481     selectOnFocus:false,
11482     /**
11483      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11484      */
11485     queryParam: 'query',
11486     /**
11487      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11488      * when mode = 'remote' (defaults to 'Loading...')
11489      */
11490     loadingText: 'Loading...',
11491     /**
11492      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11493      */
11494     resizable: false,
11495     /**
11496      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11497      */
11498     handleHeight : 8,
11499     /**
11500      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11501      * traditional select (defaults to true)
11502      */
11503     editable: true,
11504     /**
11505      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11506      */
11507     allQuery: '',
11508     /**
11509      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11510      */
11511     mode: 'remote',
11512     /**
11513      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11514      * listWidth has a higher value)
11515      */
11516     minListWidth : 70,
11517     /**
11518      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11519      * allow the user to set arbitrary text into the field (defaults to false)
11520      */
11521     forceSelection:false,
11522     /**
11523      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11524      * if typeAhead = true (defaults to 250)
11525      */
11526     typeAheadDelay : 250,
11527     /**
11528      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11529      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11530      */
11531     valueNotFoundText : undefined,
11532     /**
11533      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11534      */
11535     blockFocus : false,
11536     
11537     /**
11538      * @cfg {Boolean} disableClear Disable showing of clear button.
11539      */
11540     disableClear : false,
11541     /**
11542      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11543      */
11544     alwaysQuery : false,
11545     
11546     /**
11547      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11548      */
11549     multiple : false,
11550     
11551     /**
11552      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11553      */
11554     invalidClass : "has-warning",
11555     
11556     /**
11557      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11558      */
11559     validClass : "has-success",
11560     
11561     /**
11562      * @cfg {Boolean} specialFilter (true|false) special filter default false
11563      */
11564     specialFilter : false,
11565     
11566     /**
11567      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11568      */
11569     mobileTouchView : true,
11570     
11571     //private
11572     addicon : false,
11573     editicon: false,
11574     
11575     page: 0,
11576     hasQuery: false,
11577     append: false,
11578     loadNext: false,
11579     autoFocus : true,
11580     tickable : false,
11581     btnPosition : 'right',
11582     triggerList : true,
11583     showToggleBtn : true,
11584     animate : true,
11585     emptyResultText: 'Empty',
11586     triggerText : 'Select',
11587     
11588     // element that contains real text value.. (when hidden is used..)
11589     
11590     getAutoCreate : function()
11591     {
11592         var cfg = false;
11593         
11594         /*
11595          * Touch Devices
11596          */
11597         
11598         if(Roo.isTouch && this.mobileTouchView){
11599             cfg = this.getAutoCreateTouchView();
11600             return cfg;;
11601         }
11602         
11603         /*
11604          *  Normal ComboBox
11605          */
11606         if(!this.tickable){
11607             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11608             return cfg;
11609         }
11610         
11611         /*
11612          *  ComboBox with tickable selections
11613          */
11614              
11615         var align = this.labelAlign || this.parentLabelAlign();
11616         
11617         cfg = {
11618             cls : 'form-group roo-combobox-tickable' //input-group
11619         };
11620         
11621         var buttons = {
11622             tag : 'div',
11623             cls : 'tickable-buttons',
11624             cn : [
11625                 {
11626                     tag : 'button',
11627                     type : 'button',
11628                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11629                     html : this.triggerText
11630                 },
11631                 {
11632                     tag : 'button',
11633                     type : 'button',
11634                     name : 'ok',
11635                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11636                     html : 'Done'
11637                 },
11638                 {
11639                     tag : 'button',
11640                     type : 'button',
11641                     name : 'cancel',
11642                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11643                     html : 'Cancel'
11644                 }
11645             ]
11646         };
11647         
11648         if(this.editable){
11649             buttons.cn.unshift({
11650                 tag: 'input',
11651                 cls: 'select2-search-field-input'
11652             });
11653         }
11654         
11655         var _this = this;
11656         
11657         Roo.each(buttons.cn, function(c){
11658             if (_this.size) {
11659                 c.cls += ' btn-' + _this.size;
11660             }
11661
11662             if (_this.disabled) {
11663                 c.disabled = true;
11664             }
11665         });
11666         
11667         var box = {
11668             tag: 'div',
11669             cn: [
11670                 {
11671                     tag: 'input',
11672                     type : 'hidden',
11673                     cls: 'form-hidden-field'
11674                 },
11675                 {
11676                     tag: 'ul',
11677                     cls: 'select2-choices',
11678                     cn:[
11679                         {
11680                             tag: 'li',
11681                             cls: 'select2-search-field',
11682                             cn: [
11683
11684                                 buttons
11685                             ]
11686                         }
11687                     ]
11688                 }
11689             ]
11690         };
11691         
11692         var combobox = {
11693             cls: 'select2-container input-group select2-container-multi',
11694             cn: [
11695                 box
11696 //                {
11697 //                    tag: 'ul',
11698 //                    cls: 'typeahead typeahead-long dropdown-menu',
11699 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11700 //                }
11701             ]
11702         };
11703         
11704         if(this.hasFeedback && !this.allowBlank){
11705             
11706             var feedback = {
11707                 tag: 'span',
11708                 cls: 'glyphicon form-control-feedback'
11709             };
11710
11711             combobox.cn.push(feedback);
11712         }
11713         
11714         if (align ==='left' && this.fieldLabel.length) {
11715             
11716                 Roo.log("left and has label");
11717                 cfg.cn = [
11718                     
11719                     {
11720                         tag: 'label',
11721                         'for' :  id,
11722                         cls : 'control-label col-sm-' + this.labelWidth,
11723                         html : this.fieldLabel
11724                         
11725                     },
11726                     {
11727                         cls : "col-sm-" + (12 - this.labelWidth), 
11728                         cn: [
11729                             combobox
11730                         ]
11731                     }
11732                     
11733                 ];
11734         } else if ( this.fieldLabel.length) {
11735                 Roo.log(" label");
11736                  cfg.cn = [
11737                    
11738                     {
11739                         tag: 'label',
11740                         //cls : 'input-group-addon',
11741                         html : this.fieldLabel
11742                         
11743                     },
11744                     
11745                     combobox
11746                     
11747                 ];
11748
11749         } else {
11750             
11751                 Roo.log(" no label && no align");
11752                 cfg = combobox
11753                      
11754                 
11755         }
11756          
11757         var settings=this;
11758         ['xs','sm','md','lg'].map(function(size){
11759             if (settings[size]) {
11760                 cfg.cls += ' col-' + size + '-' + settings[size];
11761             }
11762         });
11763         
11764         return cfg;
11765         
11766     },
11767     
11768     _initEventsCalled : false,
11769     
11770     // private
11771     initEvents: function()
11772     {
11773         
11774         if (this._initEventsCalled) { // as we call render... prevent looping...
11775             return;
11776         }
11777         this._initEventsCalled = true;
11778         
11779         if (!this.store) {
11780             throw "can not find store for combo";
11781         }
11782         
11783         this.store = Roo.factory(this.store, Roo.data);
11784         
11785         // if we are building from html. then this element is so complex, that we can not really
11786         // use the rendered HTML.
11787         // so we have to trash and replace the previous code.
11788         if (Roo.XComponent.build_from_html) {
11789             
11790             // remove this element....
11791             var e = this.el.dom, k=0;
11792             while (e ) { e = e.previousSibling;  ++k;}
11793
11794             this.el.remove();
11795             
11796             this.el=false;
11797             this.rendered = false;
11798             
11799             this.render(this.parent().getChildContainer(true), k);
11800             
11801             
11802             
11803         }
11804         
11805         
11806         /*
11807          * Touch Devices
11808          */
11809         
11810         if(Roo.isTouch && this.mobileTouchView){
11811             this.initTouchView();
11812             return;
11813         }
11814         
11815         if(this.tickable){
11816             this.initTickableEvents();
11817             return;
11818         }
11819         
11820         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11821         
11822         if(this.hiddenName){
11823             
11824             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11825             
11826             this.hiddenField.dom.value =
11827                 this.hiddenValue !== undefined ? this.hiddenValue :
11828                 this.value !== undefined ? this.value : '';
11829
11830             // prevent input submission
11831             this.el.dom.removeAttribute('name');
11832             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11833              
11834              
11835         }
11836         //if(Roo.isGecko){
11837         //    this.el.dom.setAttribute('autocomplete', 'off');
11838         //}
11839         
11840         var cls = 'x-combo-list';
11841         
11842         //this.list = new Roo.Layer({
11843         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11844         //});
11845         
11846         var _this = this;
11847         
11848         (function(){
11849             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11850             _this.list.setWidth(lw);
11851         }).defer(100);
11852         
11853         this.list.on('mouseover', this.onViewOver, this);
11854         this.list.on('mousemove', this.onViewMove, this);
11855         
11856         this.list.on('scroll', this.onViewScroll, this);
11857         
11858         /*
11859         this.list.swallowEvent('mousewheel');
11860         this.assetHeight = 0;
11861
11862         if(this.title){
11863             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11864             this.assetHeight += this.header.getHeight();
11865         }
11866
11867         this.innerList = this.list.createChild({cls:cls+'-inner'});
11868         this.innerList.on('mouseover', this.onViewOver, this);
11869         this.innerList.on('mousemove', this.onViewMove, this);
11870         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11871         
11872         if(this.allowBlank && !this.pageSize && !this.disableClear){
11873             this.footer = this.list.createChild({cls:cls+'-ft'});
11874             this.pageTb = new Roo.Toolbar(this.footer);
11875            
11876         }
11877         if(this.pageSize){
11878             this.footer = this.list.createChild({cls:cls+'-ft'});
11879             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11880                     {pageSize: this.pageSize});
11881             
11882         }
11883         
11884         if (this.pageTb && this.allowBlank && !this.disableClear) {
11885             var _this = this;
11886             this.pageTb.add(new Roo.Toolbar.Fill(), {
11887                 cls: 'x-btn-icon x-btn-clear',
11888                 text: '&#160;',
11889                 handler: function()
11890                 {
11891                     _this.collapse();
11892                     _this.clearValue();
11893                     _this.onSelect(false, -1);
11894                 }
11895             });
11896         }
11897         if (this.footer) {
11898             this.assetHeight += this.footer.getHeight();
11899         }
11900         */
11901             
11902         if(!this.tpl){
11903             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11904         }
11905
11906         this.view = new Roo.View(this.list, this.tpl, {
11907             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11908         });
11909         //this.view.wrapEl.setDisplayed(false);
11910         this.view.on('click', this.onViewClick, this);
11911         
11912         
11913         
11914         this.store.on('beforeload', this.onBeforeLoad, this);
11915         this.store.on('load', this.onLoad, this);
11916         this.store.on('loadexception', this.onLoadException, this);
11917         /*
11918         if(this.resizable){
11919             this.resizer = new Roo.Resizable(this.list,  {
11920                pinned:true, handles:'se'
11921             });
11922             this.resizer.on('resize', function(r, w, h){
11923                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11924                 this.listWidth = w;
11925                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11926                 this.restrictHeight();
11927             }, this);
11928             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11929         }
11930         */
11931         if(!this.editable){
11932             this.editable = true;
11933             this.setEditable(false);
11934         }
11935         
11936         /*
11937         
11938         if (typeof(this.events.add.listeners) != 'undefined') {
11939             
11940             this.addicon = this.wrap.createChild(
11941                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11942        
11943             this.addicon.on('click', function(e) {
11944                 this.fireEvent('add', this);
11945             }, this);
11946         }
11947         if (typeof(this.events.edit.listeners) != 'undefined') {
11948             
11949             this.editicon = this.wrap.createChild(
11950                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11951             if (this.addicon) {
11952                 this.editicon.setStyle('margin-left', '40px');
11953             }
11954             this.editicon.on('click', function(e) {
11955                 
11956                 // we fire even  if inothing is selected..
11957                 this.fireEvent('edit', this, this.lastData );
11958                 
11959             }, this);
11960         }
11961         */
11962         
11963         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11964             "up" : function(e){
11965                 this.inKeyMode = true;
11966                 this.selectPrev();
11967             },
11968
11969             "down" : function(e){
11970                 if(!this.isExpanded()){
11971                     this.onTriggerClick();
11972                 }else{
11973                     this.inKeyMode = true;
11974                     this.selectNext();
11975                 }
11976             },
11977
11978             "enter" : function(e){
11979 //                this.onViewClick();
11980                 //return true;
11981                 this.collapse();
11982                 
11983                 if(this.fireEvent("specialkey", this, e)){
11984                     this.onViewClick(false);
11985                 }
11986                 
11987                 return true;
11988             },
11989
11990             "esc" : function(e){
11991                 this.collapse();
11992             },
11993
11994             "tab" : function(e){
11995                 this.collapse();
11996                 
11997                 if(this.fireEvent("specialkey", this, e)){
11998                     this.onViewClick(false);
11999                 }
12000                 
12001                 return true;
12002             },
12003
12004             scope : this,
12005
12006             doRelay : function(foo, bar, hname){
12007                 if(hname == 'down' || this.scope.isExpanded()){
12008                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12009                 }
12010                 return true;
12011             },
12012
12013             forceKeyDown: true
12014         });
12015         
12016         
12017         this.queryDelay = Math.max(this.queryDelay || 10,
12018                 this.mode == 'local' ? 10 : 250);
12019         
12020         
12021         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12022         
12023         if(this.typeAhead){
12024             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12025         }
12026         if(this.editable !== false){
12027             this.inputEl().on("keyup", this.onKeyUp, this);
12028         }
12029         if(this.forceSelection){
12030             this.inputEl().on('blur', this.doForce, this);
12031         }
12032         
12033         if(this.multiple){
12034             this.choices = this.el.select('ul.select2-choices', true).first();
12035             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12036         }
12037     },
12038     
12039     initTickableEvents: function()
12040     {   
12041         this.createList();
12042         
12043         if(this.hiddenName){
12044             
12045             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12046             
12047             this.hiddenField.dom.value =
12048                 this.hiddenValue !== undefined ? this.hiddenValue :
12049                 this.value !== undefined ? this.value : '';
12050
12051             // prevent input submission
12052             this.el.dom.removeAttribute('name');
12053             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12054              
12055              
12056         }
12057         
12058 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12059         
12060         this.choices = this.el.select('ul.select2-choices', true).first();
12061         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12062         if(this.triggerList){
12063             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12064         }
12065          
12066         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12067         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12068         
12069         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12070         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12071         
12072         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12073         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12074         
12075         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12076         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12077         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12078         
12079         this.okBtn.hide();
12080         this.cancelBtn.hide();
12081         
12082         var _this = this;
12083         
12084         (function(){
12085             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12086             _this.list.setWidth(lw);
12087         }).defer(100);
12088         
12089         this.list.on('mouseover', this.onViewOver, this);
12090         this.list.on('mousemove', this.onViewMove, this);
12091         
12092         this.list.on('scroll', this.onViewScroll, this);
12093         
12094         if(!this.tpl){
12095             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12096         }
12097
12098         this.view = new Roo.View(this.list, this.tpl, {
12099             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12100         });
12101         
12102         //this.view.wrapEl.setDisplayed(false);
12103         this.view.on('click', this.onViewClick, this);
12104         
12105         
12106         
12107         this.store.on('beforeload', this.onBeforeLoad, this);
12108         this.store.on('load', this.onLoad, this);
12109         this.store.on('loadexception', this.onLoadException, this);
12110         
12111         if(this.editable){
12112             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12113                 "up" : function(e){
12114                     this.inKeyMode = true;
12115                     this.selectPrev();
12116                 },
12117
12118                 "down" : function(e){
12119                     this.inKeyMode = true;
12120                     this.selectNext();
12121                 },
12122
12123                 "enter" : function(e){
12124                     if(this.fireEvent("specialkey", this, e)){
12125                         this.onViewClick(false);
12126                     }
12127                     
12128                     return true;
12129                 },
12130
12131                 "esc" : function(e){
12132                     this.onTickableFooterButtonClick(e, false, false);
12133                 },
12134
12135                 "tab" : function(e){
12136                     this.fireEvent("specialkey", this, e);
12137                     
12138                     this.onTickableFooterButtonClick(e, false, false);
12139                     
12140                     return true;
12141                 },
12142
12143                 scope : this,
12144
12145                 doRelay : function(e, fn, key){
12146                     if(this.scope.isExpanded()){
12147                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12148                     }
12149                     return true;
12150                 },
12151
12152                 forceKeyDown: true
12153             });
12154         }
12155         
12156         this.queryDelay = Math.max(this.queryDelay || 10,
12157                 this.mode == 'local' ? 10 : 250);
12158         
12159         
12160         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12161         
12162         if(this.typeAhead){
12163             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12164         }
12165         
12166         if(this.editable !== false){
12167             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12168         }
12169         
12170     },
12171
12172     onDestroy : function(){
12173         if(this.view){
12174             this.view.setStore(null);
12175             this.view.el.removeAllListeners();
12176             this.view.el.remove();
12177             this.view.purgeListeners();
12178         }
12179         if(this.list){
12180             this.list.dom.innerHTML  = '';
12181         }
12182         
12183         if(this.store){
12184             this.store.un('beforeload', this.onBeforeLoad, this);
12185             this.store.un('load', this.onLoad, this);
12186             this.store.un('loadexception', this.onLoadException, this);
12187         }
12188         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12189     },
12190
12191     // private
12192     fireKey : function(e){
12193         if(e.isNavKeyPress() && !this.list.isVisible()){
12194             this.fireEvent("specialkey", this, e);
12195         }
12196     },
12197
12198     // private
12199     onResize: function(w, h){
12200 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12201 //        
12202 //        if(typeof w != 'number'){
12203 //            // we do not handle it!?!?
12204 //            return;
12205 //        }
12206 //        var tw = this.trigger.getWidth();
12207 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12208 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12209 //        var x = w - tw;
12210 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12211 //            
12212 //        //this.trigger.setStyle('left', x+'px');
12213 //        
12214 //        if(this.list && this.listWidth === undefined){
12215 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12216 //            this.list.setWidth(lw);
12217 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12218 //        }
12219         
12220     
12221         
12222     },
12223
12224     /**
12225      * Allow or prevent the user from directly editing the field text.  If false is passed,
12226      * the user will only be able to select from the items defined in the dropdown list.  This method
12227      * is the runtime equivalent of setting the 'editable' config option at config time.
12228      * @param {Boolean} value True to allow the user to directly edit the field text
12229      */
12230     setEditable : function(value){
12231         if(value == this.editable){
12232             return;
12233         }
12234         this.editable = value;
12235         if(!value){
12236             this.inputEl().dom.setAttribute('readOnly', true);
12237             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12238             this.inputEl().addClass('x-combo-noedit');
12239         }else{
12240             this.inputEl().dom.setAttribute('readOnly', false);
12241             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12242             this.inputEl().removeClass('x-combo-noedit');
12243         }
12244     },
12245
12246     // private
12247     
12248     onBeforeLoad : function(combo,opts){
12249         if(!this.hasFocus){
12250             return;
12251         }
12252          if (!opts.add) {
12253             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12254          }
12255         this.restrictHeight();
12256         this.selectedIndex = -1;
12257     },
12258
12259     // private
12260     onLoad : function(){
12261         
12262         this.hasQuery = false;
12263         
12264         if(!this.hasFocus){
12265             return;
12266         }
12267         
12268         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12269             this.loading.hide();
12270         }
12271              
12272         if(this.store.getCount() > 0){
12273             this.expand();
12274             this.restrictHeight();
12275             if(this.lastQuery == this.allQuery){
12276                 if(this.editable && !this.tickable){
12277                     this.inputEl().dom.select();
12278                 }
12279                 
12280                 if(
12281                     !this.selectByValue(this.value, true) &&
12282                     this.autoFocus && 
12283                     (
12284                         !this.store.lastOptions ||
12285                         typeof(this.store.lastOptions.add) == 'undefined' || 
12286                         this.store.lastOptions.add != true
12287                     )
12288                 ){
12289                     this.select(0, true);
12290                 }
12291             }else{
12292                 if(this.autoFocus){
12293                     this.selectNext();
12294                 }
12295                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12296                     this.taTask.delay(this.typeAheadDelay);
12297                 }
12298             }
12299         }else{
12300             this.onEmptyResults();
12301         }
12302         
12303         //this.el.focus();
12304     },
12305     // private
12306     onLoadException : function()
12307     {
12308         this.hasQuery = false;
12309         
12310         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12311             this.loading.hide();
12312         }
12313         
12314         if(this.tickable && this.editable){
12315             return;
12316         }
12317         
12318         this.collapse();
12319         
12320         Roo.log(this.store.reader.jsonData);
12321         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12322             // fixme
12323             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12324         }
12325         
12326         
12327     },
12328     // private
12329     onTypeAhead : function(){
12330         if(this.store.getCount() > 0){
12331             var r = this.store.getAt(0);
12332             var newValue = r.data[this.displayField];
12333             var len = newValue.length;
12334             var selStart = this.getRawValue().length;
12335             
12336             if(selStart != len){
12337                 this.setRawValue(newValue);
12338                 this.selectText(selStart, newValue.length);
12339             }
12340         }
12341     },
12342
12343     // private
12344     onSelect : function(record, index){
12345         
12346         if(this.fireEvent('beforeselect', this, record, index) !== false){
12347         
12348             this.setFromData(index > -1 ? record.data : false);
12349             
12350             this.collapse();
12351             this.fireEvent('select', this, record, index);
12352         }
12353     },
12354
12355     /**
12356      * Returns the currently selected field value or empty string if no value is set.
12357      * @return {String} value The selected value
12358      */
12359     getValue : function(){
12360         
12361         if(this.multiple){
12362             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12363         }
12364         
12365         if(this.valueField){
12366             return typeof this.value != 'undefined' ? this.value : '';
12367         }else{
12368             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12369         }
12370     },
12371
12372     /**
12373      * Clears any text/value currently set in the field
12374      */
12375     clearValue : function(){
12376         if(this.hiddenField){
12377             this.hiddenField.dom.value = '';
12378         }
12379         this.value = '';
12380         this.setRawValue('');
12381         this.lastSelectionText = '';
12382         this.lastData = false;
12383         
12384         var close = this.closeTriggerEl();
12385         
12386         if(close){
12387             close.hide();
12388         }
12389         
12390     },
12391
12392     /**
12393      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12394      * will be displayed in the field.  If the value does not match the data value of an existing item,
12395      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12396      * Otherwise the field will be blank (although the value will still be set).
12397      * @param {String} value The value to match
12398      */
12399     setValue : function(v){
12400         if(this.multiple){
12401             this.syncValue();
12402             return;
12403         }
12404         
12405         var text = v;
12406         if(this.valueField){
12407             var r = this.findRecord(this.valueField, v);
12408             if(r){
12409                 text = r.data[this.displayField];
12410             }else if(this.valueNotFoundText !== undefined){
12411                 text = this.valueNotFoundText;
12412             }
12413         }
12414         this.lastSelectionText = text;
12415         if(this.hiddenField){
12416             this.hiddenField.dom.value = v;
12417         }
12418         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12419         this.value = v;
12420         
12421         var close = this.closeTriggerEl();
12422         
12423         if(close){
12424             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12425         }
12426     },
12427     /**
12428      * @property {Object} the last set data for the element
12429      */
12430     
12431     lastData : false,
12432     /**
12433      * Sets the value of the field based on a object which is related to the record format for the store.
12434      * @param {Object} value the value to set as. or false on reset?
12435      */
12436     setFromData : function(o){
12437         
12438         if(this.multiple){
12439             this.addItem(o);
12440             return;
12441         }
12442             
12443         var dv = ''; // display value
12444         var vv = ''; // value value..
12445         this.lastData = o;
12446         if (this.displayField) {
12447             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12448         } else {
12449             // this is an error condition!!!
12450             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12451         }
12452         
12453         if(this.valueField){
12454             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12455         }
12456         
12457         var close = this.closeTriggerEl();
12458         
12459         if(close){
12460             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12461         }
12462         
12463         if(this.hiddenField){
12464             this.hiddenField.dom.value = vv;
12465             
12466             this.lastSelectionText = dv;
12467             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12468             this.value = vv;
12469             return;
12470         }
12471         // no hidden field.. - we store the value in 'value', but still display
12472         // display field!!!!
12473         this.lastSelectionText = dv;
12474         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12475         this.value = vv;
12476         
12477         
12478         
12479     },
12480     // private
12481     reset : function(){
12482         // overridden so that last data is reset..
12483         
12484         if(this.multiple){
12485             this.clearItem();
12486             return;
12487         }
12488         
12489         this.setValue(this.originalValue);
12490         this.clearInvalid();
12491         this.lastData = false;
12492         if (this.view) {
12493             this.view.clearSelections();
12494         }
12495     },
12496     // private
12497     findRecord : function(prop, value){
12498         var record;
12499         if(this.store.getCount() > 0){
12500             this.store.each(function(r){
12501                 if(r.data[prop] == value){
12502                     record = r;
12503                     return false;
12504                 }
12505                 return true;
12506             });
12507         }
12508         return record;
12509     },
12510     
12511     getName: function()
12512     {
12513         // returns hidden if it's set..
12514         if (!this.rendered) {return ''};
12515         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12516         
12517     },
12518     // private
12519     onViewMove : function(e, t){
12520         this.inKeyMode = false;
12521     },
12522
12523     // private
12524     onViewOver : function(e, t){
12525         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12526             return;
12527         }
12528         var item = this.view.findItemFromChild(t);
12529         
12530         if(item){
12531             var index = this.view.indexOf(item);
12532             this.select(index, false);
12533         }
12534     },
12535
12536     // private
12537     onViewClick : function(view, doFocus, el, e)
12538     {
12539         var index = this.view.getSelectedIndexes()[0];
12540         
12541         var r = this.store.getAt(index);
12542         
12543         if(this.tickable){
12544             
12545             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12546                 return;
12547             }
12548             
12549             var rm = false;
12550             var _this = this;
12551             
12552             Roo.each(this.tickItems, function(v,k){
12553                 
12554                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12555                     _this.tickItems.splice(k, 1);
12556                     
12557                     if(typeof(e) == 'undefined' && view == false){
12558                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12559                     }
12560                     
12561                     rm = true;
12562                     return;
12563                 }
12564             });
12565             
12566             if(rm){
12567                 return;
12568             }
12569             
12570             this.tickItems.push(r.data);
12571             
12572             if(typeof(e) == 'undefined' && view == false){
12573                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12574             }
12575                     
12576             return;
12577         }
12578         
12579         if(r){
12580             this.onSelect(r, index);
12581         }
12582         if(doFocus !== false && !this.blockFocus){
12583             this.inputEl().focus();
12584         }
12585     },
12586
12587     // private
12588     restrictHeight : function(){
12589         //this.innerList.dom.style.height = '';
12590         //var inner = this.innerList.dom;
12591         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12592         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12593         //this.list.beginUpdate();
12594         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12595         this.list.alignTo(this.inputEl(), this.listAlign);
12596         this.list.alignTo(this.inputEl(), this.listAlign);
12597         //this.list.endUpdate();
12598     },
12599
12600     // private
12601     onEmptyResults : function(){
12602         
12603         if(this.tickable && this.editable){
12604             this.restrictHeight();
12605             return;
12606         }
12607         
12608         this.collapse();
12609     },
12610
12611     /**
12612      * Returns true if the dropdown list is expanded, else false.
12613      */
12614     isExpanded : function(){
12615         return this.list.isVisible();
12616     },
12617
12618     /**
12619      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12620      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12621      * @param {String} value The data value of the item to select
12622      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12623      * selected item if it is not currently in view (defaults to true)
12624      * @return {Boolean} True if the value matched an item in the list, else false
12625      */
12626     selectByValue : function(v, scrollIntoView){
12627         if(v !== undefined && v !== null){
12628             var r = this.findRecord(this.valueField || this.displayField, v);
12629             if(r){
12630                 this.select(this.store.indexOf(r), scrollIntoView);
12631                 return true;
12632             }
12633         }
12634         return false;
12635     },
12636
12637     /**
12638      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12639      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12640      * @param {Number} index The zero-based index of the list item to select
12641      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12642      * selected item if it is not currently in view (defaults to true)
12643      */
12644     select : function(index, scrollIntoView){
12645         this.selectedIndex = index;
12646         this.view.select(index);
12647         if(scrollIntoView !== false){
12648             var el = this.view.getNode(index);
12649             /*
12650              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12651              */
12652             if(el){
12653                 this.list.scrollChildIntoView(el, false);
12654             }
12655         }
12656     },
12657
12658     // private
12659     selectNext : function(){
12660         var ct = this.store.getCount();
12661         if(ct > 0){
12662             if(this.selectedIndex == -1){
12663                 this.select(0);
12664             }else if(this.selectedIndex < ct-1){
12665                 this.select(this.selectedIndex+1);
12666             }
12667         }
12668     },
12669
12670     // private
12671     selectPrev : function(){
12672         var ct = this.store.getCount();
12673         if(ct > 0){
12674             if(this.selectedIndex == -1){
12675                 this.select(0);
12676             }else if(this.selectedIndex != 0){
12677                 this.select(this.selectedIndex-1);
12678             }
12679         }
12680     },
12681
12682     // private
12683     onKeyUp : function(e){
12684         if(this.editable !== false && !e.isSpecialKey()){
12685             this.lastKey = e.getKey();
12686             this.dqTask.delay(this.queryDelay);
12687         }
12688     },
12689
12690     // private
12691     validateBlur : function(){
12692         return !this.list || !this.list.isVisible();   
12693     },
12694
12695     // private
12696     initQuery : function(){
12697         
12698         var v = this.getRawValue();
12699         
12700         if(this.tickable && this.editable){
12701             v = this.tickableInputEl().getValue();
12702         }
12703         
12704         this.doQuery(v);
12705     },
12706
12707     // private
12708     doForce : function(){
12709         if(this.inputEl().dom.value.length > 0){
12710             this.inputEl().dom.value =
12711                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12712              
12713         }
12714     },
12715
12716     /**
12717      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12718      * query allowing the query action to be canceled if needed.
12719      * @param {String} query The SQL query to execute
12720      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12721      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12722      * saved in the current store (defaults to false)
12723      */
12724     doQuery : function(q, forceAll){
12725         
12726         if(q === undefined || q === null){
12727             q = '';
12728         }
12729         var qe = {
12730             query: q,
12731             forceAll: forceAll,
12732             combo: this,
12733             cancel:false
12734         };
12735         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12736             return false;
12737         }
12738         q = qe.query;
12739         
12740         forceAll = qe.forceAll;
12741         if(forceAll === true || (q.length >= this.minChars)){
12742             
12743             this.hasQuery = true;
12744             
12745             if(this.lastQuery != q || this.alwaysQuery){
12746                 this.lastQuery = q;
12747                 if(this.mode == 'local'){
12748                     this.selectedIndex = -1;
12749                     if(forceAll){
12750                         this.store.clearFilter();
12751                     }else{
12752                         
12753                         if(this.specialFilter){
12754                             this.fireEvent('specialfilter', this);
12755                             this.onLoad();
12756                             return;
12757                         }
12758                         
12759                         this.store.filter(this.displayField, q);
12760                     }
12761                     
12762                     this.store.fireEvent("datachanged", this.store);
12763                     
12764                     this.onLoad();
12765                     
12766                     
12767                 }else{
12768                     
12769                     this.store.baseParams[this.queryParam] = q;
12770                     
12771                     var options = {params : this.getParams(q)};
12772                     
12773                     if(this.loadNext){
12774                         options.add = true;
12775                         options.params.start = this.page * this.pageSize;
12776                     }
12777                     
12778                     this.store.load(options);
12779                     
12780                     /*
12781                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12782                      *  we should expand the list on onLoad
12783                      *  so command out it
12784                      */
12785 //                    this.expand();
12786                 }
12787             }else{
12788                 this.selectedIndex = -1;
12789                 this.onLoad();   
12790             }
12791         }
12792         
12793         this.loadNext = false;
12794     },
12795     
12796     // private
12797     getParams : function(q){
12798         var p = {};
12799         //p[this.queryParam] = q;
12800         
12801         if(this.pageSize){
12802             p.start = 0;
12803             p.limit = this.pageSize;
12804         }
12805         return p;
12806     },
12807
12808     /**
12809      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12810      */
12811     collapse : function(){
12812         if(!this.isExpanded()){
12813             return;
12814         }
12815         
12816         this.list.hide();
12817         
12818         if(this.tickable){
12819             this.hasFocus = false;
12820             this.okBtn.hide();
12821             this.cancelBtn.hide();
12822             this.trigger.show();
12823             
12824             if(this.editable){
12825                 this.tickableInputEl().dom.value = '';
12826                 this.tickableInputEl().blur();
12827             }
12828             
12829         }
12830         
12831         Roo.get(document).un('mousedown', this.collapseIf, this);
12832         Roo.get(document).un('mousewheel', this.collapseIf, this);
12833         if (!this.editable) {
12834             Roo.get(document).un('keydown', this.listKeyPress, this);
12835         }
12836         this.fireEvent('collapse', this);
12837     },
12838
12839     // private
12840     collapseIf : function(e){
12841         var in_combo  = e.within(this.el);
12842         var in_list =  e.within(this.list);
12843         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12844         
12845         if (in_combo || in_list || is_list) {
12846             //e.stopPropagation();
12847             return;
12848         }
12849         
12850         if(this.tickable){
12851             this.onTickableFooterButtonClick(e, false, false);
12852         }
12853
12854         this.collapse();
12855         
12856     },
12857
12858     /**
12859      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12860      */
12861     expand : function(){
12862        
12863         if(this.isExpanded() || !this.hasFocus){
12864             return;
12865         }
12866         
12867         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12868         this.list.setWidth(lw);
12869         
12870         
12871          Roo.log('expand');
12872         
12873         this.list.show();
12874         
12875         this.restrictHeight();
12876         
12877         if(this.tickable){
12878             
12879             this.tickItems = Roo.apply([], this.item);
12880             
12881             this.okBtn.show();
12882             this.cancelBtn.show();
12883             this.trigger.hide();
12884             
12885             if(this.editable){
12886                 this.tickableInputEl().focus();
12887             }
12888             
12889         }
12890         
12891         Roo.get(document).on('mousedown', this.collapseIf, this);
12892         Roo.get(document).on('mousewheel', this.collapseIf, this);
12893         if (!this.editable) {
12894             Roo.get(document).on('keydown', this.listKeyPress, this);
12895         }
12896         
12897         this.fireEvent('expand', this);
12898     },
12899
12900     // private
12901     // Implements the default empty TriggerField.onTriggerClick function
12902     onTriggerClick : function(e)
12903     {
12904         Roo.log('trigger click');
12905         
12906         if(this.disabled || !this.triggerList){
12907             return;
12908         }
12909         
12910         this.page = 0;
12911         this.loadNext = false;
12912         
12913         if(this.isExpanded()){
12914             this.collapse();
12915             if (!this.blockFocus) {
12916                 this.inputEl().focus();
12917             }
12918             
12919         }else {
12920             this.hasFocus = true;
12921             if(this.triggerAction == 'all') {
12922                 this.doQuery(this.allQuery, true);
12923             } else {
12924                 this.doQuery(this.getRawValue());
12925             }
12926             if (!this.blockFocus) {
12927                 this.inputEl().focus();
12928             }
12929         }
12930     },
12931     
12932     onTickableTriggerClick : function(e)
12933     {
12934         if(this.disabled){
12935             return;
12936         }
12937         
12938         this.page = 0;
12939         this.loadNext = false;
12940         this.hasFocus = true;
12941         
12942         if(this.triggerAction == 'all') {
12943             this.doQuery(this.allQuery, true);
12944         } else {
12945             this.doQuery(this.getRawValue());
12946         }
12947     },
12948     
12949     onSearchFieldClick : function(e)
12950     {
12951         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12952             this.onTickableFooterButtonClick(e, false, false);
12953             return;
12954         }
12955         
12956         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12957             return;
12958         }
12959         
12960         this.page = 0;
12961         this.loadNext = false;
12962         this.hasFocus = true;
12963         
12964         if(this.triggerAction == 'all') {
12965             this.doQuery(this.allQuery, true);
12966         } else {
12967             this.doQuery(this.getRawValue());
12968         }
12969     },
12970     
12971     listKeyPress : function(e)
12972     {
12973         //Roo.log('listkeypress');
12974         // scroll to first matching element based on key pres..
12975         if (e.isSpecialKey()) {
12976             return false;
12977         }
12978         var k = String.fromCharCode(e.getKey()).toUpperCase();
12979         //Roo.log(k);
12980         var match  = false;
12981         var csel = this.view.getSelectedNodes();
12982         var cselitem = false;
12983         if (csel.length) {
12984             var ix = this.view.indexOf(csel[0]);
12985             cselitem  = this.store.getAt(ix);
12986             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12987                 cselitem = false;
12988             }
12989             
12990         }
12991         
12992         this.store.each(function(v) { 
12993             if (cselitem) {
12994                 // start at existing selection.
12995                 if (cselitem.id == v.id) {
12996                     cselitem = false;
12997                 }
12998                 return true;
12999             }
13000                 
13001             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13002                 match = this.store.indexOf(v);
13003                 return false;
13004             }
13005             return true;
13006         }, this);
13007         
13008         if (match === false) {
13009             return true; // no more action?
13010         }
13011         // scroll to?
13012         this.view.select(match);
13013         var sn = Roo.get(this.view.getSelectedNodes()[0])
13014         sn.scrollIntoView(sn.dom.parentNode, false);
13015     },
13016     
13017     onViewScroll : function(e, t){
13018         
13019         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
13020             return;
13021         }
13022         
13023         this.hasQuery = true;
13024         
13025         this.loading = this.list.select('.loading', true).first();
13026         
13027         if(this.loading === null){
13028             this.list.createChild({
13029                 tag: 'div',
13030                 cls: 'loading select2-more-results select2-active',
13031                 html: 'Loading more results...'
13032             })
13033             
13034             this.loading = this.list.select('.loading', true).first();
13035             
13036             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13037             
13038             this.loading.hide();
13039         }
13040         
13041         this.loading.show();
13042         
13043         var _combo = this;
13044         
13045         this.page++;
13046         this.loadNext = true;
13047         
13048         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13049         
13050         return;
13051     },
13052     
13053     addItem : function(o)
13054     {   
13055         var dv = ''; // display value
13056         
13057         if (this.displayField) {
13058             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13059         } else {
13060             // this is an error condition!!!
13061             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13062         }
13063         
13064         if(!dv.length){
13065             return;
13066         }
13067         
13068         var choice = this.choices.createChild({
13069             tag: 'li',
13070             cls: 'select2-search-choice',
13071             cn: [
13072                 {
13073                     tag: 'div',
13074                     html: dv
13075                 },
13076                 {
13077                     tag: 'a',
13078                     href: '#',
13079                     cls: 'select2-search-choice-close',
13080                     tabindex: '-1'
13081                 }
13082             ]
13083             
13084         }, this.searchField);
13085         
13086         var close = choice.select('a.select2-search-choice-close', true).first()
13087         
13088         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13089         
13090         this.item.push(o);
13091         
13092         this.lastData = o;
13093         
13094         this.syncValue();
13095         
13096         this.inputEl().dom.value = '';
13097         
13098         this.validate();
13099     },
13100     
13101     onRemoveItem : function(e, _self, o)
13102     {
13103         e.preventDefault();
13104         
13105         this.lastItem = Roo.apply([], this.item);
13106         
13107         var index = this.item.indexOf(o.data) * 1;
13108         
13109         if( index < 0){
13110             Roo.log('not this item?!');
13111             return;
13112         }
13113         
13114         this.item.splice(index, 1);
13115         o.item.remove();
13116         
13117         this.syncValue();
13118         
13119         this.fireEvent('remove', this, e);
13120         
13121         this.validate();
13122         
13123     },
13124     
13125     syncValue : function()
13126     {
13127         if(!this.item.length){
13128             this.clearValue();
13129             return;
13130         }
13131             
13132         var value = [];
13133         var _this = this;
13134         Roo.each(this.item, function(i){
13135             if(_this.valueField){
13136                 value.push(i[_this.valueField]);
13137                 return;
13138             }
13139
13140             value.push(i);
13141         });
13142
13143         this.value = value.join(',');
13144
13145         if(this.hiddenField){
13146             this.hiddenField.dom.value = this.value;
13147         }
13148         
13149         this.store.fireEvent("datachanged", this.store);
13150     },
13151     
13152     clearItem : function()
13153     {
13154         if(!this.multiple){
13155             return;
13156         }
13157         
13158         this.item = [];
13159         
13160         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13161            c.remove();
13162         });
13163         
13164         this.syncValue();
13165         
13166         this.validate();
13167         
13168         if(this.tickable && !Roo.isTouch){
13169             this.view.refresh();
13170         }
13171     },
13172     
13173     inputEl: function ()
13174     {
13175         if(Roo.isTouch && this.mobileTouchView){
13176             return this.el.select('input.form-control',true).first();
13177         }
13178         
13179         if(this.tickable){
13180             return this.searchField;
13181         }
13182         
13183         return this.el.select('input.form-control',true).first();
13184     },
13185     
13186     
13187     onTickableFooterButtonClick : function(e, btn, el)
13188     {
13189         e.preventDefault();
13190         
13191         this.lastItem = Roo.apply([], this.item);
13192         
13193         if(btn && btn.name == 'cancel'){
13194             this.tickItems = Roo.apply([], this.item);
13195             this.collapse();
13196             return;
13197         }
13198         
13199         this.clearItem();
13200         
13201         var _this = this;
13202         
13203         Roo.each(this.tickItems, function(o){
13204             _this.addItem(o);
13205         });
13206         
13207         this.collapse();
13208         
13209     },
13210     
13211     validate : function()
13212     {
13213         var v = this.getRawValue();
13214         
13215         if(this.multiple){
13216             v = this.getValue();
13217         }
13218         
13219         if(this.disabled || this.allowBlank || v.length){
13220             this.markValid();
13221             return true;
13222         }
13223         
13224         this.markInvalid();
13225         return false;
13226     },
13227     
13228     tickableInputEl : function()
13229     {
13230         if(!this.tickable || !this.editable){
13231             return this.inputEl();
13232         }
13233         
13234         return this.inputEl().select('.select2-search-field-input', true).first();
13235     },
13236     
13237     
13238     getAutoCreateTouchView : function()
13239     {
13240         var id = Roo.id();
13241         
13242         var cfg = {
13243             cls: 'form-group' //input-group
13244         };
13245         
13246         var input =  {
13247             tag: 'input',
13248             id : id,
13249             type : this.inputType,
13250             cls : 'form-control x-combo-noedit',
13251             autocomplete: 'new-password',
13252             placeholder : this.placeholder || '',
13253             readonly : true
13254         };
13255         
13256         if (this.name) {
13257             input.name = this.name;
13258         }
13259         
13260         if (this.size) {
13261             input.cls += ' input-' + this.size;
13262         }
13263         
13264         if (this.disabled) {
13265             input.disabled = true;
13266         }
13267         
13268         var inputblock = {
13269             cls : '',
13270             cn : [
13271                 input
13272             ]
13273         };
13274         
13275         if(this.before){
13276             inputblock.cls += ' input-group';
13277             
13278             inputblock.cn.unshift({
13279                 tag :'span',
13280                 cls : 'input-group-addon',
13281                 html : this.before
13282             });
13283         }
13284         
13285         if(this.removable && !this.multiple){
13286             inputblock.cls += ' roo-removable';
13287             
13288             inputblock.cn.push({
13289                 tag: 'button',
13290                 html : 'x',
13291                 cls : 'roo-combo-removable-btn close'
13292             });
13293         }
13294
13295         if(this.hasFeedback && !this.allowBlank){
13296             
13297             inputblock.cls += ' has-feedback';
13298             
13299             inputblock.cn.push({
13300                 tag: 'span',
13301                 cls: 'glyphicon form-control-feedback'
13302             });
13303             
13304         }
13305         
13306         if (this.after) {
13307             
13308             inputblock.cls += (this.before) ? '' : ' input-group';
13309             
13310             inputblock.cn.push({
13311                 tag :'span',
13312                 cls : 'input-group-addon',
13313                 html : this.after
13314             });
13315         }
13316
13317         var box = {
13318             tag: 'div',
13319             cn: [
13320                 {
13321                     tag: 'input',
13322                     type : 'hidden',
13323                     cls: 'form-hidden-field'
13324                 },
13325                 inputblock
13326             ]
13327             
13328         };
13329         
13330         if(this.multiple){
13331             box = {
13332                 tag: 'div',
13333                 cn: [
13334                     {
13335                         tag: 'input',
13336                         type : 'hidden',
13337                         cls: 'form-hidden-field'
13338                     },
13339                     {
13340                         tag: 'ul',
13341                         cls: 'select2-choices',
13342                         cn:[
13343                             {
13344                                 tag: 'li',
13345                                 cls: 'select2-search-field',
13346                                 cn: [
13347
13348                                     inputblock
13349                                 ]
13350                             }
13351                         ]
13352                     }
13353                 ]
13354             }
13355         };
13356         
13357         var combobox = {
13358             cls: 'select2-container input-group',
13359             cn: [
13360                 box
13361             ]
13362         };
13363         
13364         if(this.multiple){
13365             combobox.cls += ' select2-container-multi';
13366         }
13367         
13368         var align = this.labelAlign || this.parentLabelAlign();
13369         
13370         cfg.cn = combobox;
13371         
13372         if(this.fieldLabel.length){
13373             
13374             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13375             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13376             
13377             cfg.cn = [
13378                 {
13379                     tag: 'label',
13380                     cls : 'control-label ' + lw,
13381                     html : this.fieldLabel
13382
13383                 },
13384                 {
13385                     cls : cw, 
13386                     cn: [
13387                         combobox
13388                     ]
13389                 }
13390             ];
13391         }
13392         
13393         var settings = this;
13394         
13395         ['xs','sm','md','lg'].map(function(size){
13396             if (settings[size]) {
13397                 cfg.cls += ' col-' + size + '-' + settings[size];
13398             }
13399         });
13400         
13401         return cfg;
13402     },
13403     
13404     initTouchView : function()
13405     {
13406         this.renderTouchView();
13407         
13408         this.touchViewEl.on('scroll', function(){
13409             this.el.dom.scrollTop = 0;
13410         }, this);
13411         
13412         this.inputEl().on("click", this.showTouchView, this);
13413         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13414         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13415         
13416         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13417         
13418         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13419         this.store.on('load', this.onTouchViewLoad, this);
13420         this.store.on('loadexception', this.onTouchViewLoadException, this);
13421         
13422         if(this.hiddenName){
13423             
13424             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13425             
13426             this.hiddenField.dom.value =
13427                 this.hiddenValue !== undefined ? this.hiddenValue :
13428                 this.value !== undefined ? this.value : '';
13429         
13430             this.el.dom.removeAttribute('name');
13431             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13432         }
13433         
13434         if(this.multiple){
13435             this.choices = this.el.select('ul.select2-choices', true).first();
13436             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13437         }
13438         
13439         if(this.removable && !this.multiple){
13440             var close = this.closeTriggerEl();
13441             if(close){
13442                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13443                 close.on('click', this.removeBtnClick, this, close);
13444             }
13445         }
13446         
13447         return;
13448         
13449         
13450     },
13451     
13452     renderTouchView : function()
13453     {
13454         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13455         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13456         
13457         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13458         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13459         
13460         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13461         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13462         this.touchViewBodyEl.setStyle('overflow', 'auto');
13463         
13464         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13465         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13466         
13467         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13468         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13469         
13470     },
13471     
13472     showTouchView : function()
13473     {
13474         this.touchViewHeaderEl.hide();
13475
13476         if(this.fieldLabel.length){
13477             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13478             this.touchViewHeaderEl.show();
13479         }
13480
13481         this.touchViewEl.show();
13482
13483         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13484         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13485
13486         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13487
13488         if(this.fieldLabel.length){
13489             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13490         }
13491         
13492         this.touchViewBodyEl.setHeight(bodyHeight);
13493
13494         if(this.animate){
13495             var _this = this;
13496             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13497         }else{
13498             this.touchViewEl.addClass('in');
13499         }
13500
13501         this.doTouchViewQuery();
13502         
13503     },
13504     
13505     hideTouchView : function()
13506     {
13507         this.touchViewEl.removeClass('in');
13508
13509         if(this.animate){
13510             var _this = this;
13511             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13512         }else{
13513             this.touchViewEl.setStyle('display', 'none');
13514         }
13515         
13516     },
13517     
13518     setTouchViewValue : function()
13519     {
13520         if(this.multiple){
13521             this.clearItem();
13522         
13523             var _this = this;
13524
13525             Roo.each(this.tickItems, function(o){
13526                 this.addItem(o);
13527             }, this);
13528         }
13529         
13530         this.hideTouchView();
13531     },
13532     
13533     doTouchViewQuery : function()
13534     {
13535         var qe = {
13536             query: '',
13537             forceAll: true,
13538             combo: this,
13539             cancel:false
13540         };
13541         
13542         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13543             return false;
13544         }
13545         
13546         if(!this.alwaysQuery || this.mode == 'local'){
13547             this.onTouchViewLoad();
13548             return;
13549         }
13550         
13551         this.store.load();
13552     },
13553     
13554     onTouchViewBeforeLoad : function(combo,opts)
13555     {
13556         return;
13557     },
13558
13559     // private
13560     onTouchViewLoad : function()
13561     {
13562         if(this.store.getCount() < 1){
13563             this.onTouchViewEmptyResults();
13564             return;
13565         }
13566         
13567         this.clearTouchView();
13568         
13569         var rawValue = this.getRawValue();
13570         
13571         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13572         
13573         this.tickItems = [];
13574         
13575         this.store.data.each(function(d, rowIndex){
13576             var row = this.touchViewListGroup.createChild(template);
13577             
13578             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13579                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13580             }
13581             
13582             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13583                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13584             }
13585             
13586             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13587                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13588                 this.tickItems.push(d.data);
13589             }
13590             
13591             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13592             
13593         }, this);
13594         
13595         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13596         
13597         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13598
13599         if(this.fieldLabel.length){
13600             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13601         }
13602
13603         var listHeight = this.touchViewListGroup.getHeight();
13604         
13605         var _this = this;
13606         
13607         if(firstChecked && listHeight > bodyHeight){
13608             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13609         }
13610         
13611     },
13612     
13613     onTouchViewLoadException : function()
13614     {
13615         this.hideTouchView();
13616     },
13617     
13618     onTouchViewEmptyResults : function()
13619     {
13620         this.clearTouchView();
13621         
13622         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13623         
13624         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13625         
13626     },
13627     
13628     clearTouchView : function()
13629     {
13630         this.touchViewListGroup.dom.innerHTML = '';
13631     },
13632     
13633     onTouchViewClick : function(e, el, o)
13634     {
13635         e.preventDefault();
13636         
13637         var row = o.row;
13638         var rowIndex = o.rowIndex;
13639         
13640         var r = this.store.getAt(rowIndex);
13641         
13642         if(!this.multiple){
13643             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13644                 c.dom.removeAttribute('checked');
13645             }, this);
13646             
13647             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13648         
13649             this.setFromData(r.data);
13650             
13651             var close = this.closeTriggerEl();
13652         
13653             if(close){
13654                 close.show();
13655             }
13656
13657             this.hideTouchView();
13658             
13659             this.fireEvent('select', this, r, rowIndex);
13660             
13661             return;
13662         }
13663         
13664         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13665             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13666             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13667             return;
13668         }
13669         
13670         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13671         this.addItem(r.data);
13672         this.tickItems.push(r.data);
13673         
13674     }
13675     
13676
13677     /** 
13678     * @cfg {Boolean} grow 
13679     * @hide 
13680     */
13681     /** 
13682     * @cfg {Number} growMin 
13683     * @hide 
13684     */
13685     /** 
13686     * @cfg {Number} growMax 
13687     * @hide 
13688     */
13689     /**
13690      * @hide
13691      * @method autoSize
13692      */
13693 });
13694
13695 Roo.apply(Roo.bootstrap.ComboBox,  {
13696     
13697     header : {
13698         tag: 'div',
13699         cls: 'modal-header',
13700         cn: [
13701             {
13702                 tag: 'h4',
13703                 cls: 'modal-title'
13704             }
13705         ]
13706     },
13707     
13708     body : {
13709         tag: 'div',
13710         cls: 'modal-body',
13711         cn: [
13712             {
13713                 tag: 'ul',
13714                 cls: 'list-group'
13715             }
13716         ]
13717     },
13718     
13719     listItemRadio : {
13720         tag: 'li',
13721         cls: 'list-group-item',
13722         cn: [
13723             {
13724                 tag: 'span',
13725                 cls: 'roo-combobox-list-group-item-value'
13726             },
13727             {
13728                 tag: 'div',
13729                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13730                 cn: [
13731                     {
13732                         tag: 'input',
13733                         type: 'radio'
13734                     },
13735                     {
13736                         tag: 'label'
13737                     }
13738                 ]
13739             }
13740         ]
13741     },
13742     
13743     listItemCheckbox : {
13744         tag: 'li',
13745         cls: 'list-group-item',
13746         cn: [
13747             {
13748                 tag: 'span',
13749                 cls: 'roo-combobox-list-group-item-value'
13750             },
13751             {
13752                 tag: 'div',
13753                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13754                 cn: [
13755                     {
13756                         tag: 'input',
13757                         type: 'checkbox'
13758                     },
13759                     {
13760                         tag: 'label'
13761                     }
13762                 ]
13763             }
13764         ]
13765     },
13766     
13767     emptyResult : {
13768         tag: 'div',
13769         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13770     },
13771     
13772     footer : {
13773         tag: 'div',
13774         cls: 'modal-footer',
13775         cn: [
13776             {
13777                 tag: 'div',
13778                 cls: 'row',
13779                 cn: [
13780                     {
13781                         tag: 'div',
13782                         cls: 'col-xs-6 text-left',
13783                         cn: {
13784                             tag: 'button',
13785                             cls: 'btn btn-danger roo-touch-view-cancel',
13786                             html: 'Cancel'
13787                         }
13788                     },
13789                     {
13790                         tag: 'div',
13791                         cls: 'col-xs-6 text-right',
13792                         cn: {
13793                             tag: 'button',
13794                             cls: 'btn btn-success roo-touch-view-ok',
13795                             html: 'OK'
13796                         }
13797                     }
13798                 ]
13799             }
13800         ]
13801         
13802     }
13803 });
13804
13805 Roo.apply(Roo.bootstrap.ComboBox,  {
13806     
13807     touchViewTemplate : {
13808         tag: 'div',
13809         cls: 'modal fade roo-combobox-touch-view',
13810         cn: [
13811             {
13812                 tag: 'div',
13813                 cls: 'modal-dialog',
13814                 cn: [
13815                     {
13816                         tag: 'div',
13817                         cls: 'modal-content',
13818                         cn: [
13819                             Roo.bootstrap.ComboBox.header,
13820                             Roo.bootstrap.ComboBox.body,
13821                             Roo.bootstrap.ComboBox.footer
13822                         ]
13823                     }
13824                 ]
13825             }
13826         ]
13827     }
13828 });/*
13829  * Based on:
13830  * Ext JS Library 1.1.1
13831  * Copyright(c) 2006-2007, Ext JS, LLC.
13832  *
13833  * Originally Released Under LGPL - original licence link has changed is not relivant.
13834  *
13835  * Fork - LGPL
13836  * <script type="text/javascript">
13837  */
13838
13839 /**
13840  * @class Roo.View
13841  * @extends Roo.util.Observable
13842  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13843  * This class also supports single and multi selection modes. <br>
13844  * Create a data model bound view:
13845  <pre><code>
13846  var store = new Roo.data.Store(...);
13847
13848  var view = new Roo.View({
13849     el : "my-element",
13850     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13851  
13852     singleSelect: true,
13853     selectedClass: "ydataview-selected",
13854     store: store
13855  });
13856
13857  // listen for node click?
13858  view.on("click", function(vw, index, node, e){
13859  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13860  });
13861
13862  // load XML data
13863  dataModel.load("foobar.xml");
13864  </code></pre>
13865  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13866  * <br><br>
13867  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13868  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13869  * 
13870  * Note: old style constructor is still suported (container, template, config)
13871  * 
13872  * @constructor
13873  * Create a new View
13874  * @param {Object} config The config object
13875  * 
13876  */
13877 Roo.View = function(config, depreciated_tpl, depreciated_config){
13878     
13879     this.parent = false;
13880     
13881     if (typeof(depreciated_tpl) == 'undefined') {
13882         // new way.. - universal constructor.
13883         Roo.apply(this, config);
13884         this.el  = Roo.get(this.el);
13885     } else {
13886         // old format..
13887         this.el  = Roo.get(config);
13888         this.tpl = depreciated_tpl;
13889         Roo.apply(this, depreciated_config);
13890     }
13891     this.wrapEl  = this.el.wrap().wrap();
13892     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13893     
13894     
13895     if(typeof(this.tpl) == "string"){
13896         this.tpl = new Roo.Template(this.tpl);
13897     } else {
13898         // support xtype ctors..
13899         this.tpl = new Roo.factory(this.tpl, Roo);
13900     }
13901     
13902     
13903     this.tpl.compile();
13904     
13905     /** @private */
13906     this.addEvents({
13907         /**
13908          * @event beforeclick
13909          * Fires before a click is processed. Returns false to cancel the default action.
13910          * @param {Roo.View} this
13911          * @param {Number} index The index of the target node
13912          * @param {HTMLElement} node The target node
13913          * @param {Roo.EventObject} e The raw event object
13914          */
13915             "beforeclick" : true,
13916         /**
13917          * @event click
13918          * Fires when a template node is clicked.
13919          * @param {Roo.View} this
13920          * @param {Number} index The index of the target node
13921          * @param {HTMLElement} node The target node
13922          * @param {Roo.EventObject} e The raw event object
13923          */
13924             "click" : true,
13925         /**
13926          * @event dblclick
13927          * Fires when a template node is double clicked.
13928          * @param {Roo.View} this
13929          * @param {Number} index The index of the target node
13930          * @param {HTMLElement} node The target node
13931          * @param {Roo.EventObject} e The raw event object
13932          */
13933             "dblclick" : true,
13934         /**
13935          * @event contextmenu
13936          * Fires when a template node is right clicked.
13937          * @param {Roo.View} this
13938          * @param {Number} index The index of the target node
13939          * @param {HTMLElement} node The target node
13940          * @param {Roo.EventObject} e The raw event object
13941          */
13942             "contextmenu" : true,
13943         /**
13944          * @event selectionchange
13945          * Fires when the selected nodes change.
13946          * @param {Roo.View} this
13947          * @param {Array} selections Array of the selected nodes
13948          */
13949             "selectionchange" : true,
13950     
13951         /**
13952          * @event beforeselect
13953          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13954          * @param {Roo.View} this
13955          * @param {HTMLElement} node The node to be selected
13956          * @param {Array} selections Array of currently selected nodes
13957          */
13958             "beforeselect" : true,
13959         /**
13960          * @event preparedata
13961          * Fires on every row to render, to allow you to change the data.
13962          * @param {Roo.View} this
13963          * @param {Object} data to be rendered (change this)
13964          */
13965           "preparedata" : true
13966           
13967           
13968         });
13969
13970
13971
13972     this.el.on({
13973         "click": this.onClick,
13974         "dblclick": this.onDblClick,
13975         "contextmenu": this.onContextMenu,
13976         scope:this
13977     });
13978
13979     this.selections = [];
13980     this.nodes = [];
13981     this.cmp = new Roo.CompositeElementLite([]);
13982     if(this.store){
13983         this.store = Roo.factory(this.store, Roo.data);
13984         this.setStore(this.store, true);
13985     }
13986     
13987     if ( this.footer && this.footer.xtype) {
13988            
13989          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13990         
13991         this.footer.dataSource = this.store;
13992         this.footer.container = fctr;
13993         this.footer = Roo.factory(this.footer, Roo);
13994         fctr.insertFirst(this.el);
13995         
13996         // this is a bit insane - as the paging toolbar seems to detach the el..
13997 //        dom.parentNode.parentNode.parentNode
13998          // they get detached?
13999     }
14000     
14001     
14002     Roo.View.superclass.constructor.call(this);
14003     
14004     
14005 };
14006
14007 Roo.extend(Roo.View, Roo.util.Observable, {
14008     
14009      /**
14010      * @cfg {Roo.data.Store} store Data store to load data from.
14011      */
14012     store : false,
14013     
14014     /**
14015      * @cfg {String|Roo.Element} el The container element.
14016      */
14017     el : '',
14018     
14019     /**
14020      * @cfg {String|Roo.Template} tpl The template used by this View 
14021      */
14022     tpl : false,
14023     /**
14024      * @cfg {String} dataName the named area of the template to use as the data area
14025      *                          Works with domtemplates roo-name="name"
14026      */
14027     dataName: false,
14028     /**
14029      * @cfg {String} selectedClass The css class to add to selected nodes
14030      */
14031     selectedClass : "x-view-selected",
14032      /**
14033      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14034      */
14035     emptyText : "",
14036     
14037     /**
14038      * @cfg {String} text to display on mask (default Loading)
14039      */
14040     mask : false,
14041     /**
14042      * @cfg {Boolean} multiSelect Allow multiple selection
14043      */
14044     multiSelect : false,
14045     /**
14046      * @cfg {Boolean} singleSelect Allow single selection
14047      */
14048     singleSelect:  false,
14049     
14050     /**
14051      * @cfg {Boolean} toggleSelect - selecting 
14052      */
14053     toggleSelect : false,
14054     
14055     /**
14056      * @cfg {Boolean} tickable - selecting 
14057      */
14058     tickable : false,
14059     
14060     /**
14061      * Returns the element this view is bound to.
14062      * @return {Roo.Element}
14063      */
14064     getEl : function(){
14065         return this.wrapEl;
14066     },
14067     
14068     
14069
14070     /**
14071      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14072      */
14073     refresh : function(){
14074         //Roo.log('refresh');
14075         var t = this.tpl;
14076         
14077         // if we are using something like 'domtemplate', then
14078         // the what gets used is:
14079         // t.applySubtemplate(NAME, data, wrapping data..)
14080         // the outer template then get' applied with
14081         //     the store 'extra data'
14082         // and the body get's added to the
14083         //      roo-name="data" node?
14084         //      <span class='roo-tpl-{name}'></span> ?????
14085         
14086         
14087         
14088         this.clearSelections();
14089         this.el.update("");
14090         var html = [];
14091         var records = this.store.getRange();
14092         if(records.length < 1) {
14093             
14094             // is this valid??  = should it render a template??
14095             
14096             this.el.update(this.emptyText);
14097             return;
14098         }
14099         var el = this.el;
14100         if (this.dataName) {
14101             this.el.update(t.apply(this.store.meta)); //????
14102             el = this.el.child('.roo-tpl-' + this.dataName);
14103         }
14104         
14105         for(var i = 0, len = records.length; i < len; i++){
14106             var data = this.prepareData(records[i].data, i, records[i]);
14107             this.fireEvent("preparedata", this, data, i, records[i]);
14108             
14109             var d = Roo.apply({}, data);
14110             
14111             if(this.tickable){
14112                 Roo.apply(d, {'roo-id' : Roo.id()});
14113                 
14114                 var _this = this;
14115             
14116                 Roo.each(this.parent.item, function(item){
14117                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14118                         return;
14119                     }
14120                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14121                 });
14122             }
14123             
14124             html[html.length] = Roo.util.Format.trim(
14125                 this.dataName ?
14126                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14127                     t.apply(d)
14128             );
14129         }
14130         
14131         
14132         
14133         el.update(html.join(""));
14134         this.nodes = el.dom.childNodes;
14135         this.updateIndexes(0);
14136     },
14137     
14138
14139     /**
14140      * Function to override to reformat the data that is sent to
14141      * the template for each node.
14142      * DEPRICATED - use the preparedata event handler.
14143      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14144      * a JSON object for an UpdateManager bound view).
14145      */
14146     prepareData : function(data, index, record)
14147     {
14148         this.fireEvent("preparedata", this, data, index, record);
14149         return data;
14150     },
14151
14152     onUpdate : function(ds, record){
14153         // Roo.log('on update');   
14154         this.clearSelections();
14155         var index = this.store.indexOf(record);
14156         var n = this.nodes[index];
14157         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14158         n.parentNode.removeChild(n);
14159         this.updateIndexes(index, index);
14160     },
14161
14162     
14163     
14164 // --------- FIXME     
14165     onAdd : function(ds, records, index)
14166     {
14167         //Roo.log(['on Add', ds, records, index] );        
14168         this.clearSelections();
14169         if(this.nodes.length == 0){
14170             this.refresh();
14171             return;
14172         }
14173         var n = this.nodes[index];
14174         for(var i = 0, len = records.length; i < len; i++){
14175             var d = this.prepareData(records[i].data, i, records[i]);
14176             if(n){
14177                 this.tpl.insertBefore(n, d);
14178             }else{
14179                 
14180                 this.tpl.append(this.el, d);
14181             }
14182         }
14183         this.updateIndexes(index);
14184     },
14185
14186     onRemove : function(ds, record, index){
14187        // Roo.log('onRemove');
14188         this.clearSelections();
14189         var el = this.dataName  ?
14190             this.el.child('.roo-tpl-' + this.dataName) :
14191             this.el; 
14192         
14193         el.dom.removeChild(this.nodes[index]);
14194         this.updateIndexes(index);
14195     },
14196
14197     /**
14198      * Refresh an individual node.
14199      * @param {Number} index
14200      */
14201     refreshNode : function(index){
14202         this.onUpdate(this.store, this.store.getAt(index));
14203     },
14204
14205     updateIndexes : function(startIndex, endIndex){
14206         var ns = this.nodes;
14207         startIndex = startIndex || 0;
14208         endIndex = endIndex || ns.length - 1;
14209         for(var i = startIndex; i <= endIndex; i++){
14210             ns[i].nodeIndex = i;
14211         }
14212     },
14213
14214     /**
14215      * Changes the data store this view uses and refresh the view.
14216      * @param {Store} store
14217      */
14218     setStore : function(store, initial){
14219         if(!initial && this.store){
14220             this.store.un("datachanged", this.refresh);
14221             this.store.un("add", this.onAdd);
14222             this.store.un("remove", this.onRemove);
14223             this.store.un("update", this.onUpdate);
14224             this.store.un("clear", this.refresh);
14225             this.store.un("beforeload", this.onBeforeLoad);
14226             this.store.un("load", this.onLoad);
14227             this.store.un("loadexception", this.onLoad);
14228         }
14229         if(store){
14230           
14231             store.on("datachanged", this.refresh, this);
14232             store.on("add", this.onAdd, this);
14233             store.on("remove", this.onRemove, this);
14234             store.on("update", this.onUpdate, this);
14235             store.on("clear", this.refresh, this);
14236             store.on("beforeload", this.onBeforeLoad, this);
14237             store.on("load", this.onLoad, this);
14238             store.on("loadexception", this.onLoad, this);
14239         }
14240         
14241         if(store){
14242             this.refresh();
14243         }
14244     },
14245     /**
14246      * onbeforeLoad - masks the loading area.
14247      *
14248      */
14249     onBeforeLoad : function(store,opts)
14250     {
14251          //Roo.log('onBeforeLoad');   
14252         if (!opts.add) {
14253             this.el.update("");
14254         }
14255         this.el.mask(this.mask ? this.mask : "Loading" ); 
14256     },
14257     onLoad : function ()
14258     {
14259         this.el.unmask();
14260     },
14261     
14262
14263     /**
14264      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14265      * @param {HTMLElement} node
14266      * @return {HTMLElement} The template node
14267      */
14268     findItemFromChild : function(node){
14269         var el = this.dataName  ?
14270             this.el.child('.roo-tpl-' + this.dataName,true) :
14271             this.el.dom; 
14272         
14273         if(!node || node.parentNode == el){
14274                     return node;
14275             }
14276             var p = node.parentNode;
14277             while(p && p != el){
14278             if(p.parentNode == el){
14279                 return p;
14280             }
14281             p = p.parentNode;
14282         }
14283             return null;
14284     },
14285
14286     /** @ignore */
14287     onClick : function(e){
14288         var item = this.findItemFromChild(e.getTarget());
14289         if(item){
14290             var index = this.indexOf(item);
14291             if(this.onItemClick(item, index, e) !== false){
14292                 this.fireEvent("click", this, index, item, e);
14293             }
14294         }else{
14295             this.clearSelections();
14296         }
14297     },
14298
14299     /** @ignore */
14300     onContextMenu : function(e){
14301         var item = this.findItemFromChild(e.getTarget());
14302         if(item){
14303             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14304         }
14305     },
14306
14307     /** @ignore */
14308     onDblClick : function(e){
14309         var item = this.findItemFromChild(e.getTarget());
14310         if(item){
14311             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14312         }
14313     },
14314
14315     onItemClick : function(item, index, e)
14316     {
14317         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14318             return false;
14319         }
14320         if (this.toggleSelect) {
14321             var m = this.isSelected(item) ? 'unselect' : 'select';
14322             //Roo.log(m);
14323             var _t = this;
14324             _t[m](item, true, false);
14325             return true;
14326         }
14327         if(this.multiSelect || this.singleSelect){
14328             if(this.multiSelect && e.shiftKey && this.lastSelection){
14329                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14330             }else{
14331                 this.select(item, this.multiSelect && e.ctrlKey);
14332                 this.lastSelection = item;
14333             }
14334             
14335             if(!this.tickable){
14336                 e.preventDefault();
14337             }
14338             
14339         }
14340         return true;
14341     },
14342
14343     /**
14344      * Get the number of selected nodes.
14345      * @return {Number}
14346      */
14347     getSelectionCount : function(){
14348         return this.selections.length;
14349     },
14350
14351     /**
14352      * Get the currently selected nodes.
14353      * @return {Array} An array of HTMLElements
14354      */
14355     getSelectedNodes : function(){
14356         return this.selections;
14357     },
14358
14359     /**
14360      * Get the indexes of the selected nodes.
14361      * @return {Array}
14362      */
14363     getSelectedIndexes : function(){
14364         var indexes = [], s = this.selections;
14365         for(var i = 0, len = s.length; i < len; i++){
14366             indexes.push(s[i].nodeIndex);
14367         }
14368         return indexes;
14369     },
14370
14371     /**
14372      * Clear all selections
14373      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14374      */
14375     clearSelections : function(suppressEvent){
14376         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14377             this.cmp.elements = this.selections;
14378             this.cmp.removeClass(this.selectedClass);
14379             this.selections = [];
14380             if(!suppressEvent){
14381                 this.fireEvent("selectionchange", this, this.selections);
14382             }
14383         }
14384     },
14385
14386     /**
14387      * Returns true if the passed node is selected
14388      * @param {HTMLElement/Number} node The node or node index
14389      * @return {Boolean}
14390      */
14391     isSelected : function(node){
14392         var s = this.selections;
14393         if(s.length < 1){
14394             return false;
14395         }
14396         node = this.getNode(node);
14397         return s.indexOf(node) !== -1;
14398     },
14399
14400     /**
14401      * Selects nodes.
14402      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
14403      * @param {Boolean} keepExisting (optional) true to keep existing selections
14404      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14405      */
14406     select : function(nodeInfo, keepExisting, suppressEvent){
14407         if(nodeInfo instanceof Array){
14408             if(!keepExisting){
14409                 this.clearSelections(true);
14410             }
14411             for(var i = 0, len = nodeInfo.length; i < len; i++){
14412                 this.select(nodeInfo[i], true, true);
14413             }
14414             return;
14415         } 
14416         var node = this.getNode(nodeInfo);
14417         if(!node || this.isSelected(node)){
14418             return; // already selected.
14419         }
14420         if(!keepExisting){
14421             this.clearSelections(true);
14422         }
14423         
14424         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14425             Roo.fly(node).addClass(this.selectedClass);
14426             this.selections.push(node);
14427             if(!suppressEvent){
14428                 this.fireEvent("selectionchange", this, this.selections);
14429             }
14430         }
14431         
14432         
14433     },
14434       /**
14435      * Unselects nodes.
14436      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
14437      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14438      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14439      */
14440     unselect : function(nodeInfo, keepExisting, suppressEvent)
14441     {
14442         if(nodeInfo instanceof Array){
14443             Roo.each(this.selections, function(s) {
14444                 this.unselect(s, nodeInfo);
14445             }, this);
14446             return;
14447         }
14448         var node = this.getNode(nodeInfo);
14449         if(!node || !this.isSelected(node)){
14450             //Roo.log("not selected");
14451             return; // not selected.
14452         }
14453         // fireevent???
14454         var ns = [];
14455         Roo.each(this.selections, function(s) {
14456             if (s == node ) {
14457                 Roo.fly(node).removeClass(this.selectedClass);
14458
14459                 return;
14460             }
14461             ns.push(s);
14462         },this);
14463         
14464         this.selections= ns;
14465         this.fireEvent("selectionchange", this, this.selections);
14466     },
14467
14468     /**
14469      * Gets a template node.
14470      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14471      * @return {HTMLElement} The node or null if it wasn't found
14472      */
14473     getNode : function(nodeInfo){
14474         if(typeof nodeInfo == "string"){
14475             return document.getElementById(nodeInfo);
14476         }else if(typeof nodeInfo == "number"){
14477             return this.nodes[nodeInfo];
14478         }
14479         return nodeInfo;
14480     },
14481
14482     /**
14483      * Gets a range template nodes.
14484      * @param {Number} startIndex
14485      * @param {Number} endIndex
14486      * @return {Array} An array of nodes
14487      */
14488     getNodes : function(start, end){
14489         var ns = this.nodes;
14490         start = start || 0;
14491         end = typeof end == "undefined" ? ns.length - 1 : end;
14492         var nodes = [];
14493         if(start <= end){
14494             for(var i = start; i <= end; i++){
14495                 nodes.push(ns[i]);
14496             }
14497         } else{
14498             for(var i = start; i >= end; i--){
14499                 nodes.push(ns[i]);
14500             }
14501         }
14502         return nodes;
14503     },
14504
14505     /**
14506      * Finds the index of the passed node
14507      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14508      * @return {Number} The index of the node or -1
14509      */
14510     indexOf : function(node){
14511         node = this.getNode(node);
14512         if(typeof node.nodeIndex == "number"){
14513             return node.nodeIndex;
14514         }
14515         var ns = this.nodes;
14516         for(var i = 0, len = ns.length; i < len; i++){
14517             if(ns[i] == node){
14518                 return i;
14519             }
14520         }
14521         return -1;
14522     }
14523 });
14524 /*
14525  * - LGPL
14526  *
14527  * based on jquery fullcalendar
14528  * 
14529  */
14530
14531 Roo.bootstrap = Roo.bootstrap || {};
14532 /**
14533  * @class Roo.bootstrap.Calendar
14534  * @extends Roo.bootstrap.Component
14535  * Bootstrap Calendar class
14536  * @cfg {Boolean} loadMask (true|false) default false
14537  * @cfg {Object} header generate the user specific header of the calendar, default false
14538
14539  * @constructor
14540  * Create a new Container
14541  * @param {Object} config The config object
14542  */
14543
14544
14545
14546 Roo.bootstrap.Calendar = function(config){
14547     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14548      this.addEvents({
14549         /**
14550              * @event select
14551              * Fires when a date is selected
14552              * @param {DatePicker} this
14553              * @param {Date} date The selected date
14554              */
14555         'select': true,
14556         /**
14557              * @event monthchange
14558              * Fires when the displayed month changes 
14559              * @param {DatePicker} this
14560              * @param {Date} date The selected month
14561              */
14562         'monthchange': true,
14563         /**
14564              * @event evententer
14565              * Fires when mouse over an event
14566              * @param {Calendar} this
14567              * @param {event} Event
14568              */
14569         'evententer': true,
14570         /**
14571              * @event eventleave
14572              * Fires when the mouse leaves an
14573              * @param {Calendar} this
14574              * @param {event}
14575              */
14576         'eventleave': true,
14577         /**
14578              * @event eventclick
14579              * Fires when the mouse click an
14580              * @param {Calendar} this
14581              * @param {event}
14582              */
14583         'eventclick': true
14584         
14585     });
14586
14587 };
14588
14589 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14590     
14591      /**
14592      * @cfg {Number} startDay
14593      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14594      */
14595     startDay : 0,
14596     
14597     loadMask : false,
14598     
14599     header : false,
14600       
14601     getAutoCreate : function(){
14602         
14603         
14604         var fc_button = function(name, corner, style, content ) {
14605             return Roo.apply({},{
14606                 tag : 'span',
14607                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14608                          (corner.length ?
14609                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14610                             ''
14611                         ),
14612                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14613                 unselectable: 'on'
14614             });
14615         };
14616         
14617         var header = {};
14618         
14619         if(!this.header){
14620             header = {
14621                 tag : 'table',
14622                 cls : 'fc-header',
14623                 style : 'width:100%',
14624                 cn : [
14625                     {
14626                         tag: 'tr',
14627                         cn : [
14628                             {
14629                                 tag : 'td',
14630                                 cls : 'fc-header-left',
14631                                 cn : [
14632                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14633                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14634                                     { tag: 'span', cls: 'fc-header-space' },
14635                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14636
14637
14638                                 ]
14639                             },
14640
14641                             {
14642                                 tag : 'td',
14643                                 cls : 'fc-header-center',
14644                                 cn : [
14645                                     {
14646                                         tag: 'span',
14647                                         cls: 'fc-header-title',
14648                                         cn : {
14649                                             tag: 'H2',
14650                                             html : 'month / year'
14651                                         }
14652                                     }
14653
14654                                 ]
14655                             },
14656                             {
14657                                 tag : 'td',
14658                                 cls : 'fc-header-right',
14659                                 cn : [
14660                               /*      fc_button('month', 'left', '', 'month' ),
14661                                     fc_button('week', '', '', 'week' ),
14662                                     fc_button('day', 'right', '', 'day' )
14663                                 */    
14664
14665                                 ]
14666                             }
14667
14668                         ]
14669                     }
14670                 ]
14671             };
14672         }
14673         
14674         header = this.header;
14675         
14676        
14677         var cal_heads = function() {
14678             var ret = [];
14679             // fixme - handle this.
14680             
14681             for (var i =0; i < Date.dayNames.length; i++) {
14682                 var d = Date.dayNames[i];
14683                 ret.push({
14684                     tag: 'th',
14685                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14686                     html : d.substring(0,3)
14687                 });
14688                 
14689             }
14690             ret[0].cls += ' fc-first';
14691             ret[6].cls += ' fc-last';
14692             return ret;
14693         };
14694         var cal_cell = function(n) {
14695             return  {
14696                 tag: 'td',
14697                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14698                 cn : [
14699                     {
14700                         cn : [
14701                             {
14702                                 cls: 'fc-day-number',
14703                                 html: 'D'
14704                             },
14705                             {
14706                                 cls: 'fc-day-content',
14707                              
14708                                 cn : [
14709                                      {
14710                                         style: 'position: relative;' // height: 17px;
14711                                     }
14712                                 ]
14713                             }
14714                             
14715                             
14716                         ]
14717                     }
14718                 ]
14719                 
14720             }
14721         };
14722         var cal_rows = function() {
14723             
14724             var ret = [];
14725             for (var r = 0; r < 6; r++) {
14726                 var row= {
14727                     tag : 'tr',
14728                     cls : 'fc-week',
14729                     cn : []
14730                 };
14731                 
14732                 for (var i =0; i < Date.dayNames.length; i++) {
14733                     var d = Date.dayNames[i];
14734                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14735
14736                 }
14737                 row.cn[0].cls+=' fc-first';
14738                 row.cn[0].cn[0].style = 'min-height:90px';
14739                 row.cn[6].cls+=' fc-last';
14740                 ret.push(row);
14741                 
14742             }
14743             ret[0].cls += ' fc-first';
14744             ret[4].cls += ' fc-prev-last';
14745             ret[5].cls += ' fc-last';
14746             return ret;
14747             
14748         };
14749         
14750         var cal_table = {
14751             tag: 'table',
14752             cls: 'fc-border-separate',
14753             style : 'width:100%',
14754             cellspacing  : 0,
14755             cn : [
14756                 { 
14757                     tag: 'thead',
14758                     cn : [
14759                         { 
14760                             tag: 'tr',
14761                             cls : 'fc-first fc-last',
14762                             cn : cal_heads()
14763                         }
14764                     ]
14765                 },
14766                 { 
14767                     tag: 'tbody',
14768                     cn : cal_rows()
14769                 }
14770                   
14771             ]
14772         };
14773          
14774          var cfg = {
14775             cls : 'fc fc-ltr',
14776             cn : [
14777                 header,
14778                 {
14779                     cls : 'fc-content',
14780                     style : "position: relative;",
14781                     cn : [
14782                         {
14783                             cls : 'fc-view fc-view-month fc-grid',
14784                             style : 'position: relative',
14785                             unselectable : 'on',
14786                             cn : [
14787                                 {
14788                                     cls : 'fc-event-container',
14789                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14790                                 },
14791                                 cal_table
14792                             ]
14793                         }
14794                     ]
14795     
14796                 }
14797            ] 
14798             
14799         };
14800         
14801          
14802         
14803         return cfg;
14804     },
14805     
14806     
14807     initEvents : function()
14808     {
14809         if(!this.store){
14810             throw "can not find store for calendar";
14811         }
14812         
14813         var mark = {
14814             tag: "div",
14815             cls:"x-dlg-mask",
14816             style: "text-align:center",
14817             cn: [
14818                 {
14819                     tag: "div",
14820                     style: "background-color:white;width:50%;margin:250 auto",
14821                     cn: [
14822                         {
14823                             tag: "img",
14824                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14825                         },
14826                         {
14827                             tag: "span",
14828                             html: "Loading"
14829                         }
14830                         
14831                     ]
14832                 }
14833             ]
14834         };
14835         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14836         
14837         var size = this.el.select('.fc-content', true).first().getSize();
14838         this.maskEl.setSize(size.width, size.height);
14839         this.maskEl.enableDisplayMode("block");
14840         if(!this.loadMask){
14841             this.maskEl.hide();
14842         }
14843         
14844         this.store = Roo.factory(this.store, Roo.data);
14845         this.store.on('load', this.onLoad, this);
14846         this.store.on('beforeload', this.onBeforeLoad, this);
14847         
14848         this.resize();
14849         
14850         this.cells = this.el.select('.fc-day',true);
14851         //Roo.log(this.cells);
14852         this.textNodes = this.el.query('.fc-day-number');
14853         this.cells.addClassOnOver('fc-state-hover');
14854         
14855         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14856         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14857         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14858         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14859         
14860         this.on('monthchange', this.onMonthChange, this);
14861         
14862         this.update(new Date().clearTime());
14863     },
14864     
14865     resize : function() {
14866         var sz  = this.el.getSize();
14867         
14868         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14869         this.el.select('.fc-day-content div',true).setHeight(34);
14870     },
14871     
14872     
14873     // private
14874     showPrevMonth : function(e){
14875         this.update(this.activeDate.add("mo", -1));
14876     },
14877     showToday : function(e){
14878         this.update(new Date().clearTime());
14879     },
14880     // private
14881     showNextMonth : function(e){
14882         this.update(this.activeDate.add("mo", 1));
14883     },
14884
14885     // private
14886     showPrevYear : function(){
14887         this.update(this.activeDate.add("y", -1));
14888     },
14889
14890     // private
14891     showNextYear : function(){
14892         this.update(this.activeDate.add("y", 1));
14893     },
14894
14895     
14896    // private
14897     update : function(date)
14898     {
14899         var vd = this.activeDate;
14900         this.activeDate = date;
14901 //        if(vd && this.el){
14902 //            var t = date.getTime();
14903 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14904 //                Roo.log('using add remove');
14905 //                
14906 //                this.fireEvent('monthchange', this, date);
14907 //                
14908 //                this.cells.removeClass("fc-state-highlight");
14909 //                this.cells.each(function(c){
14910 //                   if(c.dateValue == t){
14911 //                       c.addClass("fc-state-highlight");
14912 //                       setTimeout(function(){
14913 //                            try{c.dom.firstChild.focus();}catch(e){}
14914 //                       }, 50);
14915 //                       return false;
14916 //                   }
14917 //                   return true;
14918 //                });
14919 //                return;
14920 //            }
14921 //        }
14922         
14923         var days = date.getDaysInMonth();
14924         
14925         var firstOfMonth = date.getFirstDateOfMonth();
14926         var startingPos = firstOfMonth.getDay()-this.startDay;
14927         
14928         if(startingPos < this.startDay){
14929             startingPos += 7;
14930         }
14931         
14932         var pm = date.add(Date.MONTH, -1);
14933         var prevStart = pm.getDaysInMonth()-startingPos;
14934 //        
14935         this.cells = this.el.select('.fc-day',true);
14936         this.textNodes = this.el.query('.fc-day-number');
14937         this.cells.addClassOnOver('fc-state-hover');
14938         
14939         var cells = this.cells.elements;
14940         var textEls = this.textNodes;
14941         
14942         Roo.each(cells, function(cell){
14943             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14944         });
14945         
14946         days += startingPos;
14947
14948         // convert everything to numbers so it's fast
14949         var day = 86400000;
14950         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14951         //Roo.log(d);
14952         //Roo.log(pm);
14953         //Roo.log(prevStart);
14954         
14955         var today = new Date().clearTime().getTime();
14956         var sel = date.clearTime().getTime();
14957         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14958         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14959         var ddMatch = this.disabledDatesRE;
14960         var ddText = this.disabledDatesText;
14961         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14962         var ddaysText = this.disabledDaysText;
14963         var format = this.format;
14964         
14965         var setCellClass = function(cal, cell){
14966             cell.row = 0;
14967             cell.events = [];
14968             cell.more = [];
14969             //Roo.log('set Cell Class');
14970             cell.title = "";
14971             var t = d.getTime();
14972             
14973             //Roo.log(d);
14974             
14975             cell.dateValue = t;
14976             if(t == today){
14977                 cell.className += " fc-today";
14978                 cell.className += " fc-state-highlight";
14979                 cell.title = cal.todayText;
14980             }
14981             if(t == sel){
14982                 // disable highlight in other month..
14983                 //cell.className += " fc-state-highlight";
14984                 
14985             }
14986             // disabling
14987             if(t < min) {
14988                 cell.className = " fc-state-disabled";
14989                 cell.title = cal.minText;
14990                 return;
14991             }
14992             if(t > max) {
14993                 cell.className = " fc-state-disabled";
14994                 cell.title = cal.maxText;
14995                 return;
14996             }
14997             if(ddays){
14998                 if(ddays.indexOf(d.getDay()) != -1){
14999                     cell.title = ddaysText;
15000                     cell.className = " fc-state-disabled";
15001                 }
15002             }
15003             if(ddMatch && format){
15004                 var fvalue = d.dateFormat(format);
15005                 if(ddMatch.test(fvalue)){
15006                     cell.title = ddText.replace("%0", fvalue);
15007                     cell.className = " fc-state-disabled";
15008                 }
15009             }
15010             
15011             if (!cell.initialClassName) {
15012                 cell.initialClassName = cell.dom.className;
15013             }
15014             
15015             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15016         };
15017
15018         var i = 0;
15019         
15020         for(; i < startingPos; i++) {
15021             textEls[i].innerHTML = (++prevStart);
15022             d.setDate(d.getDate()+1);
15023             
15024             cells[i].className = "fc-past fc-other-month";
15025             setCellClass(this, cells[i]);
15026         }
15027         
15028         var intDay = 0;
15029         
15030         for(; i < days; i++){
15031             intDay = i - startingPos + 1;
15032             textEls[i].innerHTML = (intDay);
15033             d.setDate(d.getDate()+1);
15034             
15035             cells[i].className = ''; // "x-date-active";
15036             setCellClass(this, cells[i]);
15037         }
15038         var extraDays = 0;
15039         
15040         for(; i < 42; i++) {
15041             textEls[i].innerHTML = (++extraDays);
15042             d.setDate(d.getDate()+1);
15043             
15044             cells[i].className = "fc-future fc-other-month";
15045             setCellClass(this, cells[i]);
15046         }
15047         
15048         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15049         
15050         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15051         
15052         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15053         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15054         
15055         if(totalRows != 6){
15056             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15057             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15058         }
15059         
15060         this.fireEvent('monthchange', this, date);
15061         
15062         
15063         /*
15064         if(!this.internalRender){
15065             var main = this.el.dom.firstChild;
15066             var w = main.offsetWidth;
15067             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15068             Roo.fly(main).setWidth(w);
15069             this.internalRender = true;
15070             // opera does not respect the auto grow header center column
15071             // then, after it gets a width opera refuses to recalculate
15072             // without a second pass
15073             if(Roo.isOpera && !this.secondPass){
15074                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15075                 this.secondPass = true;
15076                 this.update.defer(10, this, [date]);
15077             }
15078         }
15079         */
15080         
15081     },
15082     
15083     findCell : function(dt) {
15084         dt = dt.clearTime().getTime();
15085         var ret = false;
15086         this.cells.each(function(c){
15087             //Roo.log("check " +c.dateValue + '?=' + dt);
15088             if(c.dateValue == dt){
15089                 ret = c;
15090                 return false;
15091             }
15092             return true;
15093         });
15094         
15095         return ret;
15096     },
15097     
15098     findCells : function(ev) {
15099         var s = ev.start.clone().clearTime().getTime();
15100        // Roo.log(s);
15101         var e= ev.end.clone().clearTime().getTime();
15102        // Roo.log(e);
15103         var ret = [];
15104         this.cells.each(function(c){
15105              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15106             
15107             if(c.dateValue > e){
15108                 return ;
15109             }
15110             if(c.dateValue < s){
15111                 return ;
15112             }
15113             ret.push(c);
15114         });
15115         
15116         return ret;    
15117     },
15118     
15119 //    findBestRow: function(cells)
15120 //    {
15121 //        var ret = 0;
15122 //        
15123 //        for (var i =0 ; i < cells.length;i++) {
15124 //            ret  = Math.max(cells[i].rows || 0,ret);
15125 //        }
15126 //        return ret;
15127 //        
15128 //    },
15129     
15130     
15131     addItem : function(ev)
15132     {
15133         // look for vertical location slot in
15134         var cells = this.findCells(ev);
15135         
15136 //        ev.row = this.findBestRow(cells);
15137         
15138         // work out the location.
15139         
15140         var crow = false;
15141         var rows = [];
15142         for(var i =0; i < cells.length; i++) {
15143             
15144             cells[i].row = cells[0].row;
15145             
15146             if(i == 0){
15147                 cells[i].row = cells[i].row + 1;
15148             }
15149             
15150             if (!crow) {
15151                 crow = {
15152                     start : cells[i],
15153                     end :  cells[i]
15154                 };
15155                 continue;
15156             }
15157             if (crow.start.getY() == cells[i].getY()) {
15158                 // on same row.
15159                 crow.end = cells[i];
15160                 continue;
15161             }
15162             // different row.
15163             rows.push(crow);
15164             crow = {
15165                 start: cells[i],
15166                 end : cells[i]
15167             };
15168             
15169         }
15170         
15171         rows.push(crow);
15172         ev.els = [];
15173         ev.rows = rows;
15174         ev.cells = cells;
15175         
15176         cells[0].events.push(ev);
15177         
15178         this.calevents.push(ev);
15179     },
15180     
15181     clearEvents: function() {
15182         
15183         if(!this.calevents){
15184             return;
15185         }
15186         
15187         Roo.each(this.cells.elements, function(c){
15188             c.row = 0;
15189             c.events = [];
15190             c.more = [];
15191         });
15192         
15193         Roo.each(this.calevents, function(e) {
15194             Roo.each(e.els, function(el) {
15195                 el.un('mouseenter' ,this.onEventEnter, this);
15196                 el.un('mouseleave' ,this.onEventLeave, this);
15197                 el.remove();
15198             },this);
15199         },this);
15200         
15201         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15202             e.remove();
15203         });
15204         
15205     },
15206     
15207     renderEvents: function()
15208     {   
15209         var _this = this;
15210         
15211         this.cells.each(function(c) {
15212             
15213             if(c.row < 5){
15214                 return;
15215             }
15216             
15217             var ev = c.events;
15218             
15219             var r = 4;
15220             if(c.row != c.events.length){
15221                 r = 4 - (4 - (c.row - c.events.length));
15222             }
15223             
15224             c.events = ev.slice(0, r);
15225             c.more = ev.slice(r);
15226             
15227             if(c.more.length && c.more.length == 1){
15228                 c.events.push(c.more.pop());
15229             }
15230             
15231             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15232             
15233         });
15234             
15235         this.cells.each(function(c) {
15236             
15237             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15238             
15239             
15240             for (var e = 0; e < c.events.length; e++){
15241                 var ev = c.events[e];
15242                 var rows = ev.rows;
15243                 
15244                 for(var i = 0; i < rows.length; i++) {
15245                 
15246                     // how many rows should it span..
15247
15248                     var  cfg = {
15249                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15250                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15251
15252                         unselectable : "on",
15253                         cn : [
15254                             {
15255                                 cls: 'fc-event-inner',
15256                                 cn : [
15257     //                                {
15258     //                                  tag:'span',
15259     //                                  cls: 'fc-event-time',
15260     //                                  html : cells.length > 1 ? '' : ev.time
15261     //                                },
15262                                     {
15263                                       tag:'span',
15264                                       cls: 'fc-event-title',
15265                                       html : String.format('{0}', ev.title)
15266                                     }
15267
15268
15269                                 ]
15270                             },
15271                             {
15272                                 cls: 'ui-resizable-handle ui-resizable-e',
15273                                 html : '&nbsp;&nbsp;&nbsp'
15274                             }
15275
15276                         ]
15277                     };
15278
15279                     if (i == 0) {
15280                         cfg.cls += ' fc-event-start';
15281                     }
15282                     if ((i+1) == rows.length) {
15283                         cfg.cls += ' fc-event-end';
15284                     }
15285
15286                     var ctr = _this.el.select('.fc-event-container',true).first();
15287                     var cg = ctr.createChild(cfg);
15288
15289                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15290                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15291
15292                     var r = (c.more.length) ? 1 : 0;
15293                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15294                     cg.setWidth(ebox.right - sbox.x -2);
15295
15296                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15297                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15298                     cg.on('click', _this.onEventClick, _this, ev);
15299
15300                     ev.els.push(cg);
15301                     
15302                 }
15303                 
15304             }
15305             
15306             
15307             if(c.more.length){
15308                 var  cfg = {
15309                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15310                     style : 'position: absolute',
15311                     unselectable : "on",
15312                     cn : [
15313                         {
15314                             cls: 'fc-event-inner',
15315                             cn : [
15316                                 {
15317                                   tag:'span',
15318                                   cls: 'fc-event-title',
15319                                   html : 'More'
15320                                 }
15321
15322
15323                             ]
15324                         },
15325                         {
15326                             cls: 'ui-resizable-handle ui-resizable-e',
15327                             html : '&nbsp;&nbsp;&nbsp'
15328                         }
15329
15330                     ]
15331                 };
15332
15333                 var ctr = _this.el.select('.fc-event-container',true).first();
15334                 var cg = ctr.createChild(cfg);
15335
15336                 var sbox = c.select('.fc-day-content',true).first().getBox();
15337                 var ebox = c.select('.fc-day-content',true).first().getBox();
15338                 //Roo.log(cg);
15339                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15340                 cg.setWidth(ebox.right - sbox.x -2);
15341
15342                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15343                 
15344             }
15345             
15346         });
15347         
15348         
15349         
15350     },
15351     
15352     onEventEnter: function (e, el,event,d) {
15353         this.fireEvent('evententer', this, el, event);
15354     },
15355     
15356     onEventLeave: function (e, el,event,d) {
15357         this.fireEvent('eventleave', this, el, event);
15358     },
15359     
15360     onEventClick: function (e, el,event,d) {
15361         this.fireEvent('eventclick', this, el, event);
15362     },
15363     
15364     onMonthChange: function () {
15365         this.store.load();
15366     },
15367     
15368     onMoreEventClick: function(e, el, more)
15369     {
15370         var _this = this;
15371         
15372         this.calpopover.placement = 'right';
15373         this.calpopover.setTitle('More');
15374         
15375         this.calpopover.setContent('');
15376         
15377         var ctr = this.calpopover.el.select('.popover-content', true).first();
15378         
15379         Roo.each(more, function(m){
15380             var cfg = {
15381                 cls : 'fc-event-hori fc-event-draggable',
15382                 html : m.title
15383             };
15384             var cg = ctr.createChild(cfg);
15385             
15386             cg.on('click', _this.onEventClick, _this, m);
15387         });
15388         
15389         this.calpopover.show(el);
15390         
15391         
15392     },
15393     
15394     onLoad: function () 
15395     {   
15396         this.calevents = [];
15397         var cal = this;
15398         
15399         if(this.store.getCount() > 0){
15400             this.store.data.each(function(d){
15401                cal.addItem({
15402                     id : d.data.id,
15403                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15404                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15405                     time : d.data.start_time,
15406                     title : d.data.title,
15407                     description : d.data.description,
15408                     venue : d.data.venue
15409                 });
15410             });
15411         }
15412         
15413         this.renderEvents();
15414         
15415         if(this.calevents.length && this.loadMask){
15416             this.maskEl.hide();
15417         }
15418     },
15419     
15420     onBeforeLoad: function()
15421     {
15422         this.clearEvents();
15423         if(this.loadMask){
15424             this.maskEl.show();
15425         }
15426     }
15427 });
15428
15429  
15430  /*
15431  * - LGPL
15432  *
15433  * element
15434  * 
15435  */
15436
15437 /**
15438  * @class Roo.bootstrap.Popover
15439  * @extends Roo.bootstrap.Component
15440  * Bootstrap Popover class
15441  * @cfg {String} html contents of the popover   (or false to use children..)
15442  * @cfg {String} title of popover (or false to hide)
15443  * @cfg {String} placement how it is placed
15444  * @cfg {String} trigger click || hover (or false to trigger manually)
15445  * @cfg {String} over what (parent or false to trigger manually.)
15446  * @cfg {Number} delay - delay before showing
15447  
15448  * @constructor
15449  * Create a new Popover
15450  * @param {Object} config The config object
15451  */
15452
15453 Roo.bootstrap.Popover = function(config){
15454     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15455 };
15456
15457 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15458     
15459     title: 'Fill in a title',
15460     html: false,
15461     
15462     placement : 'right',
15463     trigger : 'hover', // hover
15464     
15465     delay : 0,
15466     
15467     over: 'parent',
15468     
15469     can_build_overlaid : false,
15470     
15471     getChildContainer : function()
15472     {
15473         return this.el.select('.popover-content',true).first();
15474     },
15475     
15476     getAutoCreate : function(){
15477          Roo.log('make popover?');
15478         var cfg = {
15479            cls : 'popover roo-dynamic',
15480            style: 'display:block',
15481            cn : [
15482                 {
15483                     cls : 'arrow'
15484                 },
15485                 {
15486                     cls : 'popover-inner',
15487                     cn : [
15488                         {
15489                             tag: 'h3',
15490                             cls: 'popover-title',
15491                             html : this.title
15492                         },
15493                         {
15494                             cls : 'popover-content',
15495                             html : this.html
15496                         }
15497                     ]
15498                     
15499                 }
15500            ]
15501         };
15502         
15503         return cfg;
15504     },
15505     setTitle: function(str)
15506     {
15507         this.title = str;
15508         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15509     },
15510     setContent: function(str)
15511     {
15512         this.html = str;
15513         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15514     },
15515     // as it get's added to the bottom of the page.
15516     onRender : function(ct, position)
15517     {
15518         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15519         if(!this.el){
15520             var cfg = Roo.apply({},  this.getAutoCreate());
15521             cfg.id = Roo.id();
15522             
15523             if (this.cls) {
15524                 cfg.cls += ' ' + this.cls;
15525             }
15526             if (this.style) {
15527                 cfg.style = this.style;
15528             }
15529             Roo.log("adding to ")
15530             this.el = Roo.get(document.body).createChild(cfg, position);
15531             Roo.log(this.el);
15532         }
15533         this.initEvents();
15534     },
15535     
15536     initEvents : function()
15537     {
15538         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15539         this.el.enableDisplayMode('block');
15540         this.el.hide();
15541         if (this.over === false) {
15542             return; 
15543         }
15544         if (this.triggers === false) {
15545             return;
15546         }
15547         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15548         var triggers = this.trigger ? this.trigger.split(' ') : [];
15549         Roo.each(triggers, function(trigger) {
15550         
15551             if (trigger == 'click') {
15552                 on_el.on('click', this.toggle, this);
15553             } else if (trigger != 'manual') {
15554                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15555                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15556       
15557                 on_el.on(eventIn  ,this.enter, this);
15558                 on_el.on(eventOut, this.leave, this);
15559             }
15560         }, this);
15561         
15562     },
15563     
15564     
15565     // private
15566     timeout : null,
15567     hoverState : null,
15568     
15569     toggle : function () {
15570         this.hoverState == 'in' ? this.leave() : this.enter();
15571     },
15572     
15573     enter : function () {
15574        
15575     
15576         clearTimeout(this.timeout);
15577     
15578         this.hoverState = 'in';
15579     
15580         if (!this.delay || !this.delay.show) {
15581             this.show();
15582             return;
15583         }
15584         var _t = this;
15585         this.timeout = setTimeout(function () {
15586             if (_t.hoverState == 'in') {
15587                 _t.show();
15588             }
15589         }, this.delay.show)
15590     },
15591     leave : function() {
15592         clearTimeout(this.timeout);
15593     
15594         this.hoverState = 'out';
15595     
15596         if (!this.delay || !this.delay.hide) {
15597             this.hide();
15598             return;
15599         }
15600         var _t = this;
15601         this.timeout = setTimeout(function () {
15602             if (_t.hoverState == 'out') {
15603                 _t.hide();
15604             }
15605         }, this.delay.hide)
15606     },
15607     
15608     show : function (on_el)
15609     {
15610         if (!on_el) {
15611             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15612         }
15613         // set content.
15614         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15615         if (this.html !== false) {
15616             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15617         }
15618         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15619         if (!this.title.length) {
15620             this.el.select('.popover-title',true).hide();
15621         }
15622         
15623         var placement = typeof this.placement == 'function' ?
15624             this.placement.call(this, this.el, on_el) :
15625             this.placement;
15626             
15627         var autoToken = /\s?auto?\s?/i;
15628         var autoPlace = autoToken.test(placement);
15629         if (autoPlace) {
15630             placement = placement.replace(autoToken, '') || 'top';
15631         }
15632         
15633         //this.el.detach()
15634         //this.el.setXY([0,0]);
15635         this.el.show();
15636         this.el.dom.style.display='block';
15637         this.el.addClass(placement);
15638         
15639         //this.el.appendTo(on_el);
15640         
15641         var p = this.getPosition();
15642         var box = this.el.getBox();
15643         
15644         if (autoPlace) {
15645             // fixme..
15646         }
15647         var align = Roo.bootstrap.Popover.alignment[placement];
15648         this.el.alignTo(on_el, align[0],align[1]);
15649         //var arrow = this.el.select('.arrow',true).first();
15650         //arrow.set(align[2], 
15651         
15652         this.el.addClass('in');
15653         
15654         
15655         if (this.el.hasClass('fade')) {
15656             // fade it?
15657         }
15658         
15659     },
15660     hide : function()
15661     {
15662         this.el.setXY([0,0]);
15663         this.el.removeClass('in');
15664         this.el.hide();
15665         this.hoverState = null;
15666         
15667     }
15668     
15669 });
15670
15671 Roo.bootstrap.Popover.alignment = {
15672     'left' : ['r-l', [-10,0], 'right'],
15673     'right' : ['l-r', [10,0], 'left'],
15674     'bottom' : ['t-b', [0,10], 'top'],
15675     'top' : [ 'b-t', [0,-10], 'bottom']
15676 };
15677
15678  /*
15679  * - LGPL
15680  *
15681  * Progress
15682  * 
15683  */
15684
15685 /**
15686  * @class Roo.bootstrap.Progress
15687  * @extends Roo.bootstrap.Component
15688  * Bootstrap Progress class
15689  * @cfg {Boolean} striped striped of the progress bar
15690  * @cfg {Boolean} active animated of the progress bar
15691  * 
15692  * 
15693  * @constructor
15694  * Create a new Progress
15695  * @param {Object} config The config object
15696  */
15697
15698 Roo.bootstrap.Progress = function(config){
15699     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15700 };
15701
15702 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15703     
15704     striped : false,
15705     active: false,
15706     
15707     getAutoCreate : function(){
15708         var cfg = {
15709             tag: 'div',
15710             cls: 'progress'
15711         };
15712         
15713         
15714         if(this.striped){
15715             cfg.cls += ' progress-striped';
15716         }
15717       
15718         if(this.active){
15719             cfg.cls += ' active';
15720         }
15721         
15722         
15723         return cfg;
15724     }
15725    
15726 });
15727
15728  
15729
15730  /*
15731  * - LGPL
15732  *
15733  * ProgressBar
15734  * 
15735  */
15736
15737 /**
15738  * @class Roo.bootstrap.ProgressBar
15739  * @extends Roo.bootstrap.Component
15740  * Bootstrap ProgressBar class
15741  * @cfg {Number} aria_valuenow aria-value now
15742  * @cfg {Number} aria_valuemin aria-value min
15743  * @cfg {Number} aria_valuemax aria-value max
15744  * @cfg {String} label label for the progress bar
15745  * @cfg {String} panel (success | info | warning | danger )
15746  * @cfg {String} role role of the progress bar
15747  * @cfg {String} sr_only text
15748  * 
15749  * 
15750  * @constructor
15751  * Create a new ProgressBar
15752  * @param {Object} config The config object
15753  */
15754
15755 Roo.bootstrap.ProgressBar = function(config){
15756     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15757 };
15758
15759 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15760     
15761     aria_valuenow : 0,
15762     aria_valuemin : 0,
15763     aria_valuemax : 100,
15764     label : false,
15765     panel : false,
15766     role : false,
15767     sr_only: false,
15768     
15769     getAutoCreate : function()
15770     {
15771         
15772         var cfg = {
15773             tag: 'div',
15774             cls: 'progress-bar',
15775             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15776         };
15777         
15778         if(this.sr_only){
15779             cfg.cn = {
15780                 tag: 'span',
15781                 cls: 'sr-only',
15782                 html: this.sr_only
15783             }
15784         }
15785         
15786         if(this.role){
15787             cfg.role = this.role;
15788         }
15789         
15790         if(this.aria_valuenow){
15791             cfg['aria-valuenow'] = this.aria_valuenow;
15792         }
15793         
15794         if(this.aria_valuemin){
15795             cfg['aria-valuemin'] = this.aria_valuemin;
15796         }
15797         
15798         if(this.aria_valuemax){
15799             cfg['aria-valuemax'] = this.aria_valuemax;
15800         }
15801         
15802         if(this.label && !this.sr_only){
15803             cfg.html = this.label;
15804         }
15805         
15806         if(this.panel){
15807             cfg.cls += ' progress-bar-' + this.panel;
15808         }
15809         
15810         return cfg;
15811     },
15812     
15813     update : function(aria_valuenow)
15814     {
15815         this.aria_valuenow = aria_valuenow;
15816         
15817         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15818     }
15819    
15820 });
15821
15822  
15823
15824  /*
15825  * - LGPL
15826  *
15827  * column
15828  * 
15829  */
15830
15831 /**
15832  * @class Roo.bootstrap.TabGroup
15833  * @extends Roo.bootstrap.Column
15834  * Bootstrap Column class
15835  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15836  * @cfg {Boolean} carousel true to make the group behave like a carousel
15837  * @cfg {Number} bullets show the panel pointer.. default 0
15838  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15839  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15840  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15841  * 
15842  * @constructor
15843  * Create a new TabGroup
15844  * @param {Object} config The config object
15845  */
15846
15847 Roo.bootstrap.TabGroup = function(config){
15848     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15849     if (!this.navId) {
15850         this.navId = Roo.id();
15851     }
15852     this.tabs = [];
15853     Roo.bootstrap.TabGroup.register(this);
15854     
15855 };
15856
15857 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15858     
15859     carousel : false,
15860     transition : false,
15861     bullets : 0,
15862     timer : 0,
15863     autoslide : false,
15864     slideFn : false,
15865     slideOnTouch : false,
15866     
15867     getAutoCreate : function()
15868     {
15869         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15870         
15871         cfg.cls += ' tab-content';
15872         
15873         Roo.log('get auto create...............');
15874         
15875         if (this.carousel) {
15876             cfg.cls += ' carousel slide';
15877             
15878             cfg.cn = [{
15879                cls : 'carousel-inner'
15880             }];
15881         
15882             if(this.bullets > 0 && !Roo.isTouch){
15883                 
15884                 var bullets = {
15885                     cls : 'carousel-bullets',
15886                     cn : []
15887                 };
15888                 
15889                 if(this.bullets_cls){
15890                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15891                 }
15892                 
15893                 for (var i = 0; i < this.bullets; i++){
15894                     bullets.cn.push({
15895                         cls : 'bullet bullet-' + i
15896                     });
15897                 }
15898                 
15899                 bullets.cn.push({
15900                     cls : 'clear'
15901                 });
15902                 
15903                 cfg.cn[0].cn = bullets;
15904             }
15905         }
15906         
15907         return cfg;
15908     },
15909     
15910     initEvents:  function()
15911     {
15912         Roo.log('-------- init events on tab group ---------');
15913         
15914         if(this.bullets > 0 && !Roo.isTouch){
15915             this.initBullet();
15916         }
15917         
15918         Roo.log(this);
15919         
15920         if(Roo.isTouch && this.slideOnTouch){
15921             this.el.on("touchstart", this.onTouchStart, this);
15922         }
15923         
15924         if(this.autoslide){
15925             var _this = this;
15926             
15927             this.slideFn = window.setInterval(function() {
15928                 _this.showPanelNext();
15929             }, this.timer);
15930         }
15931         
15932     },
15933     
15934     onTouchStart : function(e, el, o)
15935     {
15936         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15937             return;
15938         }
15939         
15940         this.showPanelNext();
15941     },
15942     
15943     getChildContainer : function()
15944     {
15945         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15946     },
15947     
15948     /**
15949     * register a Navigation item
15950     * @param {Roo.bootstrap.NavItem} the navitem to add
15951     */
15952     register : function(item)
15953     {
15954         this.tabs.push( item);
15955         item.navId = this.navId; // not really needed..
15956     
15957     },
15958     
15959     getActivePanel : function()
15960     {
15961         var r = false;
15962         Roo.each(this.tabs, function(t) {
15963             if (t.active) {
15964                 r = t;
15965                 return false;
15966             }
15967             return null;
15968         });
15969         return r;
15970         
15971     },
15972     getPanelByName : function(n)
15973     {
15974         var r = false;
15975         Roo.each(this.tabs, function(t) {
15976             if (t.tabId == n) {
15977                 r = t;
15978                 return false;
15979             }
15980             return null;
15981         });
15982         return r;
15983     },
15984     indexOfPanel : function(p)
15985     {
15986         var r = false;
15987         Roo.each(this.tabs, function(t,i) {
15988             if (t.tabId == p.tabId) {
15989                 r = i;
15990                 return false;
15991             }
15992             return null;
15993         });
15994         return r;
15995     },
15996     /**
15997      * show a specific panel
15998      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15999      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16000      */
16001     showPanel : function (pan)
16002     {
16003         if(this.transition){
16004             Roo.log("waiting for the transitionend");
16005             return;
16006         }
16007         
16008         if (typeof(pan) == 'number') {
16009             pan = this.tabs[pan];
16010         }
16011         if (typeof(pan) == 'string') {
16012             pan = this.getPanelByName(pan);
16013         }
16014         if (pan.tabId == this.getActivePanel().tabId) {
16015             return true;
16016         }
16017         var cur = this.getActivePanel();
16018         
16019         if (false === cur.fireEvent('beforedeactivate')) {
16020             return false;
16021         }
16022         
16023         if(this.bullets > 0 && !Roo.isTouch){
16024             this.setActiveBullet(this.indexOfPanel(pan));
16025         }
16026         
16027         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16028             
16029             this.transition = true;
16030             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16031             var lr = dir == 'next' ? 'left' : 'right';
16032             pan.el.addClass(dir); // or prev
16033             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16034             cur.el.addClass(lr); // or right
16035             pan.el.addClass(lr);
16036             
16037             var _this = this;
16038             cur.el.on('transitionend', function() {
16039                 Roo.log("trans end?");
16040                 
16041                 pan.el.removeClass([lr,dir]);
16042                 pan.setActive(true);
16043                 
16044                 cur.el.removeClass([lr]);
16045                 cur.setActive(false);
16046                 
16047                 _this.transition = false;
16048                 
16049             }, this, { single:  true } );
16050             
16051             return true;
16052         }
16053         
16054         cur.setActive(false);
16055         pan.setActive(true);
16056         
16057         return true;
16058         
16059     },
16060     showPanelNext : function()
16061     {
16062         var i = this.indexOfPanel(this.getActivePanel());
16063         
16064         if (i >= this.tabs.length - 1 && !this.autoslide) {
16065             return;
16066         }
16067         
16068         if (i >= this.tabs.length - 1 && this.autoslide) {
16069             i = -1;
16070         }
16071         
16072         this.showPanel(this.tabs[i+1]);
16073     },
16074     
16075     showPanelPrev : function()
16076     {
16077         var i = this.indexOfPanel(this.getActivePanel());
16078         
16079         if (i  < 1 && !this.autoslide) {
16080             return;
16081         }
16082         
16083         if (i < 1 && this.autoslide) {
16084             i = this.tabs.length;
16085         }
16086         
16087         this.showPanel(this.tabs[i-1]);
16088     },
16089     
16090     initBullet : function()
16091     {
16092         if(Roo.isTouch){
16093             return;
16094         }
16095         
16096         var _this = this;
16097         
16098         for (var i = 0; i < this.bullets; i++){
16099             var bullet = this.el.select('.bullet-' + i, true).first();
16100
16101             if(!bullet){
16102                 continue;
16103             }
16104
16105             bullet.on('click', (function(e, el, o, ii, t){
16106
16107                 e.preventDefault();
16108
16109                 _this.showPanel(ii);
16110
16111                 if(_this.autoslide && _this.slideFn){
16112                     clearInterval(_this.slideFn);
16113                     _this.slideFn = window.setInterval(function() {
16114                         _this.showPanelNext();
16115                     }, _this.timer);
16116                 }
16117
16118             }).createDelegate(this, [i, bullet], true));
16119         }
16120     },
16121     
16122     setActiveBullet : function(i)
16123     {
16124         if(Roo.isTouch){
16125             return;
16126         }
16127         
16128         Roo.each(this.el.select('.bullet', true).elements, function(el){
16129             el.removeClass('selected');
16130         });
16131
16132         var bullet = this.el.select('.bullet-' + i, true).first();
16133         
16134         if(!bullet){
16135             return;
16136         }
16137         
16138         bullet.addClass('selected');
16139     }
16140     
16141     
16142   
16143 });
16144
16145  
16146
16147  
16148  
16149 Roo.apply(Roo.bootstrap.TabGroup, {
16150     
16151     groups: {},
16152      /**
16153     * register a Navigation Group
16154     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16155     */
16156     register : function(navgrp)
16157     {
16158         this.groups[navgrp.navId] = navgrp;
16159         
16160     },
16161     /**
16162     * fetch a Navigation Group based on the navigation ID
16163     * if one does not exist , it will get created.
16164     * @param {string} the navgroup to add
16165     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16166     */
16167     get: function(navId) {
16168         if (typeof(this.groups[navId]) == 'undefined') {
16169             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16170         }
16171         return this.groups[navId] ;
16172     }
16173     
16174     
16175     
16176 });
16177
16178  /*
16179  * - LGPL
16180  *
16181  * TabPanel
16182  * 
16183  */
16184
16185 /**
16186  * @class Roo.bootstrap.TabPanel
16187  * @extends Roo.bootstrap.Component
16188  * Bootstrap TabPanel class
16189  * @cfg {Boolean} active panel active
16190  * @cfg {String} html panel content
16191  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16192  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16193  * 
16194  * 
16195  * @constructor
16196  * Create a new TabPanel
16197  * @param {Object} config The config object
16198  */
16199
16200 Roo.bootstrap.TabPanel = function(config){
16201     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16202     this.addEvents({
16203         /**
16204              * @event changed
16205              * Fires when the active status changes
16206              * @param {Roo.bootstrap.TabPanel} this
16207              * @param {Boolean} state the new state
16208             
16209          */
16210         'changed': true,
16211         /**
16212              * @event beforedeactivate
16213              * Fires before a tab is de-activated - can be used to do validation on a form.
16214              * @param {Roo.bootstrap.TabPanel} this
16215              * @return {Boolean} false if there is an error
16216             
16217          */
16218         'beforedeactivate': true
16219      });
16220     
16221     this.tabId = this.tabId || Roo.id();
16222   
16223 };
16224
16225 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16226     
16227     active: false,
16228     html: false,
16229     tabId: false,
16230     navId : false,
16231     
16232     getAutoCreate : function(){
16233         var cfg = {
16234             tag: 'div',
16235             // item is needed for carousel - not sure if it has any effect otherwise
16236             cls: 'tab-pane item',
16237             html: this.html || ''
16238         };
16239         
16240         if(this.active){
16241             cfg.cls += ' active';
16242         }
16243         
16244         if(this.tabId){
16245             cfg.tabId = this.tabId;
16246         }
16247         
16248         
16249         return cfg;
16250     },
16251     
16252     initEvents:  function()
16253     {
16254         Roo.log('-------- init events on tab panel ---------');
16255         
16256         var p = this.parent();
16257         this.navId = this.navId || p.navId;
16258         
16259         if (typeof(this.navId) != 'undefined') {
16260             // not really needed.. but just in case.. parent should be a NavGroup.
16261             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16262             Roo.log(['register', tg, this]);
16263             tg.register(this);
16264             
16265             var i = tg.tabs.length - 1;
16266             
16267             if(this.active && tg.bullets > 0 && i < tg.bullets){
16268                 tg.setActiveBullet(i);
16269             }
16270         }
16271         
16272     },
16273     
16274     
16275     onRender : function(ct, position)
16276     {
16277        // Roo.log("Call onRender: " + this.xtype);
16278         
16279         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16280         
16281         
16282         
16283         
16284         
16285     },
16286     
16287     setActive: function(state)
16288     {
16289         Roo.log("panel - set active " + this.tabId + "=" + state);
16290         
16291         this.active = state;
16292         if (!state) {
16293             this.el.removeClass('active');
16294             
16295         } else  if (!this.el.hasClass('active')) {
16296             this.el.addClass('active');
16297         }
16298         
16299         this.fireEvent('changed', this, state);
16300     }
16301     
16302     
16303 });
16304  
16305
16306  
16307
16308  /*
16309  * - LGPL
16310  *
16311  * DateField
16312  * 
16313  */
16314
16315 /**
16316  * @class Roo.bootstrap.DateField
16317  * @extends Roo.bootstrap.Input
16318  * Bootstrap DateField class
16319  * @cfg {Number} weekStart default 0
16320  * @cfg {String} viewMode default empty, (months|years)
16321  * @cfg {String} minViewMode default empty, (months|years)
16322  * @cfg {Number} startDate default -Infinity
16323  * @cfg {Number} endDate default Infinity
16324  * @cfg {Boolean} todayHighlight default false
16325  * @cfg {Boolean} todayBtn default false
16326  * @cfg {Boolean} calendarWeeks default false
16327  * @cfg {Object} daysOfWeekDisabled default empty
16328  * @cfg {Boolean} singleMode default false (true | false)
16329  * 
16330  * @cfg {Boolean} keyboardNavigation default true
16331  * @cfg {String} language default en
16332  * 
16333  * @constructor
16334  * Create a new DateField
16335  * @param {Object} config The config object
16336  */
16337
16338 Roo.bootstrap.DateField = function(config){
16339     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16340      this.addEvents({
16341             /**
16342              * @event show
16343              * Fires when this field show.
16344              * @param {Roo.bootstrap.DateField} this
16345              * @param {Mixed} date The date value
16346              */
16347             show : true,
16348             /**
16349              * @event show
16350              * Fires when this field hide.
16351              * @param {Roo.bootstrap.DateField} this
16352              * @param {Mixed} date The date value
16353              */
16354             hide : true,
16355             /**
16356              * @event select
16357              * Fires when select a date.
16358              * @param {Roo.bootstrap.DateField} this
16359              * @param {Mixed} date The date value
16360              */
16361             select : true,
16362             /**
16363              * @event beforeselect
16364              * Fires when before select a date.
16365              * @param {Roo.bootstrap.DateField} this
16366              * @param {Mixed} date The date value
16367              */
16368             beforeselect : true
16369         });
16370 };
16371
16372 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16373     
16374     /**
16375      * @cfg {String} format
16376      * The default date format string which can be overriden for localization support.  The format must be
16377      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16378      */
16379     format : "m/d/y",
16380     /**
16381      * @cfg {String} altFormats
16382      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16383      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16384      */
16385     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16386     
16387     weekStart : 0,
16388     
16389     viewMode : '',
16390     
16391     minViewMode : '',
16392     
16393     todayHighlight : false,
16394     
16395     todayBtn: false,
16396     
16397     language: 'en',
16398     
16399     keyboardNavigation: true,
16400     
16401     calendarWeeks: false,
16402     
16403     startDate: -Infinity,
16404     
16405     endDate: Infinity,
16406     
16407     daysOfWeekDisabled: [],
16408     
16409     _events: [],
16410     
16411     singleMode : false,
16412     
16413     UTCDate: function()
16414     {
16415         return new Date(Date.UTC.apply(Date, arguments));
16416     },
16417     
16418     UTCToday: function()
16419     {
16420         var today = new Date();
16421         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16422     },
16423     
16424     getDate: function() {
16425             var d = this.getUTCDate();
16426             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16427     },
16428     
16429     getUTCDate: function() {
16430             return this.date;
16431     },
16432     
16433     setDate: function(d) {
16434             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16435     },
16436     
16437     setUTCDate: function(d) {
16438             this.date = d;
16439             this.setValue(this.formatDate(this.date));
16440     },
16441         
16442     onRender: function(ct, position)
16443     {
16444         
16445         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16446         
16447         this.language = this.language || 'en';
16448         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16449         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16450         
16451         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16452         this.format = this.format || 'm/d/y';
16453         this.isInline = false;
16454         this.isInput = true;
16455         this.component = this.el.select('.add-on', true).first() || false;
16456         this.component = (this.component && this.component.length === 0) ? false : this.component;
16457         this.hasInput = this.component && this.inputEL().length;
16458         
16459         if (typeof(this.minViewMode === 'string')) {
16460             switch (this.minViewMode) {
16461                 case 'months':
16462                     this.minViewMode = 1;
16463                     break;
16464                 case 'years':
16465                     this.minViewMode = 2;
16466                     break;
16467                 default:
16468                     this.minViewMode = 0;
16469                     break;
16470             }
16471         }
16472         
16473         if (typeof(this.viewMode === 'string')) {
16474             switch (this.viewMode) {
16475                 case 'months':
16476                     this.viewMode = 1;
16477                     break;
16478                 case 'years':
16479                     this.viewMode = 2;
16480                     break;
16481                 default:
16482                     this.viewMode = 0;
16483                     break;
16484             }
16485         }
16486                 
16487         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16488         
16489 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16490         
16491         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16492         
16493         this.picker().on('mousedown', this.onMousedown, this);
16494         this.picker().on('click', this.onClick, this);
16495         
16496         this.picker().addClass('datepicker-dropdown');
16497         
16498         this.startViewMode = this.viewMode;
16499         
16500         if(this.singleMode){
16501             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16502                 v.setVisibilityMode(Roo.Element.DISPLAY)
16503                 v.hide();
16504             });
16505             
16506             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16507                 v.setStyle('width', '189px');
16508             });
16509         }
16510         
16511         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16512             if(!this.calendarWeeks){
16513                 v.remove();
16514                 return;
16515             }
16516             
16517             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16518             v.attr('colspan', function(i, val){
16519                 return parseInt(val) + 1;
16520             });
16521         })
16522                         
16523         
16524         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16525         
16526         this.setStartDate(this.startDate);
16527         this.setEndDate(this.endDate);
16528         
16529         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16530         
16531         this.fillDow();
16532         this.fillMonths();
16533         this.update();
16534         this.showMode();
16535         
16536         if(this.isInline) {
16537             this.show();
16538         }
16539     },
16540     
16541     picker : function()
16542     {
16543         return this.pickerEl;
16544 //        return this.el.select('.datepicker', true).first();
16545     },
16546     
16547     fillDow: function()
16548     {
16549         var dowCnt = this.weekStart;
16550         
16551         var dow = {
16552             tag: 'tr',
16553             cn: [
16554                 
16555             ]
16556         };
16557         
16558         if(this.calendarWeeks){
16559             dow.cn.push({
16560                 tag: 'th',
16561                 cls: 'cw',
16562                 html: '&nbsp;'
16563             })
16564         }
16565         
16566         while (dowCnt < this.weekStart + 7) {
16567             dow.cn.push({
16568                 tag: 'th',
16569                 cls: 'dow',
16570                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16571             });
16572         }
16573         
16574         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16575     },
16576     
16577     fillMonths: function()
16578     {    
16579         var i = 0;
16580         var months = this.picker().select('>.datepicker-months td', true).first();
16581         
16582         months.dom.innerHTML = '';
16583         
16584         while (i < 12) {
16585             var month = {
16586                 tag: 'span',
16587                 cls: 'month',
16588                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16589             };
16590             
16591             months.createChild(month);
16592         }
16593         
16594     },
16595     
16596     update: function()
16597     {
16598         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
16599         
16600         if (this.date < this.startDate) {
16601             this.viewDate = new Date(this.startDate);
16602         } else if (this.date > this.endDate) {
16603             this.viewDate = new Date(this.endDate);
16604         } else {
16605             this.viewDate = new Date(this.date);
16606         }
16607         
16608         this.fill();
16609     },
16610     
16611     fill: function() 
16612     {
16613         var d = new Date(this.viewDate),
16614                 year = d.getUTCFullYear(),
16615                 month = d.getUTCMonth(),
16616                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16617                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16618                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16619                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16620                 currentDate = this.date && this.date.valueOf(),
16621                 today = this.UTCToday();
16622         
16623         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16624         
16625 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16626         
16627 //        this.picker.select('>tfoot th.today').
16628 //                                              .text(dates[this.language].today)
16629 //                                              .toggle(this.todayBtn !== false);
16630     
16631         this.updateNavArrows();
16632         this.fillMonths();
16633                                                 
16634         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16635         
16636         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16637          
16638         prevMonth.setUTCDate(day);
16639         
16640         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16641         
16642         var nextMonth = new Date(prevMonth);
16643         
16644         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16645         
16646         nextMonth = nextMonth.valueOf();
16647         
16648         var fillMonths = false;
16649         
16650         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16651         
16652         while(prevMonth.valueOf() < nextMonth) {
16653             var clsName = '';
16654             
16655             if (prevMonth.getUTCDay() === this.weekStart) {
16656                 if(fillMonths){
16657                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16658                 }
16659                     
16660                 fillMonths = {
16661                     tag: 'tr',
16662                     cn: []
16663                 };
16664                 
16665                 if(this.calendarWeeks){
16666                     // ISO 8601: First week contains first thursday.
16667                     // ISO also states week starts on Monday, but we can be more abstract here.
16668                     var
16669                     // Start of current week: based on weekstart/current date
16670                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16671                     // Thursday of this week
16672                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16673                     // First Thursday of year, year from thursday
16674                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16675                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16676                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16677                     
16678                     fillMonths.cn.push({
16679                         tag: 'td',
16680                         cls: 'cw',
16681                         html: calWeek
16682                     });
16683                 }
16684             }
16685             
16686             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16687                 clsName += ' old';
16688             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16689                 clsName += ' new';
16690             }
16691             if (this.todayHighlight &&
16692                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16693                 prevMonth.getUTCMonth() == today.getMonth() &&
16694                 prevMonth.getUTCDate() == today.getDate()) {
16695                 clsName += ' today';
16696             }
16697             
16698             if (currentDate && prevMonth.valueOf() === currentDate) {
16699                 clsName += ' active';
16700             }
16701             
16702             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16703                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16704                     clsName += ' disabled';
16705             }
16706             
16707             fillMonths.cn.push({
16708                 tag: 'td',
16709                 cls: 'day ' + clsName,
16710                 html: prevMonth.getDate()
16711             })
16712             
16713             prevMonth.setDate(prevMonth.getDate()+1);
16714         }
16715           
16716         var currentYear = this.date && this.date.getUTCFullYear();
16717         var currentMonth = this.date && this.date.getUTCMonth();
16718         
16719         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16720         
16721         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16722             v.removeClass('active');
16723             
16724             if(currentYear === year && k === currentMonth){
16725                 v.addClass('active');
16726             }
16727             
16728             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16729                 v.addClass('disabled');
16730             }
16731             
16732         });
16733         
16734         
16735         year = parseInt(year/10, 10) * 10;
16736         
16737         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16738         
16739         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16740         
16741         year -= 1;
16742         for (var i = -1; i < 11; i++) {
16743             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16744                 tag: 'span',
16745                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16746                 html: year
16747             })
16748             
16749             year += 1;
16750         }
16751     },
16752     
16753     showMode: function(dir) 
16754     {
16755         if (dir) {
16756             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16757         }
16758         
16759         Roo.each(this.picker().select('>div',true).elements, function(v){
16760             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16761             v.hide();
16762         });
16763         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16764     },
16765     
16766     place: function()
16767     {
16768         if(this.isInline) return;
16769         
16770         this.picker().removeClass(['bottom', 'top']);
16771         
16772         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16773             /*
16774              * place to the top of element!
16775              *
16776              */
16777             
16778             this.picker().addClass('top');
16779             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16780             
16781             return;
16782         }
16783         
16784         this.picker().addClass('bottom');
16785         
16786         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16787     },
16788     
16789     parseDate : function(value)
16790     {
16791         if(!value || value instanceof Date){
16792             return value;
16793         }
16794         var v = Date.parseDate(value, this.format);
16795         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16796             v = Date.parseDate(value, 'Y-m-d');
16797         }
16798         if(!v && this.altFormats){
16799             if(!this.altFormatsArray){
16800                 this.altFormatsArray = this.altFormats.split("|");
16801             }
16802             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16803                 v = Date.parseDate(value, this.altFormatsArray[i]);
16804             }
16805         }
16806         return v;
16807     },
16808     
16809     formatDate : function(date, fmt)
16810     {   
16811         return (!date || !(date instanceof Date)) ?
16812         date : date.dateFormat(fmt || this.format);
16813     },
16814     
16815     onFocus : function()
16816     {
16817         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16818         this.show();
16819     },
16820     
16821     onBlur : function()
16822     {
16823         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16824         
16825         var d = this.inputEl().getValue();
16826         
16827         this.setValue(d);
16828                 
16829         this.hide();
16830     },
16831     
16832     show : function()
16833     {
16834         this.picker().show();
16835         this.update();
16836         this.place();
16837         
16838         this.fireEvent('show', this, this.date);
16839     },
16840     
16841     hide : function()
16842     {
16843         if(this.isInline) return;
16844         this.picker().hide();
16845         this.viewMode = this.startViewMode;
16846         this.showMode();
16847         
16848         this.fireEvent('hide', this, this.date);
16849         
16850     },
16851     
16852     onMousedown: function(e)
16853     {
16854         e.stopPropagation();
16855         e.preventDefault();
16856     },
16857     
16858     keyup: function(e)
16859     {
16860         Roo.bootstrap.DateField.superclass.keyup.call(this);
16861         this.update();
16862     },
16863
16864     setValue: function(v)
16865     {
16866         if(this.fireEvent('beforeselect', this, v) !== false){
16867             var d = new Date(this.parseDate(v) ).clearTime();
16868         
16869             if(isNaN(d.getTime())){
16870                 this.date = this.viewDate = '';
16871                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16872                 return;
16873             }
16874
16875             v = this.formatDate(d);
16876
16877             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16878
16879             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16880
16881             this.update();
16882
16883             this.fireEvent('select', this, this.date);
16884         }
16885     },
16886     
16887     getValue: function()
16888     {
16889         return this.formatDate(this.date);
16890     },
16891     
16892     fireKey: function(e)
16893     {
16894         if (!this.picker().isVisible()){
16895             if (e.keyCode == 27) // allow escape to hide and re-show picker
16896                 this.show();
16897             return;
16898         }
16899         
16900         var dateChanged = false,
16901         dir, day, month,
16902         newDate, newViewDate;
16903         
16904         switch(e.keyCode){
16905             case 27: // escape
16906                 this.hide();
16907                 e.preventDefault();
16908                 break;
16909             case 37: // left
16910             case 39: // right
16911                 if (!this.keyboardNavigation) break;
16912                 dir = e.keyCode == 37 ? -1 : 1;
16913                 
16914                 if (e.ctrlKey){
16915                     newDate = this.moveYear(this.date, dir);
16916                     newViewDate = this.moveYear(this.viewDate, dir);
16917                 } else if (e.shiftKey){
16918                     newDate = this.moveMonth(this.date, dir);
16919                     newViewDate = this.moveMonth(this.viewDate, dir);
16920                 } else {
16921                     newDate = new Date(this.date);
16922                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16923                     newViewDate = new Date(this.viewDate);
16924                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16925                 }
16926                 if (this.dateWithinRange(newDate)){
16927                     this.date = newDate;
16928                     this.viewDate = newViewDate;
16929                     this.setValue(this.formatDate(this.date));
16930 //                    this.update();
16931                     e.preventDefault();
16932                     dateChanged = true;
16933                 }
16934                 break;
16935             case 38: // up
16936             case 40: // down
16937                 if (!this.keyboardNavigation) break;
16938                 dir = e.keyCode == 38 ? -1 : 1;
16939                 if (e.ctrlKey){
16940                     newDate = this.moveYear(this.date, dir);
16941                     newViewDate = this.moveYear(this.viewDate, dir);
16942                 } else if (e.shiftKey){
16943                     newDate = this.moveMonth(this.date, dir);
16944                     newViewDate = this.moveMonth(this.viewDate, dir);
16945                 } else {
16946                     newDate = new Date(this.date);
16947                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16948                     newViewDate = new Date(this.viewDate);
16949                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16950                 }
16951                 if (this.dateWithinRange(newDate)){
16952                     this.date = newDate;
16953                     this.viewDate = newViewDate;
16954                     this.setValue(this.formatDate(this.date));
16955 //                    this.update();
16956                     e.preventDefault();
16957                     dateChanged = true;
16958                 }
16959                 break;
16960             case 13: // enter
16961                 this.setValue(this.formatDate(this.date));
16962                 this.hide();
16963                 e.preventDefault();
16964                 break;
16965             case 9: // tab
16966                 this.setValue(this.formatDate(this.date));
16967                 this.hide();
16968                 break;
16969             case 16: // shift
16970             case 17: // ctrl
16971             case 18: // alt
16972                 break;
16973             default :
16974                 this.hide();
16975                 
16976         }
16977     },
16978     
16979     
16980     onClick: function(e) 
16981     {
16982         e.stopPropagation();
16983         e.preventDefault();
16984         
16985         var target = e.getTarget();
16986         
16987         if(target.nodeName.toLowerCase() === 'i'){
16988             target = Roo.get(target).dom.parentNode;
16989         }
16990         
16991         var nodeName = target.nodeName;
16992         var className = target.className;
16993         var html = target.innerHTML;
16994         //Roo.log(nodeName);
16995         
16996         switch(nodeName.toLowerCase()) {
16997             case 'th':
16998                 switch(className) {
16999                     case 'switch':
17000                         this.showMode(1);
17001                         break;
17002                     case 'prev':
17003                     case 'next':
17004                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17005                         switch(this.viewMode){
17006                                 case 0:
17007                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17008                                         break;
17009                                 case 1:
17010                                 case 2:
17011                                         this.viewDate = this.moveYear(this.viewDate, dir);
17012                                         break;
17013                         }
17014                         this.fill();
17015                         break;
17016                     case 'today':
17017                         var date = new Date();
17018                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17019 //                        this.fill()
17020                         this.setValue(this.formatDate(this.date));
17021                         
17022                         this.hide();
17023                         break;
17024                 }
17025                 break;
17026             case 'span':
17027                 if (className.indexOf('disabled') < 0) {
17028                     this.viewDate.setUTCDate(1);
17029                     if (className.indexOf('month') > -1) {
17030                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17031                     } else {
17032                         var year = parseInt(html, 10) || 0;
17033                         this.viewDate.setUTCFullYear(year);
17034                         
17035                     }
17036                     
17037                     if(this.singleMode){
17038                         this.setValue(this.formatDate(this.viewDate));
17039                         this.hide();
17040                         return;
17041                     }
17042                     
17043                     this.showMode(-1);
17044                     this.fill();
17045                 }
17046                 break;
17047                 
17048             case 'td':
17049                 //Roo.log(className);
17050                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17051                     var day = parseInt(html, 10) || 1;
17052                     var year = this.viewDate.getUTCFullYear(),
17053                         month = this.viewDate.getUTCMonth();
17054
17055                     if (className.indexOf('old') > -1) {
17056                         if(month === 0 ){
17057                             month = 11;
17058                             year -= 1;
17059                         }else{
17060                             month -= 1;
17061                         }
17062                     } else if (className.indexOf('new') > -1) {
17063                         if (month == 11) {
17064                             month = 0;
17065                             year += 1;
17066                         } else {
17067                             month += 1;
17068                         }
17069                     }
17070                     //Roo.log([year,month,day]);
17071                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17072                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17073 //                    this.fill();
17074                     //Roo.log(this.formatDate(this.date));
17075                     this.setValue(this.formatDate(this.date));
17076                     this.hide();
17077                 }
17078                 break;
17079         }
17080     },
17081     
17082     setStartDate: function(startDate)
17083     {
17084         this.startDate = startDate || -Infinity;
17085         if (this.startDate !== -Infinity) {
17086             this.startDate = this.parseDate(this.startDate);
17087         }
17088         this.update();
17089         this.updateNavArrows();
17090     },
17091
17092     setEndDate: function(endDate)
17093     {
17094         this.endDate = endDate || Infinity;
17095         if (this.endDate !== Infinity) {
17096             this.endDate = this.parseDate(this.endDate);
17097         }
17098         this.update();
17099         this.updateNavArrows();
17100     },
17101     
17102     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17103     {
17104         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17105         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17106             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17107         }
17108         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17109             return parseInt(d, 10);
17110         });
17111         this.update();
17112         this.updateNavArrows();
17113     },
17114     
17115     updateNavArrows: function() 
17116     {
17117         if(this.singleMode){
17118             return;
17119         }
17120         
17121         var d = new Date(this.viewDate),
17122         year = d.getUTCFullYear(),
17123         month = d.getUTCMonth();
17124         
17125         Roo.each(this.picker().select('.prev', true).elements, function(v){
17126             v.show();
17127             switch (this.viewMode) {
17128                 case 0:
17129
17130                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17131                         v.hide();
17132                     }
17133                     break;
17134                 case 1:
17135                 case 2:
17136                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17137                         v.hide();
17138                     }
17139                     break;
17140             }
17141         });
17142         
17143         Roo.each(this.picker().select('.next', true).elements, function(v){
17144             v.show();
17145             switch (this.viewMode) {
17146                 case 0:
17147
17148                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17149                         v.hide();
17150                     }
17151                     break;
17152                 case 1:
17153                 case 2:
17154                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17155                         v.hide();
17156                     }
17157                     break;
17158             }
17159         })
17160     },
17161     
17162     moveMonth: function(date, dir)
17163     {
17164         if (!dir) return date;
17165         var new_date = new Date(date.valueOf()),
17166         day = new_date.getUTCDate(),
17167         month = new_date.getUTCMonth(),
17168         mag = Math.abs(dir),
17169         new_month, test;
17170         dir = dir > 0 ? 1 : -1;
17171         if (mag == 1){
17172             test = dir == -1
17173             // If going back one month, make sure month is not current month
17174             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17175             ? function(){
17176                 return new_date.getUTCMonth() == month;
17177             }
17178             // If going forward one month, make sure month is as expected
17179             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17180             : function(){
17181                 return new_date.getUTCMonth() != new_month;
17182             };
17183             new_month = month + dir;
17184             new_date.setUTCMonth(new_month);
17185             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17186             if (new_month < 0 || new_month > 11)
17187                 new_month = (new_month + 12) % 12;
17188         } else {
17189             // For magnitudes >1, move one month at a time...
17190             for (var i=0; i<mag; i++)
17191                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17192                 new_date = this.moveMonth(new_date, dir);
17193             // ...then reset the day, keeping it in the new month
17194             new_month = new_date.getUTCMonth();
17195             new_date.setUTCDate(day);
17196             test = function(){
17197                 return new_month != new_date.getUTCMonth();
17198             };
17199         }
17200         // Common date-resetting loop -- if date is beyond end of month, make it
17201         // end of month
17202         while (test()){
17203             new_date.setUTCDate(--day);
17204             new_date.setUTCMonth(new_month);
17205         }
17206         return new_date;
17207     },
17208
17209     moveYear: function(date, dir)
17210     {
17211         return this.moveMonth(date, dir*12);
17212     },
17213
17214     dateWithinRange: function(date)
17215     {
17216         return date >= this.startDate && date <= this.endDate;
17217     },
17218
17219     
17220     remove: function() 
17221     {
17222         this.picker().remove();
17223     }
17224    
17225 });
17226
17227 Roo.apply(Roo.bootstrap.DateField,  {
17228     
17229     head : {
17230         tag: 'thead',
17231         cn: [
17232         {
17233             tag: 'tr',
17234             cn: [
17235             {
17236                 tag: 'th',
17237                 cls: 'prev',
17238                 html: '<i class="fa fa-arrow-left"/>'
17239             },
17240             {
17241                 tag: 'th',
17242                 cls: 'switch',
17243                 colspan: '5'
17244             },
17245             {
17246                 tag: 'th',
17247                 cls: 'next',
17248                 html: '<i class="fa fa-arrow-right"/>'
17249             }
17250
17251             ]
17252         }
17253         ]
17254     },
17255     
17256     content : {
17257         tag: 'tbody',
17258         cn: [
17259         {
17260             tag: 'tr',
17261             cn: [
17262             {
17263                 tag: 'td',
17264                 colspan: '7'
17265             }
17266             ]
17267         }
17268         ]
17269     },
17270     
17271     footer : {
17272         tag: 'tfoot',
17273         cn: [
17274         {
17275             tag: 'tr',
17276             cn: [
17277             {
17278                 tag: 'th',
17279                 colspan: '7',
17280                 cls: 'today'
17281             }
17282                     
17283             ]
17284         }
17285         ]
17286     },
17287     
17288     dates:{
17289         en: {
17290             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17291             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17292             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17293             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17294             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17295             today: "Today"
17296         }
17297     },
17298     
17299     modes: [
17300     {
17301         clsName: 'days',
17302         navFnc: 'Month',
17303         navStep: 1
17304     },
17305     {
17306         clsName: 'months',
17307         navFnc: 'FullYear',
17308         navStep: 1
17309     },
17310     {
17311         clsName: 'years',
17312         navFnc: 'FullYear',
17313         navStep: 10
17314     }]
17315 });
17316
17317 Roo.apply(Roo.bootstrap.DateField,  {
17318   
17319     template : {
17320         tag: 'div',
17321         cls: 'datepicker dropdown-menu roo-dynamic',
17322         cn: [
17323         {
17324             tag: 'div',
17325             cls: 'datepicker-days',
17326             cn: [
17327             {
17328                 tag: 'table',
17329                 cls: 'table-condensed',
17330                 cn:[
17331                 Roo.bootstrap.DateField.head,
17332                 {
17333                     tag: 'tbody'
17334                 },
17335                 Roo.bootstrap.DateField.footer
17336                 ]
17337             }
17338             ]
17339         },
17340         {
17341             tag: 'div',
17342             cls: 'datepicker-months',
17343             cn: [
17344             {
17345                 tag: 'table',
17346                 cls: 'table-condensed',
17347                 cn:[
17348                 Roo.bootstrap.DateField.head,
17349                 Roo.bootstrap.DateField.content,
17350                 Roo.bootstrap.DateField.footer
17351                 ]
17352             }
17353             ]
17354         },
17355         {
17356             tag: 'div',
17357             cls: 'datepicker-years',
17358             cn: [
17359             {
17360                 tag: 'table',
17361                 cls: 'table-condensed',
17362                 cn:[
17363                 Roo.bootstrap.DateField.head,
17364                 Roo.bootstrap.DateField.content,
17365                 Roo.bootstrap.DateField.footer
17366                 ]
17367             }
17368             ]
17369         }
17370         ]
17371     }
17372 });
17373
17374  
17375
17376  /*
17377  * - LGPL
17378  *
17379  * TimeField
17380  * 
17381  */
17382
17383 /**
17384  * @class Roo.bootstrap.TimeField
17385  * @extends Roo.bootstrap.Input
17386  * Bootstrap DateField class
17387  * 
17388  * 
17389  * @constructor
17390  * Create a new TimeField
17391  * @param {Object} config The config object
17392  */
17393
17394 Roo.bootstrap.TimeField = function(config){
17395     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17396     this.addEvents({
17397             /**
17398              * @event show
17399              * Fires when this field show.
17400              * @param {Roo.bootstrap.DateField} thisthis
17401              * @param {Mixed} date The date value
17402              */
17403             show : true,
17404             /**
17405              * @event show
17406              * Fires when this field hide.
17407              * @param {Roo.bootstrap.DateField} this
17408              * @param {Mixed} date The date value
17409              */
17410             hide : true,
17411             /**
17412              * @event select
17413              * Fires when select a date.
17414              * @param {Roo.bootstrap.DateField} this
17415              * @param {Mixed} date The date value
17416              */
17417             select : true
17418         });
17419 };
17420
17421 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17422     
17423     /**
17424      * @cfg {String} format
17425      * The default time format string which can be overriden for localization support.  The format must be
17426      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17427      */
17428     format : "H:i",
17429        
17430     onRender: function(ct, position)
17431     {
17432         
17433         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17434                 
17435         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17436         
17437         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17438         
17439         this.pop = this.picker().select('>.datepicker-time',true).first();
17440         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17441         
17442         this.picker().on('mousedown', this.onMousedown, this);
17443         this.picker().on('click', this.onClick, this);
17444         
17445         this.picker().addClass('datepicker-dropdown');
17446     
17447         this.fillTime();
17448         this.update();
17449             
17450         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17451         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17452         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17453         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17454         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17455         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17456
17457     },
17458     
17459     fireKey: function(e){
17460         if (!this.picker().isVisible()){
17461             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17462                 this.show();
17463             }
17464             return;
17465         }
17466
17467         e.preventDefault();
17468         
17469         switch(e.keyCode){
17470             case 27: // escape
17471                 this.hide();
17472                 break;
17473             case 37: // left
17474             case 39: // right
17475                 this.onTogglePeriod();
17476                 break;
17477             case 38: // up
17478                 this.onIncrementMinutes();
17479                 break;
17480             case 40: // down
17481                 this.onDecrementMinutes();
17482                 break;
17483             case 13: // enter
17484             case 9: // tab
17485                 this.setTime();
17486                 break;
17487         }
17488     },
17489     
17490     onClick: function(e) {
17491         e.stopPropagation();
17492         e.preventDefault();
17493     },
17494     
17495     picker : function()
17496     {
17497         return this.el.select('.datepicker', true).first();
17498     },
17499     
17500     fillTime: function()
17501     {    
17502         var time = this.pop.select('tbody', true).first();
17503         
17504         time.dom.innerHTML = '';
17505         
17506         time.createChild({
17507             tag: 'tr',
17508             cn: [
17509                 {
17510                     tag: 'td',
17511                     cn: [
17512                         {
17513                             tag: 'a',
17514                             href: '#',
17515                             cls: 'btn',
17516                             cn: [
17517                                 {
17518                                     tag: 'span',
17519                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17520                                 }
17521                             ]
17522                         } 
17523                     ]
17524                 },
17525                 {
17526                     tag: 'td',
17527                     cls: 'separator'
17528                 },
17529                 {
17530                     tag: 'td',
17531                     cn: [
17532                         {
17533                             tag: 'a',
17534                             href: '#',
17535                             cls: 'btn',
17536                             cn: [
17537                                 {
17538                                     tag: 'span',
17539                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17540                                 }
17541                             ]
17542                         }
17543                     ]
17544                 },
17545                 {
17546                     tag: 'td',
17547                     cls: 'separator'
17548                 }
17549             ]
17550         });
17551         
17552         time.createChild({
17553             tag: 'tr',
17554             cn: [
17555                 {
17556                     tag: 'td',
17557                     cn: [
17558                         {
17559                             tag: 'span',
17560                             cls: 'timepicker-hour',
17561                             html: '00'
17562                         }  
17563                     ]
17564                 },
17565                 {
17566                     tag: 'td',
17567                     cls: 'separator',
17568                     html: ':'
17569                 },
17570                 {
17571                     tag: 'td',
17572                     cn: [
17573                         {
17574                             tag: 'span',
17575                             cls: 'timepicker-minute',
17576                             html: '00'
17577                         }  
17578                     ]
17579                 },
17580                 {
17581                     tag: 'td',
17582                     cls: 'separator'
17583                 },
17584                 {
17585                     tag: 'td',
17586                     cn: [
17587                         {
17588                             tag: 'button',
17589                             type: 'button',
17590                             cls: 'btn btn-primary period',
17591                             html: 'AM'
17592                             
17593                         }
17594                     ]
17595                 }
17596             ]
17597         });
17598         
17599         time.createChild({
17600             tag: 'tr',
17601             cn: [
17602                 {
17603                     tag: 'td',
17604                     cn: [
17605                         {
17606                             tag: 'a',
17607                             href: '#',
17608                             cls: 'btn',
17609                             cn: [
17610                                 {
17611                                     tag: 'span',
17612                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17613                                 }
17614                             ]
17615                         }
17616                     ]
17617                 },
17618                 {
17619                     tag: 'td',
17620                     cls: 'separator'
17621                 },
17622                 {
17623                     tag: 'td',
17624                     cn: [
17625                         {
17626                             tag: 'a',
17627                             href: '#',
17628                             cls: 'btn',
17629                             cn: [
17630                                 {
17631                                     tag: 'span',
17632                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17633                                 }
17634                             ]
17635                         }
17636                     ]
17637                 },
17638                 {
17639                     tag: 'td',
17640                     cls: 'separator'
17641                 }
17642             ]
17643         });
17644         
17645     },
17646     
17647     update: function()
17648     {
17649         
17650         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17651         
17652         this.fill();
17653     },
17654     
17655     fill: function() 
17656     {
17657         var hours = this.time.getHours();
17658         var minutes = this.time.getMinutes();
17659         var period = 'AM';
17660         
17661         if(hours > 11){
17662             period = 'PM';
17663         }
17664         
17665         if(hours == 0){
17666             hours = 12;
17667         }
17668         
17669         
17670         if(hours > 12){
17671             hours = hours - 12;
17672         }
17673         
17674         if(hours < 10){
17675             hours = '0' + hours;
17676         }
17677         
17678         if(minutes < 10){
17679             minutes = '0' + minutes;
17680         }
17681         
17682         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17683         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17684         this.pop.select('button', true).first().dom.innerHTML = period;
17685         
17686     },
17687     
17688     place: function()
17689     {   
17690         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17691         
17692         var cls = ['bottom'];
17693         
17694         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17695             cls.pop();
17696             cls.push('top');
17697         }
17698         
17699         cls.push('right');
17700         
17701         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17702             cls.pop();
17703             cls.push('left');
17704         }
17705         
17706         this.picker().addClass(cls.join('-'));
17707         
17708         var _this = this;
17709         
17710         Roo.each(cls, function(c){
17711             if(c == 'bottom'){
17712                 _this.picker().setTop(_this.inputEl().getHeight());
17713                 return;
17714             }
17715             if(c == 'top'){
17716                 _this.picker().setTop(0 - _this.picker().getHeight());
17717                 return;
17718             }
17719             
17720             if(c == 'left'){
17721                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17722                 return;
17723             }
17724             if(c == 'right'){
17725                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17726                 return;
17727             }
17728         });
17729         
17730     },
17731   
17732     onFocus : function()
17733     {
17734         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17735         this.show();
17736     },
17737     
17738     onBlur : function()
17739     {
17740         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17741         this.hide();
17742     },
17743     
17744     show : function()
17745     {
17746         this.picker().show();
17747         this.pop.show();
17748         this.update();
17749         this.place();
17750         
17751         this.fireEvent('show', this, this.date);
17752     },
17753     
17754     hide : function()
17755     {
17756         this.picker().hide();
17757         this.pop.hide();
17758         
17759         this.fireEvent('hide', this, this.date);
17760     },
17761     
17762     setTime : function()
17763     {
17764         this.hide();
17765         this.setValue(this.time.format(this.format));
17766         
17767         this.fireEvent('select', this, this.date);
17768         
17769         
17770     },
17771     
17772     onMousedown: function(e){
17773         e.stopPropagation();
17774         e.preventDefault();
17775     },
17776     
17777     onIncrementHours: function()
17778     {
17779         Roo.log('onIncrementHours');
17780         this.time = this.time.add(Date.HOUR, 1);
17781         this.update();
17782         
17783     },
17784     
17785     onDecrementHours: function()
17786     {
17787         Roo.log('onDecrementHours');
17788         this.time = this.time.add(Date.HOUR, -1);
17789         this.update();
17790     },
17791     
17792     onIncrementMinutes: function()
17793     {
17794         Roo.log('onIncrementMinutes');
17795         this.time = this.time.add(Date.MINUTE, 1);
17796         this.update();
17797     },
17798     
17799     onDecrementMinutes: function()
17800     {
17801         Roo.log('onDecrementMinutes');
17802         this.time = this.time.add(Date.MINUTE, -1);
17803         this.update();
17804     },
17805     
17806     onTogglePeriod: function()
17807     {
17808         Roo.log('onTogglePeriod');
17809         this.time = this.time.add(Date.HOUR, 12);
17810         this.update();
17811     }
17812     
17813    
17814 });
17815
17816 Roo.apply(Roo.bootstrap.TimeField,  {
17817     
17818     content : {
17819         tag: 'tbody',
17820         cn: [
17821             {
17822                 tag: 'tr',
17823                 cn: [
17824                 {
17825                     tag: 'td',
17826                     colspan: '7'
17827                 }
17828                 ]
17829             }
17830         ]
17831     },
17832     
17833     footer : {
17834         tag: 'tfoot',
17835         cn: [
17836             {
17837                 tag: 'tr',
17838                 cn: [
17839                 {
17840                     tag: 'th',
17841                     colspan: '7',
17842                     cls: '',
17843                     cn: [
17844                         {
17845                             tag: 'button',
17846                             cls: 'btn btn-info ok',
17847                             html: 'OK'
17848                         }
17849                     ]
17850                 }
17851
17852                 ]
17853             }
17854         ]
17855     }
17856 });
17857
17858 Roo.apply(Roo.bootstrap.TimeField,  {
17859   
17860     template : {
17861         tag: 'div',
17862         cls: 'datepicker dropdown-menu',
17863         cn: [
17864             {
17865                 tag: 'div',
17866                 cls: 'datepicker-time',
17867                 cn: [
17868                 {
17869                     tag: 'table',
17870                     cls: 'table-condensed',
17871                     cn:[
17872                     Roo.bootstrap.TimeField.content,
17873                     Roo.bootstrap.TimeField.footer
17874                     ]
17875                 }
17876                 ]
17877             }
17878         ]
17879     }
17880 });
17881
17882  
17883
17884  /*
17885  * - LGPL
17886  *
17887  * MonthField
17888  * 
17889  */
17890
17891 /**
17892  * @class Roo.bootstrap.MonthField
17893  * @extends Roo.bootstrap.Input
17894  * Bootstrap MonthField class
17895  * 
17896  * @cfg {String} language default en
17897  * 
17898  * @constructor
17899  * Create a new MonthField
17900  * @param {Object} config The config object
17901  */
17902
17903 Roo.bootstrap.MonthField = function(config){
17904     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17905     
17906     this.addEvents({
17907         /**
17908          * @event show
17909          * Fires when this field show.
17910          * @param {Roo.bootstrap.MonthField} this
17911          * @param {Mixed} date The date value
17912          */
17913         show : true,
17914         /**
17915          * @event show
17916          * Fires when this field hide.
17917          * @param {Roo.bootstrap.MonthField} this
17918          * @param {Mixed} date The date value
17919          */
17920         hide : true,
17921         /**
17922          * @event select
17923          * Fires when select a date.
17924          * @param {Roo.bootstrap.MonthField} this
17925          * @param {String} oldvalue The old value
17926          * @param {String} newvalue The new value
17927          */
17928         select : true
17929     });
17930 };
17931
17932 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17933     
17934     onRender: function(ct, position)
17935     {
17936         
17937         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17938         
17939         this.language = this.language || 'en';
17940         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17941         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17942         
17943         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17944         this.isInline = false;
17945         this.isInput = true;
17946         this.component = this.el.select('.add-on', true).first() || false;
17947         this.component = (this.component && this.component.length === 0) ? false : this.component;
17948         this.hasInput = this.component && this.inputEL().length;
17949         
17950         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17951         
17952         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17953         
17954         this.picker().on('mousedown', this.onMousedown, this);
17955         this.picker().on('click', this.onClick, this);
17956         
17957         this.picker().addClass('datepicker-dropdown');
17958         
17959         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17960             v.setStyle('width', '189px');
17961         });
17962         
17963         this.fillMonths();
17964         
17965         this.update();
17966         
17967         if(this.isInline) {
17968             this.show();
17969         }
17970         
17971     },
17972     
17973     setValue: function(v, suppressEvent)
17974     {   
17975         var o = this.getValue();
17976         
17977         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17978         
17979         this.update();
17980
17981         if(suppressEvent !== true){
17982             this.fireEvent('select', this, o, v);
17983         }
17984         
17985     },
17986     
17987     getValue: function()
17988     {
17989         return this.value;
17990     },
17991     
17992     onClick: function(e) 
17993     {
17994         e.stopPropagation();
17995         e.preventDefault();
17996         
17997         var target = e.getTarget();
17998         
17999         if(target.nodeName.toLowerCase() === 'i'){
18000             target = Roo.get(target).dom.parentNode;
18001         }
18002         
18003         var nodeName = target.nodeName;
18004         var className = target.className;
18005         var html = target.innerHTML;
18006         
18007         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18008             return;
18009         }
18010         
18011         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18012         
18013         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18014         
18015         this.hide();
18016                         
18017     },
18018     
18019     picker : function()
18020     {
18021         return this.pickerEl;
18022     },
18023     
18024     fillMonths: function()
18025     {    
18026         var i = 0;
18027         var months = this.picker().select('>.datepicker-months td', true).first();
18028         
18029         months.dom.innerHTML = '';
18030         
18031         while (i < 12) {
18032             var month = {
18033                 tag: 'span',
18034                 cls: 'month',
18035                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18036             };
18037             
18038             months.createChild(month);
18039         }
18040         
18041     },
18042     
18043     update: function()
18044     {
18045         var _this = this;
18046         
18047         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18048             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18049         }
18050         
18051         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18052             e.removeClass('active');
18053             
18054             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18055                 e.addClass('active');
18056             }
18057         })
18058     },
18059     
18060     place: function()
18061     {
18062         if(this.isInline) return;
18063         
18064         this.picker().removeClass(['bottom', 'top']);
18065         
18066         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18067             /*
18068              * place to the top of element!
18069              *
18070              */
18071             
18072             this.picker().addClass('top');
18073             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18074             
18075             return;
18076         }
18077         
18078         this.picker().addClass('bottom');
18079         
18080         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18081     },
18082     
18083     onFocus : function()
18084     {
18085         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18086         this.show();
18087     },
18088     
18089     onBlur : function()
18090     {
18091         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18092         
18093         var d = this.inputEl().getValue();
18094         
18095         this.setValue(d);
18096                 
18097         this.hide();
18098     },
18099     
18100     show : function()
18101     {
18102         this.picker().show();
18103         this.picker().select('>.datepicker-months', true).first().show();
18104         this.update();
18105         this.place();
18106         
18107         this.fireEvent('show', this, this.date);
18108     },
18109     
18110     hide : function()
18111     {
18112         if(this.isInline) return;
18113         this.picker().hide();
18114         this.fireEvent('hide', this, this.date);
18115         
18116     },
18117     
18118     onMousedown: function(e)
18119     {
18120         e.stopPropagation();
18121         e.preventDefault();
18122     },
18123     
18124     keyup: function(e)
18125     {
18126         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18127         this.update();
18128     },
18129
18130     fireKey: function(e)
18131     {
18132         if (!this.picker().isVisible()){
18133             if (e.keyCode == 27) // allow escape to hide and re-show picker
18134                 this.show();
18135             return;
18136         }
18137         
18138         var dir;
18139         
18140         switch(e.keyCode){
18141             case 27: // escape
18142                 this.hide();
18143                 e.preventDefault();
18144                 break;
18145             case 37: // left
18146             case 39: // right
18147                 dir = e.keyCode == 37 ? -1 : 1;
18148                 
18149                 this.vIndex = this.vIndex + dir;
18150                 
18151                 if(this.vIndex < 0){
18152                     this.vIndex = 0;
18153                 }
18154                 
18155                 if(this.vIndex > 11){
18156                     this.vIndex = 11;
18157                 }
18158                 
18159                 if(isNaN(this.vIndex)){
18160                     this.vIndex = 0;
18161                 }
18162                 
18163                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18164                 
18165                 break;
18166             case 38: // up
18167             case 40: // down
18168                 
18169                 dir = e.keyCode == 38 ? -1 : 1;
18170                 
18171                 this.vIndex = this.vIndex + dir * 4;
18172                 
18173                 if(this.vIndex < 0){
18174                     this.vIndex = 0;
18175                 }
18176                 
18177                 if(this.vIndex > 11){
18178                     this.vIndex = 11;
18179                 }
18180                 
18181                 if(isNaN(this.vIndex)){
18182                     this.vIndex = 0;
18183                 }
18184                 
18185                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18186                 break;
18187                 
18188             case 13: // enter
18189                 
18190                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18191                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18192                 }
18193                 
18194                 this.hide();
18195                 e.preventDefault();
18196                 break;
18197             case 9: // tab
18198                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18199                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18200                 }
18201                 this.hide();
18202                 break;
18203             case 16: // shift
18204             case 17: // ctrl
18205             case 18: // alt
18206                 break;
18207             default :
18208                 this.hide();
18209                 
18210         }
18211     },
18212     
18213     remove: function() 
18214     {
18215         this.picker().remove();
18216     }
18217    
18218 });
18219
18220 Roo.apply(Roo.bootstrap.MonthField,  {
18221     
18222     content : {
18223         tag: 'tbody',
18224         cn: [
18225         {
18226             tag: 'tr',
18227             cn: [
18228             {
18229                 tag: 'td',
18230                 colspan: '7'
18231             }
18232             ]
18233         }
18234         ]
18235     },
18236     
18237     dates:{
18238         en: {
18239             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18240             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18241         }
18242     }
18243 });
18244
18245 Roo.apply(Roo.bootstrap.MonthField,  {
18246   
18247     template : {
18248         tag: 'div',
18249         cls: 'datepicker dropdown-menu roo-dynamic',
18250         cn: [
18251             {
18252                 tag: 'div',
18253                 cls: 'datepicker-months',
18254                 cn: [
18255                 {
18256                     tag: 'table',
18257                     cls: 'table-condensed',
18258                     cn:[
18259                         Roo.bootstrap.DateField.content
18260                     ]
18261                 }
18262                 ]
18263             }
18264         ]
18265     }
18266 });
18267
18268  
18269
18270  
18271  /*
18272  * - LGPL
18273  *
18274  * CheckBox
18275  * 
18276  */
18277
18278 /**
18279  * @class Roo.bootstrap.CheckBox
18280  * @extends Roo.bootstrap.Input
18281  * Bootstrap CheckBox class
18282  * 
18283  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18284  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18285  * @cfg {String} boxLabel The text that appears beside the checkbox
18286  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18287  * @cfg {Boolean} checked initnal the element
18288  * @cfg {Boolean} inline inline the element (default false)
18289  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18290  * 
18291  * @constructor
18292  * Create a new CheckBox
18293  * @param {Object} config The config object
18294  */
18295
18296 Roo.bootstrap.CheckBox = function(config){
18297     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18298    
18299     this.addEvents({
18300         /**
18301         * @event check
18302         * Fires when the element is checked or unchecked.
18303         * @param {Roo.bootstrap.CheckBox} this This input
18304         * @param {Boolean} checked The new checked value
18305         */
18306        check : true
18307     });
18308     
18309 };
18310
18311 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18312   
18313     inputType: 'checkbox',
18314     inputValue: 1,
18315     valueOff: 0,
18316     boxLabel: false,
18317     checked: false,
18318     weight : false,
18319     inline: false,
18320     
18321     getAutoCreate : function()
18322     {
18323         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18324         
18325         var id = Roo.id();
18326         
18327         var cfg = {};
18328         
18329         cfg.cls = 'form-group ' + this.inputType; //input-group
18330         
18331         if(this.inline){
18332             cfg.cls += ' ' + this.inputType + '-inline';
18333         }
18334         
18335         var input =  {
18336             tag: 'input',
18337             id : id,
18338             type : this.inputType,
18339             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18340             cls : 'roo-' + this.inputType, //'form-box',
18341             placeholder : this.placeholder || ''
18342             
18343         };
18344         
18345         if (this.weight) { // Validity check?
18346             cfg.cls += " " + this.inputType + "-" + this.weight;
18347         }
18348         
18349         if (this.disabled) {
18350             input.disabled=true;
18351         }
18352         
18353         if(this.checked){
18354             input.checked = this.checked;
18355         }
18356         
18357         if (this.name) {
18358             input.name = this.name;
18359         }
18360         
18361         if (this.size) {
18362             input.cls += ' input-' + this.size;
18363         }
18364         
18365         var settings=this;
18366         
18367         ['xs','sm','md','lg'].map(function(size){
18368             if (settings[size]) {
18369                 cfg.cls += ' col-' + size + '-' + settings[size];
18370             }
18371         });
18372         
18373         var inputblock = input;
18374          
18375         if (this.before || this.after) {
18376             
18377             inputblock = {
18378                 cls : 'input-group',
18379                 cn :  [] 
18380             };
18381             
18382             if (this.before) {
18383                 inputblock.cn.push({
18384                     tag :'span',
18385                     cls : 'input-group-addon',
18386                     html : this.before
18387                 });
18388             }
18389             
18390             inputblock.cn.push(input);
18391             
18392             if (this.after) {
18393                 inputblock.cn.push({
18394                     tag :'span',
18395                     cls : 'input-group-addon',
18396                     html : this.after
18397                 });
18398             }
18399             
18400         }
18401         
18402         if (align ==='left' && this.fieldLabel.length) {
18403                 Roo.log("left and has label");
18404                 cfg.cn = [
18405                     
18406                     {
18407                         tag: 'label',
18408                         'for' :  id,
18409                         cls : 'control-label col-md-' + this.labelWidth,
18410                         html : this.fieldLabel
18411                         
18412                     },
18413                     {
18414                         cls : "col-md-" + (12 - this.labelWidth), 
18415                         cn: [
18416                             inputblock
18417                         ]
18418                     }
18419                     
18420                 ];
18421         } else if ( this.fieldLabel.length) {
18422                 Roo.log(" label");
18423                 cfg.cn = [
18424                    
18425                     {
18426                         tag: this.boxLabel ? 'span' : 'label',
18427                         'for': id,
18428                         cls: 'control-label box-input-label',
18429                         //cls : 'input-group-addon',
18430                         html : this.fieldLabel
18431                         
18432                     },
18433                     
18434                     inputblock
18435                     
18436                 ];
18437
18438         } else {
18439             
18440                 Roo.log(" no label && no align");
18441                 cfg.cn = [  inputblock ] ;
18442                 
18443                 
18444         }
18445         if(this.boxLabel){
18446              var boxLabelCfg = {
18447                 tag: 'label',
18448                 //'for': id, // box label is handled by onclick - so no for...
18449                 cls: 'box-label',
18450                 html: this.boxLabel
18451             };
18452             
18453             if(this.tooltip){
18454                 boxLabelCfg.tooltip = this.tooltip;
18455             }
18456              
18457             cfg.cn.push(boxLabelCfg);
18458         }
18459         
18460         
18461        
18462         return cfg;
18463         
18464     },
18465     
18466     /**
18467      * return the real input element.
18468      */
18469     inputEl: function ()
18470     {
18471         return this.el.select('input.roo-' + this.inputType,true).first();
18472     },
18473     
18474     labelEl: function()
18475     {
18476         return this.el.select('label.control-label',true).first();
18477     },
18478     /* depricated... */
18479     
18480     label: function()
18481     {
18482         return this.labelEl();
18483     },
18484     
18485     initEvents : function()
18486     {
18487 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18488         
18489         this.inputEl().on('click', this.onClick,  this);
18490         
18491         if (this.boxLabel) { 
18492             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18493         }
18494         
18495         this.startValue = this.getValue();
18496         
18497         if(this.groupId){
18498             Roo.bootstrap.CheckBox.register(this);
18499         }
18500     },
18501     
18502     onClick : function()
18503     {   
18504         this.setChecked(!this.checked);
18505     },
18506     
18507     setChecked : function(state,suppressEvent)
18508     {
18509         this.startValue = this.getValue();
18510         
18511         if(this.inputType == 'radio'){
18512             
18513             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18514                 e.dom.checked = false;
18515             });
18516             
18517             this.inputEl().dom.checked = true;
18518             
18519             this.inputEl().dom.value = this.inputValue;
18520             
18521             if(suppressEvent !== true){
18522                 this.fireEvent('check', this, true);
18523             }
18524             
18525             this.validate();
18526             
18527             return;
18528         }
18529         
18530         this.checked = state;
18531         
18532         this.inputEl().dom.checked = state;
18533         
18534         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18535         
18536         if(suppressEvent !== true){
18537             this.fireEvent('check', this, state);
18538         }
18539         
18540         this.validate();
18541     },
18542     
18543     getValue : function()
18544     {
18545         if(this.inputType == 'radio'){
18546             return this.getGroupValue();
18547         }
18548         
18549         return this.inputEl().getValue();
18550         
18551     },
18552     
18553     getGroupValue : function()
18554     {
18555         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18556             return '';
18557         }
18558         
18559         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18560     },
18561     
18562     setValue : function(v,suppressEvent)
18563     {
18564         if(this.inputType == 'radio'){
18565             this.setGroupValue(v, suppressEvent);
18566             return;
18567         }
18568         
18569         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18570         
18571         this.validate();
18572     },
18573     
18574     setGroupValue : function(v, suppressEvent)
18575     {
18576         this.startValue = this.getValue();
18577         
18578         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18579             e.dom.checked = false;
18580             
18581             if(e.dom.value == v){
18582                 e.dom.checked = true;
18583             }
18584         });
18585         
18586         if(suppressEvent !== true){
18587             this.fireEvent('check', this, true);
18588         }
18589
18590         this.validate();
18591         
18592         return;
18593     },
18594     
18595     validate : function()
18596     {
18597         if(
18598                 this.disabled || 
18599                 (this.inputType == 'radio' && this.validateRadio()) ||
18600                 (this.inputType == 'checkbox' && this.validateCheckbox())
18601         ){
18602             this.markValid();
18603             return true;
18604         }
18605         
18606         this.markInvalid();
18607         return false;
18608     },
18609     
18610     validateRadio : function()
18611     {
18612         var valid = false;
18613         
18614         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18615             if(!e.dom.checked){
18616                 return;
18617             }
18618             
18619             valid = true;
18620             
18621             return false;
18622         });
18623         
18624         return valid;
18625     },
18626     
18627     validateCheckbox : function()
18628     {
18629         if(!this.groupId){
18630             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18631         }
18632         
18633         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18634         
18635         if(!group){
18636             return false;
18637         }
18638         
18639         var r = false;
18640         
18641         for(var i in group){
18642             if(r){
18643                 break;
18644             }
18645             
18646             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18647         }
18648         
18649         return r;
18650     },
18651     
18652     /**
18653      * Mark this field as valid
18654      */
18655     markValid : function()
18656     {
18657         if(this.allowBlank){
18658             return;
18659         }
18660         
18661         var _this = this;
18662         
18663         this.fireEvent('valid', this);
18664         
18665         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18666         
18667         if(this.groupId){
18668             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18669         }
18670         
18671         if(label){
18672             label.markValid();
18673         }
18674         
18675         if(this.inputType == 'radio'){
18676             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18677                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18678                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18679             });
18680             
18681             return;
18682         }
18683         
18684         if(!this.groupId){
18685             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18686             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18687             return;
18688         }
18689         
18690         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18691             
18692         if(!group){
18693             return;
18694         }
18695         
18696         for(var i in group){
18697             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18698             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18699         }
18700     },
18701     
18702      /**
18703      * Mark this field as invalid
18704      * @param {String} msg The validation message
18705      */
18706     markInvalid : function(msg)
18707     {
18708         if(this.allowBlank){
18709             return;
18710         }
18711         
18712         var _this = this;
18713         
18714         this.fireEvent('invalid', this, msg);
18715         
18716         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18717         
18718         if(this.groupId){
18719             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18720         }
18721         
18722         if(label){
18723             label.markInvalid();
18724         }
18725             
18726         if(this.inputType == 'radio'){
18727             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18728                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18729                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18730             });
18731             
18732             return;
18733         }
18734         
18735         if(!this.groupId){
18736             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18737             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18738             return;
18739         }
18740         
18741         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18742         
18743         if(!group){
18744             return;
18745         }
18746         
18747         for(var i in group){
18748             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18749             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18750         }
18751         
18752     }
18753     
18754 });
18755
18756 Roo.apply(Roo.bootstrap.CheckBox, {
18757     
18758     groups: {},
18759     
18760      /**
18761     * register a CheckBox Group
18762     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18763     */
18764     register : function(checkbox)
18765     {
18766         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18767             this.groups[checkbox.groupId] = {};
18768         }
18769         
18770         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18771             return;
18772         }
18773         
18774         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18775         
18776     },
18777     /**
18778     * fetch a CheckBox Group based on the group ID
18779     * @param {string} the group ID
18780     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18781     */
18782     get: function(groupId) {
18783         if (typeof(this.groups[groupId]) == 'undefined') {
18784             return false;
18785         }
18786         
18787         return this.groups[groupId] ;
18788     }
18789     
18790     
18791 });
18792 /*
18793  * - LGPL
18794  *
18795  * Radio
18796  *
18797  *
18798  * not inline
18799  *<div class="radio">
18800   <label>
18801     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18802     Option one is this and that&mdash;be sure to include why it's great
18803   </label>
18804 </div>
18805  *
18806  *
18807  *inline
18808  *<span>
18809  *<label class="radio-inline">fieldLabel</label>
18810  *<label class="radio-inline">
18811   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18812 </label>
18813 <span>
18814  * 
18815  * 
18816  */
18817
18818 /**
18819  * @class Roo.bootstrap.Radio
18820  * @extends Roo.bootstrap.CheckBox
18821  * Bootstrap Radio class
18822
18823  * @constructor
18824  * Create a new Radio
18825  * @param {Object} config The config object
18826  */
18827
18828 Roo.bootstrap.Radio = function(config){
18829     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18830    
18831 };
18832
18833 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18834     
18835     inputType: 'radio',
18836     inputValue: '',
18837     valueOff: '',
18838     
18839     getAutoCreate : function()
18840     {
18841         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18842         align = align || 'left'; // default...
18843         
18844         
18845         
18846         var id = Roo.id();
18847         
18848         var cfg = {
18849                 tag : this.inline ? 'span' : 'div',
18850                 cls : '',
18851                 cn : []
18852         };
18853         
18854         var inline = this.inline ? ' radio-inline' : '';
18855         
18856         var lbl = {
18857                 tag: 'label' ,
18858                 // does not need for, as we wrap the input with it..
18859                 'for' : id,
18860                 cls : 'control-label box-label' + inline,
18861                 cn : []
18862         };
18863         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18864         
18865         var fieldLabel = {
18866             tag: 'label' ,
18867             //cls : 'control-label' + inline,
18868             html : this.fieldLabel,
18869             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18870         };
18871         
18872  
18873         
18874         
18875         var input =  {
18876             tag: 'input',
18877             id : id,
18878             type : this.inputType,
18879             //value : (!this.checked) ? this.valueOff : this.inputValue,
18880             value : this.inputValue,
18881             cls : 'roo-radio',
18882             placeholder : this.placeholder || '' // ?? needed????
18883             
18884         };
18885         if (this.weight) { // Validity check?
18886             input.cls += " radio-" + this.weight;
18887         }
18888         if (this.disabled) {
18889             input.disabled=true;
18890         }
18891         
18892         if(this.checked){
18893             input.checked = this.checked;
18894         }
18895         
18896         if (this.name) {
18897             input.name = this.name;
18898         }
18899         
18900         if (this.size) {
18901             input.cls += ' input-' + this.size;
18902         }
18903         
18904         //?? can span's inline have a width??
18905         
18906         var settings=this;
18907         ['xs','sm','md','lg'].map(function(size){
18908             if (settings[size]) {
18909                 cfg.cls += ' col-' + size + '-' + settings[size];
18910             }
18911         });
18912         
18913         var inputblock = input;
18914         
18915         if (this.before || this.after) {
18916             
18917             inputblock = {
18918                 cls : 'input-group',
18919                 tag : 'span',
18920                 cn :  [] 
18921             };
18922             if (this.before) {
18923                 inputblock.cn.push({
18924                     tag :'span',
18925                     cls : 'input-group-addon',
18926                     html : this.before
18927                 });
18928             }
18929             inputblock.cn.push(input);
18930             if (this.after) {
18931                 inputblock.cn.push({
18932                     tag :'span',
18933                     cls : 'input-group-addon',
18934                     html : this.after
18935                 });
18936             }
18937             
18938         };
18939         
18940         
18941         if (this.fieldLabel && this.fieldLabel.length) {
18942             cfg.cn.push(fieldLabel);
18943         }
18944        
18945         // normal bootstrap puts the input inside the label.
18946         // however with our styled version - it has to go after the input.
18947        
18948         //lbl.cn.push(inputblock);
18949         
18950         var lblwrap =  {
18951             tag: 'span',
18952             cls: 'radio' + inline,
18953             cn: [
18954                 inputblock,
18955                 lbl
18956             ]
18957         };
18958         
18959         cfg.cn.push( lblwrap);
18960         
18961         if(this.boxLabel){
18962             lbl.cn.push({
18963                 tag: 'span',
18964                 html: this.boxLabel
18965             })
18966         }
18967          
18968         
18969         return cfg;
18970         
18971     },
18972     
18973     initEvents : function()
18974     {
18975 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18976         
18977         this.inputEl().on('click', this.onClick,  this);
18978         if (this.boxLabel) {
18979             Roo.log('find label')
18980             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18981         }
18982         
18983     },
18984     
18985     inputEl: function ()
18986     {
18987         return this.el.select('input.roo-radio',true).first();
18988     },
18989     onClick : function()
18990     {   
18991         Roo.log("click");
18992         this.setChecked(true);
18993     },
18994     
18995     setChecked : function(state,suppressEvent)
18996     {
18997         if(state){
18998             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18999                 v.dom.checked = false;
19000             });
19001         }
19002         Roo.log(this.inputEl().dom);
19003         this.checked = state;
19004         this.inputEl().dom.checked = state;
19005         
19006         if(suppressEvent !== true){
19007             this.fireEvent('check', this, state);
19008         }
19009         
19010         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19011         
19012     },
19013     
19014     getGroupValue : function()
19015     {
19016         var value = '';
19017         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19018             if(v.dom.checked == true){
19019                 value = v.dom.value;
19020             }
19021         });
19022         
19023         return value;
19024     },
19025     
19026     /**
19027      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19028      * @return {Mixed} value The field value
19029      */
19030     getValue : function(){
19031         return this.getGroupValue();
19032     }
19033     
19034 });
19035
19036  
19037 //<script type="text/javascript">
19038
19039 /*
19040  * Based  Ext JS Library 1.1.1
19041  * Copyright(c) 2006-2007, Ext JS, LLC.
19042  * LGPL
19043  *
19044  */
19045  
19046 /**
19047  * @class Roo.HtmlEditorCore
19048  * @extends Roo.Component
19049  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19050  *
19051  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19052  */
19053
19054 Roo.HtmlEditorCore = function(config){
19055     
19056     
19057     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19058     
19059     
19060     this.addEvents({
19061         /**
19062          * @event initialize
19063          * Fires when the editor is fully initialized (including the iframe)
19064          * @param {Roo.HtmlEditorCore} this
19065          */
19066         initialize: true,
19067         /**
19068          * @event activate
19069          * Fires when the editor is first receives the focus. Any insertion must wait
19070          * until after this event.
19071          * @param {Roo.HtmlEditorCore} this
19072          */
19073         activate: true,
19074          /**
19075          * @event beforesync
19076          * Fires before the textarea is updated with content from the editor iframe. Return false
19077          * to cancel the sync.
19078          * @param {Roo.HtmlEditorCore} this
19079          * @param {String} html
19080          */
19081         beforesync: true,
19082          /**
19083          * @event beforepush
19084          * Fires before the iframe editor is updated with content from the textarea. Return false
19085          * to cancel the push.
19086          * @param {Roo.HtmlEditorCore} this
19087          * @param {String} html
19088          */
19089         beforepush: true,
19090          /**
19091          * @event sync
19092          * Fires when the textarea is updated with content from the editor iframe.
19093          * @param {Roo.HtmlEditorCore} this
19094          * @param {String} html
19095          */
19096         sync: true,
19097          /**
19098          * @event push
19099          * Fires when the iframe editor is updated with content from the textarea.
19100          * @param {Roo.HtmlEditorCore} this
19101          * @param {String} html
19102          */
19103         push: true,
19104         
19105         /**
19106          * @event editorevent
19107          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19108          * @param {Roo.HtmlEditorCore} this
19109          */
19110         editorevent: true
19111         
19112     });
19113     
19114     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19115     
19116     // defaults : white / black...
19117     this.applyBlacklists();
19118     
19119     
19120     
19121 };
19122
19123
19124 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19125
19126
19127      /**
19128      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19129      */
19130     
19131     owner : false,
19132     
19133      /**
19134      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19135      *                        Roo.resizable.
19136      */
19137     resizable : false,
19138      /**
19139      * @cfg {Number} height (in pixels)
19140      */   
19141     height: 300,
19142    /**
19143      * @cfg {Number} width (in pixels)
19144      */   
19145     width: 500,
19146     
19147     /**
19148      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19149      * 
19150      */
19151     stylesheets: false,
19152     
19153     // id of frame..
19154     frameId: false,
19155     
19156     // private properties
19157     validationEvent : false,
19158     deferHeight: true,
19159     initialized : false,
19160     activated : false,
19161     sourceEditMode : false,
19162     onFocus : Roo.emptyFn,
19163     iframePad:3,
19164     hideMode:'offsets',
19165     
19166     clearUp: true,
19167     
19168     // blacklist + whitelisted elements..
19169     black: false,
19170     white: false,
19171      
19172     
19173
19174     /**
19175      * Protected method that will not generally be called directly. It
19176      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19177      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19178      */
19179     getDocMarkup : function(){
19180         // body styles..
19181         var st = '';
19182         
19183         // inherit styels from page...?? 
19184         if (this.stylesheets === false) {
19185             
19186             Roo.get(document.head).select('style').each(function(node) {
19187                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19188             });
19189             
19190             Roo.get(document.head).select('link').each(function(node) { 
19191                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19192             });
19193             
19194         } else if (!this.stylesheets.length) {
19195                 // simple..
19196                 st = '<style type="text/css">' +
19197                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19198                    '</style>';
19199         } else { 
19200             
19201         }
19202         
19203         st +=  '<style type="text/css">' +
19204             'IMG { cursor: pointer } ' +
19205         '</style>';
19206
19207         
19208         return '<html><head>' + st  +
19209             //<style type="text/css">' +
19210             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19211             //'</style>' +
19212             ' </head><body class="roo-htmleditor-body"></body></html>';
19213     },
19214
19215     // private
19216     onRender : function(ct, position)
19217     {
19218         var _t = this;
19219         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19220         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19221         
19222         
19223         this.el.dom.style.border = '0 none';
19224         this.el.dom.setAttribute('tabIndex', -1);
19225         this.el.addClass('x-hidden hide');
19226         
19227         
19228         
19229         if(Roo.isIE){ // fix IE 1px bogus margin
19230             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19231         }
19232        
19233         
19234         this.frameId = Roo.id();
19235         
19236          
19237         
19238         var iframe = this.owner.wrap.createChild({
19239             tag: 'iframe',
19240             cls: 'form-control', // bootstrap..
19241             id: this.frameId,
19242             name: this.frameId,
19243             frameBorder : 'no',
19244             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19245         }, this.el
19246         );
19247         
19248         
19249         this.iframe = iframe.dom;
19250
19251          this.assignDocWin();
19252         
19253         this.doc.designMode = 'on';
19254        
19255         this.doc.open();
19256         this.doc.write(this.getDocMarkup());
19257         this.doc.close();
19258
19259         
19260         var task = { // must defer to wait for browser to be ready
19261             run : function(){
19262                 //console.log("run task?" + this.doc.readyState);
19263                 this.assignDocWin();
19264                 if(this.doc.body || this.doc.readyState == 'complete'){
19265                     try {
19266                         this.doc.designMode="on";
19267                     } catch (e) {
19268                         return;
19269                     }
19270                     Roo.TaskMgr.stop(task);
19271                     this.initEditor.defer(10, this);
19272                 }
19273             },
19274             interval : 10,
19275             duration: 10000,
19276             scope: this
19277         };
19278         Roo.TaskMgr.start(task);
19279
19280     },
19281
19282     // private
19283     onResize : function(w, h)
19284     {
19285          Roo.log('resize: ' +w + ',' + h );
19286         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19287         if(!this.iframe){
19288             return;
19289         }
19290         if(typeof w == 'number'){
19291             
19292             this.iframe.style.width = w + 'px';
19293         }
19294         if(typeof h == 'number'){
19295             
19296             this.iframe.style.height = h + 'px';
19297             if(this.doc){
19298                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19299             }
19300         }
19301         
19302     },
19303
19304     /**
19305      * Toggles the editor between standard and source edit mode.
19306      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19307      */
19308     toggleSourceEdit : function(sourceEditMode){
19309         
19310         this.sourceEditMode = sourceEditMode === true;
19311         
19312         if(this.sourceEditMode){
19313  
19314             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19315             
19316         }else{
19317             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19318             //this.iframe.className = '';
19319             this.deferFocus();
19320         }
19321         //this.setSize(this.owner.wrap.getSize());
19322         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19323     },
19324
19325     
19326   
19327
19328     /**
19329      * Protected method that will not generally be called directly. If you need/want
19330      * custom HTML cleanup, this is the method you should override.
19331      * @param {String} html The HTML to be cleaned
19332      * return {String} The cleaned HTML
19333      */
19334     cleanHtml : function(html){
19335         html = String(html);
19336         if(html.length > 5){
19337             if(Roo.isSafari){ // strip safari nonsense
19338                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19339             }
19340         }
19341         if(html == '&nbsp;'){
19342             html = '';
19343         }
19344         return html;
19345     },
19346
19347     /**
19348      * HTML Editor -> Textarea
19349      * Protected method that will not generally be called directly. Syncs the contents
19350      * of the editor iframe with the textarea.
19351      */
19352     syncValue : function(){
19353         if(this.initialized){
19354             var bd = (this.doc.body || this.doc.documentElement);
19355             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19356             var html = bd.innerHTML;
19357             if(Roo.isSafari){
19358                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19359                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19360                 if(m && m[1]){
19361                     html = '<div style="'+m[0]+'">' + html + '</div>';
19362                 }
19363             }
19364             html = this.cleanHtml(html);
19365             // fix up the special chars.. normaly like back quotes in word...
19366             // however we do not want to do this with chinese..
19367             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19368                 var cc = b.charCodeAt();
19369                 if (
19370                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19371                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19372                     (cc >= 0xf900 && cc < 0xfb00 )
19373                 ) {
19374                         return b;
19375                 }
19376                 return "&#"+cc+";" 
19377             });
19378             if(this.owner.fireEvent('beforesync', this, html) !== false){
19379                 this.el.dom.value = html;
19380                 this.owner.fireEvent('sync', this, html);
19381             }
19382         }
19383     },
19384
19385     /**
19386      * Protected method that will not generally be called directly. Pushes the value of the textarea
19387      * into the iframe editor.
19388      */
19389     pushValue : function(){
19390         if(this.initialized){
19391             var v = this.el.dom.value.trim();
19392             
19393 //            if(v.length < 1){
19394 //                v = '&#160;';
19395 //            }
19396             
19397             if(this.owner.fireEvent('beforepush', this, v) !== false){
19398                 var d = (this.doc.body || this.doc.documentElement);
19399                 d.innerHTML = v;
19400                 this.cleanUpPaste();
19401                 this.el.dom.value = d.innerHTML;
19402                 this.owner.fireEvent('push', this, v);
19403             }
19404         }
19405     },
19406
19407     // private
19408     deferFocus : function(){
19409         this.focus.defer(10, this);
19410     },
19411
19412     // doc'ed in Field
19413     focus : function(){
19414         if(this.win && !this.sourceEditMode){
19415             this.win.focus();
19416         }else{
19417             this.el.focus();
19418         }
19419     },
19420     
19421     assignDocWin: function()
19422     {
19423         var iframe = this.iframe;
19424         
19425          if(Roo.isIE){
19426             this.doc = iframe.contentWindow.document;
19427             this.win = iframe.contentWindow;
19428         } else {
19429 //            if (!Roo.get(this.frameId)) {
19430 //                return;
19431 //            }
19432 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19433 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19434             
19435             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19436                 return;
19437             }
19438             
19439             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19440             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19441         }
19442     },
19443     
19444     // private
19445     initEditor : function(){
19446         //console.log("INIT EDITOR");
19447         this.assignDocWin();
19448         
19449         
19450         
19451         this.doc.designMode="on";
19452         this.doc.open();
19453         this.doc.write(this.getDocMarkup());
19454         this.doc.close();
19455         
19456         var dbody = (this.doc.body || this.doc.documentElement);
19457         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19458         // this copies styles from the containing element into thsi one..
19459         // not sure why we need all of this..
19460         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19461         
19462         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19463         //ss['background-attachment'] = 'fixed'; // w3c
19464         dbody.bgProperties = 'fixed'; // ie
19465         //Roo.DomHelper.applyStyles(dbody, ss);
19466         Roo.EventManager.on(this.doc, {
19467             //'mousedown': this.onEditorEvent,
19468             'mouseup': this.onEditorEvent,
19469             'dblclick': this.onEditorEvent,
19470             'click': this.onEditorEvent,
19471             'keyup': this.onEditorEvent,
19472             buffer:100,
19473             scope: this
19474         });
19475         if(Roo.isGecko){
19476             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19477         }
19478         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19479             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19480         }
19481         this.initialized = true;
19482
19483         this.owner.fireEvent('initialize', this);
19484         this.pushValue();
19485     },
19486
19487     // private
19488     onDestroy : function(){
19489         
19490         
19491         
19492         if(this.rendered){
19493             
19494             //for (var i =0; i < this.toolbars.length;i++) {
19495             //    // fixme - ask toolbars for heights?
19496             //    this.toolbars[i].onDestroy();
19497            // }
19498             
19499             //this.wrap.dom.innerHTML = '';
19500             //this.wrap.remove();
19501         }
19502     },
19503
19504     // private
19505     onFirstFocus : function(){
19506         
19507         this.assignDocWin();
19508         
19509         
19510         this.activated = true;
19511          
19512     
19513         if(Roo.isGecko){ // prevent silly gecko errors
19514             this.win.focus();
19515             var s = this.win.getSelection();
19516             if(!s.focusNode || s.focusNode.nodeType != 3){
19517                 var r = s.getRangeAt(0);
19518                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19519                 r.collapse(true);
19520                 this.deferFocus();
19521             }
19522             try{
19523                 this.execCmd('useCSS', true);
19524                 this.execCmd('styleWithCSS', false);
19525             }catch(e){}
19526         }
19527         this.owner.fireEvent('activate', this);
19528     },
19529
19530     // private
19531     adjustFont: function(btn){
19532         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19533         //if(Roo.isSafari){ // safari
19534         //    adjust *= 2;
19535        // }
19536         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19537         if(Roo.isSafari){ // safari
19538             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19539             v =  (v < 10) ? 10 : v;
19540             v =  (v > 48) ? 48 : v;
19541             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19542             
19543         }
19544         
19545         
19546         v = Math.max(1, v+adjust);
19547         
19548         this.execCmd('FontSize', v  );
19549     },
19550
19551     onEditorEvent : function(e)
19552     {
19553         this.owner.fireEvent('editorevent', this, e);
19554       //  this.updateToolbar();
19555         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19556     },
19557
19558     insertTag : function(tg)
19559     {
19560         // could be a bit smarter... -> wrap the current selected tRoo..
19561         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19562             
19563             range = this.createRange(this.getSelection());
19564             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19565             wrappingNode.appendChild(range.extractContents());
19566             range.insertNode(wrappingNode);
19567
19568             return;
19569             
19570             
19571             
19572         }
19573         this.execCmd("formatblock",   tg);
19574         
19575     },
19576     
19577     insertText : function(txt)
19578     {
19579         
19580         
19581         var range = this.createRange();
19582         range.deleteContents();
19583                //alert(Sender.getAttribute('label'));
19584                
19585         range.insertNode(this.doc.createTextNode(txt));
19586     } ,
19587     
19588      
19589
19590     /**
19591      * Executes a Midas editor command on the editor document and performs necessary focus and
19592      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19593      * @param {String} cmd The Midas command
19594      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19595      */
19596     relayCmd : function(cmd, value){
19597         this.win.focus();
19598         this.execCmd(cmd, value);
19599         this.owner.fireEvent('editorevent', this);
19600         //this.updateToolbar();
19601         this.owner.deferFocus();
19602     },
19603
19604     /**
19605      * Executes a Midas editor command directly on the editor document.
19606      * For visual commands, you should use {@link #relayCmd} instead.
19607      * <b>This should only be called after the editor is initialized.</b>
19608      * @param {String} cmd The Midas command
19609      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19610      */
19611     execCmd : function(cmd, value){
19612         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19613         this.syncValue();
19614     },
19615  
19616  
19617    
19618     /**
19619      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19620      * to insert tRoo.
19621      * @param {String} text | dom node.. 
19622      */
19623     insertAtCursor : function(text)
19624     {
19625         
19626         
19627         
19628         if(!this.activated){
19629             return;
19630         }
19631         /*
19632         if(Roo.isIE){
19633             this.win.focus();
19634             var r = this.doc.selection.createRange();
19635             if(r){
19636                 r.collapse(true);
19637                 r.pasteHTML(text);
19638                 this.syncValue();
19639                 this.deferFocus();
19640             
19641             }
19642             return;
19643         }
19644         */
19645         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19646             this.win.focus();
19647             
19648             
19649             // from jquery ui (MIT licenced)
19650             var range, node;
19651             var win = this.win;
19652             
19653             if (win.getSelection && win.getSelection().getRangeAt) {
19654                 range = win.getSelection().getRangeAt(0);
19655                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19656                 range.insertNode(node);
19657             } else if (win.document.selection && win.document.selection.createRange) {
19658                 // no firefox support
19659                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19660                 win.document.selection.createRange().pasteHTML(txt);
19661             } else {
19662                 // no firefox support
19663                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19664                 this.execCmd('InsertHTML', txt);
19665             } 
19666             
19667             this.syncValue();
19668             
19669             this.deferFocus();
19670         }
19671     },
19672  // private
19673     mozKeyPress : function(e){
19674         if(e.ctrlKey){
19675             var c = e.getCharCode(), cmd;
19676           
19677             if(c > 0){
19678                 c = String.fromCharCode(c).toLowerCase();
19679                 switch(c){
19680                     case 'b':
19681                         cmd = 'bold';
19682                         break;
19683                     case 'i':
19684                         cmd = 'italic';
19685                         break;
19686                     
19687                     case 'u':
19688                         cmd = 'underline';
19689                         break;
19690                     
19691                     case 'v':
19692                         this.cleanUpPaste.defer(100, this);
19693                         return;
19694                         
19695                 }
19696                 if(cmd){
19697                     this.win.focus();
19698                     this.execCmd(cmd);
19699                     this.deferFocus();
19700                     e.preventDefault();
19701                 }
19702                 
19703             }
19704         }
19705     },
19706
19707     // private
19708     fixKeys : function(){ // load time branching for fastest keydown performance
19709         if(Roo.isIE){
19710             return function(e){
19711                 var k = e.getKey(), r;
19712                 if(k == e.TAB){
19713                     e.stopEvent();
19714                     r = this.doc.selection.createRange();
19715                     if(r){
19716                         r.collapse(true);
19717                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19718                         this.deferFocus();
19719                     }
19720                     return;
19721                 }
19722                 
19723                 if(k == e.ENTER){
19724                     r = this.doc.selection.createRange();
19725                     if(r){
19726                         var target = r.parentElement();
19727                         if(!target || target.tagName.toLowerCase() != 'li'){
19728                             e.stopEvent();
19729                             r.pasteHTML('<br />');
19730                             r.collapse(false);
19731                             r.select();
19732                         }
19733                     }
19734                 }
19735                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19736                     this.cleanUpPaste.defer(100, this);
19737                     return;
19738                 }
19739                 
19740                 
19741             };
19742         }else if(Roo.isOpera){
19743             return function(e){
19744                 var k = e.getKey();
19745                 if(k == e.TAB){
19746                     e.stopEvent();
19747                     this.win.focus();
19748                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19749                     this.deferFocus();
19750                 }
19751                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19752                     this.cleanUpPaste.defer(100, this);
19753                     return;
19754                 }
19755                 
19756             };
19757         }else if(Roo.isSafari){
19758             return function(e){
19759                 var k = e.getKey();
19760                 
19761                 if(k == e.TAB){
19762                     e.stopEvent();
19763                     this.execCmd('InsertText','\t');
19764                     this.deferFocus();
19765                     return;
19766                 }
19767                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19768                     this.cleanUpPaste.defer(100, this);
19769                     return;
19770                 }
19771                 
19772              };
19773         }
19774     }(),
19775     
19776     getAllAncestors: function()
19777     {
19778         var p = this.getSelectedNode();
19779         var a = [];
19780         if (!p) {
19781             a.push(p); // push blank onto stack..
19782             p = this.getParentElement();
19783         }
19784         
19785         
19786         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19787             a.push(p);
19788             p = p.parentNode;
19789         }
19790         a.push(this.doc.body);
19791         return a;
19792     },
19793     lastSel : false,
19794     lastSelNode : false,
19795     
19796     
19797     getSelection : function() 
19798     {
19799         this.assignDocWin();
19800         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19801     },
19802     
19803     getSelectedNode: function() 
19804     {
19805         // this may only work on Gecko!!!
19806         
19807         // should we cache this!!!!
19808         
19809         
19810         
19811          
19812         var range = this.createRange(this.getSelection()).cloneRange();
19813         
19814         if (Roo.isIE) {
19815             var parent = range.parentElement();
19816             while (true) {
19817                 var testRange = range.duplicate();
19818                 testRange.moveToElementText(parent);
19819                 if (testRange.inRange(range)) {
19820                     break;
19821                 }
19822                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19823                     break;
19824                 }
19825                 parent = parent.parentElement;
19826             }
19827             return parent;
19828         }
19829         
19830         // is ancestor a text element.
19831         var ac =  range.commonAncestorContainer;
19832         if (ac.nodeType == 3) {
19833             ac = ac.parentNode;
19834         }
19835         
19836         var ar = ac.childNodes;
19837          
19838         var nodes = [];
19839         var other_nodes = [];
19840         var has_other_nodes = false;
19841         for (var i=0;i<ar.length;i++) {
19842             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19843                 continue;
19844             }
19845             // fullly contained node.
19846             
19847             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19848                 nodes.push(ar[i]);
19849                 continue;
19850             }
19851             
19852             // probably selected..
19853             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19854                 other_nodes.push(ar[i]);
19855                 continue;
19856             }
19857             // outer..
19858             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19859                 continue;
19860             }
19861             
19862             
19863             has_other_nodes = true;
19864         }
19865         if (!nodes.length && other_nodes.length) {
19866             nodes= other_nodes;
19867         }
19868         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19869             return false;
19870         }
19871         
19872         return nodes[0];
19873     },
19874     createRange: function(sel)
19875     {
19876         // this has strange effects when using with 
19877         // top toolbar - not sure if it's a great idea.
19878         //this.editor.contentWindow.focus();
19879         if (typeof sel != "undefined") {
19880             try {
19881                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19882             } catch(e) {
19883                 return this.doc.createRange();
19884             }
19885         } else {
19886             return this.doc.createRange();
19887         }
19888     },
19889     getParentElement: function()
19890     {
19891         
19892         this.assignDocWin();
19893         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19894         
19895         var range = this.createRange(sel);
19896          
19897         try {
19898             var p = range.commonAncestorContainer;
19899             while (p.nodeType == 3) { // text node
19900                 p = p.parentNode;
19901             }
19902             return p;
19903         } catch (e) {
19904             return null;
19905         }
19906     
19907     },
19908     /***
19909      *
19910      * Range intersection.. the hard stuff...
19911      *  '-1' = before
19912      *  '0' = hits..
19913      *  '1' = after.
19914      *         [ -- selected range --- ]
19915      *   [fail]                        [fail]
19916      *
19917      *    basically..
19918      *      if end is before start or  hits it. fail.
19919      *      if start is after end or hits it fail.
19920      *
19921      *   if either hits (but other is outside. - then it's not 
19922      *   
19923      *    
19924      **/
19925     
19926     
19927     // @see http://www.thismuchiknow.co.uk/?p=64.
19928     rangeIntersectsNode : function(range, node)
19929     {
19930         var nodeRange = node.ownerDocument.createRange();
19931         try {
19932             nodeRange.selectNode(node);
19933         } catch (e) {
19934             nodeRange.selectNodeContents(node);
19935         }
19936     
19937         var rangeStartRange = range.cloneRange();
19938         rangeStartRange.collapse(true);
19939     
19940         var rangeEndRange = range.cloneRange();
19941         rangeEndRange.collapse(false);
19942     
19943         var nodeStartRange = nodeRange.cloneRange();
19944         nodeStartRange.collapse(true);
19945     
19946         var nodeEndRange = nodeRange.cloneRange();
19947         nodeEndRange.collapse(false);
19948     
19949         return rangeStartRange.compareBoundaryPoints(
19950                  Range.START_TO_START, nodeEndRange) == -1 &&
19951                rangeEndRange.compareBoundaryPoints(
19952                  Range.START_TO_START, nodeStartRange) == 1;
19953         
19954          
19955     },
19956     rangeCompareNode : function(range, node)
19957     {
19958         var nodeRange = node.ownerDocument.createRange();
19959         try {
19960             nodeRange.selectNode(node);
19961         } catch (e) {
19962             nodeRange.selectNodeContents(node);
19963         }
19964         
19965         
19966         range.collapse(true);
19967     
19968         nodeRange.collapse(true);
19969      
19970         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19971         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19972          
19973         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19974         
19975         var nodeIsBefore   =  ss == 1;
19976         var nodeIsAfter    = ee == -1;
19977         
19978         if (nodeIsBefore && nodeIsAfter)
19979             return 0; // outer
19980         if (!nodeIsBefore && nodeIsAfter)
19981             return 1; //right trailed.
19982         
19983         if (nodeIsBefore && !nodeIsAfter)
19984             return 2;  // left trailed.
19985         // fully contined.
19986         return 3;
19987     },
19988
19989     // private? - in a new class?
19990     cleanUpPaste :  function()
19991     {
19992         // cleans up the whole document..
19993         Roo.log('cleanuppaste');
19994         
19995         this.cleanUpChildren(this.doc.body);
19996         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19997         if (clean != this.doc.body.innerHTML) {
19998             this.doc.body.innerHTML = clean;
19999         }
20000         
20001     },
20002     
20003     cleanWordChars : function(input) {// change the chars to hex code
20004         var he = Roo.HtmlEditorCore;
20005         
20006         var output = input;
20007         Roo.each(he.swapCodes, function(sw) { 
20008             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20009             
20010             output = output.replace(swapper, sw[1]);
20011         });
20012         
20013         return output;
20014     },
20015     
20016     
20017     cleanUpChildren : function (n)
20018     {
20019         if (!n.childNodes.length) {
20020             return;
20021         }
20022         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20023            this.cleanUpChild(n.childNodes[i]);
20024         }
20025     },
20026     
20027     
20028         
20029     
20030     cleanUpChild : function (node)
20031     {
20032         var ed = this;
20033         //console.log(node);
20034         if (node.nodeName == "#text") {
20035             // clean up silly Windows -- stuff?
20036             return; 
20037         }
20038         if (node.nodeName == "#comment") {
20039             node.parentNode.removeChild(node);
20040             // clean up silly Windows -- stuff?
20041             return; 
20042         }
20043         var lcname = node.tagName.toLowerCase();
20044         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20045         // whitelist of tags..
20046         
20047         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20048             // remove node.
20049             node.parentNode.removeChild(node);
20050             return;
20051             
20052         }
20053         
20054         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20055         
20056         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20057         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20058         
20059         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20060         //    remove_keep_children = true;
20061         //}
20062         
20063         if (remove_keep_children) {
20064             this.cleanUpChildren(node);
20065             // inserts everything just before this node...
20066             while (node.childNodes.length) {
20067                 var cn = node.childNodes[0];
20068                 node.removeChild(cn);
20069                 node.parentNode.insertBefore(cn, node);
20070             }
20071             node.parentNode.removeChild(node);
20072             return;
20073         }
20074         
20075         if (!node.attributes || !node.attributes.length) {
20076             this.cleanUpChildren(node);
20077             return;
20078         }
20079         
20080         function cleanAttr(n,v)
20081         {
20082             
20083             if (v.match(/^\./) || v.match(/^\//)) {
20084                 return;
20085             }
20086             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20087                 return;
20088             }
20089             if (v.match(/^#/)) {
20090                 return;
20091             }
20092 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20093             node.removeAttribute(n);
20094             
20095         }
20096         
20097         var cwhite = this.cwhite;
20098         var cblack = this.cblack;
20099             
20100         function cleanStyle(n,v)
20101         {
20102             if (v.match(/expression/)) { //XSS?? should we even bother..
20103                 node.removeAttribute(n);
20104                 return;
20105             }
20106             
20107             var parts = v.split(/;/);
20108             var clean = [];
20109             
20110             Roo.each(parts, function(p) {
20111                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20112                 if (!p.length) {
20113                     return true;
20114                 }
20115                 var l = p.split(':').shift().replace(/\s+/g,'');
20116                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20117                 
20118                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20119 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20120                     //node.removeAttribute(n);
20121                     return true;
20122                 }
20123                 //Roo.log()
20124                 // only allow 'c whitelisted system attributes'
20125                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20126 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20127                     //node.removeAttribute(n);
20128                     return true;
20129                 }
20130                 
20131                 
20132                  
20133                 
20134                 clean.push(p);
20135                 return true;
20136             });
20137             if (clean.length) { 
20138                 node.setAttribute(n, clean.join(';'));
20139             } else {
20140                 node.removeAttribute(n);
20141             }
20142             
20143         }
20144         
20145         
20146         for (var i = node.attributes.length-1; i > -1 ; i--) {
20147             var a = node.attributes[i];
20148             //console.log(a);
20149             
20150             if (a.name.toLowerCase().substr(0,2)=='on')  {
20151                 node.removeAttribute(a.name);
20152                 continue;
20153             }
20154             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20155                 node.removeAttribute(a.name);
20156                 continue;
20157             }
20158             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20159                 cleanAttr(a.name,a.value); // fixme..
20160                 continue;
20161             }
20162             if (a.name == 'style') {
20163                 cleanStyle(a.name,a.value);
20164                 continue;
20165             }
20166             /// clean up MS crap..
20167             // tecnically this should be a list of valid class'es..
20168             
20169             
20170             if (a.name == 'class') {
20171                 if (a.value.match(/^Mso/)) {
20172                     node.className = '';
20173                 }
20174                 
20175                 if (a.value.match(/body/)) {
20176                     node.className = '';
20177                 }
20178                 continue;
20179             }
20180             
20181             // style cleanup!?
20182             // class cleanup?
20183             
20184         }
20185         
20186         
20187         this.cleanUpChildren(node);
20188         
20189         
20190     },
20191     
20192     /**
20193      * Clean up MS wordisms...
20194      */
20195     cleanWord : function(node)
20196     {
20197         
20198         
20199         if (!node) {
20200             this.cleanWord(this.doc.body);
20201             return;
20202         }
20203         if (node.nodeName == "#text") {
20204             // clean up silly Windows -- stuff?
20205             return; 
20206         }
20207         if (node.nodeName == "#comment") {
20208             node.parentNode.removeChild(node);
20209             // clean up silly Windows -- stuff?
20210             return; 
20211         }
20212         
20213         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20214             node.parentNode.removeChild(node);
20215             return;
20216         }
20217         
20218         // remove - but keep children..
20219         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20220             while (node.childNodes.length) {
20221                 var cn = node.childNodes[0];
20222                 node.removeChild(cn);
20223                 node.parentNode.insertBefore(cn, node);
20224             }
20225             node.parentNode.removeChild(node);
20226             this.iterateChildren(node, this.cleanWord);
20227             return;
20228         }
20229         // clean styles
20230         if (node.className.length) {
20231             
20232             var cn = node.className.split(/\W+/);
20233             var cna = [];
20234             Roo.each(cn, function(cls) {
20235                 if (cls.match(/Mso[a-zA-Z]+/)) {
20236                     return;
20237                 }
20238                 cna.push(cls);
20239             });
20240             node.className = cna.length ? cna.join(' ') : '';
20241             if (!cna.length) {
20242                 node.removeAttribute("class");
20243             }
20244         }
20245         
20246         if (node.hasAttribute("lang")) {
20247             node.removeAttribute("lang");
20248         }
20249         
20250         if (node.hasAttribute("style")) {
20251             
20252             var styles = node.getAttribute("style").split(";");
20253             var nstyle = [];
20254             Roo.each(styles, function(s) {
20255                 if (!s.match(/:/)) {
20256                     return;
20257                 }
20258                 var kv = s.split(":");
20259                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20260                     return;
20261                 }
20262                 // what ever is left... we allow.
20263                 nstyle.push(s);
20264             });
20265             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20266             if (!nstyle.length) {
20267                 node.removeAttribute('style');
20268             }
20269         }
20270         this.iterateChildren(node, this.cleanWord);
20271         
20272         
20273         
20274     },
20275     /**
20276      * iterateChildren of a Node, calling fn each time, using this as the scole..
20277      * @param {DomNode} node node to iterate children of.
20278      * @param {Function} fn method of this class to call on each item.
20279      */
20280     iterateChildren : function(node, fn)
20281     {
20282         if (!node.childNodes.length) {
20283                 return;
20284         }
20285         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20286            fn.call(this, node.childNodes[i])
20287         }
20288     },
20289     
20290     
20291     /**
20292      * cleanTableWidths.
20293      *
20294      * Quite often pasting from word etc.. results in tables with column and widths.
20295      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20296      *
20297      */
20298     cleanTableWidths : function(node)
20299     {
20300          
20301          
20302         if (!node) {
20303             this.cleanTableWidths(this.doc.body);
20304             return;
20305         }
20306         
20307         // ignore list...
20308         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20309             return; 
20310         }
20311         Roo.log(node.tagName);
20312         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20313             this.iterateChildren(node, this.cleanTableWidths);
20314             return;
20315         }
20316         if (node.hasAttribute('width')) {
20317             node.removeAttribute('width');
20318         }
20319         
20320          
20321         if (node.hasAttribute("style")) {
20322             // pretty basic...
20323             
20324             var styles = node.getAttribute("style").split(";");
20325             var nstyle = [];
20326             Roo.each(styles, function(s) {
20327                 if (!s.match(/:/)) {
20328                     return;
20329                 }
20330                 var kv = s.split(":");
20331                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20332                     return;
20333                 }
20334                 // what ever is left... we allow.
20335                 nstyle.push(s);
20336             });
20337             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20338             if (!nstyle.length) {
20339                 node.removeAttribute('style');
20340             }
20341         }
20342         
20343         this.iterateChildren(node, this.cleanTableWidths);
20344         
20345         
20346     },
20347     
20348     
20349     
20350     
20351     domToHTML : function(currentElement, depth, nopadtext) {
20352         
20353         depth = depth || 0;
20354         nopadtext = nopadtext || false;
20355     
20356         if (!currentElement) {
20357             return this.domToHTML(this.doc.body);
20358         }
20359         
20360         //Roo.log(currentElement);
20361         var j;
20362         var allText = false;
20363         var nodeName = currentElement.nodeName;
20364         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20365         
20366         if  (nodeName == '#text') {
20367             
20368             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20369         }
20370         
20371         
20372         var ret = '';
20373         if (nodeName != 'BODY') {
20374              
20375             var i = 0;
20376             // Prints the node tagName, such as <A>, <IMG>, etc
20377             if (tagName) {
20378                 var attr = [];
20379                 for(i = 0; i < currentElement.attributes.length;i++) {
20380                     // quoting?
20381                     var aname = currentElement.attributes.item(i).name;
20382                     if (!currentElement.attributes.item(i).value.length) {
20383                         continue;
20384                     }
20385                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20386                 }
20387                 
20388                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20389             } 
20390             else {
20391                 
20392                 // eack
20393             }
20394         } else {
20395             tagName = false;
20396         }
20397         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20398             return ret;
20399         }
20400         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20401             nopadtext = true;
20402         }
20403         
20404         
20405         // Traverse the tree
20406         i = 0;
20407         var currentElementChild = currentElement.childNodes.item(i);
20408         var allText = true;
20409         var innerHTML  = '';
20410         lastnode = '';
20411         while (currentElementChild) {
20412             // Formatting code (indent the tree so it looks nice on the screen)
20413             var nopad = nopadtext;
20414             if (lastnode == 'SPAN') {
20415                 nopad  = true;
20416             }
20417             // text
20418             if  (currentElementChild.nodeName == '#text') {
20419                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20420                 toadd = nopadtext ? toadd : toadd.trim();
20421                 if (!nopad && toadd.length > 80) {
20422                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20423                 }
20424                 innerHTML  += toadd;
20425                 
20426                 i++;
20427                 currentElementChild = currentElement.childNodes.item(i);
20428                 lastNode = '';
20429                 continue;
20430             }
20431             allText = false;
20432             
20433             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20434                 
20435             // Recursively traverse the tree structure of the child node
20436             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20437             lastnode = currentElementChild.nodeName;
20438             i++;
20439             currentElementChild=currentElement.childNodes.item(i);
20440         }
20441         
20442         ret += innerHTML;
20443         
20444         if (!allText) {
20445                 // The remaining code is mostly for formatting the tree
20446             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20447         }
20448         
20449         
20450         if (tagName) {
20451             ret+= "</"+tagName+">";
20452         }
20453         return ret;
20454         
20455     },
20456         
20457     applyBlacklists : function()
20458     {
20459         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20460         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20461         
20462         this.white = [];
20463         this.black = [];
20464         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20465             if (b.indexOf(tag) > -1) {
20466                 return;
20467             }
20468             this.white.push(tag);
20469             
20470         }, this);
20471         
20472         Roo.each(w, function(tag) {
20473             if (b.indexOf(tag) > -1) {
20474                 return;
20475             }
20476             if (this.white.indexOf(tag) > -1) {
20477                 return;
20478             }
20479             this.white.push(tag);
20480             
20481         }, this);
20482         
20483         
20484         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20485             if (w.indexOf(tag) > -1) {
20486                 return;
20487             }
20488             this.black.push(tag);
20489             
20490         }, this);
20491         
20492         Roo.each(b, function(tag) {
20493             if (w.indexOf(tag) > -1) {
20494                 return;
20495             }
20496             if (this.black.indexOf(tag) > -1) {
20497                 return;
20498             }
20499             this.black.push(tag);
20500             
20501         }, this);
20502         
20503         
20504         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20505         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20506         
20507         this.cwhite = [];
20508         this.cblack = [];
20509         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20510             if (b.indexOf(tag) > -1) {
20511                 return;
20512             }
20513             this.cwhite.push(tag);
20514             
20515         }, this);
20516         
20517         Roo.each(w, function(tag) {
20518             if (b.indexOf(tag) > -1) {
20519                 return;
20520             }
20521             if (this.cwhite.indexOf(tag) > -1) {
20522                 return;
20523             }
20524             this.cwhite.push(tag);
20525             
20526         }, this);
20527         
20528         
20529         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20530             if (w.indexOf(tag) > -1) {
20531                 return;
20532             }
20533             this.cblack.push(tag);
20534             
20535         }, this);
20536         
20537         Roo.each(b, function(tag) {
20538             if (w.indexOf(tag) > -1) {
20539                 return;
20540             }
20541             if (this.cblack.indexOf(tag) > -1) {
20542                 return;
20543             }
20544             this.cblack.push(tag);
20545             
20546         }, this);
20547     },
20548     
20549     setStylesheets : function(stylesheets)
20550     {
20551         if(typeof(stylesheets) == 'string'){
20552             Roo.get(this.iframe.contentDocument.head).createChild({
20553                 tag : 'link',
20554                 rel : 'stylesheet',
20555                 type : 'text/css',
20556                 href : stylesheets
20557             });
20558             
20559             return;
20560         }
20561         var _this = this;
20562      
20563         Roo.each(stylesheets, function(s) {
20564             if(!s.length){
20565                 return;
20566             }
20567             
20568             Roo.get(_this.iframe.contentDocument.head).createChild({
20569                 tag : 'link',
20570                 rel : 'stylesheet',
20571                 type : 'text/css',
20572                 href : s
20573             });
20574         });
20575
20576         
20577     },
20578     
20579     removeStylesheets : function()
20580     {
20581         var _this = this;
20582         
20583         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20584             s.remove();
20585         });
20586     }
20587     
20588     // hide stuff that is not compatible
20589     /**
20590      * @event blur
20591      * @hide
20592      */
20593     /**
20594      * @event change
20595      * @hide
20596      */
20597     /**
20598      * @event focus
20599      * @hide
20600      */
20601     /**
20602      * @event specialkey
20603      * @hide
20604      */
20605     /**
20606      * @cfg {String} fieldClass @hide
20607      */
20608     /**
20609      * @cfg {String} focusClass @hide
20610      */
20611     /**
20612      * @cfg {String} autoCreate @hide
20613      */
20614     /**
20615      * @cfg {String} inputType @hide
20616      */
20617     /**
20618      * @cfg {String} invalidClass @hide
20619      */
20620     /**
20621      * @cfg {String} invalidText @hide
20622      */
20623     /**
20624      * @cfg {String} msgFx @hide
20625      */
20626     /**
20627      * @cfg {String} validateOnBlur @hide
20628      */
20629 });
20630
20631 Roo.HtmlEditorCore.white = [
20632         'area', 'br', 'img', 'input', 'hr', 'wbr',
20633         
20634        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20635        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20636        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20637        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20638        'table',   'ul',         'xmp', 
20639        
20640        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20641       'thead',   'tr', 
20642      
20643       'dir', 'menu', 'ol', 'ul', 'dl',
20644        
20645       'embed',  'object'
20646 ];
20647
20648
20649 Roo.HtmlEditorCore.black = [
20650     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20651         'applet', // 
20652         'base',   'basefont', 'bgsound', 'blink',  'body', 
20653         'frame',  'frameset', 'head',    'html',   'ilayer', 
20654         'iframe', 'layer',  'link',     'meta',    'object',   
20655         'script', 'style' ,'title',  'xml' // clean later..
20656 ];
20657 Roo.HtmlEditorCore.clean = [
20658     'script', 'style', 'title', 'xml'
20659 ];
20660 Roo.HtmlEditorCore.remove = [
20661     'font'
20662 ];
20663 // attributes..
20664
20665 Roo.HtmlEditorCore.ablack = [
20666     'on'
20667 ];
20668     
20669 Roo.HtmlEditorCore.aclean = [ 
20670     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20671 ];
20672
20673 // protocols..
20674 Roo.HtmlEditorCore.pwhite= [
20675         'http',  'https',  'mailto'
20676 ];
20677
20678 // white listed style attributes.
20679 Roo.HtmlEditorCore.cwhite= [
20680       //  'text-align', /// default is to allow most things..
20681       
20682          
20683 //        'font-size'//??
20684 ];
20685
20686 // black listed style attributes.
20687 Roo.HtmlEditorCore.cblack= [
20688       //  'font-size' -- this can be set by the project 
20689 ];
20690
20691
20692 Roo.HtmlEditorCore.swapCodes   =[ 
20693     [    8211, "--" ], 
20694     [    8212, "--" ], 
20695     [    8216,  "'" ],  
20696     [    8217, "'" ],  
20697     [    8220, '"' ],  
20698     [    8221, '"' ],  
20699     [    8226, "*" ],  
20700     [    8230, "..." ]
20701 ]; 
20702
20703     /*
20704  * - LGPL
20705  *
20706  * HtmlEditor
20707  * 
20708  */
20709
20710 /**
20711  * @class Roo.bootstrap.HtmlEditor
20712  * @extends Roo.bootstrap.TextArea
20713  * Bootstrap HtmlEditor class
20714
20715  * @constructor
20716  * Create a new HtmlEditor
20717  * @param {Object} config The config object
20718  */
20719
20720 Roo.bootstrap.HtmlEditor = function(config){
20721     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20722     if (!this.toolbars) {
20723         this.toolbars = [];
20724     }
20725     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20726     this.addEvents({
20727             /**
20728              * @event initialize
20729              * Fires when the editor is fully initialized (including the iframe)
20730              * @param {HtmlEditor} this
20731              */
20732             initialize: true,
20733             /**
20734              * @event activate
20735              * Fires when the editor is first receives the focus. Any insertion must wait
20736              * until after this event.
20737              * @param {HtmlEditor} this
20738              */
20739             activate: true,
20740              /**
20741              * @event beforesync
20742              * Fires before the textarea is updated with content from the editor iframe. Return false
20743              * to cancel the sync.
20744              * @param {HtmlEditor} this
20745              * @param {String} html
20746              */
20747             beforesync: true,
20748              /**
20749              * @event beforepush
20750              * Fires before the iframe editor is updated with content from the textarea. Return false
20751              * to cancel the push.
20752              * @param {HtmlEditor} this
20753              * @param {String} html
20754              */
20755             beforepush: true,
20756              /**
20757              * @event sync
20758              * Fires when the textarea is updated with content from the editor iframe.
20759              * @param {HtmlEditor} this
20760              * @param {String} html
20761              */
20762             sync: true,
20763              /**
20764              * @event push
20765              * Fires when the iframe editor is updated with content from the textarea.
20766              * @param {HtmlEditor} this
20767              * @param {String} html
20768              */
20769             push: true,
20770              /**
20771              * @event editmodechange
20772              * Fires when the editor switches edit modes
20773              * @param {HtmlEditor} this
20774              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20775              */
20776             editmodechange: true,
20777             /**
20778              * @event editorevent
20779              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20780              * @param {HtmlEditor} this
20781              */
20782             editorevent: true,
20783             /**
20784              * @event firstfocus
20785              * Fires when on first focus - needed by toolbars..
20786              * @param {HtmlEditor} this
20787              */
20788             firstfocus: true,
20789             /**
20790              * @event autosave
20791              * Auto save the htmlEditor value as a file into Events
20792              * @param {HtmlEditor} this
20793              */
20794             autosave: true,
20795             /**
20796              * @event savedpreview
20797              * preview the saved version of htmlEditor
20798              * @param {HtmlEditor} this
20799              */
20800             savedpreview: true
20801         });
20802 };
20803
20804
20805 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20806     
20807     
20808       /**
20809      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20810      */
20811     toolbars : false,
20812    
20813      /**
20814      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20815      *                        Roo.resizable.
20816      */
20817     resizable : false,
20818      /**
20819      * @cfg {Number} height (in pixels)
20820      */   
20821     height: 300,
20822    /**
20823      * @cfg {Number} width (in pixels)
20824      */   
20825     width: false,
20826     
20827     /**
20828      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20829      * 
20830      */
20831     stylesheets: false,
20832     
20833     // id of frame..
20834     frameId: false,
20835     
20836     // private properties
20837     validationEvent : false,
20838     deferHeight: true,
20839     initialized : false,
20840     activated : false,
20841     
20842     onFocus : Roo.emptyFn,
20843     iframePad:3,
20844     hideMode:'offsets',
20845     
20846     
20847     tbContainer : false,
20848     
20849     toolbarContainer :function() {
20850         return this.wrap.select('.x-html-editor-tb',true).first();
20851     },
20852
20853     /**
20854      * Protected method that will not generally be called directly. It
20855      * is called when the editor creates its toolbar. Override this method if you need to
20856      * add custom toolbar buttons.
20857      * @param {HtmlEditor} editor
20858      */
20859     createToolbar : function(){
20860         
20861         Roo.log("create toolbars");
20862         
20863         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20864         this.toolbars[0].render(this.toolbarContainer());
20865         
20866         return;
20867         
20868 //        if (!editor.toolbars || !editor.toolbars.length) {
20869 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20870 //        }
20871 //        
20872 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20873 //            editor.toolbars[i] = Roo.factory(
20874 //                    typeof(editor.toolbars[i]) == 'string' ?
20875 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20876 //                Roo.bootstrap.HtmlEditor);
20877 //            editor.toolbars[i].init(editor);
20878 //        }
20879     },
20880
20881      
20882     // private
20883     onRender : function(ct, position)
20884     {
20885        // Roo.log("Call onRender: " + this.xtype);
20886         var _t = this;
20887         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20888       
20889         this.wrap = this.inputEl().wrap({
20890             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20891         });
20892         
20893         this.editorcore.onRender(ct, position);
20894          
20895         if (this.resizable) {
20896             this.resizeEl = new Roo.Resizable(this.wrap, {
20897                 pinned : true,
20898                 wrap: true,
20899                 dynamic : true,
20900                 minHeight : this.height,
20901                 height: this.height,
20902                 handles : this.resizable,
20903                 width: this.width,
20904                 listeners : {
20905                     resize : function(r, w, h) {
20906                         _t.onResize(w,h); // -something
20907                     }
20908                 }
20909             });
20910             
20911         }
20912         this.createToolbar(this);
20913        
20914         
20915         if(!this.width && this.resizable){
20916             this.setSize(this.wrap.getSize());
20917         }
20918         if (this.resizeEl) {
20919             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20920             // should trigger onReize..
20921         }
20922         
20923     },
20924
20925     // private
20926     onResize : function(w, h)
20927     {
20928         Roo.log('resize: ' +w + ',' + h );
20929         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20930         var ew = false;
20931         var eh = false;
20932         
20933         if(this.inputEl() ){
20934             if(typeof w == 'number'){
20935                 var aw = w - this.wrap.getFrameWidth('lr');
20936                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20937                 ew = aw;
20938             }
20939             if(typeof h == 'number'){
20940                  var tbh = -11;  // fixme it needs to tool bar size!
20941                 for (var i =0; i < this.toolbars.length;i++) {
20942                     // fixme - ask toolbars for heights?
20943                     tbh += this.toolbars[i].el.getHeight();
20944                     //if (this.toolbars[i].footer) {
20945                     //    tbh += this.toolbars[i].footer.el.getHeight();
20946                     //}
20947                 }
20948               
20949                 
20950                 
20951                 
20952                 
20953                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20954                 ah -= 5; // knock a few pixes off for look..
20955                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20956                 var eh = ah;
20957             }
20958         }
20959         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20960         this.editorcore.onResize(ew,eh);
20961         
20962     },
20963
20964     /**
20965      * Toggles the editor between standard and source edit mode.
20966      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20967      */
20968     toggleSourceEdit : function(sourceEditMode)
20969     {
20970         this.editorcore.toggleSourceEdit(sourceEditMode);
20971         
20972         if(this.editorcore.sourceEditMode){
20973             Roo.log('editor - showing textarea');
20974             
20975 //            Roo.log('in');
20976 //            Roo.log(this.syncValue());
20977             this.syncValue();
20978             this.inputEl().removeClass(['hide', 'x-hidden']);
20979             this.inputEl().dom.removeAttribute('tabIndex');
20980             this.inputEl().focus();
20981         }else{
20982             Roo.log('editor - hiding textarea');
20983 //            Roo.log('out')
20984 //            Roo.log(this.pushValue()); 
20985             this.pushValue();
20986             
20987             this.inputEl().addClass(['hide', 'x-hidden']);
20988             this.inputEl().dom.setAttribute('tabIndex', -1);
20989             //this.deferFocus();
20990         }
20991          
20992         if(this.resizable){
20993             this.setSize(this.wrap.getSize());
20994         }
20995         
20996         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20997     },
20998  
20999     // private (for BoxComponent)
21000     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21001
21002     // private (for BoxComponent)
21003     getResizeEl : function(){
21004         return this.wrap;
21005     },
21006
21007     // private (for BoxComponent)
21008     getPositionEl : function(){
21009         return this.wrap;
21010     },
21011
21012     // private
21013     initEvents : function(){
21014         this.originalValue = this.getValue();
21015     },
21016
21017 //    /**
21018 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21019 //     * @method
21020 //     */
21021 //    markInvalid : Roo.emptyFn,
21022 //    /**
21023 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21024 //     * @method
21025 //     */
21026 //    clearInvalid : Roo.emptyFn,
21027
21028     setValue : function(v){
21029         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21030         this.editorcore.pushValue();
21031     },
21032
21033      
21034     // private
21035     deferFocus : function(){
21036         this.focus.defer(10, this);
21037     },
21038
21039     // doc'ed in Field
21040     focus : function(){
21041         this.editorcore.focus();
21042         
21043     },
21044       
21045
21046     // private
21047     onDestroy : function(){
21048         
21049         
21050         
21051         if(this.rendered){
21052             
21053             for (var i =0; i < this.toolbars.length;i++) {
21054                 // fixme - ask toolbars for heights?
21055                 this.toolbars[i].onDestroy();
21056             }
21057             
21058             this.wrap.dom.innerHTML = '';
21059             this.wrap.remove();
21060         }
21061     },
21062
21063     // private
21064     onFirstFocus : function(){
21065         //Roo.log("onFirstFocus");
21066         this.editorcore.onFirstFocus();
21067          for (var i =0; i < this.toolbars.length;i++) {
21068             this.toolbars[i].onFirstFocus();
21069         }
21070         
21071     },
21072     
21073     // private
21074     syncValue : function()
21075     {   
21076         this.editorcore.syncValue();
21077     },
21078     
21079     pushValue : function()
21080     {   
21081         this.editorcore.pushValue();
21082     }
21083      
21084     
21085     // hide stuff that is not compatible
21086     /**
21087      * @event blur
21088      * @hide
21089      */
21090     /**
21091      * @event change
21092      * @hide
21093      */
21094     /**
21095      * @event focus
21096      * @hide
21097      */
21098     /**
21099      * @event specialkey
21100      * @hide
21101      */
21102     /**
21103      * @cfg {String} fieldClass @hide
21104      */
21105     /**
21106      * @cfg {String} focusClass @hide
21107      */
21108     /**
21109      * @cfg {String} autoCreate @hide
21110      */
21111     /**
21112      * @cfg {String} inputType @hide
21113      */
21114     /**
21115      * @cfg {String} invalidClass @hide
21116      */
21117     /**
21118      * @cfg {String} invalidText @hide
21119      */
21120     /**
21121      * @cfg {String} msgFx @hide
21122      */
21123     /**
21124      * @cfg {String} validateOnBlur @hide
21125      */
21126 });
21127  
21128     
21129    
21130    
21131    
21132       
21133 Roo.namespace('Roo.bootstrap.htmleditor');
21134 /**
21135  * @class Roo.bootstrap.HtmlEditorToolbar1
21136  * Basic Toolbar
21137  * 
21138  * Usage:
21139  *
21140  new Roo.bootstrap.HtmlEditor({
21141     ....
21142     toolbars : [
21143         new Roo.bootstrap.HtmlEditorToolbar1({
21144             disable : { fonts: 1 , format: 1, ..., ... , ...],
21145             btns : [ .... ]
21146         })
21147     }
21148      
21149  * 
21150  * @cfg {Object} disable List of elements to disable..
21151  * @cfg {Array} btns List of additional buttons.
21152  * 
21153  * 
21154  * NEEDS Extra CSS? 
21155  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21156  */
21157  
21158 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21159 {
21160     
21161     Roo.apply(this, config);
21162     
21163     // default disabled, based on 'good practice'..
21164     this.disable = this.disable || {};
21165     Roo.applyIf(this.disable, {
21166         fontSize : true,
21167         colors : true,
21168         specialElements : true
21169     });
21170     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21171     
21172     this.editor = config.editor;
21173     this.editorcore = config.editor.editorcore;
21174     
21175     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21176     
21177     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21178     // dont call parent... till later.
21179 }
21180 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21181      
21182     bar : true,
21183     
21184     editor : false,
21185     editorcore : false,
21186     
21187     
21188     formats : [
21189         "p" ,  
21190         "h1","h2","h3","h4","h5","h6", 
21191         "pre", "code", 
21192         "abbr", "acronym", "address", "cite", "samp", "var",
21193         'div','span'
21194     ],
21195     
21196     onRender : function(ct, position)
21197     {
21198        // Roo.log("Call onRender: " + this.xtype);
21199         
21200        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21201        Roo.log(this.el);
21202        this.el.dom.style.marginBottom = '0';
21203        var _this = this;
21204        var editorcore = this.editorcore;
21205        var editor= this.editor;
21206        
21207        var children = [];
21208        var btn = function(id,cmd , toggle, handler){
21209        
21210             var  event = toggle ? 'toggle' : 'click';
21211        
21212             var a = {
21213                 size : 'sm',
21214                 xtype: 'Button',
21215                 xns: Roo.bootstrap,
21216                 glyphicon : id,
21217                 cmd : id || cmd,
21218                 enableToggle:toggle !== false,
21219                 //html : 'submit'
21220                 pressed : toggle ? false : null,
21221                 listeners : {}
21222             };
21223             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21224                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21225             };
21226             children.push(a);
21227             return a;
21228        }
21229         
21230         var style = {
21231                 xtype: 'Button',
21232                 size : 'sm',
21233                 xns: Roo.bootstrap,
21234                 glyphicon : 'font',
21235                 //html : 'submit'
21236                 menu : {
21237                     xtype: 'Menu',
21238                     xns: Roo.bootstrap,
21239                     items:  []
21240                 }
21241         };
21242         Roo.each(this.formats, function(f) {
21243             style.menu.items.push({
21244                 xtype :'MenuItem',
21245                 xns: Roo.bootstrap,
21246                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21247                 tagname : f,
21248                 listeners : {
21249                     click : function()
21250                     {
21251                         editorcore.insertTag(this.tagname);
21252                         editor.focus();
21253                     }
21254                 }
21255                 
21256             });
21257         });
21258          children.push(style);   
21259             
21260             
21261         btn('bold',false,true);
21262         btn('italic',false,true);
21263         btn('align-left', 'justifyleft',true);
21264         btn('align-center', 'justifycenter',true);
21265         btn('align-right' , 'justifyright',true);
21266         btn('link', false, false, function(btn) {
21267             //Roo.log("create link?");
21268             var url = prompt(this.createLinkText, this.defaultLinkValue);
21269             if(url && url != 'http:/'+'/'){
21270                 this.editorcore.relayCmd('createlink', url);
21271             }
21272         }),
21273         btn('list','insertunorderedlist',true);
21274         btn('pencil', false,true, function(btn){
21275                 Roo.log(this);
21276                 
21277                 this.toggleSourceEdit(btn.pressed);
21278         });
21279         /*
21280         var cog = {
21281                 xtype: 'Button',
21282                 size : 'sm',
21283                 xns: Roo.bootstrap,
21284                 glyphicon : 'cog',
21285                 //html : 'submit'
21286                 menu : {
21287                     xtype: 'Menu',
21288                     xns: Roo.bootstrap,
21289                     items:  []
21290                 }
21291         };
21292         
21293         cog.menu.items.push({
21294             xtype :'MenuItem',
21295             xns: Roo.bootstrap,
21296             html : Clean styles,
21297             tagname : f,
21298             listeners : {
21299                 click : function()
21300                 {
21301                     editorcore.insertTag(this.tagname);
21302                     editor.focus();
21303                 }
21304             }
21305             
21306         });
21307        */
21308         
21309          
21310        this.xtype = 'NavSimplebar';
21311         
21312         for(var i=0;i< children.length;i++) {
21313             
21314             this.buttons.add(this.addxtypeChild(children[i]));
21315             
21316         }
21317         
21318         editor.on('editorevent', this.updateToolbar, this);
21319     },
21320     onBtnClick : function(id)
21321     {
21322        this.editorcore.relayCmd(id);
21323        this.editorcore.focus();
21324     },
21325     
21326     /**
21327      * Protected method that will not generally be called directly. It triggers
21328      * a toolbar update by reading the markup state of the current selection in the editor.
21329      */
21330     updateToolbar: function(){
21331
21332         if(!this.editorcore.activated){
21333             this.editor.onFirstFocus(); // is this neeed?
21334             return;
21335         }
21336
21337         var btns = this.buttons; 
21338         var doc = this.editorcore.doc;
21339         btns.get('bold').setActive(doc.queryCommandState('bold'));
21340         btns.get('italic').setActive(doc.queryCommandState('italic'));
21341         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21342         
21343         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21344         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21345         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21346         
21347         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21348         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21349          /*
21350         
21351         var ans = this.editorcore.getAllAncestors();
21352         if (this.formatCombo) {
21353             
21354             
21355             var store = this.formatCombo.store;
21356             this.formatCombo.setValue("");
21357             for (var i =0; i < ans.length;i++) {
21358                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21359                     // select it..
21360                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21361                     break;
21362                 }
21363             }
21364         }
21365         
21366         
21367         
21368         // hides menus... - so this cant be on a menu...
21369         Roo.bootstrap.MenuMgr.hideAll();
21370         */
21371         Roo.bootstrap.MenuMgr.hideAll();
21372         //this.editorsyncValue();
21373     },
21374     onFirstFocus: function() {
21375         this.buttons.each(function(item){
21376            item.enable();
21377         });
21378     },
21379     toggleSourceEdit : function(sourceEditMode){
21380         
21381           
21382         if(sourceEditMode){
21383             Roo.log("disabling buttons");
21384            this.buttons.each( function(item){
21385                 if(item.cmd != 'pencil'){
21386                     item.disable();
21387                 }
21388             });
21389           
21390         }else{
21391             Roo.log("enabling buttons");
21392             if(this.editorcore.initialized){
21393                 this.buttons.each( function(item){
21394                     item.enable();
21395                 });
21396             }
21397             
21398         }
21399         Roo.log("calling toggole on editor");
21400         // tell the editor that it's been pressed..
21401         this.editor.toggleSourceEdit(sourceEditMode);
21402        
21403     }
21404 });
21405
21406
21407
21408
21409
21410 /**
21411  * @class Roo.bootstrap.Table.AbstractSelectionModel
21412  * @extends Roo.util.Observable
21413  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21414  * implemented by descendant classes.  This class should not be directly instantiated.
21415  * @constructor
21416  */
21417 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21418     this.locked = false;
21419     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21420 };
21421
21422
21423 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21424     /** @ignore Called by the grid automatically. Do not call directly. */
21425     init : function(grid){
21426         this.grid = grid;
21427         this.initEvents();
21428     },
21429
21430     /**
21431      * Locks the selections.
21432      */
21433     lock : function(){
21434         this.locked = true;
21435     },
21436
21437     /**
21438      * Unlocks the selections.
21439      */
21440     unlock : function(){
21441         this.locked = false;
21442     },
21443
21444     /**
21445      * Returns true if the selections are locked.
21446      * @return {Boolean}
21447      */
21448     isLocked : function(){
21449         return this.locked;
21450     }
21451 });
21452 /**
21453  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21454  * @class Roo.bootstrap.Table.RowSelectionModel
21455  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21456  * It supports multiple selections and keyboard selection/navigation. 
21457  * @constructor
21458  * @param {Object} config
21459  */
21460
21461 Roo.bootstrap.Table.RowSelectionModel = function(config){
21462     Roo.apply(this, config);
21463     this.selections = new Roo.util.MixedCollection(false, function(o){
21464         return o.id;
21465     });
21466
21467     this.last = false;
21468     this.lastActive = false;
21469
21470     this.addEvents({
21471         /**
21472              * @event selectionchange
21473              * Fires when the selection changes
21474              * @param {SelectionModel} this
21475              */
21476             "selectionchange" : true,
21477         /**
21478              * @event afterselectionchange
21479              * Fires after the selection changes (eg. by key press or clicking)
21480              * @param {SelectionModel} this
21481              */
21482             "afterselectionchange" : true,
21483         /**
21484              * @event beforerowselect
21485              * Fires when a row is selected being selected, return false to cancel.
21486              * @param {SelectionModel} this
21487              * @param {Number} rowIndex The selected index
21488              * @param {Boolean} keepExisting False if other selections will be cleared
21489              */
21490             "beforerowselect" : true,
21491         /**
21492              * @event rowselect
21493              * Fires when a row is selected.
21494              * @param {SelectionModel} this
21495              * @param {Number} rowIndex The selected index
21496              * @param {Roo.data.Record} r The record
21497              */
21498             "rowselect" : true,
21499         /**
21500              * @event rowdeselect
21501              * Fires when a row is deselected.
21502              * @param {SelectionModel} this
21503              * @param {Number} rowIndex The selected index
21504              */
21505         "rowdeselect" : true
21506     });
21507     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21508     this.locked = false;
21509 };
21510
21511 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21512     /**
21513      * @cfg {Boolean} singleSelect
21514      * True to allow selection of only one row at a time (defaults to false)
21515      */
21516     singleSelect : false,
21517
21518     // private
21519     initEvents : function(){
21520
21521         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21522             this.grid.on("mousedown", this.handleMouseDown, this);
21523         }else{ // allow click to work like normal
21524             this.grid.on("rowclick", this.handleDragableRowClick, this);
21525         }
21526
21527         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21528             "up" : function(e){
21529                 if(!e.shiftKey){
21530                     this.selectPrevious(e.shiftKey);
21531                 }else if(this.last !== false && this.lastActive !== false){
21532                     var last = this.last;
21533                     this.selectRange(this.last,  this.lastActive-1);
21534                     this.grid.getView().focusRow(this.lastActive);
21535                     if(last !== false){
21536                         this.last = last;
21537                     }
21538                 }else{
21539                     this.selectFirstRow();
21540                 }
21541                 this.fireEvent("afterselectionchange", this);
21542             },
21543             "down" : function(e){
21544                 if(!e.shiftKey){
21545                     this.selectNext(e.shiftKey);
21546                 }else if(this.last !== false && this.lastActive !== false){
21547                     var last = this.last;
21548                     this.selectRange(this.last,  this.lastActive+1);
21549                     this.grid.getView().focusRow(this.lastActive);
21550                     if(last !== false){
21551                         this.last = last;
21552                     }
21553                 }else{
21554                     this.selectFirstRow();
21555                 }
21556                 this.fireEvent("afterselectionchange", this);
21557             },
21558             scope: this
21559         });
21560
21561         var view = this.grid.view;
21562         view.on("refresh", this.onRefresh, this);
21563         view.on("rowupdated", this.onRowUpdated, this);
21564         view.on("rowremoved", this.onRemove, this);
21565     },
21566
21567     // private
21568     onRefresh : function(){
21569         var ds = this.grid.dataSource, i, v = this.grid.view;
21570         var s = this.selections;
21571         s.each(function(r){
21572             if((i = ds.indexOfId(r.id)) != -1){
21573                 v.onRowSelect(i);
21574             }else{
21575                 s.remove(r);
21576             }
21577         });
21578     },
21579
21580     // private
21581     onRemove : function(v, index, r){
21582         this.selections.remove(r);
21583     },
21584
21585     // private
21586     onRowUpdated : function(v, index, r){
21587         if(this.isSelected(r)){
21588             v.onRowSelect(index);
21589         }
21590     },
21591
21592     /**
21593      * Select records.
21594      * @param {Array} records The records to select
21595      * @param {Boolean} keepExisting (optional) True to keep existing selections
21596      */
21597     selectRecords : function(records, keepExisting){
21598         if(!keepExisting){
21599             this.clearSelections();
21600         }
21601         var ds = this.grid.dataSource;
21602         for(var i = 0, len = records.length; i < len; i++){
21603             this.selectRow(ds.indexOf(records[i]), true);
21604         }
21605     },
21606
21607     /**
21608      * Gets the number of selected rows.
21609      * @return {Number}
21610      */
21611     getCount : function(){
21612         return this.selections.length;
21613     },
21614
21615     /**
21616      * Selects the first row in the grid.
21617      */
21618     selectFirstRow : function(){
21619         this.selectRow(0);
21620     },
21621
21622     /**
21623      * Select the last row.
21624      * @param {Boolean} keepExisting (optional) True to keep existing selections
21625      */
21626     selectLastRow : function(keepExisting){
21627         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21628     },
21629
21630     /**
21631      * Selects the row immediately following the last selected row.
21632      * @param {Boolean} keepExisting (optional) True to keep existing selections
21633      */
21634     selectNext : function(keepExisting){
21635         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21636             this.selectRow(this.last+1, keepExisting);
21637             this.grid.getView().focusRow(this.last);
21638         }
21639     },
21640
21641     /**
21642      * Selects the row that precedes the last selected row.
21643      * @param {Boolean} keepExisting (optional) True to keep existing selections
21644      */
21645     selectPrevious : function(keepExisting){
21646         if(this.last){
21647             this.selectRow(this.last-1, keepExisting);
21648             this.grid.getView().focusRow(this.last);
21649         }
21650     },
21651
21652     /**
21653      * Returns the selected records
21654      * @return {Array} Array of selected records
21655      */
21656     getSelections : function(){
21657         return [].concat(this.selections.items);
21658     },
21659
21660     /**
21661      * Returns the first selected record.
21662      * @return {Record}
21663      */
21664     getSelected : function(){
21665         return this.selections.itemAt(0);
21666     },
21667
21668
21669     /**
21670      * Clears all selections.
21671      */
21672     clearSelections : function(fast){
21673         if(this.locked) return;
21674         if(fast !== true){
21675             var ds = this.grid.dataSource;
21676             var s = this.selections;
21677             s.each(function(r){
21678                 this.deselectRow(ds.indexOfId(r.id));
21679             }, this);
21680             s.clear();
21681         }else{
21682             this.selections.clear();
21683         }
21684         this.last = false;
21685     },
21686
21687
21688     /**
21689      * Selects all rows.
21690      */
21691     selectAll : function(){
21692         if(this.locked) return;
21693         this.selections.clear();
21694         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21695             this.selectRow(i, true);
21696         }
21697     },
21698
21699     /**
21700      * Returns True if there is a selection.
21701      * @return {Boolean}
21702      */
21703     hasSelection : function(){
21704         return this.selections.length > 0;
21705     },
21706
21707     /**
21708      * Returns True if the specified row is selected.
21709      * @param {Number/Record} record The record or index of the record to check
21710      * @return {Boolean}
21711      */
21712     isSelected : function(index){
21713         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21714         return (r && this.selections.key(r.id) ? true : false);
21715     },
21716
21717     /**
21718      * Returns True if the specified record id is selected.
21719      * @param {String} id The id of record to check
21720      * @return {Boolean}
21721      */
21722     isIdSelected : function(id){
21723         return (this.selections.key(id) ? true : false);
21724     },
21725
21726     // private
21727     handleMouseDown : function(e, t){
21728         var view = this.grid.getView(), rowIndex;
21729         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21730             return;
21731         };
21732         if(e.shiftKey && this.last !== false){
21733             var last = this.last;
21734             this.selectRange(last, rowIndex, e.ctrlKey);
21735             this.last = last; // reset the last
21736             view.focusRow(rowIndex);
21737         }else{
21738             var isSelected = this.isSelected(rowIndex);
21739             if(e.button !== 0 && isSelected){
21740                 view.focusRow(rowIndex);
21741             }else if(e.ctrlKey && isSelected){
21742                 this.deselectRow(rowIndex);
21743             }else if(!isSelected){
21744                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21745                 view.focusRow(rowIndex);
21746             }
21747         }
21748         this.fireEvent("afterselectionchange", this);
21749     },
21750     // private
21751     handleDragableRowClick :  function(grid, rowIndex, e) 
21752     {
21753         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21754             this.selectRow(rowIndex, false);
21755             grid.view.focusRow(rowIndex);
21756              this.fireEvent("afterselectionchange", this);
21757         }
21758     },
21759     
21760     /**
21761      * Selects multiple rows.
21762      * @param {Array} rows Array of the indexes of the row to select
21763      * @param {Boolean} keepExisting (optional) True to keep existing selections
21764      */
21765     selectRows : function(rows, keepExisting){
21766         if(!keepExisting){
21767             this.clearSelections();
21768         }
21769         for(var i = 0, len = rows.length; i < len; i++){
21770             this.selectRow(rows[i], true);
21771         }
21772     },
21773
21774     /**
21775      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21776      * @param {Number} startRow The index of the first row in the range
21777      * @param {Number} endRow The index of the last row in the range
21778      * @param {Boolean} keepExisting (optional) True to retain existing selections
21779      */
21780     selectRange : function(startRow, endRow, keepExisting){
21781         if(this.locked) return;
21782         if(!keepExisting){
21783             this.clearSelections();
21784         }
21785         if(startRow <= endRow){
21786             for(var i = startRow; i <= endRow; i++){
21787                 this.selectRow(i, true);
21788             }
21789         }else{
21790             for(var i = startRow; i >= endRow; i--){
21791                 this.selectRow(i, true);
21792             }
21793         }
21794     },
21795
21796     /**
21797      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21798      * @param {Number} startRow The index of the first row in the range
21799      * @param {Number} endRow The index of the last row in the range
21800      */
21801     deselectRange : function(startRow, endRow, preventViewNotify){
21802         if(this.locked) return;
21803         for(var i = startRow; i <= endRow; i++){
21804             this.deselectRow(i, preventViewNotify);
21805         }
21806     },
21807
21808     /**
21809      * Selects a row.
21810      * @param {Number} row The index of the row to select
21811      * @param {Boolean} keepExisting (optional) True to keep existing selections
21812      */
21813     selectRow : function(index, keepExisting, preventViewNotify){
21814         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21815         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21816             if(!keepExisting || this.singleSelect){
21817                 this.clearSelections();
21818             }
21819             var r = this.grid.dataSource.getAt(index);
21820             this.selections.add(r);
21821             this.last = this.lastActive = index;
21822             if(!preventViewNotify){
21823                 this.grid.getView().onRowSelect(index);
21824             }
21825             this.fireEvent("rowselect", this, index, r);
21826             this.fireEvent("selectionchange", this);
21827         }
21828     },
21829
21830     /**
21831      * Deselects a row.
21832      * @param {Number} row The index of the row to deselect
21833      */
21834     deselectRow : function(index, preventViewNotify){
21835         if(this.locked) return;
21836         if(this.last == index){
21837             this.last = false;
21838         }
21839         if(this.lastActive == index){
21840             this.lastActive = false;
21841         }
21842         var r = this.grid.dataSource.getAt(index);
21843         this.selections.remove(r);
21844         if(!preventViewNotify){
21845             this.grid.getView().onRowDeselect(index);
21846         }
21847         this.fireEvent("rowdeselect", this, index);
21848         this.fireEvent("selectionchange", this);
21849     },
21850
21851     // private
21852     restoreLast : function(){
21853         if(this._last){
21854             this.last = this._last;
21855         }
21856     },
21857
21858     // private
21859     acceptsNav : function(row, col, cm){
21860         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21861     },
21862
21863     // private
21864     onEditorKey : function(field, e){
21865         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21866         if(k == e.TAB){
21867             e.stopEvent();
21868             ed.completeEdit();
21869             if(e.shiftKey){
21870                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21871             }else{
21872                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21873             }
21874         }else if(k == e.ENTER && !e.ctrlKey){
21875             e.stopEvent();
21876             ed.completeEdit();
21877             if(e.shiftKey){
21878                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21879             }else{
21880                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21881             }
21882         }else if(k == e.ESC){
21883             ed.cancelEdit();
21884         }
21885         if(newCell){
21886             g.startEditing(newCell[0], newCell[1]);
21887         }
21888     }
21889 });/*
21890  * Based on:
21891  * Ext JS Library 1.1.1
21892  * Copyright(c) 2006-2007, Ext JS, LLC.
21893  *
21894  * Originally Released Under LGPL - original licence link has changed is not relivant.
21895  *
21896  * Fork - LGPL
21897  * <script type="text/javascript">
21898  */
21899  
21900 /**
21901  * @class Roo.bootstrap.PagingToolbar
21902  * @extends Roo.bootstrap.NavSimplebar
21903  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21904  * @constructor
21905  * Create a new PagingToolbar
21906  * @param {Object} config The config object
21907  * @param {Roo.data.Store} store
21908  */
21909 Roo.bootstrap.PagingToolbar = function(config)
21910 {
21911     // old args format still supported... - xtype is prefered..
21912         // created from xtype...
21913     
21914     this.ds = config.dataSource;
21915     
21916     if (config.store && !this.ds) {
21917         this.store= Roo.factory(config.store, Roo.data);
21918         this.ds = this.store;
21919         this.ds.xmodule = this.xmodule || false;
21920     }
21921     
21922     this.toolbarItems = [];
21923     if (config.items) {
21924         this.toolbarItems = config.items;
21925     }
21926     
21927     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21928     
21929     this.cursor = 0;
21930     
21931     if (this.ds) { 
21932         this.bind(this.ds);
21933     }
21934     
21935     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21936     
21937 };
21938
21939 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21940     /**
21941      * @cfg {Roo.data.Store} dataSource
21942      * The underlying data store providing the paged data
21943      */
21944     /**
21945      * @cfg {String/HTMLElement/Element} container
21946      * container The id or element that will contain the toolbar
21947      */
21948     /**
21949      * @cfg {Boolean} displayInfo
21950      * True to display the displayMsg (defaults to false)
21951      */
21952     /**
21953      * @cfg {Number} pageSize
21954      * The number of records to display per page (defaults to 20)
21955      */
21956     pageSize: 20,
21957     /**
21958      * @cfg {String} displayMsg
21959      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21960      */
21961     displayMsg : 'Displaying {0} - {1} of {2}',
21962     /**
21963      * @cfg {String} emptyMsg
21964      * The message to display when no records are found (defaults to "No data to display")
21965      */
21966     emptyMsg : 'No data to display',
21967     /**
21968      * Customizable piece of the default paging text (defaults to "Page")
21969      * @type String
21970      */
21971     beforePageText : "Page",
21972     /**
21973      * Customizable piece of the default paging text (defaults to "of %0")
21974      * @type String
21975      */
21976     afterPageText : "of {0}",
21977     /**
21978      * Customizable piece of the default paging text (defaults to "First Page")
21979      * @type String
21980      */
21981     firstText : "First Page",
21982     /**
21983      * Customizable piece of the default paging text (defaults to "Previous Page")
21984      * @type String
21985      */
21986     prevText : "Previous Page",
21987     /**
21988      * Customizable piece of the default paging text (defaults to "Next Page")
21989      * @type String
21990      */
21991     nextText : "Next Page",
21992     /**
21993      * Customizable piece of the default paging text (defaults to "Last Page")
21994      * @type String
21995      */
21996     lastText : "Last Page",
21997     /**
21998      * Customizable piece of the default paging text (defaults to "Refresh")
21999      * @type String
22000      */
22001     refreshText : "Refresh",
22002
22003     buttons : false,
22004     // private
22005     onRender : function(ct, position) 
22006     {
22007         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22008         this.navgroup.parentId = this.id;
22009         this.navgroup.onRender(this.el, null);
22010         // add the buttons to the navgroup
22011         
22012         if(this.displayInfo){
22013             Roo.log(this.el.select('ul.navbar-nav',true).first());
22014             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22015             this.displayEl = this.el.select('.x-paging-info', true).first();
22016 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22017 //            this.displayEl = navel.el.select('span',true).first();
22018         }
22019         
22020         var _this = this;
22021         
22022         if(this.buttons){
22023             Roo.each(_this.buttons, function(e){ // this might need to use render????
22024                Roo.factory(e).onRender(_this.el, null);
22025             });
22026         }
22027             
22028         Roo.each(_this.toolbarItems, function(e) {
22029             _this.navgroup.addItem(e);
22030         });
22031         
22032         
22033         this.first = this.navgroup.addItem({
22034             tooltip: this.firstText,
22035             cls: "prev",
22036             icon : 'fa fa-backward',
22037             disabled: true,
22038             preventDefault: true,
22039             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22040         });
22041         
22042         this.prev =  this.navgroup.addItem({
22043             tooltip: this.prevText,
22044             cls: "prev",
22045             icon : 'fa fa-step-backward',
22046             disabled: true,
22047             preventDefault: true,
22048             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22049         });
22050     //this.addSeparator();
22051         
22052         
22053         var field = this.navgroup.addItem( {
22054             tagtype : 'span',
22055             cls : 'x-paging-position',
22056             
22057             html : this.beforePageText  +
22058                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22059                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22060          } ); //?? escaped?
22061         
22062         this.field = field.el.select('input', true).first();
22063         this.field.on("keydown", this.onPagingKeydown, this);
22064         this.field.on("focus", function(){this.dom.select();});
22065     
22066     
22067         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22068         //this.field.setHeight(18);
22069         //this.addSeparator();
22070         this.next = this.navgroup.addItem({
22071             tooltip: this.nextText,
22072             cls: "next",
22073             html : ' <i class="fa fa-step-forward">',
22074             disabled: true,
22075             preventDefault: true,
22076             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22077         });
22078         this.last = this.navgroup.addItem({
22079             tooltip: this.lastText,
22080             icon : 'fa fa-forward',
22081             cls: "next",
22082             disabled: true,
22083             preventDefault: true,
22084             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22085         });
22086     //this.addSeparator();
22087         this.loading = this.navgroup.addItem({
22088             tooltip: this.refreshText,
22089             icon: 'fa fa-refresh',
22090             preventDefault: true,
22091             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22092         });
22093         
22094     },
22095
22096     // private
22097     updateInfo : function(){
22098         if(this.displayEl){
22099             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22100             var msg = count == 0 ?
22101                 this.emptyMsg :
22102                 String.format(
22103                     this.displayMsg,
22104                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22105                 );
22106             this.displayEl.update(msg);
22107         }
22108     },
22109
22110     // private
22111     onLoad : function(ds, r, o){
22112        this.cursor = o.params ? o.params.start : 0;
22113        var d = this.getPageData(),
22114             ap = d.activePage,
22115             ps = d.pages;
22116         
22117        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22118        this.field.dom.value = ap;
22119        this.first.setDisabled(ap == 1);
22120        this.prev.setDisabled(ap == 1);
22121        this.next.setDisabled(ap == ps);
22122        this.last.setDisabled(ap == ps);
22123        this.loading.enable();
22124        this.updateInfo();
22125     },
22126
22127     // private
22128     getPageData : function(){
22129         var total = this.ds.getTotalCount();
22130         return {
22131             total : total,
22132             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22133             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22134         };
22135     },
22136
22137     // private
22138     onLoadError : function(){
22139         this.loading.enable();
22140     },
22141
22142     // private
22143     onPagingKeydown : function(e){
22144         var k = e.getKey();
22145         var d = this.getPageData();
22146         if(k == e.RETURN){
22147             var v = this.field.dom.value, pageNum;
22148             if(!v || isNaN(pageNum = parseInt(v, 10))){
22149                 this.field.dom.value = d.activePage;
22150                 return;
22151             }
22152             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22153             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22154             e.stopEvent();
22155         }
22156         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
22157         {
22158           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22159           this.field.dom.value = pageNum;
22160           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22161           e.stopEvent();
22162         }
22163         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22164         {
22165           var v = this.field.dom.value, pageNum; 
22166           var increment = (e.shiftKey) ? 10 : 1;
22167           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22168             increment *= -1;
22169           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22170             this.field.dom.value = d.activePage;
22171             return;
22172           }
22173           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22174           {
22175             this.field.dom.value = parseInt(v, 10) + increment;
22176             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22177             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22178           }
22179           e.stopEvent();
22180         }
22181     },
22182
22183     // private
22184     beforeLoad : function(){
22185         if(this.loading){
22186             this.loading.disable();
22187         }
22188     },
22189
22190     // private
22191     onClick : function(which){
22192         
22193         var ds = this.ds;
22194         if (!ds) {
22195             return;
22196         }
22197         
22198         switch(which){
22199             case "first":
22200                 ds.load({params:{start: 0, limit: this.pageSize}});
22201             break;
22202             case "prev":
22203                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22204             break;
22205             case "next":
22206                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22207             break;
22208             case "last":
22209                 var total = ds.getTotalCount();
22210                 var extra = total % this.pageSize;
22211                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22212                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22213             break;
22214             case "refresh":
22215                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22216             break;
22217         }
22218     },
22219
22220     /**
22221      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22222      * @param {Roo.data.Store} store The data store to unbind
22223      */
22224     unbind : function(ds){
22225         ds.un("beforeload", this.beforeLoad, this);
22226         ds.un("load", this.onLoad, this);
22227         ds.un("loadexception", this.onLoadError, this);
22228         ds.un("remove", this.updateInfo, this);
22229         ds.un("add", this.updateInfo, this);
22230         this.ds = undefined;
22231     },
22232
22233     /**
22234      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22235      * @param {Roo.data.Store} store The data store to bind
22236      */
22237     bind : function(ds){
22238         ds.on("beforeload", this.beforeLoad, this);
22239         ds.on("load", this.onLoad, this);
22240         ds.on("loadexception", this.onLoadError, this);
22241         ds.on("remove", this.updateInfo, this);
22242         ds.on("add", this.updateInfo, this);
22243         this.ds = ds;
22244     }
22245 });/*
22246  * - LGPL
22247  *
22248  * element
22249  * 
22250  */
22251
22252 /**
22253  * @class Roo.bootstrap.MessageBar
22254  * @extends Roo.bootstrap.Component
22255  * Bootstrap MessageBar class
22256  * @cfg {String} html contents of the MessageBar
22257  * @cfg {String} weight (info | success | warning | danger) default info
22258  * @cfg {String} beforeClass insert the bar before the given class
22259  * @cfg {Boolean} closable (true | false) default false
22260  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22261  * 
22262  * @constructor
22263  * Create a new Element
22264  * @param {Object} config The config object
22265  */
22266
22267 Roo.bootstrap.MessageBar = function(config){
22268     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22269 };
22270
22271 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22272     
22273     html: '',
22274     weight: 'info',
22275     closable: false,
22276     fixed: false,
22277     beforeClass: 'bootstrap-sticky-wrap',
22278     
22279     getAutoCreate : function(){
22280         
22281         var cfg = {
22282             tag: 'div',
22283             cls: 'alert alert-dismissable alert-' + this.weight,
22284             cn: [
22285                 {
22286                     tag: 'span',
22287                     cls: 'message',
22288                     html: this.html || ''
22289                 }
22290             ]
22291         };
22292         
22293         if(this.fixed){
22294             cfg.cls += ' alert-messages-fixed';
22295         }
22296         
22297         if(this.closable){
22298             cfg.cn.push({
22299                 tag: 'button',
22300                 cls: 'close',
22301                 html: 'x'
22302             });
22303         }
22304         
22305         return cfg;
22306     },
22307     
22308     onRender : function(ct, position)
22309     {
22310         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22311         
22312         if(!this.el){
22313             var cfg = Roo.apply({},  this.getAutoCreate());
22314             cfg.id = Roo.id();
22315             
22316             if (this.cls) {
22317                 cfg.cls += ' ' + this.cls;
22318             }
22319             if (this.style) {
22320                 cfg.style = this.style;
22321             }
22322             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22323             
22324             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22325         }
22326         
22327         this.el.select('>button.close').on('click', this.hide, this);
22328         
22329     },
22330     
22331     show : function()
22332     {
22333         if (!this.rendered) {
22334             this.render();
22335         }
22336         
22337         this.el.show();
22338         
22339         this.fireEvent('show', this);
22340         
22341     },
22342     
22343     hide : function()
22344     {
22345         if (!this.rendered) {
22346             this.render();
22347         }
22348         
22349         this.el.hide();
22350         
22351         this.fireEvent('hide', this);
22352     },
22353     
22354     update : function()
22355     {
22356 //        var e = this.el.dom.firstChild;
22357 //        
22358 //        if(this.closable){
22359 //            e = e.nextSibling;
22360 //        }
22361 //        
22362 //        e.data = this.html || '';
22363
22364         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22365     }
22366    
22367 });
22368
22369  
22370
22371      /*
22372  * - LGPL
22373  *
22374  * Graph
22375  * 
22376  */
22377
22378
22379 /**
22380  * @class Roo.bootstrap.Graph
22381  * @extends Roo.bootstrap.Component
22382  * Bootstrap Graph class
22383 > Prameters
22384  -sm {number} sm 4
22385  -md {number} md 5
22386  @cfg {String} graphtype  bar | vbar | pie
22387  @cfg {number} g_x coodinator | centre x (pie)
22388  @cfg {number} g_y coodinator | centre y (pie)
22389  @cfg {number} g_r radius (pie)
22390  @cfg {number} g_height height of the chart (respected by all elements in the set)
22391  @cfg {number} g_width width of the chart (respected by all elements in the set)
22392  @cfg {Object} title The title of the chart
22393     
22394  -{Array}  values
22395  -opts (object) options for the chart 
22396      o {
22397      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22398      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22399      o vgutter (number)
22400      o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
22401      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22402      o to
22403      o stretch (boolean)
22404      o }
22405  -opts (object) options for the pie
22406      o{
22407      o cut
22408      o startAngle (number)
22409      o endAngle (number)
22410      } 
22411  *
22412  * @constructor
22413  * Create a new Input
22414  * @param {Object} config The config object
22415  */
22416
22417 Roo.bootstrap.Graph = function(config){
22418     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22419     
22420     this.addEvents({
22421         // img events
22422         /**
22423          * @event click
22424          * The img click event for the img.
22425          * @param {Roo.EventObject} e
22426          */
22427         "click" : true
22428     });
22429 };
22430
22431 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22432     
22433     sm: 4,
22434     md: 5,
22435     graphtype: 'bar',
22436     g_height: 250,
22437     g_width: 400,
22438     g_x: 50,
22439     g_y: 50,
22440     g_r: 30,
22441     opts:{
22442         //g_colors: this.colors,
22443         g_type: 'soft',
22444         g_gutter: '20%'
22445
22446     },
22447     title : false,
22448
22449     getAutoCreate : function(){
22450         
22451         var cfg = {
22452             tag: 'div',
22453             html : null
22454         };
22455         
22456         
22457         return  cfg;
22458     },
22459
22460     onRender : function(ct,position){
22461         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22462         this.raphael = Raphael(this.el.dom);
22463         
22464                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22465                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22466                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22467                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22468                 /*
22469                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22470                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22471                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22472                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22473                 
22474                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22475                 r.barchart(330, 10, 300, 220, data1);
22476                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22477                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22478                 */
22479                 
22480                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22481                 // r.barchart(30, 30, 560, 250,  xdata, {
22482                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22483                 //     axis : "0 0 1 1",
22484                 //     axisxlabels :  xdata
22485                 //     //yvalues : cols,
22486                    
22487                 // });
22488 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22489 //        
22490 //        this.load(null,xdata,{
22491 //                axis : "0 0 1 1",
22492 //                axisxlabels :  xdata
22493 //                });
22494
22495     },
22496
22497     load : function(graphtype,xdata,opts){
22498         this.raphael.clear();
22499         if(!graphtype) {
22500             graphtype = this.graphtype;
22501         }
22502         if(!opts){
22503             opts = this.opts;
22504         }
22505         var r = this.raphael,
22506             fin = function () {
22507                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22508             },
22509             fout = function () {
22510                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22511             },
22512             pfin = function() {
22513                 this.sector.stop();
22514                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22515
22516                 if (this.label) {
22517                     this.label[0].stop();
22518                     this.label[0].attr({ r: 7.5 });
22519                     this.label[1].attr({ "font-weight": 800 });
22520                 }
22521             },
22522             pfout = function() {
22523                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22524
22525                 if (this.label) {
22526                     this.label[0].animate({ r: 5 }, 500, "bounce");
22527                     this.label[1].attr({ "font-weight": 400 });
22528                 }
22529             };
22530
22531         switch(graphtype){
22532             case 'bar':
22533                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22534                 break;
22535             case 'hbar':
22536                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22537                 break;
22538             case 'pie':
22539 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22540 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22541 //            
22542                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22543                 
22544                 break;
22545
22546         }
22547         
22548         if(this.title){
22549             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22550         }
22551         
22552     },
22553     
22554     setTitle: function(o)
22555     {
22556         this.title = o;
22557     },
22558     
22559     initEvents: function() {
22560         
22561         if(!this.href){
22562             this.el.on('click', this.onClick, this);
22563         }
22564     },
22565     
22566     onClick : function(e)
22567     {
22568         Roo.log('img onclick');
22569         this.fireEvent('click', this, e);
22570     }
22571    
22572 });
22573
22574  
22575 /*
22576  * - LGPL
22577  *
22578  * numberBox
22579  * 
22580  */
22581 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22582
22583 /**
22584  * @class Roo.bootstrap.dash.NumberBox
22585  * @extends Roo.bootstrap.Component
22586  * Bootstrap NumberBox class
22587  * @cfg {String} headline Box headline
22588  * @cfg {String} content Box content
22589  * @cfg {String} icon Box icon
22590  * @cfg {String} footer Footer text
22591  * @cfg {String} fhref Footer href
22592  * 
22593  * @constructor
22594  * Create a new NumberBox
22595  * @param {Object} config The config object
22596  */
22597
22598
22599 Roo.bootstrap.dash.NumberBox = function(config){
22600     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22601     
22602 };
22603
22604 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22605     
22606     headline : '',
22607     content : '',
22608     icon : '',
22609     footer : '',
22610     fhref : '',
22611     ficon : '',
22612     
22613     getAutoCreate : function(){
22614         
22615         var cfg = {
22616             tag : 'div',
22617             cls : 'small-box ',
22618             cn : [
22619                 {
22620                     tag : 'div',
22621                     cls : 'inner',
22622                     cn :[
22623                         {
22624                             tag : 'h3',
22625                             cls : 'roo-headline',
22626                             html : this.headline
22627                         },
22628                         {
22629                             tag : 'p',
22630                             cls : 'roo-content',
22631                             html : this.content
22632                         }
22633                     ]
22634                 }
22635             ]
22636         };
22637         
22638         if(this.icon){
22639             cfg.cn.push({
22640                 tag : 'div',
22641                 cls : 'icon',
22642                 cn :[
22643                     {
22644                         tag : 'i',
22645                         cls : 'ion ' + this.icon
22646                     }
22647                 ]
22648             });
22649         }
22650         
22651         if(this.footer){
22652             var footer = {
22653                 tag : 'a',
22654                 cls : 'small-box-footer',
22655                 href : this.fhref || '#',
22656                 html : this.footer
22657             };
22658             
22659             cfg.cn.push(footer);
22660             
22661         }
22662         
22663         return  cfg;
22664     },
22665
22666     onRender : function(ct,position){
22667         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22668
22669
22670        
22671                 
22672     },
22673
22674     setHeadline: function (value)
22675     {
22676         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22677     },
22678     
22679     setFooter: function (value, href)
22680     {
22681         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22682         
22683         if(href){
22684             this.el.select('a.small-box-footer',true).first().attr('href', href);
22685         }
22686         
22687     },
22688
22689     setContent: function (value)
22690     {
22691         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22692     },
22693
22694     initEvents: function() 
22695     {   
22696         
22697     }
22698     
22699 });
22700
22701  
22702 /*
22703  * - LGPL
22704  *
22705  * TabBox
22706  * 
22707  */
22708 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22709
22710 /**
22711  * @class Roo.bootstrap.dash.TabBox
22712  * @extends Roo.bootstrap.Component
22713  * Bootstrap TabBox class
22714  * @cfg {String} title Title of the TabBox
22715  * @cfg {String} icon Icon of the TabBox
22716  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22717  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22718  * 
22719  * @constructor
22720  * Create a new TabBox
22721  * @param {Object} config The config object
22722  */
22723
22724
22725 Roo.bootstrap.dash.TabBox = function(config){
22726     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22727     this.addEvents({
22728         // raw events
22729         /**
22730          * @event addpane
22731          * When a pane is added
22732          * @param {Roo.bootstrap.dash.TabPane} pane
22733          */
22734         "addpane" : true,
22735         /**
22736          * @event activatepane
22737          * When a pane is activated
22738          * @param {Roo.bootstrap.dash.TabPane} pane
22739          */
22740         "activatepane" : true
22741         
22742          
22743     });
22744     
22745     this.panes = [];
22746 };
22747
22748 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22749
22750     title : '',
22751     icon : false,
22752     showtabs : true,
22753     tabScrollable : false,
22754     
22755     getChildContainer : function()
22756     {
22757         return this.el.select('.tab-content', true).first();
22758     },
22759     
22760     getAutoCreate : function(){
22761         
22762         var header = {
22763             tag: 'li',
22764             cls: 'pull-left header',
22765             html: this.title,
22766             cn : []
22767         };
22768         
22769         if(this.icon){
22770             header.cn.push({
22771                 tag: 'i',
22772                 cls: 'fa ' + this.icon
22773             });
22774         }
22775         
22776         var h = {
22777             tag: 'ul',
22778             cls: 'nav nav-tabs pull-right',
22779             cn: [
22780                 header
22781             ]
22782         };
22783         
22784         if(this.tabScrollable){
22785             h = {
22786                 tag: 'div',
22787                 cls: 'tab-header',
22788                 cn: [
22789                     {
22790                         tag: 'ul',
22791                         cls: 'nav nav-tabs pull-right',
22792                         cn: [
22793                             header
22794                         ]
22795                     }
22796                 ]
22797             };
22798         }
22799         
22800         var cfg = {
22801             tag: 'div',
22802             cls: 'nav-tabs-custom',
22803             cn: [
22804                 h,
22805                 {
22806                     tag: 'div',
22807                     cls: 'tab-content no-padding',
22808                     cn: []
22809                 }
22810             ]
22811         };
22812
22813         return  cfg;
22814     },
22815     initEvents : function()
22816     {
22817         //Roo.log('add add pane handler');
22818         this.on('addpane', this.onAddPane, this);
22819     },
22820      /**
22821      * Updates the box title
22822      * @param {String} html to set the title to.
22823      */
22824     setTitle : function(value)
22825     {
22826         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22827     },
22828     onAddPane : function(pane)
22829     {
22830         this.panes.push(pane);
22831         //Roo.log('addpane');
22832         //Roo.log(pane);
22833         // tabs are rendere left to right..
22834         if(!this.showtabs){
22835             return;
22836         }
22837         
22838         var ctr = this.el.select('.nav-tabs', true).first();
22839          
22840          
22841         var existing = ctr.select('.nav-tab',true);
22842         var qty = existing.getCount();;
22843         
22844         
22845         var tab = ctr.createChild({
22846             tag : 'li',
22847             cls : 'nav-tab' + (qty ? '' : ' active'),
22848             cn : [
22849                 {
22850                     tag : 'a',
22851                     href:'#',
22852                     html : pane.title
22853                 }
22854             ]
22855         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22856         pane.tab = tab;
22857         
22858         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22859         if (!qty) {
22860             pane.el.addClass('active');
22861         }
22862         
22863                 
22864     },
22865     onTabClick : function(ev,un,ob,pane)
22866     {
22867         //Roo.log('tab - prev default');
22868         ev.preventDefault();
22869         
22870         
22871         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22872         pane.tab.addClass('active');
22873         //Roo.log(pane.title);
22874         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22875         // technically we should have a deactivate event.. but maybe add later.
22876         // and it should not de-activate the selected tab...
22877         this.fireEvent('activatepane', pane);
22878         pane.el.addClass('active');
22879         pane.fireEvent('activate');
22880         
22881         
22882     },
22883     
22884     getActivePane : function()
22885     {
22886         var r = false;
22887         Roo.each(this.panes, function(p) {
22888             if(p.el.hasClass('active')){
22889                 r = p;
22890                 return false;
22891             }
22892             
22893             return;
22894         });
22895         
22896         return r;
22897     }
22898     
22899     
22900 });
22901
22902  
22903 /*
22904  * - LGPL
22905  *
22906  * Tab pane
22907  * 
22908  */
22909 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22910 /**
22911  * @class Roo.bootstrap.TabPane
22912  * @extends Roo.bootstrap.Component
22913  * Bootstrap TabPane class
22914  * @cfg {Boolean} active (false | true) Default false
22915  * @cfg {String} title title of panel
22916
22917  * 
22918  * @constructor
22919  * Create a new TabPane
22920  * @param {Object} config The config object
22921  */
22922
22923 Roo.bootstrap.dash.TabPane = function(config){
22924     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22925     
22926     this.addEvents({
22927         // raw events
22928         /**
22929          * @event activate
22930          * When a pane is activated
22931          * @param {Roo.bootstrap.dash.TabPane} pane
22932          */
22933         "activate" : true
22934          
22935     });
22936 };
22937
22938 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22939     
22940     active : false,
22941     title : '',
22942     
22943     // the tabBox that this is attached to.
22944     tab : false,
22945      
22946     getAutoCreate : function() 
22947     {
22948         var cfg = {
22949             tag: 'div',
22950             cls: 'tab-pane'
22951         };
22952         
22953         if(this.active){
22954             cfg.cls += ' active';
22955         }
22956         
22957         return cfg;
22958     },
22959     initEvents  : function()
22960     {
22961         //Roo.log('trigger add pane handler');
22962         this.parent().fireEvent('addpane', this)
22963     },
22964     
22965      /**
22966      * Updates the tab title 
22967      * @param {String} html to set the title to.
22968      */
22969     setTitle: function(str)
22970     {
22971         if (!this.tab) {
22972             return;
22973         }
22974         this.title = str;
22975         this.tab.select('a', true).first().dom.innerHTML = str;
22976         
22977     }
22978     
22979     
22980     
22981 });
22982
22983  
22984
22985
22986  /*
22987  * - LGPL
22988  *
22989  * menu
22990  * 
22991  */
22992 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22993
22994 /**
22995  * @class Roo.bootstrap.menu.Menu
22996  * @extends Roo.bootstrap.Component
22997  * Bootstrap Menu class - container for Menu
22998  * @cfg {String} html Text of the menu
22999  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23000  * @cfg {String} icon Font awesome icon
23001  * @cfg {String} pos Menu align to (top | bottom) default bottom
23002  * 
23003  * 
23004  * @constructor
23005  * Create a new Menu
23006  * @param {Object} config The config object
23007  */
23008
23009
23010 Roo.bootstrap.menu.Menu = function(config){
23011     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23012     
23013     this.addEvents({
23014         /**
23015          * @event beforeshow
23016          * Fires before this menu is displayed
23017          * @param {Roo.bootstrap.menu.Menu} this
23018          */
23019         beforeshow : true,
23020         /**
23021          * @event beforehide
23022          * Fires before this menu is hidden
23023          * @param {Roo.bootstrap.menu.Menu} this
23024          */
23025         beforehide : true,
23026         /**
23027          * @event show
23028          * Fires after this menu is displayed
23029          * @param {Roo.bootstrap.menu.Menu} this
23030          */
23031         show : true,
23032         /**
23033          * @event hide
23034          * Fires after this menu is hidden
23035          * @param {Roo.bootstrap.menu.Menu} this
23036          */
23037         hide : true,
23038         /**
23039          * @event click
23040          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23041          * @param {Roo.bootstrap.menu.Menu} this
23042          * @param {Roo.EventObject} e
23043          */
23044         click : true
23045     });
23046     
23047 };
23048
23049 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23050     
23051     submenu : false,
23052     html : '',
23053     weight : 'default',
23054     icon : false,
23055     pos : 'bottom',
23056     
23057     
23058     getChildContainer : function() {
23059         if(this.isSubMenu){
23060             return this.el;
23061         }
23062         
23063         return this.el.select('ul.dropdown-menu', true).first();  
23064     },
23065     
23066     getAutoCreate : function()
23067     {
23068         var text = [
23069             {
23070                 tag : 'span',
23071                 cls : 'roo-menu-text',
23072                 html : this.html
23073             }
23074         ];
23075         
23076         if(this.icon){
23077             text.unshift({
23078                 tag : 'i',
23079                 cls : 'fa ' + this.icon
23080             })
23081         }
23082         
23083         
23084         var cfg = {
23085             tag : 'div',
23086             cls : 'btn-group',
23087             cn : [
23088                 {
23089                     tag : 'button',
23090                     cls : 'dropdown-button btn btn-' + this.weight,
23091                     cn : text
23092                 },
23093                 {
23094                     tag : 'button',
23095                     cls : 'dropdown-toggle btn btn-' + this.weight,
23096                     cn : [
23097                         {
23098                             tag : 'span',
23099                             cls : 'caret'
23100                         }
23101                     ]
23102                 },
23103                 {
23104                     tag : 'ul',
23105                     cls : 'dropdown-menu'
23106                 }
23107             ]
23108             
23109         };
23110         
23111         if(this.pos == 'top'){
23112             cfg.cls += ' dropup';
23113         }
23114         
23115         if(this.isSubMenu){
23116             cfg = {
23117                 tag : 'ul',
23118                 cls : 'dropdown-menu'
23119             }
23120         }
23121         
23122         return cfg;
23123     },
23124     
23125     onRender : function(ct, position)
23126     {
23127         this.isSubMenu = ct.hasClass('dropdown-submenu');
23128         
23129         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23130     },
23131     
23132     initEvents : function() 
23133     {
23134         if(this.isSubMenu){
23135             return;
23136         }
23137         
23138         this.hidden = true;
23139         
23140         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23141         this.triggerEl.on('click', this.onTriggerPress, this);
23142         
23143         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23144         this.buttonEl.on('click', this.onClick, this);
23145         
23146     },
23147     
23148     list : function()
23149     {
23150         if(this.isSubMenu){
23151             return this.el;
23152         }
23153         
23154         return this.el.select('ul.dropdown-menu', true).first();
23155     },
23156     
23157     onClick : function(e)
23158     {
23159         this.fireEvent("click", this, e);
23160     },
23161     
23162     onTriggerPress  : function(e)
23163     {   
23164         if (this.isVisible()) {
23165             this.hide();
23166         } else {
23167             this.show();
23168         }
23169     },
23170     
23171     isVisible : function(){
23172         return !this.hidden;
23173     },
23174     
23175     show : function()
23176     {
23177         this.fireEvent("beforeshow", this);
23178         
23179         this.hidden = false;
23180         this.el.addClass('open');
23181         
23182         Roo.get(document).on("mouseup", this.onMouseUp, this);
23183         
23184         this.fireEvent("show", this);
23185         
23186         
23187     },
23188     
23189     hide : function()
23190     {
23191         this.fireEvent("beforehide", this);
23192         
23193         this.hidden = true;
23194         this.el.removeClass('open');
23195         
23196         Roo.get(document).un("mouseup", this.onMouseUp);
23197         
23198         this.fireEvent("hide", this);
23199     },
23200     
23201     onMouseUp : function()
23202     {
23203         this.hide();
23204     }
23205     
23206 });
23207
23208  
23209  /*
23210  * - LGPL
23211  *
23212  * menu item
23213  * 
23214  */
23215 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23216
23217 /**
23218  * @class Roo.bootstrap.menu.Item
23219  * @extends Roo.bootstrap.Component
23220  * Bootstrap MenuItem class
23221  * @cfg {Boolean} submenu (true | false) default false
23222  * @cfg {String} html text of the item
23223  * @cfg {String} href the link
23224  * @cfg {Boolean} disable (true | false) default false
23225  * @cfg {Boolean} preventDefault (true | false) default true
23226  * @cfg {String} icon Font awesome icon
23227  * @cfg {String} pos Submenu align to (left | right) default right 
23228  * 
23229  * 
23230  * @constructor
23231  * Create a new Item
23232  * @param {Object} config The config object
23233  */
23234
23235
23236 Roo.bootstrap.menu.Item = function(config){
23237     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23238     this.addEvents({
23239         /**
23240          * @event mouseover
23241          * Fires when the mouse is hovering over this menu
23242          * @param {Roo.bootstrap.menu.Item} this
23243          * @param {Roo.EventObject} e
23244          */
23245         mouseover : true,
23246         /**
23247          * @event mouseout
23248          * Fires when the mouse exits this menu
23249          * @param {Roo.bootstrap.menu.Item} this
23250          * @param {Roo.EventObject} e
23251          */
23252         mouseout : true,
23253         // raw events
23254         /**
23255          * @event click
23256          * The raw click event for the entire grid.
23257          * @param {Roo.EventObject} e
23258          */
23259         click : true
23260     });
23261 };
23262
23263 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23264     
23265     submenu : false,
23266     href : '',
23267     html : '',
23268     preventDefault: true,
23269     disable : false,
23270     icon : false,
23271     pos : 'right',
23272     
23273     getAutoCreate : function()
23274     {
23275         var text = [
23276             {
23277                 tag : 'span',
23278                 cls : 'roo-menu-item-text',
23279                 html : this.html
23280             }
23281         ];
23282         
23283         if(this.icon){
23284             text.unshift({
23285                 tag : 'i',
23286                 cls : 'fa ' + this.icon
23287             })
23288         }
23289         
23290         var cfg = {
23291             tag : 'li',
23292             cn : [
23293                 {
23294                     tag : 'a',
23295                     href : this.href || '#',
23296                     cn : text
23297                 }
23298             ]
23299         };
23300         
23301         if(this.disable){
23302             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23303         }
23304         
23305         if(this.submenu){
23306             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23307             
23308             if(this.pos == 'left'){
23309                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23310             }
23311         }
23312         
23313         return cfg;
23314     },
23315     
23316     initEvents : function() 
23317     {
23318         this.el.on('mouseover', this.onMouseOver, this);
23319         this.el.on('mouseout', this.onMouseOut, this);
23320         
23321         this.el.select('a', true).first().on('click', this.onClick, this);
23322         
23323     },
23324     
23325     onClick : function(e)
23326     {
23327         if(this.preventDefault){
23328             e.preventDefault();
23329         }
23330         
23331         this.fireEvent("click", this, e);
23332     },
23333     
23334     onMouseOver : function(e)
23335     {
23336         if(this.submenu && this.pos == 'left'){
23337             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23338         }
23339         
23340         this.fireEvent("mouseover", this, e);
23341     },
23342     
23343     onMouseOut : function(e)
23344     {
23345         this.fireEvent("mouseout", this, e);
23346     }
23347 });
23348
23349  
23350
23351  /*
23352  * - LGPL
23353  *
23354  * menu separator
23355  * 
23356  */
23357 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23358
23359 /**
23360  * @class Roo.bootstrap.menu.Separator
23361  * @extends Roo.bootstrap.Component
23362  * Bootstrap Separator class
23363  * 
23364  * @constructor
23365  * Create a new Separator
23366  * @param {Object} config The config object
23367  */
23368
23369
23370 Roo.bootstrap.menu.Separator = function(config){
23371     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23372 };
23373
23374 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23375     
23376     getAutoCreate : function(){
23377         var cfg = {
23378             tag : 'li',
23379             cls: 'divider'
23380         };
23381         
23382         return cfg;
23383     }
23384    
23385 });
23386
23387  
23388
23389  /*
23390  * - LGPL
23391  *
23392  * Tooltip
23393  * 
23394  */
23395
23396 /**
23397  * @class Roo.bootstrap.Tooltip
23398  * Bootstrap Tooltip class
23399  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23400  * to determine which dom element triggers the tooltip.
23401  * 
23402  * It needs to add support for additional attributes like tooltip-position
23403  * 
23404  * @constructor
23405  * Create a new Toolti
23406  * @param {Object} config The config object
23407  */
23408
23409 Roo.bootstrap.Tooltip = function(config){
23410     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23411 };
23412
23413 Roo.apply(Roo.bootstrap.Tooltip, {
23414     /**
23415      * @function init initialize tooltip monitoring.
23416      * @static
23417      */
23418     currentEl : false,
23419     currentTip : false,
23420     currentRegion : false,
23421     
23422     //  init : delay?
23423     
23424     init : function()
23425     {
23426         Roo.get(document).on('mouseover', this.enter ,this);
23427         Roo.get(document).on('mouseout', this.leave, this);
23428          
23429         
23430         this.currentTip = new Roo.bootstrap.Tooltip();
23431     },
23432     
23433     enter : function(ev)
23434     {
23435         var dom = ev.getTarget();
23436         
23437         //Roo.log(['enter',dom]);
23438         var el = Roo.fly(dom);
23439         if (this.currentEl) {
23440             //Roo.log(dom);
23441             //Roo.log(this.currentEl);
23442             //Roo.log(this.currentEl.contains(dom));
23443             if (this.currentEl == el) {
23444                 return;
23445             }
23446             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23447                 return;
23448             }
23449
23450         }
23451         
23452         if (this.currentTip.el) {
23453             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23454         }    
23455         //Roo.log(ev);
23456         var bindEl = el;
23457         
23458         // you can not look for children, as if el is the body.. then everythign is the child..
23459         if (!el.attr('tooltip')) { //
23460             if (!el.select("[tooltip]").elements.length) {
23461                 return;
23462             }
23463             // is the mouse over this child...?
23464             bindEl = el.select("[tooltip]").first();
23465             var xy = ev.getXY();
23466             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23467                 //Roo.log("not in region.");
23468                 return;
23469             }
23470             //Roo.log("child element over..");
23471             
23472         }
23473         this.currentEl = bindEl;
23474         this.currentTip.bind(bindEl);
23475         this.currentRegion = Roo.lib.Region.getRegion(dom);
23476         this.currentTip.enter();
23477         
23478     },
23479     leave : function(ev)
23480     {
23481         var dom = ev.getTarget();
23482         //Roo.log(['leave',dom]);
23483         if (!this.currentEl) {
23484             return;
23485         }
23486         
23487         
23488         if (dom != this.currentEl.dom) {
23489             return;
23490         }
23491         var xy = ev.getXY();
23492         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23493             return;
23494         }
23495         // only activate leave if mouse cursor is outside... bounding box..
23496         
23497         
23498         
23499         
23500         if (this.currentTip) {
23501             this.currentTip.leave();
23502         }
23503         //Roo.log('clear currentEl');
23504         this.currentEl = false;
23505         
23506         
23507     },
23508     alignment : {
23509         'left' : ['r-l', [-2,0], 'right'],
23510         'right' : ['l-r', [2,0], 'left'],
23511         'bottom' : ['t-b', [0,2], 'top'],
23512         'top' : [ 'b-t', [0,-2], 'bottom']
23513     }
23514     
23515 });
23516
23517
23518 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23519     
23520     
23521     bindEl : false,
23522     
23523     delay : null, // can be { show : 300 , hide: 500}
23524     
23525     timeout : null,
23526     
23527     hoverState : null, //???
23528     
23529     placement : 'bottom', 
23530     
23531     getAutoCreate : function(){
23532     
23533         var cfg = {
23534            cls : 'tooltip',
23535            role : 'tooltip',
23536            cn : [
23537                 {
23538                     cls : 'tooltip-arrow'
23539                 },
23540                 {
23541                     cls : 'tooltip-inner'
23542                 }
23543            ]
23544         };
23545         
23546         return cfg;
23547     },
23548     bind : function(el)
23549     {
23550         this.bindEl = el;
23551     },
23552       
23553     
23554     enter : function () {
23555        
23556         if (this.timeout != null) {
23557             clearTimeout(this.timeout);
23558         }
23559         
23560         this.hoverState = 'in';
23561          //Roo.log("enter - show");
23562         if (!this.delay || !this.delay.show) {
23563             this.show();
23564             return;
23565         }
23566         var _t = this;
23567         this.timeout = setTimeout(function () {
23568             if (_t.hoverState == 'in') {
23569                 _t.show();
23570             }
23571         }, this.delay.show);
23572     },
23573     leave : function()
23574     {
23575         clearTimeout(this.timeout);
23576     
23577         this.hoverState = 'out';
23578          if (!this.delay || !this.delay.hide) {
23579             this.hide();
23580             return;
23581         }
23582        
23583         var _t = this;
23584         this.timeout = setTimeout(function () {
23585             //Roo.log("leave - timeout");
23586             
23587             if (_t.hoverState == 'out') {
23588                 _t.hide();
23589                 Roo.bootstrap.Tooltip.currentEl = false;
23590             }
23591         }, delay);
23592     },
23593     
23594     show : function ()
23595     {
23596         if (!this.el) {
23597             this.render(document.body);
23598         }
23599         // set content.
23600         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23601         
23602         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23603         
23604         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23605         
23606         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23607         
23608         var placement = typeof this.placement == 'function' ?
23609             this.placement.call(this, this.el, on_el) :
23610             this.placement;
23611             
23612         var autoToken = /\s?auto?\s?/i;
23613         var autoPlace = autoToken.test(placement);
23614         if (autoPlace) {
23615             placement = placement.replace(autoToken, '') || 'top';
23616         }
23617         
23618         //this.el.detach()
23619         //this.el.setXY([0,0]);
23620         this.el.show();
23621         //this.el.dom.style.display='block';
23622         
23623         //this.el.appendTo(on_el);
23624         
23625         var p = this.getPosition();
23626         var box = this.el.getBox();
23627         
23628         if (autoPlace) {
23629             // fixme..
23630         }
23631         
23632         var align = Roo.bootstrap.Tooltip.alignment[placement];
23633         
23634         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23635         
23636         if(placement == 'top' || placement == 'bottom'){
23637             if(xy[0] < 0){
23638                 placement = 'right';
23639             }
23640             
23641             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23642                 placement = 'left';
23643             }
23644         }
23645         
23646         align = Roo.bootstrap.Tooltip.alignment[placement];
23647         
23648         this.el.alignTo(this.bindEl, align[0],align[1]);
23649         //var arrow = this.el.select('.arrow',true).first();
23650         //arrow.set(align[2], 
23651         
23652         this.el.addClass(placement);
23653         
23654         this.el.addClass('in fade');
23655         
23656         this.hoverState = null;
23657         
23658         if (this.el.hasClass('fade')) {
23659             // fade it?
23660         }
23661         
23662     },
23663     hide : function()
23664     {
23665          
23666         if (!this.el) {
23667             return;
23668         }
23669         //this.el.setXY([0,0]);
23670         this.el.removeClass('in');
23671         //this.el.hide();
23672         
23673     }
23674     
23675 });
23676  
23677
23678  /*
23679  * - LGPL
23680  *
23681  * Location Picker
23682  * 
23683  */
23684
23685 /**
23686  * @class Roo.bootstrap.LocationPicker
23687  * @extends Roo.bootstrap.Component
23688  * Bootstrap LocationPicker class
23689  * @cfg {Number} latitude Position when init default 0
23690  * @cfg {Number} longitude Position when init default 0
23691  * @cfg {Number} zoom default 15
23692  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23693  * @cfg {Boolean} mapTypeControl default false
23694  * @cfg {Boolean} disableDoubleClickZoom default false
23695  * @cfg {Boolean} scrollwheel default true
23696  * @cfg {Boolean} streetViewControl default false
23697  * @cfg {Number} radius default 0
23698  * @cfg {String} locationName
23699  * @cfg {Boolean} draggable default true
23700  * @cfg {Boolean} enableAutocomplete default false
23701  * @cfg {Boolean} enableReverseGeocode default true
23702  * @cfg {String} markerTitle
23703  * 
23704  * @constructor
23705  * Create a new LocationPicker
23706  * @param {Object} config The config object
23707  */
23708
23709
23710 Roo.bootstrap.LocationPicker = function(config){
23711     
23712     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23713     
23714     this.addEvents({
23715         /**
23716          * @event initial
23717          * Fires when the picker initialized.
23718          * @param {Roo.bootstrap.LocationPicker} this
23719          * @param {Google Location} location
23720          */
23721         initial : true,
23722         /**
23723          * @event positionchanged
23724          * Fires when the picker position changed.
23725          * @param {Roo.bootstrap.LocationPicker} this
23726          * @param {Google Location} location
23727          */
23728         positionchanged : true,
23729         /**
23730          * @event resize
23731          * Fires when the map resize.
23732          * @param {Roo.bootstrap.LocationPicker} this
23733          */
23734         resize : true,
23735         /**
23736          * @event show
23737          * Fires when the map show.
23738          * @param {Roo.bootstrap.LocationPicker} this
23739          */
23740         show : true,
23741         /**
23742          * @event hide
23743          * Fires when the map hide.
23744          * @param {Roo.bootstrap.LocationPicker} this
23745          */
23746         hide : true,
23747         /**
23748          * @event mapClick
23749          * Fires when click the map.
23750          * @param {Roo.bootstrap.LocationPicker} this
23751          * @param {Map event} e
23752          */
23753         mapClick : true,
23754         /**
23755          * @event mapRightClick
23756          * Fires when right click the map.
23757          * @param {Roo.bootstrap.LocationPicker} this
23758          * @param {Map event} e
23759          */
23760         mapRightClick : true,
23761         /**
23762          * @event markerClick
23763          * Fires when click the marker.
23764          * @param {Roo.bootstrap.LocationPicker} this
23765          * @param {Map event} e
23766          */
23767         markerClick : true,
23768         /**
23769          * @event markerRightClick
23770          * Fires when right click the marker.
23771          * @param {Roo.bootstrap.LocationPicker} this
23772          * @param {Map event} e
23773          */
23774         markerRightClick : true,
23775         /**
23776          * @event OverlayViewDraw
23777          * Fires when OverlayView Draw
23778          * @param {Roo.bootstrap.LocationPicker} this
23779          */
23780         OverlayViewDraw : true,
23781         /**
23782          * @event OverlayViewOnAdd
23783          * Fires when OverlayView Draw
23784          * @param {Roo.bootstrap.LocationPicker} this
23785          */
23786         OverlayViewOnAdd : true,
23787         /**
23788          * @event OverlayViewOnRemove
23789          * Fires when OverlayView Draw
23790          * @param {Roo.bootstrap.LocationPicker} this
23791          */
23792         OverlayViewOnRemove : true,
23793         /**
23794          * @event OverlayViewShow
23795          * Fires when OverlayView Draw
23796          * @param {Roo.bootstrap.LocationPicker} this
23797          * @param {Pixel} cpx
23798          */
23799         OverlayViewShow : true,
23800         /**
23801          * @event OverlayViewHide
23802          * Fires when OverlayView Draw
23803          * @param {Roo.bootstrap.LocationPicker} this
23804          */
23805         OverlayViewHide : true,
23806         /**
23807          * @event loadexception
23808          * Fires when load google lib failed.
23809          * @param {Roo.bootstrap.LocationPicker} this
23810          */
23811         loadexception : true
23812     });
23813         
23814 };
23815
23816 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23817     
23818     gMapContext: false,
23819     
23820     latitude: 0,
23821     longitude: 0,
23822     zoom: 15,
23823     mapTypeId: false,
23824     mapTypeControl: false,
23825     disableDoubleClickZoom: false,
23826     scrollwheel: true,
23827     streetViewControl: false,
23828     radius: 0,
23829     locationName: '',
23830     draggable: true,
23831     enableAutocomplete: false,
23832     enableReverseGeocode: true,
23833     markerTitle: '',
23834     
23835     getAutoCreate: function()
23836     {
23837
23838         var cfg = {
23839             tag: 'div',
23840             cls: 'roo-location-picker'
23841         };
23842         
23843         return cfg
23844     },
23845     
23846     initEvents: function(ct, position)
23847     {       
23848         if(!this.el.getWidth() || this.isApplied()){
23849             return;
23850         }
23851         
23852         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23853         
23854         this.initial();
23855     },
23856     
23857     initial: function()
23858     {
23859         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
23860             this.fireEvent('loadexception', this);
23861             return;
23862         }
23863         
23864         if(!this.mapTypeId){
23865             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23866         }
23867         
23868         this.gMapContext = this.GMapContext();
23869         
23870         this.initOverlayView();
23871         
23872         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23873         
23874         var _this = this;
23875                 
23876         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23877             _this.setPosition(_this.gMapContext.marker.position);
23878         });
23879         
23880         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23881             _this.fireEvent('mapClick', this, event);
23882             
23883         });
23884
23885         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23886             _this.fireEvent('mapRightClick', this, event);
23887             
23888         });
23889         
23890         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23891             _this.fireEvent('markerClick', this, event);
23892             
23893         });
23894
23895         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23896             _this.fireEvent('markerRightClick', this, event);
23897             
23898         });
23899         
23900         this.setPosition(this.gMapContext.location);
23901         
23902         this.fireEvent('initial', this, this.gMapContext.location);
23903     },
23904     
23905     initOverlayView: function()
23906     {
23907         var _this = this;
23908         
23909         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23910             
23911             draw: function()
23912             {
23913                 _this.fireEvent('OverlayViewDraw', _this);
23914             },
23915             
23916             onAdd: function()
23917             {
23918                 _this.fireEvent('OverlayViewOnAdd', _this);
23919             },
23920             
23921             onRemove: function()
23922             {
23923                 _this.fireEvent('OverlayViewOnRemove', _this);
23924             },
23925             
23926             show: function(cpx)
23927             {
23928                 _this.fireEvent('OverlayViewShow', _this, cpx);
23929             },
23930             
23931             hide: function()
23932             {
23933                 _this.fireEvent('OverlayViewHide', _this);
23934             }
23935             
23936         });
23937     },
23938     
23939     fromLatLngToContainerPixel: function(event)
23940     {
23941         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23942     },
23943     
23944     isApplied: function() 
23945     {
23946         return this.getGmapContext() == false ? false : true;
23947     },
23948     
23949     getGmapContext: function() 
23950     {
23951         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
23952     },
23953     
23954     GMapContext: function() 
23955     {
23956         var position = new google.maps.LatLng(this.latitude, this.longitude);
23957         
23958         var _map = new google.maps.Map(this.el.dom, {
23959             center: position,
23960             zoom: this.zoom,
23961             mapTypeId: this.mapTypeId,
23962             mapTypeControl: this.mapTypeControl,
23963             disableDoubleClickZoom: this.disableDoubleClickZoom,
23964             scrollwheel: this.scrollwheel,
23965             streetViewControl: this.streetViewControl,
23966             locationName: this.locationName,
23967             draggable: this.draggable,
23968             enableAutocomplete: this.enableAutocomplete,
23969             enableReverseGeocode: this.enableReverseGeocode
23970         });
23971         
23972         var _marker = new google.maps.Marker({
23973             position: position,
23974             map: _map,
23975             title: this.markerTitle,
23976             draggable: this.draggable
23977         });
23978         
23979         return {
23980             map: _map,
23981             marker: _marker,
23982             circle: null,
23983             location: position,
23984             radius: this.radius,
23985             locationName: this.locationName,
23986             addressComponents: {
23987                 formatted_address: null,
23988                 addressLine1: null,
23989                 addressLine2: null,
23990                 streetName: null,
23991                 streetNumber: null,
23992                 city: null,
23993                 district: null,
23994                 state: null,
23995                 stateOrProvince: null
23996             },
23997             settings: this,
23998             domContainer: this.el.dom,
23999             geodecoder: new google.maps.Geocoder()
24000         };
24001     },
24002     
24003     drawCircle: function(center, radius, options) 
24004     {
24005         if (this.gMapContext.circle != null) {
24006             this.gMapContext.circle.setMap(null);
24007         }
24008         if (radius > 0) {
24009             radius *= 1;
24010             options = Roo.apply({}, options, {
24011                 strokeColor: "#0000FF",
24012                 strokeOpacity: .35,
24013                 strokeWeight: 2,
24014                 fillColor: "#0000FF",
24015                 fillOpacity: .2
24016             });
24017             
24018             options.map = this.gMapContext.map;
24019             options.radius = radius;
24020             options.center = center;
24021             this.gMapContext.circle = new google.maps.Circle(options);
24022             return this.gMapContext.circle;
24023         }
24024         
24025         return null;
24026     },
24027     
24028     setPosition: function(location) 
24029     {
24030         this.gMapContext.location = location;
24031         this.gMapContext.marker.setPosition(location);
24032         this.gMapContext.map.panTo(location);
24033         this.drawCircle(location, this.gMapContext.radius, {});
24034         
24035         var _this = this;
24036         
24037         if (this.gMapContext.settings.enableReverseGeocode) {
24038             this.gMapContext.geodecoder.geocode({
24039                 latLng: this.gMapContext.location
24040             }, function(results, status) {
24041                 
24042                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24043                     _this.gMapContext.locationName = results[0].formatted_address;
24044                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24045                     
24046                     _this.fireEvent('positionchanged', this, location);
24047                 }
24048             });
24049             
24050             return;
24051         }
24052         
24053         this.fireEvent('positionchanged', this, location);
24054     },
24055     
24056     resize: function()
24057     {
24058         google.maps.event.trigger(this.gMapContext.map, "resize");
24059         
24060         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24061         
24062         this.fireEvent('resize', this);
24063     },
24064     
24065     setPositionByLatLng: function(latitude, longitude)
24066     {
24067         this.setPosition(new google.maps.LatLng(latitude, longitude));
24068     },
24069     
24070     getCurrentPosition: function() 
24071     {
24072         return {
24073             latitude: this.gMapContext.location.lat(),
24074             longitude: this.gMapContext.location.lng()
24075         };
24076     },
24077     
24078     getAddressName: function() 
24079     {
24080         return this.gMapContext.locationName;
24081     },
24082     
24083     getAddressComponents: function() 
24084     {
24085         return this.gMapContext.addressComponents;
24086     },
24087     
24088     address_component_from_google_geocode: function(address_components) 
24089     {
24090         var result = {};
24091         
24092         for (var i = 0; i < address_components.length; i++) {
24093             var component = address_components[i];
24094             if (component.types.indexOf("postal_code") >= 0) {
24095                 result.postalCode = component.short_name;
24096             } else if (component.types.indexOf("street_number") >= 0) {
24097                 result.streetNumber = component.short_name;
24098             } else if (component.types.indexOf("route") >= 0) {
24099                 result.streetName = component.short_name;
24100             } else if (component.types.indexOf("neighborhood") >= 0) {
24101                 result.city = component.short_name;
24102             } else if (component.types.indexOf("locality") >= 0) {
24103                 result.city = component.short_name;
24104             } else if (component.types.indexOf("sublocality") >= 0) {
24105                 result.district = component.short_name;
24106             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24107                 result.stateOrProvince = component.short_name;
24108             } else if (component.types.indexOf("country") >= 0) {
24109                 result.country = component.short_name;
24110             }
24111         }
24112         
24113         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24114         result.addressLine2 = "";
24115         return result;
24116     },
24117     
24118     setZoomLevel: function(zoom)
24119     {
24120         this.gMapContext.map.setZoom(zoom);
24121     },
24122     
24123     show: function()
24124     {
24125         if(!this.el){
24126             return;
24127         }
24128         
24129         this.el.show();
24130         
24131         this.resize();
24132         
24133         this.fireEvent('show', this);
24134     },
24135     
24136     hide: function()
24137     {
24138         if(!this.el){
24139             return;
24140         }
24141         
24142         this.el.hide();
24143         
24144         this.fireEvent('hide', this);
24145     }
24146     
24147 });
24148
24149 Roo.apply(Roo.bootstrap.LocationPicker, {
24150     
24151     OverlayView : function(map, options)
24152     {
24153         options = options || {};
24154         
24155         this.setMap(map);
24156     }
24157     
24158     
24159 });/*
24160  * - LGPL
24161  *
24162  * Alert
24163  * 
24164  */
24165
24166 /**
24167  * @class Roo.bootstrap.Alert
24168  * @extends Roo.bootstrap.Component
24169  * Bootstrap Alert class
24170  * @cfg {String} title The title of alert
24171  * @cfg {String} html The content of alert
24172  * @cfg {String} weight (  success | info | warning | danger )
24173  * @cfg {String} faicon font-awesomeicon
24174  * 
24175  * @constructor
24176  * Create a new alert
24177  * @param {Object} config The config object
24178  */
24179
24180
24181 Roo.bootstrap.Alert = function(config){
24182     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24183     
24184 };
24185
24186 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24187     
24188     title: '',
24189     html: '',
24190     weight: false,
24191     faicon: false,
24192     
24193     getAutoCreate : function()
24194     {
24195         
24196         var cfg = {
24197             tag : 'div',
24198             cls : 'alert',
24199             cn : [
24200                 {
24201                     tag : 'i',
24202                     cls : 'roo-alert-icon'
24203                     
24204                 },
24205                 {
24206                     tag : 'b',
24207                     cls : 'roo-alert-title',
24208                     html : this.title
24209                 },
24210                 {
24211                     tag : 'span',
24212                     cls : 'roo-alert-text',
24213                     html : this.html
24214                 }
24215             ]
24216         };
24217         
24218         if(this.faicon){
24219             cfg.cn[0].cls += ' fa ' + this.faicon;
24220         }
24221         
24222         if(this.weight){
24223             cfg.cls += ' alert-' + this.weight;
24224         }
24225         
24226         return cfg;
24227     },
24228     
24229     initEvents: function() 
24230     {
24231         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24232     },
24233     
24234     setTitle : function(str)
24235     {
24236         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24237     },
24238     
24239     setText : function(str)
24240     {
24241         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24242     },
24243     
24244     setWeight : function(weight)
24245     {
24246         if(this.weight){
24247             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24248         }
24249         
24250         this.weight = weight;
24251         
24252         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24253     },
24254     
24255     setIcon : function(icon)
24256     {
24257         if(this.faicon){
24258             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24259         }
24260         
24261         this.faicon = icon;
24262         
24263         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24264     },
24265     
24266     hide: function() 
24267     {
24268         this.el.hide();   
24269     },
24270     
24271     show: function() 
24272     {  
24273         this.el.show();   
24274     }
24275     
24276 });
24277
24278  
24279 /*
24280 * Licence: LGPL
24281 */
24282
24283 /**
24284  * @class Roo.bootstrap.UploadCropbox
24285  * @extends Roo.bootstrap.Component
24286  * Bootstrap UploadCropbox class
24287  * @cfg {String} emptyText show when image has been loaded
24288  * @cfg {String} rotateNotify show when image too small to rotate
24289  * @cfg {Number} errorTimeout default 3000
24290  * @cfg {Number} minWidth default 300
24291  * @cfg {Number} minHeight default 300
24292  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24293  * @cfg {Boolean} isDocument (true|false) default false
24294  * @cfg {String} url action url
24295  * @cfg {String} paramName default 'imageUpload'
24296  * @cfg {String} method default POST
24297  * @cfg {Boolean} loadMask (true|false) default true
24298  * @cfg {Boolean} loadingText default 'Loading...'
24299  * 
24300  * @constructor
24301  * Create a new UploadCropbox
24302  * @param {Object} config The config object
24303  */
24304
24305 Roo.bootstrap.UploadCropbox = function(config){
24306     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24307     
24308     this.addEvents({
24309         /**
24310          * @event beforeselectfile
24311          * Fire before select file
24312          * @param {Roo.bootstrap.UploadCropbox} this
24313          */
24314         "beforeselectfile" : true,
24315         /**
24316          * @event initial
24317          * Fire after initEvent
24318          * @param {Roo.bootstrap.UploadCropbox} this
24319          */
24320         "initial" : true,
24321         /**
24322          * @event crop
24323          * Fire after initEvent
24324          * @param {Roo.bootstrap.UploadCropbox} this
24325          * @param {String} data
24326          */
24327         "crop" : true,
24328         /**
24329          * @event prepare
24330          * Fire when preparing the file data
24331          * @param {Roo.bootstrap.UploadCropbox} this
24332          * @param {Object} file
24333          */
24334         "prepare" : true,
24335         /**
24336          * @event exception
24337          * Fire when get exception
24338          * @param {Roo.bootstrap.UploadCropbox} this
24339          * @param {XMLHttpRequest} xhr
24340          */
24341         "exception" : true,
24342         /**
24343          * @event beforeloadcanvas
24344          * Fire before load the canvas
24345          * @param {Roo.bootstrap.UploadCropbox} this
24346          * @param {String} src
24347          */
24348         "beforeloadcanvas" : true,
24349         /**
24350          * @event trash
24351          * Fire when trash image
24352          * @param {Roo.bootstrap.UploadCropbox} this
24353          */
24354         "trash" : true,
24355         /**
24356          * @event download
24357          * Fire when download the image
24358          * @param {Roo.bootstrap.UploadCropbox} this
24359          */
24360         "download" : true,
24361         /**
24362          * @event footerbuttonclick
24363          * Fire when footerbuttonclick
24364          * @param {Roo.bootstrap.UploadCropbox} this
24365          * @param {String} type
24366          */
24367         "footerbuttonclick" : true,
24368         /**
24369          * @event resize
24370          * Fire when resize
24371          * @param {Roo.bootstrap.UploadCropbox} this
24372          */
24373         "resize" : true,
24374         /**
24375          * @event rotate
24376          * Fire when rotate the image
24377          * @param {Roo.bootstrap.UploadCropbox} this
24378          * @param {String} pos
24379          */
24380         "rotate" : true,
24381         /**
24382          * @event inspect
24383          * Fire when inspect the file
24384          * @param {Roo.bootstrap.UploadCropbox} this
24385          * @param {Object} file
24386          */
24387         "inspect" : true,
24388         /**
24389          * @event upload
24390          * Fire when xhr upload the file
24391          * @param {Roo.bootstrap.UploadCropbox} this
24392          * @param {Object} data
24393          */
24394         "upload" : true,
24395         /**
24396          * @event arrange
24397          * Fire when arrange the file data
24398          * @param {Roo.bootstrap.UploadCropbox} this
24399          * @param {Object} formData
24400          */
24401         "arrange" : true
24402     });
24403     
24404     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24405 };
24406
24407 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24408     
24409     emptyText : 'Click to upload image',
24410     rotateNotify : 'Image is too small to rotate',
24411     errorTimeout : 3000,
24412     scale : 0,
24413     baseScale : 1,
24414     rotate : 0,
24415     dragable : false,
24416     pinching : false,
24417     mouseX : 0,
24418     mouseY : 0,
24419     cropData : false,
24420     minWidth : 300,
24421     minHeight : 300,
24422     file : false,
24423     exif : {},
24424     baseRotate : 1,
24425     cropType : 'image/jpeg',
24426     buttons : false,
24427     canvasLoaded : false,
24428     isDocument : false,
24429     method : 'POST',
24430     paramName : 'imageUpload',
24431     loadMask : true,
24432     loadingText : 'Loading...',
24433     maskEl : false,
24434     
24435     getAutoCreate : function()
24436     {
24437         var cfg = {
24438             tag : 'div',
24439             cls : 'roo-upload-cropbox',
24440             cn : [
24441                 {
24442                     tag : 'input',
24443                     cls : 'roo-upload-cropbox-selector',
24444                     type : 'file'
24445                 },
24446                 {
24447                     tag : 'div',
24448                     cls : 'roo-upload-cropbox-body',
24449                     style : 'cursor:pointer',
24450                     cn : [
24451                         {
24452                             tag : 'div',
24453                             cls : 'roo-upload-cropbox-preview'
24454                         },
24455                         {
24456                             tag : 'div',
24457                             cls : 'roo-upload-cropbox-thumb'
24458                         },
24459                         {
24460                             tag : 'div',
24461                             cls : 'roo-upload-cropbox-empty-notify',
24462                             html : this.emptyText
24463                         },
24464                         {
24465                             tag : 'div',
24466                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24467                             html : this.rotateNotify
24468                         }
24469                     ]
24470                 },
24471                 {
24472                     tag : 'div',
24473                     cls : 'roo-upload-cropbox-footer',
24474                     cn : {
24475                         tag : 'div',
24476                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24477                         cn : []
24478                     }
24479                 }
24480             ]
24481         };
24482         
24483         return cfg;
24484     },
24485     
24486     onRender : function(ct, position)
24487     {
24488         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24489         
24490         if (this.buttons.length) {
24491             
24492             Roo.each(this.buttons, function(bb) {
24493                 
24494                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24495                 
24496                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24497                 
24498             }, this);
24499         }
24500         
24501         if(this.loadMask){
24502             this.maskEl = this.el;
24503         }
24504     },
24505     
24506     initEvents : function()
24507     {
24508         this.urlAPI = (window.createObjectURL && window) || 
24509                                 (window.URL && URL.revokeObjectURL && URL) || 
24510                                 (window.webkitURL && webkitURL);
24511                         
24512         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24513         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24514         
24515         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24516         this.selectorEl.hide();
24517         
24518         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24519         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24520         
24521         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24522         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24523         this.thumbEl.hide();
24524         
24525         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24526         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24527         
24528         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24529         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24530         this.errorEl.hide();
24531         
24532         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24533         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24534         this.footerEl.hide();
24535         
24536         this.setThumbBoxSize();
24537         
24538         this.bind();
24539         
24540         this.resize();
24541         
24542         this.fireEvent('initial', this);
24543     },
24544
24545     bind : function()
24546     {
24547         var _this = this;
24548         
24549         window.addEventListener("resize", function() { _this.resize(); } );
24550         
24551         this.bodyEl.on('click', this.beforeSelectFile, this);
24552         
24553         if(Roo.isTouch){
24554             this.bodyEl.on('touchstart', this.onTouchStart, this);
24555             this.bodyEl.on('touchmove', this.onTouchMove, this);
24556             this.bodyEl.on('touchend', this.onTouchEnd, this);
24557         }
24558         
24559         if(!Roo.isTouch){
24560             this.bodyEl.on('mousedown', this.onMouseDown, this);
24561             this.bodyEl.on('mousemove', this.onMouseMove, this);
24562             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24563             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24564             Roo.get(document).on('mouseup', this.onMouseUp, this);
24565         }
24566         
24567         this.selectorEl.on('change', this.onFileSelected, this);
24568     },
24569     
24570     reset : function()
24571     {    
24572         this.scale = 0;
24573         this.baseScale = 1;
24574         this.rotate = 0;
24575         this.baseRotate = 1;
24576         this.dragable = false;
24577         this.pinching = false;
24578         this.mouseX = 0;
24579         this.mouseY = 0;
24580         this.cropData = false;
24581         this.notifyEl.dom.innerHTML = this.emptyText;
24582         
24583         this.selectorEl.dom.value = '';
24584         
24585     },
24586     
24587     resize : function()
24588     {
24589         if(this.fireEvent('resize', this) != false){
24590             this.setThumbBoxPosition();
24591             this.setCanvasPosition();
24592         }
24593     },
24594     
24595     onFooterButtonClick : function(e, el, o, type)
24596     {
24597         switch (type) {
24598             case 'rotate-left' :
24599                 this.onRotateLeft(e);
24600                 break;
24601             case 'rotate-right' :
24602                 this.onRotateRight(e);
24603                 break;
24604             case 'picture' :
24605                 this.beforeSelectFile(e);
24606                 break;
24607             case 'trash' :
24608                 this.trash(e);
24609                 break;
24610             case 'crop' :
24611                 this.crop(e);
24612                 break;
24613             case 'download' :
24614                 this.download(e);
24615                 break;
24616             default :
24617                 break;
24618         }
24619         
24620         this.fireEvent('footerbuttonclick', this, type);
24621     },
24622     
24623     beforeSelectFile : function(e)
24624     {
24625         e.preventDefault();
24626         
24627         if(this.fireEvent('beforeselectfile', this) != false){
24628             this.selectorEl.dom.click();
24629         }
24630     },
24631     
24632     onFileSelected : function(e)
24633     {
24634         e.preventDefault();
24635         
24636         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24637             return;
24638         }
24639         
24640         var file = this.selectorEl.dom.files[0];
24641         
24642         if(this.fireEvent('inspect', this, file) != false){
24643             this.prepare(file);
24644         }
24645         
24646     },
24647     
24648     trash : function(e)
24649     {
24650         this.fireEvent('trash', this);
24651     },
24652     
24653     download : function(e)
24654     {
24655         this.fireEvent('download', this);
24656     },
24657     
24658     loadCanvas : function(src)
24659     {   
24660         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24661             
24662             this.reset();
24663             
24664             this.imageEl = document.createElement('img');
24665             
24666             var _this = this;
24667             
24668             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24669             
24670             this.imageEl.src = src;
24671         }
24672     },
24673     
24674     onLoadCanvas : function()
24675     {   
24676         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24677         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24678         
24679         this.bodyEl.un('click', this.beforeSelectFile, this);
24680         
24681         this.notifyEl.hide();
24682         this.thumbEl.show();
24683         this.footerEl.show();
24684         
24685         this.baseRotateLevel();
24686         
24687         if(this.isDocument){
24688             this.setThumbBoxSize();
24689         }
24690         
24691         this.setThumbBoxPosition();
24692         
24693         this.baseScaleLevel();
24694         
24695         this.draw();
24696         
24697         this.resize();
24698         
24699         this.canvasLoaded = true;
24700         
24701         if(this.loadMask){
24702             this.maskEl.unmask();
24703         }
24704         
24705     },
24706     
24707     setCanvasPosition : function()
24708     {   
24709         if(!this.canvasEl){
24710             return;
24711         }
24712         
24713         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24714         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24715         
24716         this.previewEl.setLeft(pw);
24717         this.previewEl.setTop(ph);
24718         
24719     },
24720     
24721     onMouseDown : function(e)
24722     {   
24723         e.stopEvent();
24724         
24725         this.dragable = true;
24726         this.pinching = false;
24727         
24728         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24729             this.dragable = false;
24730             return;
24731         }
24732         
24733         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24734         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24735         
24736     },
24737     
24738     onMouseMove : function(e)
24739     {   
24740         e.stopEvent();
24741         
24742         if(!this.canvasLoaded){
24743             return;
24744         }
24745         
24746         if (!this.dragable){
24747             return;
24748         }
24749         
24750         var minX = Math.ceil(this.thumbEl.getLeft(true));
24751         var minY = Math.ceil(this.thumbEl.getTop(true));
24752         
24753         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24754         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24755         
24756         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24757         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24758         
24759         x = x - this.mouseX;
24760         y = y - this.mouseY;
24761         
24762         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24763         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24764         
24765         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24766         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24767         
24768         this.previewEl.setLeft(bgX);
24769         this.previewEl.setTop(bgY);
24770         
24771         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24772         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24773     },
24774     
24775     onMouseUp : function(e)
24776     {   
24777         e.stopEvent();
24778         
24779         this.dragable = false;
24780     },
24781     
24782     onMouseWheel : function(e)
24783     {   
24784         e.stopEvent();
24785         
24786         this.startScale = this.scale;
24787         
24788         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24789         
24790         if(!this.zoomable()){
24791             this.scale = this.startScale;
24792             return;
24793         }
24794         
24795         this.draw();
24796         
24797         return;
24798     },
24799     
24800     zoomable : function()
24801     {
24802         var minScale = this.thumbEl.getWidth() / this.minWidth;
24803         
24804         if(this.minWidth < this.minHeight){
24805             minScale = this.thumbEl.getHeight() / this.minHeight;
24806         }
24807         
24808         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24809         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24810         
24811         if(
24812                 this.isDocument &&
24813                 (this.rotate == 0 || this.rotate == 180) && 
24814                 (
24815                     width > this.imageEl.OriginWidth || 
24816                     height > this.imageEl.OriginHeight ||
24817                     (width < this.minWidth && height < this.minHeight)
24818                 )
24819         ){
24820             return false;
24821         }
24822         
24823         if(
24824                 this.isDocument &&
24825                 (this.rotate == 90 || this.rotate == 270) && 
24826                 (
24827                     width > this.imageEl.OriginWidth || 
24828                     height > this.imageEl.OriginHeight ||
24829                     (width < this.minHeight && height < this.minWidth)
24830                 )
24831         ){
24832             return false;
24833         }
24834         
24835         if(
24836                 !this.isDocument &&
24837                 (this.rotate == 0 || this.rotate == 180) && 
24838                 (
24839                     width < this.minWidth || 
24840                     width > this.imageEl.OriginWidth || 
24841                     height < this.minHeight || 
24842                     height > this.imageEl.OriginHeight
24843                 )
24844         ){
24845             return false;
24846         }
24847         
24848         if(
24849                 !this.isDocument &&
24850                 (this.rotate == 90 || this.rotate == 270) && 
24851                 (
24852                     width < this.minHeight || 
24853                     width > this.imageEl.OriginWidth || 
24854                     height < this.minWidth || 
24855                     height > this.imageEl.OriginHeight
24856                 )
24857         ){
24858             return false;
24859         }
24860         
24861         return true;
24862         
24863     },
24864     
24865     onRotateLeft : function(e)
24866     {   
24867         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24868             
24869             var minScale = this.thumbEl.getWidth() / this.minWidth;
24870             
24871             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24872             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24873             
24874             this.startScale = this.scale;
24875             
24876             while (this.getScaleLevel() < minScale){
24877             
24878                 this.scale = this.scale + 1;
24879                 
24880                 if(!this.zoomable()){
24881                     break;
24882                 }
24883                 
24884                 if(
24885                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24886                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24887                 ){
24888                     continue;
24889                 }
24890                 
24891                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24892
24893                 this.draw();
24894                 
24895                 return;
24896             }
24897             
24898             this.scale = this.startScale;
24899             
24900             this.onRotateFail();
24901             
24902             return false;
24903         }
24904         
24905         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24906
24907         if(this.isDocument){
24908             this.setThumbBoxSize();
24909             this.setThumbBoxPosition();
24910             this.setCanvasPosition();
24911         }
24912         
24913         this.draw();
24914         
24915         this.fireEvent('rotate', this, 'left');
24916         
24917     },
24918     
24919     onRotateRight : function(e)
24920     {
24921         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24922             
24923             var minScale = this.thumbEl.getWidth() / this.minWidth;
24924         
24925             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24926             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24927             
24928             this.startScale = this.scale;
24929             
24930             while (this.getScaleLevel() < minScale){
24931             
24932                 this.scale = this.scale + 1;
24933                 
24934                 if(!this.zoomable()){
24935                     break;
24936                 }
24937                 
24938                 if(
24939                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24940                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24941                 ){
24942                     continue;
24943                 }
24944                 
24945                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24946
24947                 this.draw();
24948                 
24949                 return;
24950             }
24951             
24952             this.scale = this.startScale;
24953             
24954             this.onRotateFail();
24955             
24956             return false;
24957         }
24958         
24959         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24960
24961         if(this.isDocument){
24962             this.setThumbBoxSize();
24963             this.setThumbBoxPosition();
24964             this.setCanvasPosition();
24965         }
24966         
24967         this.draw();
24968         
24969         this.fireEvent('rotate', this, 'right');
24970     },
24971     
24972     onRotateFail : function()
24973     {
24974         this.errorEl.show(true);
24975         
24976         var _this = this;
24977         
24978         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24979     },
24980     
24981     draw : function()
24982     {
24983         this.previewEl.dom.innerHTML = '';
24984         
24985         var canvasEl = document.createElement("canvas");
24986         
24987         var contextEl = canvasEl.getContext("2d");
24988         
24989         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24990         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24991         var center = this.imageEl.OriginWidth / 2;
24992         
24993         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24994             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24995             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24996             center = this.imageEl.OriginHeight / 2;
24997         }
24998         
24999         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25000         
25001         contextEl.translate(center, center);
25002         contextEl.rotate(this.rotate * Math.PI / 180);
25003
25004         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25005         
25006         this.canvasEl = document.createElement("canvas");
25007         
25008         this.contextEl = this.canvasEl.getContext("2d");
25009         
25010         switch (this.rotate) {
25011             case 0 :
25012                 
25013                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25014                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25015                 
25016                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25017                 
25018                 break;
25019             case 90 : 
25020                 
25021                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25022                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25023                 
25024                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25025                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25026                     break;
25027                 }
25028                 
25029                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25030                 
25031                 break;
25032             case 180 :
25033                 
25034                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25035                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25036                 
25037                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25038                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25039                     break;
25040                 }
25041                 
25042                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25043                 
25044                 break;
25045             case 270 :
25046                 
25047                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25048                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25049         
25050                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25051                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25052                     break;
25053                 }
25054                 
25055                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25056                 
25057                 break;
25058             default : 
25059                 break;
25060         }
25061         
25062         this.previewEl.appendChild(this.canvasEl);
25063         
25064         this.setCanvasPosition();
25065     },
25066     
25067     crop : function()
25068     {
25069         if(!this.canvasLoaded){
25070             return;
25071         }
25072         
25073         var imageCanvas = document.createElement("canvas");
25074         
25075         var imageContext = imageCanvas.getContext("2d");
25076         
25077         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25078         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25079         
25080         var center = imageCanvas.width / 2;
25081         
25082         imageContext.translate(center, center);
25083         
25084         imageContext.rotate(this.rotate * Math.PI / 180);
25085         
25086         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25087         
25088         var canvas = document.createElement("canvas");
25089         
25090         var context = canvas.getContext("2d");
25091                 
25092         canvas.width = this.minWidth;
25093         canvas.height = this.minHeight;
25094
25095         switch (this.rotate) {
25096             case 0 :
25097                 
25098                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25099                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25100                 
25101                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25102                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25103                 
25104                 var targetWidth = this.minWidth - 2 * x;
25105                 var targetHeight = this.minHeight - 2 * y;
25106                 
25107                 var scale = 1;
25108                 
25109                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25110                     scale = targetWidth / width;
25111                 }
25112                 
25113                 if(x > 0 && y == 0){
25114                     scale = targetHeight / height;
25115                 }
25116                 
25117                 if(x > 0 && y > 0){
25118                     scale = targetWidth / width;
25119                     
25120                     if(width < height){
25121                         scale = targetHeight / height;
25122                     }
25123                 }
25124                 
25125                 context.scale(scale, scale);
25126                 
25127                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25128                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25129
25130                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25131                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25132
25133                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25134                 
25135                 break;
25136             case 90 : 
25137                 
25138                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25139                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25140                 
25141                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25142                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25143                 
25144                 var targetWidth = this.minWidth - 2 * x;
25145                 var targetHeight = this.minHeight - 2 * y;
25146                 
25147                 var scale = 1;
25148                 
25149                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25150                     scale = targetWidth / width;
25151                 }
25152                 
25153                 if(x > 0 && y == 0){
25154                     scale = targetHeight / height;
25155                 }
25156                 
25157                 if(x > 0 && y > 0){
25158                     scale = targetWidth / width;
25159                     
25160                     if(width < height){
25161                         scale = targetHeight / height;
25162                     }
25163                 }
25164                 
25165                 context.scale(scale, scale);
25166                 
25167                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25168                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25169
25170                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25171                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25172                 
25173                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25174                 
25175                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25176                 
25177                 break;
25178             case 180 :
25179                 
25180                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25181                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25182                 
25183                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25184                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25185                 
25186                 var targetWidth = this.minWidth - 2 * x;
25187                 var targetHeight = this.minHeight - 2 * y;
25188                 
25189                 var scale = 1;
25190                 
25191                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25192                     scale = targetWidth / width;
25193                 }
25194                 
25195                 if(x > 0 && y == 0){
25196                     scale = targetHeight / height;
25197                 }
25198                 
25199                 if(x > 0 && y > 0){
25200                     scale = targetWidth / width;
25201                     
25202                     if(width < height){
25203                         scale = targetHeight / height;
25204                     }
25205                 }
25206                 
25207                 context.scale(scale, scale);
25208                 
25209                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25210                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25211
25212                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25213                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25214
25215                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25216                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25217                 
25218                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25219                 
25220                 break;
25221             case 270 :
25222                 
25223                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25224                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25225                 
25226                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25227                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25228                 
25229                 var targetWidth = this.minWidth - 2 * x;
25230                 var targetHeight = this.minHeight - 2 * y;
25231                 
25232                 var scale = 1;
25233                 
25234                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25235                     scale = targetWidth / width;
25236                 }
25237                 
25238                 if(x > 0 && y == 0){
25239                     scale = targetHeight / height;
25240                 }
25241                 
25242                 if(x > 0 && y > 0){
25243                     scale = targetWidth / width;
25244                     
25245                     if(width < height){
25246                         scale = targetHeight / height;
25247                     }
25248                 }
25249                 
25250                 context.scale(scale, scale);
25251                 
25252                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25253                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25254
25255                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25256                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25257                 
25258                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25259                 
25260                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25261                 
25262                 break;
25263             default : 
25264                 break;
25265         }
25266         
25267         this.cropData = canvas.toDataURL(this.cropType);
25268         
25269         if(this.fireEvent('crop', this, this.cropData) !== false){
25270             this.process(this.file, this.cropData);
25271         }
25272         
25273         return;
25274         
25275     },
25276     
25277     setThumbBoxSize : function()
25278     {
25279         var width, height;
25280         
25281         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25282             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25283             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25284             
25285             this.minWidth = width;
25286             this.minHeight = height;
25287             
25288             if(this.rotate == 90 || this.rotate == 270){
25289                 this.minWidth = height;
25290                 this.minHeight = width;
25291             }
25292         }
25293         
25294         height = 300;
25295         width = Math.ceil(this.minWidth * height / this.minHeight);
25296         
25297         if(this.minWidth > this.minHeight){
25298             width = 300;
25299             height = Math.ceil(this.minHeight * width / this.minWidth);
25300         }
25301         
25302         this.thumbEl.setStyle({
25303             width : width + 'px',
25304             height : height + 'px'
25305         });
25306
25307         return;
25308             
25309     },
25310     
25311     setThumbBoxPosition : function()
25312     {
25313         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25314         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25315         
25316         this.thumbEl.setLeft(x);
25317         this.thumbEl.setTop(y);
25318         
25319     },
25320     
25321     baseRotateLevel : function()
25322     {
25323         this.baseRotate = 1;
25324         
25325         if(
25326                 typeof(this.exif) != 'undefined' &&
25327                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25328                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25329         ){
25330             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25331         }
25332         
25333         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25334         
25335     },
25336     
25337     baseScaleLevel : function()
25338     {
25339         var width, height;
25340         
25341         if(this.isDocument){
25342             
25343             if(this.baseRotate == 6 || this.baseRotate == 8){
25344             
25345                 height = this.thumbEl.getHeight();
25346                 this.baseScale = height / this.imageEl.OriginWidth;
25347
25348                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25349                     width = this.thumbEl.getWidth();
25350                     this.baseScale = width / this.imageEl.OriginHeight;
25351                 }
25352
25353                 return;
25354             }
25355
25356             height = this.thumbEl.getHeight();
25357             this.baseScale = height / this.imageEl.OriginHeight;
25358
25359             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25360                 width = this.thumbEl.getWidth();
25361                 this.baseScale = width / this.imageEl.OriginWidth;
25362             }
25363
25364             return;
25365         }
25366         
25367         if(this.baseRotate == 6 || this.baseRotate == 8){
25368             
25369             width = this.thumbEl.getHeight();
25370             this.baseScale = width / this.imageEl.OriginHeight;
25371             
25372             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25373                 height = this.thumbEl.getWidth();
25374                 this.baseScale = height / this.imageEl.OriginHeight;
25375             }
25376             
25377             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25378                 height = this.thumbEl.getWidth();
25379                 this.baseScale = height / this.imageEl.OriginHeight;
25380                 
25381                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25382                     width = this.thumbEl.getHeight();
25383                     this.baseScale = width / this.imageEl.OriginWidth;
25384                 }
25385             }
25386             
25387             return;
25388         }
25389         
25390         width = this.thumbEl.getWidth();
25391         this.baseScale = width / this.imageEl.OriginWidth;
25392         
25393         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25394             height = this.thumbEl.getHeight();
25395             this.baseScale = height / this.imageEl.OriginHeight;
25396         }
25397         
25398         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25399             
25400             height = this.thumbEl.getHeight();
25401             this.baseScale = height / this.imageEl.OriginHeight;
25402             
25403             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25404                 width = this.thumbEl.getWidth();
25405                 this.baseScale = width / this.imageEl.OriginWidth;
25406             }
25407             
25408         }
25409         
25410         return;
25411     },
25412     
25413     getScaleLevel : function()
25414     {
25415         return this.baseScale * Math.pow(1.1, this.scale);
25416     },
25417     
25418     onTouchStart : function(e)
25419     {
25420         if(!this.canvasLoaded){
25421             this.beforeSelectFile(e);
25422             return;
25423         }
25424         
25425         var touches = e.browserEvent.touches;
25426         
25427         if(!touches){
25428             return;
25429         }
25430         
25431         if(touches.length == 1){
25432             this.onMouseDown(e);
25433             return;
25434         }
25435         
25436         if(touches.length != 2){
25437             return;
25438         }
25439         
25440         var coords = [];
25441         
25442         for(var i = 0, finger; finger = touches[i]; i++){
25443             coords.push(finger.pageX, finger.pageY);
25444         }
25445         
25446         var x = Math.pow(coords[0] - coords[2], 2);
25447         var y = Math.pow(coords[1] - coords[3], 2);
25448         
25449         this.startDistance = Math.sqrt(x + y);
25450         
25451         this.startScale = this.scale;
25452         
25453         this.pinching = true;
25454         this.dragable = false;
25455         
25456     },
25457     
25458     onTouchMove : function(e)
25459     {
25460         if(!this.pinching && !this.dragable){
25461             return;
25462         }
25463         
25464         var touches = e.browserEvent.touches;
25465         
25466         if(!touches){
25467             return;
25468         }
25469         
25470         if(this.dragable){
25471             this.onMouseMove(e);
25472             return;
25473         }
25474         
25475         var coords = [];
25476         
25477         for(var i = 0, finger; finger = touches[i]; i++){
25478             coords.push(finger.pageX, finger.pageY);
25479         }
25480         
25481         var x = Math.pow(coords[0] - coords[2], 2);
25482         var y = Math.pow(coords[1] - coords[3], 2);
25483         
25484         this.endDistance = Math.sqrt(x + y);
25485         
25486         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25487         
25488         if(!this.zoomable()){
25489             this.scale = this.startScale;
25490             return;
25491         }
25492         
25493         this.draw();
25494         
25495     },
25496     
25497     onTouchEnd : function(e)
25498     {
25499         this.pinching = false;
25500         this.dragable = false;
25501         
25502     },
25503     
25504     process : function(file, crop)
25505     {
25506         if(this.loadMask){
25507             this.maskEl.mask(this.loadingText);
25508         }
25509         
25510         this.xhr = new XMLHttpRequest();
25511         
25512         file.xhr = this.xhr;
25513
25514         this.xhr.open(this.method, this.url, true);
25515         
25516         var headers = {
25517             "Accept": "application/json",
25518             "Cache-Control": "no-cache",
25519             "X-Requested-With": "XMLHttpRequest"
25520         };
25521         
25522         for (var headerName in headers) {
25523             var headerValue = headers[headerName];
25524             if (headerValue) {
25525                 this.xhr.setRequestHeader(headerName, headerValue);
25526             }
25527         }
25528         
25529         var _this = this;
25530         
25531         this.xhr.onload = function()
25532         {
25533             _this.xhrOnLoad(_this.xhr);
25534         }
25535         
25536         this.xhr.onerror = function()
25537         {
25538             _this.xhrOnError(_this.xhr);
25539         }
25540         
25541         var formData = new FormData();
25542
25543         formData.append('returnHTML', 'NO');
25544         
25545         if(crop){
25546             formData.append('crop', crop);
25547         }
25548         
25549         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25550             formData.append(this.paramName, file, file.name);
25551         }
25552         
25553         if(typeof(file.filename) != 'undefined'){
25554             formData.append('filename', file.filename);
25555         }
25556         
25557         if(typeof(file.mimetype) != 'undefined'){
25558             formData.append('mimetype', file.mimetype);
25559         }
25560         
25561         if(this.fireEvent('arrange', this, formData) != false){
25562             this.xhr.send(formData);
25563         };
25564     },
25565     
25566     xhrOnLoad : function(xhr)
25567     {
25568         if(this.loadMask){
25569             this.maskEl.unmask();
25570         }
25571         
25572         if (xhr.readyState !== 4) {
25573             this.fireEvent('exception', this, xhr);
25574             return;
25575         }
25576
25577         var response = Roo.decode(xhr.responseText);
25578         
25579         if(!response.success){
25580             this.fireEvent('exception', this, xhr);
25581             return;
25582         }
25583         
25584         var response = Roo.decode(xhr.responseText);
25585         
25586         this.fireEvent('upload', this, response);
25587         
25588     },
25589     
25590     xhrOnError : function()
25591     {
25592         if(this.loadMask){
25593             this.maskEl.unmask();
25594         }
25595         
25596         Roo.log('xhr on error');
25597         
25598         var response = Roo.decode(xhr.responseText);
25599           
25600         Roo.log(response);
25601         
25602     },
25603     
25604     prepare : function(file)
25605     {   
25606         if(this.loadMask){
25607             this.maskEl.mask(this.loadingText);
25608         }
25609         
25610         this.file = false;
25611         this.exif = {};
25612         
25613         if(typeof(file) === 'string'){
25614             this.loadCanvas(file);
25615             return;
25616         }
25617         
25618         if(!file || !this.urlAPI){
25619             return;
25620         }
25621         
25622         this.file = file;
25623         this.cropType = file.type;
25624         
25625         var _this = this;
25626         
25627         if(this.fireEvent('prepare', this, this.file) != false){
25628             
25629             var reader = new FileReader();
25630             
25631             reader.onload = function (e) {
25632                 if (e.target.error) {
25633                     Roo.log(e.target.error);
25634                     return;
25635                 }
25636                 
25637                 var buffer = e.target.result,
25638                     dataView = new DataView(buffer),
25639                     offset = 2,
25640                     maxOffset = dataView.byteLength - 4,
25641                     markerBytes,
25642                     markerLength;
25643                 
25644                 if (dataView.getUint16(0) === 0xffd8) {
25645                     while (offset < maxOffset) {
25646                         markerBytes = dataView.getUint16(offset);
25647                         
25648                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25649                             markerLength = dataView.getUint16(offset + 2) + 2;
25650                             if (offset + markerLength > dataView.byteLength) {
25651                                 Roo.log('Invalid meta data: Invalid segment size.');
25652                                 break;
25653                             }
25654                             
25655                             if(markerBytes == 0xffe1){
25656                                 _this.parseExifData(
25657                                     dataView,
25658                                     offset,
25659                                     markerLength
25660                                 );
25661                             }
25662                             
25663                             offset += markerLength;
25664                             
25665                             continue;
25666                         }
25667                         
25668                         break;
25669                     }
25670                     
25671                 }
25672                 
25673                 var url = _this.urlAPI.createObjectURL(_this.file);
25674                 
25675                 _this.loadCanvas(url);
25676                 
25677                 return;
25678             }
25679             
25680             reader.readAsArrayBuffer(this.file);
25681             
25682         }
25683         
25684     },
25685     
25686     parseExifData : function(dataView, offset, length)
25687     {
25688         var tiffOffset = offset + 10,
25689             littleEndian,
25690             dirOffset;
25691     
25692         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25693             // No Exif data, might be XMP data instead
25694             return;
25695         }
25696         
25697         // Check for the ASCII code for "Exif" (0x45786966):
25698         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25699             // No Exif data, might be XMP data instead
25700             return;
25701         }
25702         if (tiffOffset + 8 > dataView.byteLength) {
25703             Roo.log('Invalid Exif data: Invalid segment size.');
25704             return;
25705         }
25706         // Check for the two null bytes:
25707         if (dataView.getUint16(offset + 8) !== 0x0000) {
25708             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25709             return;
25710         }
25711         // Check the byte alignment:
25712         switch (dataView.getUint16(tiffOffset)) {
25713         case 0x4949:
25714             littleEndian = true;
25715             break;
25716         case 0x4D4D:
25717             littleEndian = false;
25718             break;
25719         default:
25720             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25721             return;
25722         }
25723         // Check for the TIFF tag marker (0x002A):
25724         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25725             Roo.log('Invalid Exif data: Missing TIFF marker.');
25726             return;
25727         }
25728         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25729         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25730         
25731         this.parseExifTags(
25732             dataView,
25733             tiffOffset,
25734             tiffOffset + dirOffset,
25735             littleEndian
25736         );
25737     },
25738     
25739     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25740     {
25741         var tagsNumber,
25742             dirEndOffset,
25743             i;
25744         if (dirOffset + 6 > dataView.byteLength) {
25745             Roo.log('Invalid Exif data: Invalid directory offset.');
25746             return;
25747         }
25748         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25749         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25750         if (dirEndOffset + 4 > dataView.byteLength) {
25751             Roo.log('Invalid Exif data: Invalid directory size.');
25752             return;
25753         }
25754         for (i = 0; i < tagsNumber; i += 1) {
25755             this.parseExifTag(
25756                 dataView,
25757                 tiffOffset,
25758                 dirOffset + 2 + 12 * i, // tag offset
25759                 littleEndian
25760             );
25761         }
25762         // Return the offset to the next directory:
25763         return dataView.getUint32(dirEndOffset, littleEndian);
25764     },
25765     
25766     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25767     {
25768         var tag = dataView.getUint16(offset, littleEndian);
25769         
25770         this.exif[tag] = this.getExifValue(
25771             dataView,
25772             tiffOffset,
25773             offset,
25774             dataView.getUint16(offset + 2, littleEndian), // tag type
25775             dataView.getUint32(offset + 4, littleEndian), // tag length
25776             littleEndian
25777         );
25778     },
25779     
25780     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25781     {
25782         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25783             tagSize,
25784             dataOffset,
25785             values,
25786             i,
25787             str,
25788             c;
25789     
25790         if (!tagType) {
25791             Roo.log('Invalid Exif data: Invalid tag type.');
25792             return;
25793         }
25794         
25795         tagSize = tagType.size * length;
25796         // Determine if the value is contained in the dataOffset bytes,
25797         // or if the value at the dataOffset is a pointer to the actual data:
25798         dataOffset = tagSize > 4 ?
25799                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25800         if (dataOffset + tagSize > dataView.byteLength) {
25801             Roo.log('Invalid Exif data: Invalid data offset.');
25802             return;
25803         }
25804         if (length === 1) {
25805             return tagType.getValue(dataView, dataOffset, littleEndian);
25806         }
25807         values = [];
25808         for (i = 0; i < length; i += 1) {
25809             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25810         }
25811         
25812         if (tagType.ascii) {
25813             str = '';
25814             // Concatenate the chars:
25815             for (i = 0; i < values.length; i += 1) {
25816                 c = values[i];
25817                 // Ignore the terminating NULL byte(s):
25818                 if (c === '\u0000') {
25819                     break;
25820                 }
25821                 str += c;
25822             }
25823             return str;
25824         }
25825         return values;
25826     }
25827     
25828 });
25829
25830 Roo.apply(Roo.bootstrap.UploadCropbox, {
25831     tags : {
25832         'Orientation': 0x0112
25833     },
25834     
25835     Orientation: {
25836             1: 0, //'top-left',
25837 //            2: 'top-right',
25838             3: 180, //'bottom-right',
25839 //            4: 'bottom-left',
25840 //            5: 'left-top',
25841             6: 90, //'right-top',
25842 //            7: 'right-bottom',
25843             8: 270 //'left-bottom'
25844     },
25845     
25846     exifTagTypes : {
25847         // byte, 8-bit unsigned int:
25848         1: {
25849             getValue: function (dataView, dataOffset) {
25850                 return dataView.getUint8(dataOffset);
25851             },
25852             size: 1
25853         },
25854         // ascii, 8-bit byte:
25855         2: {
25856             getValue: function (dataView, dataOffset) {
25857                 return String.fromCharCode(dataView.getUint8(dataOffset));
25858             },
25859             size: 1,
25860             ascii: true
25861         },
25862         // short, 16 bit int:
25863         3: {
25864             getValue: function (dataView, dataOffset, littleEndian) {
25865                 return dataView.getUint16(dataOffset, littleEndian);
25866             },
25867             size: 2
25868         },
25869         // long, 32 bit int:
25870         4: {
25871             getValue: function (dataView, dataOffset, littleEndian) {
25872                 return dataView.getUint32(dataOffset, littleEndian);
25873             },
25874             size: 4
25875         },
25876         // rational = two long values, first is numerator, second is denominator:
25877         5: {
25878             getValue: function (dataView, dataOffset, littleEndian) {
25879                 return dataView.getUint32(dataOffset, littleEndian) /
25880                     dataView.getUint32(dataOffset + 4, littleEndian);
25881             },
25882             size: 8
25883         },
25884         // slong, 32 bit signed int:
25885         9: {
25886             getValue: function (dataView, dataOffset, littleEndian) {
25887                 return dataView.getInt32(dataOffset, littleEndian);
25888             },
25889             size: 4
25890         },
25891         // srational, two slongs, first is numerator, second is denominator:
25892         10: {
25893             getValue: function (dataView, dataOffset, littleEndian) {
25894                 return dataView.getInt32(dataOffset, littleEndian) /
25895                     dataView.getInt32(dataOffset + 4, littleEndian);
25896             },
25897             size: 8
25898         }
25899     },
25900     
25901     footer : {
25902         STANDARD : [
25903             {
25904                 tag : 'div',
25905                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25906                 action : 'rotate-left',
25907                 cn : [
25908                     {
25909                         tag : 'button',
25910                         cls : 'btn btn-default',
25911                         html : '<i class="fa fa-undo"></i>'
25912                     }
25913                 ]
25914             },
25915             {
25916                 tag : 'div',
25917                 cls : 'btn-group roo-upload-cropbox-picture',
25918                 action : 'picture',
25919                 cn : [
25920                     {
25921                         tag : 'button',
25922                         cls : 'btn btn-default',
25923                         html : '<i class="fa fa-picture-o"></i>'
25924                     }
25925                 ]
25926             },
25927             {
25928                 tag : 'div',
25929                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25930                 action : 'rotate-right',
25931                 cn : [
25932                     {
25933                         tag : 'button',
25934                         cls : 'btn btn-default',
25935                         html : '<i class="fa fa-repeat"></i>'
25936                     }
25937                 ]
25938             }
25939         ],
25940         DOCUMENT : [
25941             {
25942                 tag : 'div',
25943                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25944                 action : 'rotate-left',
25945                 cn : [
25946                     {
25947                         tag : 'button',
25948                         cls : 'btn btn-default',
25949                         html : '<i class="fa fa-undo"></i>'
25950                     }
25951                 ]
25952             },
25953             {
25954                 tag : 'div',
25955                 cls : 'btn-group roo-upload-cropbox-download',
25956                 action : 'download',
25957                 cn : [
25958                     {
25959                         tag : 'button',
25960                         cls : 'btn btn-default',
25961                         html : '<i class="fa fa-download"></i>'
25962                     }
25963                 ]
25964             },
25965             {
25966                 tag : 'div',
25967                 cls : 'btn-group roo-upload-cropbox-crop',
25968                 action : 'crop',
25969                 cn : [
25970                     {
25971                         tag : 'button',
25972                         cls : 'btn btn-default',
25973                         html : '<i class="fa fa-crop"></i>'
25974                     }
25975                 ]
25976             },
25977             {
25978                 tag : 'div',
25979                 cls : 'btn-group roo-upload-cropbox-trash',
25980                 action : 'trash',
25981                 cn : [
25982                     {
25983                         tag : 'button',
25984                         cls : 'btn btn-default',
25985                         html : '<i class="fa fa-trash"></i>'
25986                     }
25987                 ]
25988             },
25989             {
25990                 tag : 'div',
25991                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25992                 action : 'rotate-right',
25993                 cn : [
25994                     {
25995                         tag : 'button',
25996                         cls : 'btn btn-default',
25997                         html : '<i class="fa fa-repeat"></i>'
25998                     }
25999                 ]
26000             }
26001         ],
26002         ROTATOR : [
26003             {
26004                 tag : 'div',
26005                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26006                 action : 'rotate-left',
26007                 cn : [
26008                     {
26009                         tag : 'button',
26010                         cls : 'btn btn-default',
26011                         html : '<i class="fa fa-undo"></i>'
26012                     }
26013                 ]
26014             },
26015             {
26016                 tag : 'div',
26017                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26018                 action : 'rotate-right',
26019                 cn : [
26020                     {
26021                         tag : 'button',
26022                         cls : 'btn btn-default',
26023                         html : '<i class="fa fa-repeat"></i>'
26024                     }
26025                 ]
26026             }
26027         ]
26028     }
26029 });
26030
26031 /*
26032 * Licence: LGPL
26033 */
26034
26035 /**
26036  * @class Roo.bootstrap.DocumentManager
26037  * @extends Roo.bootstrap.Component
26038  * Bootstrap DocumentManager class
26039  * @cfg {String} paramName default 'imageUpload'
26040  * @cfg {String} method default POST
26041  * @cfg {String} url action url
26042  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26043  * @cfg {Boolean} multiple multiple upload default true
26044  * @cfg {Number} thumbSize default 300
26045  * @cfg {String} fieldLabel
26046  * @cfg {Number} labelWidth default 4
26047  * @cfg {String} labelAlign (left|top) default left
26048  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26049  * 
26050  * @constructor
26051  * Create a new DocumentManager
26052  * @param {Object} config The config object
26053  */
26054
26055 Roo.bootstrap.DocumentManager = function(config){
26056     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26057     
26058     this.addEvents({
26059         /**
26060          * @event initial
26061          * Fire when initial the DocumentManager
26062          * @param {Roo.bootstrap.DocumentManager} this
26063          */
26064         "initial" : true,
26065         /**
26066          * @event inspect
26067          * inspect selected file
26068          * @param {Roo.bootstrap.DocumentManager} this
26069          * @param {File} file
26070          */
26071         "inspect" : true,
26072         /**
26073          * @event exception
26074          * Fire when xhr load exception
26075          * @param {Roo.bootstrap.DocumentManager} this
26076          * @param {XMLHttpRequest} xhr
26077          */
26078         "exception" : true,
26079         /**
26080          * @event prepare
26081          * prepare the form data
26082          * @param {Roo.bootstrap.DocumentManager} this
26083          * @param {Object} formData
26084          */
26085         "prepare" : true,
26086         /**
26087          * @event remove
26088          * Fire when remove the file
26089          * @param {Roo.bootstrap.DocumentManager} this
26090          * @param {Object} file
26091          */
26092         "remove" : true,
26093         /**
26094          * @event refresh
26095          * Fire after refresh the file
26096          * @param {Roo.bootstrap.DocumentManager} this
26097          */
26098         "refresh" : true,
26099         /**
26100          * @event click
26101          * Fire after click the image
26102          * @param {Roo.bootstrap.DocumentManager} this
26103          * @param {Object} file
26104          */
26105         "click" : true,
26106         /**
26107          * @event edit
26108          * Fire when upload a image and editable set to true
26109          * @param {Roo.bootstrap.DocumentManager} this
26110          * @param {Object} file
26111          */
26112         "edit" : true,
26113         /**
26114          * @event beforeselectfile
26115          * Fire before select file
26116          * @param {Roo.bootstrap.DocumentManager} this
26117          */
26118         "beforeselectfile" : true,
26119         /**
26120          * @event process
26121          * Fire before process file
26122          * @param {Roo.bootstrap.DocumentManager} this
26123          * @param {Object} file
26124          */
26125         "process" : true
26126         
26127     });
26128 };
26129
26130 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26131     
26132     boxes : 0,
26133     inputName : '',
26134     thumbSize : 300,
26135     multiple : true,
26136     files : [],
26137     method : 'POST',
26138     url : '',
26139     paramName : 'imageUpload',
26140     fieldLabel : '',
26141     labelWidth : 4,
26142     labelAlign : 'left',
26143     editable : true,
26144     delegates : [],
26145     
26146     
26147     xhr : false, 
26148     
26149     getAutoCreate : function()
26150     {   
26151         var managerWidget = {
26152             tag : 'div',
26153             cls : 'roo-document-manager',
26154             cn : [
26155                 {
26156                     tag : 'input',
26157                     cls : 'roo-document-manager-selector',
26158                     type : 'file'
26159                 },
26160                 {
26161                     tag : 'div',
26162                     cls : 'roo-document-manager-uploader',
26163                     cn : [
26164                         {
26165                             tag : 'div',
26166                             cls : 'roo-document-manager-upload-btn',
26167                             html : '<i class="fa fa-plus"></i>'
26168                         }
26169                     ]
26170                     
26171                 }
26172             ]
26173         };
26174         
26175         var content = [
26176             {
26177                 tag : 'div',
26178                 cls : 'column col-md-12',
26179                 cn : managerWidget
26180             }
26181         ];
26182         
26183         if(this.fieldLabel.length){
26184             
26185             content = [
26186                 {
26187                     tag : 'div',
26188                     cls : 'column col-md-12',
26189                     html : this.fieldLabel
26190                 },
26191                 {
26192                     tag : 'div',
26193                     cls : 'column col-md-12',
26194                     cn : managerWidget
26195                 }
26196             ];
26197
26198             if(this.labelAlign == 'left'){
26199                 content = [
26200                     {
26201                         tag : 'div',
26202                         cls : 'column col-md-' + this.labelWidth,
26203                         html : this.fieldLabel
26204                     },
26205                     {
26206                         tag : 'div',
26207                         cls : 'column col-md-' + (12 - this.labelWidth),
26208                         cn : managerWidget
26209                     }
26210                 ];
26211                 
26212             }
26213         }
26214         
26215         var cfg = {
26216             tag : 'div',
26217             cls : 'row clearfix',
26218             cn : content
26219         };
26220         
26221         return cfg;
26222         
26223     },
26224     
26225     initEvents : function()
26226     {
26227         this.managerEl = this.el.select('.roo-document-manager', true).first();
26228         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26229         
26230         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26231         this.selectorEl.hide();
26232         
26233         if(this.multiple){
26234             this.selectorEl.attr('multiple', 'multiple');
26235         }
26236         
26237         this.selectorEl.on('change', this.onFileSelected, this);
26238         
26239         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26240         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26241         
26242         this.uploader.on('click', this.onUploaderClick, this);
26243         
26244         this.renderProgressDialog();
26245         
26246         var _this = this;
26247         
26248         window.addEventListener("resize", function() { _this.refresh(); } );
26249         
26250         this.fireEvent('initial', this);
26251     },
26252     
26253     renderProgressDialog : function()
26254     {
26255         var _this = this;
26256         
26257         this.progressDialog = new Roo.bootstrap.Modal({
26258             cls : 'roo-document-manager-progress-dialog',
26259             allow_close : false,
26260             title : '',
26261             buttons : [
26262                 {
26263                     name  :'cancel',
26264                     weight : 'danger',
26265                     html : 'Cancel'
26266                 }
26267             ], 
26268             listeners : { 
26269                 btnclick : function() {
26270                     _this.uploadCancel();
26271                     this.hide();
26272                 }
26273             }
26274         });
26275          
26276         this.progressDialog.render(Roo.get(document.body));
26277          
26278         this.progress = new Roo.bootstrap.Progress({
26279             cls : 'roo-document-manager-progress',
26280             active : true,
26281             striped : true
26282         });
26283         
26284         this.progress.render(this.progressDialog.getChildContainer());
26285         
26286         this.progressBar = new Roo.bootstrap.ProgressBar({
26287             cls : 'roo-document-manager-progress-bar',
26288             aria_valuenow : 0,
26289             aria_valuemin : 0,
26290             aria_valuemax : 12,
26291             panel : 'success'
26292         });
26293         
26294         this.progressBar.render(this.progress.getChildContainer());
26295     },
26296     
26297     onUploaderClick : function(e)
26298     {
26299         e.preventDefault();
26300      
26301         if(this.fireEvent('beforeselectfile', this) != false){
26302             this.selectorEl.dom.click();
26303         }
26304         
26305     },
26306     
26307     onFileSelected : function(e)
26308     {
26309         e.preventDefault();
26310         
26311         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26312             return;
26313         }
26314         
26315         Roo.each(this.selectorEl.dom.files, function(file){
26316             if(this.fireEvent('inspect', this, file) != false){
26317                 this.files.push(file);
26318             }
26319         }, this);
26320         
26321         this.queue();
26322         
26323     },
26324     
26325     queue : function()
26326     {
26327         this.selectorEl.dom.value = '';
26328         
26329         if(!this.files.length){
26330             return;
26331         }
26332         
26333         if(this.boxes > 0 && this.files.length > this.boxes){
26334             this.files = this.files.slice(0, this.boxes);
26335         }
26336         
26337         this.uploader.show();
26338         
26339         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26340             this.uploader.hide();
26341         }
26342         
26343         var _this = this;
26344         
26345         var files = [];
26346         
26347         var docs = [];
26348         
26349         Roo.each(this.files, function(file){
26350             
26351             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26352                 var f = this.renderPreview(file);
26353                 files.push(f);
26354                 return;
26355             }
26356             
26357             if(file.type.indexOf('image') != -1){
26358                 this.delegates.push(
26359                     (function(){
26360                         _this.process(file);
26361                     }).createDelegate(this)
26362                 );
26363         
26364                 return;
26365             }
26366             
26367             docs.push(
26368                 (function(){
26369                     _this.process(file);
26370                 }).createDelegate(this)
26371             );
26372             
26373         }, this);
26374         
26375         this.files = files;
26376         
26377         this.delegates = this.delegates.concat(docs);
26378         
26379         if(!this.delegates.length){
26380             this.refresh();
26381             return;
26382         }
26383         
26384         this.progressBar.aria_valuemax = this.delegates.length;
26385         
26386         this.arrange();
26387         
26388         return;
26389     },
26390     
26391     arrange : function()
26392     {
26393         if(!this.delegates.length){
26394             this.progressDialog.hide();
26395             this.refresh();
26396             return;
26397         }
26398         
26399         var delegate = this.delegates.shift();
26400         
26401         this.progressDialog.show();
26402         
26403         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26404         
26405         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26406         
26407         delegate();
26408     },
26409     
26410     refresh : function()
26411     {
26412         this.uploader.show();
26413         
26414         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26415             this.uploader.hide();
26416         }
26417         
26418         Roo.isTouch ? this.closable(false) : this.closable(true);
26419         
26420         this.fireEvent('refresh', this);
26421     },
26422     
26423     onRemove : function(e, el, o)
26424     {
26425         e.preventDefault();
26426         
26427         this.fireEvent('remove', this, o);
26428         
26429     },
26430     
26431     remove : function(o)
26432     {
26433         var files = [];
26434         
26435         Roo.each(this.files, function(file){
26436             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26437                 files.push(file);
26438                 return;
26439             }
26440
26441             o.target.remove();
26442
26443         }, this);
26444         
26445         this.files = files;
26446         
26447         this.refresh();
26448     },
26449     
26450     clear : function()
26451     {
26452         Roo.each(this.files, function(file){
26453             if(!file.target){
26454                 return;
26455             }
26456             
26457             file.target.remove();
26458
26459         }, this);
26460         
26461         this.files = [];
26462         
26463         this.refresh();
26464     },
26465     
26466     onClick : function(e, el, o)
26467     {
26468         e.preventDefault();
26469         
26470         this.fireEvent('click', this, o);
26471         
26472     },
26473     
26474     closable : function(closable)
26475     {
26476         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26477             
26478             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26479             
26480             if(closable){
26481                 el.show();
26482                 return;
26483             }
26484             
26485             el.hide();
26486             
26487         }, this);
26488     },
26489     
26490     xhrOnLoad : function(xhr)
26491     {
26492         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26493             el.remove();
26494         }, this);
26495         
26496         if (xhr.readyState !== 4) {
26497             this.arrange();
26498             this.fireEvent('exception', this, xhr);
26499             return;
26500         }
26501
26502         var response = Roo.decode(xhr.responseText);
26503         
26504         if(!response.success){
26505             this.arrange();
26506             this.fireEvent('exception', this, xhr);
26507             return;
26508         }
26509         
26510         var file = this.renderPreview(response.data);
26511         
26512         this.files.push(file);
26513         
26514         this.arrange();
26515         
26516     },
26517     
26518     xhrOnError : function()
26519     {
26520         Roo.log('xhr on error');
26521         
26522         var response = Roo.decode(xhr.responseText);
26523           
26524         Roo.log(response);
26525         
26526         this.arrange();
26527     },
26528     
26529     process : function(file)
26530     {
26531         if(this.fireEvent('process', this, file) !== false){
26532             if(this.editable && file.type.indexOf('image') != -1){
26533                 this.fireEvent('edit', this, file);
26534                 return;
26535             }
26536
26537             this.uploadStart(file, false);
26538
26539             return;
26540         }
26541         
26542     },
26543     
26544     uploadStart : function(file, crop)
26545     {
26546         this.xhr = new XMLHttpRequest();
26547         
26548         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26549             this.arrange();
26550             return;
26551         }
26552         
26553         file.xhr = this.xhr;
26554             
26555         this.managerEl.createChild({
26556             tag : 'div',
26557             cls : 'roo-document-manager-loading',
26558             cn : [
26559                 {
26560                     tag : 'div',
26561                     tooltip : file.name,
26562                     cls : 'roo-document-manager-thumb',
26563                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26564                 }
26565             ]
26566
26567         });
26568
26569         this.xhr.open(this.method, this.url, true);
26570         
26571         var headers = {
26572             "Accept": "application/json",
26573             "Cache-Control": "no-cache",
26574             "X-Requested-With": "XMLHttpRequest"
26575         };
26576         
26577         for (var headerName in headers) {
26578             var headerValue = headers[headerName];
26579             if (headerValue) {
26580                 this.xhr.setRequestHeader(headerName, headerValue);
26581             }
26582         }
26583         
26584         var _this = this;
26585         
26586         this.xhr.onload = function()
26587         {
26588             _this.xhrOnLoad(_this.xhr);
26589         }
26590         
26591         this.xhr.onerror = function()
26592         {
26593             _this.xhrOnError(_this.xhr);
26594         }
26595         
26596         var formData = new FormData();
26597
26598         formData.append('returnHTML', 'NO');
26599         
26600         if(crop){
26601             formData.append('crop', crop);
26602         }
26603         
26604         formData.append(this.paramName, file, file.name);
26605         
26606         if(this.fireEvent('prepare', this, formData) != false){
26607             this.xhr.send(formData);
26608         };
26609     },
26610     
26611     uploadCancel : function()
26612     {
26613         if (this.xhr) {
26614             this.xhr.abort();
26615         }
26616         
26617         
26618         this.delegates = [];
26619         
26620         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26621             el.remove();
26622         }, this);
26623         
26624         this.arrange();
26625     },
26626     
26627     renderPreview : function(file)
26628     {
26629         if(typeof(file.target) != 'undefined' && file.target){
26630             return file;
26631         }
26632         
26633         var previewEl = this.managerEl.createChild({
26634             tag : 'div',
26635             cls : 'roo-document-manager-preview',
26636             cn : [
26637                 {
26638                     tag : 'div',
26639                     tooltip : file.filename,
26640                     cls : 'roo-document-manager-thumb',
26641                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26642                 },
26643                 {
26644                     tag : 'button',
26645                     cls : 'close',
26646                     html : '<i class="fa fa-times-circle"></i>'
26647                 }
26648             ]
26649         });
26650
26651         var close = previewEl.select('button.close', true).first();
26652
26653         close.on('click', this.onRemove, this, file);
26654
26655         file.target = previewEl;
26656
26657         var image = previewEl.select('img', true).first();
26658         
26659         var _this = this;
26660         
26661         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26662         
26663         image.on('click', this.onClick, this, file);
26664         
26665         return file;
26666         
26667     },
26668     
26669     onPreviewLoad : function(file, image)
26670     {
26671         if(typeof(file.target) == 'undefined' || !file.target){
26672             return;
26673         }
26674         
26675         var width = image.dom.naturalWidth || image.dom.width;
26676         var height = image.dom.naturalHeight || image.dom.height;
26677         
26678         if(width > height){
26679             file.target.addClass('wide');
26680             return;
26681         }
26682         
26683         file.target.addClass('tall');
26684         return;
26685         
26686     },
26687     
26688     uploadFromSource : function(file, crop)
26689     {
26690         this.xhr = new XMLHttpRequest();
26691         
26692         this.managerEl.createChild({
26693             tag : 'div',
26694             cls : 'roo-document-manager-loading',
26695             cn : [
26696                 {
26697                     tag : 'div',
26698                     tooltip : file.name,
26699                     cls : 'roo-document-manager-thumb',
26700                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26701                 }
26702             ]
26703
26704         });
26705
26706         this.xhr.open(this.method, this.url, true);
26707         
26708         var headers = {
26709             "Accept": "application/json",
26710             "Cache-Control": "no-cache",
26711             "X-Requested-With": "XMLHttpRequest"
26712         };
26713         
26714         for (var headerName in headers) {
26715             var headerValue = headers[headerName];
26716             if (headerValue) {
26717                 this.xhr.setRequestHeader(headerName, headerValue);
26718             }
26719         }
26720         
26721         var _this = this;
26722         
26723         this.xhr.onload = function()
26724         {
26725             _this.xhrOnLoad(_this.xhr);
26726         }
26727         
26728         this.xhr.onerror = function()
26729         {
26730             _this.xhrOnError(_this.xhr);
26731         }
26732         
26733         var formData = new FormData();
26734
26735         formData.append('returnHTML', 'NO');
26736         
26737         formData.append('crop', crop);
26738         
26739         if(typeof(file.filename) != 'undefined'){
26740             formData.append('filename', file.filename);
26741         }
26742         
26743         if(typeof(file.mimetype) != 'undefined'){
26744             formData.append('mimetype', file.mimetype);
26745         }
26746         
26747         if(this.fireEvent('prepare', this, formData) != false){
26748             this.xhr.send(formData);
26749         };
26750     }
26751 });
26752
26753 /*
26754 * Licence: LGPL
26755 */
26756
26757 /**
26758  * @class Roo.bootstrap.DocumentViewer
26759  * @extends Roo.bootstrap.Component
26760  * Bootstrap DocumentViewer class
26761  * 
26762  * @constructor
26763  * Create a new DocumentViewer
26764  * @param {Object} config The config object
26765  */
26766
26767 Roo.bootstrap.DocumentViewer = function(config){
26768     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26769     
26770     this.addEvents({
26771         /**
26772          * @event initial
26773          * Fire after initEvent
26774          * @param {Roo.bootstrap.DocumentViewer} this
26775          */
26776         "initial" : true,
26777         /**
26778          * @event click
26779          * Fire after click
26780          * @param {Roo.bootstrap.DocumentViewer} this
26781          */
26782         "click" : true,
26783         /**
26784          * @event trash
26785          * Fire after trash button
26786          * @param {Roo.bootstrap.DocumentViewer} this
26787          */
26788         "trash" : true
26789         
26790     });
26791 };
26792
26793 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
26794     
26795     getAutoCreate : function()
26796     {
26797         var cfg = {
26798             tag : 'div',
26799             cls : 'roo-document-viewer',
26800             cn : [
26801                 {
26802                     tag : 'div',
26803                     cls : 'roo-document-viewer-body',
26804                     cn : [
26805                         {
26806                             tag : 'div',
26807                             cls : 'roo-document-viewer-thumb',
26808                             cn : [
26809                                 {
26810                                     tag : 'img',
26811                                     cls : 'roo-document-viewer-image'
26812                                 }
26813                             ]
26814                         }
26815                     ]
26816                 },
26817                 {
26818                     tag : 'div',
26819                     cls : 'roo-document-viewer-footer',
26820                     cn : {
26821                         tag : 'div',
26822                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26823                         cn : [
26824                             {
26825                                 tag : 'div',
26826                                 cls : 'btn-group',
26827                                 cn : [
26828                                     {
26829                                         tag : 'button',
26830                                         cls : 'btn btn-default roo-document-viewer-trash',
26831                                         html : '<i class="fa fa-trash"></i>'
26832                                     }
26833                                 ]
26834                             }
26835                         ]
26836                     }
26837                 }
26838             ]
26839         };
26840         
26841         return cfg;
26842     },
26843     
26844     initEvents : function()
26845     {
26846         
26847         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26848         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26849         
26850         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26851         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26852         
26853         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26854         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26855         
26856         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26857         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26858         
26859         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26860         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26861         
26862         this.bodyEl.on('click', this.onClick, this);
26863         
26864         this.trashBtn.on('click', this.onTrash, this);
26865         
26866     },
26867     
26868     initial : function()
26869     {
26870 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26871         
26872         
26873         this.fireEvent('initial', this);
26874         
26875     },
26876     
26877     onClick : function(e)
26878     {
26879         e.preventDefault();
26880         
26881         this.fireEvent('click', this);
26882     },
26883     
26884     onTrash : function(e)
26885     {
26886         e.preventDefault();
26887         
26888         this.fireEvent('trash', this);
26889     }
26890     
26891 });
26892 /*
26893  * - LGPL
26894  *
26895  * nav progress bar
26896  * 
26897  */
26898
26899 /**
26900  * @class Roo.bootstrap.NavProgressBar
26901  * @extends Roo.bootstrap.Component
26902  * Bootstrap NavProgressBar class
26903  * 
26904  * @constructor
26905  * Create a new nav progress bar
26906  * @param {Object} config The config object
26907  */
26908
26909 Roo.bootstrap.NavProgressBar = function(config){
26910     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26911
26912     this.bullets = this.bullets || [];
26913    
26914 //    Roo.bootstrap.NavProgressBar.register(this);
26915      this.addEvents({
26916         /**
26917              * @event changed
26918              * Fires when the active item changes
26919              * @param {Roo.bootstrap.NavProgressBar} this
26920              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26921              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
26922          */
26923         'changed': true
26924      });
26925     
26926 };
26927
26928 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
26929     
26930     bullets : [],
26931     barItems : [],
26932     
26933     getAutoCreate : function()
26934     {
26935         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26936         
26937         cfg = {
26938             tag : 'div',
26939             cls : 'roo-navigation-bar-group',
26940             cn : [
26941                 {
26942                     tag : 'div',
26943                     cls : 'roo-navigation-top-bar'
26944                 },
26945                 {
26946                     tag : 'div',
26947                     cls : 'roo-navigation-bullets-bar',
26948                     cn : [
26949                         {
26950                             tag : 'ul',
26951                             cls : 'roo-navigation-bar'
26952                         }
26953                     ]
26954                 },
26955                 
26956                 {
26957                     tag : 'div',
26958                     cls : 'roo-navigation-bottom-bar'
26959                 }
26960             ]
26961             
26962         };
26963         
26964         return cfg;
26965         
26966     },
26967     
26968     initEvents: function() 
26969     {
26970         
26971     },
26972     
26973     onRender : function(ct, position) 
26974     {
26975         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26976         
26977         if(this.bullets.length){
26978             Roo.each(this.bullets, function(b){
26979                this.addItem(b);
26980             }, this);
26981         }
26982         
26983         this.format();
26984         
26985     },
26986     
26987     addItem : function(cfg)
26988     {
26989         var item = new Roo.bootstrap.NavProgressItem(cfg);
26990         
26991         item.parentId = this.id;
26992         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
26993         
26994         if(cfg.html){
26995             var top = new Roo.bootstrap.Element({
26996                 tag : 'div',
26997                 cls : 'roo-navigation-bar-text'
26998             });
26999             
27000             var bottom = new Roo.bootstrap.Element({
27001                 tag : 'div',
27002                 cls : 'roo-navigation-bar-text'
27003             });
27004             
27005             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27006             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27007             
27008             var topText = new Roo.bootstrap.Element({
27009                 tag : 'span',
27010                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27011             });
27012             
27013             var bottomText = new Roo.bootstrap.Element({
27014                 tag : 'span',
27015                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27016             });
27017             
27018             topText.onRender(top.el, null);
27019             bottomText.onRender(bottom.el, null);
27020             
27021             item.topEl = top;
27022             item.bottomEl = bottom;
27023         }
27024         
27025         this.barItems.push(item);
27026         
27027         return item;
27028     },
27029     
27030     getActive : function()
27031     {
27032         var active = false;
27033         
27034         Roo.each(this.barItems, function(v){
27035             
27036             if (!v.isActive()) {
27037                 return;
27038             }
27039             
27040             active = v;
27041             return false;
27042             
27043         });
27044         
27045         return active;
27046     },
27047     
27048     setActiveItem : function(item)
27049     {
27050         var prev = false;
27051         
27052         Roo.each(this.barItems, function(v){
27053             if (v.rid == item.rid) {
27054                 return ;
27055             }
27056             
27057             if (v.isActive()) {
27058                 v.setActive(false);
27059                 prev = v;
27060             }
27061         });
27062
27063         item.setActive(true);
27064         
27065         this.fireEvent('changed', this, item, prev);
27066     },
27067     
27068     getBarItem: function(rid)
27069     {
27070         var ret = false;
27071         
27072         Roo.each(this.barItems, function(e) {
27073             if (e.rid != rid) {
27074                 return;
27075             }
27076             
27077             ret =  e;
27078             return false;
27079         });
27080         
27081         return ret;
27082     },
27083     
27084     indexOfItem : function(item)
27085     {
27086         var index = false;
27087         
27088         Roo.each(this.barItems, function(v, i){
27089             
27090             if (v.rid != item.rid) {
27091                 return;
27092             }
27093             
27094             index = i;
27095             return false
27096         });
27097         
27098         return index;
27099     },
27100     
27101     setActiveNext : function()
27102     {
27103         var i = this.indexOfItem(this.getActive());
27104         
27105         if (i > this.barItems.length) {
27106             return;
27107         }
27108         
27109         this.setActiveItem(this.barItems[i+1]);
27110     },
27111     
27112     setActivePrev : function()
27113     {
27114         var i = this.indexOfItem(this.getActive());
27115         
27116         if (i  < 1) {
27117             return;
27118         }
27119         
27120         this.setActiveItem(this.barItems[i-1]);
27121     },
27122     
27123     format : function()
27124     {
27125         if(!this.barItems.length){
27126             return;
27127         }
27128      
27129         var width = 100 / this.barItems.length;
27130         
27131         Roo.each(this.barItems, function(i){
27132             i.el.setStyle('width', width + '%');
27133             i.topEl.el.setStyle('width', width + '%');
27134             i.bottomEl.el.setStyle('width', width + '%');
27135         }, this);
27136         
27137     }
27138     
27139 });
27140 /*
27141  * - LGPL
27142  *
27143  * Nav Progress Item
27144  * 
27145  */
27146
27147 /**
27148  * @class Roo.bootstrap.NavProgressItem
27149  * @extends Roo.bootstrap.Component
27150  * Bootstrap NavProgressItem class
27151  * @cfg {String} rid the reference id
27152  * @cfg {Boolean} active (true|false) Is item active default false
27153  * @cfg {Boolean} disabled (true|false) Is item active default false
27154  * @cfg {String} html
27155  * @cfg {String} position (top|bottom) text position default bottom
27156  * @cfg {String} icon show icon instead of number
27157  * 
27158  * @constructor
27159  * Create a new NavProgressItem
27160  * @param {Object} config The config object
27161  */
27162 Roo.bootstrap.NavProgressItem = function(config){
27163     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27164     this.addEvents({
27165         // raw events
27166         /**
27167          * @event click
27168          * The raw click event for the entire grid.
27169          * @param {Roo.bootstrap.NavProgressItem} this
27170          * @param {Roo.EventObject} e
27171          */
27172         "click" : true
27173     });
27174    
27175 };
27176
27177 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27178     
27179     rid : '',
27180     active : false,
27181     disabled : false,
27182     html : '',
27183     position : 'bottom',
27184     icon : false,
27185     
27186     getAutoCreate : function()
27187     {
27188         var iconCls = 'roo-navigation-bar-item-icon';
27189         
27190         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27191         
27192         var cfg = {
27193             tag: 'li',
27194             cls: 'roo-navigation-bar-item',
27195             cn : [
27196                 {
27197                     tag : 'i',
27198                     cls : iconCls
27199                 }
27200             ]
27201         };
27202         
27203         if(this.active){
27204             cfg.cls += ' active';
27205         }
27206         if(this.disabled){
27207             cfg.cls += ' disabled';
27208         }
27209         
27210         return cfg;
27211     },
27212     
27213     disable : function()
27214     {
27215         this.setDisabled(true);
27216     },
27217     
27218     enable : function()
27219     {
27220         this.setDisabled(false);
27221     },
27222     
27223     initEvents: function() 
27224     {
27225         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27226         
27227         this.iconEl.on('click', this.onClick, this);
27228     },
27229     
27230     onClick : function(e)
27231     {
27232         e.preventDefault();
27233         
27234         if(this.disabled){
27235             return;
27236         }
27237         
27238         if(this.fireEvent('click', this, e) === false){
27239             return;
27240         };
27241         
27242         this.parent().setActiveItem(this);
27243     },
27244     
27245     isActive: function () 
27246     {
27247         return this.active;
27248     },
27249     
27250     setActive : function(state)
27251     {
27252         if(this.active == state){
27253             return;
27254         }
27255         
27256         this.active = state;
27257         
27258         if (state) {
27259             this.el.addClass('active');
27260             return;
27261         }
27262         
27263         this.el.removeClass('active');
27264         
27265         return;
27266     },
27267     
27268     setDisabled : function(state)
27269     {
27270         if(this.disabled == state){
27271             return;
27272         }
27273         
27274         this.disabled = state;
27275         
27276         if (state) {
27277             this.el.addClass('disabled');
27278             return;
27279         }
27280         
27281         this.el.removeClass('disabled');
27282     },
27283     
27284     tooltipEl : function()
27285     {
27286         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27287     }
27288 });
27289  
27290
27291  /*
27292  * - LGPL
27293  *
27294  * FieldLabel
27295  * 
27296  */
27297
27298 /**
27299  * @class Roo.bootstrap.FieldLabel
27300  * @extends Roo.bootstrap.Component
27301  * Bootstrap FieldLabel class
27302  * @cfg {String} html contents of the element
27303  * @cfg {String} tag tag of the element default label
27304  * @cfg {String} cls class of the element
27305  * @cfg {String} target label target 
27306  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27307  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27308  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27309  * @cfg {String} iconTooltip default "This field is required"
27310  * 
27311  * @constructor
27312  * Create a new FieldLabel
27313  * @param {Object} config The config object
27314  */
27315
27316 Roo.bootstrap.FieldLabel = function(config){
27317     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27318     
27319     this.addEvents({
27320             /**
27321              * @event invalid
27322              * Fires after the field has been marked as invalid.
27323              * @param {Roo.form.FieldLabel} this
27324              * @param {String} msg The validation message
27325              */
27326             invalid : true,
27327             /**
27328              * @event valid
27329              * Fires after the field has been validated with no errors.
27330              * @param {Roo.form.FieldLabel} this
27331              */
27332             valid : true
27333         });
27334 };
27335
27336 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27337     
27338     tag: 'label',
27339     cls: '',
27340     html: '',
27341     target: '',
27342     allowBlank : true,
27343     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27344     validClass : 'text-success fa fa-lg fa-check',
27345     iconTooltip : 'This field is required',
27346     
27347     getAutoCreate : function(){
27348         
27349         var cfg = {
27350             tag : this.tag,
27351             cls : 'roo-bootstrap-field-label ' + this.cls,
27352             for : this.target,
27353             cn : [
27354                 {
27355                     tag : 'i',
27356                     cls : '',
27357                     tooltip : this.iconTooltip
27358                 },
27359                 {
27360                     tag : 'span',
27361                     html : this.html
27362                 }
27363             ] 
27364         };
27365         
27366         return cfg;
27367     },
27368     
27369     initEvents: function() 
27370     {
27371         Roo.bootstrap.Element.superclass.initEvents.call(this);
27372         
27373         this.iconEl = this.el.select('i', true).first();
27374         
27375         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27376         
27377         Roo.bootstrap.FieldLabel.register(this);
27378     },
27379     
27380     /**
27381      * Mark this field as valid
27382      */
27383     markValid : function()
27384     {
27385         this.iconEl.show();
27386         
27387         this.iconEl.removeClass(this.invalidClass);
27388         
27389         this.iconEl.addClass(this.validClass);
27390         
27391         this.fireEvent('valid', this);
27392     },
27393     
27394     /**
27395      * Mark this field as invalid
27396      * @param {String} msg The validation message
27397      */
27398     markInvalid : function(msg)
27399     {
27400         this.iconEl.show();
27401         
27402         this.iconEl.removeClass(this.validClass);
27403         
27404         this.iconEl.addClass(this.invalidClass);
27405         
27406         this.fireEvent('invalid', this, msg);
27407     }
27408     
27409    
27410 });
27411
27412 Roo.apply(Roo.bootstrap.FieldLabel, {
27413     
27414     groups: {},
27415     
27416      /**
27417     * register a FieldLabel Group
27418     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27419     */
27420     register : function(label)
27421     {
27422         if(this.groups.hasOwnProperty(label.target)){
27423             return;
27424         }
27425      
27426         this.groups[label.target] = label;
27427         
27428     },
27429     /**
27430     * fetch a FieldLabel Group based on the target
27431     * @param {string} target
27432     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27433     */
27434     get: function(target) {
27435         if (typeof(this.groups[target]) == 'undefined') {
27436             return false;
27437         }
27438         
27439         return this.groups[target] ;
27440     }
27441 });
27442
27443  
27444
27445