roojs-bootstrap.js
[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         if (this.active) {
4085             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4086         }
4087         if (this.disabled) {
4088             cfg.cls += ' disabled';
4089         }
4090         
4091         if (this.href || this.html || this.glyphicon || this.icon) {
4092             cfg.cn = [
4093                 {
4094                     tag: this.tagtype,
4095                     href : this.href || "#",
4096                     html: this.html || ''
4097                 }
4098             ];
4099             
4100             if (this.icon) {
4101                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4102             }
4103
4104             if(this.glyphicon) {
4105                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4106             }
4107             
4108             if (this.menu) {
4109                 
4110                 cfg.cn[0].html += " <span class='caret'></span>";
4111              
4112             }
4113             
4114             if (this.badge !== '') {
4115                  
4116                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4117             }
4118         }
4119         
4120         
4121         
4122         return cfg;
4123     },
4124     initEvents: function() 
4125     {
4126         if (typeof (this.menu) != 'undefined') {
4127             this.menu.parentType = this.xtype;
4128             this.menu.triggerEl = this.el;
4129             this.menu = this.addxtype(Roo.apply({}, this.menu));
4130         }
4131         
4132         this.el.select('a',true).on('click', this.onClick, this);
4133         
4134         if(this.tagtype == 'span'){
4135             this.el.select('span',true).on('click', this.onClick, this);
4136         }
4137        
4138         // at this point parent should be available..
4139         this.parent().register(this);
4140     },
4141     
4142     onClick : function(e)
4143     {
4144         if(
4145                 this.preventDefault || 
4146                 this.href == '#' 
4147         ){
4148             
4149             e.preventDefault();
4150         }
4151         
4152         if (this.disabled) {
4153             return;
4154         }
4155         
4156         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4157         if (tg && tg.transition) {
4158             Roo.log("waiting for the transitionend");
4159             return;
4160         }
4161         
4162         
4163         
4164         //Roo.log("fire event clicked");
4165         if(this.fireEvent('click', this, e) === false){
4166             return;
4167         };
4168         
4169         if(this.tagtype == 'span'){
4170             return;
4171         }
4172         
4173         //Roo.log(this.href);
4174         var ael = this.el.select('a',true).first();
4175         //Roo.log(ael);
4176         
4177         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4178             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4179             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4180                 return; // ignore... - it's a 'hash' to another page.
4181             }
4182             
4183             e.preventDefault();
4184             this.scrollToElement(e);
4185         }
4186         
4187         
4188         var p =  this.parent();
4189    
4190         if (['tabs','pills'].indexOf(p.type)!==-1) {
4191             if (typeof(p.setActiveItem) !== 'undefined') {
4192                 p.setActiveItem(this);
4193             }
4194         }
4195         
4196         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4197         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4198             // remove the collapsed menu expand...
4199             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4200         }
4201     },
4202     
4203     isActive: function () {
4204         return this.active
4205     },
4206     setActive : function(state, fire, is_was_active)
4207     {
4208         if (this.active && !state && this.navId) {
4209             this.was_active = true;
4210             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4211             if (nv) {
4212                 nv.clearWasActive(this);
4213             }
4214             
4215         }
4216         this.active = state;
4217         
4218         if (!state ) {
4219             this.el.removeClass('active');
4220         } else if (!this.el.hasClass('active')) {
4221             this.el.addClass('active');
4222         }
4223         if (fire) {
4224             this.fireEvent('changed', this, state);
4225         }
4226         
4227         // show a panel if it's registered and related..
4228         
4229         if (!this.navId || !this.tabId || !state || is_was_active) {
4230             return;
4231         }
4232         
4233         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4234         if (!tg) {
4235             return;
4236         }
4237         var pan = tg.getPanelByName(this.tabId);
4238         if (!pan) {
4239             return;
4240         }
4241         // if we can not flip to new panel - go back to old nav highlight..
4242         if (false == tg.showPanel(pan)) {
4243             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4244             if (nv) {
4245                 var onav = nv.getWasActive();
4246                 if (onav) {
4247                     onav.setActive(true, false, true);
4248                 }
4249             }
4250             
4251         }
4252         
4253         
4254         
4255     },
4256      // this should not be here...
4257     setDisabled : function(state)
4258     {
4259         this.disabled = state;
4260         if (!state ) {
4261             this.el.removeClass('disabled');
4262         } else if (!this.el.hasClass('disabled')) {
4263             this.el.addClass('disabled');
4264         }
4265         
4266     },
4267     
4268     /**
4269      * Fetch the element to display the tooltip on.
4270      * @return {Roo.Element} defaults to this.el
4271      */
4272     tooltipEl : function()
4273     {
4274         return this.el.select('' + this.tagtype + '', true).first();
4275     },
4276     
4277     scrollToElement : function(e)
4278     {
4279         var c = document.body;
4280         
4281         /*
4282          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4283          */
4284         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4285             c = document.documentElement;
4286         }
4287         
4288         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4289         
4290         if(!target){
4291             return;
4292         }
4293
4294         var o = target.calcOffsetsTo(c);
4295         
4296         var options = {
4297             target : target,
4298             value : o[1]
4299         }
4300         
4301         this.fireEvent('scrollto', this, options, e);
4302         
4303         Roo.get(c).scrollTo('top', options.value, true);
4304         
4305         return;
4306     }
4307 });
4308  
4309
4310  /*
4311  * - LGPL
4312  *
4313  * sidebar item
4314  *
4315  *  li
4316  *    <span> icon </span>
4317  *    <span> text </span>
4318  *    <span>badge </span>
4319  */
4320
4321 /**
4322  * @class Roo.bootstrap.NavSidebarItem
4323  * @extends Roo.bootstrap.NavItem
4324  * Bootstrap Navbar.NavSidebarItem class
4325  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4326  * @constructor
4327  * Create a new Navbar Button
4328  * @param {Object} config The config object
4329  */
4330 Roo.bootstrap.NavSidebarItem = function(config){
4331     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4332     this.addEvents({
4333         // raw events
4334         /**
4335          * @event click
4336          * The raw click event for the entire grid.
4337          * @param {Roo.EventObject} e
4338          */
4339         "click" : true,
4340          /**
4341             * @event changed
4342             * Fires when the active item active state changes
4343             * @param {Roo.bootstrap.NavSidebarItem} this
4344             * @param {boolean} state the new state
4345              
4346          */
4347         'changed': true
4348     });
4349    
4350 };
4351
4352 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4353     
4354     badgeWeight : 'default',
4355     
4356     getAutoCreate : function(){
4357         
4358         
4359         var a = {
4360                 tag: 'a',
4361                 href : this.href || '#',
4362                 cls: '',
4363                 html : '',
4364                 cn : []
4365         };
4366         var cfg = {
4367             tag: 'li',
4368             cls: '',
4369             cn: [ a ]
4370         };
4371         var span = {
4372             tag: 'span',
4373             html : this.html || ''
4374         };
4375         
4376         
4377         if (this.active) {
4378             cfg.cls += ' active';
4379         }
4380         
4381         if (this.disabled) {
4382             cfg.cls += ' disabled';
4383         }
4384         
4385         // left icon..
4386         if (this.glyphicon || this.icon) {
4387             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4388             a.cn.push({ tag : 'i', cls : c }) ;
4389         }
4390         // html..
4391         a.cn.push(span);
4392         // then badge..
4393         if (this.badge !== '') {
4394             
4395             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4396         }
4397         // fi
4398         if (this.menu) {
4399             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4400             a.cls += 'dropdown-toggle treeview' ;
4401             
4402         }
4403         
4404         
4405         
4406         return cfg;
4407          
4408            
4409     },
4410     
4411     initEvents : function()
4412     { 
4413         this.el.on('click', this.onClick, this);
4414        
4415     
4416         if(this.badge !== ''){
4417  
4418             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4419         }
4420         
4421     },
4422     
4423     onClick : function(e)
4424     {
4425         if(this.disabled){
4426             e.preventDefault();
4427             return;
4428         }
4429         
4430         if(this.preventDefault){
4431             e.preventDefault();
4432         }
4433         
4434         this.fireEvent('click', this);
4435     },
4436     
4437     disable : function()
4438     {
4439         this.setDisabled(true);
4440     },
4441     
4442     enable : function()
4443     {
4444         this.setDisabled(false);
4445     },
4446     
4447     setDisabled : function(state)
4448     {
4449         if(this.disabled == state){
4450             return;
4451         }
4452         
4453         this.disabled = state;
4454         
4455         if (state) {
4456             this.el.addClass('disabled');
4457             return;
4458         }
4459         
4460         this.el.removeClass('disabled');
4461         
4462         return;
4463     },
4464     
4465     setActive : function(state)
4466     {
4467         if(this.active == state){
4468             return;
4469         }
4470         
4471         this.active = state;
4472         
4473         if (state) {
4474             this.el.addClass('active');
4475             return;
4476         }
4477         
4478         this.el.removeClass('active');
4479         
4480         return;
4481     },
4482     
4483     isActive: function () 
4484     {
4485         return this.active;
4486     },
4487     
4488     setBadge : function(str)
4489     {
4490         if(!this.badgeEl){
4491             return;
4492         }
4493         
4494         this.badgeEl.dom.innerHTML = str;
4495     }
4496     
4497    
4498      
4499  
4500 });
4501  
4502
4503  /*
4504  * - LGPL
4505  *
4506  * row
4507  * 
4508  */
4509
4510 /**
4511  * @class Roo.bootstrap.Row
4512  * @extends Roo.bootstrap.Component
4513  * Bootstrap Row class (contains columns...)
4514  * 
4515  * @constructor
4516  * Create a new Row
4517  * @param {Object} config The config object
4518  */
4519
4520 Roo.bootstrap.Row = function(config){
4521     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4522 };
4523
4524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4525     
4526     getAutoCreate : function(){
4527        return {
4528             cls: 'row clearfix'
4529        };
4530     }
4531     
4532     
4533 });
4534
4535  
4536
4537  /*
4538  * - LGPL
4539  *
4540  * element
4541  * 
4542  */
4543
4544 /**
4545  * @class Roo.bootstrap.Element
4546  * @extends Roo.bootstrap.Component
4547  * Bootstrap Element class
4548  * @cfg {String} html contents of the element
4549  * @cfg {String} tag tag of the element
4550  * @cfg {String} cls class of the element
4551  * @cfg {Boolean} preventDefault (true|false) default false
4552  * @cfg {Boolean} clickable (true|false) default false
4553  * 
4554  * @constructor
4555  * Create a new Element
4556  * @param {Object} config The config object
4557  */
4558
4559 Roo.bootstrap.Element = function(config){
4560     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4561     
4562     this.addEvents({
4563         // raw events
4564         /**
4565          * @event click
4566          * When a element is chick
4567          * @param {Roo.bootstrap.Element} this
4568          * @param {Roo.EventObject} e
4569          */
4570         "click" : true
4571     });
4572 };
4573
4574 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4575     
4576     tag: 'div',
4577     cls: '',
4578     html: '',
4579     preventDefault: false, 
4580     clickable: false,
4581     
4582     getAutoCreate : function(){
4583         
4584         var cfg = {
4585             tag: this.tag,
4586             cls: this.cls,
4587             html: this.html
4588         }
4589         
4590         return cfg;
4591     },
4592     
4593     initEvents: function() 
4594     {
4595         Roo.bootstrap.Element.superclass.initEvents.call(this);
4596         
4597         if(this.clickable){
4598             this.el.on('click', this.onClick, this);
4599         }
4600         
4601     },
4602     
4603     onClick : function(e)
4604     {
4605         if(this.preventDefault){
4606             e.preventDefault();
4607         }
4608         
4609         this.fireEvent('click', this, e);
4610     },
4611     
4612     getValue : function()
4613     {
4614         return this.el.dom.innerHTML;
4615     },
4616     
4617     setValue : function(value)
4618     {
4619         this.el.dom.innerHTML = value;
4620     }
4621    
4622 });
4623
4624  
4625
4626  /*
4627  * - LGPL
4628  *
4629  * pagination
4630  * 
4631  */
4632
4633 /**
4634  * @class Roo.bootstrap.Pagination
4635  * @extends Roo.bootstrap.Component
4636  * Bootstrap Pagination class
4637  * @cfg {String} size xs | sm | md | lg
4638  * @cfg {Boolean} inverse false | true
4639  * 
4640  * @constructor
4641  * Create a new Pagination
4642  * @param {Object} config The config object
4643  */
4644
4645 Roo.bootstrap.Pagination = function(config){
4646     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4647 };
4648
4649 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4650     
4651     cls: false,
4652     size: false,
4653     inverse: false,
4654     
4655     getAutoCreate : function(){
4656         var cfg = {
4657             tag: 'ul',
4658                 cls: 'pagination'
4659         };
4660         if (this.inverse) {
4661             cfg.cls += ' inverse';
4662         }
4663         if (this.html) {
4664             cfg.html=this.html;
4665         }
4666         if (this.cls) {
4667             cfg.cls += " " + this.cls;
4668         }
4669         return cfg;
4670     }
4671    
4672 });
4673
4674  
4675
4676  /*
4677  * - LGPL
4678  *
4679  * Pagination item
4680  * 
4681  */
4682
4683
4684 /**
4685  * @class Roo.bootstrap.PaginationItem
4686  * @extends Roo.bootstrap.Component
4687  * Bootstrap PaginationItem class
4688  * @cfg {String} html text
4689  * @cfg {String} href the link
4690  * @cfg {Boolean} preventDefault (true | false) default true
4691  * @cfg {Boolean} active (true | false) default false
4692  * @cfg {Boolean} disabled default false
4693  * 
4694  * 
4695  * @constructor
4696  * Create a new PaginationItem
4697  * @param {Object} config The config object
4698  */
4699
4700
4701 Roo.bootstrap.PaginationItem = function(config){
4702     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4703     this.addEvents({
4704         // raw events
4705         /**
4706          * @event click
4707          * The raw click event for the entire grid.
4708          * @param {Roo.EventObject} e
4709          */
4710         "click" : true
4711     });
4712 };
4713
4714 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4715     
4716     href : false,
4717     html : false,
4718     preventDefault: true,
4719     active : false,
4720     cls : false,
4721     disabled: false,
4722     
4723     getAutoCreate : function(){
4724         var cfg= {
4725             tag: 'li',
4726             cn: [
4727                 {
4728                     tag : 'a',
4729                     href : this.href ? this.href : '#',
4730                     html : this.html ? this.html : ''
4731                 }
4732             ]
4733         };
4734         
4735         if(this.cls){
4736             cfg.cls = this.cls;
4737         }
4738         
4739         if(this.disabled){
4740             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4741         }
4742         
4743         if(this.active){
4744             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4745         }
4746         
4747         return cfg;
4748     },
4749     
4750     initEvents: function() {
4751         
4752         this.el.on('click', this.onClick, this);
4753         
4754     },
4755     onClick : function(e)
4756     {
4757         Roo.log('PaginationItem on click ');
4758         if(this.preventDefault){
4759             e.preventDefault();
4760         }
4761         
4762         if(this.disabled){
4763             return;
4764         }
4765         
4766         this.fireEvent('click', this, e);
4767     }
4768    
4769 });
4770
4771  
4772
4773  /*
4774  * - LGPL
4775  *
4776  * slider
4777  * 
4778  */
4779
4780
4781 /**
4782  * @class Roo.bootstrap.Slider
4783  * @extends Roo.bootstrap.Component
4784  * Bootstrap Slider class
4785  *    
4786  * @constructor
4787  * Create a new Slider
4788  * @param {Object} config The config object
4789  */
4790
4791 Roo.bootstrap.Slider = function(config){
4792     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4793 };
4794
4795 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4796     
4797     getAutoCreate : function(){
4798         
4799         var cfg = {
4800             tag: 'div',
4801             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4802             cn: [
4803                 {
4804                     tag: 'a',
4805                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4806                 }
4807             ]
4808         }
4809         
4810         return cfg;
4811     }
4812    
4813 });
4814
4815  /*
4816  * Based on:
4817  * Ext JS Library 1.1.1
4818  * Copyright(c) 2006-2007, Ext JS, LLC.
4819  *
4820  * Originally Released Under LGPL - original licence link has changed is not relivant.
4821  *
4822  * Fork - LGPL
4823  * <script type="text/javascript">
4824  */
4825  
4826
4827 /**
4828  * @class Roo.grid.ColumnModel
4829  * @extends Roo.util.Observable
4830  * This is the default implementation of a ColumnModel used by the Grid. It defines
4831  * the columns in the grid.
4832  * <br>Usage:<br>
4833  <pre><code>
4834  var colModel = new Roo.grid.ColumnModel([
4835         {header: "Ticker", width: 60, sortable: true, locked: true},
4836         {header: "Company Name", width: 150, sortable: true},
4837         {header: "Market Cap.", width: 100, sortable: true},
4838         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4839         {header: "Employees", width: 100, sortable: true, resizable: false}
4840  ]);
4841  </code></pre>
4842  * <p>
4843  
4844  * The config options listed for this class are options which may appear in each
4845  * individual column definition.
4846  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4847  * @constructor
4848  * @param {Object} config An Array of column config objects. See this class's
4849  * config objects for details.
4850 */
4851 Roo.grid.ColumnModel = function(config){
4852         /**
4853      * The config passed into the constructor
4854      */
4855     this.config = config;
4856     this.lookup = {};
4857
4858     // if no id, create one
4859     // if the column does not have a dataIndex mapping,
4860     // map it to the order it is in the config
4861     for(var i = 0, len = config.length; i < len; i++){
4862         var c = config[i];
4863         if(typeof c.dataIndex == "undefined"){
4864             c.dataIndex = i;
4865         }
4866         if(typeof c.renderer == "string"){
4867             c.renderer = Roo.util.Format[c.renderer];
4868         }
4869         if(typeof c.id == "undefined"){
4870             c.id = Roo.id();
4871         }
4872         if(c.editor && c.editor.xtype){
4873             c.editor  = Roo.factory(c.editor, Roo.grid);
4874         }
4875         if(c.editor && c.editor.isFormField){
4876             c.editor = new Roo.grid.GridEditor(c.editor);
4877         }
4878         this.lookup[c.id] = c;
4879     }
4880
4881     /**
4882      * The width of columns which have no width specified (defaults to 100)
4883      * @type Number
4884      */
4885     this.defaultWidth = 100;
4886
4887     /**
4888      * Default sortable of columns which have no sortable specified (defaults to false)
4889      * @type Boolean
4890      */
4891     this.defaultSortable = false;
4892
4893     this.addEvents({
4894         /**
4895              * @event widthchange
4896              * Fires when the width of a column changes.
4897              * @param {ColumnModel} this
4898              * @param {Number} columnIndex The column index
4899              * @param {Number} newWidth The new width
4900              */
4901             "widthchange": true,
4902         /**
4903              * @event headerchange
4904              * Fires when the text of a header changes.
4905              * @param {ColumnModel} this
4906              * @param {Number} columnIndex The column index
4907              * @param {Number} newText The new header text
4908              */
4909             "headerchange": true,
4910         /**
4911              * @event hiddenchange
4912              * Fires when a column is hidden or "unhidden".
4913              * @param {ColumnModel} this
4914              * @param {Number} columnIndex The column index
4915              * @param {Boolean} hidden true if hidden, false otherwise
4916              */
4917             "hiddenchange": true,
4918             /**
4919          * @event columnmoved
4920          * Fires when a column is moved.
4921          * @param {ColumnModel} this
4922          * @param {Number} oldIndex
4923          * @param {Number} newIndex
4924          */
4925         "columnmoved" : true,
4926         /**
4927          * @event columlockchange
4928          * Fires when a column's locked state is changed
4929          * @param {ColumnModel} this
4930          * @param {Number} colIndex
4931          * @param {Boolean} locked true if locked
4932          */
4933         "columnlockchange" : true
4934     });
4935     Roo.grid.ColumnModel.superclass.constructor.call(this);
4936 };
4937 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4938     /**
4939      * @cfg {String} header The header text to display in the Grid view.
4940      */
4941     /**
4942      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4943      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4944      * specified, the column's index is used as an index into the Record's data Array.
4945      */
4946     /**
4947      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4948      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4949      */
4950     /**
4951      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4952      * Defaults to the value of the {@link #defaultSortable} property.
4953      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4954      */
4955     /**
4956      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4957      */
4958     /**
4959      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4960      */
4961     /**
4962      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4963      */
4964     /**
4965      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4966      */
4967     /**
4968      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4969      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4970      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4971      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4972      */
4973        /**
4974      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4975      */
4976     /**
4977      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4978      */
4979     /**
4980      * @cfg {String} cursor (Optional)
4981      */
4982     /**
4983      * @cfg {String} tooltip (Optional)
4984      */
4985     /**
4986      * Returns the id of the column at the specified index.
4987      * @param {Number} index The column index
4988      * @return {String} the id
4989      */
4990     getColumnId : function(index){
4991         return this.config[index].id;
4992     },
4993
4994     /**
4995      * Returns the column for a specified id.
4996      * @param {String} id The column id
4997      * @return {Object} the column
4998      */
4999     getColumnById : function(id){
5000         return this.lookup[id];
5001     },
5002
5003     
5004     /**
5005      * Returns the column for a specified dataIndex.
5006      * @param {String} dataIndex The column dataIndex
5007      * @return {Object|Boolean} the column or false if not found
5008      */
5009     getColumnByDataIndex: function(dataIndex){
5010         var index = this.findColumnIndex(dataIndex);
5011         return index > -1 ? this.config[index] : false;
5012     },
5013     
5014     /**
5015      * Returns the index for a specified column id.
5016      * @param {String} id The column id
5017      * @return {Number} the index, or -1 if not found
5018      */
5019     getIndexById : function(id){
5020         for(var i = 0, len = this.config.length; i < len; i++){
5021             if(this.config[i].id == id){
5022                 return i;
5023             }
5024         }
5025         return -1;
5026     },
5027     
5028     /**
5029      * Returns the index for a specified column dataIndex.
5030      * @param {String} dataIndex The column dataIndex
5031      * @return {Number} the index, or -1 if not found
5032      */
5033     
5034     findColumnIndex : function(dataIndex){
5035         for(var i = 0, len = this.config.length; i < len; i++){
5036             if(this.config[i].dataIndex == dataIndex){
5037                 return i;
5038             }
5039         }
5040         return -1;
5041     },
5042     
5043     
5044     moveColumn : function(oldIndex, newIndex){
5045         var c = this.config[oldIndex];
5046         this.config.splice(oldIndex, 1);
5047         this.config.splice(newIndex, 0, c);
5048         this.dataMap = null;
5049         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5050     },
5051
5052     isLocked : function(colIndex){
5053         return this.config[colIndex].locked === true;
5054     },
5055
5056     setLocked : function(colIndex, value, suppressEvent){
5057         if(this.isLocked(colIndex) == value){
5058             return;
5059         }
5060         this.config[colIndex].locked = value;
5061         if(!suppressEvent){
5062             this.fireEvent("columnlockchange", this, colIndex, value);
5063         }
5064     },
5065
5066     getTotalLockedWidth : function(){
5067         var totalWidth = 0;
5068         for(var i = 0; i < this.config.length; i++){
5069             if(this.isLocked(i) && !this.isHidden(i)){
5070                 this.totalWidth += this.getColumnWidth(i);
5071             }
5072         }
5073         return totalWidth;
5074     },
5075
5076     getLockedCount : function(){
5077         for(var i = 0, len = this.config.length; i < len; i++){
5078             if(!this.isLocked(i)){
5079                 return i;
5080             }
5081         }
5082     },
5083
5084     /**
5085      * Returns the number of columns.
5086      * @return {Number}
5087      */
5088     getColumnCount : function(visibleOnly){
5089         if(visibleOnly === true){
5090             var c = 0;
5091             for(var i = 0, len = this.config.length; i < len; i++){
5092                 if(!this.isHidden(i)){
5093                     c++;
5094                 }
5095             }
5096             return c;
5097         }
5098         return this.config.length;
5099     },
5100
5101     /**
5102      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5103      * @param {Function} fn
5104      * @param {Object} scope (optional)
5105      * @return {Array} result
5106      */
5107     getColumnsBy : function(fn, scope){
5108         var r = [];
5109         for(var i = 0, len = this.config.length; i < len; i++){
5110             var c = this.config[i];
5111             if(fn.call(scope||this, c, i) === true){
5112                 r[r.length] = c;
5113             }
5114         }
5115         return r;
5116     },
5117
5118     /**
5119      * Returns true if the specified column is sortable.
5120      * @param {Number} col The column index
5121      * @return {Boolean}
5122      */
5123     isSortable : function(col){
5124         if(typeof this.config[col].sortable == "undefined"){
5125             return this.defaultSortable;
5126         }
5127         return this.config[col].sortable;
5128     },
5129
5130     /**
5131      * Returns the rendering (formatting) function defined for the column.
5132      * @param {Number} col The column index.
5133      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5134      */
5135     getRenderer : function(col){
5136         if(!this.config[col].renderer){
5137             return Roo.grid.ColumnModel.defaultRenderer;
5138         }
5139         return this.config[col].renderer;
5140     },
5141
5142     /**
5143      * Sets the rendering (formatting) function for a column.
5144      * @param {Number} col The column index
5145      * @param {Function} fn The function to use to process the cell's raw data
5146      * to return HTML markup for the grid view. The render function is called with
5147      * the following parameters:<ul>
5148      * <li>Data value.</li>
5149      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5150      * <li>css A CSS style string to apply to the table cell.</li>
5151      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5152      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5153      * <li>Row index</li>
5154      * <li>Column index</li>
5155      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5156      */
5157     setRenderer : function(col, fn){
5158         this.config[col].renderer = fn;
5159     },
5160
5161     /**
5162      * Returns the width for the specified column.
5163      * @param {Number} col The column index
5164      * @return {Number}
5165      */
5166     getColumnWidth : function(col){
5167         return this.config[col].width * 1 || this.defaultWidth;
5168     },
5169
5170     /**
5171      * Sets the width for a column.
5172      * @param {Number} col The column index
5173      * @param {Number} width The new width
5174      */
5175     setColumnWidth : function(col, width, suppressEvent){
5176         this.config[col].width = width;
5177         this.totalWidth = null;
5178         if(!suppressEvent){
5179              this.fireEvent("widthchange", this, col, width);
5180         }
5181     },
5182
5183     /**
5184      * Returns the total width of all columns.
5185      * @param {Boolean} includeHidden True to include hidden column widths
5186      * @return {Number}
5187      */
5188     getTotalWidth : function(includeHidden){
5189         if(!this.totalWidth){
5190             this.totalWidth = 0;
5191             for(var i = 0, len = this.config.length; i < len; i++){
5192                 if(includeHidden || !this.isHidden(i)){
5193                     this.totalWidth += this.getColumnWidth(i);
5194                 }
5195             }
5196         }
5197         return this.totalWidth;
5198     },
5199
5200     /**
5201      * Returns the header for the specified column.
5202      * @param {Number} col The column index
5203      * @return {String}
5204      */
5205     getColumnHeader : function(col){
5206         return this.config[col].header;
5207     },
5208
5209     /**
5210      * Sets the header for a column.
5211      * @param {Number} col The column index
5212      * @param {String} header The new header
5213      */
5214     setColumnHeader : function(col, header){
5215         this.config[col].header = header;
5216         this.fireEvent("headerchange", this, col, header);
5217     },
5218
5219     /**
5220      * Returns the tooltip for the specified column.
5221      * @param {Number} col The column index
5222      * @return {String}
5223      */
5224     getColumnTooltip : function(col){
5225             return this.config[col].tooltip;
5226     },
5227     /**
5228      * Sets the tooltip for a column.
5229      * @param {Number} col The column index
5230      * @param {String} tooltip The new tooltip
5231      */
5232     setColumnTooltip : function(col, tooltip){
5233             this.config[col].tooltip = tooltip;
5234     },
5235
5236     /**
5237      * Returns the dataIndex for the specified column.
5238      * @param {Number} col The column index
5239      * @return {Number}
5240      */
5241     getDataIndex : function(col){
5242         return this.config[col].dataIndex;
5243     },
5244
5245     /**
5246      * Sets the dataIndex for a column.
5247      * @param {Number} col The column index
5248      * @param {Number} dataIndex The new dataIndex
5249      */
5250     setDataIndex : function(col, dataIndex){
5251         this.config[col].dataIndex = dataIndex;
5252     },
5253
5254     
5255     
5256     /**
5257      * Returns true if the cell is editable.
5258      * @param {Number} colIndex The column index
5259      * @param {Number} rowIndex The row index
5260      * @return {Boolean}
5261      */
5262     isCellEditable : function(colIndex, rowIndex){
5263         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5264     },
5265
5266     /**
5267      * Returns the editor defined for the cell/column.
5268      * return false or null to disable editing.
5269      * @param {Number} colIndex The column index
5270      * @param {Number} rowIndex The row index
5271      * @return {Object}
5272      */
5273     getCellEditor : function(colIndex, rowIndex){
5274         return this.config[colIndex].editor;
5275     },
5276
5277     /**
5278      * Sets if a column is editable.
5279      * @param {Number} col The column index
5280      * @param {Boolean} editable True if the column is editable
5281      */
5282     setEditable : function(col, editable){
5283         this.config[col].editable = editable;
5284     },
5285
5286
5287     /**
5288      * Returns true if the column is hidden.
5289      * @param {Number} colIndex The column index
5290      * @return {Boolean}
5291      */
5292     isHidden : function(colIndex){
5293         return this.config[colIndex].hidden;
5294     },
5295
5296
5297     /**
5298      * Returns true if the column width cannot be changed
5299      */
5300     isFixed : function(colIndex){
5301         return this.config[colIndex].fixed;
5302     },
5303
5304     /**
5305      * Returns true if the column can be resized
5306      * @return {Boolean}
5307      */
5308     isResizable : function(colIndex){
5309         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5310     },
5311     /**
5312      * Sets if a column is hidden.
5313      * @param {Number} colIndex The column index
5314      * @param {Boolean} hidden True if the column is hidden
5315      */
5316     setHidden : function(colIndex, hidden){
5317         this.config[colIndex].hidden = hidden;
5318         this.totalWidth = null;
5319         this.fireEvent("hiddenchange", this, colIndex, hidden);
5320     },
5321
5322     /**
5323      * Sets the editor for a column.
5324      * @param {Number} col The column index
5325      * @param {Object} editor The editor object
5326      */
5327     setEditor : function(col, editor){
5328         this.config[col].editor = editor;
5329     }
5330 });
5331
5332 Roo.grid.ColumnModel.defaultRenderer = function(value){
5333         if(typeof value == "string" && value.length < 1){
5334             return "&#160;";
5335         }
5336         return value;
5337 };
5338
5339 // Alias for backwards compatibility
5340 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5341 /*
5342  * Based on:
5343  * Ext JS Library 1.1.1
5344  * Copyright(c) 2006-2007, Ext JS, LLC.
5345  *
5346  * Originally Released Under LGPL - original licence link has changed is not relivant.
5347  *
5348  * Fork - LGPL
5349  * <script type="text/javascript">
5350  */
5351  
5352 /**
5353  * @class Roo.LoadMask
5354  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5355  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5356  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5357  * element's UpdateManager load indicator and will be destroyed after the initial load.
5358  * @constructor
5359  * Create a new LoadMask
5360  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5361  * @param {Object} config The config object
5362  */
5363 Roo.LoadMask = function(el, config){
5364     this.el = Roo.get(el);
5365     Roo.apply(this, config);
5366     if(this.store){
5367         this.store.on('beforeload', this.onBeforeLoad, this);
5368         this.store.on('load', this.onLoad, this);
5369         this.store.on('loadexception', this.onLoadException, this);
5370         this.removeMask = false;
5371     }else{
5372         var um = this.el.getUpdateManager();
5373         um.showLoadIndicator = false; // disable the default indicator
5374         um.on('beforeupdate', this.onBeforeLoad, this);
5375         um.on('update', this.onLoad, this);
5376         um.on('failure', this.onLoad, this);
5377         this.removeMask = true;
5378     }
5379 };
5380
5381 Roo.LoadMask.prototype = {
5382     /**
5383      * @cfg {Boolean} removeMask
5384      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5385      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5386      */
5387     /**
5388      * @cfg {String} msg
5389      * The text to display in a centered loading message box (defaults to 'Loading...')
5390      */
5391     msg : 'Loading...',
5392     /**
5393      * @cfg {String} msgCls
5394      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5395      */
5396     msgCls : 'x-mask-loading',
5397
5398     /**
5399      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5400      * @type Boolean
5401      */
5402     disabled: false,
5403
5404     /**
5405      * Disables the mask to prevent it from being displayed
5406      */
5407     disable : function(){
5408        this.disabled = true;
5409     },
5410
5411     /**
5412      * Enables the mask so that it can be displayed
5413      */
5414     enable : function(){
5415         this.disabled = false;
5416     },
5417     
5418     onLoadException : function()
5419     {
5420         Roo.log(arguments);
5421         
5422         if (typeof(arguments[3]) != 'undefined') {
5423             Roo.MessageBox.alert("Error loading",arguments[3]);
5424         } 
5425         /*
5426         try {
5427             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5428                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5429             }   
5430         } catch(e) {
5431             
5432         }
5433         */
5434     
5435         
5436         
5437         this.el.unmask(this.removeMask);
5438     },
5439     // private
5440     onLoad : function()
5441     {
5442         this.el.unmask(this.removeMask);
5443     },
5444
5445     // private
5446     onBeforeLoad : function(){
5447         if(!this.disabled){
5448             this.el.mask(this.msg, this.msgCls);
5449         }
5450     },
5451
5452     // private
5453     destroy : function(){
5454         if(this.store){
5455             this.store.un('beforeload', this.onBeforeLoad, this);
5456             this.store.un('load', this.onLoad, this);
5457             this.store.un('loadexception', this.onLoadException, this);
5458         }else{
5459             var um = this.el.getUpdateManager();
5460             um.un('beforeupdate', this.onBeforeLoad, this);
5461             um.un('update', this.onLoad, this);
5462             um.un('failure', this.onLoad, this);
5463         }
5464     }
5465 };/*
5466  * - LGPL
5467  *
5468  * table
5469  * 
5470  */
5471
5472 /**
5473  * @class Roo.bootstrap.Table
5474  * @extends Roo.bootstrap.Component
5475  * Bootstrap Table class
5476  * @cfg {String} cls table class
5477  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5478  * @cfg {String} bgcolor Specifies the background color for a table
5479  * @cfg {Number} border Specifies whether the table cells should have borders or not
5480  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5481  * @cfg {Number} cellspacing Specifies the space between cells
5482  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5483  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5484  * @cfg {String} sortable Specifies that the table should be sortable
5485  * @cfg {String} summary Specifies a summary of the content of a table
5486  * @cfg {Number} width Specifies the width of a table
5487  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5488  * 
5489  * @cfg {boolean} striped Should the rows be alternative striped
5490  * @cfg {boolean} bordered Add borders to the table
5491  * @cfg {boolean} hover Add hover highlighting
5492  * @cfg {boolean} condensed Format condensed
5493  * @cfg {boolean} responsive Format condensed
5494  * @cfg {Boolean} loadMask (true|false) default false
5495  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5496  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5497  * @cfg {Boolean} rowSelection (true|false) default false
5498  * @cfg {Boolean} cellSelection (true|false) default false
5499  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5500  
5501  * 
5502  * @constructor
5503  * Create a new Table
5504  * @param {Object} config The config object
5505  */
5506
5507 Roo.bootstrap.Table = function(config){
5508     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5509     
5510     // BC...
5511     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5512     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5513     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5514     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5515     
5516     
5517     if (this.sm) {
5518         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5519         this.sm = this.selModel;
5520         this.sm.xmodule = this.xmodule || false;
5521     }
5522     if (this.cm && typeof(this.cm.config) == 'undefined') {
5523         this.colModel = new Roo.grid.ColumnModel(this.cm);
5524         this.cm = this.colModel;
5525         this.cm.xmodule = this.xmodule || false;
5526     }
5527     if (this.store) {
5528         this.store= Roo.factory(this.store, Roo.data);
5529         this.ds = this.store;
5530         this.ds.xmodule = this.xmodule || false;
5531          
5532     }
5533     if (this.footer && this.store) {
5534         this.footer.dataSource = this.ds;
5535         this.footer = Roo.factory(this.footer);
5536     }
5537     
5538     /** @private */
5539     this.addEvents({
5540         /**
5541          * @event cellclick
5542          * Fires when a cell is clicked
5543          * @param {Roo.bootstrap.Table} this
5544          * @param {Roo.Element} el
5545          * @param {Number} rowIndex
5546          * @param {Number} columnIndex
5547          * @param {Roo.EventObject} e
5548          */
5549         "cellclick" : true,
5550         /**
5551          * @event celldblclick
5552          * Fires when a cell is double clicked
5553          * @param {Roo.bootstrap.Table} this
5554          * @param {Roo.Element} el
5555          * @param {Number} rowIndex
5556          * @param {Number} columnIndex
5557          * @param {Roo.EventObject} e
5558          */
5559         "celldblclick" : true,
5560         /**
5561          * @event rowclick
5562          * Fires when a row is clicked
5563          * @param {Roo.bootstrap.Table} this
5564          * @param {Roo.Element} el
5565          * @param {Number} rowIndex
5566          * @param {Roo.EventObject} e
5567          */
5568         "rowclick" : true,
5569         /**
5570          * @event rowdblclick
5571          * Fires when a row is double clicked
5572          * @param {Roo.bootstrap.Table} this
5573          * @param {Roo.Element} el
5574          * @param {Number} rowIndex
5575          * @param {Roo.EventObject} e
5576          */
5577         "rowdblclick" : true,
5578         /**
5579          * @event mouseover
5580          * Fires when a mouseover occur
5581          * @param {Roo.bootstrap.Table} this
5582          * @param {Roo.Element} el
5583          * @param {Number} rowIndex
5584          * @param {Number} columnIndex
5585          * @param {Roo.EventObject} e
5586          */
5587         "mouseover" : true,
5588         /**
5589          * @event mouseout
5590          * Fires when a mouseout occur
5591          * @param {Roo.bootstrap.Table} this
5592          * @param {Roo.Element} el
5593          * @param {Number} rowIndex
5594          * @param {Number} columnIndex
5595          * @param {Roo.EventObject} e
5596          */
5597         "mouseout" : true,
5598         /**
5599          * @event rowclass
5600          * Fires when a row is rendered, so you can change add a style to it.
5601          * @param {Roo.bootstrap.Table} this
5602          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5603          */
5604         'rowclass' : true,
5605           /**
5606          * @event rowsrendered
5607          * Fires when all the  rows have been rendered
5608          * @param {Roo.bootstrap.Table} this
5609          */
5610         'rowsrendered' : true
5611         
5612     });
5613 };
5614
5615 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5616     
5617     cls: false,
5618     align: false,
5619     bgcolor: false,
5620     border: false,
5621     cellpadding: false,
5622     cellspacing: false,
5623     frame: false,
5624     rules: false,
5625     sortable: false,
5626     summary: false,
5627     width: false,
5628     striped : false,
5629     bordered: false,
5630     hover:  false,
5631     condensed : false,
5632     responsive : false,
5633     sm : false,
5634     cm : false,
5635     store : false,
5636     loadMask : false,
5637     footerShow : true,
5638     headerShow : true,
5639   
5640     rowSelection : false,
5641     cellSelection : false,
5642     layout : false,
5643     
5644     // Roo.Element - the tbody
5645     mainBody: false, 
5646     
5647     getAutoCreate : function(){
5648         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5649         
5650         cfg = {
5651             tag: 'table',
5652             cls : 'table',
5653             cn : []
5654         }
5655             
5656         if (this.striped) {
5657             cfg.cls += ' table-striped';
5658         }
5659         
5660         if (this.hover) {
5661             cfg.cls += ' table-hover';
5662         }
5663         if (this.bordered) {
5664             cfg.cls += ' table-bordered';
5665         }
5666         if (this.condensed) {
5667             cfg.cls += ' table-condensed';
5668         }
5669         if (this.responsive) {
5670             cfg.cls += ' table-responsive';
5671         }
5672         
5673         if (this.cls) {
5674             cfg.cls+=  ' ' +this.cls;
5675         }
5676         
5677         // this lot should be simplifed...
5678         
5679         if (this.align) {
5680             cfg.align=this.align;
5681         }
5682         if (this.bgcolor) {
5683             cfg.bgcolor=this.bgcolor;
5684         }
5685         if (this.border) {
5686             cfg.border=this.border;
5687         }
5688         if (this.cellpadding) {
5689             cfg.cellpadding=this.cellpadding;
5690         }
5691         if (this.cellspacing) {
5692             cfg.cellspacing=this.cellspacing;
5693         }
5694         if (this.frame) {
5695             cfg.frame=this.frame;
5696         }
5697         if (this.rules) {
5698             cfg.rules=this.rules;
5699         }
5700         if (this.sortable) {
5701             cfg.sortable=this.sortable;
5702         }
5703         if (this.summary) {
5704             cfg.summary=this.summary;
5705         }
5706         if (this.width) {
5707             cfg.width=this.width;
5708         }
5709         if (this.layout) {
5710             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5711         }
5712         
5713         if(this.store || this.cm){
5714             if(this.headerShow){
5715                 cfg.cn.push(this.renderHeader());
5716             }
5717             
5718             cfg.cn.push(this.renderBody());
5719             
5720             if(this.footerShow){
5721                 cfg.cn.push(this.renderFooter());
5722             }
5723             
5724             cfg.cls+=  ' TableGrid';
5725         }
5726         
5727         return { cn : [ cfg ] };
5728     },
5729     
5730     initEvents : function()
5731     {   
5732         if(!this.store || !this.cm){
5733             return;
5734         }
5735         
5736         //Roo.log('initEvents with ds!!!!');
5737         
5738         this.mainBody = this.el.select('tbody', true).first();
5739         
5740         
5741         var _this = this;
5742         
5743         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5744             e.on('click', _this.sort, _this);
5745         });
5746         
5747         this.el.on("click", this.onClick, this);
5748         this.el.on("dblclick", this.onDblClick, this);
5749         
5750         // why is this done????? = it breaks dialogs??
5751         //this.parent().el.setStyle('position', 'relative');
5752         
5753         
5754         if (this.footer) {
5755             this.footer.parentId = this.id;
5756             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5757         }
5758         
5759         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5760         
5761         this.store.on('load', this.onLoad, this);
5762         this.store.on('beforeload', this.onBeforeLoad, this);
5763         this.store.on('update', this.onUpdate, this);
5764         this.store.on('add', this.onAdd, this);
5765         
5766     },
5767     
5768     onMouseover : function(e, el)
5769     {
5770         var cell = Roo.get(el);
5771         
5772         if(!cell){
5773             return;
5774         }
5775         
5776         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5777             cell = cell.findParent('td', false, true);
5778         }
5779         
5780         var row = cell.findParent('tr', false, true);
5781         var cellIndex = cell.dom.cellIndex;
5782         var rowIndex = row.dom.rowIndex - 1; // start from 0
5783         
5784         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5785         
5786     },
5787     
5788     onMouseout : function(e, el)
5789     {
5790         var cell = Roo.get(el);
5791         
5792         if(!cell){
5793             return;
5794         }
5795         
5796         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5797             cell = cell.findParent('td', false, true);
5798         }
5799         
5800         var row = cell.findParent('tr', false, true);
5801         var cellIndex = cell.dom.cellIndex;
5802         var rowIndex = row.dom.rowIndex - 1; // start from 0
5803         
5804         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5805         
5806     },
5807     
5808     onClick : function(e, el)
5809     {
5810         var cell = Roo.get(el);
5811         
5812         if(!cell || (!this.cellSelection && !this.rowSelection)){
5813             return;
5814         }
5815         
5816         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5817             cell = cell.findParent('td', false, true);
5818         }
5819         
5820         if(!cell || typeof(cell) == 'undefined'){
5821             return;
5822         }
5823         
5824         var row = cell.findParent('tr', false, true);
5825         
5826         if(!row || typeof(row) == 'undefined'){
5827             return;
5828         }
5829         
5830         var cellIndex = cell.dom.cellIndex;
5831         var rowIndex = this.getRowIndex(row);
5832         
5833         // why??? - should these not be based on SelectionModel?
5834         if(this.cellSelection){
5835             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5836         }
5837         
5838         if(this.rowSelection){
5839             this.fireEvent('rowclick', this, row, rowIndex, e);
5840         }
5841         
5842         
5843     },
5844     
5845     onDblClick : function(e,el)
5846     {
5847         var cell = Roo.get(el);
5848         
5849         if(!cell || (!this.CellSelection && !this.RowSelection)){
5850             return;
5851         }
5852         
5853         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5854             cell = cell.findParent('td', false, true);
5855         }
5856         
5857         if(!cell || typeof(cell) == 'undefined'){
5858             return;
5859         }
5860         
5861         var row = cell.findParent('tr', false, true);
5862         
5863         if(!row || typeof(row) == 'undefined'){
5864             return;
5865         }
5866         
5867         var cellIndex = cell.dom.cellIndex;
5868         var rowIndex = this.getRowIndex(row);
5869         
5870         if(this.CellSelection){
5871             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5872         }
5873         
5874         if(this.RowSelection){
5875             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5876         }
5877     },
5878     
5879     sort : function(e,el)
5880     {
5881         var col = Roo.get(el);
5882         
5883         if(!col.hasClass('sortable')){
5884             return;
5885         }
5886         
5887         var sort = col.attr('sort');
5888         var dir = 'ASC';
5889         
5890         if(col.hasClass('glyphicon-arrow-up')){
5891             dir = 'DESC';
5892         }
5893         
5894         this.store.sortInfo = {field : sort, direction : dir};
5895         
5896         if (this.footer) {
5897             Roo.log("calling footer first");
5898             this.footer.onClick('first');
5899         } else {
5900         
5901             this.store.load({ params : { start : 0 } });
5902         }
5903     },
5904     
5905     renderHeader : function()
5906     {
5907         var header = {
5908             tag: 'thead',
5909             cn : []
5910         };
5911         
5912         var cm = this.cm;
5913         
5914         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5915             
5916             var config = cm.config[i];
5917             
5918             var c = {
5919                 tag: 'th',
5920                 style : '',
5921                 html: cm.getColumnHeader(i)
5922             };
5923             
5924             var hh = '';
5925             
5926             if(typeof(config.lgHeader) != 'undefined'){
5927                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5928             }
5929             
5930             if(typeof(config.mdHeader) != 'undefined'){
5931                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5932             }
5933             
5934             if(typeof(config.smHeader) != 'undefined'){
5935                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5936             }
5937             
5938             if(typeof(config.xsHeader) != 'undefined'){
5939                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5940             }
5941             
5942             if(hh.length){
5943                 c.html = hh;
5944             }
5945             
5946             if(typeof(config.tooltip) != 'undefined'){
5947                 c.tooltip = config.tooltip;
5948             }
5949             
5950             if(typeof(config.colspan) != 'undefined'){
5951                 c.colspan = config.colspan;
5952             }
5953             
5954             if(typeof(config.hidden) != 'undefined' && config.hidden){
5955                 c.style += ' display:none;';
5956             }
5957             
5958             if(typeof(config.dataIndex) != 'undefined'){
5959                 c.sort = config.dataIndex;
5960             }
5961             
5962             if(typeof(config.sortable) != 'undefined' && config.sortable){
5963                 c.cls = 'sortable';
5964             }
5965             
5966             if(typeof(config.align) != 'undefined' && config.align.length){
5967                 c.style += ' text-align:' + config.align + ';';
5968             }
5969             
5970             if(typeof(config.width) != 'undefined'){
5971                 c.style += ' width:' + config.width + 'px;';
5972             }
5973             
5974             if(typeof(config.cls) != 'undefined'){
5975                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5976             }
5977             
5978             header.cn.push(c)
5979         }
5980         
5981         return header;
5982     },
5983     
5984     renderBody : function()
5985     {
5986         var body = {
5987             tag: 'tbody',
5988             cn : [
5989                 {
5990                     tag: 'tr',
5991                     cn : [
5992                         {
5993                             tag : 'td',
5994                             colspan :  this.cm.getColumnCount()
5995                         }
5996                     ]
5997                 }
5998             ]
5999         };
6000         
6001         return body;
6002     },
6003     
6004     renderFooter : function()
6005     {
6006         var footer = {
6007             tag: 'tfoot',
6008             cn : [
6009                 {
6010                     tag: 'tr',
6011                     cn : [
6012                         {
6013                             tag : 'td',
6014                             colspan :  this.cm.getColumnCount()
6015                         }
6016                     ]
6017                 }
6018             ]
6019         };
6020         
6021         return footer;
6022     },
6023     
6024     
6025     
6026     onLoad : function()
6027     {
6028         Roo.log('ds onload');
6029         this.clear();
6030         
6031         var _this = this;
6032         var cm = this.cm;
6033         var ds = this.store;
6034         
6035         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6036             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6037             
6038             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6039                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6040             }
6041             
6042             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6043                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6044             }
6045         });
6046         
6047         var tbody =  this.mainBody;
6048               
6049         if(ds.getCount() > 0){
6050             ds.data.each(function(d,rowIndex){
6051                 var row =  this.renderRow(cm, ds, rowIndex);
6052                 
6053                 tbody.createChild(row);
6054                 
6055                 var _this = this;
6056                 
6057                 if(row.cellObjects.length){
6058                     Roo.each(row.cellObjects, function(r){
6059                         _this.renderCellObject(r);
6060                     })
6061                 }
6062                 
6063             }, this);
6064         }
6065         
6066         Roo.each(this.el.select('tbody td', true).elements, function(e){
6067             e.on('mouseover', _this.onMouseover, _this);
6068         });
6069         
6070         Roo.each(this.el.select('tbody td', true).elements, function(e){
6071             e.on('mouseout', _this.onMouseout, _this);
6072         });
6073         this.fireEvent('rowsrendered', this);
6074         //if(this.loadMask){
6075         //    this.maskEl.hide();
6076         //}
6077     },
6078     
6079     
6080     onUpdate : function(ds,record)
6081     {
6082         this.refreshRow(record);
6083     },
6084     
6085     onRemove : function(ds, record, index, isUpdate){
6086         if(isUpdate !== true){
6087             this.fireEvent("beforerowremoved", this, index, record);
6088         }
6089         var bt = this.mainBody.dom;
6090         
6091         var rows = this.el.select('tbody > tr', true).elements;
6092         
6093         if(typeof(rows[index]) != 'undefined'){
6094             bt.removeChild(rows[index].dom);
6095         }
6096         
6097 //        if(bt.rows[index]){
6098 //            bt.removeChild(bt.rows[index]);
6099 //        }
6100         
6101         if(isUpdate !== true){
6102             //this.stripeRows(index);
6103             //this.syncRowHeights(index, index);
6104             //this.layout();
6105             this.fireEvent("rowremoved", this, index, record);
6106         }
6107     },
6108     
6109     onAdd : function(ds, records, rowIndex)
6110     {
6111         //Roo.log('on Add called');
6112         // - note this does not handle multiple adding very well..
6113         var bt = this.mainBody.dom;
6114         for (var i =0 ; i < records.length;i++) {
6115             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6116             //Roo.log(records[i]);
6117             //Roo.log(this.store.getAt(rowIndex+i));
6118             this.insertRow(this.store, rowIndex + i, false);
6119             return;
6120         }
6121         
6122     },
6123     
6124     
6125     refreshRow : function(record){
6126         var ds = this.store, index;
6127         if(typeof record == 'number'){
6128             index = record;
6129             record = ds.getAt(index);
6130         }else{
6131             index = ds.indexOf(record);
6132         }
6133         this.insertRow(ds, index, true);
6134         this.onRemove(ds, record, index+1, true);
6135         //this.syncRowHeights(index, index);
6136         //this.layout();
6137         this.fireEvent("rowupdated", this, index, record);
6138     },
6139     
6140     insertRow : function(dm, rowIndex, isUpdate){
6141         
6142         if(!isUpdate){
6143             this.fireEvent("beforerowsinserted", this, rowIndex);
6144         }
6145             //var s = this.getScrollState();
6146         var row = this.renderRow(this.cm, this.store, rowIndex);
6147         // insert before rowIndex..
6148         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6149         
6150         var _this = this;
6151                 
6152         if(row.cellObjects.length){
6153             Roo.each(row.cellObjects, function(r){
6154                 _this.renderCellObject(r);
6155             })
6156         }
6157             
6158         if(!isUpdate){
6159             this.fireEvent("rowsinserted", this, rowIndex);
6160             //this.syncRowHeights(firstRow, lastRow);
6161             //this.stripeRows(firstRow);
6162             //this.layout();
6163         }
6164         
6165     },
6166     
6167     
6168     getRowDom : function(rowIndex)
6169     {
6170         var rows = this.el.select('tbody > tr', true).elements;
6171         
6172         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6173         
6174     },
6175     // returns the object tree for a tr..
6176   
6177     
6178     renderRow : function(cm, ds, rowIndex) 
6179     {
6180         
6181         var d = ds.getAt(rowIndex);
6182         
6183         var row = {
6184             tag : 'tr',
6185             cn : []
6186         };
6187             
6188         var cellObjects = [];
6189         
6190         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6191             var config = cm.config[i];
6192             
6193             var renderer = cm.getRenderer(i);
6194             var value = '';
6195             var id = false;
6196             
6197             if(typeof(renderer) !== 'undefined'){
6198                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6199             }
6200             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6201             // and are rendered into the cells after the row is rendered - using the id for the element.
6202             
6203             if(typeof(value) === 'object'){
6204                 id = Roo.id();
6205                 cellObjects.push({
6206                     container : id,
6207                     cfg : value 
6208                 })
6209             }
6210             
6211             var rowcfg = {
6212                 record: d,
6213                 rowIndex : rowIndex,
6214                 colIndex : i,
6215                 rowClass : ''
6216             }
6217
6218             this.fireEvent('rowclass', this, rowcfg);
6219             
6220             var td = {
6221                 tag: 'td',
6222                 cls : rowcfg.rowClass,
6223                 style: '',
6224                 html: (typeof(value) === 'object') ? '' : value
6225             };
6226             
6227             if (id) {
6228                 td.id = id;
6229             }
6230             
6231             if(typeof(config.colspan) != 'undefined'){
6232                 td.colspan = config.colspan;
6233             }
6234             
6235             if(typeof(config.hidden) != 'undefined' && config.hidden){
6236                 td.style += ' display:none;';
6237             }
6238             
6239             if(typeof(config.align) != 'undefined' && config.align.length){
6240                 td.style += ' text-align:' + config.align + ';';
6241             }
6242             
6243             if(typeof(config.width) != 'undefined'){
6244                 td.style += ' width:' +  config.width + 'px;';
6245             }
6246             
6247             if(typeof(config.cursor) != 'undefined'){
6248                 td.style += ' cursor:' +  config.cursor + ';';
6249             }
6250             
6251             if(typeof(config.cls) != 'undefined'){
6252                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6253             }
6254              
6255             row.cn.push(td);
6256            
6257         }
6258         
6259         row.cellObjects = cellObjects;
6260         
6261         return row;
6262           
6263     },
6264     
6265     
6266     
6267     onBeforeLoad : function()
6268     {
6269         //Roo.log('ds onBeforeLoad');
6270         
6271         //this.clear();
6272         
6273         //if(this.loadMask){
6274         //    this.maskEl.show();
6275         //}
6276     },
6277      /**
6278      * Remove all rows
6279      */
6280     clear : function()
6281     {
6282         this.el.select('tbody', true).first().dom.innerHTML = '';
6283     },
6284     /**
6285      * Show or hide a row.
6286      * @param {Number} rowIndex to show or hide
6287      * @param {Boolean} state hide
6288      */
6289     setRowVisibility : function(rowIndex, state)
6290     {
6291         var bt = this.mainBody.dom;
6292         
6293         var rows = this.el.select('tbody > tr', true).elements;
6294         
6295         if(typeof(rows[rowIndex]) == 'undefined'){
6296             return;
6297         }
6298         rows[rowIndex].dom.style.display = state ? '' : 'none';
6299     },
6300     
6301     
6302     getSelectionModel : function(){
6303         if(!this.selModel){
6304             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6305         }
6306         return this.selModel;
6307     },
6308     /*
6309      * Render the Roo.bootstrap object from renderder
6310      */
6311     renderCellObject : function(r)
6312     {
6313         var _this = this;
6314         
6315         var t = r.cfg.render(r.container);
6316         
6317         if(r.cfg.cn){
6318             Roo.each(r.cfg.cn, function(c){
6319                 var child = {
6320                     container: t.getChildContainer(),
6321                     cfg: c
6322                 }
6323                 _this.renderCellObject(child);
6324             })
6325         }
6326     },
6327     
6328     getRowIndex : function(row)
6329     {
6330         var rowIndex = -1;
6331         
6332         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6333             if(el != row){
6334                 return;
6335             }
6336             
6337             rowIndex = index;
6338         });
6339         
6340         return rowIndex;
6341     }
6342    
6343 });
6344
6345  
6346
6347  /*
6348  * - LGPL
6349  *
6350  * table cell
6351  * 
6352  */
6353
6354 /**
6355  * @class Roo.bootstrap.TableCell
6356  * @extends Roo.bootstrap.Component
6357  * Bootstrap TableCell class
6358  * @cfg {String} html cell contain text
6359  * @cfg {String} cls cell class
6360  * @cfg {String} tag cell tag (td|th) default td
6361  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6362  * @cfg {String} align Aligns the content in a cell
6363  * @cfg {String} axis Categorizes cells
6364  * @cfg {String} bgcolor Specifies the background color of a cell
6365  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6366  * @cfg {Number} colspan Specifies the number of columns a cell should span
6367  * @cfg {String} headers Specifies one or more header cells a cell is related to
6368  * @cfg {Number} height Sets the height of a cell
6369  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6370  * @cfg {Number} rowspan Sets the number of rows a cell should span
6371  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6372  * @cfg {String} valign Vertical aligns the content in a cell
6373  * @cfg {Number} width Specifies the width of a cell
6374  * 
6375  * @constructor
6376  * Create a new TableCell
6377  * @param {Object} config The config object
6378  */
6379
6380 Roo.bootstrap.TableCell = function(config){
6381     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6382 };
6383
6384 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6385     
6386     html: false,
6387     cls: false,
6388     tag: false,
6389     abbr: false,
6390     align: false,
6391     axis: false,
6392     bgcolor: false,
6393     charoff: false,
6394     colspan: false,
6395     headers: false,
6396     height: false,
6397     nowrap: false,
6398     rowspan: false,
6399     scope: false,
6400     valign: false,
6401     width: false,
6402     
6403     
6404     getAutoCreate : function(){
6405         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6406         
6407         cfg = {
6408             tag: 'td'
6409         }
6410         
6411         if(this.tag){
6412             cfg.tag = this.tag;
6413         }
6414         
6415         if (this.html) {
6416             cfg.html=this.html
6417         }
6418         if (this.cls) {
6419             cfg.cls=this.cls
6420         }
6421         if (this.abbr) {
6422             cfg.abbr=this.abbr
6423         }
6424         if (this.align) {
6425             cfg.align=this.align
6426         }
6427         if (this.axis) {
6428             cfg.axis=this.axis
6429         }
6430         if (this.bgcolor) {
6431             cfg.bgcolor=this.bgcolor
6432         }
6433         if (this.charoff) {
6434             cfg.charoff=this.charoff
6435         }
6436         if (this.colspan) {
6437             cfg.colspan=this.colspan
6438         }
6439         if (this.headers) {
6440             cfg.headers=this.headers
6441         }
6442         if (this.height) {
6443             cfg.height=this.height
6444         }
6445         if (this.nowrap) {
6446             cfg.nowrap=this.nowrap
6447         }
6448         if (this.rowspan) {
6449             cfg.rowspan=this.rowspan
6450         }
6451         if (this.scope) {
6452             cfg.scope=this.scope
6453         }
6454         if (this.valign) {
6455             cfg.valign=this.valign
6456         }
6457         if (this.width) {
6458             cfg.width=this.width
6459         }
6460         
6461         
6462         return cfg;
6463     }
6464    
6465 });
6466
6467  
6468
6469  /*
6470  * - LGPL
6471  *
6472  * table row
6473  * 
6474  */
6475
6476 /**
6477  * @class Roo.bootstrap.TableRow
6478  * @extends Roo.bootstrap.Component
6479  * Bootstrap TableRow class
6480  * @cfg {String} cls row class
6481  * @cfg {String} align Aligns the content in a table row
6482  * @cfg {String} bgcolor Specifies a background color for a table row
6483  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6484  * @cfg {String} valign Vertical aligns the content in a table row
6485  * 
6486  * @constructor
6487  * Create a new TableRow
6488  * @param {Object} config The config object
6489  */
6490
6491 Roo.bootstrap.TableRow = function(config){
6492     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6493 };
6494
6495 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6496     
6497     cls: false,
6498     align: false,
6499     bgcolor: false,
6500     charoff: false,
6501     valign: false,
6502     
6503     getAutoCreate : function(){
6504         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6505         
6506         cfg = {
6507             tag: 'tr'
6508         }
6509             
6510         if(this.cls){
6511             cfg.cls = this.cls;
6512         }
6513         if(this.align){
6514             cfg.align = this.align;
6515         }
6516         if(this.bgcolor){
6517             cfg.bgcolor = this.bgcolor;
6518         }
6519         if(this.charoff){
6520             cfg.charoff = this.charoff;
6521         }
6522         if(this.valign){
6523             cfg.valign = this.valign;
6524         }
6525         
6526         return cfg;
6527     }
6528    
6529 });
6530
6531  
6532
6533  /*
6534  * - LGPL
6535  *
6536  * table body
6537  * 
6538  */
6539
6540 /**
6541  * @class Roo.bootstrap.TableBody
6542  * @extends Roo.bootstrap.Component
6543  * Bootstrap TableBody class
6544  * @cfg {String} cls element class
6545  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6546  * @cfg {String} align Aligns the content inside the element
6547  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6548  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6549  * 
6550  * @constructor
6551  * Create a new TableBody
6552  * @param {Object} config The config object
6553  */
6554
6555 Roo.bootstrap.TableBody = function(config){
6556     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6557 };
6558
6559 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6560     
6561     cls: false,
6562     tag: false,
6563     align: false,
6564     charoff: false,
6565     valign: false,
6566     
6567     getAutoCreate : function(){
6568         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6569         
6570         cfg = {
6571             tag: 'tbody'
6572         }
6573             
6574         if (this.cls) {
6575             cfg.cls=this.cls
6576         }
6577         if(this.tag){
6578             cfg.tag = this.tag;
6579         }
6580         
6581         if(this.align){
6582             cfg.align = this.align;
6583         }
6584         if(this.charoff){
6585             cfg.charoff = this.charoff;
6586         }
6587         if(this.valign){
6588             cfg.valign = this.valign;
6589         }
6590         
6591         return cfg;
6592     }
6593     
6594     
6595 //    initEvents : function()
6596 //    {
6597 //        
6598 //        if(!this.store){
6599 //            return;
6600 //        }
6601 //        
6602 //        this.store = Roo.factory(this.store, Roo.data);
6603 //        this.store.on('load', this.onLoad, this);
6604 //        
6605 //        this.store.load();
6606 //        
6607 //    },
6608 //    
6609 //    onLoad: function () 
6610 //    {   
6611 //        this.fireEvent('load', this);
6612 //    }
6613 //    
6614 //   
6615 });
6616
6617  
6618
6619  /*
6620  * Based on:
6621  * Ext JS Library 1.1.1
6622  * Copyright(c) 2006-2007, Ext JS, LLC.
6623  *
6624  * Originally Released Under LGPL - original licence link has changed is not relivant.
6625  *
6626  * Fork - LGPL
6627  * <script type="text/javascript">
6628  */
6629
6630 // as we use this in bootstrap.
6631 Roo.namespace('Roo.form');
6632  /**
6633  * @class Roo.form.Action
6634  * Internal Class used to handle form actions
6635  * @constructor
6636  * @param {Roo.form.BasicForm} el The form element or its id
6637  * @param {Object} config Configuration options
6638  */
6639
6640  
6641  
6642 // define the action interface
6643 Roo.form.Action = function(form, options){
6644     this.form = form;
6645     this.options = options || {};
6646 };
6647 /**
6648  * Client Validation Failed
6649  * @const 
6650  */
6651 Roo.form.Action.CLIENT_INVALID = 'client';
6652 /**
6653  * Server Validation Failed
6654  * @const 
6655  */
6656 Roo.form.Action.SERVER_INVALID = 'server';
6657  /**
6658  * Connect to Server Failed
6659  * @const 
6660  */
6661 Roo.form.Action.CONNECT_FAILURE = 'connect';
6662 /**
6663  * Reading Data from Server Failed
6664  * @const 
6665  */
6666 Roo.form.Action.LOAD_FAILURE = 'load';
6667
6668 Roo.form.Action.prototype = {
6669     type : 'default',
6670     failureType : undefined,
6671     response : undefined,
6672     result : undefined,
6673
6674     // interface method
6675     run : function(options){
6676
6677     },
6678
6679     // interface method
6680     success : function(response){
6681
6682     },
6683
6684     // interface method
6685     handleResponse : function(response){
6686
6687     },
6688
6689     // default connection failure
6690     failure : function(response){
6691         
6692         this.response = response;
6693         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6694         this.form.afterAction(this, false);
6695     },
6696
6697     processResponse : function(response){
6698         this.response = response;
6699         if(!response.responseText){
6700             return true;
6701         }
6702         this.result = this.handleResponse(response);
6703         return this.result;
6704     },
6705
6706     // utility functions used internally
6707     getUrl : function(appendParams){
6708         var url = this.options.url || this.form.url || this.form.el.dom.action;
6709         if(appendParams){
6710             var p = this.getParams();
6711             if(p){
6712                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6713             }
6714         }
6715         return url;
6716     },
6717
6718     getMethod : function(){
6719         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6720     },
6721
6722     getParams : function(){
6723         var bp = this.form.baseParams;
6724         var p = this.options.params;
6725         if(p){
6726             if(typeof p == "object"){
6727                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6728             }else if(typeof p == 'string' && bp){
6729                 p += '&' + Roo.urlEncode(bp);
6730             }
6731         }else if(bp){
6732             p = Roo.urlEncode(bp);
6733         }
6734         return p;
6735     },
6736
6737     createCallback : function(){
6738         return {
6739             success: this.success,
6740             failure: this.failure,
6741             scope: this,
6742             timeout: (this.form.timeout*1000),
6743             upload: this.form.fileUpload ? this.success : undefined
6744         };
6745     }
6746 };
6747
6748 Roo.form.Action.Submit = function(form, options){
6749     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6750 };
6751
6752 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6753     type : 'submit',
6754
6755     haveProgress : false,
6756     uploadComplete : false,
6757     
6758     // uploadProgress indicator.
6759     uploadProgress : function()
6760     {
6761         if (!this.form.progressUrl) {
6762             return;
6763         }
6764         
6765         if (!this.haveProgress) {
6766             Roo.MessageBox.progress("Uploading", "Uploading");
6767         }
6768         if (this.uploadComplete) {
6769            Roo.MessageBox.hide();
6770            return;
6771         }
6772         
6773         this.haveProgress = true;
6774    
6775         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6776         
6777         var c = new Roo.data.Connection();
6778         c.request({
6779             url : this.form.progressUrl,
6780             params: {
6781                 id : uid
6782             },
6783             method: 'GET',
6784             success : function(req){
6785                //console.log(data);
6786                 var rdata = false;
6787                 var edata;
6788                 try  {
6789                    rdata = Roo.decode(req.responseText)
6790                 } catch (e) {
6791                     Roo.log("Invalid data from server..");
6792                     Roo.log(edata);
6793                     return;
6794                 }
6795                 if (!rdata || !rdata.success) {
6796                     Roo.log(rdata);
6797                     Roo.MessageBox.alert(Roo.encode(rdata));
6798                     return;
6799                 }
6800                 var data = rdata.data;
6801                 
6802                 if (this.uploadComplete) {
6803                    Roo.MessageBox.hide();
6804                    return;
6805                 }
6806                    
6807                 if (data){
6808                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6809                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6810                     );
6811                 }
6812                 this.uploadProgress.defer(2000,this);
6813             },
6814        
6815             failure: function(data) {
6816                 Roo.log('progress url failed ');
6817                 Roo.log(data);
6818             },
6819             scope : this
6820         });
6821            
6822     },
6823     
6824     
6825     run : function()
6826     {
6827         // run get Values on the form, so it syncs any secondary forms.
6828         this.form.getValues();
6829         
6830         var o = this.options;
6831         var method = this.getMethod();
6832         var isPost = method == 'POST';
6833         if(o.clientValidation === false || this.form.isValid()){
6834             
6835             if (this.form.progressUrl) {
6836                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6837                     (new Date() * 1) + '' + Math.random());
6838                     
6839             } 
6840             
6841             
6842             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6843                 form:this.form.el.dom,
6844                 url:this.getUrl(!isPost),
6845                 method: method,
6846                 params:isPost ? this.getParams() : null,
6847                 isUpload: this.form.fileUpload
6848             }));
6849             
6850             this.uploadProgress();
6851
6852         }else if (o.clientValidation !== false){ // client validation failed
6853             this.failureType = Roo.form.Action.CLIENT_INVALID;
6854             this.form.afterAction(this, false);
6855         }
6856     },
6857
6858     success : function(response)
6859     {
6860         this.uploadComplete= true;
6861         if (this.haveProgress) {
6862             Roo.MessageBox.hide();
6863         }
6864         
6865         
6866         var result = this.processResponse(response);
6867         if(result === true || result.success){
6868             this.form.afterAction(this, true);
6869             return;
6870         }
6871         if(result.errors){
6872             this.form.markInvalid(result.errors);
6873             this.failureType = Roo.form.Action.SERVER_INVALID;
6874         }
6875         this.form.afterAction(this, false);
6876     },
6877     failure : function(response)
6878     {
6879         this.uploadComplete= true;
6880         if (this.haveProgress) {
6881             Roo.MessageBox.hide();
6882         }
6883         
6884         this.response = response;
6885         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6886         this.form.afterAction(this, false);
6887     },
6888     
6889     handleResponse : function(response){
6890         if(this.form.errorReader){
6891             var rs = this.form.errorReader.read(response);
6892             var errors = [];
6893             if(rs.records){
6894                 for(var i = 0, len = rs.records.length; i < len; i++) {
6895                     var r = rs.records[i];
6896                     errors[i] = r.data;
6897                 }
6898             }
6899             if(errors.length < 1){
6900                 errors = null;
6901             }
6902             return {
6903                 success : rs.success,
6904                 errors : errors
6905             };
6906         }
6907         var ret = false;
6908         try {
6909             ret = Roo.decode(response.responseText);
6910         } catch (e) {
6911             ret = {
6912                 success: false,
6913                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6914                 errors : []
6915             };
6916         }
6917         return ret;
6918         
6919     }
6920 });
6921
6922
6923 Roo.form.Action.Load = function(form, options){
6924     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6925     this.reader = this.form.reader;
6926 };
6927
6928 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6929     type : 'load',
6930
6931     run : function(){
6932         
6933         Roo.Ajax.request(Roo.apply(
6934                 this.createCallback(), {
6935                     method:this.getMethod(),
6936                     url:this.getUrl(false),
6937                     params:this.getParams()
6938         }));
6939     },
6940
6941     success : function(response){
6942         
6943         var result = this.processResponse(response);
6944         if(result === true || !result.success || !result.data){
6945             this.failureType = Roo.form.Action.LOAD_FAILURE;
6946             this.form.afterAction(this, false);
6947             return;
6948         }
6949         this.form.clearInvalid();
6950         this.form.setValues(result.data);
6951         this.form.afterAction(this, true);
6952     },
6953
6954     handleResponse : function(response){
6955         if(this.form.reader){
6956             var rs = this.form.reader.read(response);
6957             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6958             return {
6959                 success : rs.success,
6960                 data : data
6961             };
6962         }
6963         return Roo.decode(response.responseText);
6964     }
6965 });
6966
6967 Roo.form.Action.ACTION_TYPES = {
6968     'load' : Roo.form.Action.Load,
6969     'submit' : Roo.form.Action.Submit
6970 };/*
6971  * - LGPL
6972  *
6973  * form
6974  * 
6975  */
6976
6977 /**
6978  * @class Roo.bootstrap.Form
6979  * @extends Roo.bootstrap.Component
6980  * Bootstrap Form class
6981  * @cfg {String} method  GET | POST (default POST)
6982  * @cfg {String} labelAlign top | left (default top)
6983  * @cfg {String} align left  | right - for navbars
6984  * @cfg {Boolean} loadMask load mask when submit (default true)
6985
6986  * 
6987  * @constructor
6988  * Create a new Form
6989  * @param {Object} config The config object
6990  */
6991
6992
6993 Roo.bootstrap.Form = function(config){
6994     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6995     this.addEvents({
6996         /**
6997          * @event clientvalidation
6998          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6999          * @param {Form} this
7000          * @param {Boolean} valid true if the form has passed client-side validation
7001          */
7002         clientvalidation: true,
7003         /**
7004          * @event beforeaction
7005          * Fires before any action is performed. Return false to cancel the action.
7006          * @param {Form} this
7007          * @param {Action} action The action to be performed
7008          */
7009         beforeaction: true,
7010         /**
7011          * @event actionfailed
7012          * Fires when an action fails.
7013          * @param {Form} this
7014          * @param {Action} action The action that failed
7015          */
7016         actionfailed : true,
7017         /**
7018          * @event actioncomplete
7019          * Fires when an action is completed.
7020          * @param {Form} this
7021          * @param {Action} action The action that completed
7022          */
7023         actioncomplete : true
7024     });
7025     
7026 };
7027
7028 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7029       
7030      /**
7031      * @cfg {String} method
7032      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7033      */
7034     method : 'POST',
7035     /**
7036      * @cfg {String} url
7037      * The URL to use for form actions if one isn't supplied in the action options.
7038      */
7039     /**
7040      * @cfg {Boolean} fileUpload
7041      * Set to true if this form is a file upload.
7042      */
7043      
7044     /**
7045      * @cfg {Object} baseParams
7046      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7047      */
7048       
7049     /**
7050      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7051      */
7052     timeout: 30,
7053     /**
7054      * @cfg {Sting} align (left|right) for navbar forms
7055      */
7056     align : 'left',
7057
7058     // private
7059     activeAction : null,
7060  
7061     /**
7062      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7063      * element by passing it or its id or mask the form itself by passing in true.
7064      * @type Mixed
7065      */
7066     waitMsgTarget : false,
7067     
7068     loadMask : true,
7069     
7070     getAutoCreate : function(){
7071         
7072         var cfg = {
7073             tag: 'form',
7074             method : this.method || 'POST',
7075             id : this.id || Roo.id(),
7076             cls : ''
7077         }
7078         if (this.parent().xtype.match(/^Nav/)) {
7079             cfg.cls = 'navbar-form navbar-' + this.align;
7080             
7081         }
7082         
7083         if (this.labelAlign == 'left' ) {
7084             cfg.cls += ' form-horizontal';
7085         }
7086         
7087         
7088         return cfg;
7089     },
7090     initEvents : function()
7091     {
7092         this.el.on('submit', this.onSubmit, this);
7093         // this was added as random key presses on the form where triggering form submit.
7094         this.el.on('keypress', function(e) {
7095             if (e.getCharCode() != 13) {
7096                 return true;
7097             }
7098             // we might need to allow it for textareas.. and some other items.
7099             // check e.getTarget().
7100             
7101             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7102                 return true;
7103             }
7104         
7105             Roo.log("keypress blocked");
7106             
7107             e.preventDefault();
7108             return false;
7109         });
7110         
7111     },
7112     // private
7113     onSubmit : function(e){
7114         e.stopEvent();
7115     },
7116     
7117      /**
7118      * Returns true if client-side validation on the form is successful.
7119      * @return Boolean
7120      */
7121     isValid : function(){
7122         var items = this.getItems();
7123         var valid = true;
7124         items.each(function(f){
7125            if(!f.validate()){
7126                valid = false;
7127                
7128            }
7129         });
7130         return valid;
7131     },
7132     /**
7133      * Returns true if any fields in this form have changed since their original load.
7134      * @return Boolean
7135      */
7136     isDirty : function(){
7137         var dirty = false;
7138         var items = this.getItems();
7139         items.each(function(f){
7140            if(f.isDirty()){
7141                dirty = true;
7142                return false;
7143            }
7144            return true;
7145         });
7146         return dirty;
7147     },
7148      /**
7149      * Performs a predefined action (submit or load) or custom actions you define on this form.
7150      * @param {String} actionName The name of the action type
7151      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7152      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7153      * accept other config options):
7154      * <pre>
7155 Property          Type             Description
7156 ----------------  ---------------  ----------------------------------------------------------------------------------
7157 url               String           The url for the action (defaults to the form's url)
7158 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7159 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7160 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7161                                    validate the form on the client (defaults to false)
7162      * </pre>
7163      * @return {BasicForm} this
7164      */
7165     doAction : function(action, options){
7166         if(typeof action == 'string'){
7167             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7168         }
7169         if(this.fireEvent('beforeaction', this, action) !== false){
7170             this.beforeAction(action);
7171             action.run.defer(100, action);
7172         }
7173         return this;
7174     },
7175     
7176     // private
7177     beforeAction : function(action){
7178         var o = action.options;
7179         
7180         if(this.loadMask){
7181             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7182         }
7183         // not really supported yet.. ??
7184         
7185         //if(this.waitMsgTarget === true){
7186         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7187         //}else if(this.waitMsgTarget){
7188         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7189         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7190         //}else {
7191         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7192        // }
7193          
7194     },
7195
7196     // private
7197     afterAction : function(action, success){
7198         this.activeAction = null;
7199         var o = action.options;
7200         
7201         //if(this.waitMsgTarget === true){
7202             this.el.unmask();
7203         //}else if(this.waitMsgTarget){
7204         //    this.waitMsgTarget.unmask();
7205         //}else{
7206         //    Roo.MessageBox.updateProgress(1);
7207         //    Roo.MessageBox.hide();
7208        // }
7209         // 
7210         if(success){
7211             if(o.reset){
7212                 this.reset();
7213             }
7214             Roo.callback(o.success, o.scope, [this, action]);
7215             this.fireEvent('actioncomplete', this, action);
7216             
7217         }else{
7218             
7219             // failure condition..
7220             // we have a scenario where updates need confirming.
7221             // eg. if a locking scenario exists..
7222             // we look for { errors : { needs_confirm : true }} in the response.
7223             if (
7224                 (typeof(action.result) != 'undefined')  &&
7225                 (typeof(action.result.errors) != 'undefined')  &&
7226                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7227            ){
7228                 var _t = this;
7229                 Roo.log("not supported yet");
7230                  /*
7231                 
7232                 Roo.MessageBox.confirm(
7233                     "Change requires confirmation",
7234                     action.result.errorMsg,
7235                     function(r) {
7236                         if (r != 'yes') {
7237                             return;
7238                         }
7239                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7240                     }
7241                     
7242                 );
7243                 */
7244                 
7245                 
7246                 return;
7247             }
7248             
7249             Roo.callback(o.failure, o.scope, [this, action]);
7250             // show an error message if no failed handler is set..
7251             if (!this.hasListener('actionfailed')) {
7252                 Roo.log("need to add dialog support");
7253                 /*
7254                 Roo.MessageBox.alert("Error",
7255                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7256                         action.result.errorMsg :
7257                         "Saving Failed, please check your entries or try again"
7258                 );
7259                 */
7260             }
7261             
7262             this.fireEvent('actionfailed', this, action);
7263         }
7264         
7265     },
7266     /**
7267      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7268      * @param {String} id The value to search for
7269      * @return Field
7270      */
7271     findField : function(id){
7272         var items = this.getItems();
7273         var field = items.get(id);
7274         if(!field){
7275              items.each(function(f){
7276                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7277                     field = f;
7278                     return false;
7279                 }
7280                 return true;
7281             });
7282         }
7283         return field || null;
7284     },
7285      /**
7286      * Mark fields in this form invalid in bulk.
7287      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7288      * @return {BasicForm} this
7289      */
7290     markInvalid : function(errors){
7291         if(errors instanceof Array){
7292             for(var i = 0, len = errors.length; i < len; i++){
7293                 var fieldError = errors[i];
7294                 var f = this.findField(fieldError.id);
7295                 if(f){
7296                     f.markInvalid(fieldError.msg);
7297                 }
7298             }
7299         }else{
7300             var field, id;
7301             for(id in errors){
7302                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7303                     field.markInvalid(errors[id]);
7304                 }
7305             }
7306         }
7307         //Roo.each(this.childForms || [], function (f) {
7308         //    f.markInvalid(errors);
7309         //});
7310         
7311         return this;
7312     },
7313
7314     /**
7315      * Set values for fields in this form in bulk.
7316      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7317      * @return {BasicForm} this
7318      */
7319     setValues : function(values){
7320         if(values instanceof Array){ // array of objects
7321             for(var i = 0, len = values.length; i < len; i++){
7322                 var v = values[i];
7323                 var f = this.findField(v.id);
7324                 if(f){
7325                     f.setValue(v.value);
7326                     if(this.trackResetOnLoad){
7327                         f.originalValue = f.getValue();
7328                     }
7329                 }
7330             }
7331         }else{ // object hash
7332             var field, id;
7333             for(id in values){
7334                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7335                     
7336                     if (field.setFromData && 
7337                         field.valueField && 
7338                         field.displayField &&
7339                         // combos' with local stores can 
7340                         // be queried via setValue()
7341                         // to set their value..
7342                         (field.store && !field.store.isLocal)
7343                         ) {
7344                         // it's a combo
7345                         var sd = { };
7346                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7347                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7348                         field.setFromData(sd);
7349                         
7350                     } else {
7351                         field.setValue(values[id]);
7352                     }
7353                     
7354                     
7355                     if(this.trackResetOnLoad){
7356                         field.originalValue = field.getValue();
7357                     }
7358                 }
7359             }
7360         }
7361          
7362         //Roo.each(this.childForms || [], function (f) {
7363         //    f.setValues(values);
7364         //});
7365                 
7366         return this;
7367     },
7368
7369     /**
7370      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7371      * they are returned as an array.
7372      * @param {Boolean} asString
7373      * @return {Object}
7374      */
7375     getValues : function(asString){
7376         //if (this.childForms) {
7377             // copy values from the child forms
7378         //    Roo.each(this.childForms, function (f) {
7379         //        this.setValues(f.getValues());
7380         //    }, this);
7381         //}
7382         
7383         
7384         
7385         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7386         if(asString === true){
7387             return fs;
7388         }
7389         return Roo.urlDecode(fs);
7390     },
7391     
7392     /**
7393      * Returns the fields in this form as an object with key/value pairs. 
7394      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7395      * @return {Object}
7396      */
7397     getFieldValues : function(with_hidden)
7398     {
7399         var items = this.getItems();
7400         var ret = {};
7401         items.each(function(f){
7402             if (!f.getName()) {
7403                 return;
7404             }
7405             var v = f.getValue();
7406             if (f.inputType =='radio') {
7407                 if (typeof(ret[f.getName()]) == 'undefined') {
7408                     ret[f.getName()] = ''; // empty..
7409                 }
7410                 
7411                 if (!f.el.dom.checked) {
7412                     return;
7413                     
7414                 }
7415                 v = f.el.dom.value;
7416                 
7417             }
7418             
7419             // not sure if this supported any more..
7420             if ((typeof(v) == 'object') && f.getRawValue) {
7421                 v = f.getRawValue() ; // dates..
7422             }
7423             // combo boxes where name != hiddenName...
7424             if (f.name != f.getName()) {
7425                 ret[f.name] = f.getRawValue();
7426             }
7427             ret[f.getName()] = v;
7428         });
7429         
7430         return ret;
7431     },
7432
7433     /**
7434      * Clears all invalid messages in this form.
7435      * @return {BasicForm} this
7436      */
7437     clearInvalid : function(){
7438         var items = this.getItems();
7439         
7440         items.each(function(f){
7441            f.clearInvalid();
7442         });
7443         
7444         
7445         
7446         return this;
7447     },
7448
7449     /**
7450      * Resets this form.
7451      * @return {BasicForm} this
7452      */
7453     reset : function(){
7454         var items = this.getItems();
7455         items.each(function(f){
7456             f.reset();
7457         });
7458         
7459         Roo.each(this.childForms || [], function (f) {
7460             f.reset();
7461         });
7462        
7463         
7464         return this;
7465     },
7466     getItems : function()
7467     {
7468         var r=new Roo.util.MixedCollection(false, function(o){
7469             return o.id || (o.id = Roo.id());
7470         });
7471         var iter = function(el) {
7472             if (el.inputEl) {
7473                 r.add(el);
7474             }
7475             if (!el.items) {
7476                 return;
7477             }
7478             Roo.each(el.items,function(e) {
7479                 iter(e);
7480             });
7481             
7482             
7483         };
7484         
7485         iter(this);
7486         return r;
7487         
7488         
7489         
7490         
7491     }
7492     
7493 });
7494
7495  
7496 /*
7497  * Based on:
7498  * Ext JS Library 1.1.1
7499  * Copyright(c) 2006-2007, Ext JS, LLC.
7500  *
7501  * Originally Released Under LGPL - original licence link has changed is not relivant.
7502  *
7503  * Fork - LGPL
7504  * <script type="text/javascript">
7505  */
7506 /**
7507  * @class Roo.form.VTypes
7508  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7509  * @singleton
7510  */
7511 Roo.form.VTypes = function(){
7512     // closure these in so they are only created once.
7513     var alpha = /^[a-zA-Z_]+$/;
7514     var alphanum = /^[a-zA-Z0-9_]+$/;
7515     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7516     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7517
7518     // All these messages and functions are configurable
7519     return {
7520         /**
7521          * The function used to validate email addresses
7522          * @param {String} value The email address
7523          */
7524         'email' : function(v){
7525             return email.test(v);
7526         },
7527         /**
7528          * The error text to display when the email validation function returns false
7529          * @type String
7530          */
7531         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7532         /**
7533          * The keystroke filter mask to be applied on email input
7534          * @type RegExp
7535          */
7536         'emailMask' : /[a-z0-9_\.\-@]/i,
7537
7538         /**
7539          * The function used to validate URLs
7540          * @param {String} value The URL
7541          */
7542         'url' : function(v){
7543             return url.test(v);
7544         },
7545         /**
7546          * The error text to display when the url validation function returns false
7547          * @type String
7548          */
7549         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7550         
7551         /**
7552          * The function used to validate alpha values
7553          * @param {String} value The value
7554          */
7555         'alpha' : function(v){
7556             return alpha.test(v);
7557         },
7558         /**
7559          * The error text to display when the alpha validation function returns false
7560          * @type String
7561          */
7562         'alphaText' : 'This field should only contain letters and _',
7563         /**
7564          * The keystroke filter mask to be applied on alpha input
7565          * @type RegExp
7566          */
7567         'alphaMask' : /[a-z_]/i,
7568
7569         /**
7570          * The function used to validate alphanumeric values
7571          * @param {String} value The value
7572          */
7573         'alphanum' : function(v){
7574             return alphanum.test(v);
7575         },
7576         /**
7577          * The error text to display when the alphanumeric validation function returns false
7578          * @type String
7579          */
7580         'alphanumText' : 'This field should only contain letters, numbers and _',
7581         /**
7582          * The keystroke filter mask to be applied on alphanumeric input
7583          * @type RegExp
7584          */
7585         'alphanumMask' : /[a-z0-9_]/i
7586     };
7587 }();/*
7588  * - LGPL
7589  *
7590  * Input
7591  * 
7592  */
7593
7594 /**
7595  * @class Roo.bootstrap.Input
7596  * @extends Roo.bootstrap.Component
7597  * Bootstrap Input class
7598  * @cfg {Boolean} disabled is it disabled
7599  * @cfg {String} fieldLabel - the label associated
7600  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7601  * @cfg {String} name name of the input
7602  * @cfg {string} fieldLabel - the label associated
7603  * @cfg {string}  inputType - input / file submit ...
7604  * @cfg {string} placeholder - placeholder to put in text.
7605  * @cfg {string}  before - input group add on before
7606  * @cfg {string} after - input group add on after
7607  * @cfg {string} size - (lg|sm) or leave empty..
7608  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7609  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7610  * @cfg {Number} md colspan out of 12 for computer-sized screens
7611  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7612  * @cfg {string} value default value of the input
7613  * @cfg {Number} labelWidth set the width of label (0-12)
7614  * @cfg {String} labelAlign (top|left)
7615  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7616  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7617
7618  * @cfg {String} align (left|center|right) Default left
7619  * @cfg {Boolean} forceFeedback (true|false) Default false
7620  * 
7621  * 
7622  * 
7623  * 
7624  * @constructor
7625  * Create a new Input
7626  * @param {Object} config The config object
7627  */
7628
7629 Roo.bootstrap.Input = function(config){
7630     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7631    
7632         this.addEvents({
7633             /**
7634              * @event focus
7635              * Fires when this field receives input focus.
7636              * @param {Roo.form.Field} this
7637              */
7638             focus : true,
7639             /**
7640              * @event blur
7641              * Fires when this field loses input focus.
7642              * @param {Roo.form.Field} this
7643              */
7644             blur : true,
7645             /**
7646              * @event specialkey
7647              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7648              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7649              * @param {Roo.form.Field} this
7650              * @param {Roo.EventObject} e The event object
7651              */
7652             specialkey : true,
7653             /**
7654              * @event change
7655              * Fires just before the field blurs if the field value has changed.
7656              * @param {Roo.form.Field} this
7657              * @param {Mixed} newValue The new value
7658              * @param {Mixed} oldValue The original value
7659              */
7660             change : true,
7661             /**
7662              * @event invalid
7663              * Fires after the field has been marked as invalid.
7664              * @param {Roo.form.Field} this
7665              * @param {String} msg The validation message
7666              */
7667             invalid : true,
7668             /**
7669              * @event valid
7670              * Fires after the field has been validated with no errors.
7671              * @param {Roo.form.Field} this
7672              */
7673             valid : true,
7674              /**
7675              * @event keyup
7676              * Fires after the key up
7677              * @param {Roo.form.Field} this
7678              * @param {Roo.EventObject}  e The event Object
7679              */
7680             keyup : true
7681         });
7682 };
7683
7684 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7685      /**
7686      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7687       automatic validation (defaults to "keyup").
7688      */
7689     validationEvent : "keyup",
7690      /**
7691      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7692      */
7693     validateOnBlur : true,
7694     /**
7695      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7696      */
7697     validationDelay : 250,
7698      /**
7699      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7700      */
7701     focusClass : "x-form-focus",  // not needed???
7702     
7703        
7704     /**
7705      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7706      */
7707     invalidClass : "has-warning",
7708     
7709     /**
7710      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7711      */
7712     validClass : "has-success",
7713     
7714     /**
7715      * @cfg {Boolean} hasFeedback (true|false) default true
7716      */
7717     hasFeedback : true,
7718     
7719     /**
7720      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7721      */
7722     invalidFeedbackClass : "glyphicon-warning-sign",
7723     
7724     /**
7725      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7726      */
7727     validFeedbackClass : "glyphicon-ok",
7728     
7729     /**
7730      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7731      */
7732     selectOnFocus : false,
7733     
7734      /**
7735      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7736      */
7737     maskRe : null,
7738        /**
7739      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7740      */
7741     vtype : null,
7742     
7743       /**
7744      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7745      */
7746     disableKeyFilter : false,
7747     
7748        /**
7749      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7750      */
7751     disabled : false,
7752      /**
7753      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7754      */
7755     allowBlank : true,
7756     /**
7757      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7758      */
7759     blankText : "This field is required",
7760     
7761      /**
7762      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7763      */
7764     minLength : 0,
7765     /**
7766      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7767      */
7768     maxLength : Number.MAX_VALUE,
7769     /**
7770      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7771      */
7772     minLengthText : "The minimum length for this field is {0}",
7773     /**
7774      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7775      */
7776     maxLengthText : "The maximum length for this field is {0}",
7777   
7778     
7779     /**
7780      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7781      * If available, this function will be called only after the basic validators all return true, and will be passed the
7782      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7783      */
7784     validator : null,
7785     /**
7786      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7787      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7788      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7789      */
7790     regex : null,
7791     /**
7792      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7793      */
7794     regexText : "",
7795     
7796     autocomplete: false,
7797     
7798     
7799     fieldLabel : '',
7800     inputType : 'text',
7801     
7802     name : false,
7803     placeholder: false,
7804     before : false,
7805     after : false,
7806     size : false,
7807     hasFocus : false,
7808     preventMark: false,
7809     isFormField : true,
7810     value : '',
7811     labelWidth : 2,
7812     labelAlign : false,
7813     readOnly : false,
7814     align : false,
7815     formatedValue : false,
7816     forceFeedback : false,
7817     
7818     parentLabelAlign : function()
7819     {
7820         var parent = this;
7821         while (parent.parent()) {
7822             parent = parent.parent();
7823             if (typeof(parent.labelAlign) !='undefined') {
7824                 return parent.labelAlign;
7825             }
7826         }
7827         return 'left';
7828         
7829     },
7830     
7831     getAutoCreate : function(){
7832         
7833         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7834         
7835         var id = Roo.id();
7836         
7837         var cfg = {};
7838         
7839         if(this.inputType != 'hidden'){
7840             cfg.cls = 'form-group' //input-group
7841         }
7842         
7843         var input =  {
7844             tag: 'input',
7845             id : id,
7846             type : this.inputType,
7847             value : this.value,
7848             cls : 'form-control',
7849             placeholder : this.placeholder || '',
7850             autocomplete : this.autocomplete || 'new-password'
7851         };
7852         
7853         
7854         if(this.align){
7855             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7856         }
7857         
7858         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7859             input.maxLength = this.maxLength;
7860         }
7861         
7862         if (this.disabled) {
7863             input.disabled=true;
7864         }
7865         
7866         if (this.readOnly) {
7867             input.readonly=true;
7868         }
7869         
7870         if (this.name) {
7871             input.name = this.name;
7872         }
7873         if (this.size) {
7874             input.cls += ' input-' + this.size;
7875         }
7876         var settings=this;
7877         ['xs','sm','md','lg'].map(function(size){
7878             if (settings[size]) {
7879                 cfg.cls += ' col-' + size + '-' + settings[size];
7880             }
7881         });
7882         
7883         var inputblock = input;
7884         
7885         var feedback = {
7886             tag: 'span',
7887             cls: 'glyphicon form-control-feedback'
7888         };
7889             
7890         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7891             
7892             inputblock = {
7893                 cls : 'has-feedback',
7894                 cn :  [
7895                     input,
7896                     feedback
7897                 ] 
7898             };  
7899         }
7900         
7901         if (this.before || this.after) {
7902             
7903             inputblock = {
7904                 cls : 'input-group',
7905                 cn :  [] 
7906             };
7907             
7908             if (this.before && typeof(this.before) == 'string') {
7909                 
7910                 inputblock.cn.push({
7911                     tag :'span',
7912                     cls : 'roo-input-before input-group-addon',
7913                     html : this.before
7914                 });
7915             }
7916             if (this.before && typeof(this.before) == 'object') {
7917                 this.before = Roo.factory(this.before);
7918                 Roo.log(this.before);
7919                 inputblock.cn.push({
7920                     tag :'span',
7921                     cls : 'roo-input-before input-group-' +
7922                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7923                 });
7924             }
7925             
7926             inputblock.cn.push(input);
7927             
7928             if (this.after && typeof(this.after) == 'string') {
7929                 inputblock.cn.push({
7930                     tag :'span',
7931                     cls : 'roo-input-after input-group-addon',
7932                     html : this.after
7933                 });
7934             }
7935             if (this.after && typeof(this.after) == 'object') {
7936                 this.after = Roo.factory(this.after);
7937                 Roo.log(this.after);
7938                 inputblock.cn.push({
7939                     tag :'span',
7940                     cls : 'roo-input-after input-group-' +
7941                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7942                 });
7943             }
7944             
7945             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7946                 inputblock.cls += ' has-feedback';
7947                 inputblock.cn.push(feedback);
7948             }
7949         };
7950         
7951         if (align ==='left' && this.fieldLabel.length) {
7952                 Roo.log("left and has label");
7953                 cfg.cn = [
7954                     
7955                     {
7956                         tag: 'label',
7957                         'for' :  id,
7958                         cls : 'control-label col-sm-' + this.labelWidth,
7959                         html : this.fieldLabel
7960                         
7961                     },
7962                     {
7963                         cls : "col-sm-" + (12 - this.labelWidth), 
7964                         cn: [
7965                             inputblock
7966                         ]
7967                     }
7968                     
7969                 ];
7970         } else if ( this.fieldLabel.length) {
7971                 Roo.log(" label");
7972                  cfg.cn = [
7973                    
7974                     {
7975                         tag: 'label',
7976                         //cls : 'input-group-addon',
7977                         html : this.fieldLabel
7978                         
7979                     },
7980                     
7981                     inputblock
7982                     
7983                 ];
7984
7985         } else {
7986             
7987                 Roo.log(" no label && no align");
7988                 cfg.cn = [
7989                     
7990                         inputblock
7991                     
7992                 ];
7993                 
7994                 
7995         };
7996         Roo.log('input-parentType: ' + this.parentType);
7997         
7998         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7999            cfg.cls += ' navbar-form';
8000            Roo.log(cfg);
8001         }
8002         
8003         return cfg;
8004         
8005     },
8006     /**
8007      * return the real input element.
8008      */
8009     inputEl: function ()
8010     {
8011         return this.el.select('input.form-control',true).first();
8012     },
8013     
8014     tooltipEl : function()
8015     {
8016         return this.inputEl();
8017     },
8018     
8019     setDisabled : function(v)
8020     {
8021         var i  = this.inputEl().dom;
8022         if (!v) {
8023             i.removeAttribute('disabled');
8024             return;
8025             
8026         }
8027         i.setAttribute('disabled','true');
8028     },
8029     initEvents : function()
8030     {
8031           
8032         this.inputEl().on("keydown" , this.fireKey,  this);
8033         this.inputEl().on("focus", this.onFocus,  this);
8034         this.inputEl().on("blur", this.onBlur,  this);
8035         
8036         this.inputEl().relayEvent('keyup', this);
8037  
8038         // reference to original value for reset
8039         this.originalValue = this.getValue();
8040         //Roo.form.TextField.superclass.initEvents.call(this);
8041         if(this.validationEvent == 'keyup'){
8042             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8043             this.inputEl().on('keyup', this.filterValidation, this);
8044         }
8045         else if(this.validationEvent !== false){
8046             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8047         }
8048         
8049         if(this.selectOnFocus){
8050             this.on("focus", this.preFocus, this);
8051             
8052         }
8053         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8054             this.inputEl().on("keypress", this.filterKeys, this);
8055         }
8056        /* if(this.grow){
8057             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8058             this.el.on("click", this.autoSize,  this);
8059         }
8060         */
8061         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8062             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8063         }
8064         
8065         if (typeof(this.before) == 'object') {
8066             this.before.render(this.el.select('.roo-input-before',true).first());
8067         }
8068         if (typeof(this.after) == 'object') {
8069             this.after.render(this.el.select('.roo-input-after',true).first());
8070         }
8071         
8072         
8073     },
8074     filterValidation : function(e){
8075         if(!e.isNavKeyPress()){
8076             this.validationTask.delay(this.validationDelay);
8077         }
8078     },
8079      /**
8080      * Validates the field value
8081      * @return {Boolean} True if the value is valid, else false
8082      */
8083     validate : function(){
8084         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8085         if(this.disabled || this.validateValue(this.getRawValue())){
8086             this.markValid();
8087             return true;
8088         }
8089         
8090         this.markInvalid();
8091         return false;
8092     },
8093     
8094     
8095     /**
8096      * Validates a value according to the field's validation rules and marks the field as invalid
8097      * if the validation fails
8098      * @param {Mixed} value The value to validate
8099      * @return {Boolean} True if the value is valid, else false
8100      */
8101     validateValue : function(value){
8102         if(value.length < 1)  { // if it's blank
8103             if(this.allowBlank){
8104                 return true;
8105             }
8106             return false;
8107         }
8108         
8109         if(value.length < this.minLength){
8110             return false;
8111         }
8112         if(value.length > this.maxLength){
8113             return false;
8114         }
8115         if(this.vtype){
8116             var vt = Roo.form.VTypes;
8117             if(!vt[this.vtype](value, this)){
8118                 return false;
8119             }
8120         }
8121         if(typeof this.validator == "function"){
8122             var msg = this.validator(value);
8123             if(msg !== true){
8124                 return false;
8125             }
8126         }
8127         
8128         if(this.regex && !this.regex.test(value)){
8129             return false;
8130         }
8131         
8132         return true;
8133     },
8134
8135     
8136     
8137      // private
8138     fireKey : function(e){
8139         //Roo.log('field ' + e.getKey());
8140         if(e.isNavKeyPress()){
8141             this.fireEvent("specialkey", this, e);
8142         }
8143     },
8144     focus : function (selectText){
8145         if(this.rendered){
8146             this.inputEl().focus();
8147             if(selectText === true){
8148                 this.inputEl().dom.select();
8149             }
8150         }
8151         return this;
8152     } ,
8153     
8154     onFocus : function(){
8155         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8156            // this.el.addClass(this.focusClass);
8157         }
8158         if(!this.hasFocus){
8159             this.hasFocus = true;
8160             this.startValue = this.getValue();
8161             this.fireEvent("focus", this);
8162         }
8163     },
8164     
8165     beforeBlur : Roo.emptyFn,
8166
8167     
8168     // private
8169     onBlur : function(){
8170         this.beforeBlur();
8171         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8172             //this.el.removeClass(this.focusClass);
8173         }
8174         this.hasFocus = false;
8175         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8176             this.validate();
8177         }
8178         var v = this.getValue();
8179         if(String(v) !== String(this.startValue)){
8180             this.fireEvent('change', this, v, this.startValue);
8181         }
8182         this.fireEvent("blur", this);
8183     },
8184     
8185     /**
8186      * Resets the current field value to the originally loaded value and clears any validation messages
8187      */
8188     reset : function(){
8189         this.setValue(this.originalValue);
8190         this.validate();
8191     },
8192      /**
8193      * Returns the name of the field
8194      * @return {Mixed} name The name field
8195      */
8196     getName: function(){
8197         return this.name;
8198     },
8199      /**
8200      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8201      * @return {Mixed} value The field value
8202      */
8203     getValue : function(){
8204         
8205         var v = this.inputEl().getValue();
8206         
8207         return v;
8208     },
8209     /**
8210      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8211      * @return {Mixed} value The field value
8212      */
8213     getRawValue : function(){
8214         var v = this.inputEl().getValue();
8215         
8216         return v;
8217     },
8218     
8219     /**
8220      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8221      * @param {Mixed} value The value to set
8222      */
8223     setRawValue : function(v){
8224         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8225     },
8226     
8227     selectText : function(start, end){
8228         var v = this.getRawValue();
8229         if(v.length > 0){
8230             start = start === undefined ? 0 : start;
8231             end = end === undefined ? v.length : end;
8232             var d = this.inputEl().dom;
8233             if(d.setSelectionRange){
8234                 d.setSelectionRange(start, end);
8235             }else if(d.createTextRange){
8236                 var range = d.createTextRange();
8237                 range.moveStart("character", start);
8238                 range.moveEnd("character", v.length-end);
8239                 range.select();
8240             }
8241         }
8242     },
8243     
8244     /**
8245      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8246      * @param {Mixed} value The value to set
8247      */
8248     setValue : function(v){
8249         this.value = v;
8250         if(this.rendered){
8251             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8252             this.validate();
8253         }
8254     },
8255     
8256     /*
8257     processValue : function(value){
8258         if(this.stripCharsRe){
8259             var newValue = value.replace(this.stripCharsRe, '');
8260             if(newValue !== value){
8261                 this.setRawValue(newValue);
8262                 return newValue;
8263             }
8264         }
8265         return value;
8266     },
8267   */
8268     preFocus : function(){
8269         
8270         if(this.selectOnFocus){
8271             this.inputEl().dom.select();
8272         }
8273     },
8274     filterKeys : function(e){
8275         var k = e.getKey();
8276         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8277             return;
8278         }
8279         var c = e.getCharCode(), cc = String.fromCharCode(c);
8280         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8281             return;
8282         }
8283         if(!this.maskRe.test(cc)){
8284             e.stopEvent();
8285         }
8286     },
8287      /**
8288      * Clear any invalid styles/messages for this field
8289      */
8290     clearInvalid : function(){
8291         
8292         if(!this.el || this.preventMark){ // not rendered
8293             return;
8294         }
8295         this.el.removeClass(this.invalidClass);
8296         
8297         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8298             
8299             var feedback = this.el.select('.form-control-feedback', true).first();
8300             
8301             if(feedback){
8302                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8303             }
8304             
8305         }
8306         
8307         this.fireEvent('valid', this);
8308     },
8309     
8310      /**
8311      * Mark this field as valid
8312      */
8313     markValid : function(){
8314         if(!this.el  || this.preventMark){ // not rendered
8315             return;
8316         }
8317         
8318         this.el.removeClass([this.invalidClass, this.validClass]);
8319         
8320         var feedback = this.el.select('.form-control-feedback', true).first();
8321             
8322         if(feedback){
8323             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8324         }
8325
8326         if(this.disabled || this.allowBlank){
8327             return;
8328         }
8329         
8330         this.el.addClass(this.validClass);
8331         
8332         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8333             
8334             var feedback = this.el.select('.form-control-feedback', true).first();
8335             
8336             if(feedback){
8337                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8338                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8339             }
8340             
8341         }
8342         
8343         this.fireEvent('valid', this);
8344     },
8345     
8346      /**
8347      * Mark this field as invalid
8348      * @param {String} msg The validation message
8349      */
8350     markInvalid : function(msg)
8351     {
8352         if(!this.el  || this.preventMark){ // not rendered
8353             return;
8354         }
8355         
8356         this.el.removeClass([this.invalidClass, this.validClass]);
8357         
8358         var feedback = this.el.select('.form-control-feedback', true).first();
8359             
8360         if(feedback){
8361             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8362         }
8363
8364         if(this.disabled || this.allowBlank){
8365             return;
8366         }
8367         
8368         this.el.addClass(this.invalidClass);
8369         
8370         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8371             
8372             var feedback = this.el.select('.form-control-feedback', true).first();
8373             
8374             if(feedback){
8375                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8376                 
8377                 if(this.getValue().length || this.forceFeedback){
8378                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8379                 }
8380                 
8381             }
8382             
8383         }
8384         
8385         this.fireEvent('invalid', this, msg);
8386     },
8387     // private
8388     SafariOnKeyDown : function(event)
8389     {
8390         // this is a workaround for a password hang bug on chrome/ webkit.
8391         
8392         var isSelectAll = false;
8393         
8394         if(this.inputEl().dom.selectionEnd > 0){
8395             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8396         }
8397         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8398             event.preventDefault();
8399             this.setValue('');
8400             return;
8401         }
8402         
8403         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8404             
8405             event.preventDefault();
8406             // this is very hacky as keydown always get's upper case.
8407             //
8408             var cc = String.fromCharCode(event.getCharCode());
8409             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8410             
8411         }
8412     },
8413     adjustWidth : function(tag, w){
8414         tag = tag.toLowerCase();
8415         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8416             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8417                 if(tag == 'input'){
8418                     return w + 2;
8419                 }
8420                 if(tag == 'textarea'){
8421                     return w-2;
8422                 }
8423             }else if(Roo.isOpera){
8424                 if(tag == 'input'){
8425                     return w + 2;
8426                 }
8427                 if(tag == 'textarea'){
8428                     return w-2;
8429                 }
8430             }
8431         }
8432         return w;
8433     }
8434     
8435 });
8436
8437  
8438 /*
8439  * - LGPL
8440  *
8441  * Input
8442  * 
8443  */
8444
8445 /**
8446  * @class Roo.bootstrap.TextArea
8447  * @extends Roo.bootstrap.Input
8448  * Bootstrap TextArea class
8449  * @cfg {Number} cols Specifies the visible width of a text area
8450  * @cfg {Number} rows Specifies the visible number of lines in a text area
8451  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8452  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8453  * @cfg {string} html text
8454  * 
8455  * @constructor
8456  * Create a new TextArea
8457  * @param {Object} config The config object
8458  */
8459
8460 Roo.bootstrap.TextArea = function(config){
8461     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8462    
8463 };
8464
8465 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8466      
8467     cols : false,
8468     rows : 5,
8469     readOnly : false,
8470     warp : 'soft',
8471     resize : false,
8472     value: false,
8473     html: false,
8474     
8475     getAutoCreate : function(){
8476         
8477         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8478         
8479         var id = Roo.id();
8480         
8481         var cfg = {};
8482         
8483         var input =  {
8484             tag: 'textarea',
8485             id : id,
8486             warp : this.warp,
8487             rows : this.rows,
8488             value : this.value || '',
8489             html: this.html || '',
8490             cls : 'form-control',
8491             placeholder : this.placeholder || '' 
8492             
8493         };
8494         
8495         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8496             input.maxLength = this.maxLength;
8497         }
8498         
8499         if(this.resize){
8500             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8501         }
8502         
8503         if(this.cols){
8504             input.cols = this.cols;
8505         }
8506         
8507         if (this.readOnly) {
8508             input.readonly = true;
8509         }
8510         
8511         if (this.name) {
8512             input.name = this.name;
8513         }
8514         
8515         if (this.size) {
8516             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8517         }
8518         
8519         var settings=this;
8520         ['xs','sm','md','lg'].map(function(size){
8521             if (settings[size]) {
8522                 cfg.cls += ' col-' + size + '-' + settings[size];
8523             }
8524         });
8525         
8526         var inputblock = input;
8527         
8528         if(this.hasFeedback && !this.allowBlank){
8529             
8530             var feedback = {
8531                 tag: 'span',
8532                 cls: 'glyphicon form-control-feedback'
8533             };
8534
8535             inputblock = {
8536                 cls : 'has-feedback',
8537                 cn :  [
8538                     input,
8539                     feedback
8540                 ] 
8541             };  
8542         }
8543         
8544         
8545         if (this.before || this.after) {
8546             
8547             inputblock = {
8548                 cls : 'input-group',
8549                 cn :  [] 
8550             };
8551             if (this.before) {
8552                 inputblock.cn.push({
8553                     tag :'span',
8554                     cls : 'input-group-addon',
8555                     html : this.before
8556                 });
8557             }
8558             
8559             inputblock.cn.push(input);
8560             
8561             if(this.hasFeedback && !this.allowBlank){
8562                 inputblock.cls += ' has-feedback';
8563                 inputblock.cn.push(feedback);
8564             }
8565             
8566             if (this.after) {
8567                 inputblock.cn.push({
8568                     tag :'span',
8569                     cls : 'input-group-addon',
8570                     html : this.after
8571                 });
8572             }
8573             
8574         }
8575         
8576         if (align ==='left' && this.fieldLabel.length) {
8577                 Roo.log("left and has label");
8578                 cfg.cn = [
8579                     
8580                     {
8581                         tag: 'label',
8582                         'for' :  id,
8583                         cls : 'control-label col-sm-' + this.labelWidth,
8584                         html : this.fieldLabel
8585                         
8586                     },
8587                     {
8588                         cls : "col-sm-" + (12 - this.labelWidth), 
8589                         cn: [
8590                             inputblock
8591                         ]
8592                     }
8593                     
8594                 ];
8595         } else if ( this.fieldLabel.length) {
8596                 Roo.log(" label");
8597                  cfg.cn = [
8598                    
8599                     {
8600                         tag: 'label',
8601                         //cls : 'input-group-addon',
8602                         html : this.fieldLabel
8603                         
8604                     },
8605                     
8606                     inputblock
8607                     
8608                 ];
8609
8610         } else {
8611             
8612                    Roo.log(" no label && no align");
8613                 cfg.cn = [
8614                     
8615                         inputblock
8616                     
8617                 ];
8618                 
8619                 
8620         }
8621         
8622         if (this.disabled) {
8623             input.disabled=true;
8624         }
8625         
8626         return cfg;
8627         
8628     },
8629     /**
8630      * return the real textarea element.
8631      */
8632     inputEl: function ()
8633     {
8634         return this.el.select('textarea.form-control',true).first();
8635     }
8636 });
8637
8638  
8639 /*
8640  * - LGPL
8641  *
8642  * trigger field - base class for combo..
8643  * 
8644  */
8645  
8646 /**
8647  * @class Roo.bootstrap.TriggerField
8648  * @extends Roo.bootstrap.Input
8649  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8650  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8651  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8652  * for which you can provide a custom implementation.  For example:
8653  * <pre><code>
8654 var trigger = new Roo.bootstrap.TriggerField();
8655 trigger.onTriggerClick = myTriggerFn;
8656 trigger.applyTo('my-field');
8657 </code></pre>
8658  *
8659  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8660  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8661  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8662  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8663  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8664
8665  * @constructor
8666  * Create a new TriggerField.
8667  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8668  * to the base TextField)
8669  */
8670 Roo.bootstrap.TriggerField = function(config){
8671     this.mimicing = false;
8672     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8673 };
8674
8675 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8676     /**
8677      * @cfg {String} triggerClass A CSS class to apply to the trigger
8678      */
8679      /**
8680      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8681      */
8682     hideTrigger:false,
8683
8684     /**
8685      * @cfg {Boolean} removable (true|false) special filter default false
8686      */
8687     removable : false,
8688     
8689     /** @cfg {Boolean} grow @hide */
8690     /** @cfg {Number} growMin @hide */
8691     /** @cfg {Number} growMax @hide */
8692
8693     /**
8694      * @hide 
8695      * @method
8696      */
8697     autoSize: Roo.emptyFn,
8698     // private
8699     monitorTab : true,
8700     // private
8701     deferHeight : true,
8702
8703     
8704     actionMode : 'wrap',
8705     
8706     caret : false,
8707     
8708     
8709     getAutoCreate : function(){
8710        
8711         var align = this.labelAlign || this.parentLabelAlign();
8712         
8713         var id = Roo.id();
8714         
8715         var cfg = {
8716             cls: 'form-group' //input-group
8717         };
8718         
8719         
8720         var input =  {
8721             tag: 'input',
8722             id : id,
8723             type : this.inputType,
8724             cls : 'form-control',
8725             autocomplete: 'new-password',
8726             placeholder : this.placeholder || '' 
8727             
8728         };
8729         if (this.name) {
8730             input.name = this.name;
8731         }
8732         if (this.size) {
8733             input.cls += ' input-' + this.size;
8734         }
8735         
8736         if (this.disabled) {
8737             input.disabled=true;
8738         }
8739         
8740         var inputblock = input;
8741         
8742         if(this.hasFeedback && !this.allowBlank){
8743             
8744             var feedback = {
8745                 tag: 'span',
8746                 cls: 'glyphicon form-control-feedback'
8747             };
8748             
8749             if(this.removable && !this.editable && !this.tickable){
8750                 inputblock = {
8751                     cls : 'has-feedback',
8752                     cn :  [
8753                         inputblock,
8754                         {
8755                             tag: 'button',
8756                             html : 'x',
8757                             cls : 'roo-combo-removable-btn close'
8758                         },
8759                         feedback
8760                     ] 
8761                 };
8762             } else {
8763                 inputblock = {
8764                     cls : 'has-feedback',
8765                     cn :  [
8766                         inputblock,
8767                         feedback
8768                     ] 
8769                 };
8770             }
8771
8772         } else {
8773             if(this.removable && !this.editable && !this.tickable){
8774                 inputblock = {
8775                     cls : 'roo-removable',
8776                     cn :  [
8777                         inputblock,
8778                         {
8779                             tag: 'button',
8780                             html : 'x',
8781                             cls : 'roo-combo-removable-btn close'
8782                         }
8783                     ] 
8784                 };
8785             }
8786         }
8787         
8788         if (this.before || this.after) {
8789             
8790             inputblock = {
8791                 cls : 'input-group',
8792                 cn :  [] 
8793             };
8794             if (this.before) {
8795                 inputblock.cn.push({
8796                     tag :'span',
8797                     cls : 'input-group-addon',
8798                     html : this.before
8799                 });
8800             }
8801             
8802             inputblock.cn.push(input);
8803             
8804             if(this.hasFeedback && !this.allowBlank){
8805                 inputblock.cls += ' has-feedback';
8806                 inputblock.cn.push(feedback);
8807             }
8808             
8809             if (this.after) {
8810                 inputblock.cn.push({
8811                     tag :'span',
8812                     cls : 'input-group-addon',
8813                     html : this.after
8814                 });
8815             }
8816             
8817         };
8818         
8819         var box = {
8820             tag: 'div',
8821             cn: [
8822                 {
8823                     tag: 'input',
8824                     type : 'hidden',
8825                     cls: 'form-hidden-field'
8826                 },
8827                 inputblock
8828             ]
8829             
8830         };
8831         
8832         if(this.multiple){
8833             Roo.log('multiple');
8834             
8835             box = {
8836                 tag: 'div',
8837                 cn: [
8838                     {
8839                         tag: 'input',
8840                         type : 'hidden',
8841                         cls: 'form-hidden-field'
8842                     },
8843                     {
8844                         tag: 'ul',
8845                         cls: 'select2-choices',
8846                         cn:[
8847                             {
8848                                 tag: 'li',
8849                                 cls: 'select2-search-field',
8850                                 cn: [
8851
8852                                     inputblock
8853                                 ]
8854                             }
8855                         ]
8856                     }
8857                 ]
8858             }
8859         };
8860         
8861         var combobox = {
8862             cls: 'select2-container input-group',
8863             cn: [
8864                 box
8865 //                {
8866 //                    tag: 'ul',
8867 //                    cls: 'typeahead typeahead-long dropdown-menu',
8868 //                    style: 'display:none'
8869 //                }
8870             ]
8871         };
8872         
8873         if(!this.multiple && this.showToggleBtn){
8874             
8875             var caret = {
8876                         tag: 'span',
8877                         cls: 'caret'
8878              };
8879             if (this.caret != false) {
8880                 caret = {
8881                      tag: 'i',
8882                      cls: 'fa fa-' + this.caret
8883                 };
8884                 
8885             }
8886             
8887             combobox.cn.push({
8888                 tag :'span',
8889                 cls : 'input-group-addon btn dropdown-toggle',
8890                 cn : [
8891                     caret,
8892                     {
8893                         tag: 'span',
8894                         cls: 'combobox-clear',
8895                         cn  : [
8896                             {
8897                                 tag : 'i',
8898                                 cls: 'icon-remove'
8899                             }
8900                         ]
8901                     }
8902                 ]
8903
8904             })
8905         }
8906         
8907         if(this.multiple){
8908             combobox.cls += ' select2-container-multi';
8909         }
8910         
8911         if (align ==='left' && this.fieldLabel.length) {
8912             
8913                 Roo.log("left and has label");
8914                 cfg.cn = [
8915                     
8916                     {
8917                         tag: 'label',
8918                         'for' :  id,
8919                         cls : 'control-label col-sm-' + this.labelWidth,
8920                         html : this.fieldLabel
8921                         
8922                     },
8923                     {
8924                         cls : "col-sm-" + (12 - this.labelWidth), 
8925                         cn: [
8926                             combobox
8927                         ]
8928                     }
8929                     
8930                 ];
8931         } else if ( this.fieldLabel.length) {
8932                 Roo.log(" label");
8933                  cfg.cn = [
8934                    
8935                     {
8936                         tag: 'label',
8937                         //cls : 'input-group-addon',
8938                         html : this.fieldLabel
8939                         
8940                     },
8941                     
8942                     combobox
8943                     
8944                 ];
8945
8946         } else {
8947             
8948                 Roo.log(" no label && no align");
8949                 cfg = combobox
8950                      
8951                 
8952         }
8953          
8954         var settings=this;
8955         ['xs','sm','md','lg'].map(function(size){
8956             if (settings[size]) {
8957                 cfg.cls += ' col-' + size + '-' + settings[size];
8958             }
8959         });
8960         Roo.log(cfg);
8961         return cfg;
8962         
8963     },
8964     
8965     
8966     
8967     // private
8968     onResize : function(w, h){
8969 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8970 //        if(typeof w == 'number'){
8971 //            var x = w - this.trigger.getWidth();
8972 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8973 //            this.trigger.setStyle('left', x+'px');
8974 //        }
8975     },
8976
8977     // private
8978     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8979
8980     // private
8981     getResizeEl : function(){
8982         return this.inputEl();
8983     },
8984
8985     // private
8986     getPositionEl : function(){
8987         return this.inputEl();
8988     },
8989
8990     // private
8991     alignErrorIcon : function(){
8992         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8993     },
8994
8995     // private
8996     initEvents : function(){
8997         
8998         this.createList();
8999         
9000         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9001         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9002         if(!this.multiple && this.showToggleBtn){
9003             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9004             if(this.hideTrigger){
9005                 this.trigger.setDisplayed(false);
9006             }
9007             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9008         }
9009         
9010         if(this.multiple){
9011             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9012         }
9013         
9014         if(this.removable && !this.editable && !this.tickable){
9015             var close = this.closeTriggerEl();
9016             
9017             if(close){
9018                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9019                 close.on('click', this.removeBtnClick, this, close);
9020             }
9021         }
9022         
9023         //this.trigger.addClassOnOver('x-form-trigger-over');
9024         //this.trigger.addClassOnClick('x-form-trigger-click');
9025         
9026         //if(!this.width){
9027         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9028         //}
9029     },
9030     
9031     closeTriggerEl : function()
9032     {
9033         var close = this.el.select('.roo-combo-removable-btn', true).first();
9034         return close ? close : false;
9035     },
9036     
9037     removeBtnClick : function(e, h, el)
9038     {
9039         e.preventDefault();
9040         
9041         if(this.fireEvent("remove", this) !== false){
9042             this.reset();
9043         }
9044     },
9045     
9046     createList : function()
9047     {
9048         this.list = Roo.get(document.body).createChild({
9049             tag: 'ul',
9050             cls: 'typeahead typeahead-long dropdown-menu',
9051             style: 'display:none'
9052         });
9053         
9054         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9055         
9056     },
9057
9058     // private
9059     initTrigger : function(){
9060        
9061     },
9062
9063     // private
9064     onDestroy : function(){
9065         if(this.trigger){
9066             this.trigger.removeAllListeners();
9067           //  this.trigger.remove();
9068         }
9069         //if(this.wrap){
9070         //    this.wrap.remove();
9071         //}
9072         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9073     },
9074
9075     // private
9076     onFocus : function(){
9077         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9078         /*
9079         if(!this.mimicing){
9080             this.wrap.addClass('x-trigger-wrap-focus');
9081             this.mimicing = true;
9082             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9083             if(this.monitorTab){
9084                 this.el.on("keydown", this.checkTab, this);
9085             }
9086         }
9087         */
9088     },
9089
9090     // private
9091     checkTab : function(e){
9092         if(e.getKey() == e.TAB){
9093             this.triggerBlur();
9094         }
9095     },
9096
9097     // private
9098     onBlur : function(){
9099         // do nothing
9100     },
9101
9102     // private
9103     mimicBlur : function(e, t){
9104         /*
9105         if(!this.wrap.contains(t) && this.validateBlur()){
9106             this.triggerBlur();
9107         }
9108         */
9109     },
9110
9111     // private
9112     triggerBlur : function(){
9113         this.mimicing = false;
9114         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9115         if(this.monitorTab){
9116             this.el.un("keydown", this.checkTab, this);
9117         }
9118         //this.wrap.removeClass('x-trigger-wrap-focus');
9119         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9120     },
9121
9122     // private
9123     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9124     validateBlur : function(e, t){
9125         return true;
9126     },
9127
9128     // private
9129     onDisable : function(){
9130         this.inputEl().dom.disabled = true;
9131         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9132         //if(this.wrap){
9133         //    this.wrap.addClass('x-item-disabled');
9134         //}
9135     },
9136
9137     // private
9138     onEnable : function(){
9139         this.inputEl().dom.disabled = false;
9140         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9141         //if(this.wrap){
9142         //    this.el.removeClass('x-item-disabled');
9143         //}
9144     },
9145
9146     // private
9147     onShow : function(){
9148         var ae = this.getActionEl();
9149         
9150         if(ae){
9151             ae.dom.style.display = '';
9152             ae.dom.style.visibility = 'visible';
9153         }
9154     },
9155
9156     // private
9157     
9158     onHide : function(){
9159         var ae = this.getActionEl();
9160         ae.dom.style.display = 'none';
9161     },
9162
9163     /**
9164      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9165      * by an implementing function.
9166      * @method
9167      * @param {EventObject} e
9168      */
9169     onTriggerClick : Roo.emptyFn
9170 });
9171  /*
9172  * Based on:
9173  * Ext JS Library 1.1.1
9174  * Copyright(c) 2006-2007, Ext JS, LLC.
9175  *
9176  * Originally Released Under LGPL - original licence link has changed is not relivant.
9177  *
9178  * Fork - LGPL
9179  * <script type="text/javascript">
9180  */
9181
9182
9183 /**
9184  * @class Roo.data.SortTypes
9185  * @singleton
9186  * Defines the default sorting (casting?) comparison functions used when sorting data.
9187  */
9188 Roo.data.SortTypes = {
9189     /**
9190      * Default sort that does nothing
9191      * @param {Mixed} s The value being converted
9192      * @return {Mixed} The comparison value
9193      */
9194     none : function(s){
9195         return s;
9196     },
9197     
9198     /**
9199      * The regular expression used to strip tags
9200      * @type {RegExp}
9201      * @property
9202      */
9203     stripTagsRE : /<\/?[^>]+>/gi,
9204     
9205     /**
9206      * Strips all HTML tags to sort on text only
9207      * @param {Mixed} s The value being converted
9208      * @return {String} The comparison value
9209      */
9210     asText : function(s){
9211         return String(s).replace(this.stripTagsRE, "");
9212     },
9213     
9214     /**
9215      * Strips all HTML tags to sort on text only - Case insensitive
9216      * @param {Mixed} s The value being converted
9217      * @return {String} The comparison value
9218      */
9219     asUCText : function(s){
9220         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9221     },
9222     
9223     /**
9224      * Case insensitive string
9225      * @param {Mixed} s The value being converted
9226      * @return {String} The comparison value
9227      */
9228     asUCString : function(s) {
9229         return String(s).toUpperCase();
9230     },
9231     
9232     /**
9233      * Date sorting
9234      * @param {Mixed} s The value being converted
9235      * @return {Number} The comparison value
9236      */
9237     asDate : function(s) {
9238         if(!s){
9239             return 0;
9240         }
9241         if(s instanceof Date){
9242             return s.getTime();
9243         }
9244         return Date.parse(String(s));
9245     },
9246     
9247     /**
9248      * Float sorting
9249      * @param {Mixed} s The value being converted
9250      * @return {Float} The comparison value
9251      */
9252     asFloat : function(s) {
9253         var val = parseFloat(String(s).replace(/,/g, ""));
9254         if(isNaN(val)) val = 0;
9255         return val;
9256     },
9257     
9258     /**
9259      * Integer sorting
9260      * @param {Mixed} s The value being converted
9261      * @return {Number} The comparison value
9262      */
9263     asInt : function(s) {
9264         var val = parseInt(String(s).replace(/,/g, ""));
9265         if(isNaN(val)) val = 0;
9266         return val;
9267     }
9268 };/*
9269  * Based on:
9270  * Ext JS Library 1.1.1
9271  * Copyright(c) 2006-2007, Ext JS, LLC.
9272  *
9273  * Originally Released Under LGPL - original licence link has changed is not relivant.
9274  *
9275  * Fork - LGPL
9276  * <script type="text/javascript">
9277  */
9278
9279 /**
9280 * @class Roo.data.Record
9281  * Instances of this class encapsulate both record <em>definition</em> information, and record
9282  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9283  * to access Records cached in an {@link Roo.data.Store} object.<br>
9284  * <p>
9285  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9286  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9287  * objects.<br>
9288  * <p>
9289  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9290  * @constructor
9291  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9292  * {@link #create}. The parameters are the same.
9293  * @param {Array} data An associative Array of data values keyed by the field name.
9294  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9295  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9296  * not specified an integer id is generated.
9297  */
9298 Roo.data.Record = function(data, id){
9299     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9300     this.data = data;
9301 };
9302
9303 /**
9304  * Generate a constructor for a specific record layout.
9305  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9306  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9307  * Each field definition object may contain the following properties: <ul>
9308  * <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,
9309  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9310  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9311  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9312  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9313  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9314  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9315  * this may be omitted.</p></li>
9316  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9317  * <ul><li>auto (Default, implies no conversion)</li>
9318  * <li>string</li>
9319  * <li>int</li>
9320  * <li>float</li>
9321  * <li>boolean</li>
9322  * <li>date</li></ul></p></li>
9323  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9324  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9325  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9326  * by the Reader into an object that will be stored in the Record. It is passed the
9327  * following parameters:<ul>
9328  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9329  * </ul></p></li>
9330  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9331  * </ul>
9332  * <br>usage:<br><pre><code>
9333 var TopicRecord = Roo.data.Record.create(
9334     {name: 'title', mapping: 'topic_title'},
9335     {name: 'author', mapping: 'username'},
9336     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9337     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9338     {name: 'lastPoster', mapping: 'user2'},
9339     {name: 'excerpt', mapping: 'post_text'}
9340 );
9341
9342 var myNewRecord = new TopicRecord({
9343     title: 'Do my job please',
9344     author: 'noobie',
9345     totalPosts: 1,
9346     lastPost: new Date(),
9347     lastPoster: 'Animal',
9348     excerpt: 'No way dude!'
9349 });
9350 myStore.add(myNewRecord);
9351 </code></pre>
9352  * @method create
9353  * @static
9354  */
9355 Roo.data.Record.create = function(o){
9356     var f = function(){
9357         f.superclass.constructor.apply(this, arguments);
9358     };
9359     Roo.extend(f, Roo.data.Record);
9360     var p = f.prototype;
9361     p.fields = new Roo.util.MixedCollection(false, function(field){
9362         return field.name;
9363     });
9364     for(var i = 0, len = o.length; i < len; i++){
9365         p.fields.add(new Roo.data.Field(o[i]));
9366     }
9367     f.getField = function(name){
9368         return p.fields.get(name);  
9369     };
9370     return f;
9371 };
9372
9373 Roo.data.Record.AUTO_ID = 1000;
9374 Roo.data.Record.EDIT = 'edit';
9375 Roo.data.Record.REJECT = 'reject';
9376 Roo.data.Record.COMMIT = 'commit';
9377
9378 Roo.data.Record.prototype = {
9379     /**
9380      * Readonly flag - true if this record has been modified.
9381      * @type Boolean
9382      */
9383     dirty : false,
9384     editing : false,
9385     error: null,
9386     modified: null,
9387
9388     // private
9389     join : function(store){
9390         this.store = store;
9391     },
9392
9393     /**
9394      * Set the named field to the specified value.
9395      * @param {String} name The name of the field to set.
9396      * @param {Object} value The value to set the field to.
9397      */
9398     set : function(name, value){
9399         if(this.data[name] == value){
9400             return;
9401         }
9402         this.dirty = true;
9403         if(!this.modified){
9404             this.modified = {};
9405         }
9406         if(typeof this.modified[name] == 'undefined'){
9407             this.modified[name] = this.data[name];
9408         }
9409         this.data[name] = value;
9410         if(!this.editing && this.store){
9411             this.store.afterEdit(this);
9412         }       
9413     },
9414
9415     /**
9416      * Get the value of the named field.
9417      * @param {String} name The name of the field to get the value of.
9418      * @return {Object} The value of the field.
9419      */
9420     get : function(name){
9421         return this.data[name]; 
9422     },
9423
9424     // private
9425     beginEdit : function(){
9426         this.editing = true;
9427         this.modified = {}; 
9428     },
9429
9430     // private
9431     cancelEdit : function(){
9432         this.editing = false;
9433         delete this.modified;
9434     },
9435
9436     // private
9437     endEdit : function(){
9438         this.editing = false;
9439         if(this.dirty && this.store){
9440             this.store.afterEdit(this);
9441         }
9442     },
9443
9444     /**
9445      * Usually called by the {@link Roo.data.Store} which owns the Record.
9446      * Rejects all changes made to the Record since either creation, or the last commit operation.
9447      * Modified fields are reverted to their original values.
9448      * <p>
9449      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9450      * of reject operations.
9451      */
9452     reject : function(){
9453         var m = this.modified;
9454         for(var n in m){
9455             if(typeof m[n] != "function"){
9456                 this.data[n] = m[n];
9457             }
9458         }
9459         this.dirty = false;
9460         delete this.modified;
9461         this.editing = false;
9462         if(this.store){
9463             this.store.afterReject(this);
9464         }
9465     },
9466
9467     /**
9468      * Usually called by the {@link Roo.data.Store} which owns the Record.
9469      * Commits all changes made to the Record since either creation, or the last commit operation.
9470      * <p>
9471      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9472      * of commit operations.
9473      */
9474     commit : function(){
9475         this.dirty = false;
9476         delete this.modified;
9477         this.editing = false;
9478         if(this.store){
9479             this.store.afterCommit(this);
9480         }
9481     },
9482
9483     // private
9484     hasError : function(){
9485         return this.error != null;
9486     },
9487
9488     // private
9489     clearError : function(){
9490         this.error = null;
9491     },
9492
9493     /**
9494      * Creates a copy of this record.
9495      * @param {String} id (optional) A new record id if you don't want to use this record's id
9496      * @return {Record}
9497      */
9498     copy : function(newId) {
9499         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9500     }
9501 };/*
9502  * Based on:
9503  * Ext JS Library 1.1.1
9504  * Copyright(c) 2006-2007, Ext JS, LLC.
9505  *
9506  * Originally Released Under LGPL - original licence link has changed is not relivant.
9507  *
9508  * Fork - LGPL
9509  * <script type="text/javascript">
9510  */
9511
9512
9513
9514 /**
9515  * @class Roo.data.Store
9516  * @extends Roo.util.Observable
9517  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9518  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9519  * <p>
9520  * 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
9521  * has no knowledge of the format of the data returned by the Proxy.<br>
9522  * <p>
9523  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9524  * instances from the data object. These records are cached and made available through accessor functions.
9525  * @constructor
9526  * Creates a new Store.
9527  * @param {Object} config A config object containing the objects needed for the Store to access data,
9528  * and read the data into Records.
9529  */
9530 Roo.data.Store = function(config){
9531     this.data = new Roo.util.MixedCollection(false);
9532     this.data.getKey = function(o){
9533         return o.id;
9534     };
9535     this.baseParams = {};
9536     // private
9537     this.paramNames = {
9538         "start" : "start",
9539         "limit" : "limit",
9540         "sort" : "sort",
9541         "dir" : "dir",
9542         "multisort" : "_multisort"
9543     };
9544
9545     if(config && config.data){
9546         this.inlineData = config.data;
9547         delete config.data;
9548     }
9549
9550     Roo.apply(this, config);
9551     
9552     if(this.reader){ // reader passed
9553         this.reader = Roo.factory(this.reader, Roo.data);
9554         this.reader.xmodule = this.xmodule || false;
9555         if(!this.recordType){
9556             this.recordType = this.reader.recordType;
9557         }
9558         if(this.reader.onMetaChange){
9559             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9560         }
9561     }
9562
9563     if(this.recordType){
9564         this.fields = this.recordType.prototype.fields;
9565     }
9566     this.modified = [];
9567
9568     this.addEvents({
9569         /**
9570          * @event datachanged
9571          * Fires when the data cache has changed, and a widget which is using this Store
9572          * as a Record cache should refresh its view.
9573          * @param {Store} this
9574          */
9575         datachanged : true,
9576         /**
9577          * @event metachange
9578          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9579          * @param {Store} this
9580          * @param {Object} meta The JSON metadata
9581          */
9582         metachange : true,
9583         /**
9584          * @event add
9585          * Fires when Records have been added to the Store
9586          * @param {Store} this
9587          * @param {Roo.data.Record[]} records The array of Records added
9588          * @param {Number} index The index at which the record(s) were added
9589          */
9590         add : true,
9591         /**
9592          * @event remove
9593          * Fires when a Record has been removed from the Store
9594          * @param {Store} this
9595          * @param {Roo.data.Record} record The Record that was removed
9596          * @param {Number} index The index at which the record was removed
9597          */
9598         remove : true,
9599         /**
9600          * @event update
9601          * Fires when a Record has been updated
9602          * @param {Store} this
9603          * @param {Roo.data.Record} record The Record that was updated
9604          * @param {String} operation The update operation being performed.  Value may be one of:
9605          * <pre><code>
9606  Roo.data.Record.EDIT
9607  Roo.data.Record.REJECT
9608  Roo.data.Record.COMMIT
9609          * </code></pre>
9610          */
9611         update : true,
9612         /**
9613          * @event clear
9614          * Fires when the data cache has been cleared.
9615          * @param {Store} this
9616          */
9617         clear : true,
9618         /**
9619          * @event beforeload
9620          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9621          * the load action will be canceled.
9622          * @param {Store} this
9623          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9624          */
9625         beforeload : true,
9626         /**
9627          * @event beforeloadadd
9628          * Fires after a new set of Records has been loaded.
9629          * @param {Store} this
9630          * @param {Roo.data.Record[]} records The Records that were loaded
9631          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9632          */
9633         beforeloadadd : true,
9634         /**
9635          * @event load
9636          * Fires after a new set of Records has been loaded, before they are added to the store.
9637          * @param {Store} this
9638          * @param {Roo.data.Record[]} records The Records that were loaded
9639          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9640          * @params {Object} return from reader
9641          */
9642         load : true,
9643         /**
9644          * @event loadexception
9645          * Fires if an exception occurs in the Proxy during loading.
9646          * Called with the signature of the Proxy's "loadexception" event.
9647          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9648          * 
9649          * @param {Proxy} 
9650          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9651          * @param {Object} load options 
9652          * @param {Object} jsonData from your request (normally this contains the Exception)
9653          */
9654         loadexception : true
9655     });
9656     
9657     if(this.proxy){
9658         this.proxy = Roo.factory(this.proxy, Roo.data);
9659         this.proxy.xmodule = this.xmodule || false;
9660         this.relayEvents(this.proxy,  ["loadexception"]);
9661     }
9662     this.sortToggle = {};
9663     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9664
9665     Roo.data.Store.superclass.constructor.call(this);
9666
9667     if(this.inlineData){
9668         this.loadData(this.inlineData);
9669         delete this.inlineData;
9670     }
9671 };
9672
9673 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9674      /**
9675     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9676     * without a remote query - used by combo/forms at present.
9677     */
9678     
9679     /**
9680     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9681     */
9682     /**
9683     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9684     */
9685     /**
9686     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9687     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9688     */
9689     /**
9690     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9691     * on any HTTP request
9692     */
9693     /**
9694     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9695     */
9696     /**
9697     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9698     */
9699     multiSort: false,
9700     /**
9701     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9702     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9703     */
9704     remoteSort : false,
9705
9706     /**
9707     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9708      * loaded or when a record is removed. (defaults to false).
9709     */
9710     pruneModifiedRecords : false,
9711
9712     // private
9713     lastOptions : null,
9714
9715     /**
9716      * Add Records to the Store and fires the add event.
9717      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9718      */
9719     add : function(records){
9720         records = [].concat(records);
9721         for(var i = 0, len = records.length; i < len; i++){
9722             records[i].join(this);
9723         }
9724         var index = this.data.length;
9725         this.data.addAll(records);
9726         this.fireEvent("add", this, records, index);
9727     },
9728
9729     /**
9730      * Remove a Record from the Store and fires the remove event.
9731      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9732      */
9733     remove : function(record){
9734         var index = this.data.indexOf(record);
9735         this.data.removeAt(index);
9736         if(this.pruneModifiedRecords){
9737             this.modified.remove(record);
9738         }
9739         this.fireEvent("remove", this, record, index);
9740     },
9741
9742     /**
9743      * Remove all Records from the Store and fires the clear event.
9744      */
9745     removeAll : function(){
9746         this.data.clear();
9747         if(this.pruneModifiedRecords){
9748             this.modified = [];
9749         }
9750         this.fireEvent("clear", this);
9751     },
9752
9753     /**
9754      * Inserts Records to the Store at the given index and fires the add event.
9755      * @param {Number} index The start index at which to insert the passed Records.
9756      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9757      */
9758     insert : function(index, records){
9759         records = [].concat(records);
9760         for(var i = 0, len = records.length; i < len; i++){
9761             this.data.insert(index, records[i]);
9762             records[i].join(this);
9763         }
9764         this.fireEvent("add", this, records, index);
9765     },
9766
9767     /**
9768      * Get the index within the cache of the passed Record.
9769      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9770      * @return {Number} The index of the passed Record. Returns -1 if not found.
9771      */
9772     indexOf : function(record){
9773         return this.data.indexOf(record);
9774     },
9775
9776     /**
9777      * Get the index within the cache of the Record with the passed id.
9778      * @param {String} id The id of the Record to find.
9779      * @return {Number} The index of the Record. Returns -1 if not found.
9780      */
9781     indexOfId : function(id){
9782         return this.data.indexOfKey(id);
9783     },
9784
9785     /**
9786      * Get the Record with the specified id.
9787      * @param {String} id The id of the Record to find.
9788      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9789      */
9790     getById : function(id){
9791         return this.data.key(id);
9792     },
9793
9794     /**
9795      * Get the Record at the specified index.
9796      * @param {Number} index The index of the Record to find.
9797      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9798      */
9799     getAt : function(index){
9800         return this.data.itemAt(index);
9801     },
9802
9803     /**
9804      * Returns a range of Records between specified indices.
9805      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9806      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9807      * @return {Roo.data.Record[]} An array of Records
9808      */
9809     getRange : function(start, end){
9810         return this.data.getRange(start, end);
9811     },
9812
9813     // private
9814     storeOptions : function(o){
9815         o = Roo.apply({}, o);
9816         delete o.callback;
9817         delete o.scope;
9818         this.lastOptions = o;
9819     },
9820
9821     /**
9822      * Loads the Record cache from the configured Proxy using the configured Reader.
9823      * <p>
9824      * If using remote paging, then the first load call must specify the <em>start</em>
9825      * and <em>limit</em> properties in the options.params property to establish the initial
9826      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9827      * <p>
9828      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9829      * and this call will return before the new data has been loaded. Perform any post-processing
9830      * in a callback function, or in a "load" event handler.</strong>
9831      * <p>
9832      * @param {Object} options An object containing properties which control loading options:<ul>
9833      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9834      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9835      * passed the following arguments:<ul>
9836      * <li>r : Roo.data.Record[]</li>
9837      * <li>options: Options object from the load call</li>
9838      * <li>success: Boolean success indicator</li></ul></li>
9839      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9840      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9841      * </ul>
9842      */
9843     load : function(options){
9844         options = options || {};
9845         if(this.fireEvent("beforeload", this, options) !== false){
9846             this.storeOptions(options);
9847             var p = Roo.apply(options.params || {}, this.baseParams);
9848             // if meta was not loaded from remote source.. try requesting it.
9849             if (!this.reader.metaFromRemote) {
9850                 p._requestMeta = 1;
9851             }
9852             if(this.sortInfo && this.remoteSort){
9853                 var pn = this.paramNames;
9854                 p[pn["sort"]] = this.sortInfo.field;
9855                 p[pn["dir"]] = this.sortInfo.direction;
9856             }
9857             if (this.multiSort) {
9858                 var pn = this.paramNames;
9859                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9860             }
9861             
9862             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9863         }
9864     },
9865
9866     /**
9867      * Reloads the Record cache from the configured Proxy using the configured Reader and
9868      * the options from the last load operation performed.
9869      * @param {Object} options (optional) An object containing properties which may override the options
9870      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9871      * the most recently used options are reused).
9872      */
9873     reload : function(options){
9874         this.load(Roo.applyIf(options||{}, this.lastOptions));
9875     },
9876
9877     // private
9878     // Called as a callback by the Reader during a load operation.
9879     loadRecords : function(o, options, success){
9880         if(!o || success === false){
9881             if(success !== false){
9882                 this.fireEvent("load", this, [], options, o);
9883             }
9884             if(options.callback){
9885                 options.callback.call(options.scope || this, [], options, false);
9886             }
9887             return;
9888         }
9889         // if data returned failure - throw an exception.
9890         if (o.success === false) {
9891             // show a message if no listener is registered.
9892             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9893                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9894             }
9895             // loadmask wil be hooked into this..
9896             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9897             return;
9898         }
9899         var r = o.records, t = o.totalRecords || r.length;
9900         
9901         this.fireEvent("beforeloadadd", this, r, options, o);
9902         
9903         if(!options || options.add !== true){
9904             if(this.pruneModifiedRecords){
9905                 this.modified = [];
9906             }
9907             for(var i = 0, len = r.length; i < len; i++){
9908                 r[i].join(this);
9909             }
9910             if(this.snapshot){
9911                 this.data = this.snapshot;
9912                 delete this.snapshot;
9913             }
9914             this.data.clear();
9915             this.data.addAll(r);
9916             this.totalLength = t;
9917             this.applySort();
9918             this.fireEvent("datachanged", this);
9919         }else{
9920             this.totalLength = Math.max(t, this.data.length+r.length);
9921             this.add(r);
9922         }
9923         this.fireEvent("load", this, r, options, o);
9924         if(options.callback){
9925             options.callback.call(options.scope || this, r, options, true);
9926         }
9927     },
9928
9929
9930     /**
9931      * Loads data from a passed data block. A Reader which understands the format of the data
9932      * must have been configured in the constructor.
9933      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9934      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9935      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9936      */
9937     loadData : function(o, append){
9938         var r = this.reader.readRecords(o);
9939         this.loadRecords(r, {add: append}, true);
9940     },
9941
9942     /**
9943      * Gets the number of cached records.
9944      * <p>
9945      * <em>If using paging, this may not be the total size of the dataset. If the data object
9946      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9947      * the data set size</em>
9948      */
9949     getCount : function(){
9950         return this.data.length || 0;
9951     },
9952
9953     /**
9954      * Gets the total number of records in the dataset as returned by the server.
9955      * <p>
9956      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9957      * the dataset size</em>
9958      */
9959     getTotalCount : function(){
9960         return this.totalLength || 0;
9961     },
9962
9963     /**
9964      * Returns the sort state of the Store as an object with two properties:
9965      * <pre><code>
9966  field {String} The name of the field by which the Records are sorted
9967  direction {String} The sort order, "ASC" or "DESC"
9968      * </code></pre>
9969      */
9970     getSortState : function(){
9971         return this.sortInfo;
9972     },
9973
9974     // private
9975     applySort : function(){
9976         if(this.sortInfo && !this.remoteSort){
9977             var s = this.sortInfo, f = s.field;
9978             var st = this.fields.get(f).sortType;
9979             var fn = function(r1, r2){
9980                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9981                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9982             };
9983             this.data.sort(s.direction, fn);
9984             if(this.snapshot && this.snapshot != this.data){
9985                 this.snapshot.sort(s.direction, fn);
9986             }
9987         }
9988     },
9989
9990     /**
9991      * Sets the default sort column and order to be used by the next load operation.
9992      * @param {String} fieldName The name of the field to sort by.
9993      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9994      */
9995     setDefaultSort : function(field, dir){
9996         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9997     },
9998
9999     /**
10000      * Sort the Records.
10001      * If remote sorting is used, the sort is performed on the server, and the cache is
10002      * reloaded. If local sorting is used, the cache is sorted internally.
10003      * @param {String} fieldName The name of the field to sort by.
10004      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10005      */
10006     sort : function(fieldName, dir){
10007         var f = this.fields.get(fieldName);
10008         if(!dir){
10009             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10010             
10011             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10012                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10013             }else{
10014                 dir = f.sortDir;
10015             }
10016         }
10017         this.sortToggle[f.name] = dir;
10018         this.sortInfo = {field: f.name, direction: dir};
10019         if(!this.remoteSort){
10020             this.applySort();
10021             this.fireEvent("datachanged", this);
10022         }else{
10023             this.load(this.lastOptions);
10024         }
10025     },
10026
10027     /**
10028      * Calls the specified function for each of the Records in the cache.
10029      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10030      * Returning <em>false</em> aborts and exits the iteration.
10031      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10032      */
10033     each : function(fn, scope){
10034         this.data.each(fn, scope);
10035     },
10036
10037     /**
10038      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10039      * (e.g., during paging).
10040      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10041      */
10042     getModifiedRecords : function(){
10043         return this.modified;
10044     },
10045
10046     // private
10047     createFilterFn : function(property, value, anyMatch){
10048         if(!value.exec){ // not a regex
10049             value = String(value);
10050             if(value.length == 0){
10051                 return false;
10052             }
10053             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10054         }
10055         return function(r){
10056             return value.test(r.data[property]);
10057         };
10058     },
10059
10060     /**
10061      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10062      * @param {String} property A field on your records
10063      * @param {Number} start The record index to start at (defaults to 0)
10064      * @param {Number} end The last record index to include (defaults to length - 1)
10065      * @return {Number} The sum
10066      */
10067     sum : function(property, start, end){
10068         var rs = this.data.items, v = 0;
10069         start = start || 0;
10070         end = (end || end === 0) ? end : rs.length-1;
10071
10072         for(var i = start; i <= end; i++){
10073             v += (rs[i].data[property] || 0);
10074         }
10075         return v;
10076     },
10077
10078     /**
10079      * Filter the records by a specified property.
10080      * @param {String} field A field on your records
10081      * @param {String/RegExp} value Either a string that the field
10082      * should start with or a RegExp to test against the field
10083      * @param {Boolean} anyMatch True to match any part not just the beginning
10084      */
10085     filter : function(property, value, anyMatch){
10086         var fn = this.createFilterFn(property, value, anyMatch);
10087         return fn ? this.filterBy(fn) : this.clearFilter();
10088     },
10089
10090     /**
10091      * Filter by a function. The specified function will be called with each
10092      * record in this data source. If the function returns true the record is included,
10093      * otherwise it is filtered.
10094      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10095      * @param {Object} scope (optional) The scope of the function (defaults to this)
10096      */
10097     filterBy : function(fn, scope){
10098         this.snapshot = this.snapshot || this.data;
10099         this.data = this.queryBy(fn, scope||this);
10100         this.fireEvent("datachanged", this);
10101     },
10102
10103     /**
10104      * Query the records by a specified property.
10105      * @param {String} field A field on your records
10106      * @param {String/RegExp} value Either a string that the field
10107      * should start with or a RegExp to test against the field
10108      * @param {Boolean} anyMatch True to match any part not just the beginning
10109      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10110      */
10111     query : function(property, value, anyMatch){
10112         var fn = this.createFilterFn(property, value, anyMatch);
10113         return fn ? this.queryBy(fn) : this.data.clone();
10114     },
10115
10116     /**
10117      * Query by a function. The specified function will be called with each
10118      * record in this data source. If the function returns true the record is included
10119      * in the results.
10120      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10121      * @param {Object} scope (optional) The scope of the function (defaults to this)
10122       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10123      **/
10124     queryBy : function(fn, scope){
10125         var data = this.snapshot || this.data;
10126         return data.filterBy(fn, scope||this);
10127     },
10128
10129     /**
10130      * Collects unique values for a particular dataIndex from this store.
10131      * @param {String} dataIndex The property to collect
10132      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10133      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10134      * @return {Array} An array of the unique values
10135      **/
10136     collect : function(dataIndex, allowNull, bypassFilter){
10137         var d = (bypassFilter === true && this.snapshot) ?
10138                 this.snapshot.items : this.data.items;
10139         var v, sv, r = [], l = {};
10140         for(var i = 0, len = d.length; i < len; i++){
10141             v = d[i].data[dataIndex];
10142             sv = String(v);
10143             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10144                 l[sv] = true;
10145                 r[r.length] = v;
10146             }
10147         }
10148         return r;
10149     },
10150
10151     /**
10152      * Revert to a view of the Record cache with no filtering applied.
10153      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10154      */
10155     clearFilter : function(suppressEvent){
10156         if(this.snapshot && this.snapshot != this.data){
10157             this.data = this.snapshot;
10158             delete this.snapshot;
10159             if(suppressEvent !== true){
10160                 this.fireEvent("datachanged", this);
10161             }
10162         }
10163     },
10164
10165     // private
10166     afterEdit : function(record){
10167         if(this.modified.indexOf(record) == -1){
10168             this.modified.push(record);
10169         }
10170         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10171     },
10172     
10173     // private
10174     afterReject : function(record){
10175         this.modified.remove(record);
10176         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10177     },
10178
10179     // private
10180     afterCommit : function(record){
10181         this.modified.remove(record);
10182         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10183     },
10184
10185     /**
10186      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10187      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10188      */
10189     commitChanges : function(){
10190         var m = this.modified.slice(0);
10191         this.modified = [];
10192         for(var i = 0, len = m.length; i < len; i++){
10193             m[i].commit();
10194         }
10195     },
10196
10197     /**
10198      * Cancel outstanding changes on all changed records.
10199      */
10200     rejectChanges : function(){
10201         var m = this.modified.slice(0);
10202         this.modified = [];
10203         for(var i = 0, len = m.length; i < len; i++){
10204             m[i].reject();
10205         }
10206     },
10207
10208     onMetaChange : function(meta, rtype, o){
10209         this.recordType = rtype;
10210         this.fields = rtype.prototype.fields;
10211         delete this.snapshot;
10212         this.sortInfo = meta.sortInfo || this.sortInfo;
10213         this.modified = [];
10214         this.fireEvent('metachange', this, this.reader.meta);
10215     },
10216     
10217     moveIndex : function(data, type)
10218     {
10219         var index = this.indexOf(data);
10220         
10221         var newIndex = index + type;
10222         
10223         this.remove(data);
10224         
10225         this.insert(newIndex, data);
10226         
10227     }
10228 });/*
10229  * Based on:
10230  * Ext JS Library 1.1.1
10231  * Copyright(c) 2006-2007, Ext JS, LLC.
10232  *
10233  * Originally Released Under LGPL - original licence link has changed is not relivant.
10234  *
10235  * Fork - LGPL
10236  * <script type="text/javascript">
10237  */
10238
10239 /**
10240  * @class Roo.data.SimpleStore
10241  * @extends Roo.data.Store
10242  * Small helper class to make creating Stores from Array data easier.
10243  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10244  * @cfg {Array} fields An array of field definition objects, or field name strings.
10245  * @cfg {Array} data The multi-dimensional array of data
10246  * @constructor
10247  * @param {Object} config
10248  */
10249 Roo.data.SimpleStore = function(config){
10250     Roo.data.SimpleStore.superclass.constructor.call(this, {
10251         isLocal : true,
10252         reader: new Roo.data.ArrayReader({
10253                 id: config.id
10254             },
10255             Roo.data.Record.create(config.fields)
10256         ),
10257         proxy : new Roo.data.MemoryProxy(config.data)
10258     });
10259     this.load();
10260 };
10261 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10262  * Based on:
10263  * Ext JS Library 1.1.1
10264  * Copyright(c) 2006-2007, Ext JS, LLC.
10265  *
10266  * Originally Released Under LGPL - original licence link has changed is not relivant.
10267  *
10268  * Fork - LGPL
10269  * <script type="text/javascript">
10270  */
10271
10272 /**
10273 /**
10274  * @extends Roo.data.Store
10275  * @class Roo.data.JsonStore
10276  * Small helper class to make creating Stores for JSON data easier. <br/>
10277 <pre><code>
10278 var store = new Roo.data.JsonStore({
10279     url: 'get-images.php',
10280     root: 'images',
10281     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10282 });
10283 </code></pre>
10284  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10285  * JsonReader and HttpProxy (unless inline data is provided).</b>
10286  * @cfg {Array} fields An array of field definition objects, or field name strings.
10287  * @constructor
10288  * @param {Object} config
10289  */
10290 Roo.data.JsonStore = function(c){
10291     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10292         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10293         reader: new Roo.data.JsonReader(c, c.fields)
10294     }));
10295 };
10296 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10297  * Based on:
10298  * Ext JS Library 1.1.1
10299  * Copyright(c) 2006-2007, Ext JS, LLC.
10300  *
10301  * Originally Released Under LGPL - original licence link has changed is not relivant.
10302  *
10303  * Fork - LGPL
10304  * <script type="text/javascript">
10305  */
10306
10307  
10308 Roo.data.Field = function(config){
10309     if(typeof config == "string"){
10310         config = {name: config};
10311     }
10312     Roo.apply(this, config);
10313     
10314     if(!this.type){
10315         this.type = "auto";
10316     }
10317     
10318     var st = Roo.data.SortTypes;
10319     // named sortTypes are supported, here we look them up
10320     if(typeof this.sortType == "string"){
10321         this.sortType = st[this.sortType];
10322     }
10323     
10324     // set default sortType for strings and dates
10325     if(!this.sortType){
10326         switch(this.type){
10327             case "string":
10328                 this.sortType = st.asUCString;
10329                 break;
10330             case "date":
10331                 this.sortType = st.asDate;
10332                 break;
10333             default:
10334                 this.sortType = st.none;
10335         }
10336     }
10337
10338     // define once
10339     var stripRe = /[\$,%]/g;
10340
10341     // prebuilt conversion function for this field, instead of
10342     // switching every time we're reading a value
10343     if(!this.convert){
10344         var cv, dateFormat = this.dateFormat;
10345         switch(this.type){
10346             case "":
10347             case "auto":
10348             case undefined:
10349                 cv = function(v){ return v; };
10350                 break;
10351             case "string":
10352                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10353                 break;
10354             case "int":
10355                 cv = function(v){
10356                     return v !== undefined && v !== null && v !== '' ?
10357                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10358                     };
10359                 break;
10360             case "float":
10361                 cv = function(v){
10362                     return v !== undefined && v !== null && v !== '' ?
10363                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10364                     };
10365                 break;
10366             case "bool":
10367             case "boolean":
10368                 cv = function(v){ return v === true || v === "true" || v == 1; };
10369                 break;
10370             case "date":
10371                 cv = function(v){
10372                     if(!v){
10373                         return '';
10374                     }
10375                     if(v instanceof Date){
10376                         return v;
10377                     }
10378                     if(dateFormat){
10379                         if(dateFormat == "timestamp"){
10380                             return new Date(v*1000);
10381                         }
10382                         return Date.parseDate(v, dateFormat);
10383                     }
10384                     var parsed = Date.parse(v);
10385                     return parsed ? new Date(parsed) : null;
10386                 };
10387              break;
10388             
10389         }
10390         this.convert = cv;
10391     }
10392 };
10393
10394 Roo.data.Field.prototype = {
10395     dateFormat: null,
10396     defaultValue: "",
10397     mapping: null,
10398     sortType : null,
10399     sortDir : "ASC"
10400 };/*
10401  * Based on:
10402  * Ext JS Library 1.1.1
10403  * Copyright(c) 2006-2007, Ext JS, LLC.
10404  *
10405  * Originally Released Under LGPL - original licence link has changed is not relivant.
10406  *
10407  * Fork - LGPL
10408  * <script type="text/javascript">
10409  */
10410  
10411 // Base class for reading structured data from a data source.  This class is intended to be
10412 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10413
10414 /**
10415  * @class Roo.data.DataReader
10416  * Base class for reading structured data from a data source.  This class is intended to be
10417  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10418  */
10419
10420 Roo.data.DataReader = function(meta, recordType){
10421     
10422     this.meta = meta;
10423     
10424     this.recordType = recordType instanceof Array ? 
10425         Roo.data.Record.create(recordType) : recordType;
10426 };
10427
10428 Roo.data.DataReader.prototype = {
10429      /**
10430      * Create an empty record
10431      * @param {Object} data (optional) - overlay some values
10432      * @return {Roo.data.Record} record created.
10433      */
10434     newRow :  function(d) {
10435         var da =  {};
10436         this.recordType.prototype.fields.each(function(c) {
10437             switch( c.type) {
10438                 case 'int' : da[c.name] = 0; break;
10439                 case 'date' : da[c.name] = new Date(); break;
10440                 case 'float' : da[c.name] = 0.0; break;
10441                 case 'boolean' : da[c.name] = false; break;
10442                 default : da[c.name] = ""; break;
10443             }
10444             
10445         });
10446         return new this.recordType(Roo.apply(da, d));
10447     }
10448     
10449 };/*
10450  * Based on:
10451  * Ext JS Library 1.1.1
10452  * Copyright(c) 2006-2007, Ext JS, LLC.
10453  *
10454  * Originally Released Under LGPL - original licence link has changed is not relivant.
10455  *
10456  * Fork - LGPL
10457  * <script type="text/javascript">
10458  */
10459
10460 /**
10461  * @class Roo.data.DataProxy
10462  * @extends Roo.data.Observable
10463  * This class is an abstract base class for implementations which provide retrieval of
10464  * unformatted data objects.<br>
10465  * <p>
10466  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10467  * (of the appropriate type which knows how to parse the data object) to provide a block of
10468  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10469  * <p>
10470  * Custom implementations must implement the load method as described in
10471  * {@link Roo.data.HttpProxy#load}.
10472  */
10473 Roo.data.DataProxy = function(){
10474     this.addEvents({
10475         /**
10476          * @event beforeload
10477          * Fires before a network request is made to retrieve a data object.
10478          * @param {Object} This DataProxy object.
10479          * @param {Object} params The params parameter to the load function.
10480          */
10481         beforeload : true,
10482         /**
10483          * @event load
10484          * Fires before the load method's callback is called.
10485          * @param {Object} This DataProxy object.
10486          * @param {Object} o The data object.
10487          * @param {Object} arg The callback argument object passed to the load function.
10488          */
10489         load : true,
10490         /**
10491          * @event loadexception
10492          * Fires if an Exception occurs during data retrieval.
10493          * @param {Object} This DataProxy object.
10494          * @param {Object} o The data object.
10495          * @param {Object} arg The callback argument object passed to the load function.
10496          * @param {Object} e The Exception.
10497          */
10498         loadexception : true
10499     });
10500     Roo.data.DataProxy.superclass.constructor.call(this);
10501 };
10502
10503 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10504
10505     /**
10506      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10507      */
10508 /*
10509  * Based on:
10510  * Ext JS Library 1.1.1
10511  * Copyright(c) 2006-2007, Ext JS, LLC.
10512  *
10513  * Originally Released Under LGPL - original licence link has changed is not relivant.
10514  *
10515  * Fork - LGPL
10516  * <script type="text/javascript">
10517  */
10518 /**
10519  * @class Roo.data.MemoryProxy
10520  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10521  * to the Reader when its load method is called.
10522  * @constructor
10523  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10524  */
10525 Roo.data.MemoryProxy = function(data){
10526     if (data.data) {
10527         data = data.data;
10528     }
10529     Roo.data.MemoryProxy.superclass.constructor.call(this);
10530     this.data = data;
10531 };
10532
10533 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10534     /**
10535      * Load data from the requested source (in this case an in-memory
10536      * data object passed to the constructor), read the data object into
10537      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10538      * process that block using the passed callback.
10539      * @param {Object} params This parameter is not used by the MemoryProxy class.
10540      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10541      * object into a block of Roo.data.Records.
10542      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10543      * The function must be passed <ul>
10544      * <li>The Record block object</li>
10545      * <li>The "arg" argument from the load function</li>
10546      * <li>A boolean success indicator</li>
10547      * </ul>
10548      * @param {Object} scope The scope in which to call the callback
10549      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10550      */
10551     load : function(params, reader, callback, scope, arg){
10552         params = params || {};
10553         var result;
10554         try {
10555             result = reader.readRecords(this.data);
10556         }catch(e){
10557             this.fireEvent("loadexception", this, arg, null, e);
10558             callback.call(scope, null, arg, false);
10559             return;
10560         }
10561         callback.call(scope, result, arg, true);
10562     },
10563     
10564     // private
10565     update : function(params, records){
10566         
10567     }
10568 });/*
10569  * Based on:
10570  * Ext JS Library 1.1.1
10571  * Copyright(c) 2006-2007, Ext JS, LLC.
10572  *
10573  * Originally Released Under LGPL - original licence link has changed is not relivant.
10574  *
10575  * Fork - LGPL
10576  * <script type="text/javascript">
10577  */
10578 /**
10579  * @class Roo.data.HttpProxy
10580  * @extends Roo.data.DataProxy
10581  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10582  * configured to reference a certain URL.<br><br>
10583  * <p>
10584  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10585  * from which the running page was served.<br><br>
10586  * <p>
10587  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10588  * <p>
10589  * Be aware that to enable the browser to parse an XML document, the server must set
10590  * the Content-Type header in the HTTP response to "text/xml".
10591  * @constructor
10592  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10593  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10594  * will be used to make the request.
10595  */
10596 Roo.data.HttpProxy = function(conn){
10597     Roo.data.HttpProxy.superclass.constructor.call(this);
10598     // is conn a conn config or a real conn?
10599     this.conn = conn;
10600     this.useAjax = !conn || !conn.events;
10601   
10602 };
10603
10604 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10605     // thse are take from connection...
10606     
10607     /**
10608      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10609      */
10610     /**
10611      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10612      * extra parameters to each request made by this object. (defaults to undefined)
10613      */
10614     /**
10615      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10616      *  to each request made by this object. (defaults to undefined)
10617      */
10618     /**
10619      * @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)
10620      */
10621     /**
10622      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10623      */
10624      /**
10625      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10626      * @type Boolean
10627      */
10628   
10629
10630     /**
10631      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10632      * @type Boolean
10633      */
10634     /**
10635      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10636      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10637      * a finer-grained basis than the DataProxy events.
10638      */
10639     getConnection : function(){
10640         return this.useAjax ? Roo.Ajax : this.conn;
10641     },
10642
10643     /**
10644      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10645      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10646      * process that block using the passed callback.
10647      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10648      * for the request to the remote server.
10649      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10650      * object into a block of Roo.data.Records.
10651      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10652      * The function must be passed <ul>
10653      * <li>The Record block object</li>
10654      * <li>The "arg" argument from the load function</li>
10655      * <li>A boolean success indicator</li>
10656      * </ul>
10657      * @param {Object} scope The scope in which to call the callback
10658      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10659      */
10660     load : function(params, reader, callback, scope, arg){
10661         if(this.fireEvent("beforeload", this, params) !== false){
10662             var  o = {
10663                 params : params || {},
10664                 request: {
10665                     callback : callback,
10666                     scope : scope,
10667                     arg : arg
10668                 },
10669                 reader: reader,
10670                 callback : this.loadResponse,
10671                 scope: this
10672             };
10673             if(this.useAjax){
10674                 Roo.applyIf(o, this.conn);
10675                 if(this.activeRequest){
10676                     Roo.Ajax.abort(this.activeRequest);
10677                 }
10678                 this.activeRequest = Roo.Ajax.request(o);
10679             }else{
10680                 this.conn.request(o);
10681             }
10682         }else{
10683             callback.call(scope||this, null, arg, false);
10684         }
10685     },
10686
10687     // private
10688     loadResponse : function(o, success, response){
10689         delete this.activeRequest;
10690         if(!success){
10691             this.fireEvent("loadexception", this, o, response);
10692             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10693             return;
10694         }
10695         var result;
10696         try {
10697             result = o.reader.read(response);
10698         }catch(e){
10699             this.fireEvent("loadexception", this, o, response, e);
10700             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10701             return;
10702         }
10703         
10704         this.fireEvent("load", this, o, o.request.arg);
10705         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10706     },
10707
10708     // private
10709     update : function(dataSet){
10710
10711     },
10712
10713     // private
10714     updateResponse : function(dataSet){
10715
10716     }
10717 });/*
10718  * Based on:
10719  * Ext JS Library 1.1.1
10720  * Copyright(c) 2006-2007, Ext JS, LLC.
10721  *
10722  * Originally Released Under LGPL - original licence link has changed is not relivant.
10723  *
10724  * Fork - LGPL
10725  * <script type="text/javascript">
10726  */
10727
10728 /**
10729  * @class Roo.data.ScriptTagProxy
10730  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10731  * other than the originating domain of the running page.<br><br>
10732  * <p>
10733  * <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
10734  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10735  * <p>
10736  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10737  * source code that is used as the source inside a &lt;script> tag.<br><br>
10738  * <p>
10739  * In order for the browser to process the returned data, the server must wrap the data object
10740  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10741  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10742  * depending on whether the callback name was passed:
10743  * <p>
10744  * <pre><code>
10745 boolean scriptTag = false;
10746 String cb = request.getParameter("callback");
10747 if (cb != null) {
10748     scriptTag = true;
10749     response.setContentType("text/javascript");
10750 } else {
10751     response.setContentType("application/x-json");
10752 }
10753 Writer out = response.getWriter();
10754 if (scriptTag) {
10755     out.write(cb + "(");
10756 }
10757 out.print(dataBlock.toJsonString());
10758 if (scriptTag) {
10759     out.write(");");
10760 }
10761 </pre></code>
10762  *
10763  * @constructor
10764  * @param {Object} config A configuration object.
10765  */
10766 Roo.data.ScriptTagProxy = function(config){
10767     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10768     Roo.apply(this, config);
10769     this.head = document.getElementsByTagName("head")[0];
10770 };
10771
10772 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10773
10774 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10775     /**
10776      * @cfg {String} url The URL from which to request the data object.
10777      */
10778     /**
10779      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10780      */
10781     timeout : 30000,
10782     /**
10783      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10784      * the server the name of the callback function set up by the load call to process the returned data object.
10785      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10786      * javascript output which calls this named function passing the data object as its only parameter.
10787      */
10788     callbackParam : "callback",
10789     /**
10790      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10791      * name to the request.
10792      */
10793     nocache : true,
10794
10795     /**
10796      * Load data from the configured URL, read the data object into
10797      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10798      * process that block using the passed callback.
10799      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10800      * for the request to the remote server.
10801      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10802      * object into a block of Roo.data.Records.
10803      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10804      * The function must be passed <ul>
10805      * <li>The Record block object</li>
10806      * <li>The "arg" argument from the load function</li>
10807      * <li>A boolean success indicator</li>
10808      * </ul>
10809      * @param {Object} scope The scope in which to call the callback
10810      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10811      */
10812     load : function(params, reader, callback, scope, arg){
10813         if(this.fireEvent("beforeload", this, params) !== false){
10814
10815             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10816
10817             var url = this.url;
10818             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10819             if(this.nocache){
10820                 url += "&_dc=" + (new Date().getTime());
10821             }
10822             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10823             var trans = {
10824                 id : transId,
10825                 cb : "stcCallback"+transId,
10826                 scriptId : "stcScript"+transId,
10827                 params : params,
10828                 arg : arg,
10829                 url : url,
10830                 callback : callback,
10831                 scope : scope,
10832                 reader : reader
10833             };
10834             var conn = this;
10835
10836             window[trans.cb] = function(o){
10837                 conn.handleResponse(o, trans);
10838             };
10839
10840             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10841
10842             if(this.autoAbort !== false){
10843                 this.abort();
10844             }
10845
10846             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10847
10848             var script = document.createElement("script");
10849             script.setAttribute("src", url);
10850             script.setAttribute("type", "text/javascript");
10851             script.setAttribute("id", trans.scriptId);
10852             this.head.appendChild(script);
10853
10854             this.trans = trans;
10855         }else{
10856             callback.call(scope||this, null, arg, false);
10857         }
10858     },
10859
10860     // private
10861     isLoading : function(){
10862         return this.trans ? true : false;
10863     },
10864
10865     /**
10866      * Abort the current server request.
10867      */
10868     abort : function(){
10869         if(this.isLoading()){
10870             this.destroyTrans(this.trans);
10871         }
10872     },
10873
10874     // private
10875     destroyTrans : function(trans, isLoaded){
10876         this.head.removeChild(document.getElementById(trans.scriptId));
10877         clearTimeout(trans.timeoutId);
10878         if(isLoaded){
10879             window[trans.cb] = undefined;
10880             try{
10881                 delete window[trans.cb];
10882             }catch(e){}
10883         }else{
10884             // if hasn't been loaded, wait for load to remove it to prevent script error
10885             window[trans.cb] = function(){
10886                 window[trans.cb] = undefined;
10887                 try{
10888                     delete window[trans.cb];
10889                 }catch(e){}
10890             };
10891         }
10892     },
10893
10894     // private
10895     handleResponse : function(o, trans){
10896         this.trans = false;
10897         this.destroyTrans(trans, true);
10898         var result;
10899         try {
10900             result = trans.reader.readRecords(o);
10901         }catch(e){
10902             this.fireEvent("loadexception", this, o, trans.arg, e);
10903             trans.callback.call(trans.scope||window, null, trans.arg, false);
10904             return;
10905         }
10906         this.fireEvent("load", this, o, trans.arg);
10907         trans.callback.call(trans.scope||window, result, trans.arg, true);
10908     },
10909
10910     // private
10911     handleFailure : function(trans){
10912         this.trans = false;
10913         this.destroyTrans(trans, false);
10914         this.fireEvent("loadexception", this, null, trans.arg);
10915         trans.callback.call(trans.scope||window, null, trans.arg, false);
10916     }
10917 });/*
10918  * Based on:
10919  * Ext JS Library 1.1.1
10920  * Copyright(c) 2006-2007, Ext JS, LLC.
10921  *
10922  * Originally Released Under LGPL - original licence link has changed is not relivant.
10923  *
10924  * Fork - LGPL
10925  * <script type="text/javascript">
10926  */
10927
10928 /**
10929  * @class Roo.data.JsonReader
10930  * @extends Roo.data.DataReader
10931  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10932  * based on mappings in a provided Roo.data.Record constructor.
10933  * 
10934  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10935  * in the reply previously. 
10936  * 
10937  * <p>
10938  * Example code:
10939  * <pre><code>
10940 var RecordDef = Roo.data.Record.create([
10941     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10942     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10943 ]);
10944 var myReader = new Roo.data.JsonReader({
10945     totalProperty: "results",    // The property which contains the total dataset size (optional)
10946     root: "rows",                // The property which contains an Array of row objects
10947     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10948 }, RecordDef);
10949 </code></pre>
10950  * <p>
10951  * This would consume a JSON file like this:
10952  * <pre><code>
10953 { 'results': 2, 'rows': [
10954     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10955     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10956 }
10957 </code></pre>
10958  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10959  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10960  * paged from the remote server.
10961  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10962  * @cfg {String} root name of the property which contains the Array of row objects.
10963  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10964  * @cfg {Array} fields Array of field definition objects
10965  * @constructor
10966  * Create a new JsonReader
10967  * @param {Object} meta Metadata configuration options
10968  * @param {Object} recordType Either an Array of field definition objects,
10969  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10970  */
10971 Roo.data.JsonReader = function(meta, recordType){
10972     
10973     meta = meta || {};
10974     // set some defaults:
10975     Roo.applyIf(meta, {
10976         totalProperty: 'total',
10977         successProperty : 'success',
10978         root : 'data',
10979         id : 'id'
10980     });
10981     
10982     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10983 };
10984 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10985     
10986     /**
10987      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10988      * Used by Store query builder to append _requestMeta to params.
10989      * 
10990      */
10991     metaFromRemote : false,
10992     /**
10993      * This method is only used by a DataProxy which has retrieved data from a remote server.
10994      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10995      * @return {Object} data A data block which is used by an Roo.data.Store object as
10996      * a cache of Roo.data.Records.
10997      */
10998     read : function(response){
10999         var json = response.responseText;
11000        
11001         var o = /* eval:var:o */ eval("("+json+")");
11002         if(!o) {
11003             throw {message: "JsonReader.read: Json object not found"};
11004         }
11005         
11006         if(o.metaData){
11007             
11008             delete this.ef;
11009             this.metaFromRemote = true;
11010             this.meta = o.metaData;
11011             this.recordType = Roo.data.Record.create(o.metaData.fields);
11012             this.onMetaChange(this.meta, this.recordType, o);
11013         }
11014         return this.readRecords(o);
11015     },
11016
11017     // private function a store will implement
11018     onMetaChange : function(meta, recordType, o){
11019
11020     },
11021
11022     /**
11023          * @ignore
11024          */
11025     simpleAccess: function(obj, subsc) {
11026         return obj[subsc];
11027     },
11028
11029         /**
11030          * @ignore
11031          */
11032     getJsonAccessor: function(){
11033         var re = /[\[\.]/;
11034         return function(expr) {
11035             try {
11036                 return(re.test(expr))
11037                     ? new Function("obj", "return obj." + expr)
11038                     : function(obj){
11039                         return obj[expr];
11040                     };
11041             } catch(e){}
11042             return Roo.emptyFn;
11043         };
11044     }(),
11045
11046     /**
11047      * Create a data block containing Roo.data.Records from an XML document.
11048      * @param {Object} o An object which contains an Array of row objects in the property specified
11049      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11050      * which contains the total size of the dataset.
11051      * @return {Object} data A data block which is used by an Roo.data.Store object as
11052      * a cache of Roo.data.Records.
11053      */
11054     readRecords : function(o){
11055         /**
11056          * After any data loads, the raw JSON data is available for further custom processing.
11057          * @type Object
11058          */
11059         this.o = o;
11060         var s = this.meta, Record = this.recordType,
11061             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11062
11063 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11064         if (!this.ef) {
11065             if(s.totalProperty) {
11066                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11067                 }
11068                 if(s.successProperty) {
11069                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11070                 }
11071                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11072                 if (s.id) {
11073                         var g = this.getJsonAccessor(s.id);
11074                         this.getId = function(rec) {
11075                                 var r = g(rec);  
11076                                 return (r === undefined || r === "") ? null : r;
11077                         };
11078                 } else {
11079                         this.getId = function(){return null;};
11080                 }
11081             this.ef = [];
11082             for(var jj = 0; jj < fl; jj++){
11083                 f = fi[jj];
11084                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11085                 this.ef[jj] = this.getJsonAccessor(map);
11086             }
11087         }
11088
11089         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11090         if(s.totalProperty){
11091             var vt = parseInt(this.getTotal(o), 10);
11092             if(!isNaN(vt)){
11093                 totalRecords = vt;
11094             }
11095         }
11096         if(s.successProperty){
11097             var vs = this.getSuccess(o);
11098             if(vs === false || vs === 'false'){
11099                 success = false;
11100             }
11101         }
11102         var records = [];
11103         for(var i = 0; i < c; i++){
11104                 var n = root[i];
11105             var values = {};
11106             var id = this.getId(n);
11107             for(var j = 0; j < fl; j++){
11108                 f = fi[j];
11109             var v = this.ef[j](n);
11110             if (!f.convert) {
11111                 Roo.log('missing convert for ' + f.name);
11112                 Roo.log(f);
11113                 continue;
11114             }
11115             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11116             }
11117             var record = new Record(values, id);
11118             record.json = n;
11119             records[i] = record;
11120         }
11121         return {
11122             raw : o,
11123             success : success,
11124             records : records,
11125             totalRecords : totalRecords
11126         };
11127     }
11128 });/*
11129  * Based on:
11130  * Ext JS Library 1.1.1
11131  * Copyright(c) 2006-2007, Ext JS, LLC.
11132  *
11133  * Originally Released Under LGPL - original licence link has changed is not relivant.
11134  *
11135  * Fork - LGPL
11136  * <script type="text/javascript">
11137  */
11138
11139 /**
11140  * @class Roo.data.ArrayReader
11141  * @extends Roo.data.DataReader
11142  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11143  * Each element of that Array represents a row of data fields. The
11144  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11145  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11146  * <p>
11147  * Example code:.
11148  * <pre><code>
11149 var RecordDef = Roo.data.Record.create([
11150     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11151     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11152 ]);
11153 var myReader = new Roo.data.ArrayReader({
11154     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11155 }, RecordDef);
11156 </code></pre>
11157  * <p>
11158  * This would consume an Array like this:
11159  * <pre><code>
11160 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11161   </code></pre>
11162  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11163  * @constructor
11164  * Create a new JsonReader
11165  * @param {Object} meta Metadata configuration options.
11166  * @param {Object} recordType Either an Array of field definition objects
11167  * as specified to {@link Roo.data.Record#create},
11168  * or an {@link Roo.data.Record} object
11169  * created using {@link Roo.data.Record#create}.
11170  */
11171 Roo.data.ArrayReader = function(meta, recordType){
11172     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11173 };
11174
11175 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11176     /**
11177      * Create a data block containing Roo.data.Records from an XML document.
11178      * @param {Object} o An Array of row objects which represents the dataset.
11179      * @return {Object} data A data block which is used by an Roo.data.Store object as
11180      * a cache of Roo.data.Records.
11181      */
11182     readRecords : function(o){
11183         var sid = this.meta ? this.meta.id : null;
11184         var recordType = this.recordType, fields = recordType.prototype.fields;
11185         var records = [];
11186         var root = o;
11187             for(var i = 0; i < root.length; i++){
11188                     var n = root[i];
11189                 var values = {};
11190                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11191                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11192                 var f = fields.items[j];
11193                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11194                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11195                 v = f.convert(v);
11196                 values[f.name] = v;
11197             }
11198                 var record = new recordType(values, id);
11199                 record.json = n;
11200                 records[records.length] = record;
11201             }
11202             return {
11203                 records : records,
11204                 totalRecords : records.length
11205             };
11206     }
11207 });/*
11208  * - LGPL
11209  * * 
11210  */
11211
11212 /**
11213  * @class Roo.bootstrap.ComboBox
11214  * @extends Roo.bootstrap.TriggerField
11215  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11216  * @cfg {Boolean} append (true|false) default false
11217  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11218  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11219  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11220  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11221  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11222  * @cfg {Boolean} animate default true
11223  * @cfg {Boolean} emptyResultText only for touch device
11224  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11225  * @constructor
11226  * Create a new ComboBox.
11227  * @param {Object} config Configuration options
11228  */
11229 Roo.bootstrap.ComboBox = function(config){
11230     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11231     this.addEvents({
11232         /**
11233          * @event expand
11234          * Fires when the dropdown list is expanded
11235              * @param {Roo.bootstrap.ComboBox} combo This combo box
11236              */
11237         'expand' : true,
11238         /**
11239          * @event collapse
11240          * Fires when the dropdown list is collapsed
11241              * @param {Roo.bootstrap.ComboBox} combo This combo box
11242              */
11243         'collapse' : true,
11244         /**
11245          * @event beforeselect
11246          * Fires before a list item is selected. Return false to cancel the selection.
11247              * @param {Roo.bootstrap.ComboBox} combo This combo box
11248              * @param {Roo.data.Record} record The data record returned from the underlying store
11249              * @param {Number} index The index of the selected item in the dropdown list
11250              */
11251         'beforeselect' : true,
11252         /**
11253          * @event select
11254          * Fires when a list item is selected
11255              * @param {Roo.bootstrap.ComboBox} combo This combo box
11256              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11257              * @param {Number} index The index of the selected item in the dropdown list
11258              */
11259         'select' : true,
11260         /**
11261          * @event beforequery
11262          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11263          * The event object passed has these properties:
11264              * @param {Roo.bootstrap.ComboBox} combo This combo box
11265              * @param {String} query The query
11266              * @param {Boolean} forceAll true to force "all" query
11267              * @param {Boolean} cancel true to cancel the query
11268              * @param {Object} e The query event object
11269              */
11270         'beforequery': true,
11271          /**
11272          * @event add
11273          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11274              * @param {Roo.bootstrap.ComboBox} combo This combo box
11275              */
11276         'add' : true,
11277         /**
11278          * @event edit
11279          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11280              * @param {Roo.bootstrap.ComboBox} combo This combo box
11281              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11282              */
11283         'edit' : true,
11284         /**
11285          * @event remove
11286          * Fires when the remove value from the combobox array
11287              * @param {Roo.bootstrap.ComboBox} combo This combo box
11288              */
11289         'remove' : true,
11290         /**
11291          * @event specialfilter
11292          * Fires when specialfilter
11293             * @param {Roo.bootstrap.ComboBox} combo This combo box
11294             */
11295         'specialfilter' : true
11296         
11297     });
11298     
11299     this.item = [];
11300     this.tickItems = [];
11301     
11302     this.selectedIndex = -1;
11303     if(this.mode == 'local'){
11304         if(config.queryDelay === undefined){
11305             this.queryDelay = 10;
11306         }
11307         if(config.minChars === undefined){
11308             this.minChars = 0;
11309         }
11310     }
11311 };
11312
11313 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11314      
11315     /**
11316      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11317      * rendering into an Roo.Editor, defaults to false)
11318      */
11319     /**
11320      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11321      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11322      */
11323     /**
11324      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11325      */
11326     /**
11327      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11328      * the dropdown list (defaults to undefined, with no header element)
11329      */
11330
11331      /**
11332      * @cfg {String/Roo.Template} tpl The template to use to render the output
11333      */
11334      
11335      /**
11336      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11337      */
11338     listWidth: undefined,
11339     /**
11340      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11341      * mode = 'remote' or 'text' if mode = 'local')
11342      */
11343     displayField: undefined,
11344     
11345     /**
11346      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11347      * mode = 'remote' or 'value' if mode = 'local'). 
11348      * Note: use of a valueField requires the user make a selection
11349      * in order for a value to be mapped.
11350      */
11351     valueField: undefined,
11352     
11353     
11354     /**
11355      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11356      * field's data value (defaults to the underlying DOM element's name)
11357      */
11358     hiddenName: undefined,
11359     /**
11360      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11361      */
11362     listClass: '',
11363     /**
11364      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11365      */
11366     selectedClass: 'active',
11367     
11368     /**
11369      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11370      */
11371     shadow:'sides',
11372     /**
11373      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11374      * anchor positions (defaults to 'tl-bl')
11375      */
11376     listAlign: 'tl-bl?',
11377     /**
11378      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11379      */
11380     maxHeight: 300,
11381     /**
11382      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11383      * query specified by the allQuery config option (defaults to 'query')
11384      */
11385     triggerAction: 'query',
11386     /**
11387      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11388      * (defaults to 4, does not apply if editable = false)
11389      */
11390     minChars : 4,
11391     /**
11392      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11393      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11394      */
11395     typeAhead: false,
11396     /**
11397      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11398      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11399      */
11400     queryDelay: 500,
11401     /**
11402      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11403      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11404      */
11405     pageSize: 0,
11406     /**
11407      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11408      * when editable = true (defaults to false)
11409      */
11410     selectOnFocus:false,
11411     /**
11412      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11413      */
11414     queryParam: 'query',
11415     /**
11416      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11417      * when mode = 'remote' (defaults to 'Loading...')
11418      */
11419     loadingText: 'Loading...',
11420     /**
11421      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11422      */
11423     resizable: false,
11424     /**
11425      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11426      */
11427     handleHeight : 8,
11428     /**
11429      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11430      * traditional select (defaults to true)
11431      */
11432     editable: true,
11433     /**
11434      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11435      */
11436     allQuery: '',
11437     /**
11438      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11439      */
11440     mode: 'remote',
11441     /**
11442      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11443      * listWidth has a higher value)
11444      */
11445     minListWidth : 70,
11446     /**
11447      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11448      * allow the user to set arbitrary text into the field (defaults to false)
11449      */
11450     forceSelection:false,
11451     /**
11452      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11453      * if typeAhead = true (defaults to 250)
11454      */
11455     typeAheadDelay : 250,
11456     /**
11457      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11458      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11459      */
11460     valueNotFoundText : undefined,
11461     /**
11462      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11463      */
11464     blockFocus : false,
11465     
11466     /**
11467      * @cfg {Boolean} disableClear Disable showing of clear button.
11468      */
11469     disableClear : false,
11470     /**
11471      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11472      */
11473     alwaysQuery : false,
11474     
11475     /**
11476      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11477      */
11478     multiple : false,
11479     
11480     /**
11481      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11482      */
11483     invalidClass : "has-warning",
11484     
11485     /**
11486      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11487      */
11488     validClass : "has-success",
11489     
11490     /**
11491      * @cfg {Boolean} specialFilter (true|false) special filter default false
11492      */
11493     specialFilter : false,
11494     
11495     /**
11496      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11497      */
11498     mobileTouchView : true,
11499     
11500     //private
11501     addicon : false,
11502     editicon: false,
11503     
11504     page: 0,
11505     hasQuery: false,
11506     append: false,
11507     loadNext: false,
11508     autoFocus : true,
11509     tickable : false,
11510     btnPosition : 'right',
11511     triggerList : true,
11512     showToggleBtn : true,
11513     animate : true,
11514     emptyResultText: 'Empty',
11515     triggerText : 'Select',
11516     
11517     // element that contains real text value.. (when hidden is used..)
11518     
11519     getAutoCreate : function()
11520     {
11521         var cfg = false;
11522         
11523         /*
11524          * Touch Devices
11525          */
11526         
11527         if(Roo.isTouch && this.mobileTouchView){
11528             cfg = this.getAutoCreateTouchView();
11529             return cfg;;
11530         }
11531         
11532         /*
11533          *  Normal ComboBox
11534          */
11535         if(!this.tickable){
11536             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11537             return cfg;
11538         }
11539         
11540         /*
11541          *  ComboBox with tickable selections
11542          */
11543              
11544         var align = this.labelAlign || this.parentLabelAlign();
11545         
11546         cfg = {
11547             cls : 'form-group roo-combobox-tickable' //input-group
11548         };
11549         
11550         var buttons = {
11551             tag : 'div',
11552             cls : 'tickable-buttons',
11553             cn : [
11554                 {
11555                     tag : 'button',
11556                     type : 'button',
11557                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11558                     html : this.triggerText
11559                 },
11560                 {
11561                     tag : 'button',
11562                     type : 'button',
11563                     name : 'ok',
11564                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11565                     html : 'Done'
11566                 },
11567                 {
11568                     tag : 'button',
11569                     type : 'button',
11570                     name : 'cancel',
11571                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11572                     html : 'Cancel'
11573                 }
11574             ]
11575         };
11576         
11577         if(this.editable){
11578             buttons.cn.unshift({
11579                 tag: 'input',
11580                 cls: 'select2-search-field-input'
11581             });
11582         }
11583         
11584         var _this = this;
11585         
11586         Roo.each(buttons.cn, function(c){
11587             if (_this.size) {
11588                 c.cls += ' btn-' + _this.size;
11589             }
11590
11591             if (_this.disabled) {
11592                 c.disabled = true;
11593             }
11594         });
11595         
11596         var box = {
11597             tag: 'div',
11598             cn: [
11599                 {
11600                     tag: 'input',
11601                     type : 'hidden',
11602                     cls: 'form-hidden-field'
11603                 },
11604                 {
11605                     tag: 'ul',
11606                     cls: 'select2-choices',
11607                     cn:[
11608                         {
11609                             tag: 'li',
11610                             cls: 'select2-search-field',
11611                             cn: [
11612
11613                                 buttons
11614                             ]
11615                         }
11616                     ]
11617                 }
11618             ]
11619         }
11620         
11621         var combobox = {
11622             cls: 'select2-container input-group select2-container-multi',
11623             cn: [
11624                 box
11625 //                {
11626 //                    tag: 'ul',
11627 //                    cls: 'typeahead typeahead-long dropdown-menu',
11628 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11629 //                }
11630             ]
11631         };
11632         
11633         if(this.hasFeedback && !this.allowBlank){
11634             
11635             var feedback = {
11636                 tag: 'span',
11637                 cls: 'glyphicon form-control-feedback'
11638             };
11639
11640             combobox.cn.push(feedback);
11641         }
11642         
11643         if (align ==='left' && this.fieldLabel.length) {
11644             
11645                 Roo.log("left and has label");
11646                 cfg.cn = [
11647                     
11648                     {
11649                         tag: 'label',
11650                         'for' :  id,
11651                         cls : 'control-label col-sm-' + this.labelWidth,
11652                         html : this.fieldLabel
11653                         
11654                     },
11655                     {
11656                         cls : "col-sm-" + (12 - this.labelWidth), 
11657                         cn: [
11658                             combobox
11659                         ]
11660                     }
11661                     
11662                 ];
11663         } else if ( this.fieldLabel.length) {
11664                 Roo.log(" label");
11665                  cfg.cn = [
11666                    
11667                     {
11668                         tag: 'label',
11669                         //cls : 'input-group-addon',
11670                         html : this.fieldLabel
11671                         
11672                     },
11673                     
11674                     combobox
11675                     
11676                 ];
11677
11678         } else {
11679             
11680                 Roo.log(" no label && no align");
11681                 cfg = combobox
11682                      
11683                 
11684         }
11685          
11686         var settings=this;
11687         ['xs','sm','md','lg'].map(function(size){
11688             if (settings[size]) {
11689                 cfg.cls += ' col-' + size + '-' + settings[size];
11690             }
11691         });
11692         
11693         return cfg;
11694         
11695     },
11696     
11697     _initEventsCalled : false,
11698     
11699     // private
11700     initEvents: function()
11701     {
11702         
11703         if (this._initEventsCalled) { // as we call render... prevent looping...
11704             return;
11705         }
11706         this._initEventsCalled = true;
11707         
11708         if (!this.store) {
11709             throw "can not find store for combo";
11710         }
11711         
11712         this.store = Roo.factory(this.store, Roo.data);
11713         
11714         // if we are building from html. then this element is so complex, that we can not really
11715         // use the rendered HTML.
11716         // so we have to trash and replace the previous code.
11717         if (Roo.XComponent.build_from_html) {
11718             
11719             // remove this element....
11720             var e = this.el.dom, k=0;
11721             while (e ) { e = e.previousSibling;  ++k;}
11722
11723             this.el.remove();
11724             
11725             this.el=false;
11726             this.rendered = false;
11727             
11728             this.render(this.parent().getChildContainer(true), k);
11729             
11730             
11731             
11732         }
11733         
11734         
11735         /*
11736          * Touch Devices
11737          */
11738         
11739         if(Roo.isTouch && this.mobileTouchView){
11740             this.initTouchView();
11741             return;
11742         }
11743         
11744         if(this.tickable){
11745             this.initTickableEvents();
11746             return;
11747         }
11748         
11749         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11750         
11751         if(this.hiddenName){
11752             
11753             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11754             
11755             this.hiddenField.dom.value =
11756                 this.hiddenValue !== undefined ? this.hiddenValue :
11757                 this.value !== undefined ? this.value : '';
11758
11759             // prevent input submission
11760             this.el.dom.removeAttribute('name');
11761             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11762              
11763              
11764         }
11765         //if(Roo.isGecko){
11766         //    this.el.dom.setAttribute('autocomplete', 'off');
11767         //}
11768         
11769         var cls = 'x-combo-list';
11770         
11771         //this.list = new Roo.Layer({
11772         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11773         //});
11774         
11775         var _this = this;
11776         
11777         (function(){
11778             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11779             _this.list.setWidth(lw);
11780         }).defer(100);
11781         
11782         this.list.on('mouseover', this.onViewOver, this);
11783         this.list.on('mousemove', this.onViewMove, this);
11784         
11785         this.list.on('scroll', this.onViewScroll, this);
11786         
11787         /*
11788         this.list.swallowEvent('mousewheel');
11789         this.assetHeight = 0;
11790
11791         if(this.title){
11792             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11793             this.assetHeight += this.header.getHeight();
11794         }
11795
11796         this.innerList = this.list.createChild({cls:cls+'-inner'});
11797         this.innerList.on('mouseover', this.onViewOver, this);
11798         this.innerList.on('mousemove', this.onViewMove, this);
11799         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11800         
11801         if(this.allowBlank && !this.pageSize && !this.disableClear){
11802             this.footer = this.list.createChild({cls:cls+'-ft'});
11803             this.pageTb = new Roo.Toolbar(this.footer);
11804            
11805         }
11806         if(this.pageSize){
11807             this.footer = this.list.createChild({cls:cls+'-ft'});
11808             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11809                     {pageSize: this.pageSize});
11810             
11811         }
11812         
11813         if (this.pageTb && this.allowBlank && !this.disableClear) {
11814             var _this = this;
11815             this.pageTb.add(new Roo.Toolbar.Fill(), {
11816                 cls: 'x-btn-icon x-btn-clear',
11817                 text: '&#160;',
11818                 handler: function()
11819                 {
11820                     _this.collapse();
11821                     _this.clearValue();
11822                     _this.onSelect(false, -1);
11823                 }
11824             });
11825         }
11826         if (this.footer) {
11827             this.assetHeight += this.footer.getHeight();
11828         }
11829         */
11830             
11831         if(!this.tpl){
11832             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11833         }
11834
11835         this.view = new Roo.View(this.list, this.tpl, {
11836             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11837         });
11838         //this.view.wrapEl.setDisplayed(false);
11839         this.view.on('click', this.onViewClick, this);
11840         
11841         
11842         
11843         this.store.on('beforeload', this.onBeforeLoad, this);
11844         this.store.on('load', this.onLoad, this);
11845         this.store.on('loadexception', this.onLoadException, this);
11846         /*
11847         if(this.resizable){
11848             this.resizer = new Roo.Resizable(this.list,  {
11849                pinned:true, handles:'se'
11850             });
11851             this.resizer.on('resize', function(r, w, h){
11852                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11853                 this.listWidth = w;
11854                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11855                 this.restrictHeight();
11856             }, this);
11857             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11858         }
11859         */
11860         if(!this.editable){
11861             this.editable = true;
11862             this.setEditable(false);
11863         }
11864         
11865         /*
11866         
11867         if (typeof(this.events.add.listeners) != 'undefined') {
11868             
11869             this.addicon = this.wrap.createChild(
11870                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11871        
11872             this.addicon.on('click', function(e) {
11873                 this.fireEvent('add', this);
11874             }, this);
11875         }
11876         if (typeof(this.events.edit.listeners) != 'undefined') {
11877             
11878             this.editicon = this.wrap.createChild(
11879                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11880             if (this.addicon) {
11881                 this.editicon.setStyle('margin-left', '40px');
11882             }
11883             this.editicon.on('click', function(e) {
11884                 
11885                 // we fire even  if inothing is selected..
11886                 this.fireEvent('edit', this, this.lastData );
11887                 
11888             }, this);
11889         }
11890         */
11891         
11892         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11893             "up" : function(e){
11894                 this.inKeyMode = true;
11895                 this.selectPrev();
11896             },
11897
11898             "down" : function(e){
11899                 if(!this.isExpanded()){
11900                     this.onTriggerClick();
11901                 }else{
11902                     this.inKeyMode = true;
11903                     this.selectNext();
11904                 }
11905             },
11906
11907             "enter" : function(e){
11908 //                this.onViewClick();
11909                 //return true;
11910                 this.collapse();
11911                 
11912                 if(this.fireEvent("specialkey", this, e)){
11913                     this.onViewClick(false);
11914                 }
11915                 
11916                 return true;
11917             },
11918
11919             "esc" : function(e){
11920                 this.collapse();
11921             },
11922
11923             "tab" : function(e){
11924                 this.collapse();
11925                 
11926                 if(this.fireEvent("specialkey", this, e)){
11927                     this.onViewClick(false);
11928                 }
11929                 
11930                 return true;
11931             },
11932
11933             scope : this,
11934
11935             doRelay : function(foo, bar, hname){
11936                 if(hname == 'down' || this.scope.isExpanded()){
11937                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11938                 }
11939                 return true;
11940             },
11941
11942             forceKeyDown: true
11943         });
11944         
11945         
11946         this.queryDelay = Math.max(this.queryDelay || 10,
11947                 this.mode == 'local' ? 10 : 250);
11948         
11949         
11950         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11951         
11952         if(this.typeAhead){
11953             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11954         }
11955         if(this.editable !== false){
11956             this.inputEl().on("keyup", this.onKeyUp, this);
11957         }
11958         if(this.forceSelection){
11959             this.inputEl().on('blur', this.doForce, this);
11960         }
11961         
11962         if(this.multiple){
11963             this.choices = this.el.select('ul.select2-choices', true).first();
11964             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11965         }
11966     },
11967     
11968     initTickableEvents: function()
11969     {   
11970         this.createList();
11971         
11972         if(this.hiddenName){
11973             
11974             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11975             
11976             this.hiddenField.dom.value =
11977                 this.hiddenValue !== undefined ? this.hiddenValue :
11978                 this.value !== undefined ? this.value : '';
11979
11980             // prevent input submission
11981             this.el.dom.removeAttribute('name');
11982             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11983              
11984              
11985         }
11986         
11987 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11988         
11989         this.choices = this.el.select('ul.select2-choices', true).first();
11990         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11991         if(this.triggerList){
11992             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11993         }
11994          
11995         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11996         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11997         
11998         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11999         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12000         
12001         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12002         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12003         
12004         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12005         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12006         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12007         
12008         this.okBtn.hide();
12009         this.cancelBtn.hide();
12010         
12011         var _this = this;
12012         
12013         (function(){
12014             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12015             _this.list.setWidth(lw);
12016         }).defer(100);
12017         
12018         this.list.on('mouseover', this.onViewOver, this);
12019         this.list.on('mousemove', this.onViewMove, this);
12020         
12021         this.list.on('scroll', this.onViewScroll, this);
12022         
12023         if(!this.tpl){
12024             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>';
12025         }
12026
12027         this.view = new Roo.View(this.list, this.tpl, {
12028             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12029         });
12030         
12031         //this.view.wrapEl.setDisplayed(false);
12032         this.view.on('click', this.onViewClick, this);
12033         
12034         
12035         
12036         this.store.on('beforeload', this.onBeforeLoad, this);
12037         this.store.on('load', this.onLoad, this);
12038         this.store.on('loadexception', this.onLoadException, this);
12039         
12040         if(this.editable){
12041             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12042                 "up" : function(e){
12043                     this.inKeyMode = true;
12044                     this.selectPrev();
12045                 },
12046
12047                 "down" : function(e){
12048                     this.inKeyMode = true;
12049                     this.selectNext();
12050                 },
12051
12052                 "enter" : function(e){
12053                     if(this.fireEvent("specialkey", this, e)){
12054                         this.onViewClick(false);
12055                     }
12056                     
12057                     return true;
12058                 },
12059
12060                 "esc" : function(e){
12061                     this.onTickableFooterButtonClick(e, false, false);
12062                 },
12063
12064                 "tab" : function(e){
12065                     this.fireEvent("specialkey", this, e);
12066                     
12067                     this.onTickableFooterButtonClick(e, false, false);
12068                     
12069                     return true;
12070                 },
12071
12072                 scope : this,
12073
12074                 doRelay : function(e, fn, key){
12075                     if(this.scope.isExpanded()){
12076                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12077                     }
12078                     return true;
12079                 },
12080
12081                 forceKeyDown: true
12082             });
12083         }
12084         
12085         this.queryDelay = Math.max(this.queryDelay || 10,
12086                 this.mode == 'local' ? 10 : 250);
12087         
12088         
12089         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12090         
12091         if(this.typeAhead){
12092             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12093         }
12094         
12095         if(this.editable !== false){
12096             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12097         }
12098         
12099     },
12100
12101     onDestroy : function(){
12102         if(this.view){
12103             this.view.setStore(null);
12104             this.view.el.removeAllListeners();
12105             this.view.el.remove();
12106             this.view.purgeListeners();
12107         }
12108         if(this.list){
12109             this.list.dom.innerHTML  = '';
12110         }
12111         
12112         if(this.store){
12113             this.store.un('beforeload', this.onBeforeLoad, this);
12114             this.store.un('load', this.onLoad, this);
12115             this.store.un('loadexception', this.onLoadException, this);
12116         }
12117         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12118     },
12119
12120     // private
12121     fireKey : function(e){
12122         if(e.isNavKeyPress() && !this.list.isVisible()){
12123             this.fireEvent("specialkey", this, e);
12124         }
12125     },
12126
12127     // private
12128     onResize: function(w, h){
12129 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12130 //        
12131 //        if(typeof w != 'number'){
12132 //            // we do not handle it!?!?
12133 //            return;
12134 //        }
12135 //        var tw = this.trigger.getWidth();
12136 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12137 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12138 //        var x = w - tw;
12139 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12140 //            
12141 //        //this.trigger.setStyle('left', x+'px');
12142 //        
12143 //        if(this.list && this.listWidth === undefined){
12144 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12145 //            this.list.setWidth(lw);
12146 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12147 //        }
12148         
12149     
12150         
12151     },
12152
12153     /**
12154      * Allow or prevent the user from directly editing the field text.  If false is passed,
12155      * the user will only be able to select from the items defined in the dropdown list.  This method
12156      * is the runtime equivalent of setting the 'editable' config option at config time.
12157      * @param {Boolean} value True to allow the user to directly edit the field text
12158      */
12159     setEditable : function(value){
12160         if(value == this.editable){
12161             return;
12162         }
12163         this.editable = value;
12164         if(!value){
12165             this.inputEl().dom.setAttribute('readOnly', true);
12166             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12167             this.inputEl().addClass('x-combo-noedit');
12168         }else{
12169             this.inputEl().dom.setAttribute('readOnly', false);
12170             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12171             this.inputEl().removeClass('x-combo-noedit');
12172         }
12173     },
12174
12175     // private
12176     
12177     onBeforeLoad : function(combo,opts){
12178         if(!this.hasFocus){
12179             return;
12180         }
12181          if (!opts.add) {
12182             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12183          }
12184         this.restrictHeight();
12185         this.selectedIndex = -1;
12186     },
12187
12188     // private
12189     onLoad : function(){
12190         
12191         this.hasQuery = false;
12192         
12193         if(!this.hasFocus){
12194             return;
12195         }
12196         
12197         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12198             this.loading.hide();
12199         }
12200              
12201         if(this.store.getCount() > 0){
12202             this.expand();
12203             this.restrictHeight();
12204             if(this.lastQuery == this.allQuery){
12205                 if(this.editable && !this.tickable){
12206                     this.inputEl().dom.select();
12207                 }
12208                 
12209                 if(
12210                     !this.selectByValue(this.value, true) &&
12211                     this.autoFocus && 
12212                     (
12213                         !this.store.lastOptions ||
12214                         typeof(this.store.lastOptions.add) == 'undefined' || 
12215                         this.store.lastOptions.add != true
12216                     )
12217                 ){
12218                     this.select(0, true);
12219                 }
12220             }else{
12221                 if(this.autoFocus){
12222                     this.selectNext();
12223                 }
12224                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12225                     this.taTask.delay(this.typeAheadDelay);
12226                 }
12227             }
12228         }else{
12229             this.onEmptyResults();
12230         }
12231         
12232         //this.el.focus();
12233     },
12234     // private
12235     onLoadException : function()
12236     {
12237         this.hasQuery = false;
12238         
12239         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12240             this.loading.hide();
12241         }
12242         
12243         if(this.tickable && this.editable){
12244             return;
12245         }
12246         
12247         this.collapse();
12248         
12249         Roo.log(this.store.reader.jsonData);
12250         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12251             // fixme
12252             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12253         }
12254         
12255         
12256     },
12257     // private
12258     onTypeAhead : function(){
12259         if(this.store.getCount() > 0){
12260             var r = this.store.getAt(0);
12261             var newValue = r.data[this.displayField];
12262             var len = newValue.length;
12263             var selStart = this.getRawValue().length;
12264             
12265             if(selStart != len){
12266                 this.setRawValue(newValue);
12267                 this.selectText(selStart, newValue.length);
12268             }
12269         }
12270     },
12271
12272     // private
12273     onSelect : function(record, index){
12274         
12275         if(this.fireEvent('beforeselect', this, record, index) !== false){
12276         
12277             this.setFromData(index > -1 ? record.data : false);
12278             
12279             this.collapse();
12280             this.fireEvent('select', this, record, index);
12281         }
12282     },
12283
12284     /**
12285      * Returns the currently selected field value or empty string if no value is set.
12286      * @return {String} value The selected value
12287      */
12288     getValue : function(){
12289         
12290         if(this.multiple){
12291             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12292         }
12293         
12294         if(this.valueField){
12295             return typeof this.value != 'undefined' ? this.value : '';
12296         }else{
12297             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12298         }
12299     },
12300
12301     /**
12302      * Clears any text/value currently set in the field
12303      */
12304     clearValue : function(){
12305         if(this.hiddenField){
12306             this.hiddenField.dom.value = '';
12307         }
12308         this.value = '';
12309         this.setRawValue('');
12310         this.lastSelectionText = '';
12311         this.lastData = false;
12312         
12313         var close = this.closeTriggerEl();
12314         
12315         if(close){
12316             close.hide();
12317         }
12318         
12319     },
12320
12321     /**
12322      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12323      * will be displayed in the field.  If the value does not match the data value of an existing item,
12324      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12325      * Otherwise the field will be blank (although the value will still be set).
12326      * @param {String} value The value to match
12327      */
12328     setValue : function(v){
12329         if(this.multiple){
12330             this.syncValue();
12331             return;
12332         }
12333         
12334         var text = v;
12335         if(this.valueField){
12336             var r = this.findRecord(this.valueField, v);
12337             if(r){
12338                 text = r.data[this.displayField];
12339             }else if(this.valueNotFoundText !== undefined){
12340                 text = this.valueNotFoundText;
12341             }
12342         }
12343         this.lastSelectionText = text;
12344         if(this.hiddenField){
12345             this.hiddenField.dom.value = v;
12346         }
12347         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12348         this.value = v;
12349         
12350         var close = this.closeTriggerEl();
12351         
12352         if(close){
12353             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12354         }
12355     },
12356     /**
12357      * @property {Object} the last set data for the element
12358      */
12359     
12360     lastData : false,
12361     /**
12362      * Sets the value of the field based on a object which is related to the record format for the store.
12363      * @param {Object} value the value to set as. or false on reset?
12364      */
12365     setFromData : function(o){
12366         
12367         if(this.multiple){
12368             this.addItem(o);
12369             return;
12370         }
12371             
12372         var dv = ''; // display value
12373         var vv = ''; // value value..
12374         this.lastData = o;
12375         if (this.displayField) {
12376             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12377         } else {
12378             // this is an error condition!!!
12379             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12380         }
12381         
12382         if(this.valueField){
12383             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12384         }
12385         
12386         var close = this.closeTriggerEl();
12387         
12388         if(close){
12389             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12390         }
12391         
12392         if(this.hiddenField){
12393             this.hiddenField.dom.value = vv;
12394             
12395             this.lastSelectionText = dv;
12396             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12397             this.value = vv;
12398             return;
12399         }
12400         // no hidden field.. - we store the value in 'value', but still display
12401         // display field!!!!
12402         this.lastSelectionText = dv;
12403         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12404         this.value = vv;
12405         
12406         
12407         
12408     },
12409     // private
12410     reset : function(){
12411         // overridden so that last data is reset..
12412         
12413         if(this.multiple){
12414             this.clearItem();
12415             return;
12416         }
12417         
12418         this.setValue(this.originalValue);
12419         this.clearInvalid();
12420         this.lastData = false;
12421         if (this.view) {
12422             this.view.clearSelections();
12423         }
12424     },
12425     // private
12426     findRecord : function(prop, value){
12427         var record;
12428         if(this.store.getCount() > 0){
12429             this.store.each(function(r){
12430                 if(r.data[prop] == value){
12431                     record = r;
12432                     return false;
12433                 }
12434                 return true;
12435             });
12436         }
12437         return record;
12438     },
12439     
12440     getName: function()
12441     {
12442         // returns hidden if it's set..
12443         if (!this.rendered) {return ''};
12444         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12445         
12446     },
12447     // private
12448     onViewMove : function(e, t){
12449         this.inKeyMode = false;
12450     },
12451
12452     // private
12453     onViewOver : function(e, t){
12454         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12455             return;
12456         }
12457         var item = this.view.findItemFromChild(t);
12458         
12459         if(item){
12460             var index = this.view.indexOf(item);
12461             this.select(index, false);
12462         }
12463     },
12464
12465     // private
12466     onViewClick : function(view, doFocus, el, e)
12467     {
12468         var index = this.view.getSelectedIndexes()[0];
12469         
12470         var r = this.store.getAt(index);
12471         
12472         if(this.tickable){
12473             
12474             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12475                 return;
12476             }
12477             
12478             var rm = false;
12479             var _this = this;
12480             
12481             Roo.each(this.tickItems, function(v,k){
12482                 
12483                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12484                     _this.tickItems.splice(k, 1);
12485                     
12486                     if(typeof(e) == 'undefined' && view == false){
12487                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12488                     }
12489                     
12490                     rm = true;
12491                     return;
12492                 }
12493             });
12494             
12495             if(rm){
12496                 return;
12497             }
12498             
12499             this.tickItems.push(r.data);
12500             
12501             if(typeof(e) == 'undefined' && view == false){
12502                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12503             }
12504                     
12505             return;
12506         }
12507         
12508         if(r){
12509             this.onSelect(r, index);
12510         }
12511         if(doFocus !== false && !this.blockFocus){
12512             this.inputEl().focus();
12513         }
12514     },
12515
12516     // private
12517     restrictHeight : function(){
12518         //this.innerList.dom.style.height = '';
12519         //var inner = this.innerList.dom;
12520         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12521         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12522         //this.list.beginUpdate();
12523         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12524         this.list.alignTo(this.inputEl(), this.listAlign);
12525         this.list.alignTo(this.inputEl(), this.listAlign);
12526         //this.list.endUpdate();
12527     },
12528
12529     // private
12530     onEmptyResults : function(){
12531         
12532         if(this.tickable && this.editable){
12533             this.restrictHeight();
12534             return;
12535         }
12536         
12537         this.collapse();
12538     },
12539
12540     /**
12541      * Returns true if the dropdown list is expanded, else false.
12542      */
12543     isExpanded : function(){
12544         return this.list.isVisible();
12545     },
12546
12547     /**
12548      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12549      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12550      * @param {String} value The data value of the item to select
12551      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12552      * selected item if it is not currently in view (defaults to true)
12553      * @return {Boolean} True if the value matched an item in the list, else false
12554      */
12555     selectByValue : function(v, scrollIntoView){
12556         if(v !== undefined && v !== null){
12557             var r = this.findRecord(this.valueField || this.displayField, v);
12558             if(r){
12559                 this.select(this.store.indexOf(r), scrollIntoView);
12560                 return true;
12561             }
12562         }
12563         return false;
12564     },
12565
12566     /**
12567      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12568      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12569      * @param {Number} index The zero-based index of the list item to select
12570      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12571      * selected item if it is not currently in view (defaults to true)
12572      */
12573     select : function(index, scrollIntoView){
12574         this.selectedIndex = index;
12575         this.view.select(index);
12576         if(scrollIntoView !== false){
12577             var el = this.view.getNode(index);
12578             /*
12579              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12580              */
12581             if(el){
12582                 this.list.scrollChildIntoView(el, false);
12583             }
12584         }
12585     },
12586
12587     // private
12588     selectNext : function(){
12589         var ct = this.store.getCount();
12590         if(ct > 0){
12591             if(this.selectedIndex == -1){
12592                 this.select(0);
12593             }else if(this.selectedIndex < ct-1){
12594                 this.select(this.selectedIndex+1);
12595             }
12596         }
12597     },
12598
12599     // private
12600     selectPrev : function(){
12601         var ct = this.store.getCount();
12602         if(ct > 0){
12603             if(this.selectedIndex == -1){
12604                 this.select(0);
12605             }else if(this.selectedIndex != 0){
12606                 this.select(this.selectedIndex-1);
12607             }
12608         }
12609     },
12610
12611     // private
12612     onKeyUp : function(e){
12613         if(this.editable !== false && !e.isSpecialKey()){
12614             this.lastKey = e.getKey();
12615             this.dqTask.delay(this.queryDelay);
12616         }
12617     },
12618
12619     // private
12620     validateBlur : function(){
12621         return !this.list || !this.list.isVisible();   
12622     },
12623
12624     // private
12625     initQuery : function(){
12626         
12627         var v = this.getRawValue();
12628         
12629         if(this.tickable && this.editable){
12630             v = this.tickableInputEl().getValue();
12631         }
12632         
12633         this.doQuery(v);
12634     },
12635
12636     // private
12637     doForce : function(){
12638         if(this.inputEl().dom.value.length > 0){
12639             this.inputEl().dom.value =
12640                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12641              
12642         }
12643     },
12644
12645     /**
12646      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12647      * query allowing the query action to be canceled if needed.
12648      * @param {String} query The SQL query to execute
12649      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12650      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12651      * saved in the current store (defaults to false)
12652      */
12653     doQuery : function(q, forceAll){
12654         
12655         if(q === undefined || q === null){
12656             q = '';
12657         }
12658         var qe = {
12659             query: q,
12660             forceAll: forceAll,
12661             combo: this,
12662             cancel:false
12663         };
12664         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12665             return false;
12666         }
12667         q = qe.query;
12668         
12669         forceAll = qe.forceAll;
12670         if(forceAll === true || (q.length >= this.minChars)){
12671             
12672             this.hasQuery = true;
12673             
12674             if(this.lastQuery != q || this.alwaysQuery){
12675                 this.lastQuery = q;
12676                 if(this.mode == 'local'){
12677                     this.selectedIndex = -1;
12678                     if(forceAll){
12679                         this.store.clearFilter();
12680                     }else{
12681                         
12682                         if(this.specialFilter){
12683                             this.fireEvent('specialfilter', this);
12684                             this.onLoad();
12685                             return;
12686                         }
12687                         
12688                         this.store.filter(this.displayField, q);
12689                     }
12690                     
12691                     this.store.fireEvent("datachanged", this.store);
12692                     
12693                     this.onLoad();
12694                     
12695                     
12696                 }else{
12697                     
12698                     this.store.baseParams[this.queryParam] = q;
12699                     
12700                     var options = {params : this.getParams(q)};
12701                     
12702                     if(this.loadNext){
12703                         options.add = true;
12704                         options.params.start = this.page * this.pageSize;
12705                     }
12706                     
12707                     this.store.load(options);
12708                     
12709                     /*
12710                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12711                      *  we should expand the list on onLoad
12712                      *  so command out it
12713                      */
12714 //                    this.expand();
12715                 }
12716             }else{
12717                 this.selectedIndex = -1;
12718                 this.onLoad();   
12719             }
12720         }
12721         
12722         this.loadNext = false;
12723     },
12724     
12725     // private
12726     getParams : function(q){
12727         var p = {};
12728         //p[this.queryParam] = q;
12729         
12730         if(this.pageSize){
12731             p.start = 0;
12732             p.limit = this.pageSize;
12733         }
12734         return p;
12735     },
12736
12737     /**
12738      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12739      */
12740     collapse : function(){
12741         if(!this.isExpanded()){
12742             return;
12743         }
12744         
12745         this.list.hide();
12746         
12747         if(this.tickable){
12748             this.hasFocus = false;
12749             this.okBtn.hide();
12750             this.cancelBtn.hide();
12751             this.trigger.show();
12752             
12753             if(this.editable){
12754                 this.tickableInputEl().dom.value = '';
12755                 this.tickableInputEl().blur();
12756             }
12757             
12758         }
12759         
12760         Roo.get(document).un('mousedown', this.collapseIf, this);
12761         Roo.get(document).un('mousewheel', this.collapseIf, this);
12762         if (!this.editable) {
12763             Roo.get(document).un('keydown', this.listKeyPress, this);
12764         }
12765         this.fireEvent('collapse', this);
12766     },
12767
12768     // private
12769     collapseIf : function(e){
12770         var in_combo  = e.within(this.el);
12771         var in_list =  e.within(this.list);
12772         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12773         
12774         if (in_combo || in_list || is_list) {
12775             //e.stopPropagation();
12776             return;
12777         }
12778         
12779         if(this.tickable){
12780             this.onTickableFooterButtonClick(e, false, false);
12781         }
12782
12783         this.collapse();
12784         
12785     },
12786
12787     /**
12788      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12789      */
12790     expand : function(){
12791        
12792         if(this.isExpanded() || !this.hasFocus){
12793             return;
12794         }
12795         
12796         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12797         this.list.setWidth(lw);
12798         
12799         
12800          Roo.log('expand');
12801         
12802         this.list.show();
12803         
12804         this.restrictHeight();
12805         
12806         if(this.tickable){
12807             
12808             this.tickItems = Roo.apply([], this.item);
12809             
12810             this.okBtn.show();
12811             this.cancelBtn.show();
12812             this.trigger.hide();
12813             
12814             if(this.editable){
12815                 this.tickableInputEl().focus();
12816             }
12817             
12818         }
12819         
12820         Roo.get(document).on('mousedown', this.collapseIf, this);
12821         Roo.get(document).on('mousewheel', this.collapseIf, this);
12822         if (!this.editable) {
12823             Roo.get(document).on('keydown', this.listKeyPress, this);
12824         }
12825         
12826         this.fireEvent('expand', this);
12827     },
12828
12829     // private
12830     // Implements the default empty TriggerField.onTriggerClick function
12831     onTriggerClick : function(e)
12832     {
12833         Roo.log('trigger click');
12834         
12835         if(this.disabled || !this.triggerList){
12836             return;
12837         }
12838         
12839         this.page = 0;
12840         this.loadNext = false;
12841         
12842         if(this.isExpanded()){
12843             this.collapse();
12844             if (!this.blockFocus) {
12845                 this.inputEl().focus();
12846             }
12847             
12848         }else {
12849             this.hasFocus = true;
12850             if(this.triggerAction == 'all') {
12851                 this.doQuery(this.allQuery, true);
12852             } else {
12853                 this.doQuery(this.getRawValue());
12854             }
12855             if (!this.blockFocus) {
12856                 this.inputEl().focus();
12857             }
12858         }
12859     },
12860     
12861     onTickableTriggerClick : function(e)
12862     {
12863         if(this.disabled){
12864             return;
12865         }
12866         
12867         this.page = 0;
12868         this.loadNext = false;
12869         this.hasFocus = true;
12870         
12871         if(this.triggerAction == 'all') {
12872             this.doQuery(this.allQuery, true);
12873         } else {
12874             this.doQuery(this.getRawValue());
12875         }
12876     },
12877     
12878     onSearchFieldClick : function(e)
12879     {
12880         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12881             this.onTickableFooterButtonClick(e, false, false);
12882             return;
12883         }
12884         
12885         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12886             return;
12887         }
12888         
12889         this.page = 0;
12890         this.loadNext = false;
12891         this.hasFocus = true;
12892         
12893         if(this.triggerAction == 'all') {
12894             this.doQuery(this.allQuery, true);
12895         } else {
12896             this.doQuery(this.getRawValue());
12897         }
12898     },
12899     
12900     listKeyPress : function(e)
12901     {
12902         //Roo.log('listkeypress');
12903         // scroll to first matching element based on key pres..
12904         if (e.isSpecialKey()) {
12905             return false;
12906         }
12907         var k = String.fromCharCode(e.getKey()).toUpperCase();
12908         //Roo.log(k);
12909         var match  = false;
12910         var csel = this.view.getSelectedNodes();
12911         var cselitem = false;
12912         if (csel.length) {
12913             var ix = this.view.indexOf(csel[0]);
12914             cselitem  = this.store.getAt(ix);
12915             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12916                 cselitem = false;
12917             }
12918             
12919         }
12920         
12921         this.store.each(function(v) { 
12922             if (cselitem) {
12923                 // start at existing selection.
12924                 if (cselitem.id == v.id) {
12925                     cselitem = false;
12926                 }
12927                 return true;
12928             }
12929                 
12930             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12931                 match = this.store.indexOf(v);
12932                 return false;
12933             }
12934             return true;
12935         }, this);
12936         
12937         if (match === false) {
12938             return true; // no more action?
12939         }
12940         // scroll to?
12941         this.view.select(match);
12942         var sn = Roo.get(this.view.getSelectedNodes()[0])
12943         sn.scrollIntoView(sn.dom.parentNode, false);
12944     },
12945     
12946     onViewScroll : function(e, t){
12947         
12948         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){
12949             return;
12950         }
12951         
12952         this.hasQuery = true;
12953         
12954         this.loading = this.list.select('.loading', true).first();
12955         
12956         if(this.loading === null){
12957             this.list.createChild({
12958                 tag: 'div',
12959                 cls: 'loading select2-more-results select2-active',
12960                 html: 'Loading more results...'
12961             })
12962             
12963             this.loading = this.list.select('.loading', true).first();
12964             
12965             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12966             
12967             this.loading.hide();
12968         }
12969         
12970         this.loading.show();
12971         
12972         var _combo = this;
12973         
12974         this.page++;
12975         this.loadNext = true;
12976         
12977         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12978         
12979         return;
12980     },
12981     
12982     addItem : function(o)
12983     {   
12984         var dv = ''; // display value
12985         
12986         if (this.displayField) {
12987             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12988         } else {
12989             // this is an error condition!!!
12990             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12991         }
12992         
12993         if(!dv.length){
12994             return;
12995         }
12996         
12997         var choice = this.choices.createChild({
12998             tag: 'li',
12999             cls: 'select2-search-choice',
13000             cn: [
13001                 {
13002                     tag: 'div',
13003                     html: dv
13004                 },
13005                 {
13006                     tag: 'a',
13007                     href: '#',
13008                     cls: 'select2-search-choice-close',
13009                     tabindex: '-1'
13010                 }
13011             ]
13012             
13013         }, this.searchField);
13014         
13015         var close = choice.select('a.select2-search-choice-close', true).first()
13016         
13017         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13018         
13019         this.item.push(o);
13020         
13021         this.lastData = o;
13022         
13023         this.syncValue();
13024         
13025         this.inputEl().dom.value = '';
13026         
13027         this.validate();
13028     },
13029     
13030     onRemoveItem : function(e, _self, o)
13031     {
13032         e.preventDefault();
13033         
13034         this.lastItem = Roo.apply([], this.item);
13035         
13036         var index = this.item.indexOf(o.data) * 1;
13037         
13038         if( index < 0){
13039             Roo.log('not this item?!');
13040             return;
13041         }
13042         
13043         this.item.splice(index, 1);
13044         o.item.remove();
13045         
13046         this.syncValue();
13047         
13048         this.fireEvent('remove', this, e);
13049         
13050         this.validate();
13051         
13052     },
13053     
13054     syncValue : function()
13055     {
13056         if(!this.item.length){
13057             this.clearValue();
13058             return;
13059         }
13060             
13061         var value = [];
13062         var _this = this;
13063         Roo.each(this.item, function(i){
13064             if(_this.valueField){
13065                 value.push(i[_this.valueField]);
13066                 return;
13067             }
13068
13069             value.push(i);
13070         });
13071
13072         this.value = value.join(',');
13073
13074         if(this.hiddenField){
13075             this.hiddenField.dom.value = this.value;
13076         }
13077         
13078         this.store.fireEvent("datachanged", this.store);
13079     },
13080     
13081     clearItem : function()
13082     {
13083         if(!this.multiple){
13084             return;
13085         }
13086         
13087         this.item = [];
13088         
13089         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13090            c.remove();
13091         });
13092         
13093         this.syncValue();
13094         
13095         this.validate();
13096         
13097         if(this.tickable && !Roo.isTouch){
13098             this.view.refresh();
13099         }
13100     },
13101     
13102     inputEl: function ()
13103     {
13104         if(Roo.isTouch && this.mobileTouchView){
13105             return this.el.select('input.form-control',true).first();
13106         }
13107         
13108         if(this.tickable){
13109             return this.searchField;
13110         }
13111         
13112         return this.el.select('input.form-control',true).first();
13113     },
13114     
13115     
13116     onTickableFooterButtonClick : function(e, btn, el)
13117     {
13118         e.preventDefault();
13119         
13120         this.lastItem = Roo.apply([], this.item);
13121         
13122         if(btn && btn.name == 'cancel'){
13123             this.tickItems = Roo.apply([], this.item);
13124             this.collapse();
13125             return;
13126         }
13127         
13128         this.clearItem();
13129         
13130         var _this = this;
13131         
13132         Roo.each(this.tickItems, function(o){
13133             _this.addItem(o);
13134         });
13135         
13136         this.collapse();
13137         
13138     },
13139     
13140     validate : function()
13141     {
13142         var v = this.getRawValue();
13143         
13144         if(this.multiple){
13145             v = this.getValue();
13146         }
13147         
13148         if(this.disabled || this.allowBlank || v.length){
13149             this.markValid();
13150             return true;
13151         }
13152         
13153         this.markInvalid();
13154         return false;
13155     },
13156     
13157     tickableInputEl : function()
13158     {
13159         if(!this.tickable || !this.editable){
13160             return this.inputEl();
13161         }
13162         
13163         return this.inputEl().select('.select2-search-field-input', true).first();
13164     },
13165     
13166     
13167     getAutoCreateTouchView : function()
13168     {
13169         var id = Roo.id();
13170         
13171         var cfg = {
13172             cls: 'form-group' //input-group
13173         };
13174         
13175         var input =  {
13176             tag: 'input',
13177             id : id,
13178             type : this.inputType,
13179             cls : 'form-control x-combo-noedit',
13180             autocomplete: 'new-password',
13181             placeholder : this.placeholder || '',
13182             readonly : true
13183         };
13184         
13185         if (this.name) {
13186             input.name = this.name;
13187         }
13188         
13189         if (this.size) {
13190             input.cls += ' input-' + this.size;
13191         }
13192         
13193         if (this.disabled) {
13194             input.disabled = true;
13195         }
13196         
13197         var inputblock = {
13198             cls : '',
13199             cn : [
13200                 input
13201             ]
13202         };
13203         
13204         if(this.before){
13205             inputblock.cls += ' input-group';
13206             
13207             inputblock.cn.unshift({
13208                 tag :'span',
13209                 cls : 'input-group-addon',
13210                 html : this.before
13211             });
13212         }
13213         
13214         if(this.removable && !this.multiple){
13215             inputblock.cls += ' roo-removable';
13216             
13217             inputblock.cn.push({
13218                 tag: 'button',
13219                 html : 'x',
13220                 cls : 'roo-combo-removable-btn close'
13221             });
13222         }
13223
13224         if(this.hasFeedback && !this.allowBlank){
13225             
13226             inputblock.cls += ' has-feedback';
13227             
13228             inputblock.cn.push({
13229                 tag: 'span',
13230                 cls: 'glyphicon form-control-feedback'
13231             });
13232             
13233         }
13234         
13235         if (this.after) {
13236             
13237             inputblock.cls += (this.before) ? '' : ' input-group';
13238             
13239             inputblock.cn.push({
13240                 tag :'span',
13241                 cls : 'input-group-addon',
13242                 html : this.after
13243             });
13244         }
13245
13246         var box = {
13247             tag: 'div',
13248             cn: [
13249                 {
13250                     tag: 'input',
13251                     type : 'hidden',
13252                     cls: 'form-hidden-field'
13253                 },
13254                 inputblock
13255             ]
13256             
13257         };
13258         
13259         if(this.multiple){
13260             box = {
13261                 tag: 'div',
13262                 cn: [
13263                     {
13264                         tag: 'input',
13265                         type : 'hidden',
13266                         cls: 'form-hidden-field'
13267                     },
13268                     {
13269                         tag: 'ul',
13270                         cls: 'select2-choices',
13271                         cn:[
13272                             {
13273                                 tag: 'li',
13274                                 cls: 'select2-search-field',
13275                                 cn: [
13276
13277                                     inputblock
13278                                 ]
13279                             }
13280                         ]
13281                     }
13282                 ]
13283             }
13284         };
13285         
13286         var combobox = {
13287             cls: 'select2-container input-group',
13288             cn: [
13289                 box
13290             ]
13291         };
13292         
13293         if(this.multiple){
13294             combobox.cls += ' select2-container-multi';
13295         }
13296         
13297         var align = this.labelAlign || this.parentLabelAlign();
13298         
13299         cfg.cn = combobox;
13300         
13301         if(this.fieldLabel.length){
13302             
13303             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13304             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13305             
13306             cfg.cn = [
13307                 {
13308                     tag: 'label',
13309                     cls : 'control-label ' + lw,
13310                     html : this.fieldLabel
13311
13312                 },
13313                 {
13314                     cls : cw, 
13315                     cn: [
13316                         combobox
13317                     ]
13318                 }
13319             ];
13320         }
13321         
13322         var settings = this;
13323         
13324         ['xs','sm','md','lg'].map(function(size){
13325             if (settings[size]) {
13326                 cfg.cls += ' col-' + size + '-' + settings[size];
13327             }
13328         });
13329         
13330         return cfg;
13331     },
13332     
13333     initTouchView : function()
13334     {
13335         this.renderTouchView();
13336         
13337         this.touchViewEl.on('scroll', function(){
13338             this.el.dom.scrollTop = 0;
13339         }, this);
13340         
13341         this.inputEl().on("click", this.showTouchView, this);
13342         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13343         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13344         
13345         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13346         
13347         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13348         this.store.on('load', this.onTouchViewLoad, this);
13349         this.store.on('loadexception', this.onTouchViewLoadException, this);
13350         
13351         if(this.hiddenName){
13352             
13353             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13354             
13355             this.hiddenField.dom.value =
13356                 this.hiddenValue !== undefined ? this.hiddenValue :
13357                 this.value !== undefined ? this.value : '';
13358         
13359             this.el.dom.removeAttribute('name');
13360             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13361         }
13362         
13363         if(this.multiple){
13364             this.choices = this.el.select('ul.select2-choices', true).first();
13365             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13366         }
13367         
13368         if(this.removable && !this.multiple){
13369             var close = this.closeTriggerEl();
13370             if(close){
13371                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13372                 close.on('click', this.removeBtnClick, this, close);
13373             }
13374         }
13375         
13376         return;
13377         
13378         
13379     },
13380     
13381     renderTouchView : function()
13382     {
13383         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13384         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13385         
13386         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13387         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13388         
13389         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13390         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13391         this.touchViewBodyEl.setStyle('overflow', 'auto');
13392         
13393         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13394         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13395         
13396         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13397         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13398         
13399     },
13400     
13401     showTouchView : function()
13402     {
13403         this.touchViewHeaderEl.hide();
13404
13405         if(this.fieldLabel.length){
13406             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13407             this.touchViewHeaderEl.show();
13408         }
13409
13410         this.touchViewEl.show();
13411
13412         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13413         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13414
13415         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13416
13417         if(this.fieldLabel.length){
13418             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13419         }
13420         
13421         this.touchViewBodyEl.setHeight(bodyHeight);
13422
13423         if(this.animate){
13424             var _this = this;
13425             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13426         }else{
13427             this.touchViewEl.addClass('in');
13428         }
13429
13430         this.doTouchViewQuery();
13431         
13432     },
13433     
13434     hideTouchView : function()
13435     {
13436         this.touchViewEl.removeClass('in');
13437
13438         if(this.animate){
13439             var _this = this;
13440             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13441         }else{
13442             this.touchViewEl.setStyle('display', 'none');
13443         }
13444         
13445     },
13446     
13447     setTouchViewValue : function()
13448     {
13449         if(this.multiple){
13450             this.clearItem();
13451         
13452             var _this = this;
13453
13454             Roo.each(this.tickItems, function(o){
13455                 this.addItem(o);
13456             }, this);
13457         }
13458         
13459         this.hideTouchView();
13460     },
13461     
13462     doTouchViewQuery : function()
13463     {
13464         var qe = {
13465             query: '',
13466             forceAll: true,
13467             combo: this,
13468             cancel:false
13469         };
13470         
13471         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13472             return false;
13473         }
13474         
13475         if(!this.alwaysQuery || this.mode == 'local'){
13476             this.onTouchViewLoad();
13477             return;
13478         }
13479         
13480         this.store.load();
13481     },
13482     
13483     onTouchViewBeforeLoad : function(combo,opts)
13484     {
13485         return;
13486     },
13487
13488     // private
13489     onTouchViewLoad : function()
13490     {
13491         if(this.store.getCount() < 1){
13492             this.onTouchViewEmptyResults();
13493             return;
13494         }
13495         
13496         this.clearTouchView();
13497         
13498         var rawValue = this.getRawValue();
13499         
13500         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13501         
13502         this.tickItems = [];
13503         
13504         this.store.data.each(function(d, rowIndex){
13505             var row = this.touchViewListGroup.createChild(template);
13506             
13507             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13508                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13509             }
13510             
13511             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13512                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13513             }
13514             
13515             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13516                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13517                 this.tickItems.push(d.data);
13518             }
13519             
13520             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13521             
13522         }, this);
13523         
13524         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13525         
13526         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13527
13528         if(this.fieldLabel.length){
13529             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13530         }
13531
13532         var listHeight = this.touchViewListGroup.getHeight();
13533         
13534         var _this = this;
13535         
13536         if(firstChecked && listHeight > bodyHeight){
13537             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13538         }
13539         
13540     },
13541     
13542     onTouchViewLoadException : function()
13543     {
13544         this.hideTouchView();
13545     },
13546     
13547     onTouchViewEmptyResults : function()
13548     {
13549         this.clearTouchView();
13550         
13551         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13552         
13553         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13554         
13555     },
13556     
13557     clearTouchView : function()
13558     {
13559         this.touchViewListGroup.dom.innerHTML = '';
13560     },
13561     
13562     onTouchViewClick : function(e, el, o)
13563     {
13564         e.preventDefault();
13565         
13566         var row = o.row;
13567         var rowIndex = o.rowIndex;
13568         
13569         var r = this.store.getAt(rowIndex);
13570         
13571         if(!this.multiple){
13572             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13573                 c.dom.removeAttribute('checked');
13574             }, this);
13575             
13576             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13577         
13578             this.setFromData(r.data);
13579             
13580             var close = this.closeTriggerEl();
13581         
13582             if(close){
13583                 close.show();
13584             }
13585
13586             this.hideTouchView();
13587             
13588             this.fireEvent('select', this, r, rowIndex);
13589             
13590             return;
13591         }
13592         
13593         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13594             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13595             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13596             return;
13597         }
13598         
13599         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13600         this.addItem(r.data);
13601         this.tickItems.push(r.data);
13602         
13603     }
13604     
13605
13606     /** 
13607     * @cfg {Boolean} grow 
13608     * @hide 
13609     */
13610     /** 
13611     * @cfg {Number} growMin 
13612     * @hide 
13613     */
13614     /** 
13615     * @cfg {Number} growMax 
13616     * @hide 
13617     */
13618     /**
13619      * @hide
13620      * @method autoSize
13621      */
13622 });
13623
13624 Roo.apply(Roo.bootstrap.ComboBox,  {
13625     
13626     header : {
13627         tag: 'div',
13628         cls: 'modal-header',
13629         cn: [
13630             {
13631                 tag: 'h4',
13632                 cls: 'modal-title'
13633             }
13634         ]
13635     },
13636     
13637     body : {
13638         tag: 'div',
13639         cls: 'modal-body',
13640         cn: [
13641             {
13642                 tag: 'ul',
13643                 cls: 'list-group'
13644             }
13645         ]
13646     },
13647     
13648     listItemRadio : {
13649         tag: 'li',
13650         cls: 'list-group-item',
13651         cn: [
13652             {
13653                 tag: 'span',
13654                 cls: 'roo-combobox-list-group-item-value'
13655             },
13656             {
13657                 tag: 'div',
13658                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13659                 cn: [
13660                     {
13661                         tag: 'input',
13662                         type: 'radio'
13663                     },
13664                     {
13665                         tag: 'label'
13666                     }
13667                 ]
13668             }
13669         ]
13670     },
13671     
13672     listItemCheckbox : {
13673         tag: 'li',
13674         cls: 'list-group-item',
13675         cn: [
13676             {
13677                 tag: 'span',
13678                 cls: 'roo-combobox-list-group-item-value'
13679             },
13680             {
13681                 tag: 'div',
13682                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13683                 cn: [
13684                     {
13685                         tag: 'input',
13686                         type: 'checkbox'
13687                     },
13688                     {
13689                         tag: 'label'
13690                     }
13691                 ]
13692             }
13693         ]
13694     },
13695     
13696     emptyResult : {
13697         tag: 'div',
13698         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13699     },
13700     
13701     footer : {
13702         tag: 'div',
13703         cls: 'modal-footer',
13704         cn: [
13705             {
13706                 tag: 'div',
13707                 cls: 'row',
13708                 cn: [
13709                     {
13710                         tag: 'div',
13711                         cls: 'col-xs-6 text-left',
13712                         cn: {
13713                             tag: 'button',
13714                             cls: 'btn btn-danger roo-touch-view-cancel',
13715                             html: 'Cancel'
13716                         }
13717                     },
13718                     {
13719                         tag: 'div',
13720                         cls: 'col-xs-6 text-right',
13721                         cn: {
13722                             tag: 'button',
13723                             cls: 'btn btn-success roo-touch-view-ok',
13724                             html: 'OK'
13725                         }
13726                     }
13727                 ]
13728             }
13729         ]
13730         
13731     }
13732 });
13733
13734 Roo.apply(Roo.bootstrap.ComboBox,  {
13735     
13736     touchViewTemplate : {
13737         tag: 'div',
13738         cls: 'modal fade roo-combobox-touch-view',
13739         cn: [
13740             {
13741                 tag: 'div',
13742                 cls: 'modal-dialog',
13743                 cn: [
13744                     {
13745                         tag: 'div',
13746                         cls: 'modal-content',
13747                         cn: [
13748                             Roo.bootstrap.ComboBox.header,
13749                             Roo.bootstrap.ComboBox.body,
13750                             Roo.bootstrap.ComboBox.footer
13751                         ]
13752                     }
13753                 ]
13754             }
13755         ]
13756     }
13757 });/*
13758  * Based on:
13759  * Ext JS Library 1.1.1
13760  * Copyright(c) 2006-2007, Ext JS, LLC.
13761  *
13762  * Originally Released Under LGPL - original licence link has changed is not relivant.
13763  *
13764  * Fork - LGPL
13765  * <script type="text/javascript">
13766  */
13767
13768 /**
13769  * @class Roo.View
13770  * @extends Roo.util.Observable
13771  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13772  * This class also supports single and multi selection modes. <br>
13773  * Create a data model bound view:
13774  <pre><code>
13775  var store = new Roo.data.Store(...);
13776
13777  var view = new Roo.View({
13778     el : "my-element",
13779     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13780  
13781     singleSelect: true,
13782     selectedClass: "ydataview-selected",
13783     store: store
13784  });
13785
13786  // listen for node click?
13787  view.on("click", function(vw, index, node, e){
13788  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13789  });
13790
13791  // load XML data
13792  dataModel.load("foobar.xml");
13793  </code></pre>
13794  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13795  * <br><br>
13796  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13797  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13798  * 
13799  * Note: old style constructor is still suported (container, template, config)
13800  * 
13801  * @constructor
13802  * Create a new View
13803  * @param {Object} config The config object
13804  * 
13805  */
13806 Roo.View = function(config, depreciated_tpl, depreciated_config){
13807     
13808     this.parent = false;
13809     
13810     if (typeof(depreciated_tpl) == 'undefined') {
13811         // new way.. - universal constructor.
13812         Roo.apply(this, config);
13813         this.el  = Roo.get(this.el);
13814     } else {
13815         // old format..
13816         this.el  = Roo.get(config);
13817         this.tpl = depreciated_tpl;
13818         Roo.apply(this, depreciated_config);
13819     }
13820     this.wrapEl  = this.el.wrap().wrap();
13821     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13822     
13823     
13824     if(typeof(this.tpl) == "string"){
13825         this.tpl = new Roo.Template(this.tpl);
13826     } else {
13827         // support xtype ctors..
13828         this.tpl = new Roo.factory(this.tpl, Roo);
13829     }
13830     
13831     
13832     this.tpl.compile();
13833     
13834     /** @private */
13835     this.addEvents({
13836         /**
13837          * @event beforeclick
13838          * Fires before a click is processed. Returns false to cancel the default action.
13839          * @param {Roo.View} this
13840          * @param {Number} index The index of the target node
13841          * @param {HTMLElement} node The target node
13842          * @param {Roo.EventObject} e The raw event object
13843          */
13844             "beforeclick" : true,
13845         /**
13846          * @event click
13847          * Fires when a template node is clicked.
13848          * @param {Roo.View} this
13849          * @param {Number} index The index of the target node
13850          * @param {HTMLElement} node The target node
13851          * @param {Roo.EventObject} e The raw event object
13852          */
13853             "click" : true,
13854         /**
13855          * @event dblclick
13856          * Fires when a template node is double clicked.
13857          * @param {Roo.View} this
13858          * @param {Number} index The index of the target node
13859          * @param {HTMLElement} node The target node
13860          * @param {Roo.EventObject} e The raw event object
13861          */
13862             "dblclick" : true,
13863         /**
13864          * @event contextmenu
13865          * Fires when a template node is right clicked.
13866          * @param {Roo.View} this
13867          * @param {Number} index The index of the target node
13868          * @param {HTMLElement} node The target node
13869          * @param {Roo.EventObject} e The raw event object
13870          */
13871             "contextmenu" : true,
13872         /**
13873          * @event selectionchange
13874          * Fires when the selected nodes change.
13875          * @param {Roo.View} this
13876          * @param {Array} selections Array of the selected nodes
13877          */
13878             "selectionchange" : true,
13879     
13880         /**
13881          * @event beforeselect
13882          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13883          * @param {Roo.View} this
13884          * @param {HTMLElement} node The node to be selected
13885          * @param {Array} selections Array of currently selected nodes
13886          */
13887             "beforeselect" : true,
13888         /**
13889          * @event preparedata
13890          * Fires on every row to render, to allow you to change the data.
13891          * @param {Roo.View} this
13892          * @param {Object} data to be rendered (change this)
13893          */
13894           "preparedata" : true
13895           
13896           
13897         });
13898
13899
13900
13901     this.el.on({
13902         "click": this.onClick,
13903         "dblclick": this.onDblClick,
13904         "contextmenu": this.onContextMenu,
13905         scope:this
13906     });
13907
13908     this.selections = [];
13909     this.nodes = [];
13910     this.cmp = new Roo.CompositeElementLite([]);
13911     if(this.store){
13912         this.store = Roo.factory(this.store, Roo.data);
13913         this.setStore(this.store, true);
13914     }
13915     
13916     if ( this.footer && this.footer.xtype) {
13917            
13918          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13919         
13920         this.footer.dataSource = this.store;
13921         this.footer.container = fctr;
13922         this.footer = Roo.factory(this.footer, Roo);
13923         fctr.insertFirst(this.el);
13924         
13925         // this is a bit insane - as the paging toolbar seems to detach the el..
13926 //        dom.parentNode.parentNode.parentNode
13927          // they get detached?
13928     }
13929     
13930     
13931     Roo.View.superclass.constructor.call(this);
13932     
13933     
13934 };
13935
13936 Roo.extend(Roo.View, Roo.util.Observable, {
13937     
13938      /**
13939      * @cfg {Roo.data.Store} store Data store to load data from.
13940      */
13941     store : false,
13942     
13943     /**
13944      * @cfg {String|Roo.Element} el The container element.
13945      */
13946     el : '',
13947     
13948     /**
13949      * @cfg {String|Roo.Template} tpl The template used by this View 
13950      */
13951     tpl : false,
13952     /**
13953      * @cfg {String} dataName the named area of the template to use as the data area
13954      *                          Works with domtemplates roo-name="name"
13955      */
13956     dataName: false,
13957     /**
13958      * @cfg {String} selectedClass The css class to add to selected nodes
13959      */
13960     selectedClass : "x-view-selected",
13961      /**
13962      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13963      */
13964     emptyText : "",
13965     
13966     /**
13967      * @cfg {String} text to display on mask (default Loading)
13968      */
13969     mask : false,
13970     /**
13971      * @cfg {Boolean} multiSelect Allow multiple selection
13972      */
13973     multiSelect : false,
13974     /**
13975      * @cfg {Boolean} singleSelect Allow single selection
13976      */
13977     singleSelect:  false,
13978     
13979     /**
13980      * @cfg {Boolean} toggleSelect - selecting 
13981      */
13982     toggleSelect : false,
13983     
13984     /**
13985      * @cfg {Boolean} tickable - selecting 
13986      */
13987     tickable : false,
13988     
13989     /**
13990      * Returns the element this view is bound to.
13991      * @return {Roo.Element}
13992      */
13993     getEl : function(){
13994         return this.wrapEl;
13995     },
13996     
13997     
13998
13999     /**
14000      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14001      */
14002     refresh : function(){
14003         //Roo.log('refresh');
14004         var t = this.tpl;
14005         
14006         // if we are using something like 'domtemplate', then
14007         // the what gets used is:
14008         // t.applySubtemplate(NAME, data, wrapping data..)
14009         // the outer template then get' applied with
14010         //     the store 'extra data'
14011         // and the body get's added to the
14012         //      roo-name="data" node?
14013         //      <span class='roo-tpl-{name}'></span> ?????
14014         
14015         
14016         
14017         this.clearSelections();
14018         this.el.update("");
14019         var html = [];
14020         var records = this.store.getRange();
14021         if(records.length < 1) {
14022             
14023             // is this valid??  = should it render a template??
14024             
14025             this.el.update(this.emptyText);
14026             return;
14027         }
14028         var el = this.el;
14029         if (this.dataName) {
14030             this.el.update(t.apply(this.store.meta)); //????
14031             el = this.el.child('.roo-tpl-' + this.dataName);
14032         }
14033         
14034         for(var i = 0, len = records.length; i < len; i++){
14035             var data = this.prepareData(records[i].data, i, records[i]);
14036             this.fireEvent("preparedata", this, data, i, records[i]);
14037             
14038             var d = Roo.apply({}, data);
14039             
14040             if(this.tickable){
14041                 Roo.apply(d, {'roo-id' : Roo.id()});
14042                 
14043                 var _this = this;
14044             
14045                 Roo.each(this.parent.item, function(item){
14046                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14047                         return;
14048                     }
14049                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14050                 });
14051             }
14052             
14053             html[html.length] = Roo.util.Format.trim(
14054                 this.dataName ?
14055                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14056                     t.apply(d)
14057             );
14058         }
14059         
14060         
14061         
14062         el.update(html.join(""));
14063         this.nodes = el.dom.childNodes;
14064         this.updateIndexes(0);
14065     },
14066     
14067
14068     /**
14069      * Function to override to reformat the data that is sent to
14070      * the template for each node.
14071      * DEPRICATED - use the preparedata event handler.
14072      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14073      * a JSON object for an UpdateManager bound view).
14074      */
14075     prepareData : function(data, index, record)
14076     {
14077         this.fireEvent("preparedata", this, data, index, record);
14078         return data;
14079     },
14080
14081     onUpdate : function(ds, record){
14082         // Roo.log('on update');   
14083         this.clearSelections();
14084         var index = this.store.indexOf(record);
14085         var n = this.nodes[index];
14086         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14087         n.parentNode.removeChild(n);
14088         this.updateIndexes(index, index);
14089     },
14090
14091     
14092     
14093 // --------- FIXME     
14094     onAdd : function(ds, records, index)
14095     {
14096         //Roo.log(['on Add', ds, records, index] );        
14097         this.clearSelections();
14098         if(this.nodes.length == 0){
14099             this.refresh();
14100             return;
14101         }
14102         var n = this.nodes[index];
14103         for(var i = 0, len = records.length; i < len; i++){
14104             var d = this.prepareData(records[i].data, i, records[i]);
14105             if(n){
14106                 this.tpl.insertBefore(n, d);
14107             }else{
14108                 
14109                 this.tpl.append(this.el, d);
14110             }
14111         }
14112         this.updateIndexes(index);
14113     },
14114
14115     onRemove : function(ds, record, index){
14116        // Roo.log('onRemove');
14117         this.clearSelections();
14118         var el = this.dataName  ?
14119             this.el.child('.roo-tpl-' + this.dataName) :
14120             this.el; 
14121         
14122         el.dom.removeChild(this.nodes[index]);
14123         this.updateIndexes(index);
14124     },
14125
14126     /**
14127      * Refresh an individual node.
14128      * @param {Number} index
14129      */
14130     refreshNode : function(index){
14131         this.onUpdate(this.store, this.store.getAt(index));
14132     },
14133
14134     updateIndexes : function(startIndex, endIndex){
14135         var ns = this.nodes;
14136         startIndex = startIndex || 0;
14137         endIndex = endIndex || ns.length - 1;
14138         for(var i = startIndex; i <= endIndex; i++){
14139             ns[i].nodeIndex = i;
14140         }
14141     },
14142
14143     /**
14144      * Changes the data store this view uses and refresh the view.
14145      * @param {Store} store
14146      */
14147     setStore : function(store, initial){
14148         if(!initial && this.store){
14149             this.store.un("datachanged", this.refresh);
14150             this.store.un("add", this.onAdd);
14151             this.store.un("remove", this.onRemove);
14152             this.store.un("update", this.onUpdate);
14153             this.store.un("clear", this.refresh);
14154             this.store.un("beforeload", this.onBeforeLoad);
14155             this.store.un("load", this.onLoad);
14156             this.store.un("loadexception", this.onLoad);
14157         }
14158         if(store){
14159           
14160             store.on("datachanged", this.refresh, this);
14161             store.on("add", this.onAdd, this);
14162             store.on("remove", this.onRemove, this);
14163             store.on("update", this.onUpdate, this);
14164             store.on("clear", this.refresh, this);
14165             store.on("beforeload", this.onBeforeLoad, this);
14166             store.on("load", this.onLoad, this);
14167             store.on("loadexception", this.onLoad, this);
14168         }
14169         
14170         if(store){
14171             this.refresh();
14172         }
14173     },
14174     /**
14175      * onbeforeLoad - masks the loading area.
14176      *
14177      */
14178     onBeforeLoad : function(store,opts)
14179     {
14180          //Roo.log('onBeforeLoad');   
14181         if (!opts.add) {
14182             this.el.update("");
14183         }
14184         this.el.mask(this.mask ? this.mask : "Loading" ); 
14185     },
14186     onLoad : function ()
14187     {
14188         this.el.unmask();
14189     },
14190     
14191
14192     /**
14193      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14194      * @param {HTMLElement} node
14195      * @return {HTMLElement} The template node
14196      */
14197     findItemFromChild : function(node){
14198         var el = this.dataName  ?
14199             this.el.child('.roo-tpl-' + this.dataName,true) :
14200             this.el.dom; 
14201         
14202         if(!node || node.parentNode == el){
14203                     return node;
14204             }
14205             var p = node.parentNode;
14206             while(p && p != el){
14207             if(p.parentNode == el){
14208                 return p;
14209             }
14210             p = p.parentNode;
14211         }
14212             return null;
14213     },
14214
14215     /** @ignore */
14216     onClick : function(e){
14217         var item = this.findItemFromChild(e.getTarget());
14218         if(item){
14219             var index = this.indexOf(item);
14220             if(this.onItemClick(item, index, e) !== false){
14221                 this.fireEvent("click", this, index, item, e);
14222             }
14223         }else{
14224             this.clearSelections();
14225         }
14226     },
14227
14228     /** @ignore */
14229     onContextMenu : function(e){
14230         var item = this.findItemFromChild(e.getTarget());
14231         if(item){
14232             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14233         }
14234     },
14235
14236     /** @ignore */
14237     onDblClick : function(e){
14238         var item = this.findItemFromChild(e.getTarget());
14239         if(item){
14240             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14241         }
14242     },
14243
14244     onItemClick : function(item, index, e)
14245     {
14246         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14247             return false;
14248         }
14249         if (this.toggleSelect) {
14250             var m = this.isSelected(item) ? 'unselect' : 'select';
14251             //Roo.log(m);
14252             var _t = this;
14253             _t[m](item, true, false);
14254             return true;
14255         }
14256         if(this.multiSelect || this.singleSelect){
14257             if(this.multiSelect && e.shiftKey && this.lastSelection){
14258                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14259             }else{
14260                 this.select(item, this.multiSelect && e.ctrlKey);
14261                 this.lastSelection = item;
14262             }
14263             
14264             if(!this.tickable){
14265                 e.preventDefault();
14266             }
14267             
14268         }
14269         return true;
14270     },
14271
14272     /**
14273      * Get the number of selected nodes.
14274      * @return {Number}
14275      */
14276     getSelectionCount : function(){
14277         return this.selections.length;
14278     },
14279
14280     /**
14281      * Get the currently selected nodes.
14282      * @return {Array} An array of HTMLElements
14283      */
14284     getSelectedNodes : function(){
14285         return this.selections;
14286     },
14287
14288     /**
14289      * Get the indexes of the selected nodes.
14290      * @return {Array}
14291      */
14292     getSelectedIndexes : function(){
14293         var indexes = [], s = this.selections;
14294         for(var i = 0, len = s.length; i < len; i++){
14295             indexes.push(s[i].nodeIndex);
14296         }
14297         return indexes;
14298     },
14299
14300     /**
14301      * Clear all selections
14302      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14303      */
14304     clearSelections : function(suppressEvent){
14305         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14306             this.cmp.elements = this.selections;
14307             this.cmp.removeClass(this.selectedClass);
14308             this.selections = [];
14309             if(!suppressEvent){
14310                 this.fireEvent("selectionchange", this, this.selections);
14311             }
14312         }
14313     },
14314
14315     /**
14316      * Returns true if the passed node is selected
14317      * @param {HTMLElement/Number} node The node or node index
14318      * @return {Boolean}
14319      */
14320     isSelected : function(node){
14321         var s = this.selections;
14322         if(s.length < 1){
14323             return false;
14324         }
14325         node = this.getNode(node);
14326         return s.indexOf(node) !== -1;
14327     },
14328
14329     /**
14330      * Selects nodes.
14331      * @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
14332      * @param {Boolean} keepExisting (optional) true to keep existing selections
14333      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14334      */
14335     select : function(nodeInfo, keepExisting, suppressEvent){
14336         if(nodeInfo instanceof Array){
14337             if(!keepExisting){
14338                 this.clearSelections(true);
14339             }
14340             for(var i = 0, len = nodeInfo.length; i < len; i++){
14341                 this.select(nodeInfo[i], true, true);
14342             }
14343             return;
14344         } 
14345         var node = this.getNode(nodeInfo);
14346         if(!node || this.isSelected(node)){
14347             return; // already selected.
14348         }
14349         if(!keepExisting){
14350             this.clearSelections(true);
14351         }
14352         
14353         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14354             Roo.fly(node).addClass(this.selectedClass);
14355             this.selections.push(node);
14356             if(!suppressEvent){
14357                 this.fireEvent("selectionchange", this, this.selections);
14358             }
14359         }
14360         
14361         
14362     },
14363       /**
14364      * Unselects nodes.
14365      * @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
14366      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14367      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14368      */
14369     unselect : function(nodeInfo, keepExisting, suppressEvent)
14370     {
14371         if(nodeInfo instanceof Array){
14372             Roo.each(this.selections, function(s) {
14373                 this.unselect(s, nodeInfo);
14374             }, this);
14375             return;
14376         }
14377         var node = this.getNode(nodeInfo);
14378         if(!node || !this.isSelected(node)){
14379             //Roo.log("not selected");
14380             return; // not selected.
14381         }
14382         // fireevent???
14383         var ns = [];
14384         Roo.each(this.selections, function(s) {
14385             if (s == node ) {
14386                 Roo.fly(node).removeClass(this.selectedClass);
14387
14388                 return;
14389             }
14390             ns.push(s);
14391         },this);
14392         
14393         this.selections= ns;
14394         this.fireEvent("selectionchange", this, this.selections);
14395     },
14396
14397     /**
14398      * Gets a template node.
14399      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14400      * @return {HTMLElement} The node or null if it wasn't found
14401      */
14402     getNode : function(nodeInfo){
14403         if(typeof nodeInfo == "string"){
14404             return document.getElementById(nodeInfo);
14405         }else if(typeof nodeInfo == "number"){
14406             return this.nodes[nodeInfo];
14407         }
14408         return nodeInfo;
14409     },
14410
14411     /**
14412      * Gets a range template nodes.
14413      * @param {Number} startIndex
14414      * @param {Number} endIndex
14415      * @return {Array} An array of nodes
14416      */
14417     getNodes : function(start, end){
14418         var ns = this.nodes;
14419         start = start || 0;
14420         end = typeof end == "undefined" ? ns.length - 1 : end;
14421         var nodes = [];
14422         if(start <= end){
14423             for(var i = start; i <= end; i++){
14424                 nodes.push(ns[i]);
14425             }
14426         } else{
14427             for(var i = start; i >= end; i--){
14428                 nodes.push(ns[i]);
14429             }
14430         }
14431         return nodes;
14432     },
14433
14434     /**
14435      * Finds the index of the passed node
14436      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14437      * @return {Number} The index of the node or -1
14438      */
14439     indexOf : function(node){
14440         node = this.getNode(node);
14441         if(typeof node.nodeIndex == "number"){
14442             return node.nodeIndex;
14443         }
14444         var ns = this.nodes;
14445         for(var i = 0, len = ns.length; i < len; i++){
14446             if(ns[i] == node){
14447                 return i;
14448             }
14449         }
14450         return -1;
14451     }
14452 });
14453 /*
14454  * - LGPL
14455  *
14456  * based on jquery fullcalendar
14457  * 
14458  */
14459
14460 Roo.bootstrap = Roo.bootstrap || {};
14461 /**
14462  * @class Roo.bootstrap.Calendar
14463  * @extends Roo.bootstrap.Component
14464  * Bootstrap Calendar class
14465  * @cfg {Boolean} loadMask (true|false) default false
14466  * @cfg {Object} header generate the user specific header of the calendar, default false
14467
14468  * @constructor
14469  * Create a new Container
14470  * @param {Object} config The config object
14471  */
14472
14473
14474
14475 Roo.bootstrap.Calendar = function(config){
14476     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14477      this.addEvents({
14478         /**
14479              * @event select
14480              * Fires when a date is selected
14481              * @param {DatePicker} this
14482              * @param {Date} date The selected date
14483              */
14484         'select': true,
14485         /**
14486              * @event monthchange
14487              * Fires when the displayed month changes 
14488              * @param {DatePicker} this
14489              * @param {Date} date The selected month
14490              */
14491         'monthchange': true,
14492         /**
14493              * @event evententer
14494              * Fires when mouse over an event
14495              * @param {Calendar} this
14496              * @param {event} Event
14497              */
14498         'evententer': true,
14499         /**
14500              * @event eventleave
14501              * Fires when the mouse leaves an
14502              * @param {Calendar} this
14503              * @param {event}
14504              */
14505         'eventleave': true,
14506         /**
14507              * @event eventclick
14508              * Fires when the mouse click an
14509              * @param {Calendar} this
14510              * @param {event}
14511              */
14512         'eventclick': true
14513         
14514     });
14515
14516 };
14517
14518 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14519     
14520      /**
14521      * @cfg {Number} startDay
14522      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14523      */
14524     startDay : 0,
14525     
14526     loadMask : false,
14527     
14528     header : false,
14529       
14530     getAutoCreate : function(){
14531         
14532         
14533         var fc_button = function(name, corner, style, content ) {
14534             return Roo.apply({},{
14535                 tag : 'span',
14536                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14537                          (corner.length ?
14538                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14539                             ''
14540                         ),
14541                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14542                 unselectable: 'on'
14543             });
14544         };
14545         
14546         var header = {};
14547         
14548         if(!this.header){
14549             header = {
14550                 tag : 'table',
14551                 cls : 'fc-header',
14552                 style : 'width:100%',
14553                 cn : [
14554                     {
14555                         tag: 'tr',
14556                         cn : [
14557                             {
14558                                 tag : 'td',
14559                                 cls : 'fc-header-left',
14560                                 cn : [
14561                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14562                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14563                                     { tag: 'span', cls: 'fc-header-space' },
14564                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14565
14566
14567                                 ]
14568                             },
14569
14570                             {
14571                                 tag : 'td',
14572                                 cls : 'fc-header-center',
14573                                 cn : [
14574                                     {
14575                                         tag: 'span',
14576                                         cls: 'fc-header-title',
14577                                         cn : {
14578                                             tag: 'H2',
14579                                             html : 'month / year'
14580                                         }
14581                                     }
14582
14583                                 ]
14584                             },
14585                             {
14586                                 tag : 'td',
14587                                 cls : 'fc-header-right',
14588                                 cn : [
14589                               /*      fc_button('month', 'left', '', 'month' ),
14590                                     fc_button('week', '', '', 'week' ),
14591                                     fc_button('day', 'right', '', 'day' )
14592                                 */    
14593
14594                                 ]
14595                             }
14596
14597                         ]
14598                     }
14599                 ]
14600             };
14601         }
14602         
14603         header = this.header;
14604         
14605        
14606         var cal_heads = function() {
14607             var ret = [];
14608             // fixme - handle this.
14609             
14610             for (var i =0; i < Date.dayNames.length; i++) {
14611                 var d = Date.dayNames[i];
14612                 ret.push({
14613                     tag: 'th',
14614                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14615                     html : d.substring(0,3)
14616                 });
14617                 
14618             }
14619             ret[0].cls += ' fc-first';
14620             ret[6].cls += ' fc-last';
14621             return ret;
14622         };
14623         var cal_cell = function(n) {
14624             return  {
14625                 tag: 'td',
14626                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14627                 cn : [
14628                     {
14629                         cn : [
14630                             {
14631                                 cls: 'fc-day-number',
14632                                 html: 'D'
14633                             },
14634                             {
14635                                 cls: 'fc-day-content',
14636                              
14637                                 cn : [
14638                                      {
14639                                         style: 'position: relative;' // height: 17px;
14640                                     }
14641                                 ]
14642                             }
14643                             
14644                             
14645                         ]
14646                     }
14647                 ]
14648                 
14649             }
14650         };
14651         var cal_rows = function() {
14652             
14653             var ret = [];
14654             for (var r = 0; r < 6; r++) {
14655                 var row= {
14656                     tag : 'tr',
14657                     cls : 'fc-week',
14658                     cn : []
14659                 };
14660                 
14661                 for (var i =0; i < Date.dayNames.length; i++) {
14662                     var d = Date.dayNames[i];
14663                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14664
14665                 }
14666                 row.cn[0].cls+=' fc-first';
14667                 row.cn[0].cn[0].style = 'min-height:90px';
14668                 row.cn[6].cls+=' fc-last';
14669                 ret.push(row);
14670                 
14671             }
14672             ret[0].cls += ' fc-first';
14673             ret[4].cls += ' fc-prev-last';
14674             ret[5].cls += ' fc-last';
14675             return ret;
14676             
14677         };
14678         
14679         var cal_table = {
14680             tag: 'table',
14681             cls: 'fc-border-separate',
14682             style : 'width:100%',
14683             cellspacing  : 0,
14684             cn : [
14685                 { 
14686                     tag: 'thead',
14687                     cn : [
14688                         { 
14689                             tag: 'tr',
14690                             cls : 'fc-first fc-last',
14691                             cn : cal_heads()
14692                         }
14693                     ]
14694                 },
14695                 { 
14696                     tag: 'tbody',
14697                     cn : cal_rows()
14698                 }
14699                   
14700             ]
14701         };
14702          
14703          var cfg = {
14704             cls : 'fc fc-ltr',
14705             cn : [
14706                 header,
14707                 {
14708                     cls : 'fc-content',
14709                     style : "position: relative;",
14710                     cn : [
14711                         {
14712                             cls : 'fc-view fc-view-month fc-grid',
14713                             style : 'position: relative',
14714                             unselectable : 'on',
14715                             cn : [
14716                                 {
14717                                     cls : 'fc-event-container',
14718                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14719                                 },
14720                                 cal_table
14721                             ]
14722                         }
14723                     ]
14724     
14725                 }
14726            ] 
14727             
14728         };
14729         
14730          
14731         
14732         return cfg;
14733     },
14734     
14735     
14736     initEvents : function()
14737     {
14738         if(!this.store){
14739             throw "can not find store for calendar";
14740         }
14741         
14742         var mark = {
14743             tag: "div",
14744             cls:"x-dlg-mask",
14745             style: "text-align:center",
14746             cn: [
14747                 {
14748                     tag: "div",
14749                     style: "background-color:white;width:50%;margin:250 auto",
14750                     cn: [
14751                         {
14752                             tag: "img",
14753                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14754                         },
14755                         {
14756                             tag: "span",
14757                             html: "Loading"
14758                         }
14759                         
14760                     ]
14761                 }
14762             ]
14763         }
14764         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14765         
14766         var size = this.el.select('.fc-content', true).first().getSize();
14767         this.maskEl.setSize(size.width, size.height);
14768         this.maskEl.enableDisplayMode("block");
14769         if(!this.loadMask){
14770             this.maskEl.hide();
14771         }
14772         
14773         this.store = Roo.factory(this.store, Roo.data);
14774         this.store.on('load', this.onLoad, this);
14775         this.store.on('beforeload', this.onBeforeLoad, this);
14776         
14777         this.resize();
14778         
14779         this.cells = this.el.select('.fc-day',true);
14780         //Roo.log(this.cells);
14781         this.textNodes = this.el.query('.fc-day-number');
14782         this.cells.addClassOnOver('fc-state-hover');
14783         
14784         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14785         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14786         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14787         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14788         
14789         this.on('monthchange', this.onMonthChange, this);
14790         
14791         this.update(new Date().clearTime());
14792     },
14793     
14794     resize : function() {
14795         var sz  = this.el.getSize();
14796         
14797         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14798         this.el.select('.fc-day-content div',true).setHeight(34);
14799     },
14800     
14801     
14802     // private
14803     showPrevMonth : function(e){
14804         this.update(this.activeDate.add("mo", -1));
14805     },
14806     showToday : function(e){
14807         this.update(new Date().clearTime());
14808     },
14809     // private
14810     showNextMonth : function(e){
14811         this.update(this.activeDate.add("mo", 1));
14812     },
14813
14814     // private
14815     showPrevYear : function(){
14816         this.update(this.activeDate.add("y", -1));
14817     },
14818
14819     // private
14820     showNextYear : function(){
14821         this.update(this.activeDate.add("y", 1));
14822     },
14823
14824     
14825    // private
14826     update : function(date)
14827     {
14828         var vd = this.activeDate;
14829         this.activeDate = date;
14830 //        if(vd && this.el){
14831 //            var t = date.getTime();
14832 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14833 //                Roo.log('using add remove');
14834 //                
14835 //                this.fireEvent('monthchange', this, date);
14836 //                
14837 //                this.cells.removeClass("fc-state-highlight");
14838 //                this.cells.each(function(c){
14839 //                   if(c.dateValue == t){
14840 //                       c.addClass("fc-state-highlight");
14841 //                       setTimeout(function(){
14842 //                            try{c.dom.firstChild.focus();}catch(e){}
14843 //                       }, 50);
14844 //                       return false;
14845 //                   }
14846 //                   return true;
14847 //                });
14848 //                return;
14849 //            }
14850 //        }
14851         
14852         var days = date.getDaysInMonth();
14853         
14854         var firstOfMonth = date.getFirstDateOfMonth();
14855         var startingPos = firstOfMonth.getDay()-this.startDay;
14856         
14857         if(startingPos < this.startDay){
14858             startingPos += 7;
14859         }
14860         
14861         var pm = date.add(Date.MONTH, -1);
14862         var prevStart = pm.getDaysInMonth()-startingPos;
14863 //        
14864         this.cells = this.el.select('.fc-day',true);
14865         this.textNodes = this.el.query('.fc-day-number');
14866         this.cells.addClassOnOver('fc-state-hover');
14867         
14868         var cells = this.cells.elements;
14869         var textEls = this.textNodes;
14870         
14871         Roo.each(cells, function(cell){
14872             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14873         });
14874         
14875         days += startingPos;
14876
14877         // convert everything to numbers so it's fast
14878         var day = 86400000;
14879         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14880         //Roo.log(d);
14881         //Roo.log(pm);
14882         //Roo.log(prevStart);
14883         
14884         var today = new Date().clearTime().getTime();
14885         var sel = date.clearTime().getTime();
14886         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14887         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14888         var ddMatch = this.disabledDatesRE;
14889         var ddText = this.disabledDatesText;
14890         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14891         var ddaysText = this.disabledDaysText;
14892         var format = this.format;
14893         
14894         var setCellClass = function(cal, cell){
14895             cell.row = 0;
14896             cell.events = [];
14897             cell.more = [];
14898             //Roo.log('set Cell Class');
14899             cell.title = "";
14900             var t = d.getTime();
14901             
14902             //Roo.log(d);
14903             
14904             cell.dateValue = t;
14905             if(t == today){
14906                 cell.className += " fc-today";
14907                 cell.className += " fc-state-highlight";
14908                 cell.title = cal.todayText;
14909             }
14910             if(t == sel){
14911                 // disable highlight in other month..
14912                 //cell.className += " fc-state-highlight";
14913                 
14914             }
14915             // disabling
14916             if(t < min) {
14917                 cell.className = " fc-state-disabled";
14918                 cell.title = cal.minText;
14919                 return;
14920             }
14921             if(t > max) {
14922                 cell.className = " fc-state-disabled";
14923                 cell.title = cal.maxText;
14924                 return;
14925             }
14926             if(ddays){
14927                 if(ddays.indexOf(d.getDay()) != -1){
14928                     cell.title = ddaysText;
14929                     cell.className = " fc-state-disabled";
14930                 }
14931             }
14932             if(ddMatch && format){
14933                 var fvalue = d.dateFormat(format);
14934                 if(ddMatch.test(fvalue)){
14935                     cell.title = ddText.replace("%0", fvalue);
14936                     cell.className = " fc-state-disabled";
14937                 }
14938             }
14939             
14940             if (!cell.initialClassName) {
14941                 cell.initialClassName = cell.dom.className;
14942             }
14943             
14944             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14945         };
14946
14947         var i = 0;
14948         
14949         for(; i < startingPos; i++) {
14950             textEls[i].innerHTML = (++prevStart);
14951             d.setDate(d.getDate()+1);
14952             
14953             cells[i].className = "fc-past fc-other-month";
14954             setCellClass(this, cells[i]);
14955         }
14956         
14957         var intDay = 0;
14958         
14959         for(; i < days; i++){
14960             intDay = i - startingPos + 1;
14961             textEls[i].innerHTML = (intDay);
14962             d.setDate(d.getDate()+1);
14963             
14964             cells[i].className = ''; // "x-date-active";
14965             setCellClass(this, cells[i]);
14966         }
14967         var extraDays = 0;
14968         
14969         for(; i < 42; i++) {
14970             textEls[i].innerHTML = (++extraDays);
14971             d.setDate(d.getDate()+1);
14972             
14973             cells[i].className = "fc-future fc-other-month";
14974             setCellClass(this, cells[i]);
14975         }
14976         
14977         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14978         
14979         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14980         
14981         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14982         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14983         
14984         if(totalRows != 6){
14985             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14986             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14987         }
14988         
14989         this.fireEvent('monthchange', this, date);
14990         
14991         
14992         /*
14993         if(!this.internalRender){
14994             var main = this.el.dom.firstChild;
14995             var w = main.offsetWidth;
14996             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14997             Roo.fly(main).setWidth(w);
14998             this.internalRender = true;
14999             // opera does not respect the auto grow header center column
15000             // then, after it gets a width opera refuses to recalculate
15001             // without a second pass
15002             if(Roo.isOpera && !this.secondPass){
15003                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15004                 this.secondPass = true;
15005                 this.update.defer(10, this, [date]);
15006             }
15007         }
15008         */
15009         
15010     },
15011     
15012     findCell : function(dt) {
15013         dt = dt.clearTime().getTime();
15014         var ret = false;
15015         this.cells.each(function(c){
15016             //Roo.log("check " +c.dateValue + '?=' + dt);
15017             if(c.dateValue == dt){
15018                 ret = c;
15019                 return false;
15020             }
15021             return true;
15022         });
15023         
15024         return ret;
15025     },
15026     
15027     findCells : function(ev) {
15028         var s = ev.start.clone().clearTime().getTime();
15029        // Roo.log(s);
15030         var e= ev.end.clone().clearTime().getTime();
15031        // Roo.log(e);
15032         var ret = [];
15033         this.cells.each(function(c){
15034              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15035             
15036             if(c.dateValue > e){
15037                 return ;
15038             }
15039             if(c.dateValue < s){
15040                 return ;
15041             }
15042             ret.push(c);
15043         });
15044         
15045         return ret;    
15046     },
15047     
15048 //    findBestRow: function(cells)
15049 //    {
15050 //        var ret = 0;
15051 //        
15052 //        for (var i =0 ; i < cells.length;i++) {
15053 //            ret  = Math.max(cells[i].rows || 0,ret);
15054 //        }
15055 //        return ret;
15056 //        
15057 //    },
15058     
15059     
15060     addItem : function(ev)
15061     {
15062         // look for vertical location slot in
15063         var cells = this.findCells(ev);
15064         
15065 //        ev.row = this.findBestRow(cells);
15066         
15067         // work out the location.
15068         
15069         var crow = false;
15070         var rows = [];
15071         for(var i =0; i < cells.length; i++) {
15072             
15073             cells[i].row = cells[0].row;
15074             
15075             if(i == 0){
15076                 cells[i].row = cells[i].row + 1;
15077             }
15078             
15079             if (!crow) {
15080                 crow = {
15081                     start : cells[i],
15082                     end :  cells[i]
15083                 };
15084                 continue;
15085             }
15086             if (crow.start.getY() == cells[i].getY()) {
15087                 // on same row.
15088                 crow.end = cells[i];
15089                 continue;
15090             }
15091             // different row.
15092             rows.push(crow);
15093             crow = {
15094                 start: cells[i],
15095                 end : cells[i]
15096             };
15097             
15098         }
15099         
15100         rows.push(crow);
15101         ev.els = [];
15102         ev.rows = rows;
15103         ev.cells = cells;
15104         
15105         cells[0].events.push(ev);
15106         
15107         this.calevents.push(ev);
15108     },
15109     
15110     clearEvents: function() {
15111         
15112         if(!this.calevents){
15113             return;
15114         }
15115         
15116         Roo.each(this.cells.elements, function(c){
15117             c.row = 0;
15118             c.events = [];
15119             c.more = [];
15120         });
15121         
15122         Roo.each(this.calevents, function(e) {
15123             Roo.each(e.els, function(el) {
15124                 el.un('mouseenter' ,this.onEventEnter, this);
15125                 el.un('mouseleave' ,this.onEventLeave, this);
15126                 el.remove();
15127             },this);
15128         },this);
15129         
15130         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15131             e.remove();
15132         });
15133         
15134     },
15135     
15136     renderEvents: function()
15137     {   
15138         var _this = this;
15139         
15140         this.cells.each(function(c) {
15141             
15142             if(c.row < 5){
15143                 return;
15144             }
15145             
15146             var ev = c.events;
15147             
15148             var r = 4;
15149             if(c.row != c.events.length){
15150                 r = 4 - (4 - (c.row - c.events.length));
15151             }
15152             
15153             c.events = ev.slice(0, r);
15154             c.more = ev.slice(r);
15155             
15156             if(c.more.length && c.more.length == 1){
15157                 c.events.push(c.more.pop());
15158             }
15159             
15160             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15161             
15162         });
15163             
15164         this.cells.each(function(c) {
15165             
15166             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15167             
15168             
15169             for (var e = 0; e < c.events.length; e++){
15170                 var ev = c.events[e];
15171                 var rows = ev.rows;
15172                 
15173                 for(var i = 0; i < rows.length; i++) {
15174                 
15175                     // how many rows should it span..
15176
15177                     var  cfg = {
15178                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15179                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15180
15181                         unselectable : "on",
15182                         cn : [
15183                             {
15184                                 cls: 'fc-event-inner',
15185                                 cn : [
15186     //                                {
15187     //                                  tag:'span',
15188     //                                  cls: 'fc-event-time',
15189     //                                  html : cells.length > 1 ? '' : ev.time
15190     //                                },
15191                                     {
15192                                       tag:'span',
15193                                       cls: 'fc-event-title',
15194                                       html : String.format('{0}', ev.title)
15195                                     }
15196
15197
15198                                 ]
15199                             },
15200                             {
15201                                 cls: 'ui-resizable-handle ui-resizable-e',
15202                                 html : '&nbsp;&nbsp;&nbsp'
15203                             }
15204
15205                         ]
15206                     };
15207
15208                     if (i == 0) {
15209                         cfg.cls += ' fc-event-start';
15210                     }
15211                     if ((i+1) == rows.length) {
15212                         cfg.cls += ' fc-event-end';
15213                     }
15214
15215                     var ctr = _this.el.select('.fc-event-container',true).first();
15216                     var cg = ctr.createChild(cfg);
15217
15218                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15219                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15220
15221                     var r = (c.more.length) ? 1 : 0;
15222                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15223                     cg.setWidth(ebox.right - sbox.x -2);
15224
15225                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15226                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15227                     cg.on('click', _this.onEventClick, _this, ev);
15228
15229                     ev.els.push(cg);
15230                     
15231                 }
15232                 
15233             }
15234             
15235             
15236             if(c.more.length){
15237                 var  cfg = {
15238                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15239                     style : 'position: absolute',
15240                     unselectable : "on",
15241                     cn : [
15242                         {
15243                             cls: 'fc-event-inner',
15244                             cn : [
15245                                 {
15246                                   tag:'span',
15247                                   cls: 'fc-event-title',
15248                                   html : 'More'
15249                                 }
15250
15251
15252                             ]
15253                         },
15254                         {
15255                             cls: 'ui-resizable-handle ui-resizable-e',
15256                             html : '&nbsp;&nbsp;&nbsp'
15257                         }
15258
15259                     ]
15260                 };
15261
15262                 var ctr = _this.el.select('.fc-event-container',true).first();
15263                 var cg = ctr.createChild(cfg);
15264
15265                 var sbox = c.select('.fc-day-content',true).first().getBox();
15266                 var ebox = c.select('.fc-day-content',true).first().getBox();
15267                 //Roo.log(cg);
15268                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15269                 cg.setWidth(ebox.right - sbox.x -2);
15270
15271                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15272                 
15273             }
15274             
15275         });
15276         
15277         
15278         
15279     },
15280     
15281     onEventEnter: function (e, el,event,d) {
15282         this.fireEvent('evententer', this, el, event);
15283     },
15284     
15285     onEventLeave: function (e, el,event,d) {
15286         this.fireEvent('eventleave', this, el, event);
15287     },
15288     
15289     onEventClick: function (e, el,event,d) {
15290         this.fireEvent('eventclick', this, el, event);
15291     },
15292     
15293     onMonthChange: function () {
15294         this.store.load();
15295     },
15296     
15297     onMoreEventClick: function(e, el, more)
15298     {
15299         var _this = this;
15300         
15301         this.calpopover.placement = 'right';
15302         this.calpopover.setTitle('More');
15303         
15304         this.calpopover.setContent('');
15305         
15306         var ctr = this.calpopover.el.select('.popover-content', true).first();
15307         
15308         Roo.each(more, function(m){
15309             var cfg = {
15310                 cls : 'fc-event-hori fc-event-draggable',
15311                 html : m.title
15312             }
15313             var cg = ctr.createChild(cfg);
15314             
15315             cg.on('click', _this.onEventClick, _this, m);
15316         });
15317         
15318         this.calpopover.show(el);
15319         
15320         
15321     },
15322     
15323     onLoad: function () 
15324     {   
15325         this.calevents = [];
15326         var cal = this;
15327         
15328         if(this.store.getCount() > 0){
15329             this.store.data.each(function(d){
15330                cal.addItem({
15331                     id : d.data.id,
15332                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15333                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15334                     time : d.data.start_time,
15335                     title : d.data.title,
15336                     description : d.data.description,
15337                     venue : d.data.venue
15338                 });
15339             });
15340         }
15341         
15342         this.renderEvents();
15343         
15344         if(this.calevents.length && this.loadMask){
15345             this.maskEl.hide();
15346         }
15347     },
15348     
15349     onBeforeLoad: function()
15350     {
15351         this.clearEvents();
15352         if(this.loadMask){
15353             this.maskEl.show();
15354         }
15355     }
15356 });
15357
15358  
15359  /*
15360  * - LGPL
15361  *
15362  * element
15363  * 
15364  */
15365
15366 /**
15367  * @class Roo.bootstrap.Popover
15368  * @extends Roo.bootstrap.Component
15369  * Bootstrap Popover class
15370  * @cfg {String} html contents of the popover   (or false to use children..)
15371  * @cfg {String} title of popover (or false to hide)
15372  * @cfg {String} placement how it is placed
15373  * @cfg {String} trigger click || hover (or false to trigger manually)
15374  * @cfg {String} over what (parent or false to trigger manually.)
15375  * @cfg {Number} delay - delay before showing
15376  
15377  * @constructor
15378  * Create a new Popover
15379  * @param {Object} config The config object
15380  */
15381
15382 Roo.bootstrap.Popover = function(config){
15383     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15384 };
15385
15386 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15387     
15388     title: 'Fill in a title',
15389     html: false,
15390     
15391     placement : 'right',
15392     trigger : 'hover', // hover
15393     
15394     delay : 0,
15395     
15396     over: 'parent',
15397     
15398     can_build_overlaid : false,
15399     
15400     getChildContainer : function()
15401     {
15402         return this.el.select('.popover-content',true).first();
15403     },
15404     
15405     getAutoCreate : function(){
15406          Roo.log('make popover?');
15407         var cfg = {
15408            cls : 'popover roo-dynamic',
15409            style: 'display:block',
15410            cn : [
15411                 {
15412                     cls : 'arrow'
15413                 },
15414                 {
15415                     cls : 'popover-inner',
15416                     cn : [
15417                         {
15418                             tag: 'h3',
15419                             cls: 'popover-title',
15420                             html : this.title
15421                         },
15422                         {
15423                             cls : 'popover-content',
15424                             html : this.html
15425                         }
15426                     ]
15427                     
15428                 }
15429            ]
15430         };
15431         
15432         return cfg;
15433     },
15434     setTitle: function(str)
15435     {
15436         this.title = str;
15437         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15438     },
15439     setContent: function(str)
15440     {
15441         this.html = str;
15442         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15443     },
15444     // as it get's added to the bottom of the page.
15445     onRender : function(ct, position)
15446     {
15447         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15448         if(!this.el){
15449             var cfg = Roo.apply({},  this.getAutoCreate());
15450             cfg.id = Roo.id();
15451             
15452             if (this.cls) {
15453                 cfg.cls += ' ' + this.cls;
15454             }
15455             if (this.style) {
15456                 cfg.style = this.style;
15457             }
15458             Roo.log("adding to ")
15459             this.el = Roo.get(document.body).createChild(cfg, position);
15460             Roo.log(this.el);
15461         }
15462         this.initEvents();
15463     },
15464     
15465     initEvents : function()
15466     {
15467         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15468         this.el.enableDisplayMode('block');
15469         this.el.hide();
15470         if (this.over === false) {
15471             return; 
15472         }
15473         if (this.triggers === false) {
15474             return;
15475         }
15476         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15477         var triggers = this.trigger ? this.trigger.split(' ') : [];
15478         Roo.each(triggers, function(trigger) {
15479         
15480             if (trigger == 'click') {
15481                 on_el.on('click', this.toggle, this);
15482             } else if (trigger != 'manual') {
15483                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15484                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15485       
15486                 on_el.on(eventIn  ,this.enter, this);
15487                 on_el.on(eventOut, this.leave, this);
15488             }
15489         }, this);
15490         
15491     },
15492     
15493     
15494     // private
15495     timeout : null,
15496     hoverState : null,
15497     
15498     toggle : function () {
15499         this.hoverState == 'in' ? this.leave() : this.enter();
15500     },
15501     
15502     enter : function () {
15503        
15504     
15505         clearTimeout(this.timeout);
15506     
15507         this.hoverState = 'in';
15508     
15509         if (!this.delay || !this.delay.show) {
15510             this.show();
15511             return;
15512         }
15513         var _t = this;
15514         this.timeout = setTimeout(function () {
15515             if (_t.hoverState == 'in') {
15516                 _t.show();
15517             }
15518         }, this.delay.show)
15519     },
15520     leave : function() {
15521         clearTimeout(this.timeout);
15522     
15523         this.hoverState = 'out';
15524     
15525         if (!this.delay || !this.delay.hide) {
15526             this.hide();
15527             return;
15528         }
15529         var _t = this;
15530         this.timeout = setTimeout(function () {
15531             if (_t.hoverState == 'out') {
15532                 _t.hide();
15533             }
15534         }, this.delay.hide)
15535     },
15536     
15537     show : function (on_el)
15538     {
15539         if (!on_el) {
15540             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15541         }
15542         // set content.
15543         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15544         if (this.html !== false) {
15545             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15546         }
15547         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15548         if (!this.title.length) {
15549             this.el.select('.popover-title',true).hide();
15550         }
15551         
15552         var placement = typeof this.placement == 'function' ?
15553             this.placement.call(this, this.el, on_el) :
15554             this.placement;
15555             
15556         var autoToken = /\s?auto?\s?/i;
15557         var autoPlace = autoToken.test(placement);
15558         if (autoPlace) {
15559             placement = placement.replace(autoToken, '') || 'top';
15560         }
15561         
15562         //this.el.detach()
15563         //this.el.setXY([0,0]);
15564         this.el.show();
15565         this.el.dom.style.display='block';
15566         this.el.addClass(placement);
15567         
15568         //this.el.appendTo(on_el);
15569         
15570         var p = this.getPosition();
15571         var box = this.el.getBox();
15572         
15573         if (autoPlace) {
15574             // fixme..
15575         }
15576         var align = Roo.bootstrap.Popover.alignment[placement];
15577         this.el.alignTo(on_el, align[0],align[1]);
15578         //var arrow = this.el.select('.arrow',true).first();
15579         //arrow.set(align[2], 
15580         
15581         this.el.addClass('in');
15582         
15583         
15584         if (this.el.hasClass('fade')) {
15585             // fade it?
15586         }
15587         
15588     },
15589     hide : function()
15590     {
15591         this.el.setXY([0,0]);
15592         this.el.removeClass('in');
15593         this.el.hide();
15594         this.hoverState = null;
15595         
15596     }
15597     
15598 });
15599
15600 Roo.bootstrap.Popover.alignment = {
15601     'left' : ['r-l', [-10,0], 'right'],
15602     'right' : ['l-r', [10,0], 'left'],
15603     'bottom' : ['t-b', [0,10], 'top'],
15604     'top' : [ 'b-t', [0,-10], 'bottom']
15605 };
15606
15607  /*
15608  * - LGPL
15609  *
15610  * Progress
15611  * 
15612  */
15613
15614 /**
15615  * @class Roo.bootstrap.Progress
15616  * @extends Roo.bootstrap.Component
15617  * Bootstrap Progress class
15618  * @cfg {Boolean} striped striped of the progress bar
15619  * @cfg {Boolean} active animated of the progress bar
15620  * 
15621  * 
15622  * @constructor
15623  * Create a new Progress
15624  * @param {Object} config The config object
15625  */
15626
15627 Roo.bootstrap.Progress = function(config){
15628     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15629 };
15630
15631 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15632     
15633     striped : false,
15634     active: false,
15635     
15636     getAutoCreate : function(){
15637         var cfg = {
15638             tag: 'div',
15639             cls: 'progress'
15640         };
15641         
15642         
15643         if(this.striped){
15644             cfg.cls += ' progress-striped';
15645         }
15646       
15647         if(this.active){
15648             cfg.cls += ' active';
15649         }
15650         
15651         
15652         return cfg;
15653     }
15654    
15655 });
15656
15657  
15658
15659  /*
15660  * - LGPL
15661  *
15662  * ProgressBar
15663  * 
15664  */
15665
15666 /**
15667  * @class Roo.bootstrap.ProgressBar
15668  * @extends Roo.bootstrap.Component
15669  * Bootstrap ProgressBar class
15670  * @cfg {Number} aria_valuenow aria-value now
15671  * @cfg {Number} aria_valuemin aria-value min
15672  * @cfg {Number} aria_valuemax aria-value max
15673  * @cfg {String} label label for the progress bar
15674  * @cfg {String} panel (success | info | warning | danger )
15675  * @cfg {String} role role of the progress bar
15676  * @cfg {String} sr_only text
15677  * 
15678  * 
15679  * @constructor
15680  * Create a new ProgressBar
15681  * @param {Object} config The config object
15682  */
15683
15684 Roo.bootstrap.ProgressBar = function(config){
15685     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15686 };
15687
15688 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15689     
15690     aria_valuenow : 0,
15691     aria_valuemin : 0,
15692     aria_valuemax : 100,
15693     label : false,
15694     panel : false,
15695     role : false,
15696     sr_only: false,
15697     
15698     getAutoCreate : function()
15699     {
15700         
15701         var cfg = {
15702             tag: 'div',
15703             cls: 'progress-bar',
15704             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15705         };
15706         
15707         if(this.sr_only){
15708             cfg.cn = {
15709                 tag: 'span',
15710                 cls: 'sr-only',
15711                 html: this.sr_only
15712             }
15713         }
15714         
15715         if(this.role){
15716             cfg.role = this.role;
15717         }
15718         
15719         if(this.aria_valuenow){
15720             cfg['aria-valuenow'] = this.aria_valuenow;
15721         }
15722         
15723         if(this.aria_valuemin){
15724             cfg['aria-valuemin'] = this.aria_valuemin;
15725         }
15726         
15727         if(this.aria_valuemax){
15728             cfg['aria-valuemax'] = this.aria_valuemax;
15729         }
15730         
15731         if(this.label && !this.sr_only){
15732             cfg.html = this.label;
15733         }
15734         
15735         if(this.panel){
15736             cfg.cls += ' progress-bar-' + this.panel;
15737         }
15738         
15739         return cfg;
15740     },
15741     
15742     update : function(aria_valuenow)
15743     {
15744         this.aria_valuenow = aria_valuenow;
15745         
15746         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15747     }
15748    
15749 });
15750
15751  
15752
15753  /*
15754  * - LGPL
15755  *
15756  * column
15757  * 
15758  */
15759
15760 /**
15761  * @class Roo.bootstrap.TabGroup
15762  * @extends Roo.bootstrap.Column
15763  * Bootstrap Column class
15764  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15765  * @cfg {Boolean} carousel true to make the group behave like a carousel
15766  * @cfg {Number} bullets show the panel pointer.. default 0
15767  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15768  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15769  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15770  * 
15771  * @constructor
15772  * Create a new TabGroup
15773  * @param {Object} config The config object
15774  */
15775
15776 Roo.bootstrap.TabGroup = function(config){
15777     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15778     if (!this.navId) {
15779         this.navId = Roo.id();
15780     }
15781     this.tabs = [];
15782     Roo.bootstrap.TabGroup.register(this);
15783     
15784 };
15785
15786 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15787     
15788     carousel : false,
15789     transition : false,
15790     bullets : 0,
15791     timer : 0,
15792     autoslide : false,
15793     slideFn : false,
15794     slideOnTouch : false,
15795     
15796     getAutoCreate : function()
15797     {
15798         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15799         
15800         cfg.cls += ' tab-content';
15801         
15802         Roo.log('get auto create...............');
15803         
15804         if (this.carousel) {
15805             cfg.cls += ' carousel slide';
15806             
15807             cfg.cn = [{
15808                cls : 'carousel-inner'
15809             }];
15810         
15811             if(this.bullets > 0 && !Roo.isTouch){
15812                 
15813                 var bullets = {
15814                     cls : 'carousel-bullets',
15815                     cn : []
15816                 };
15817                 
15818                 if(this.bullets_cls){
15819                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15820                 }
15821                 
15822                 for (var i = 0; i < this.bullets; i++){
15823                     bullets.cn.push({
15824                         cls : 'bullet bullet-' + i
15825                     });
15826                 }
15827                 
15828                 bullets.cn.push({
15829                     cls : 'clear'
15830                 });
15831                 
15832                 cfg.cn[0].cn = bullets;
15833             }
15834         }
15835         
15836         return cfg;
15837     },
15838     
15839     initEvents:  function()
15840     {
15841         Roo.log('-------- init events on tab group ---------');
15842         
15843         if(this.bullets > 0 && !Roo.isTouch){
15844             this.initBullet();
15845         }
15846         
15847         Roo.log(this);
15848         
15849         if(Roo.isTouch && this.slideOnTouch){
15850             this.el.on("touchstart", this.onTouchStart, this);
15851         }
15852         
15853         if(this.autoslide){
15854             var _this = this;
15855             
15856             this.slideFn = window.setInterval(function() {
15857                 _this.showPanelNext();
15858             }, this.timer);
15859         }
15860         
15861     },
15862     
15863     onTouchStart : function(e, el, o)
15864     {
15865         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15866             return;
15867         }
15868         
15869         this.showPanelNext();
15870     },
15871     
15872     getChildContainer : function()
15873     {
15874         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15875     },
15876     
15877     /**
15878     * register a Navigation item
15879     * @param {Roo.bootstrap.NavItem} the navitem to add
15880     */
15881     register : function(item)
15882     {
15883         this.tabs.push( item);
15884         item.navId = this.navId; // not really needed..
15885     
15886     },
15887     
15888     getActivePanel : function()
15889     {
15890         var r = false;
15891         Roo.each(this.tabs, function(t) {
15892             if (t.active) {
15893                 r = t;
15894                 return false;
15895             }
15896             return null;
15897         });
15898         return r;
15899         
15900     },
15901     getPanelByName : function(n)
15902     {
15903         var r = false;
15904         Roo.each(this.tabs, function(t) {
15905             if (t.tabId == n) {
15906                 r = t;
15907                 return false;
15908             }
15909             return null;
15910         });
15911         return r;
15912     },
15913     indexOfPanel : function(p)
15914     {
15915         var r = false;
15916         Roo.each(this.tabs, function(t,i) {
15917             if (t.tabId == p.tabId) {
15918                 r = i;
15919                 return false;
15920             }
15921             return null;
15922         });
15923         return r;
15924     },
15925     /**
15926      * show a specific panel
15927      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15928      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15929      */
15930     showPanel : function (pan)
15931     {
15932         if(this.transition){
15933             Roo.log("waiting for the transitionend");
15934             return;
15935         }
15936         
15937         if (typeof(pan) == 'number') {
15938             pan = this.tabs[pan];
15939         }
15940         if (typeof(pan) == 'string') {
15941             pan = this.getPanelByName(pan);
15942         }
15943         if (pan.tabId == this.getActivePanel().tabId) {
15944             return true;
15945         }
15946         var cur = this.getActivePanel();
15947         
15948         if (false === cur.fireEvent('beforedeactivate')) {
15949             return false;
15950         }
15951         
15952         if(this.bullets > 0 && !Roo.isTouch){
15953             this.setActiveBullet(this.indexOfPanel(pan));
15954         }
15955         
15956         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15957             
15958             this.transition = true;
15959             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15960             var lr = dir == 'next' ? 'left' : 'right';
15961             pan.el.addClass(dir); // or prev
15962             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15963             cur.el.addClass(lr); // or right
15964             pan.el.addClass(lr);
15965             
15966             var _this = this;
15967             cur.el.on('transitionend', function() {
15968                 Roo.log("trans end?");
15969                 
15970                 pan.el.removeClass([lr,dir]);
15971                 pan.setActive(true);
15972                 
15973                 cur.el.removeClass([lr]);
15974                 cur.setActive(false);
15975                 
15976                 _this.transition = false;
15977                 
15978             }, this, { single:  true } );
15979             
15980             return true;
15981         }
15982         
15983         cur.setActive(false);
15984         pan.setActive(true);
15985         
15986         return true;
15987         
15988     },
15989     showPanelNext : function()
15990     {
15991         var i = this.indexOfPanel(this.getActivePanel());
15992         
15993         if (i >= this.tabs.length - 1 && !this.autoslide) {
15994             return;
15995         }
15996         
15997         if (i >= this.tabs.length - 1 && this.autoslide) {
15998             i = -1;
15999         }
16000         
16001         this.showPanel(this.tabs[i+1]);
16002     },
16003     
16004     showPanelPrev : function()
16005     {
16006         var i = this.indexOfPanel(this.getActivePanel());
16007         
16008         if (i  < 1 && !this.autoslide) {
16009             return;
16010         }
16011         
16012         if (i < 1 && this.autoslide) {
16013             i = this.tabs.length;
16014         }
16015         
16016         this.showPanel(this.tabs[i-1]);
16017     },
16018     
16019     initBullet : function()
16020     {
16021         if(Roo.isTouch){
16022             return;
16023         }
16024         
16025         var _this = this;
16026         
16027         for (var i = 0; i < this.bullets; i++){
16028             var bullet = this.el.select('.bullet-' + i, true).first();
16029
16030             if(!bullet){
16031                 continue;
16032             }
16033
16034             bullet.on('click', (function(e, el, o, ii, t){
16035
16036                 e.preventDefault();
16037
16038                 _this.showPanel(ii);
16039
16040                 if(_this.autoslide && _this.slideFn){
16041                     clearInterval(_this.slideFn);
16042                     _this.slideFn = window.setInterval(function() {
16043                         _this.showPanelNext();
16044                     }, _this.timer);
16045                 }
16046
16047             }).createDelegate(this, [i, bullet], true));
16048         }
16049     },
16050     
16051     setActiveBullet : function(i)
16052     {
16053         if(Roo.isTouch){
16054             return;
16055         }
16056         
16057         Roo.each(this.el.select('.bullet', true).elements, function(el){
16058             el.removeClass('selected');
16059         });
16060
16061         var bullet = this.el.select('.bullet-' + i, true).first();
16062         
16063         if(!bullet){
16064             return;
16065         }
16066         
16067         bullet.addClass('selected');
16068     }
16069     
16070     
16071   
16072 });
16073
16074  
16075
16076  
16077  
16078 Roo.apply(Roo.bootstrap.TabGroup, {
16079     
16080     groups: {},
16081      /**
16082     * register a Navigation Group
16083     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16084     */
16085     register : function(navgrp)
16086     {
16087         this.groups[navgrp.navId] = navgrp;
16088         
16089     },
16090     /**
16091     * fetch a Navigation Group based on the navigation ID
16092     * if one does not exist , it will get created.
16093     * @param {string} the navgroup to add
16094     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16095     */
16096     get: function(navId) {
16097         if (typeof(this.groups[navId]) == 'undefined') {
16098             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16099         }
16100         return this.groups[navId] ;
16101     }
16102     
16103     
16104     
16105 });
16106
16107  /*
16108  * - LGPL
16109  *
16110  * TabPanel
16111  * 
16112  */
16113
16114 /**
16115  * @class Roo.bootstrap.TabPanel
16116  * @extends Roo.bootstrap.Component
16117  * Bootstrap TabPanel class
16118  * @cfg {Boolean} active panel active
16119  * @cfg {String} html panel content
16120  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16121  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16122  * 
16123  * 
16124  * @constructor
16125  * Create a new TabPanel
16126  * @param {Object} config The config object
16127  */
16128
16129 Roo.bootstrap.TabPanel = function(config){
16130     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16131     this.addEvents({
16132         /**
16133              * @event changed
16134              * Fires when the active status changes
16135              * @param {Roo.bootstrap.TabPanel} this
16136              * @param {Boolean} state the new state
16137             
16138          */
16139         'changed': true,
16140         /**
16141              * @event beforedeactivate
16142              * Fires before a tab is de-activated - can be used to do validation on a form.
16143              * @param {Roo.bootstrap.TabPanel} this
16144              * @return {Boolean} false if there is an error
16145             
16146          */
16147         'beforedeactivate': true
16148      });
16149     
16150     this.tabId = this.tabId || Roo.id();
16151   
16152 };
16153
16154 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16155     
16156     active: false,
16157     html: false,
16158     tabId: false,
16159     navId : false,
16160     
16161     getAutoCreate : function(){
16162         var cfg = {
16163             tag: 'div',
16164             // item is needed for carousel - not sure if it has any effect otherwise
16165             cls: 'tab-pane item',
16166             html: this.html || ''
16167         };
16168         
16169         if(this.active){
16170             cfg.cls += ' active';
16171         }
16172         
16173         if(this.tabId){
16174             cfg.tabId = this.tabId;
16175         }
16176         
16177         
16178         return cfg;
16179     },
16180     
16181     initEvents:  function()
16182     {
16183         Roo.log('-------- init events on tab panel ---------');
16184         
16185         var p = this.parent();
16186         this.navId = this.navId || p.navId;
16187         
16188         if (typeof(this.navId) != 'undefined') {
16189             // not really needed.. but just in case.. parent should be a NavGroup.
16190             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16191             Roo.log(['register', tg, this]);
16192             tg.register(this);
16193             
16194             var i = tg.tabs.length - 1;
16195             
16196             if(this.active && tg.bullets > 0 && i < tg.bullets){
16197                 tg.setActiveBullet(i);
16198             }
16199         }
16200         
16201     },
16202     
16203     
16204     onRender : function(ct, position)
16205     {
16206        // Roo.log("Call onRender: " + this.xtype);
16207         
16208         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16209         
16210         
16211         
16212         
16213         
16214     },
16215     
16216     setActive: function(state)
16217     {
16218         Roo.log("panel - set active " + this.tabId + "=" + state);
16219         
16220         this.active = state;
16221         if (!state) {
16222             this.el.removeClass('active');
16223             
16224         } else  if (!this.el.hasClass('active')) {
16225             this.el.addClass('active');
16226         }
16227         
16228         this.fireEvent('changed', this, state);
16229     }
16230     
16231     
16232 });
16233  
16234
16235  
16236
16237  /*
16238  * - LGPL
16239  *
16240  * DateField
16241  * 
16242  */
16243
16244 /**
16245  * @class Roo.bootstrap.DateField
16246  * @extends Roo.bootstrap.Input
16247  * Bootstrap DateField class
16248  * @cfg {Number} weekStart default 0
16249  * @cfg {String} viewMode default empty, (months|years)
16250  * @cfg {String} minViewMode default empty, (months|years)
16251  * @cfg {Number} startDate default -Infinity
16252  * @cfg {Number} endDate default Infinity
16253  * @cfg {Boolean} todayHighlight default false
16254  * @cfg {Boolean} todayBtn default false
16255  * @cfg {Boolean} calendarWeeks default false
16256  * @cfg {Object} daysOfWeekDisabled default empty
16257  * @cfg {Boolean} singleMode default false (true | false)
16258  * 
16259  * @cfg {Boolean} keyboardNavigation default true
16260  * @cfg {String} language default en
16261  * 
16262  * @constructor
16263  * Create a new DateField
16264  * @param {Object} config The config object
16265  */
16266
16267 Roo.bootstrap.DateField = function(config){
16268     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16269      this.addEvents({
16270             /**
16271              * @event show
16272              * Fires when this field show.
16273              * @param {Roo.bootstrap.DateField} this
16274              * @param {Mixed} date The date value
16275              */
16276             show : true,
16277             /**
16278              * @event show
16279              * Fires when this field hide.
16280              * @param {Roo.bootstrap.DateField} this
16281              * @param {Mixed} date The date value
16282              */
16283             hide : true,
16284             /**
16285              * @event select
16286              * Fires when select a date.
16287              * @param {Roo.bootstrap.DateField} this
16288              * @param {Mixed} date The date value
16289              */
16290             select : true
16291         });
16292 };
16293
16294 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16295     
16296     /**
16297      * @cfg {String} format
16298      * The default date format string which can be overriden for localization support.  The format must be
16299      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16300      */
16301     format : "m/d/y",
16302     /**
16303      * @cfg {String} altFormats
16304      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16305      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16306      */
16307     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16308     
16309     weekStart : 0,
16310     
16311     viewMode : '',
16312     
16313     minViewMode : '',
16314     
16315     todayHighlight : false,
16316     
16317     todayBtn: false,
16318     
16319     language: 'en',
16320     
16321     keyboardNavigation: true,
16322     
16323     calendarWeeks: false,
16324     
16325     startDate: -Infinity,
16326     
16327     endDate: Infinity,
16328     
16329     daysOfWeekDisabled: [],
16330     
16331     _events: [],
16332     
16333     singleMode : false,
16334     
16335     UTCDate: function()
16336     {
16337         return new Date(Date.UTC.apply(Date, arguments));
16338     },
16339     
16340     UTCToday: function()
16341     {
16342         var today = new Date();
16343         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16344     },
16345     
16346     getDate: function() {
16347             var d = this.getUTCDate();
16348             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16349     },
16350     
16351     getUTCDate: function() {
16352             return this.date;
16353     },
16354     
16355     setDate: function(d) {
16356             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16357     },
16358     
16359     setUTCDate: function(d) {
16360             this.date = d;
16361             this.setValue(this.formatDate(this.date));
16362     },
16363         
16364     onRender: function(ct, position)
16365     {
16366         
16367         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16368         
16369         this.language = this.language || 'en';
16370         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16371         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16372         
16373         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16374         this.format = this.format || 'm/d/y';
16375         this.isInline = false;
16376         this.isInput = true;
16377         this.component = this.el.select('.add-on', true).first() || false;
16378         this.component = (this.component && this.component.length === 0) ? false : this.component;
16379         this.hasInput = this.component && this.inputEL().length;
16380         
16381         if (typeof(this.minViewMode === 'string')) {
16382             switch (this.minViewMode) {
16383                 case 'months':
16384                     this.minViewMode = 1;
16385                     break;
16386                 case 'years':
16387                     this.minViewMode = 2;
16388                     break;
16389                 default:
16390                     this.minViewMode = 0;
16391                     break;
16392             }
16393         }
16394         
16395         if (typeof(this.viewMode === 'string')) {
16396             switch (this.viewMode) {
16397                 case 'months':
16398                     this.viewMode = 1;
16399                     break;
16400                 case 'years':
16401                     this.viewMode = 2;
16402                     break;
16403                 default:
16404                     this.viewMode = 0;
16405                     break;
16406             }
16407         }
16408                 
16409         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16410         
16411 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16412         
16413         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16414         
16415         this.picker().on('mousedown', this.onMousedown, this);
16416         this.picker().on('click', this.onClick, this);
16417         
16418         this.picker().addClass('datepicker-dropdown');
16419         
16420         this.startViewMode = this.viewMode;
16421         
16422         if(this.singleMode){
16423             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16424                 v.setVisibilityMode(Roo.Element.DISPLAY)
16425                 v.hide();
16426             });
16427             
16428             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16429                 v.setStyle('width', '189px');
16430             });
16431         }
16432         
16433         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16434             if(!this.calendarWeeks){
16435                 v.remove();
16436                 return;
16437             }
16438             
16439             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16440             v.attr('colspan', function(i, val){
16441                 return parseInt(val) + 1;
16442             });
16443         })
16444                         
16445         
16446         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16447         
16448         this.setStartDate(this.startDate);
16449         this.setEndDate(this.endDate);
16450         
16451         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16452         
16453         this.fillDow();
16454         this.fillMonths();
16455         this.update();
16456         this.showMode();
16457         
16458         if(this.isInline) {
16459             this.show();
16460         }
16461     },
16462     
16463     picker : function()
16464     {
16465         return this.pickerEl;
16466 //        return this.el.select('.datepicker', true).first();
16467     },
16468     
16469     fillDow: function()
16470     {
16471         var dowCnt = this.weekStart;
16472         
16473         var dow = {
16474             tag: 'tr',
16475             cn: [
16476                 
16477             ]
16478         };
16479         
16480         if(this.calendarWeeks){
16481             dow.cn.push({
16482                 tag: 'th',
16483                 cls: 'cw',
16484                 html: '&nbsp;'
16485             })
16486         }
16487         
16488         while (dowCnt < this.weekStart + 7) {
16489             dow.cn.push({
16490                 tag: 'th',
16491                 cls: 'dow',
16492                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16493             });
16494         }
16495         
16496         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16497     },
16498     
16499     fillMonths: function()
16500     {    
16501         var i = 0;
16502         var months = this.picker().select('>.datepicker-months td', true).first();
16503         
16504         months.dom.innerHTML = '';
16505         
16506         while (i < 12) {
16507             var month = {
16508                 tag: 'span',
16509                 cls: 'month',
16510                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16511             }
16512             
16513             months.createChild(month);
16514         }
16515         
16516     },
16517     
16518     update: function()
16519     {
16520         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;
16521         
16522         if (this.date < this.startDate) {
16523             this.viewDate = new Date(this.startDate);
16524         } else if (this.date > this.endDate) {
16525             this.viewDate = new Date(this.endDate);
16526         } else {
16527             this.viewDate = new Date(this.date);
16528         }
16529         
16530         this.fill();
16531     },
16532     
16533     fill: function() 
16534     {
16535         var d = new Date(this.viewDate),
16536                 year = d.getUTCFullYear(),
16537                 month = d.getUTCMonth(),
16538                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16539                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16540                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16541                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16542                 currentDate = this.date && this.date.valueOf(),
16543                 today = this.UTCToday();
16544         
16545         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16546         
16547 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16548         
16549 //        this.picker.select('>tfoot th.today').
16550 //                                              .text(dates[this.language].today)
16551 //                                              .toggle(this.todayBtn !== false);
16552     
16553         this.updateNavArrows();
16554         this.fillMonths();
16555                                                 
16556         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16557         
16558         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16559          
16560         prevMonth.setUTCDate(day);
16561         
16562         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16563         
16564         var nextMonth = new Date(prevMonth);
16565         
16566         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16567         
16568         nextMonth = nextMonth.valueOf();
16569         
16570         var fillMonths = false;
16571         
16572         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16573         
16574         while(prevMonth.valueOf() < nextMonth) {
16575             var clsName = '';
16576             
16577             if (prevMonth.getUTCDay() === this.weekStart) {
16578                 if(fillMonths){
16579                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16580                 }
16581                     
16582                 fillMonths = {
16583                     tag: 'tr',
16584                     cn: []
16585                 };
16586                 
16587                 if(this.calendarWeeks){
16588                     // ISO 8601: First week contains first thursday.
16589                     // ISO also states week starts on Monday, but we can be more abstract here.
16590                     var
16591                     // Start of current week: based on weekstart/current date
16592                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16593                     // Thursday of this week
16594                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16595                     // First Thursday of year, year from thursday
16596                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16597                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16598                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16599                     
16600                     fillMonths.cn.push({
16601                         tag: 'td',
16602                         cls: 'cw',
16603                         html: calWeek
16604                     });
16605                 }
16606             }
16607             
16608             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16609                 clsName += ' old';
16610             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16611                 clsName += ' new';
16612             }
16613             if (this.todayHighlight &&
16614                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16615                 prevMonth.getUTCMonth() == today.getMonth() &&
16616                 prevMonth.getUTCDate() == today.getDate()) {
16617                 clsName += ' today';
16618             }
16619             
16620             if (currentDate && prevMonth.valueOf() === currentDate) {
16621                 clsName += ' active';
16622             }
16623             
16624             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16625                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16626                     clsName += ' disabled';
16627             }
16628             
16629             fillMonths.cn.push({
16630                 tag: 'td',
16631                 cls: 'day ' + clsName,
16632                 html: prevMonth.getDate()
16633             })
16634             
16635             prevMonth.setDate(prevMonth.getDate()+1);
16636         }
16637           
16638         var currentYear = this.date && this.date.getUTCFullYear();
16639         var currentMonth = this.date && this.date.getUTCMonth();
16640         
16641         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16642         
16643         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16644             v.removeClass('active');
16645             
16646             if(currentYear === year && k === currentMonth){
16647                 v.addClass('active');
16648             }
16649             
16650             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16651                 v.addClass('disabled');
16652             }
16653             
16654         });
16655         
16656         
16657         year = parseInt(year/10, 10) * 10;
16658         
16659         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16660         
16661         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16662         
16663         year -= 1;
16664         for (var i = -1; i < 11; i++) {
16665             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16666                 tag: 'span',
16667                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16668                 html: year
16669             })
16670             
16671             year += 1;
16672         }
16673     },
16674     
16675     showMode: function(dir) 
16676     {
16677         if (dir) {
16678             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16679         }
16680         
16681         Roo.each(this.picker().select('>div',true).elements, function(v){
16682             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16683             v.hide();
16684         });
16685         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16686     },
16687     
16688     place: function()
16689     {
16690         if(this.isInline) return;
16691         
16692         this.picker().removeClass(['bottom', 'top']);
16693         
16694         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16695             /*
16696              * place to the top of element!
16697              *
16698              */
16699             
16700             this.picker().addClass('top');
16701             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16702             
16703             return;
16704         }
16705         
16706         this.picker().addClass('bottom');
16707         
16708         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16709     },
16710     
16711     parseDate : function(value)
16712     {
16713         if(!value || value instanceof Date){
16714             return value;
16715         }
16716         var v = Date.parseDate(value, this.format);
16717         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16718             v = Date.parseDate(value, 'Y-m-d');
16719         }
16720         if(!v && this.altFormats){
16721             if(!this.altFormatsArray){
16722                 this.altFormatsArray = this.altFormats.split("|");
16723             }
16724             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16725                 v = Date.parseDate(value, this.altFormatsArray[i]);
16726             }
16727         }
16728         return v;
16729     },
16730     
16731     formatDate : function(date, fmt)
16732     {   
16733         return (!date || !(date instanceof Date)) ?
16734         date : date.dateFormat(fmt || this.format);
16735     },
16736     
16737     onFocus : function()
16738     {
16739         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16740         this.show();
16741     },
16742     
16743     onBlur : function()
16744     {
16745         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16746         
16747         var d = this.inputEl().getValue();
16748         
16749         this.setValue(d);
16750                 
16751         this.hide();
16752     },
16753     
16754     show : function()
16755     {
16756         this.picker().show();
16757         this.update();
16758         this.place();
16759         
16760         this.fireEvent('show', this, this.date);
16761     },
16762     
16763     hide : function()
16764     {
16765         if(this.isInline) return;
16766         this.picker().hide();
16767         this.viewMode = this.startViewMode;
16768         this.showMode();
16769         
16770         this.fireEvent('hide', this, this.date);
16771         
16772     },
16773     
16774     onMousedown: function(e)
16775     {
16776         e.stopPropagation();
16777         e.preventDefault();
16778     },
16779     
16780     keyup: function(e)
16781     {
16782         Roo.bootstrap.DateField.superclass.keyup.call(this);
16783         this.update();
16784     },
16785
16786     setValue: function(v)
16787     {
16788         
16789         // v can be a string or a date..
16790         
16791         
16792         var d = new Date(this.parseDate(v) ).clearTime();
16793         
16794         if(isNaN(d.getTime())){
16795             this.date = this.viewDate = '';
16796             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16797             return;
16798         }
16799         
16800         v = this.formatDate(d);
16801         
16802         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16803         
16804         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16805      
16806         this.update();
16807
16808         this.fireEvent('select', this, this.date);
16809         
16810     },
16811     
16812     getValue: function()
16813     {
16814         return this.formatDate(this.date);
16815     },
16816     
16817     fireKey: function(e)
16818     {
16819         if (!this.picker().isVisible()){
16820             if (e.keyCode == 27) // allow escape to hide and re-show picker
16821                 this.show();
16822             return;
16823         }
16824         
16825         var dateChanged = false,
16826         dir, day, month,
16827         newDate, newViewDate;
16828         
16829         switch(e.keyCode){
16830             case 27: // escape
16831                 this.hide();
16832                 e.preventDefault();
16833                 break;
16834             case 37: // left
16835             case 39: // right
16836                 if (!this.keyboardNavigation) break;
16837                 dir = e.keyCode == 37 ? -1 : 1;
16838                 
16839                 if (e.ctrlKey){
16840                     newDate = this.moveYear(this.date, dir);
16841                     newViewDate = this.moveYear(this.viewDate, dir);
16842                 } else if (e.shiftKey){
16843                     newDate = this.moveMonth(this.date, dir);
16844                     newViewDate = this.moveMonth(this.viewDate, dir);
16845                 } else {
16846                     newDate = new Date(this.date);
16847                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16848                     newViewDate = new Date(this.viewDate);
16849                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16850                 }
16851                 if (this.dateWithinRange(newDate)){
16852                     this.date = newDate;
16853                     this.viewDate = newViewDate;
16854                     this.setValue(this.formatDate(this.date));
16855 //                    this.update();
16856                     e.preventDefault();
16857                     dateChanged = true;
16858                 }
16859                 break;
16860             case 38: // up
16861             case 40: // down
16862                 if (!this.keyboardNavigation) break;
16863                 dir = e.keyCode == 38 ? -1 : 1;
16864                 if (e.ctrlKey){
16865                     newDate = this.moveYear(this.date, dir);
16866                     newViewDate = this.moveYear(this.viewDate, dir);
16867                 } else if (e.shiftKey){
16868                     newDate = this.moveMonth(this.date, dir);
16869                     newViewDate = this.moveMonth(this.viewDate, dir);
16870                 } else {
16871                     newDate = new Date(this.date);
16872                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16873                     newViewDate = new Date(this.viewDate);
16874                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16875                 }
16876                 if (this.dateWithinRange(newDate)){
16877                     this.date = newDate;
16878                     this.viewDate = newViewDate;
16879                     this.setValue(this.formatDate(this.date));
16880 //                    this.update();
16881                     e.preventDefault();
16882                     dateChanged = true;
16883                 }
16884                 break;
16885             case 13: // enter
16886                 this.setValue(this.formatDate(this.date));
16887                 this.hide();
16888                 e.preventDefault();
16889                 break;
16890             case 9: // tab
16891                 this.setValue(this.formatDate(this.date));
16892                 this.hide();
16893                 break;
16894             case 16: // shift
16895             case 17: // ctrl
16896             case 18: // alt
16897                 break;
16898             default :
16899                 this.hide();
16900                 
16901         }
16902     },
16903     
16904     
16905     onClick: function(e) 
16906     {
16907         e.stopPropagation();
16908         e.preventDefault();
16909         
16910         var target = e.getTarget();
16911         
16912         if(target.nodeName.toLowerCase() === 'i'){
16913             target = Roo.get(target).dom.parentNode;
16914         }
16915         
16916         var nodeName = target.nodeName;
16917         var className = target.className;
16918         var html = target.innerHTML;
16919         //Roo.log(nodeName);
16920         
16921         switch(nodeName.toLowerCase()) {
16922             case 'th':
16923                 switch(className) {
16924                     case 'switch':
16925                         this.showMode(1);
16926                         break;
16927                     case 'prev':
16928                     case 'next':
16929                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16930                         switch(this.viewMode){
16931                                 case 0:
16932                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16933                                         break;
16934                                 case 1:
16935                                 case 2:
16936                                         this.viewDate = this.moveYear(this.viewDate, dir);
16937                                         break;
16938                         }
16939                         this.fill();
16940                         break;
16941                     case 'today':
16942                         var date = new Date();
16943                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16944 //                        this.fill()
16945                         this.setValue(this.formatDate(this.date));
16946                         
16947                         this.hide();
16948                         break;
16949                 }
16950                 break;
16951             case 'span':
16952                 if (className.indexOf('disabled') < 0) {
16953                     this.viewDate.setUTCDate(1);
16954                     if (className.indexOf('month') > -1) {
16955                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16956                     } else {
16957                         var year = parseInt(html, 10) || 0;
16958                         this.viewDate.setUTCFullYear(year);
16959                         
16960                     }
16961                     
16962                     if(this.singleMode){
16963                         this.setValue(this.formatDate(this.viewDate));
16964                         this.hide();
16965                         return;
16966                     }
16967                     
16968                     this.showMode(-1);
16969                     this.fill();
16970                 }
16971                 break;
16972                 
16973             case 'td':
16974                 //Roo.log(className);
16975                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16976                     var day = parseInt(html, 10) || 1;
16977                     var year = this.viewDate.getUTCFullYear(),
16978                         month = this.viewDate.getUTCMonth();
16979
16980                     if (className.indexOf('old') > -1) {
16981                         if(month === 0 ){
16982                             month = 11;
16983                             year -= 1;
16984                         }else{
16985                             month -= 1;
16986                         }
16987                     } else if (className.indexOf('new') > -1) {
16988                         if (month == 11) {
16989                             month = 0;
16990                             year += 1;
16991                         } else {
16992                             month += 1;
16993                         }
16994                     }
16995                     //Roo.log([year,month,day]);
16996                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16997                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16998 //                    this.fill();
16999                     //Roo.log(this.formatDate(this.date));
17000                     this.setValue(this.formatDate(this.date));
17001                     this.hide();
17002                 }
17003                 break;
17004         }
17005     },
17006     
17007     setStartDate: function(startDate)
17008     {
17009         this.startDate = startDate || -Infinity;
17010         if (this.startDate !== -Infinity) {
17011             this.startDate = this.parseDate(this.startDate);
17012         }
17013         this.update();
17014         this.updateNavArrows();
17015     },
17016
17017     setEndDate: function(endDate)
17018     {
17019         this.endDate = endDate || Infinity;
17020         if (this.endDate !== Infinity) {
17021             this.endDate = this.parseDate(this.endDate);
17022         }
17023         this.update();
17024         this.updateNavArrows();
17025     },
17026     
17027     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17028     {
17029         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17030         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17031             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17032         }
17033         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17034             return parseInt(d, 10);
17035         });
17036         this.update();
17037         this.updateNavArrows();
17038     },
17039     
17040     updateNavArrows: function() 
17041     {
17042         if(this.singleMode){
17043             return;
17044         }
17045         
17046         var d = new Date(this.viewDate),
17047         year = d.getUTCFullYear(),
17048         month = d.getUTCMonth();
17049         
17050         Roo.each(this.picker().select('.prev', true).elements, function(v){
17051             v.show();
17052             switch (this.viewMode) {
17053                 case 0:
17054
17055                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17056                         v.hide();
17057                     }
17058                     break;
17059                 case 1:
17060                 case 2:
17061                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17062                         v.hide();
17063                     }
17064                     break;
17065             }
17066         });
17067         
17068         Roo.each(this.picker().select('.next', true).elements, function(v){
17069             v.show();
17070             switch (this.viewMode) {
17071                 case 0:
17072
17073                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17074                         v.hide();
17075                     }
17076                     break;
17077                 case 1:
17078                 case 2:
17079                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17080                         v.hide();
17081                     }
17082                     break;
17083             }
17084         })
17085     },
17086     
17087     moveMonth: function(date, dir)
17088     {
17089         if (!dir) return date;
17090         var new_date = new Date(date.valueOf()),
17091         day = new_date.getUTCDate(),
17092         month = new_date.getUTCMonth(),
17093         mag = Math.abs(dir),
17094         new_month, test;
17095         dir = dir > 0 ? 1 : -1;
17096         if (mag == 1){
17097             test = dir == -1
17098             // If going back one month, make sure month is not current month
17099             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17100             ? function(){
17101                 return new_date.getUTCMonth() == month;
17102             }
17103             // If going forward one month, make sure month is as expected
17104             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17105             : function(){
17106                 return new_date.getUTCMonth() != new_month;
17107             };
17108             new_month = month + dir;
17109             new_date.setUTCMonth(new_month);
17110             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17111             if (new_month < 0 || new_month > 11)
17112                 new_month = (new_month + 12) % 12;
17113         } else {
17114             // For magnitudes >1, move one month at a time...
17115             for (var i=0; i<mag; i++)
17116                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17117                 new_date = this.moveMonth(new_date, dir);
17118             // ...then reset the day, keeping it in the new month
17119             new_month = new_date.getUTCMonth();
17120             new_date.setUTCDate(day);
17121             test = function(){
17122                 return new_month != new_date.getUTCMonth();
17123             };
17124         }
17125         // Common date-resetting loop -- if date is beyond end of month, make it
17126         // end of month
17127         while (test()){
17128             new_date.setUTCDate(--day);
17129             new_date.setUTCMonth(new_month);
17130         }
17131         return new_date;
17132     },
17133
17134     moveYear: function(date, dir)
17135     {
17136         return this.moveMonth(date, dir*12);
17137     },
17138
17139     dateWithinRange: function(date)
17140     {
17141         return date >= this.startDate && date <= this.endDate;
17142     },
17143
17144     
17145     remove: function() 
17146     {
17147         this.picker().remove();
17148     }
17149    
17150 });
17151
17152 Roo.apply(Roo.bootstrap.DateField,  {
17153     
17154     head : {
17155         tag: 'thead',
17156         cn: [
17157         {
17158             tag: 'tr',
17159             cn: [
17160             {
17161                 tag: 'th',
17162                 cls: 'prev',
17163                 html: '<i class="fa fa-arrow-left"/>'
17164             },
17165             {
17166                 tag: 'th',
17167                 cls: 'switch',
17168                 colspan: '5'
17169             },
17170             {
17171                 tag: 'th',
17172                 cls: 'next',
17173                 html: '<i class="fa fa-arrow-right"/>'
17174             }
17175
17176             ]
17177         }
17178         ]
17179     },
17180     
17181     content : {
17182         tag: 'tbody',
17183         cn: [
17184         {
17185             tag: 'tr',
17186             cn: [
17187             {
17188                 tag: 'td',
17189                 colspan: '7'
17190             }
17191             ]
17192         }
17193         ]
17194     },
17195     
17196     footer : {
17197         tag: 'tfoot',
17198         cn: [
17199         {
17200             tag: 'tr',
17201             cn: [
17202             {
17203                 tag: 'th',
17204                 colspan: '7',
17205                 cls: 'today'
17206             }
17207                     
17208             ]
17209         }
17210         ]
17211     },
17212     
17213     dates:{
17214         en: {
17215             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17216             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17217             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17218             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17219             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17220             today: "Today"
17221         }
17222     },
17223     
17224     modes: [
17225     {
17226         clsName: 'days',
17227         navFnc: 'Month',
17228         navStep: 1
17229     },
17230     {
17231         clsName: 'months',
17232         navFnc: 'FullYear',
17233         navStep: 1
17234     },
17235     {
17236         clsName: 'years',
17237         navFnc: 'FullYear',
17238         navStep: 10
17239     }]
17240 });
17241
17242 Roo.apply(Roo.bootstrap.DateField,  {
17243   
17244     template : {
17245         tag: 'div',
17246         cls: 'datepicker dropdown-menu roo-dynamic',
17247         cn: [
17248         {
17249             tag: 'div',
17250             cls: 'datepicker-days',
17251             cn: [
17252             {
17253                 tag: 'table',
17254                 cls: 'table-condensed',
17255                 cn:[
17256                 Roo.bootstrap.DateField.head,
17257                 {
17258                     tag: 'tbody'
17259                 },
17260                 Roo.bootstrap.DateField.footer
17261                 ]
17262             }
17263             ]
17264         },
17265         {
17266             tag: 'div',
17267             cls: 'datepicker-months',
17268             cn: [
17269             {
17270                 tag: 'table',
17271                 cls: 'table-condensed',
17272                 cn:[
17273                 Roo.bootstrap.DateField.head,
17274                 Roo.bootstrap.DateField.content,
17275                 Roo.bootstrap.DateField.footer
17276                 ]
17277             }
17278             ]
17279         },
17280         {
17281             tag: 'div',
17282             cls: 'datepicker-years',
17283             cn: [
17284             {
17285                 tag: 'table',
17286                 cls: 'table-condensed',
17287                 cn:[
17288                 Roo.bootstrap.DateField.head,
17289                 Roo.bootstrap.DateField.content,
17290                 Roo.bootstrap.DateField.footer
17291                 ]
17292             }
17293             ]
17294         }
17295         ]
17296     }
17297 });
17298
17299  
17300
17301  /*
17302  * - LGPL
17303  *
17304  * TimeField
17305  * 
17306  */
17307
17308 /**
17309  * @class Roo.bootstrap.TimeField
17310  * @extends Roo.bootstrap.Input
17311  * Bootstrap DateField class
17312  * 
17313  * 
17314  * @constructor
17315  * Create a new TimeField
17316  * @param {Object} config The config object
17317  */
17318
17319 Roo.bootstrap.TimeField = function(config){
17320     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17321     this.addEvents({
17322             /**
17323              * @event show
17324              * Fires when this field show.
17325              * @param {Roo.bootstrap.DateField} thisthis
17326              * @param {Mixed} date The date value
17327              */
17328             show : true,
17329             /**
17330              * @event show
17331              * Fires when this field hide.
17332              * @param {Roo.bootstrap.DateField} this
17333              * @param {Mixed} date The date value
17334              */
17335             hide : true,
17336             /**
17337              * @event select
17338              * Fires when select a date.
17339              * @param {Roo.bootstrap.DateField} this
17340              * @param {Mixed} date The date value
17341              */
17342             select : true
17343         });
17344 };
17345
17346 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17347     
17348     /**
17349      * @cfg {String} format
17350      * The default time format string which can be overriden for localization support.  The format must be
17351      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17352      */
17353     format : "H:i",
17354        
17355     onRender: function(ct, position)
17356     {
17357         
17358         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17359                 
17360         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17361         
17362         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17363         
17364         this.pop = this.picker().select('>.datepicker-time',true).first();
17365         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17366         
17367         this.picker().on('mousedown', this.onMousedown, this);
17368         this.picker().on('click', this.onClick, this);
17369         
17370         this.picker().addClass('datepicker-dropdown');
17371     
17372         this.fillTime();
17373         this.update();
17374             
17375         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17376         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17377         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17378         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17379         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17380         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17381
17382     },
17383     
17384     fireKey: function(e){
17385         if (!this.picker().isVisible()){
17386             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17387                 this.show();
17388             }
17389             return;
17390         }
17391
17392         e.preventDefault();
17393         
17394         switch(e.keyCode){
17395             case 27: // escape
17396                 this.hide();
17397                 break;
17398             case 37: // left
17399             case 39: // right
17400                 this.onTogglePeriod();
17401                 break;
17402             case 38: // up
17403                 this.onIncrementMinutes();
17404                 break;
17405             case 40: // down
17406                 this.onDecrementMinutes();
17407                 break;
17408             case 13: // enter
17409             case 9: // tab
17410                 this.setTime();
17411                 break;
17412         }
17413     },
17414     
17415     onClick: function(e) {
17416         e.stopPropagation();
17417         e.preventDefault();
17418     },
17419     
17420     picker : function()
17421     {
17422         return this.el.select('.datepicker', true).first();
17423     },
17424     
17425     fillTime: function()
17426     {    
17427         var time = this.pop.select('tbody', true).first();
17428         
17429         time.dom.innerHTML = '';
17430         
17431         time.createChild({
17432             tag: 'tr',
17433             cn: [
17434                 {
17435                     tag: 'td',
17436                     cn: [
17437                         {
17438                             tag: 'a',
17439                             href: '#',
17440                             cls: 'btn',
17441                             cn: [
17442                                 {
17443                                     tag: 'span',
17444                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17445                                 }
17446                             ]
17447                         } 
17448                     ]
17449                 },
17450                 {
17451                     tag: 'td',
17452                     cls: 'separator'
17453                 },
17454                 {
17455                     tag: 'td',
17456                     cn: [
17457                         {
17458                             tag: 'a',
17459                             href: '#',
17460                             cls: 'btn',
17461                             cn: [
17462                                 {
17463                                     tag: 'span',
17464                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17465                                 }
17466                             ]
17467                         }
17468                     ]
17469                 },
17470                 {
17471                     tag: 'td',
17472                     cls: 'separator'
17473                 }
17474             ]
17475         });
17476         
17477         time.createChild({
17478             tag: 'tr',
17479             cn: [
17480                 {
17481                     tag: 'td',
17482                     cn: [
17483                         {
17484                             tag: 'span',
17485                             cls: 'timepicker-hour',
17486                             html: '00'
17487                         }  
17488                     ]
17489                 },
17490                 {
17491                     tag: 'td',
17492                     cls: 'separator',
17493                     html: ':'
17494                 },
17495                 {
17496                     tag: 'td',
17497                     cn: [
17498                         {
17499                             tag: 'span',
17500                             cls: 'timepicker-minute',
17501                             html: '00'
17502                         }  
17503                     ]
17504                 },
17505                 {
17506                     tag: 'td',
17507                     cls: 'separator'
17508                 },
17509                 {
17510                     tag: 'td',
17511                     cn: [
17512                         {
17513                             tag: 'button',
17514                             type: 'button',
17515                             cls: 'btn btn-primary period',
17516                             html: 'AM'
17517                             
17518                         }
17519                     ]
17520                 }
17521             ]
17522         });
17523         
17524         time.createChild({
17525             tag: 'tr',
17526             cn: [
17527                 {
17528                     tag: 'td',
17529                     cn: [
17530                         {
17531                             tag: 'a',
17532                             href: '#',
17533                             cls: 'btn',
17534                             cn: [
17535                                 {
17536                                     tag: 'span',
17537                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17538                                 }
17539                             ]
17540                         }
17541                     ]
17542                 },
17543                 {
17544                     tag: 'td',
17545                     cls: 'separator'
17546                 },
17547                 {
17548                     tag: 'td',
17549                     cn: [
17550                         {
17551                             tag: 'a',
17552                             href: '#',
17553                             cls: 'btn',
17554                             cn: [
17555                                 {
17556                                     tag: 'span',
17557                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17558                                 }
17559                             ]
17560                         }
17561                     ]
17562                 },
17563                 {
17564                     tag: 'td',
17565                     cls: 'separator'
17566                 }
17567             ]
17568         });
17569         
17570     },
17571     
17572     update: function()
17573     {
17574         
17575         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17576         
17577         this.fill();
17578     },
17579     
17580     fill: function() 
17581     {
17582         var hours = this.time.getHours();
17583         var minutes = this.time.getMinutes();
17584         var period = 'AM';
17585         
17586         if(hours > 11){
17587             period = 'PM';
17588         }
17589         
17590         if(hours == 0){
17591             hours = 12;
17592         }
17593         
17594         
17595         if(hours > 12){
17596             hours = hours - 12;
17597         }
17598         
17599         if(hours < 10){
17600             hours = '0' + hours;
17601         }
17602         
17603         if(minutes < 10){
17604             minutes = '0' + minutes;
17605         }
17606         
17607         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17608         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17609         this.pop.select('button', true).first().dom.innerHTML = period;
17610         
17611     },
17612     
17613     place: function()
17614     {   
17615         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17616         
17617         var cls = ['bottom'];
17618         
17619         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17620             cls.pop();
17621             cls.push('top');
17622         }
17623         
17624         cls.push('right');
17625         
17626         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17627             cls.pop();
17628             cls.push('left');
17629         }
17630         
17631         this.picker().addClass(cls.join('-'));
17632         
17633         var _this = this;
17634         
17635         Roo.each(cls, function(c){
17636             if(c == 'bottom'){
17637                 _this.picker().setTop(_this.inputEl().getHeight());
17638                 return;
17639             }
17640             if(c == 'top'){
17641                 _this.picker().setTop(0 - _this.picker().getHeight());
17642                 return;
17643             }
17644             
17645             if(c == 'left'){
17646                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17647                 return;
17648             }
17649             if(c == 'right'){
17650                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17651                 return;
17652             }
17653         });
17654         
17655     },
17656   
17657     onFocus : function()
17658     {
17659         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17660         this.show();
17661     },
17662     
17663     onBlur : function()
17664     {
17665         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17666         this.hide();
17667     },
17668     
17669     show : function()
17670     {
17671         this.picker().show();
17672         this.pop.show();
17673         this.update();
17674         this.place();
17675         
17676         this.fireEvent('show', this, this.date);
17677     },
17678     
17679     hide : function()
17680     {
17681         this.picker().hide();
17682         this.pop.hide();
17683         
17684         this.fireEvent('hide', this, this.date);
17685     },
17686     
17687     setTime : function()
17688     {
17689         this.hide();
17690         this.setValue(this.time.format(this.format));
17691         
17692         this.fireEvent('select', this, this.date);
17693         
17694         
17695     },
17696     
17697     onMousedown: function(e){
17698         e.stopPropagation();
17699         e.preventDefault();
17700     },
17701     
17702     onIncrementHours: function()
17703     {
17704         Roo.log('onIncrementHours');
17705         this.time = this.time.add(Date.HOUR, 1);
17706         this.update();
17707         
17708     },
17709     
17710     onDecrementHours: function()
17711     {
17712         Roo.log('onDecrementHours');
17713         this.time = this.time.add(Date.HOUR, -1);
17714         this.update();
17715     },
17716     
17717     onIncrementMinutes: function()
17718     {
17719         Roo.log('onIncrementMinutes');
17720         this.time = this.time.add(Date.MINUTE, 1);
17721         this.update();
17722     },
17723     
17724     onDecrementMinutes: function()
17725     {
17726         Roo.log('onDecrementMinutes');
17727         this.time = this.time.add(Date.MINUTE, -1);
17728         this.update();
17729     },
17730     
17731     onTogglePeriod: function()
17732     {
17733         Roo.log('onTogglePeriod');
17734         this.time = this.time.add(Date.HOUR, 12);
17735         this.update();
17736     }
17737     
17738    
17739 });
17740
17741 Roo.apply(Roo.bootstrap.TimeField,  {
17742     
17743     content : {
17744         tag: 'tbody',
17745         cn: [
17746             {
17747                 tag: 'tr',
17748                 cn: [
17749                 {
17750                     tag: 'td',
17751                     colspan: '7'
17752                 }
17753                 ]
17754             }
17755         ]
17756     },
17757     
17758     footer : {
17759         tag: 'tfoot',
17760         cn: [
17761             {
17762                 tag: 'tr',
17763                 cn: [
17764                 {
17765                     tag: 'th',
17766                     colspan: '7',
17767                     cls: '',
17768                     cn: [
17769                         {
17770                             tag: 'button',
17771                             cls: 'btn btn-info ok',
17772                             html: 'OK'
17773                         }
17774                     ]
17775                 }
17776
17777                 ]
17778             }
17779         ]
17780     }
17781 });
17782
17783 Roo.apply(Roo.bootstrap.TimeField,  {
17784   
17785     template : {
17786         tag: 'div',
17787         cls: 'datepicker dropdown-menu',
17788         cn: [
17789             {
17790                 tag: 'div',
17791                 cls: 'datepicker-time',
17792                 cn: [
17793                 {
17794                     tag: 'table',
17795                     cls: 'table-condensed',
17796                     cn:[
17797                     Roo.bootstrap.TimeField.content,
17798                     Roo.bootstrap.TimeField.footer
17799                     ]
17800                 }
17801                 ]
17802             }
17803         ]
17804     }
17805 });
17806
17807  
17808
17809  /*
17810  * - LGPL
17811  *
17812  * MonthField
17813  * 
17814  */
17815
17816 /**
17817  * @class Roo.bootstrap.MonthField
17818  * @extends Roo.bootstrap.Input
17819  * Bootstrap MonthField class
17820  * 
17821  * @cfg {String} language default en
17822  * 
17823  * @constructor
17824  * Create a new MonthField
17825  * @param {Object} config The config object
17826  */
17827
17828 Roo.bootstrap.MonthField = function(config){
17829     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17830     
17831     this.addEvents({
17832         /**
17833          * @event show
17834          * Fires when this field show.
17835          * @param {Roo.bootstrap.MonthField} this
17836          * @param {Mixed} date The date value
17837          */
17838         show : true,
17839         /**
17840          * @event show
17841          * Fires when this field hide.
17842          * @param {Roo.bootstrap.MonthField} this
17843          * @param {Mixed} date The date value
17844          */
17845         hide : true,
17846         /**
17847          * @event select
17848          * Fires when select a date.
17849          * @param {Roo.bootstrap.MonthField} this
17850          * @param {String} oldvalue The old value
17851          * @param {String} newvalue The new value
17852          */
17853         select : true
17854     });
17855 };
17856
17857 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17858     
17859     onRender: function(ct, position)
17860     {
17861         
17862         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17863         
17864         this.language = this.language || 'en';
17865         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17866         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17867         
17868         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17869         this.isInline = false;
17870         this.isInput = true;
17871         this.component = this.el.select('.add-on', true).first() || false;
17872         this.component = (this.component && this.component.length === 0) ? false : this.component;
17873         this.hasInput = this.component && this.inputEL().length;
17874         
17875         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17876         
17877         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17878         
17879         this.picker().on('mousedown', this.onMousedown, this);
17880         this.picker().on('click', this.onClick, this);
17881         
17882         this.picker().addClass('datepicker-dropdown');
17883         
17884         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17885             v.setStyle('width', '189px');
17886         });
17887         
17888         this.fillMonths();
17889         
17890         this.update();
17891         
17892         if(this.isInline) {
17893             this.show();
17894         }
17895         
17896     },
17897     
17898     setValue: function(v, suppressEvent)
17899     {   
17900         var o = this.getValue();
17901         
17902         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17903         
17904         this.update();
17905
17906         if(suppressEvent !== true){
17907             this.fireEvent('select', this, o, v);
17908         }
17909         
17910     },
17911     
17912     getValue: function()
17913     {
17914         return this.value;
17915     },
17916     
17917     onClick: function(e) 
17918     {
17919         e.stopPropagation();
17920         e.preventDefault();
17921         
17922         var target = e.getTarget();
17923         
17924         if(target.nodeName.toLowerCase() === 'i'){
17925             target = Roo.get(target).dom.parentNode;
17926         }
17927         
17928         var nodeName = target.nodeName;
17929         var className = target.className;
17930         var html = target.innerHTML;
17931         
17932         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17933             return;
17934         }
17935         
17936         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17937         
17938         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17939         
17940         this.hide();
17941                         
17942     },
17943     
17944     picker : function()
17945     {
17946         return this.pickerEl;
17947     },
17948     
17949     fillMonths: function()
17950     {    
17951         var i = 0;
17952         var months = this.picker().select('>.datepicker-months td', true).first();
17953         
17954         months.dom.innerHTML = '';
17955         
17956         while (i < 12) {
17957             var month = {
17958                 tag: 'span',
17959                 cls: 'month',
17960                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17961             }
17962             
17963             months.createChild(month);
17964         }
17965         
17966     },
17967     
17968     update: function()
17969     {
17970         var _this = this;
17971         
17972         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17973             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17974         }
17975         
17976         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17977             e.removeClass('active');
17978             
17979             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17980                 e.addClass('active');
17981             }
17982         })
17983     },
17984     
17985     place: function()
17986     {
17987         if(this.isInline) return;
17988         
17989         this.picker().removeClass(['bottom', 'top']);
17990         
17991         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17992             /*
17993              * place to the top of element!
17994              *
17995              */
17996             
17997             this.picker().addClass('top');
17998             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17999             
18000             return;
18001         }
18002         
18003         this.picker().addClass('bottom');
18004         
18005         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18006     },
18007     
18008     onFocus : function()
18009     {
18010         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18011         this.show();
18012     },
18013     
18014     onBlur : function()
18015     {
18016         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18017         
18018         var d = this.inputEl().getValue();
18019         
18020         this.setValue(d);
18021                 
18022         this.hide();
18023     },
18024     
18025     show : function()
18026     {
18027         this.picker().show();
18028         this.picker().select('>.datepicker-months', true).first().show();
18029         this.update();
18030         this.place();
18031         
18032         this.fireEvent('show', this, this.date);
18033     },
18034     
18035     hide : function()
18036     {
18037         if(this.isInline) return;
18038         this.picker().hide();
18039         this.fireEvent('hide', this, this.date);
18040         
18041     },
18042     
18043     onMousedown: function(e)
18044     {
18045         e.stopPropagation();
18046         e.preventDefault();
18047     },
18048     
18049     keyup: function(e)
18050     {
18051         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18052         this.update();
18053     },
18054
18055     fireKey: function(e)
18056     {
18057         if (!this.picker().isVisible()){
18058             if (e.keyCode == 27) // allow escape to hide and re-show picker
18059                 this.show();
18060             return;
18061         }
18062         
18063         var dir;
18064         
18065         switch(e.keyCode){
18066             case 27: // escape
18067                 this.hide();
18068                 e.preventDefault();
18069                 break;
18070             case 37: // left
18071             case 39: // right
18072                 dir = e.keyCode == 37 ? -1 : 1;
18073                 
18074                 this.vIndex = this.vIndex + dir;
18075                 
18076                 if(this.vIndex < 0){
18077                     this.vIndex = 0;
18078                 }
18079                 
18080                 if(this.vIndex > 11){
18081                     this.vIndex = 11;
18082                 }
18083                 
18084                 if(isNaN(this.vIndex)){
18085                     this.vIndex = 0;
18086                 }
18087                 
18088                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18089                 
18090                 break;
18091             case 38: // up
18092             case 40: // down
18093                 
18094                 dir = e.keyCode == 38 ? -1 : 1;
18095                 
18096                 this.vIndex = this.vIndex + dir * 4;
18097                 
18098                 if(this.vIndex < 0){
18099                     this.vIndex = 0;
18100                 }
18101                 
18102                 if(this.vIndex > 11){
18103                     this.vIndex = 11;
18104                 }
18105                 
18106                 if(isNaN(this.vIndex)){
18107                     this.vIndex = 0;
18108                 }
18109                 
18110                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18111                 break;
18112                 
18113             case 13: // enter
18114                 
18115                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18116                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18117                 }
18118                 
18119                 this.hide();
18120                 e.preventDefault();
18121                 break;
18122             case 9: // tab
18123                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18124                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18125                 }
18126                 this.hide();
18127                 break;
18128             case 16: // shift
18129             case 17: // ctrl
18130             case 18: // alt
18131                 break;
18132             default :
18133                 this.hide();
18134                 
18135         }
18136     },
18137     
18138     remove: function() 
18139     {
18140         this.picker().remove();
18141     }
18142    
18143 });
18144
18145 Roo.apply(Roo.bootstrap.MonthField,  {
18146     
18147     content : {
18148         tag: 'tbody',
18149         cn: [
18150         {
18151             tag: 'tr',
18152             cn: [
18153             {
18154                 tag: 'td',
18155                 colspan: '7'
18156             }
18157             ]
18158         }
18159         ]
18160     },
18161     
18162     dates:{
18163         en: {
18164             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18165             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18166         }
18167     }
18168 });
18169
18170 Roo.apply(Roo.bootstrap.MonthField,  {
18171   
18172     template : {
18173         tag: 'div',
18174         cls: 'datepicker dropdown-menu roo-dynamic',
18175         cn: [
18176             {
18177                 tag: 'div',
18178                 cls: 'datepicker-months',
18179                 cn: [
18180                 {
18181                     tag: 'table',
18182                     cls: 'table-condensed',
18183                     cn:[
18184                         Roo.bootstrap.DateField.content
18185                     ]
18186                 }
18187                 ]
18188             }
18189         ]
18190     }
18191 });
18192
18193  
18194
18195  
18196  /*
18197  * - LGPL
18198  *
18199  * CheckBox
18200  * 
18201  */
18202
18203 /**
18204  * @class Roo.bootstrap.CheckBox
18205  * @extends Roo.bootstrap.Input
18206  * Bootstrap CheckBox class
18207  * 
18208  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18209  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18210  * @cfg {String} boxLabel The text that appears beside the checkbox
18211  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18212  * @cfg {Boolean} checked initnal the element
18213  * @cfg {Boolean} inline inline the element (default false)
18214  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18215  * 
18216  * @constructor
18217  * Create a new CheckBox
18218  * @param {Object} config The config object
18219  */
18220
18221 Roo.bootstrap.CheckBox = function(config){
18222     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18223    
18224     this.addEvents({
18225         /**
18226         * @event check
18227         * Fires when the element is checked or unchecked.
18228         * @param {Roo.bootstrap.CheckBox} this This input
18229         * @param {Boolean} checked The new checked value
18230         */
18231        check : true
18232     });
18233     
18234 };
18235
18236 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18237   
18238     inputType: 'checkbox',
18239     inputValue: 1,
18240     valueOff: 0,
18241     boxLabel: false,
18242     checked: false,
18243     weight : false,
18244     inline: false,
18245     
18246     getAutoCreate : function()
18247     {
18248         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18249         
18250         var id = Roo.id();
18251         
18252         var cfg = {};
18253         
18254         cfg.cls = 'form-group ' + this.inputType; //input-group
18255         
18256         if(this.inline){
18257             cfg.cls += ' ' + this.inputType + '-inline';
18258         }
18259         
18260         var input =  {
18261             tag: 'input',
18262             id : id,
18263             type : this.inputType,
18264             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18265             cls : 'roo-' + this.inputType, //'form-box',
18266             placeholder : this.placeholder || ''
18267             
18268         };
18269         
18270         if (this.weight) { // Validity check?
18271             cfg.cls += " " + this.inputType + "-" + this.weight;
18272         }
18273         
18274         if (this.disabled) {
18275             input.disabled=true;
18276         }
18277         
18278         if(this.checked){
18279             input.checked = this.checked;
18280         }
18281         
18282         if (this.name) {
18283             input.name = this.name;
18284         }
18285         
18286         if (this.size) {
18287             input.cls += ' input-' + this.size;
18288         }
18289         
18290         var settings=this;
18291         
18292         ['xs','sm','md','lg'].map(function(size){
18293             if (settings[size]) {
18294                 cfg.cls += ' col-' + size + '-' + settings[size];
18295             }
18296         });
18297         
18298         var inputblock = input;
18299          
18300         if (this.before || this.after) {
18301             
18302             inputblock = {
18303                 cls : 'input-group',
18304                 cn :  [] 
18305             };
18306             
18307             if (this.before) {
18308                 inputblock.cn.push({
18309                     tag :'span',
18310                     cls : 'input-group-addon',
18311                     html : this.before
18312                 });
18313             }
18314             
18315             inputblock.cn.push(input);
18316             
18317             if (this.after) {
18318                 inputblock.cn.push({
18319                     tag :'span',
18320                     cls : 'input-group-addon',
18321                     html : this.after
18322                 });
18323             }
18324             
18325         }
18326         
18327         if (align ==='left' && this.fieldLabel.length) {
18328                 Roo.log("left and has label");
18329                 cfg.cn = [
18330                     
18331                     {
18332                         tag: 'label',
18333                         'for' :  id,
18334                         cls : 'control-label col-md-' + this.labelWidth,
18335                         html : this.fieldLabel
18336                         
18337                     },
18338                     {
18339                         cls : "col-md-" + (12 - this.labelWidth), 
18340                         cn: [
18341                             inputblock
18342                         ]
18343                     }
18344                     
18345                 ];
18346         } else if ( this.fieldLabel.length) {
18347                 Roo.log(" label");
18348                 cfg.cn = [
18349                    
18350                     {
18351                         tag: this.boxLabel ? 'span' : 'label',
18352                         'for': id,
18353                         cls: 'control-label box-input-label',
18354                         //cls : 'input-group-addon',
18355                         html : this.fieldLabel
18356                         
18357                     },
18358                     
18359                     inputblock
18360                     
18361                 ];
18362
18363         } else {
18364             
18365                 Roo.log(" no label && no align");
18366                 cfg.cn = [  inputblock ] ;
18367                 
18368                 
18369         }
18370         if(this.boxLabel){
18371              var boxLabelCfg = {
18372                 tag: 'label',
18373                 //'for': id, // box label is handled by onclick - so no for...
18374                 cls: 'box-label',
18375                 html: this.boxLabel
18376             }
18377             
18378             if(this.tooltip){
18379                 boxLabelCfg.tooltip = this.tooltip;
18380             }
18381              
18382             cfg.cn.push(boxLabelCfg);
18383         }
18384         
18385         
18386        
18387         return cfg;
18388         
18389     },
18390     
18391     /**
18392      * return the real input element.
18393      */
18394     inputEl: function ()
18395     {
18396         return this.el.select('input.roo-' + this.inputType,true).first();
18397     },
18398     
18399     labelEl: function()
18400     {
18401         return this.el.select('label.control-label',true).first();
18402     },
18403     /* depricated... */
18404     
18405     label: function()
18406     {
18407         return this.labelEl();
18408     },
18409     
18410     initEvents : function()
18411     {
18412 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18413         
18414         this.inputEl().on('click', this.onClick,  this);
18415         
18416         if (this.boxLabel) { 
18417             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18418         }
18419         
18420         this.startValue = this.getValue();
18421         
18422         if(this.groupId){
18423             Roo.bootstrap.CheckBox.register(this);
18424         }
18425     },
18426     
18427     onClick : function()
18428     {   
18429         this.setChecked(!this.checked);
18430     },
18431     
18432     setChecked : function(state,suppressEvent)
18433     {
18434         this.startValue = this.getValue();
18435         
18436         if(this.inputType == 'radio'){
18437             
18438             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18439                 e.dom.checked = false;
18440             });
18441             
18442             this.inputEl().dom.checked = true;
18443             
18444             this.inputEl().dom.value = this.inputValue;
18445             
18446             if(suppressEvent !== true){
18447                 this.fireEvent('check', this, true);
18448             }
18449             
18450             this.validate();
18451             
18452             return;
18453         }
18454         
18455         this.checked = state;
18456         
18457         this.inputEl().dom.checked = state;
18458         
18459         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18460         
18461         if(suppressEvent !== true){
18462             this.fireEvent('check', this, state);
18463         }
18464         
18465         this.validate();
18466     },
18467     
18468     getValue : function()
18469     {
18470         if(this.inputType == 'radio'){
18471             return this.getGroupValue();
18472         }
18473         
18474         return this.inputEl().getValue();
18475         
18476     },
18477     
18478     getGroupValue : function()
18479     {
18480         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18481             return '';
18482         }
18483         
18484         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18485     },
18486     
18487     setValue : function(v,suppressEvent)
18488     {
18489         if(this.inputType == 'radio'){
18490             this.setGroupValue(v, suppressEvent);
18491             return;
18492         }
18493         
18494         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18495         
18496         this.validate();
18497     },
18498     
18499     setGroupValue : function(v, suppressEvent)
18500     {
18501         this.startValue = this.getValue();
18502         
18503         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18504             e.dom.checked = false;
18505             
18506             if(e.dom.value == v){
18507                 e.dom.checked = true;
18508             }
18509         });
18510         
18511         if(suppressEvent !== true){
18512             this.fireEvent('check', this, true);
18513         }
18514
18515         this.validate();
18516         
18517         return;
18518     },
18519     
18520     validate : function()
18521     {
18522         if(
18523                 this.disabled || 
18524                 (this.inputType == 'radio' && this.validateRadio()) ||
18525                 (this.inputType == 'checkbox' && this.validateCheckbox())
18526         ){
18527             this.markValid();
18528             return true;
18529         }
18530         
18531         this.markInvalid();
18532         return false;
18533     },
18534     
18535     validateRadio : function()
18536     {
18537         var valid = false;
18538         
18539         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18540             if(!e.dom.checked){
18541                 return;
18542             }
18543             
18544             valid = true;
18545             
18546             return false;
18547         });
18548         
18549         return valid;
18550     },
18551     
18552     validateCheckbox : function()
18553     {
18554         if(!this.groupId){
18555             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18556         }
18557         
18558         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18559         
18560         if(!group){
18561             return false;
18562         }
18563         
18564         var r = false;
18565         
18566         for(var i in group){
18567             if(r){
18568                 break;
18569             }
18570             
18571             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18572         }
18573         
18574         return r;
18575     },
18576     
18577     /**
18578      * Mark this field as valid
18579      */
18580     markValid : function()
18581     {
18582         if(this.allowBlank){
18583             return;
18584         }
18585         
18586         var _this = this;
18587         
18588         this.fireEvent('valid', this);
18589         
18590         if(this.inputType == 'radio'){
18591             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18592                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18593                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18594             });
18595             
18596             return;
18597         }
18598         
18599         if(!this.groupId){
18600             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18601             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18602             return;
18603         }
18604         
18605         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18606             
18607         if(!group){
18608             return;
18609         }
18610         
18611         for(var i in group){
18612             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18613             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18614         }
18615     },
18616     
18617      /**
18618      * Mark this field as invalid
18619      * @param {String} msg The validation message
18620      */
18621     markInvalid : function(msg)
18622     {
18623         if(this.allowBlank){
18624             return;
18625         }
18626         
18627         var _this = this;
18628         
18629         this.fireEvent('invalid', this, msg);
18630         
18631         if(this.inputType == 'radio'){
18632             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18633                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18634                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18635             });
18636             
18637             return;
18638         }
18639         
18640         if(!this.groupId){
18641             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18642             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18643             return;
18644         }
18645         
18646         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18647             
18648         if(!group){
18649             return;
18650         }
18651         
18652         for(var i in group){
18653             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18654             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18655         }
18656         
18657     }
18658     
18659 });
18660
18661 Roo.apply(Roo.bootstrap.CheckBox, {
18662     
18663     groups: {},
18664     
18665      /**
18666     * register a CheckBox Group
18667     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18668     */
18669     register : function(checkbox)
18670     {
18671         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18672             this.groups[checkbox.groupId] = {};
18673         }
18674         
18675         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18676             return;
18677         }
18678         
18679         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18680         
18681     },
18682     /**
18683     * fetch a CheckBox Group based on the group ID
18684     * @param {string} the group ID
18685     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18686     */
18687     get: function(groupId) {
18688         if (typeof(this.groups[groupId]) == 'undefined') {
18689             return false;
18690         }
18691         
18692         return this.groups[groupId] ;
18693     }
18694     
18695     
18696 });
18697 /*
18698  * - LGPL
18699  *
18700  * Radio
18701  *
18702  *
18703  * not inline
18704  *<div class="radio">
18705   <label>
18706     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18707     Option one is this and that&mdash;be sure to include why it's great
18708   </label>
18709 </div>
18710  *
18711  *
18712  *inline
18713  *<span>
18714  *<label class="radio-inline">fieldLabel</label>
18715  *<label class="radio-inline">
18716   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18717 </label>
18718 <span>
18719  * 
18720  * 
18721  */
18722
18723 /**
18724  * @class Roo.bootstrap.Radio
18725  * @extends Roo.bootstrap.CheckBox
18726  * Bootstrap Radio class
18727
18728  * @constructor
18729  * Create a new Radio
18730  * @param {Object} config The config object
18731  */
18732
18733 Roo.bootstrap.Radio = function(config){
18734     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18735    
18736 };
18737
18738 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18739     
18740     inputType: 'radio',
18741     inputValue: '',
18742     valueOff: '',
18743     
18744     getAutoCreate : function()
18745     {
18746         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18747         align = align || 'left'; // default...
18748         
18749         
18750         
18751         var id = Roo.id();
18752         
18753         var cfg = {
18754                 tag : this.inline ? 'span' : 'div',
18755                 cls : '',
18756                 cn : []
18757         };
18758         
18759         var inline = this.inline ? ' radio-inline' : '';
18760         
18761         var lbl = {
18762                 tag: 'label' ,
18763                 // does not need for, as we wrap the input with it..
18764                 'for' : id,
18765                 cls : 'control-label box-label' + inline,
18766                 cn : []
18767         };
18768         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18769         
18770         var fieldLabel = {
18771             tag: 'label' ,
18772             //cls : 'control-label' + inline,
18773             html : this.fieldLabel,
18774             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18775         };
18776         
18777  
18778         
18779         
18780         var input =  {
18781             tag: 'input',
18782             id : id,
18783             type : this.inputType,
18784             //value : (!this.checked) ? this.valueOff : this.inputValue,
18785             value : this.inputValue,
18786             cls : 'roo-radio',
18787             placeholder : this.placeholder || '' // ?? needed????
18788             
18789         };
18790         if (this.weight) { // Validity check?
18791             input.cls += " radio-" + this.weight;
18792         }
18793         if (this.disabled) {
18794             input.disabled=true;
18795         }
18796         
18797         if(this.checked){
18798             input.checked = this.checked;
18799         }
18800         
18801         if (this.name) {
18802             input.name = this.name;
18803         }
18804         
18805         if (this.size) {
18806             input.cls += ' input-' + this.size;
18807         }
18808         
18809         //?? can span's inline have a width??
18810         
18811         var settings=this;
18812         ['xs','sm','md','lg'].map(function(size){
18813             if (settings[size]) {
18814                 cfg.cls += ' col-' + size + '-' + settings[size];
18815             }
18816         });
18817         
18818         var inputblock = input;
18819         
18820         if (this.before || this.after) {
18821             
18822             inputblock = {
18823                 cls : 'input-group',
18824                 tag : 'span',
18825                 cn :  [] 
18826             };
18827             if (this.before) {
18828                 inputblock.cn.push({
18829                     tag :'span',
18830                     cls : 'input-group-addon',
18831                     html : this.before
18832                 });
18833             }
18834             inputblock.cn.push(input);
18835             if (this.after) {
18836                 inputblock.cn.push({
18837                     tag :'span',
18838                     cls : 'input-group-addon',
18839                     html : this.after
18840                 });
18841             }
18842             
18843         };
18844         
18845         
18846         if (this.fieldLabel && this.fieldLabel.length) {
18847             cfg.cn.push(fieldLabel);
18848         }
18849        
18850         // normal bootstrap puts the input inside the label.
18851         // however with our styled version - it has to go after the input.
18852        
18853         //lbl.cn.push(inputblock);
18854         
18855         var lblwrap =  {
18856             tag: 'span',
18857             cls: 'radio' + inline,
18858             cn: [
18859                 inputblock,
18860                 lbl
18861             ]
18862         };
18863         
18864         cfg.cn.push( lblwrap);
18865         
18866         if(this.boxLabel){
18867             lbl.cn.push({
18868                 tag: 'span',
18869                 html: this.boxLabel
18870             })
18871         }
18872          
18873         
18874         return cfg;
18875         
18876     },
18877     
18878     initEvents : function()
18879     {
18880 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18881         
18882         this.inputEl().on('click', this.onClick,  this);
18883         if (this.boxLabel) {
18884             Roo.log('find label')
18885             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18886         }
18887         
18888     },
18889     
18890     inputEl: function ()
18891     {
18892         return this.el.select('input.roo-radio',true).first();
18893     },
18894     onClick : function()
18895     {   
18896         Roo.log("click");
18897         this.setChecked(true);
18898     },
18899     
18900     setChecked : function(state,suppressEvent)
18901     {
18902         if(state){
18903             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18904                 v.dom.checked = false;
18905             });
18906         }
18907         Roo.log(this.inputEl().dom);
18908         this.checked = state;
18909         this.inputEl().dom.checked = state;
18910         
18911         if(suppressEvent !== true){
18912             this.fireEvent('check', this, state);
18913         }
18914         
18915         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18916         
18917     },
18918     
18919     getGroupValue : function()
18920     {
18921         var value = '';
18922         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18923             if(v.dom.checked == true){
18924                 value = v.dom.value;
18925             }
18926         });
18927         
18928         return value;
18929     },
18930     
18931     /**
18932      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18933      * @return {Mixed} value The field value
18934      */
18935     getValue : function(){
18936         return this.getGroupValue();
18937     }
18938     
18939 });
18940
18941  
18942 //<script type="text/javascript">
18943
18944 /*
18945  * Based  Ext JS Library 1.1.1
18946  * Copyright(c) 2006-2007, Ext JS, LLC.
18947  * LGPL
18948  *
18949  */
18950  
18951 /**
18952  * @class Roo.HtmlEditorCore
18953  * @extends Roo.Component
18954  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18955  *
18956  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18957  */
18958
18959 Roo.HtmlEditorCore = function(config){
18960     
18961     
18962     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18963     
18964     
18965     this.addEvents({
18966         /**
18967          * @event initialize
18968          * Fires when the editor is fully initialized (including the iframe)
18969          * @param {Roo.HtmlEditorCore} this
18970          */
18971         initialize: true,
18972         /**
18973          * @event activate
18974          * Fires when the editor is first receives the focus. Any insertion must wait
18975          * until after this event.
18976          * @param {Roo.HtmlEditorCore} this
18977          */
18978         activate: true,
18979          /**
18980          * @event beforesync
18981          * Fires before the textarea is updated with content from the editor iframe. Return false
18982          * to cancel the sync.
18983          * @param {Roo.HtmlEditorCore} this
18984          * @param {String} html
18985          */
18986         beforesync: true,
18987          /**
18988          * @event beforepush
18989          * Fires before the iframe editor is updated with content from the textarea. Return false
18990          * to cancel the push.
18991          * @param {Roo.HtmlEditorCore} this
18992          * @param {String} html
18993          */
18994         beforepush: true,
18995          /**
18996          * @event sync
18997          * Fires when the textarea is updated with content from the editor iframe.
18998          * @param {Roo.HtmlEditorCore} this
18999          * @param {String} html
19000          */
19001         sync: true,
19002          /**
19003          * @event push
19004          * Fires when the iframe editor is updated with content from the textarea.
19005          * @param {Roo.HtmlEditorCore} this
19006          * @param {String} html
19007          */
19008         push: true,
19009         
19010         /**
19011          * @event editorevent
19012          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19013          * @param {Roo.HtmlEditorCore} this
19014          */
19015         editorevent: true
19016         
19017     });
19018     
19019     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19020     
19021     // defaults : white / black...
19022     this.applyBlacklists();
19023     
19024     
19025     
19026 };
19027
19028
19029 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19030
19031
19032      /**
19033      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19034      */
19035     
19036     owner : false,
19037     
19038      /**
19039      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19040      *                        Roo.resizable.
19041      */
19042     resizable : false,
19043      /**
19044      * @cfg {Number} height (in pixels)
19045      */   
19046     height: 300,
19047    /**
19048      * @cfg {Number} width (in pixels)
19049      */   
19050     width: 500,
19051     
19052     /**
19053      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19054      * 
19055      */
19056     stylesheets: false,
19057     
19058     // id of frame..
19059     frameId: false,
19060     
19061     // private properties
19062     validationEvent : false,
19063     deferHeight: true,
19064     initialized : false,
19065     activated : false,
19066     sourceEditMode : false,
19067     onFocus : Roo.emptyFn,
19068     iframePad:3,
19069     hideMode:'offsets',
19070     
19071     clearUp: true,
19072     
19073     // blacklist + whitelisted elements..
19074     black: false,
19075     white: false,
19076      
19077     
19078
19079     /**
19080      * Protected method that will not generally be called directly. It
19081      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19082      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19083      */
19084     getDocMarkup : function(){
19085         // body styles..
19086         var st = '';
19087         
19088         // inherit styels from page...?? 
19089         if (this.stylesheets === false) {
19090             
19091             Roo.get(document.head).select('style').each(function(node) {
19092                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19093             });
19094             
19095             Roo.get(document.head).select('link').each(function(node) { 
19096                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19097             });
19098             
19099         } else if (!this.stylesheets.length) {
19100                 // simple..
19101                 st = '<style type="text/css">' +
19102                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19103                    '</style>';
19104         } else { 
19105             
19106         }
19107         
19108         st +=  '<style type="text/css">' +
19109             'IMG { cursor: pointer } ' +
19110         '</style>';
19111
19112         
19113         return '<html><head>' + st  +
19114             //<style type="text/css">' +
19115             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19116             //'</style>' +
19117             ' </head><body class="roo-htmleditor-body"></body></html>';
19118     },
19119
19120     // private
19121     onRender : function(ct, position)
19122     {
19123         var _t = this;
19124         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19125         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19126         
19127         
19128         this.el.dom.style.border = '0 none';
19129         this.el.dom.setAttribute('tabIndex', -1);
19130         this.el.addClass('x-hidden hide');
19131         
19132         
19133         
19134         if(Roo.isIE){ // fix IE 1px bogus margin
19135             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19136         }
19137        
19138         
19139         this.frameId = Roo.id();
19140         
19141          
19142         
19143         var iframe = this.owner.wrap.createChild({
19144             tag: 'iframe',
19145             cls: 'form-control', // bootstrap..
19146             id: this.frameId,
19147             name: this.frameId,
19148             frameBorder : 'no',
19149             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19150         }, this.el
19151         );
19152         
19153         
19154         this.iframe = iframe.dom;
19155
19156          this.assignDocWin();
19157         
19158         this.doc.designMode = 'on';
19159        
19160         this.doc.open();
19161         this.doc.write(this.getDocMarkup());
19162         this.doc.close();
19163
19164         
19165         var task = { // must defer to wait for browser to be ready
19166             run : function(){
19167                 //console.log("run task?" + this.doc.readyState);
19168                 this.assignDocWin();
19169                 if(this.doc.body || this.doc.readyState == 'complete'){
19170                     try {
19171                         this.doc.designMode="on";
19172                     } catch (e) {
19173                         return;
19174                     }
19175                     Roo.TaskMgr.stop(task);
19176                     this.initEditor.defer(10, this);
19177                 }
19178             },
19179             interval : 10,
19180             duration: 10000,
19181             scope: this
19182         };
19183         Roo.TaskMgr.start(task);
19184
19185     },
19186
19187     // private
19188     onResize : function(w, h)
19189     {
19190          Roo.log('resize: ' +w + ',' + h );
19191         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19192         if(!this.iframe){
19193             return;
19194         }
19195         if(typeof w == 'number'){
19196             
19197             this.iframe.style.width = w + 'px';
19198         }
19199         if(typeof h == 'number'){
19200             
19201             this.iframe.style.height = h + 'px';
19202             if(this.doc){
19203                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19204             }
19205         }
19206         
19207     },
19208
19209     /**
19210      * Toggles the editor between standard and source edit mode.
19211      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19212      */
19213     toggleSourceEdit : function(sourceEditMode){
19214         
19215         this.sourceEditMode = sourceEditMode === true;
19216         
19217         if(this.sourceEditMode){
19218  
19219             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19220             
19221         }else{
19222             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19223             //this.iframe.className = '';
19224             this.deferFocus();
19225         }
19226         //this.setSize(this.owner.wrap.getSize());
19227         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19228     },
19229
19230     
19231   
19232
19233     /**
19234      * Protected method that will not generally be called directly. If you need/want
19235      * custom HTML cleanup, this is the method you should override.
19236      * @param {String} html The HTML to be cleaned
19237      * return {String} The cleaned HTML
19238      */
19239     cleanHtml : function(html){
19240         html = String(html);
19241         if(html.length > 5){
19242             if(Roo.isSafari){ // strip safari nonsense
19243                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19244             }
19245         }
19246         if(html == '&nbsp;'){
19247             html = '';
19248         }
19249         return html;
19250     },
19251
19252     /**
19253      * HTML Editor -> Textarea
19254      * Protected method that will not generally be called directly. Syncs the contents
19255      * of the editor iframe with the textarea.
19256      */
19257     syncValue : function(){
19258         if(this.initialized){
19259             var bd = (this.doc.body || this.doc.documentElement);
19260             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19261             var html = bd.innerHTML;
19262             if(Roo.isSafari){
19263                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19264                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19265                 if(m && m[1]){
19266                     html = '<div style="'+m[0]+'">' + html + '</div>';
19267                 }
19268             }
19269             html = this.cleanHtml(html);
19270             // fix up the special chars.. normaly like back quotes in word...
19271             // however we do not want to do this with chinese..
19272             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19273                 var cc = b.charCodeAt();
19274                 if (
19275                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19276                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19277                     (cc >= 0xf900 && cc < 0xfb00 )
19278                 ) {
19279                         return b;
19280                 }
19281                 return "&#"+cc+";" 
19282             });
19283             if(this.owner.fireEvent('beforesync', this, html) !== false){
19284                 this.el.dom.value = html;
19285                 this.owner.fireEvent('sync', this, html);
19286             }
19287         }
19288     },
19289
19290     /**
19291      * Protected method that will not generally be called directly. Pushes the value of the textarea
19292      * into the iframe editor.
19293      */
19294     pushValue : function(){
19295         if(this.initialized){
19296             var v = this.el.dom.value.trim();
19297             
19298 //            if(v.length < 1){
19299 //                v = '&#160;';
19300 //            }
19301             
19302             if(this.owner.fireEvent('beforepush', this, v) !== false){
19303                 var d = (this.doc.body || this.doc.documentElement);
19304                 d.innerHTML = v;
19305                 this.cleanUpPaste();
19306                 this.el.dom.value = d.innerHTML;
19307                 this.owner.fireEvent('push', this, v);
19308             }
19309         }
19310     },
19311
19312     // private
19313     deferFocus : function(){
19314         this.focus.defer(10, this);
19315     },
19316
19317     // doc'ed in Field
19318     focus : function(){
19319         if(this.win && !this.sourceEditMode){
19320             this.win.focus();
19321         }else{
19322             this.el.focus();
19323         }
19324     },
19325     
19326     assignDocWin: function()
19327     {
19328         var iframe = this.iframe;
19329         
19330          if(Roo.isIE){
19331             this.doc = iframe.contentWindow.document;
19332             this.win = iframe.contentWindow;
19333         } else {
19334 //            if (!Roo.get(this.frameId)) {
19335 //                return;
19336 //            }
19337 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19338 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19339             
19340             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19341                 return;
19342             }
19343             
19344             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19345             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19346         }
19347     },
19348     
19349     // private
19350     initEditor : function(){
19351         //console.log("INIT EDITOR");
19352         this.assignDocWin();
19353         
19354         
19355         
19356         this.doc.designMode="on";
19357         this.doc.open();
19358         this.doc.write(this.getDocMarkup());
19359         this.doc.close();
19360         
19361         var dbody = (this.doc.body || this.doc.documentElement);
19362         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19363         // this copies styles from the containing element into thsi one..
19364         // not sure why we need all of this..
19365         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19366         
19367         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19368         //ss['background-attachment'] = 'fixed'; // w3c
19369         dbody.bgProperties = 'fixed'; // ie
19370         //Roo.DomHelper.applyStyles(dbody, ss);
19371         Roo.EventManager.on(this.doc, {
19372             //'mousedown': this.onEditorEvent,
19373             'mouseup': this.onEditorEvent,
19374             'dblclick': this.onEditorEvent,
19375             'click': this.onEditorEvent,
19376             'keyup': this.onEditorEvent,
19377             buffer:100,
19378             scope: this
19379         });
19380         if(Roo.isGecko){
19381             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19382         }
19383         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19384             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19385         }
19386         this.initialized = true;
19387
19388         this.owner.fireEvent('initialize', this);
19389         this.pushValue();
19390     },
19391
19392     // private
19393     onDestroy : function(){
19394         
19395         
19396         
19397         if(this.rendered){
19398             
19399             //for (var i =0; i < this.toolbars.length;i++) {
19400             //    // fixme - ask toolbars for heights?
19401             //    this.toolbars[i].onDestroy();
19402            // }
19403             
19404             //this.wrap.dom.innerHTML = '';
19405             //this.wrap.remove();
19406         }
19407     },
19408
19409     // private
19410     onFirstFocus : function(){
19411         
19412         this.assignDocWin();
19413         
19414         
19415         this.activated = true;
19416          
19417     
19418         if(Roo.isGecko){ // prevent silly gecko errors
19419             this.win.focus();
19420             var s = this.win.getSelection();
19421             if(!s.focusNode || s.focusNode.nodeType != 3){
19422                 var r = s.getRangeAt(0);
19423                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19424                 r.collapse(true);
19425                 this.deferFocus();
19426             }
19427             try{
19428                 this.execCmd('useCSS', true);
19429                 this.execCmd('styleWithCSS', false);
19430             }catch(e){}
19431         }
19432         this.owner.fireEvent('activate', this);
19433     },
19434
19435     // private
19436     adjustFont: function(btn){
19437         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19438         //if(Roo.isSafari){ // safari
19439         //    adjust *= 2;
19440        // }
19441         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19442         if(Roo.isSafari){ // safari
19443             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19444             v =  (v < 10) ? 10 : v;
19445             v =  (v > 48) ? 48 : v;
19446             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19447             
19448         }
19449         
19450         
19451         v = Math.max(1, v+adjust);
19452         
19453         this.execCmd('FontSize', v  );
19454     },
19455
19456     onEditorEvent : function(e)
19457     {
19458         this.owner.fireEvent('editorevent', this, e);
19459       //  this.updateToolbar();
19460         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19461     },
19462
19463     insertTag : function(tg)
19464     {
19465         // could be a bit smarter... -> wrap the current selected tRoo..
19466         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19467             
19468             range = this.createRange(this.getSelection());
19469             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19470             wrappingNode.appendChild(range.extractContents());
19471             range.insertNode(wrappingNode);
19472
19473             return;
19474             
19475             
19476             
19477         }
19478         this.execCmd("formatblock",   tg);
19479         
19480     },
19481     
19482     insertText : function(txt)
19483     {
19484         
19485         
19486         var range = this.createRange();
19487         range.deleteContents();
19488                //alert(Sender.getAttribute('label'));
19489                
19490         range.insertNode(this.doc.createTextNode(txt));
19491     } ,
19492     
19493      
19494
19495     /**
19496      * Executes a Midas editor command on the editor document and performs necessary focus and
19497      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19498      * @param {String} cmd The Midas command
19499      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19500      */
19501     relayCmd : function(cmd, value){
19502         this.win.focus();
19503         this.execCmd(cmd, value);
19504         this.owner.fireEvent('editorevent', this);
19505         //this.updateToolbar();
19506         this.owner.deferFocus();
19507     },
19508
19509     /**
19510      * Executes a Midas editor command directly on the editor document.
19511      * For visual commands, you should use {@link #relayCmd} instead.
19512      * <b>This should only be called after the editor is initialized.</b>
19513      * @param {String} cmd The Midas command
19514      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19515      */
19516     execCmd : function(cmd, value){
19517         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19518         this.syncValue();
19519     },
19520  
19521  
19522    
19523     /**
19524      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19525      * to insert tRoo.
19526      * @param {String} text | dom node.. 
19527      */
19528     insertAtCursor : function(text)
19529     {
19530         
19531         
19532         
19533         if(!this.activated){
19534             return;
19535         }
19536         /*
19537         if(Roo.isIE){
19538             this.win.focus();
19539             var r = this.doc.selection.createRange();
19540             if(r){
19541                 r.collapse(true);
19542                 r.pasteHTML(text);
19543                 this.syncValue();
19544                 this.deferFocus();
19545             
19546             }
19547             return;
19548         }
19549         */
19550         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19551             this.win.focus();
19552             
19553             
19554             // from jquery ui (MIT licenced)
19555             var range, node;
19556             var win = this.win;
19557             
19558             if (win.getSelection && win.getSelection().getRangeAt) {
19559                 range = win.getSelection().getRangeAt(0);
19560                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19561                 range.insertNode(node);
19562             } else if (win.document.selection && win.document.selection.createRange) {
19563                 // no firefox support
19564                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19565                 win.document.selection.createRange().pasteHTML(txt);
19566             } else {
19567                 // no firefox support
19568                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19569                 this.execCmd('InsertHTML', txt);
19570             } 
19571             
19572             this.syncValue();
19573             
19574             this.deferFocus();
19575         }
19576     },
19577  // private
19578     mozKeyPress : function(e){
19579         if(e.ctrlKey){
19580             var c = e.getCharCode(), cmd;
19581           
19582             if(c > 0){
19583                 c = String.fromCharCode(c).toLowerCase();
19584                 switch(c){
19585                     case 'b':
19586                         cmd = 'bold';
19587                         break;
19588                     case 'i':
19589                         cmd = 'italic';
19590                         break;
19591                     
19592                     case 'u':
19593                         cmd = 'underline';
19594                         break;
19595                     
19596                     case 'v':
19597                         this.cleanUpPaste.defer(100, this);
19598                         return;
19599                         
19600                 }
19601                 if(cmd){
19602                     this.win.focus();
19603                     this.execCmd(cmd);
19604                     this.deferFocus();
19605                     e.preventDefault();
19606                 }
19607                 
19608             }
19609         }
19610     },
19611
19612     // private
19613     fixKeys : function(){ // load time branching for fastest keydown performance
19614         if(Roo.isIE){
19615             return function(e){
19616                 var k = e.getKey(), r;
19617                 if(k == e.TAB){
19618                     e.stopEvent();
19619                     r = this.doc.selection.createRange();
19620                     if(r){
19621                         r.collapse(true);
19622                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19623                         this.deferFocus();
19624                     }
19625                     return;
19626                 }
19627                 
19628                 if(k == e.ENTER){
19629                     r = this.doc.selection.createRange();
19630                     if(r){
19631                         var target = r.parentElement();
19632                         if(!target || target.tagName.toLowerCase() != 'li'){
19633                             e.stopEvent();
19634                             r.pasteHTML('<br />');
19635                             r.collapse(false);
19636                             r.select();
19637                         }
19638                     }
19639                 }
19640                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19641                     this.cleanUpPaste.defer(100, this);
19642                     return;
19643                 }
19644                 
19645                 
19646             };
19647         }else if(Roo.isOpera){
19648             return function(e){
19649                 var k = e.getKey();
19650                 if(k == e.TAB){
19651                     e.stopEvent();
19652                     this.win.focus();
19653                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19654                     this.deferFocus();
19655                 }
19656                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19657                     this.cleanUpPaste.defer(100, this);
19658                     return;
19659                 }
19660                 
19661             };
19662         }else if(Roo.isSafari){
19663             return function(e){
19664                 var k = e.getKey();
19665                 
19666                 if(k == e.TAB){
19667                     e.stopEvent();
19668                     this.execCmd('InsertText','\t');
19669                     this.deferFocus();
19670                     return;
19671                 }
19672                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19673                     this.cleanUpPaste.defer(100, this);
19674                     return;
19675                 }
19676                 
19677              };
19678         }
19679     }(),
19680     
19681     getAllAncestors: function()
19682     {
19683         var p = this.getSelectedNode();
19684         var a = [];
19685         if (!p) {
19686             a.push(p); // push blank onto stack..
19687             p = this.getParentElement();
19688         }
19689         
19690         
19691         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19692             a.push(p);
19693             p = p.parentNode;
19694         }
19695         a.push(this.doc.body);
19696         return a;
19697     },
19698     lastSel : false,
19699     lastSelNode : false,
19700     
19701     
19702     getSelection : function() 
19703     {
19704         this.assignDocWin();
19705         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19706     },
19707     
19708     getSelectedNode: function() 
19709     {
19710         // this may only work on Gecko!!!
19711         
19712         // should we cache this!!!!
19713         
19714         
19715         
19716          
19717         var range = this.createRange(this.getSelection()).cloneRange();
19718         
19719         if (Roo.isIE) {
19720             var parent = range.parentElement();
19721             while (true) {
19722                 var testRange = range.duplicate();
19723                 testRange.moveToElementText(parent);
19724                 if (testRange.inRange(range)) {
19725                     break;
19726                 }
19727                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19728                     break;
19729                 }
19730                 parent = parent.parentElement;
19731             }
19732             return parent;
19733         }
19734         
19735         // is ancestor a text element.
19736         var ac =  range.commonAncestorContainer;
19737         if (ac.nodeType == 3) {
19738             ac = ac.parentNode;
19739         }
19740         
19741         var ar = ac.childNodes;
19742          
19743         var nodes = [];
19744         var other_nodes = [];
19745         var has_other_nodes = false;
19746         for (var i=0;i<ar.length;i++) {
19747             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19748                 continue;
19749             }
19750             // fullly contained node.
19751             
19752             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19753                 nodes.push(ar[i]);
19754                 continue;
19755             }
19756             
19757             // probably selected..
19758             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19759                 other_nodes.push(ar[i]);
19760                 continue;
19761             }
19762             // outer..
19763             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19764                 continue;
19765             }
19766             
19767             
19768             has_other_nodes = true;
19769         }
19770         if (!nodes.length && other_nodes.length) {
19771             nodes= other_nodes;
19772         }
19773         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19774             return false;
19775         }
19776         
19777         return nodes[0];
19778     },
19779     createRange: function(sel)
19780     {
19781         // this has strange effects when using with 
19782         // top toolbar - not sure if it's a great idea.
19783         //this.editor.contentWindow.focus();
19784         if (typeof sel != "undefined") {
19785             try {
19786                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19787             } catch(e) {
19788                 return this.doc.createRange();
19789             }
19790         } else {
19791             return this.doc.createRange();
19792         }
19793     },
19794     getParentElement: function()
19795     {
19796         
19797         this.assignDocWin();
19798         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19799         
19800         var range = this.createRange(sel);
19801          
19802         try {
19803             var p = range.commonAncestorContainer;
19804             while (p.nodeType == 3) { // text node
19805                 p = p.parentNode;
19806             }
19807             return p;
19808         } catch (e) {
19809             return null;
19810         }
19811     
19812     },
19813     /***
19814      *
19815      * Range intersection.. the hard stuff...
19816      *  '-1' = before
19817      *  '0' = hits..
19818      *  '1' = after.
19819      *         [ -- selected range --- ]
19820      *   [fail]                        [fail]
19821      *
19822      *    basically..
19823      *      if end is before start or  hits it. fail.
19824      *      if start is after end or hits it fail.
19825      *
19826      *   if either hits (but other is outside. - then it's not 
19827      *   
19828      *    
19829      **/
19830     
19831     
19832     // @see http://www.thismuchiknow.co.uk/?p=64.
19833     rangeIntersectsNode : function(range, node)
19834     {
19835         var nodeRange = node.ownerDocument.createRange();
19836         try {
19837             nodeRange.selectNode(node);
19838         } catch (e) {
19839             nodeRange.selectNodeContents(node);
19840         }
19841     
19842         var rangeStartRange = range.cloneRange();
19843         rangeStartRange.collapse(true);
19844     
19845         var rangeEndRange = range.cloneRange();
19846         rangeEndRange.collapse(false);
19847     
19848         var nodeStartRange = nodeRange.cloneRange();
19849         nodeStartRange.collapse(true);
19850     
19851         var nodeEndRange = nodeRange.cloneRange();
19852         nodeEndRange.collapse(false);
19853     
19854         return rangeStartRange.compareBoundaryPoints(
19855                  Range.START_TO_START, nodeEndRange) == -1 &&
19856                rangeEndRange.compareBoundaryPoints(
19857                  Range.START_TO_START, nodeStartRange) == 1;
19858         
19859          
19860     },
19861     rangeCompareNode : function(range, node)
19862     {
19863         var nodeRange = node.ownerDocument.createRange();
19864         try {
19865             nodeRange.selectNode(node);
19866         } catch (e) {
19867             nodeRange.selectNodeContents(node);
19868         }
19869         
19870         
19871         range.collapse(true);
19872     
19873         nodeRange.collapse(true);
19874      
19875         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19876         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19877          
19878         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19879         
19880         var nodeIsBefore   =  ss == 1;
19881         var nodeIsAfter    = ee == -1;
19882         
19883         if (nodeIsBefore && nodeIsAfter)
19884             return 0; // outer
19885         if (!nodeIsBefore && nodeIsAfter)
19886             return 1; //right trailed.
19887         
19888         if (nodeIsBefore && !nodeIsAfter)
19889             return 2;  // left trailed.
19890         // fully contined.
19891         return 3;
19892     },
19893
19894     // private? - in a new class?
19895     cleanUpPaste :  function()
19896     {
19897         // cleans up the whole document..
19898         Roo.log('cleanuppaste');
19899         
19900         this.cleanUpChildren(this.doc.body);
19901         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19902         if (clean != this.doc.body.innerHTML) {
19903             this.doc.body.innerHTML = clean;
19904         }
19905         
19906     },
19907     
19908     cleanWordChars : function(input) {// change the chars to hex code
19909         var he = Roo.HtmlEditorCore;
19910         
19911         var output = input;
19912         Roo.each(he.swapCodes, function(sw) { 
19913             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19914             
19915             output = output.replace(swapper, sw[1]);
19916         });
19917         
19918         return output;
19919     },
19920     
19921     
19922     cleanUpChildren : function (n)
19923     {
19924         if (!n.childNodes.length) {
19925             return;
19926         }
19927         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19928            this.cleanUpChild(n.childNodes[i]);
19929         }
19930     },
19931     
19932     
19933         
19934     
19935     cleanUpChild : function (node)
19936     {
19937         var ed = this;
19938         //console.log(node);
19939         if (node.nodeName == "#text") {
19940             // clean up silly Windows -- stuff?
19941             return; 
19942         }
19943         if (node.nodeName == "#comment") {
19944             node.parentNode.removeChild(node);
19945             // clean up silly Windows -- stuff?
19946             return; 
19947         }
19948         var lcname = node.tagName.toLowerCase();
19949         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19950         // whitelist of tags..
19951         
19952         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19953             // remove node.
19954             node.parentNode.removeChild(node);
19955             return;
19956             
19957         }
19958         
19959         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19960         
19961         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19962         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19963         
19964         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19965         //    remove_keep_children = true;
19966         //}
19967         
19968         if (remove_keep_children) {
19969             this.cleanUpChildren(node);
19970             // inserts everything just before this node...
19971             while (node.childNodes.length) {
19972                 var cn = node.childNodes[0];
19973                 node.removeChild(cn);
19974                 node.parentNode.insertBefore(cn, node);
19975             }
19976             node.parentNode.removeChild(node);
19977             return;
19978         }
19979         
19980         if (!node.attributes || !node.attributes.length) {
19981             this.cleanUpChildren(node);
19982             return;
19983         }
19984         
19985         function cleanAttr(n,v)
19986         {
19987             
19988             if (v.match(/^\./) || v.match(/^\//)) {
19989                 return;
19990             }
19991             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19992                 return;
19993             }
19994             if (v.match(/^#/)) {
19995                 return;
19996             }
19997 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19998             node.removeAttribute(n);
19999             
20000         }
20001         
20002         var cwhite = this.cwhite;
20003         var cblack = this.cblack;
20004             
20005         function cleanStyle(n,v)
20006         {
20007             if (v.match(/expression/)) { //XSS?? should we even bother..
20008                 node.removeAttribute(n);
20009                 return;
20010             }
20011             
20012             var parts = v.split(/;/);
20013             var clean = [];
20014             
20015             Roo.each(parts, function(p) {
20016                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20017                 if (!p.length) {
20018                     return true;
20019                 }
20020                 var l = p.split(':').shift().replace(/\s+/g,'');
20021                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20022                 
20023                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20024 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20025                     //node.removeAttribute(n);
20026                     return true;
20027                 }
20028                 //Roo.log()
20029                 // only allow 'c whitelisted system attributes'
20030                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20031 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20032                     //node.removeAttribute(n);
20033                     return true;
20034                 }
20035                 
20036                 
20037                  
20038                 
20039                 clean.push(p);
20040                 return true;
20041             });
20042             if (clean.length) { 
20043                 node.setAttribute(n, clean.join(';'));
20044             } else {
20045                 node.removeAttribute(n);
20046             }
20047             
20048         }
20049         
20050         
20051         for (var i = node.attributes.length-1; i > -1 ; i--) {
20052             var a = node.attributes[i];
20053             //console.log(a);
20054             
20055             if (a.name.toLowerCase().substr(0,2)=='on')  {
20056                 node.removeAttribute(a.name);
20057                 continue;
20058             }
20059             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20060                 node.removeAttribute(a.name);
20061                 continue;
20062             }
20063             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20064                 cleanAttr(a.name,a.value); // fixme..
20065                 continue;
20066             }
20067             if (a.name == 'style') {
20068                 cleanStyle(a.name,a.value);
20069                 continue;
20070             }
20071             /// clean up MS crap..
20072             // tecnically this should be a list of valid class'es..
20073             
20074             
20075             if (a.name == 'class') {
20076                 if (a.value.match(/^Mso/)) {
20077                     node.className = '';
20078                 }
20079                 
20080                 if (a.value.match(/body/)) {
20081                     node.className = '';
20082                 }
20083                 continue;
20084             }
20085             
20086             // style cleanup!?
20087             // class cleanup?
20088             
20089         }
20090         
20091         
20092         this.cleanUpChildren(node);
20093         
20094         
20095     },
20096     
20097     /**
20098      * Clean up MS wordisms...
20099      */
20100     cleanWord : function(node)
20101     {
20102         
20103         
20104         if (!node) {
20105             this.cleanWord(this.doc.body);
20106             return;
20107         }
20108         if (node.nodeName == "#text") {
20109             // clean up silly Windows -- stuff?
20110             return; 
20111         }
20112         if (node.nodeName == "#comment") {
20113             node.parentNode.removeChild(node);
20114             // clean up silly Windows -- stuff?
20115             return; 
20116         }
20117         
20118         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20119             node.parentNode.removeChild(node);
20120             return;
20121         }
20122         
20123         // remove - but keep children..
20124         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20125             while (node.childNodes.length) {
20126                 var cn = node.childNodes[0];
20127                 node.removeChild(cn);
20128                 node.parentNode.insertBefore(cn, node);
20129             }
20130             node.parentNode.removeChild(node);
20131             this.iterateChildren(node, this.cleanWord);
20132             return;
20133         }
20134         // clean styles
20135         if (node.className.length) {
20136             
20137             var cn = node.className.split(/\W+/);
20138             var cna = [];
20139             Roo.each(cn, function(cls) {
20140                 if (cls.match(/Mso[a-zA-Z]+/)) {
20141                     return;
20142                 }
20143                 cna.push(cls);
20144             });
20145             node.className = cna.length ? cna.join(' ') : '';
20146             if (!cna.length) {
20147                 node.removeAttribute("class");
20148             }
20149         }
20150         
20151         if (node.hasAttribute("lang")) {
20152             node.removeAttribute("lang");
20153         }
20154         
20155         if (node.hasAttribute("style")) {
20156             
20157             var styles = node.getAttribute("style").split(";");
20158             var nstyle = [];
20159             Roo.each(styles, function(s) {
20160                 if (!s.match(/:/)) {
20161                     return;
20162                 }
20163                 var kv = s.split(":");
20164                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20165                     return;
20166                 }
20167                 // what ever is left... we allow.
20168                 nstyle.push(s);
20169             });
20170             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20171             if (!nstyle.length) {
20172                 node.removeAttribute('style');
20173             }
20174         }
20175         this.iterateChildren(node, this.cleanWord);
20176         
20177         
20178         
20179     },
20180     /**
20181      * iterateChildren of a Node, calling fn each time, using this as the scole..
20182      * @param {DomNode} node node to iterate children of.
20183      * @param {Function} fn method of this class to call on each item.
20184      */
20185     iterateChildren : function(node, fn)
20186     {
20187         if (!node.childNodes.length) {
20188                 return;
20189         }
20190         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20191            fn.call(this, node.childNodes[i])
20192         }
20193     },
20194     
20195     
20196     /**
20197      * cleanTableWidths.
20198      *
20199      * Quite often pasting from word etc.. results in tables with column and widths.
20200      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20201      *
20202      */
20203     cleanTableWidths : function(node)
20204     {
20205          
20206          
20207         if (!node) {
20208             this.cleanTableWidths(this.doc.body);
20209             return;
20210         }
20211         
20212         // ignore list...
20213         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20214             return; 
20215         }
20216         Roo.log(node.tagName);
20217         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20218             this.iterateChildren(node, this.cleanTableWidths);
20219             return;
20220         }
20221         if (node.hasAttribute('width')) {
20222             node.removeAttribute('width');
20223         }
20224         
20225          
20226         if (node.hasAttribute("style")) {
20227             // pretty basic...
20228             
20229             var styles = node.getAttribute("style").split(";");
20230             var nstyle = [];
20231             Roo.each(styles, function(s) {
20232                 if (!s.match(/:/)) {
20233                     return;
20234                 }
20235                 var kv = s.split(":");
20236                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20237                     return;
20238                 }
20239                 // what ever is left... we allow.
20240                 nstyle.push(s);
20241             });
20242             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20243             if (!nstyle.length) {
20244                 node.removeAttribute('style');
20245             }
20246         }
20247         
20248         this.iterateChildren(node, this.cleanTableWidths);
20249         
20250         
20251     },
20252     
20253     
20254     
20255     
20256     domToHTML : function(currentElement, depth, nopadtext) {
20257         
20258         depth = depth || 0;
20259         nopadtext = nopadtext || false;
20260     
20261         if (!currentElement) {
20262             return this.domToHTML(this.doc.body);
20263         }
20264         
20265         //Roo.log(currentElement);
20266         var j;
20267         var allText = false;
20268         var nodeName = currentElement.nodeName;
20269         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20270         
20271         if  (nodeName == '#text') {
20272             
20273             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20274         }
20275         
20276         
20277         var ret = '';
20278         if (nodeName != 'BODY') {
20279              
20280             var i = 0;
20281             // Prints the node tagName, such as <A>, <IMG>, etc
20282             if (tagName) {
20283                 var attr = [];
20284                 for(i = 0; i < currentElement.attributes.length;i++) {
20285                     // quoting?
20286                     var aname = currentElement.attributes.item(i).name;
20287                     if (!currentElement.attributes.item(i).value.length) {
20288                         continue;
20289                     }
20290                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20291                 }
20292                 
20293                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20294             } 
20295             else {
20296                 
20297                 // eack
20298             }
20299         } else {
20300             tagName = false;
20301         }
20302         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20303             return ret;
20304         }
20305         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20306             nopadtext = true;
20307         }
20308         
20309         
20310         // Traverse the tree
20311         i = 0;
20312         var currentElementChild = currentElement.childNodes.item(i);
20313         var allText = true;
20314         var innerHTML  = '';
20315         lastnode = '';
20316         while (currentElementChild) {
20317             // Formatting code (indent the tree so it looks nice on the screen)
20318             var nopad = nopadtext;
20319             if (lastnode == 'SPAN') {
20320                 nopad  = true;
20321             }
20322             // text
20323             if  (currentElementChild.nodeName == '#text') {
20324                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20325                 toadd = nopadtext ? toadd : toadd.trim();
20326                 if (!nopad && toadd.length > 80) {
20327                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20328                 }
20329                 innerHTML  += toadd;
20330                 
20331                 i++;
20332                 currentElementChild = currentElement.childNodes.item(i);
20333                 lastNode = '';
20334                 continue;
20335             }
20336             allText = false;
20337             
20338             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20339                 
20340             // Recursively traverse the tree structure of the child node
20341             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20342             lastnode = currentElementChild.nodeName;
20343             i++;
20344             currentElementChild=currentElement.childNodes.item(i);
20345         }
20346         
20347         ret += innerHTML;
20348         
20349         if (!allText) {
20350                 // The remaining code is mostly for formatting the tree
20351             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20352         }
20353         
20354         
20355         if (tagName) {
20356             ret+= "</"+tagName+">";
20357         }
20358         return ret;
20359         
20360     },
20361         
20362     applyBlacklists : function()
20363     {
20364         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20365         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20366         
20367         this.white = [];
20368         this.black = [];
20369         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20370             if (b.indexOf(tag) > -1) {
20371                 return;
20372             }
20373             this.white.push(tag);
20374             
20375         }, this);
20376         
20377         Roo.each(w, function(tag) {
20378             if (b.indexOf(tag) > -1) {
20379                 return;
20380             }
20381             if (this.white.indexOf(tag) > -1) {
20382                 return;
20383             }
20384             this.white.push(tag);
20385             
20386         }, this);
20387         
20388         
20389         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20390             if (w.indexOf(tag) > -1) {
20391                 return;
20392             }
20393             this.black.push(tag);
20394             
20395         }, this);
20396         
20397         Roo.each(b, function(tag) {
20398             if (w.indexOf(tag) > -1) {
20399                 return;
20400             }
20401             if (this.black.indexOf(tag) > -1) {
20402                 return;
20403             }
20404             this.black.push(tag);
20405             
20406         }, this);
20407         
20408         
20409         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20410         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20411         
20412         this.cwhite = [];
20413         this.cblack = [];
20414         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20415             if (b.indexOf(tag) > -1) {
20416                 return;
20417             }
20418             this.cwhite.push(tag);
20419             
20420         }, this);
20421         
20422         Roo.each(w, function(tag) {
20423             if (b.indexOf(tag) > -1) {
20424                 return;
20425             }
20426             if (this.cwhite.indexOf(tag) > -1) {
20427                 return;
20428             }
20429             this.cwhite.push(tag);
20430             
20431         }, this);
20432         
20433         
20434         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20435             if (w.indexOf(tag) > -1) {
20436                 return;
20437             }
20438             this.cblack.push(tag);
20439             
20440         }, this);
20441         
20442         Roo.each(b, function(tag) {
20443             if (w.indexOf(tag) > -1) {
20444                 return;
20445             }
20446             if (this.cblack.indexOf(tag) > -1) {
20447                 return;
20448             }
20449             this.cblack.push(tag);
20450             
20451         }, this);
20452     },
20453     
20454     setStylesheets : function(stylesheets)
20455     {
20456         if(typeof(stylesheets) == 'string'){
20457             Roo.get(this.iframe.contentDocument.head).createChild({
20458                 tag : 'link',
20459                 rel : 'stylesheet',
20460                 type : 'text/css',
20461                 href : stylesheets
20462             });
20463             
20464             return;
20465         }
20466         var _this = this;
20467      
20468         Roo.each(stylesheets, function(s) {
20469             if(!s.length){
20470                 return;
20471             }
20472             
20473             Roo.get(_this.iframe.contentDocument.head).createChild({
20474                 tag : 'link',
20475                 rel : 'stylesheet',
20476                 type : 'text/css',
20477                 href : s
20478             });
20479         });
20480
20481         
20482     },
20483     
20484     removeStylesheets : function()
20485     {
20486         var _this = this;
20487         
20488         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20489             s.remove();
20490         });
20491     }
20492     
20493     // hide stuff that is not compatible
20494     /**
20495      * @event blur
20496      * @hide
20497      */
20498     /**
20499      * @event change
20500      * @hide
20501      */
20502     /**
20503      * @event focus
20504      * @hide
20505      */
20506     /**
20507      * @event specialkey
20508      * @hide
20509      */
20510     /**
20511      * @cfg {String} fieldClass @hide
20512      */
20513     /**
20514      * @cfg {String} focusClass @hide
20515      */
20516     /**
20517      * @cfg {String} autoCreate @hide
20518      */
20519     /**
20520      * @cfg {String} inputType @hide
20521      */
20522     /**
20523      * @cfg {String} invalidClass @hide
20524      */
20525     /**
20526      * @cfg {String} invalidText @hide
20527      */
20528     /**
20529      * @cfg {String} msgFx @hide
20530      */
20531     /**
20532      * @cfg {String} validateOnBlur @hide
20533      */
20534 });
20535
20536 Roo.HtmlEditorCore.white = [
20537         'area', 'br', 'img', 'input', 'hr', 'wbr',
20538         
20539        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20540        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20541        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20542        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20543        'table',   'ul',         'xmp', 
20544        
20545        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20546       'thead',   'tr', 
20547      
20548       'dir', 'menu', 'ol', 'ul', 'dl',
20549        
20550       'embed',  'object'
20551 ];
20552
20553
20554 Roo.HtmlEditorCore.black = [
20555     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20556         'applet', // 
20557         'base',   'basefont', 'bgsound', 'blink',  'body', 
20558         'frame',  'frameset', 'head',    'html',   'ilayer', 
20559         'iframe', 'layer',  'link',     'meta',    'object',   
20560         'script', 'style' ,'title',  'xml' // clean later..
20561 ];
20562 Roo.HtmlEditorCore.clean = [
20563     'script', 'style', 'title', 'xml'
20564 ];
20565 Roo.HtmlEditorCore.remove = [
20566     'font'
20567 ];
20568 // attributes..
20569
20570 Roo.HtmlEditorCore.ablack = [
20571     'on'
20572 ];
20573     
20574 Roo.HtmlEditorCore.aclean = [ 
20575     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20576 ];
20577
20578 // protocols..
20579 Roo.HtmlEditorCore.pwhite= [
20580         'http',  'https',  'mailto'
20581 ];
20582
20583 // white listed style attributes.
20584 Roo.HtmlEditorCore.cwhite= [
20585       //  'text-align', /// default is to allow most things..
20586       
20587          
20588 //        'font-size'//??
20589 ];
20590
20591 // black listed style attributes.
20592 Roo.HtmlEditorCore.cblack= [
20593       //  'font-size' -- this can be set by the project 
20594 ];
20595
20596
20597 Roo.HtmlEditorCore.swapCodes   =[ 
20598     [    8211, "--" ], 
20599     [    8212, "--" ], 
20600     [    8216,  "'" ],  
20601     [    8217, "'" ],  
20602     [    8220, '"' ],  
20603     [    8221, '"' ],  
20604     [    8226, "*" ],  
20605     [    8230, "..." ]
20606 ]; 
20607
20608     /*
20609  * - LGPL
20610  *
20611  * HtmlEditor
20612  * 
20613  */
20614
20615 /**
20616  * @class Roo.bootstrap.HtmlEditor
20617  * @extends Roo.bootstrap.TextArea
20618  * Bootstrap HtmlEditor class
20619
20620  * @constructor
20621  * Create a new HtmlEditor
20622  * @param {Object} config The config object
20623  */
20624
20625 Roo.bootstrap.HtmlEditor = function(config){
20626     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20627     if (!this.toolbars) {
20628         this.toolbars = [];
20629     }
20630     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20631     this.addEvents({
20632             /**
20633              * @event initialize
20634              * Fires when the editor is fully initialized (including the iframe)
20635              * @param {HtmlEditor} this
20636              */
20637             initialize: true,
20638             /**
20639              * @event activate
20640              * Fires when the editor is first receives the focus. Any insertion must wait
20641              * until after this event.
20642              * @param {HtmlEditor} this
20643              */
20644             activate: true,
20645              /**
20646              * @event beforesync
20647              * Fires before the textarea is updated with content from the editor iframe. Return false
20648              * to cancel the sync.
20649              * @param {HtmlEditor} this
20650              * @param {String} html
20651              */
20652             beforesync: true,
20653              /**
20654              * @event beforepush
20655              * Fires before the iframe editor is updated with content from the textarea. Return false
20656              * to cancel the push.
20657              * @param {HtmlEditor} this
20658              * @param {String} html
20659              */
20660             beforepush: true,
20661              /**
20662              * @event sync
20663              * Fires when the textarea is updated with content from the editor iframe.
20664              * @param {HtmlEditor} this
20665              * @param {String} html
20666              */
20667             sync: true,
20668              /**
20669              * @event push
20670              * Fires when the iframe editor is updated with content from the textarea.
20671              * @param {HtmlEditor} this
20672              * @param {String} html
20673              */
20674             push: true,
20675              /**
20676              * @event editmodechange
20677              * Fires when the editor switches edit modes
20678              * @param {HtmlEditor} this
20679              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20680              */
20681             editmodechange: true,
20682             /**
20683              * @event editorevent
20684              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20685              * @param {HtmlEditor} this
20686              */
20687             editorevent: true,
20688             /**
20689              * @event firstfocus
20690              * Fires when on first focus - needed by toolbars..
20691              * @param {HtmlEditor} this
20692              */
20693             firstfocus: true,
20694             /**
20695              * @event autosave
20696              * Auto save the htmlEditor value as a file into Events
20697              * @param {HtmlEditor} this
20698              */
20699             autosave: true,
20700             /**
20701              * @event savedpreview
20702              * preview the saved version of htmlEditor
20703              * @param {HtmlEditor} this
20704              */
20705             savedpreview: true
20706         });
20707 };
20708
20709
20710 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20711     
20712     
20713       /**
20714      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20715      */
20716     toolbars : false,
20717    
20718      /**
20719      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20720      *                        Roo.resizable.
20721      */
20722     resizable : false,
20723      /**
20724      * @cfg {Number} height (in pixels)
20725      */   
20726     height: 300,
20727    /**
20728      * @cfg {Number} width (in pixels)
20729      */   
20730     width: false,
20731     
20732     /**
20733      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20734      * 
20735      */
20736     stylesheets: false,
20737     
20738     // id of frame..
20739     frameId: false,
20740     
20741     // private properties
20742     validationEvent : false,
20743     deferHeight: true,
20744     initialized : false,
20745     activated : false,
20746     
20747     onFocus : Roo.emptyFn,
20748     iframePad:3,
20749     hideMode:'offsets',
20750     
20751     
20752     tbContainer : false,
20753     
20754     toolbarContainer :function() {
20755         return this.wrap.select('.x-html-editor-tb',true).first();
20756     },
20757
20758     /**
20759      * Protected method that will not generally be called directly. It
20760      * is called when the editor creates its toolbar. Override this method if you need to
20761      * add custom toolbar buttons.
20762      * @param {HtmlEditor} editor
20763      */
20764     createToolbar : function(){
20765         
20766         Roo.log("create toolbars");
20767         
20768         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20769         this.toolbars[0].render(this.toolbarContainer());
20770         
20771         return;
20772         
20773 //        if (!editor.toolbars || !editor.toolbars.length) {
20774 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20775 //        }
20776 //        
20777 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20778 //            editor.toolbars[i] = Roo.factory(
20779 //                    typeof(editor.toolbars[i]) == 'string' ?
20780 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20781 //                Roo.bootstrap.HtmlEditor);
20782 //            editor.toolbars[i].init(editor);
20783 //        }
20784     },
20785
20786      
20787     // private
20788     onRender : function(ct, position)
20789     {
20790        // Roo.log("Call onRender: " + this.xtype);
20791         var _t = this;
20792         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20793       
20794         this.wrap = this.inputEl().wrap({
20795             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20796         });
20797         
20798         this.editorcore.onRender(ct, position);
20799          
20800         if (this.resizable) {
20801             this.resizeEl = new Roo.Resizable(this.wrap, {
20802                 pinned : true,
20803                 wrap: true,
20804                 dynamic : true,
20805                 minHeight : this.height,
20806                 height: this.height,
20807                 handles : this.resizable,
20808                 width: this.width,
20809                 listeners : {
20810                     resize : function(r, w, h) {
20811                         _t.onResize(w,h); // -something
20812                     }
20813                 }
20814             });
20815             
20816         }
20817         this.createToolbar(this);
20818        
20819         
20820         if(!this.width && this.resizable){
20821             this.setSize(this.wrap.getSize());
20822         }
20823         if (this.resizeEl) {
20824             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20825             // should trigger onReize..
20826         }
20827         
20828     },
20829
20830     // private
20831     onResize : function(w, h)
20832     {
20833         Roo.log('resize: ' +w + ',' + h );
20834         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20835         var ew = false;
20836         var eh = false;
20837         
20838         if(this.inputEl() ){
20839             if(typeof w == 'number'){
20840                 var aw = w - this.wrap.getFrameWidth('lr');
20841                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20842                 ew = aw;
20843             }
20844             if(typeof h == 'number'){
20845                  var tbh = -11;  // fixme it needs to tool bar size!
20846                 for (var i =0; i < this.toolbars.length;i++) {
20847                     // fixme - ask toolbars for heights?
20848                     tbh += this.toolbars[i].el.getHeight();
20849                     //if (this.toolbars[i].footer) {
20850                     //    tbh += this.toolbars[i].footer.el.getHeight();
20851                     //}
20852                 }
20853               
20854                 
20855                 
20856                 
20857                 
20858                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20859                 ah -= 5; // knock a few pixes off for look..
20860                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20861                 var eh = ah;
20862             }
20863         }
20864         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20865         this.editorcore.onResize(ew,eh);
20866         
20867     },
20868
20869     /**
20870      * Toggles the editor between standard and source edit mode.
20871      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20872      */
20873     toggleSourceEdit : function(sourceEditMode)
20874     {
20875         this.editorcore.toggleSourceEdit(sourceEditMode);
20876         
20877         if(this.editorcore.sourceEditMode){
20878             Roo.log('editor - showing textarea');
20879             
20880 //            Roo.log('in');
20881 //            Roo.log(this.syncValue());
20882             this.syncValue();
20883             this.inputEl().removeClass(['hide', 'x-hidden']);
20884             this.inputEl().dom.removeAttribute('tabIndex');
20885             this.inputEl().focus();
20886         }else{
20887             Roo.log('editor - hiding textarea');
20888 //            Roo.log('out')
20889 //            Roo.log(this.pushValue()); 
20890             this.pushValue();
20891             
20892             this.inputEl().addClass(['hide', 'x-hidden']);
20893             this.inputEl().dom.setAttribute('tabIndex', -1);
20894             //this.deferFocus();
20895         }
20896          
20897         if(this.resizable){
20898             this.setSize(this.wrap.getSize());
20899         }
20900         
20901         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20902     },
20903  
20904     // private (for BoxComponent)
20905     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20906
20907     // private (for BoxComponent)
20908     getResizeEl : function(){
20909         return this.wrap;
20910     },
20911
20912     // private (for BoxComponent)
20913     getPositionEl : function(){
20914         return this.wrap;
20915     },
20916
20917     // private
20918     initEvents : function(){
20919         this.originalValue = this.getValue();
20920     },
20921
20922 //    /**
20923 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20924 //     * @method
20925 //     */
20926 //    markInvalid : Roo.emptyFn,
20927 //    /**
20928 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20929 //     * @method
20930 //     */
20931 //    clearInvalid : Roo.emptyFn,
20932
20933     setValue : function(v){
20934         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20935         this.editorcore.pushValue();
20936     },
20937
20938      
20939     // private
20940     deferFocus : function(){
20941         this.focus.defer(10, this);
20942     },
20943
20944     // doc'ed in Field
20945     focus : function(){
20946         this.editorcore.focus();
20947         
20948     },
20949       
20950
20951     // private
20952     onDestroy : function(){
20953         
20954         
20955         
20956         if(this.rendered){
20957             
20958             for (var i =0; i < this.toolbars.length;i++) {
20959                 // fixme - ask toolbars for heights?
20960                 this.toolbars[i].onDestroy();
20961             }
20962             
20963             this.wrap.dom.innerHTML = '';
20964             this.wrap.remove();
20965         }
20966     },
20967
20968     // private
20969     onFirstFocus : function(){
20970         //Roo.log("onFirstFocus");
20971         this.editorcore.onFirstFocus();
20972          for (var i =0; i < this.toolbars.length;i++) {
20973             this.toolbars[i].onFirstFocus();
20974         }
20975         
20976     },
20977     
20978     // private
20979     syncValue : function()
20980     {   
20981         this.editorcore.syncValue();
20982     },
20983     
20984     pushValue : function()
20985     {   
20986         this.editorcore.pushValue();
20987     }
20988      
20989     
20990     // hide stuff that is not compatible
20991     /**
20992      * @event blur
20993      * @hide
20994      */
20995     /**
20996      * @event change
20997      * @hide
20998      */
20999     /**
21000      * @event focus
21001      * @hide
21002      */
21003     /**
21004      * @event specialkey
21005      * @hide
21006      */
21007     /**
21008      * @cfg {String} fieldClass @hide
21009      */
21010     /**
21011      * @cfg {String} focusClass @hide
21012      */
21013     /**
21014      * @cfg {String} autoCreate @hide
21015      */
21016     /**
21017      * @cfg {String} inputType @hide
21018      */
21019     /**
21020      * @cfg {String} invalidClass @hide
21021      */
21022     /**
21023      * @cfg {String} invalidText @hide
21024      */
21025     /**
21026      * @cfg {String} msgFx @hide
21027      */
21028     /**
21029      * @cfg {String} validateOnBlur @hide
21030      */
21031 });
21032  
21033     
21034    
21035    
21036    
21037       
21038 Roo.namespace('Roo.bootstrap.htmleditor');
21039 /**
21040  * @class Roo.bootstrap.HtmlEditorToolbar1
21041  * Basic Toolbar
21042  * 
21043  * Usage:
21044  *
21045  new Roo.bootstrap.HtmlEditor({
21046     ....
21047     toolbars : [
21048         new Roo.bootstrap.HtmlEditorToolbar1({
21049             disable : { fonts: 1 , format: 1, ..., ... , ...],
21050             btns : [ .... ]
21051         })
21052     }
21053      
21054  * 
21055  * @cfg {Object} disable List of elements to disable..
21056  * @cfg {Array} btns List of additional buttons.
21057  * 
21058  * 
21059  * NEEDS Extra CSS? 
21060  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21061  */
21062  
21063 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21064 {
21065     
21066     Roo.apply(this, config);
21067     
21068     // default disabled, based on 'good practice'..
21069     this.disable = this.disable || {};
21070     Roo.applyIf(this.disable, {
21071         fontSize : true,
21072         colors : true,
21073         specialElements : true
21074     });
21075     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21076     
21077     this.editor = config.editor;
21078     this.editorcore = config.editor.editorcore;
21079     
21080     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21081     
21082     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21083     // dont call parent... till later.
21084 }
21085 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21086      
21087     bar : true,
21088     
21089     editor : false,
21090     editorcore : false,
21091     
21092     
21093     formats : [
21094         "p" ,  
21095         "h1","h2","h3","h4","h5","h6", 
21096         "pre", "code", 
21097         "abbr", "acronym", "address", "cite", "samp", "var",
21098         'div','span'
21099     ],
21100     
21101     onRender : function(ct, position)
21102     {
21103        // Roo.log("Call onRender: " + this.xtype);
21104         
21105        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21106        Roo.log(this.el);
21107        this.el.dom.style.marginBottom = '0';
21108        var _this = this;
21109        var editorcore = this.editorcore;
21110        var editor= this.editor;
21111        
21112        var children = [];
21113        var btn = function(id,cmd , toggle, handler){
21114        
21115             var  event = toggle ? 'toggle' : 'click';
21116        
21117             var a = {
21118                 size : 'sm',
21119                 xtype: 'Button',
21120                 xns: Roo.bootstrap,
21121                 glyphicon : id,
21122                 cmd : id || cmd,
21123                 enableToggle:toggle !== false,
21124                 //html : 'submit'
21125                 pressed : toggle ? false : null,
21126                 listeners : {}
21127             };
21128             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21129                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21130             };
21131             children.push(a);
21132             return a;
21133        }
21134         
21135         var style = {
21136                 xtype: 'Button',
21137                 size : 'sm',
21138                 xns: Roo.bootstrap,
21139                 glyphicon : 'font',
21140                 //html : 'submit'
21141                 menu : {
21142                     xtype: 'Menu',
21143                     xns: Roo.bootstrap,
21144                     items:  []
21145                 }
21146         };
21147         Roo.each(this.formats, function(f) {
21148             style.menu.items.push({
21149                 xtype :'MenuItem',
21150                 xns: Roo.bootstrap,
21151                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21152                 tagname : f,
21153                 listeners : {
21154                     click : function()
21155                     {
21156                         editorcore.insertTag(this.tagname);
21157                         editor.focus();
21158                     }
21159                 }
21160                 
21161             });
21162         });
21163          children.push(style);   
21164             
21165             
21166         btn('bold',false,true);
21167         btn('italic',false,true);
21168         btn('align-left', 'justifyleft',true);
21169         btn('align-center', 'justifycenter',true);
21170         btn('align-right' , 'justifyright',true);
21171         btn('link', false, false, function(btn) {
21172             //Roo.log("create link?");
21173             var url = prompt(this.createLinkText, this.defaultLinkValue);
21174             if(url && url != 'http:/'+'/'){
21175                 this.editorcore.relayCmd('createlink', url);
21176             }
21177         }),
21178         btn('list','insertunorderedlist',true);
21179         btn('pencil', false,true, function(btn){
21180                 Roo.log(this);
21181                 
21182                 this.toggleSourceEdit(btn.pressed);
21183         });
21184         /*
21185         var cog = {
21186                 xtype: 'Button',
21187                 size : 'sm',
21188                 xns: Roo.bootstrap,
21189                 glyphicon : 'cog',
21190                 //html : 'submit'
21191                 menu : {
21192                     xtype: 'Menu',
21193                     xns: Roo.bootstrap,
21194                     items:  []
21195                 }
21196         };
21197         
21198         cog.menu.items.push({
21199             xtype :'MenuItem',
21200             xns: Roo.bootstrap,
21201             html : Clean styles,
21202             tagname : f,
21203             listeners : {
21204                 click : function()
21205                 {
21206                     editorcore.insertTag(this.tagname);
21207                     editor.focus();
21208                 }
21209             }
21210             
21211         });
21212        */
21213         
21214          
21215        this.xtype = 'NavSimplebar';
21216         
21217         for(var i=0;i< children.length;i++) {
21218             
21219             this.buttons.add(this.addxtypeChild(children[i]));
21220             
21221         }
21222         
21223         editor.on('editorevent', this.updateToolbar, this);
21224     },
21225     onBtnClick : function(id)
21226     {
21227        this.editorcore.relayCmd(id);
21228        this.editorcore.focus();
21229     },
21230     
21231     /**
21232      * Protected method that will not generally be called directly. It triggers
21233      * a toolbar update by reading the markup state of the current selection in the editor.
21234      */
21235     updateToolbar: function(){
21236
21237         if(!this.editorcore.activated){
21238             this.editor.onFirstFocus(); // is this neeed?
21239             return;
21240         }
21241
21242         var btns = this.buttons; 
21243         var doc = this.editorcore.doc;
21244         btns.get('bold').setActive(doc.queryCommandState('bold'));
21245         btns.get('italic').setActive(doc.queryCommandState('italic'));
21246         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21247         
21248         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21249         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21250         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21251         
21252         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21253         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21254          /*
21255         
21256         var ans = this.editorcore.getAllAncestors();
21257         if (this.formatCombo) {
21258             
21259             
21260             var store = this.formatCombo.store;
21261             this.formatCombo.setValue("");
21262             for (var i =0; i < ans.length;i++) {
21263                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21264                     // select it..
21265                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21266                     break;
21267                 }
21268             }
21269         }
21270         
21271         
21272         
21273         // hides menus... - so this cant be on a menu...
21274         Roo.bootstrap.MenuMgr.hideAll();
21275         */
21276         Roo.bootstrap.MenuMgr.hideAll();
21277         //this.editorsyncValue();
21278     },
21279     onFirstFocus: function() {
21280         this.buttons.each(function(item){
21281            item.enable();
21282         });
21283     },
21284     toggleSourceEdit : function(sourceEditMode){
21285         
21286           
21287         if(sourceEditMode){
21288             Roo.log("disabling buttons");
21289            this.buttons.each( function(item){
21290                 if(item.cmd != 'pencil'){
21291                     item.disable();
21292                 }
21293             });
21294           
21295         }else{
21296             Roo.log("enabling buttons");
21297             if(this.editorcore.initialized){
21298                 this.buttons.each( function(item){
21299                     item.enable();
21300                 });
21301             }
21302             
21303         }
21304         Roo.log("calling toggole on editor");
21305         // tell the editor that it's been pressed..
21306         this.editor.toggleSourceEdit(sourceEditMode);
21307        
21308     }
21309 });
21310
21311
21312
21313
21314
21315 /**
21316  * @class Roo.bootstrap.Table.AbstractSelectionModel
21317  * @extends Roo.util.Observable
21318  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21319  * implemented by descendant classes.  This class should not be directly instantiated.
21320  * @constructor
21321  */
21322 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21323     this.locked = false;
21324     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21325 };
21326
21327
21328 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21329     /** @ignore Called by the grid automatically. Do not call directly. */
21330     init : function(grid){
21331         this.grid = grid;
21332         this.initEvents();
21333     },
21334
21335     /**
21336      * Locks the selections.
21337      */
21338     lock : function(){
21339         this.locked = true;
21340     },
21341
21342     /**
21343      * Unlocks the selections.
21344      */
21345     unlock : function(){
21346         this.locked = false;
21347     },
21348
21349     /**
21350      * Returns true if the selections are locked.
21351      * @return {Boolean}
21352      */
21353     isLocked : function(){
21354         return this.locked;
21355     }
21356 });
21357 /**
21358  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21359  * @class Roo.bootstrap.Table.RowSelectionModel
21360  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21361  * It supports multiple selections and keyboard selection/navigation. 
21362  * @constructor
21363  * @param {Object} config
21364  */
21365
21366 Roo.bootstrap.Table.RowSelectionModel = function(config){
21367     Roo.apply(this, config);
21368     this.selections = new Roo.util.MixedCollection(false, function(o){
21369         return o.id;
21370     });
21371
21372     this.last = false;
21373     this.lastActive = false;
21374
21375     this.addEvents({
21376         /**
21377              * @event selectionchange
21378              * Fires when the selection changes
21379              * @param {SelectionModel} this
21380              */
21381             "selectionchange" : true,
21382         /**
21383              * @event afterselectionchange
21384              * Fires after the selection changes (eg. by key press or clicking)
21385              * @param {SelectionModel} this
21386              */
21387             "afterselectionchange" : true,
21388         /**
21389              * @event beforerowselect
21390              * Fires when a row is selected being selected, return false to cancel.
21391              * @param {SelectionModel} this
21392              * @param {Number} rowIndex The selected index
21393              * @param {Boolean} keepExisting False if other selections will be cleared
21394              */
21395             "beforerowselect" : true,
21396         /**
21397              * @event rowselect
21398              * Fires when a row is selected.
21399              * @param {SelectionModel} this
21400              * @param {Number} rowIndex The selected index
21401              * @param {Roo.data.Record} r The record
21402              */
21403             "rowselect" : true,
21404         /**
21405              * @event rowdeselect
21406              * Fires when a row is deselected.
21407              * @param {SelectionModel} this
21408              * @param {Number} rowIndex The selected index
21409              */
21410         "rowdeselect" : true
21411     });
21412     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21413     this.locked = false;
21414 };
21415
21416 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21417     /**
21418      * @cfg {Boolean} singleSelect
21419      * True to allow selection of only one row at a time (defaults to false)
21420      */
21421     singleSelect : false,
21422
21423     // private
21424     initEvents : function(){
21425
21426         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21427             this.grid.on("mousedown", this.handleMouseDown, this);
21428         }else{ // allow click to work like normal
21429             this.grid.on("rowclick", this.handleDragableRowClick, this);
21430         }
21431
21432         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21433             "up" : function(e){
21434                 if(!e.shiftKey){
21435                     this.selectPrevious(e.shiftKey);
21436                 }else if(this.last !== false && this.lastActive !== false){
21437                     var last = this.last;
21438                     this.selectRange(this.last,  this.lastActive-1);
21439                     this.grid.getView().focusRow(this.lastActive);
21440                     if(last !== false){
21441                         this.last = last;
21442                     }
21443                 }else{
21444                     this.selectFirstRow();
21445                 }
21446                 this.fireEvent("afterselectionchange", this);
21447             },
21448             "down" : function(e){
21449                 if(!e.shiftKey){
21450                     this.selectNext(e.shiftKey);
21451                 }else if(this.last !== false && this.lastActive !== false){
21452                     var last = this.last;
21453                     this.selectRange(this.last,  this.lastActive+1);
21454                     this.grid.getView().focusRow(this.lastActive);
21455                     if(last !== false){
21456                         this.last = last;
21457                     }
21458                 }else{
21459                     this.selectFirstRow();
21460                 }
21461                 this.fireEvent("afterselectionchange", this);
21462             },
21463             scope: this
21464         });
21465
21466         var view = this.grid.view;
21467         view.on("refresh", this.onRefresh, this);
21468         view.on("rowupdated", this.onRowUpdated, this);
21469         view.on("rowremoved", this.onRemove, this);
21470     },
21471
21472     // private
21473     onRefresh : function(){
21474         var ds = this.grid.dataSource, i, v = this.grid.view;
21475         var s = this.selections;
21476         s.each(function(r){
21477             if((i = ds.indexOfId(r.id)) != -1){
21478                 v.onRowSelect(i);
21479             }else{
21480                 s.remove(r);
21481             }
21482         });
21483     },
21484
21485     // private
21486     onRemove : function(v, index, r){
21487         this.selections.remove(r);
21488     },
21489
21490     // private
21491     onRowUpdated : function(v, index, r){
21492         if(this.isSelected(r)){
21493             v.onRowSelect(index);
21494         }
21495     },
21496
21497     /**
21498      * Select records.
21499      * @param {Array} records The records to select
21500      * @param {Boolean} keepExisting (optional) True to keep existing selections
21501      */
21502     selectRecords : function(records, keepExisting){
21503         if(!keepExisting){
21504             this.clearSelections();
21505         }
21506         var ds = this.grid.dataSource;
21507         for(var i = 0, len = records.length; i < len; i++){
21508             this.selectRow(ds.indexOf(records[i]), true);
21509         }
21510     },
21511
21512     /**
21513      * Gets the number of selected rows.
21514      * @return {Number}
21515      */
21516     getCount : function(){
21517         return this.selections.length;
21518     },
21519
21520     /**
21521      * Selects the first row in the grid.
21522      */
21523     selectFirstRow : function(){
21524         this.selectRow(0);
21525     },
21526
21527     /**
21528      * Select the last row.
21529      * @param {Boolean} keepExisting (optional) True to keep existing selections
21530      */
21531     selectLastRow : function(keepExisting){
21532         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21533     },
21534
21535     /**
21536      * Selects the row immediately following the last selected row.
21537      * @param {Boolean} keepExisting (optional) True to keep existing selections
21538      */
21539     selectNext : function(keepExisting){
21540         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21541             this.selectRow(this.last+1, keepExisting);
21542             this.grid.getView().focusRow(this.last);
21543         }
21544     },
21545
21546     /**
21547      * Selects the row that precedes the last selected row.
21548      * @param {Boolean} keepExisting (optional) True to keep existing selections
21549      */
21550     selectPrevious : function(keepExisting){
21551         if(this.last){
21552             this.selectRow(this.last-1, keepExisting);
21553             this.grid.getView().focusRow(this.last);
21554         }
21555     },
21556
21557     /**
21558      * Returns the selected records
21559      * @return {Array} Array of selected records
21560      */
21561     getSelections : function(){
21562         return [].concat(this.selections.items);
21563     },
21564
21565     /**
21566      * Returns the first selected record.
21567      * @return {Record}
21568      */
21569     getSelected : function(){
21570         return this.selections.itemAt(0);
21571     },
21572
21573
21574     /**
21575      * Clears all selections.
21576      */
21577     clearSelections : function(fast){
21578         if(this.locked) return;
21579         if(fast !== true){
21580             var ds = this.grid.dataSource;
21581             var s = this.selections;
21582             s.each(function(r){
21583                 this.deselectRow(ds.indexOfId(r.id));
21584             }, this);
21585             s.clear();
21586         }else{
21587             this.selections.clear();
21588         }
21589         this.last = false;
21590     },
21591
21592
21593     /**
21594      * Selects all rows.
21595      */
21596     selectAll : function(){
21597         if(this.locked) return;
21598         this.selections.clear();
21599         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21600             this.selectRow(i, true);
21601         }
21602     },
21603
21604     /**
21605      * Returns True if there is a selection.
21606      * @return {Boolean}
21607      */
21608     hasSelection : function(){
21609         return this.selections.length > 0;
21610     },
21611
21612     /**
21613      * Returns True if the specified row is selected.
21614      * @param {Number/Record} record The record or index of the record to check
21615      * @return {Boolean}
21616      */
21617     isSelected : function(index){
21618         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21619         return (r && this.selections.key(r.id) ? true : false);
21620     },
21621
21622     /**
21623      * Returns True if the specified record id is selected.
21624      * @param {String} id The id of record to check
21625      * @return {Boolean}
21626      */
21627     isIdSelected : function(id){
21628         return (this.selections.key(id) ? true : false);
21629     },
21630
21631     // private
21632     handleMouseDown : function(e, t){
21633         var view = this.grid.getView(), rowIndex;
21634         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21635             return;
21636         };
21637         if(e.shiftKey && this.last !== false){
21638             var last = this.last;
21639             this.selectRange(last, rowIndex, e.ctrlKey);
21640             this.last = last; // reset the last
21641             view.focusRow(rowIndex);
21642         }else{
21643             var isSelected = this.isSelected(rowIndex);
21644             if(e.button !== 0 && isSelected){
21645                 view.focusRow(rowIndex);
21646             }else if(e.ctrlKey && isSelected){
21647                 this.deselectRow(rowIndex);
21648             }else if(!isSelected){
21649                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21650                 view.focusRow(rowIndex);
21651             }
21652         }
21653         this.fireEvent("afterselectionchange", this);
21654     },
21655     // private
21656     handleDragableRowClick :  function(grid, rowIndex, e) 
21657     {
21658         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21659             this.selectRow(rowIndex, false);
21660             grid.view.focusRow(rowIndex);
21661              this.fireEvent("afterselectionchange", this);
21662         }
21663     },
21664     
21665     /**
21666      * Selects multiple rows.
21667      * @param {Array} rows Array of the indexes of the row to select
21668      * @param {Boolean} keepExisting (optional) True to keep existing selections
21669      */
21670     selectRows : function(rows, keepExisting){
21671         if(!keepExisting){
21672             this.clearSelections();
21673         }
21674         for(var i = 0, len = rows.length; i < len; i++){
21675             this.selectRow(rows[i], true);
21676         }
21677     },
21678
21679     /**
21680      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21681      * @param {Number} startRow The index of the first row in the range
21682      * @param {Number} endRow The index of the last row in the range
21683      * @param {Boolean} keepExisting (optional) True to retain existing selections
21684      */
21685     selectRange : function(startRow, endRow, keepExisting){
21686         if(this.locked) return;
21687         if(!keepExisting){
21688             this.clearSelections();
21689         }
21690         if(startRow <= endRow){
21691             for(var i = startRow; i <= endRow; i++){
21692                 this.selectRow(i, true);
21693             }
21694         }else{
21695             for(var i = startRow; i >= endRow; i--){
21696                 this.selectRow(i, true);
21697             }
21698         }
21699     },
21700
21701     /**
21702      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21703      * @param {Number} startRow The index of the first row in the range
21704      * @param {Number} endRow The index of the last row in the range
21705      */
21706     deselectRange : function(startRow, endRow, preventViewNotify){
21707         if(this.locked) return;
21708         for(var i = startRow; i <= endRow; i++){
21709             this.deselectRow(i, preventViewNotify);
21710         }
21711     },
21712
21713     /**
21714      * Selects a row.
21715      * @param {Number} row The index of the row to select
21716      * @param {Boolean} keepExisting (optional) True to keep existing selections
21717      */
21718     selectRow : function(index, keepExisting, preventViewNotify){
21719         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21720         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21721             if(!keepExisting || this.singleSelect){
21722                 this.clearSelections();
21723             }
21724             var r = this.grid.dataSource.getAt(index);
21725             this.selections.add(r);
21726             this.last = this.lastActive = index;
21727             if(!preventViewNotify){
21728                 this.grid.getView().onRowSelect(index);
21729             }
21730             this.fireEvent("rowselect", this, index, r);
21731             this.fireEvent("selectionchange", this);
21732         }
21733     },
21734
21735     /**
21736      * Deselects a row.
21737      * @param {Number} row The index of the row to deselect
21738      */
21739     deselectRow : function(index, preventViewNotify){
21740         if(this.locked) return;
21741         if(this.last == index){
21742             this.last = false;
21743         }
21744         if(this.lastActive == index){
21745             this.lastActive = false;
21746         }
21747         var r = this.grid.dataSource.getAt(index);
21748         this.selections.remove(r);
21749         if(!preventViewNotify){
21750             this.grid.getView().onRowDeselect(index);
21751         }
21752         this.fireEvent("rowdeselect", this, index);
21753         this.fireEvent("selectionchange", this);
21754     },
21755
21756     // private
21757     restoreLast : function(){
21758         if(this._last){
21759             this.last = this._last;
21760         }
21761     },
21762
21763     // private
21764     acceptsNav : function(row, col, cm){
21765         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21766     },
21767
21768     // private
21769     onEditorKey : function(field, e){
21770         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21771         if(k == e.TAB){
21772             e.stopEvent();
21773             ed.completeEdit();
21774             if(e.shiftKey){
21775                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21776             }else{
21777                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21778             }
21779         }else if(k == e.ENTER && !e.ctrlKey){
21780             e.stopEvent();
21781             ed.completeEdit();
21782             if(e.shiftKey){
21783                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21784             }else{
21785                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21786             }
21787         }else if(k == e.ESC){
21788             ed.cancelEdit();
21789         }
21790         if(newCell){
21791             g.startEditing(newCell[0], newCell[1]);
21792         }
21793     }
21794 });/*
21795  * Based on:
21796  * Ext JS Library 1.1.1
21797  * Copyright(c) 2006-2007, Ext JS, LLC.
21798  *
21799  * Originally Released Under LGPL - original licence link has changed is not relivant.
21800  *
21801  * Fork - LGPL
21802  * <script type="text/javascript">
21803  */
21804  
21805 /**
21806  * @class Roo.bootstrap.PagingToolbar
21807  * @extends Roo.bootstrap.NavSimplebar
21808  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21809  * @constructor
21810  * Create a new PagingToolbar
21811  * @param {Object} config The config object
21812  * @param {Roo.data.Store} store
21813  */
21814 Roo.bootstrap.PagingToolbar = function(config)
21815 {
21816     // old args format still supported... - xtype is prefered..
21817         // created from xtype...
21818     
21819     this.ds = config.dataSource;
21820     
21821     if (config.store && !this.ds) {
21822         this.store= Roo.factory(config.store, Roo.data);
21823         this.ds = this.store;
21824         this.ds.xmodule = this.xmodule || false;
21825     }
21826     
21827     this.toolbarItems = [];
21828     if (config.items) {
21829         this.toolbarItems = config.items;
21830     }
21831     
21832     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21833     
21834     this.cursor = 0;
21835     
21836     if (this.ds) { 
21837         this.bind(this.ds);
21838     }
21839     
21840     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21841     
21842 };
21843
21844 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21845     /**
21846      * @cfg {Roo.data.Store} dataSource
21847      * The underlying data store providing the paged data
21848      */
21849     /**
21850      * @cfg {String/HTMLElement/Element} container
21851      * container The id or element that will contain the toolbar
21852      */
21853     /**
21854      * @cfg {Boolean} displayInfo
21855      * True to display the displayMsg (defaults to false)
21856      */
21857     /**
21858      * @cfg {Number} pageSize
21859      * The number of records to display per page (defaults to 20)
21860      */
21861     pageSize: 20,
21862     /**
21863      * @cfg {String} displayMsg
21864      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21865      */
21866     displayMsg : 'Displaying {0} - {1} of {2}',
21867     /**
21868      * @cfg {String} emptyMsg
21869      * The message to display when no records are found (defaults to "No data to display")
21870      */
21871     emptyMsg : 'No data to display',
21872     /**
21873      * Customizable piece of the default paging text (defaults to "Page")
21874      * @type String
21875      */
21876     beforePageText : "Page",
21877     /**
21878      * Customizable piece of the default paging text (defaults to "of %0")
21879      * @type String
21880      */
21881     afterPageText : "of {0}",
21882     /**
21883      * Customizable piece of the default paging text (defaults to "First Page")
21884      * @type String
21885      */
21886     firstText : "First Page",
21887     /**
21888      * Customizable piece of the default paging text (defaults to "Previous Page")
21889      * @type String
21890      */
21891     prevText : "Previous Page",
21892     /**
21893      * Customizable piece of the default paging text (defaults to "Next Page")
21894      * @type String
21895      */
21896     nextText : "Next Page",
21897     /**
21898      * Customizable piece of the default paging text (defaults to "Last Page")
21899      * @type String
21900      */
21901     lastText : "Last Page",
21902     /**
21903      * Customizable piece of the default paging text (defaults to "Refresh")
21904      * @type String
21905      */
21906     refreshText : "Refresh",
21907
21908     buttons : false,
21909     // private
21910     onRender : function(ct, position) 
21911     {
21912         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21913         this.navgroup.parentId = this.id;
21914         this.navgroup.onRender(this.el, null);
21915         // add the buttons to the navgroup
21916         
21917         if(this.displayInfo){
21918             Roo.log(this.el.select('ul.navbar-nav',true).first());
21919             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21920             this.displayEl = this.el.select('.x-paging-info', true).first();
21921 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21922 //            this.displayEl = navel.el.select('span',true).first();
21923         }
21924         
21925         var _this = this;
21926         
21927         if(this.buttons){
21928             Roo.each(_this.buttons, function(e){ // this might need to use render????
21929                Roo.factory(e).onRender(_this.el, null);
21930             });
21931         }
21932             
21933         Roo.each(_this.toolbarItems, function(e) {
21934             _this.navgroup.addItem(e);
21935         });
21936         
21937         
21938         this.first = this.navgroup.addItem({
21939             tooltip: this.firstText,
21940             cls: "prev",
21941             icon : 'fa fa-backward',
21942             disabled: true,
21943             preventDefault: true,
21944             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21945         });
21946         
21947         this.prev =  this.navgroup.addItem({
21948             tooltip: this.prevText,
21949             cls: "prev",
21950             icon : 'fa fa-step-backward',
21951             disabled: true,
21952             preventDefault: true,
21953             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21954         });
21955     //this.addSeparator();
21956         
21957         
21958         var field = this.navgroup.addItem( {
21959             tagtype : 'span',
21960             cls : 'x-paging-position',
21961             
21962             html : this.beforePageText  +
21963                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21964                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21965          } ); //?? escaped?
21966         
21967         this.field = field.el.select('input', true).first();
21968         this.field.on("keydown", this.onPagingKeydown, this);
21969         this.field.on("focus", function(){this.dom.select();});
21970     
21971     
21972         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21973         //this.field.setHeight(18);
21974         //this.addSeparator();
21975         this.next = this.navgroup.addItem({
21976             tooltip: this.nextText,
21977             cls: "next",
21978             html : ' <i class="fa fa-step-forward">',
21979             disabled: true,
21980             preventDefault: true,
21981             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21982         });
21983         this.last = this.navgroup.addItem({
21984             tooltip: this.lastText,
21985             icon : 'fa fa-forward',
21986             cls: "next",
21987             disabled: true,
21988             preventDefault: true,
21989             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21990         });
21991     //this.addSeparator();
21992         this.loading = this.navgroup.addItem({
21993             tooltip: this.refreshText,
21994             icon: 'fa fa-refresh',
21995             preventDefault: true,
21996             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21997         });
21998         
21999     },
22000
22001     // private
22002     updateInfo : function(){
22003         if(this.displayEl){
22004             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22005             var msg = count == 0 ?
22006                 this.emptyMsg :
22007                 String.format(
22008                     this.displayMsg,
22009                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22010                 );
22011             this.displayEl.update(msg);
22012         }
22013     },
22014
22015     // private
22016     onLoad : function(ds, r, o){
22017        this.cursor = o.params ? o.params.start : 0;
22018        var d = this.getPageData(),
22019             ap = d.activePage,
22020             ps = d.pages;
22021         
22022        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22023        this.field.dom.value = ap;
22024        this.first.setDisabled(ap == 1);
22025        this.prev.setDisabled(ap == 1);
22026        this.next.setDisabled(ap == ps);
22027        this.last.setDisabled(ap == ps);
22028        this.loading.enable();
22029        this.updateInfo();
22030     },
22031
22032     // private
22033     getPageData : function(){
22034         var total = this.ds.getTotalCount();
22035         return {
22036             total : total,
22037             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22038             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22039         };
22040     },
22041
22042     // private
22043     onLoadError : function(){
22044         this.loading.enable();
22045     },
22046
22047     // private
22048     onPagingKeydown : function(e){
22049         var k = e.getKey();
22050         var d = this.getPageData();
22051         if(k == e.RETURN){
22052             var v = this.field.dom.value, pageNum;
22053             if(!v || isNaN(pageNum = parseInt(v, 10))){
22054                 this.field.dom.value = d.activePage;
22055                 return;
22056             }
22057             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22058             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22059             e.stopEvent();
22060         }
22061         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))
22062         {
22063           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22064           this.field.dom.value = pageNum;
22065           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22066           e.stopEvent();
22067         }
22068         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22069         {
22070           var v = this.field.dom.value, pageNum; 
22071           var increment = (e.shiftKey) ? 10 : 1;
22072           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22073             increment *= -1;
22074           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22075             this.field.dom.value = d.activePage;
22076             return;
22077           }
22078           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22079           {
22080             this.field.dom.value = parseInt(v, 10) + increment;
22081             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22082             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22083           }
22084           e.stopEvent();
22085         }
22086     },
22087
22088     // private
22089     beforeLoad : function(){
22090         if(this.loading){
22091             this.loading.disable();
22092         }
22093     },
22094
22095     // private
22096     onClick : function(which){
22097         
22098         var ds = this.ds;
22099         if (!ds) {
22100             return;
22101         }
22102         
22103         switch(which){
22104             case "first":
22105                 ds.load({params:{start: 0, limit: this.pageSize}});
22106             break;
22107             case "prev":
22108                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22109             break;
22110             case "next":
22111                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22112             break;
22113             case "last":
22114                 var total = ds.getTotalCount();
22115                 var extra = total % this.pageSize;
22116                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22117                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22118             break;
22119             case "refresh":
22120                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22121             break;
22122         }
22123     },
22124
22125     /**
22126      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22127      * @param {Roo.data.Store} store The data store to unbind
22128      */
22129     unbind : function(ds){
22130         ds.un("beforeload", this.beforeLoad, this);
22131         ds.un("load", this.onLoad, this);
22132         ds.un("loadexception", this.onLoadError, this);
22133         ds.un("remove", this.updateInfo, this);
22134         ds.un("add", this.updateInfo, this);
22135         this.ds = undefined;
22136     },
22137
22138     /**
22139      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22140      * @param {Roo.data.Store} store The data store to bind
22141      */
22142     bind : function(ds){
22143         ds.on("beforeload", this.beforeLoad, this);
22144         ds.on("load", this.onLoad, this);
22145         ds.on("loadexception", this.onLoadError, this);
22146         ds.on("remove", this.updateInfo, this);
22147         ds.on("add", this.updateInfo, this);
22148         this.ds = ds;
22149     }
22150 });/*
22151  * - LGPL
22152  *
22153  * element
22154  * 
22155  */
22156
22157 /**
22158  * @class Roo.bootstrap.MessageBar
22159  * @extends Roo.bootstrap.Component
22160  * Bootstrap MessageBar class
22161  * @cfg {String} html contents of the MessageBar
22162  * @cfg {String} weight (info | success | warning | danger) default info
22163  * @cfg {String} beforeClass insert the bar before the given class
22164  * @cfg {Boolean} closable (true | false) default false
22165  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22166  * 
22167  * @constructor
22168  * Create a new Element
22169  * @param {Object} config The config object
22170  */
22171
22172 Roo.bootstrap.MessageBar = function(config){
22173     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22174 };
22175
22176 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22177     
22178     html: '',
22179     weight: 'info',
22180     closable: false,
22181     fixed: false,
22182     beforeClass: 'bootstrap-sticky-wrap',
22183     
22184     getAutoCreate : function(){
22185         
22186         var cfg = {
22187             tag: 'div',
22188             cls: 'alert alert-dismissable alert-' + this.weight,
22189             cn: [
22190                 {
22191                     tag: 'span',
22192                     cls: 'message',
22193                     html: this.html || ''
22194                 }
22195             ]
22196         }
22197         
22198         if(this.fixed){
22199             cfg.cls += ' alert-messages-fixed';
22200         }
22201         
22202         if(this.closable){
22203             cfg.cn.push({
22204                 tag: 'button',
22205                 cls: 'close',
22206                 html: 'x'
22207             });
22208         }
22209         
22210         return cfg;
22211     },
22212     
22213     onRender : function(ct, position)
22214     {
22215         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22216         
22217         if(!this.el){
22218             var cfg = Roo.apply({},  this.getAutoCreate());
22219             cfg.id = Roo.id();
22220             
22221             if (this.cls) {
22222                 cfg.cls += ' ' + this.cls;
22223             }
22224             if (this.style) {
22225                 cfg.style = this.style;
22226             }
22227             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22228             
22229             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22230         }
22231         
22232         this.el.select('>button.close').on('click', this.hide, this);
22233         
22234     },
22235     
22236     show : function()
22237     {
22238         if (!this.rendered) {
22239             this.render();
22240         }
22241         
22242         this.el.show();
22243         
22244         this.fireEvent('show', this);
22245         
22246     },
22247     
22248     hide : function()
22249     {
22250         if (!this.rendered) {
22251             this.render();
22252         }
22253         
22254         this.el.hide();
22255         
22256         this.fireEvent('hide', this);
22257     },
22258     
22259     update : function()
22260     {
22261 //        var e = this.el.dom.firstChild;
22262 //        
22263 //        if(this.closable){
22264 //            e = e.nextSibling;
22265 //        }
22266 //        
22267 //        e.data = this.html || '';
22268
22269         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22270     }
22271    
22272 });
22273
22274  
22275
22276      /*
22277  * - LGPL
22278  *
22279  * Graph
22280  * 
22281  */
22282
22283
22284 /**
22285  * @class Roo.bootstrap.Graph
22286  * @extends Roo.bootstrap.Component
22287  * Bootstrap Graph class
22288 > Prameters
22289  -sm {number} sm 4
22290  -md {number} md 5
22291  @cfg {String} graphtype  bar | vbar | pie
22292  @cfg {number} g_x coodinator | centre x (pie)
22293  @cfg {number} g_y coodinator | centre y (pie)
22294  @cfg {number} g_r radius (pie)
22295  @cfg {number} g_height height of the chart (respected by all elements in the set)
22296  @cfg {number} g_width width of the chart (respected by all elements in the set)
22297  @cfg {Object} title The title of the chart
22298     
22299  -{Array}  values
22300  -opts (object) options for the chart 
22301      o {
22302      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22303      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22304      o vgutter (number)
22305      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.
22306      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22307      o to
22308      o stretch (boolean)
22309      o }
22310  -opts (object) options for the pie
22311      o{
22312      o cut
22313      o startAngle (number)
22314      o endAngle (number)
22315      } 
22316  *
22317  * @constructor
22318  * Create a new Input
22319  * @param {Object} config The config object
22320  */
22321
22322 Roo.bootstrap.Graph = function(config){
22323     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22324     
22325     this.addEvents({
22326         // img events
22327         /**
22328          * @event click
22329          * The img click event for the img.
22330          * @param {Roo.EventObject} e
22331          */
22332         "click" : true
22333     });
22334 };
22335
22336 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22337     
22338     sm: 4,
22339     md: 5,
22340     graphtype: 'bar',
22341     g_height: 250,
22342     g_width: 400,
22343     g_x: 50,
22344     g_y: 50,
22345     g_r: 30,
22346     opts:{
22347         //g_colors: this.colors,
22348         g_type: 'soft',
22349         g_gutter: '20%'
22350
22351     },
22352     title : false,
22353
22354     getAutoCreate : function(){
22355         
22356         var cfg = {
22357             tag: 'div',
22358             html : null
22359         }
22360         
22361         
22362         return  cfg;
22363     },
22364
22365     onRender : function(ct,position){
22366         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22367         this.raphael = Raphael(this.el.dom);
22368         
22369                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22370                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22371                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22372                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22373                 /*
22374                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22375                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22376                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22377                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22378                 
22379                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22380                 r.barchart(330, 10, 300, 220, data1);
22381                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22382                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22383                 */
22384                 
22385                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22386                 // r.barchart(30, 30, 560, 250,  xdata, {
22387                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22388                 //     axis : "0 0 1 1",
22389                 //     axisxlabels :  xdata
22390                 //     //yvalues : cols,
22391                    
22392                 // });
22393 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22394 //        
22395 //        this.load(null,xdata,{
22396 //                axis : "0 0 1 1",
22397 //                axisxlabels :  xdata
22398 //                });
22399
22400     },
22401
22402     load : function(graphtype,xdata,opts){
22403         this.raphael.clear();
22404         if(!graphtype) {
22405             graphtype = this.graphtype;
22406         }
22407         if(!opts){
22408             opts = this.opts;
22409         }
22410         var r = this.raphael,
22411             fin = function () {
22412                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22413             },
22414             fout = function () {
22415                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22416             },
22417             pfin = function() {
22418                 this.sector.stop();
22419                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22420
22421                 if (this.label) {
22422                     this.label[0].stop();
22423                     this.label[0].attr({ r: 7.5 });
22424                     this.label[1].attr({ "font-weight": 800 });
22425                 }
22426             },
22427             pfout = function() {
22428                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22429
22430                 if (this.label) {
22431                     this.label[0].animate({ r: 5 }, 500, "bounce");
22432                     this.label[1].attr({ "font-weight": 400 });
22433                 }
22434             };
22435
22436         switch(graphtype){
22437             case 'bar':
22438                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22439                 break;
22440             case 'hbar':
22441                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22442                 break;
22443             case 'pie':
22444 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22445 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22446 //            
22447                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22448                 
22449                 break;
22450
22451         }
22452         
22453         if(this.title){
22454             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22455         }
22456         
22457     },
22458     
22459     setTitle: function(o)
22460     {
22461         this.title = o;
22462     },
22463     
22464     initEvents: function() {
22465         
22466         if(!this.href){
22467             this.el.on('click', this.onClick, this);
22468         }
22469     },
22470     
22471     onClick : function(e)
22472     {
22473         Roo.log('img onclick');
22474         this.fireEvent('click', this, e);
22475     }
22476    
22477 });
22478
22479  
22480 /*
22481  * - LGPL
22482  *
22483  * numberBox
22484  * 
22485  */
22486 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22487
22488 /**
22489  * @class Roo.bootstrap.dash.NumberBox
22490  * @extends Roo.bootstrap.Component
22491  * Bootstrap NumberBox class
22492  * @cfg {String} headline Box headline
22493  * @cfg {String} content Box content
22494  * @cfg {String} icon Box icon
22495  * @cfg {String} footer Footer text
22496  * @cfg {String} fhref Footer href
22497  * 
22498  * @constructor
22499  * Create a new NumberBox
22500  * @param {Object} config The config object
22501  */
22502
22503
22504 Roo.bootstrap.dash.NumberBox = function(config){
22505     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22506     
22507 };
22508
22509 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22510     
22511     headline : '',
22512     content : '',
22513     icon : '',
22514     footer : '',
22515     fhref : '',
22516     ficon : '',
22517     
22518     getAutoCreate : function(){
22519         
22520         var cfg = {
22521             tag : 'div',
22522             cls : 'small-box ',
22523             cn : [
22524                 {
22525                     tag : 'div',
22526                     cls : 'inner',
22527                     cn :[
22528                         {
22529                             tag : 'h3',
22530                             cls : 'roo-headline',
22531                             html : this.headline
22532                         },
22533                         {
22534                             tag : 'p',
22535                             cls : 'roo-content',
22536                             html : this.content
22537                         }
22538                     ]
22539                 }
22540             ]
22541         }
22542         
22543         if(this.icon){
22544             cfg.cn.push({
22545                 tag : 'div',
22546                 cls : 'icon',
22547                 cn :[
22548                     {
22549                         tag : 'i',
22550                         cls : 'ion ' + this.icon
22551                     }
22552                 ]
22553             });
22554         }
22555         
22556         if(this.footer){
22557             var footer = {
22558                 tag : 'a',
22559                 cls : 'small-box-footer',
22560                 href : this.fhref || '#',
22561                 html : this.footer
22562             };
22563             
22564             cfg.cn.push(footer);
22565             
22566         }
22567         
22568         return  cfg;
22569     },
22570
22571     onRender : function(ct,position){
22572         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22573
22574
22575        
22576                 
22577     },
22578
22579     setHeadline: function (value)
22580     {
22581         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22582     },
22583     
22584     setFooter: function (value, href)
22585     {
22586         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22587         
22588         if(href){
22589             this.el.select('a.small-box-footer',true).first().attr('href', href);
22590         }
22591         
22592     },
22593
22594     setContent: function (value)
22595     {
22596         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22597     },
22598
22599     initEvents: function() 
22600     {   
22601         
22602     }
22603     
22604 });
22605
22606  
22607 /*
22608  * - LGPL
22609  *
22610  * TabBox
22611  * 
22612  */
22613 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22614
22615 /**
22616  * @class Roo.bootstrap.dash.TabBox
22617  * @extends Roo.bootstrap.Component
22618  * Bootstrap TabBox class
22619  * @cfg {String} title Title of the TabBox
22620  * @cfg {String} icon Icon of the TabBox
22621  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22622  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22623  * 
22624  * @constructor
22625  * Create a new TabBox
22626  * @param {Object} config The config object
22627  */
22628
22629
22630 Roo.bootstrap.dash.TabBox = function(config){
22631     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22632     this.addEvents({
22633         // raw events
22634         /**
22635          * @event addpane
22636          * When a pane is added
22637          * @param {Roo.bootstrap.dash.TabPane} pane
22638          */
22639         "addpane" : true,
22640         /**
22641          * @event activatepane
22642          * When a pane is activated
22643          * @param {Roo.bootstrap.dash.TabPane} pane
22644          */
22645         "activatepane" : true
22646         
22647          
22648     });
22649     
22650     this.panes = [];
22651 };
22652
22653 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22654
22655     title : '',
22656     icon : false,
22657     showtabs : true,
22658     tabScrollable : false,
22659     
22660     getChildContainer : function()
22661     {
22662         return this.el.select('.tab-content', true).first();
22663     },
22664     
22665     getAutoCreate : function(){
22666         
22667         var header = {
22668             tag: 'li',
22669             cls: 'pull-left header',
22670             html: this.title,
22671             cn : []
22672         };
22673         
22674         if(this.icon){
22675             header.cn.push({
22676                 tag: 'i',
22677                 cls: 'fa ' + this.icon
22678             });
22679         }
22680         
22681         var h = {
22682             tag: 'ul',
22683             cls: 'nav nav-tabs pull-right',
22684             cn: [
22685                 header
22686             ]
22687         };
22688         
22689         if(this.tabScrollable){
22690             h = {
22691                 tag: 'div',
22692                 cls: 'tab-header',
22693                 cn: [
22694                     {
22695                         tag: 'ul',
22696                         cls: 'nav nav-tabs pull-right',
22697                         cn: [
22698                             header
22699                         ]
22700                     }
22701                 ]
22702             }
22703         }
22704         
22705         var cfg = {
22706             tag: 'div',
22707             cls: 'nav-tabs-custom',
22708             cn: [
22709                 h,
22710                 {
22711                     tag: 'div',
22712                     cls: 'tab-content no-padding',
22713                     cn: []
22714                 }
22715             ]
22716         }
22717
22718         return  cfg;
22719     },
22720     initEvents : function()
22721     {
22722         //Roo.log('add add pane handler');
22723         this.on('addpane', this.onAddPane, this);
22724     },
22725      /**
22726      * Updates the box title
22727      * @param {String} html to set the title to.
22728      */
22729     setTitle : function(value)
22730     {
22731         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22732     },
22733     onAddPane : function(pane)
22734     {
22735         this.panes.push(pane);
22736         //Roo.log('addpane');
22737         //Roo.log(pane);
22738         // tabs are rendere left to right..
22739         if(!this.showtabs){
22740             return;
22741         }
22742         
22743         var ctr = this.el.select('.nav-tabs', true).first();
22744          
22745          
22746         var existing = ctr.select('.nav-tab',true);
22747         var qty = existing.getCount();;
22748         
22749         
22750         var tab = ctr.createChild({
22751             tag : 'li',
22752             cls : 'nav-tab' + (qty ? '' : ' active'),
22753             cn : [
22754                 {
22755                     tag : 'a',
22756                     href:'#',
22757                     html : pane.title
22758                 }
22759             ]
22760         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22761         pane.tab = tab;
22762         
22763         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22764         if (!qty) {
22765             pane.el.addClass('active');
22766         }
22767         
22768                 
22769     },
22770     onTabClick : function(ev,un,ob,pane)
22771     {
22772         //Roo.log('tab - prev default');
22773         ev.preventDefault();
22774         
22775         
22776         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22777         pane.tab.addClass('active');
22778         //Roo.log(pane.title);
22779         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22780         // technically we should have a deactivate event.. but maybe add later.
22781         // and it should not de-activate the selected tab...
22782         this.fireEvent('activatepane', pane);
22783         pane.el.addClass('active');
22784         pane.fireEvent('activate');
22785         
22786         
22787     },
22788     
22789     getActivePane : function()
22790     {
22791         var r = false;
22792         Roo.each(this.panes, function(p) {
22793             if(p.el.hasClass('active')){
22794                 r = p;
22795                 return false;
22796             }
22797             
22798             return;
22799         });
22800         
22801         return r;
22802     }
22803     
22804     
22805 });
22806
22807  
22808 /*
22809  * - LGPL
22810  *
22811  * Tab pane
22812  * 
22813  */
22814 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22815 /**
22816  * @class Roo.bootstrap.TabPane
22817  * @extends Roo.bootstrap.Component
22818  * Bootstrap TabPane class
22819  * @cfg {Boolean} active (false | true) Default false
22820  * @cfg {String} title title of panel
22821
22822  * 
22823  * @constructor
22824  * Create a new TabPane
22825  * @param {Object} config The config object
22826  */
22827
22828 Roo.bootstrap.dash.TabPane = function(config){
22829     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22830     
22831     this.addEvents({
22832         // raw events
22833         /**
22834          * @event activate
22835          * When a pane is activated
22836          * @param {Roo.bootstrap.dash.TabPane} pane
22837          */
22838         "activate" : true
22839          
22840     });
22841 };
22842
22843 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22844     
22845     active : false,
22846     title : '',
22847     
22848     // the tabBox that this is attached to.
22849     tab : false,
22850      
22851     getAutoCreate : function() 
22852     {
22853         var cfg = {
22854             tag: 'div',
22855             cls: 'tab-pane'
22856         }
22857         
22858         if(this.active){
22859             cfg.cls += ' active';
22860         }
22861         
22862         return cfg;
22863     },
22864     initEvents  : function()
22865     {
22866         //Roo.log('trigger add pane handler');
22867         this.parent().fireEvent('addpane', this)
22868     },
22869     
22870      /**
22871      * Updates the tab title 
22872      * @param {String} html to set the title to.
22873      */
22874     setTitle: function(str)
22875     {
22876         if (!this.tab) {
22877             return;
22878         }
22879         this.title = str;
22880         this.tab.select('a', true).first().dom.innerHTML = str;
22881         
22882     }
22883     
22884     
22885     
22886 });
22887
22888  
22889
22890
22891  /*
22892  * - LGPL
22893  *
22894  * menu
22895  * 
22896  */
22897 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22898
22899 /**
22900  * @class Roo.bootstrap.menu.Menu
22901  * @extends Roo.bootstrap.Component
22902  * Bootstrap Menu class - container for Menu
22903  * @cfg {String} html Text of the menu
22904  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22905  * @cfg {String} icon Font awesome icon
22906  * @cfg {String} pos Menu align to (top | bottom) default bottom
22907  * 
22908  * 
22909  * @constructor
22910  * Create a new Menu
22911  * @param {Object} config The config object
22912  */
22913
22914
22915 Roo.bootstrap.menu.Menu = function(config){
22916     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22917     
22918     this.addEvents({
22919         /**
22920          * @event beforeshow
22921          * Fires before this menu is displayed
22922          * @param {Roo.bootstrap.menu.Menu} this
22923          */
22924         beforeshow : true,
22925         /**
22926          * @event beforehide
22927          * Fires before this menu is hidden
22928          * @param {Roo.bootstrap.menu.Menu} this
22929          */
22930         beforehide : true,
22931         /**
22932          * @event show
22933          * Fires after this menu is displayed
22934          * @param {Roo.bootstrap.menu.Menu} this
22935          */
22936         show : true,
22937         /**
22938          * @event hide
22939          * Fires after this menu is hidden
22940          * @param {Roo.bootstrap.menu.Menu} this
22941          */
22942         hide : true,
22943         /**
22944          * @event click
22945          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22946          * @param {Roo.bootstrap.menu.Menu} this
22947          * @param {Roo.EventObject} e
22948          */
22949         click : true
22950     });
22951     
22952 };
22953
22954 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22955     
22956     submenu : false,
22957     html : '',
22958     weight : 'default',
22959     icon : false,
22960     pos : 'bottom',
22961     
22962     
22963     getChildContainer : function() {
22964         if(this.isSubMenu){
22965             return this.el;
22966         }
22967         
22968         return this.el.select('ul.dropdown-menu', true).first();  
22969     },
22970     
22971     getAutoCreate : function()
22972     {
22973         var text = [
22974             {
22975                 tag : 'span',
22976                 cls : 'roo-menu-text',
22977                 html : this.html
22978             }
22979         ];
22980         
22981         if(this.icon){
22982             text.unshift({
22983                 tag : 'i',
22984                 cls : 'fa ' + this.icon
22985             })
22986         }
22987         
22988         
22989         var cfg = {
22990             tag : 'div',
22991             cls : 'btn-group',
22992             cn : [
22993                 {
22994                     tag : 'button',
22995                     cls : 'dropdown-button btn btn-' + this.weight,
22996                     cn : text
22997                 },
22998                 {
22999                     tag : 'button',
23000                     cls : 'dropdown-toggle btn btn-' + this.weight,
23001                     cn : [
23002                         {
23003                             tag : 'span',
23004                             cls : 'caret'
23005                         }
23006                     ]
23007                 },
23008                 {
23009                     tag : 'ul',
23010                     cls : 'dropdown-menu'
23011                 }
23012             ]
23013             
23014         };
23015         
23016         if(this.pos == 'top'){
23017             cfg.cls += ' dropup';
23018         }
23019         
23020         if(this.isSubMenu){
23021             cfg = {
23022                 tag : 'ul',
23023                 cls : 'dropdown-menu'
23024             }
23025         }
23026         
23027         return cfg;
23028     },
23029     
23030     onRender : function(ct, position)
23031     {
23032         this.isSubMenu = ct.hasClass('dropdown-submenu');
23033         
23034         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23035     },
23036     
23037     initEvents : function() 
23038     {
23039         if(this.isSubMenu){
23040             return;
23041         }
23042         
23043         this.hidden = true;
23044         
23045         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23046         this.triggerEl.on('click', this.onTriggerPress, this);
23047         
23048         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23049         this.buttonEl.on('click', this.onClick, this);
23050         
23051     },
23052     
23053     list : function()
23054     {
23055         if(this.isSubMenu){
23056             return this.el;
23057         }
23058         
23059         return this.el.select('ul.dropdown-menu', true).first();
23060     },
23061     
23062     onClick : function(e)
23063     {
23064         this.fireEvent("click", this, e);
23065     },
23066     
23067     onTriggerPress  : function(e)
23068     {   
23069         if (this.isVisible()) {
23070             this.hide();
23071         } else {
23072             this.show();
23073         }
23074     },
23075     
23076     isVisible : function(){
23077         return !this.hidden;
23078     },
23079     
23080     show : function()
23081     {
23082         this.fireEvent("beforeshow", this);
23083         
23084         this.hidden = false;
23085         this.el.addClass('open');
23086         
23087         Roo.get(document).on("mouseup", this.onMouseUp, this);
23088         
23089         this.fireEvent("show", this);
23090         
23091         
23092     },
23093     
23094     hide : function()
23095     {
23096         this.fireEvent("beforehide", this);
23097         
23098         this.hidden = true;
23099         this.el.removeClass('open');
23100         
23101         Roo.get(document).un("mouseup", this.onMouseUp);
23102         
23103         this.fireEvent("hide", this);
23104     },
23105     
23106     onMouseUp : function()
23107     {
23108         this.hide();
23109     }
23110     
23111 });
23112
23113  
23114  /*
23115  * - LGPL
23116  *
23117  * menu item
23118  * 
23119  */
23120 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23121
23122 /**
23123  * @class Roo.bootstrap.menu.Item
23124  * @extends Roo.bootstrap.Component
23125  * Bootstrap MenuItem class
23126  * @cfg {Boolean} submenu (true | false) default false
23127  * @cfg {String} html text of the item
23128  * @cfg {String} href the link
23129  * @cfg {Boolean} disable (true | false) default false
23130  * @cfg {Boolean} preventDefault (true | false) default true
23131  * @cfg {String} icon Font awesome icon
23132  * @cfg {String} pos Submenu align to (left | right) default right 
23133  * 
23134  * 
23135  * @constructor
23136  * Create a new Item
23137  * @param {Object} config The config object
23138  */
23139
23140
23141 Roo.bootstrap.menu.Item = function(config){
23142     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23143     this.addEvents({
23144         /**
23145          * @event mouseover
23146          * Fires when the mouse is hovering over this menu
23147          * @param {Roo.bootstrap.menu.Item} this
23148          * @param {Roo.EventObject} e
23149          */
23150         mouseover : true,
23151         /**
23152          * @event mouseout
23153          * Fires when the mouse exits this menu
23154          * @param {Roo.bootstrap.menu.Item} this
23155          * @param {Roo.EventObject} e
23156          */
23157         mouseout : true,
23158         // raw events
23159         /**
23160          * @event click
23161          * The raw click event for the entire grid.
23162          * @param {Roo.EventObject} e
23163          */
23164         click : true
23165     });
23166 };
23167
23168 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23169     
23170     submenu : false,
23171     href : '',
23172     html : '',
23173     preventDefault: true,
23174     disable : false,
23175     icon : false,
23176     pos : 'right',
23177     
23178     getAutoCreate : function()
23179     {
23180         var text = [
23181             {
23182                 tag : 'span',
23183                 cls : 'roo-menu-item-text',
23184                 html : this.html
23185             }
23186         ];
23187         
23188         if(this.icon){
23189             text.unshift({
23190                 tag : 'i',
23191                 cls : 'fa ' + this.icon
23192             })
23193         }
23194         
23195         var cfg = {
23196             tag : 'li',
23197             cn : [
23198                 {
23199                     tag : 'a',
23200                     href : this.href || '#',
23201                     cn : text
23202                 }
23203             ]
23204         };
23205         
23206         if(this.disable){
23207             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23208         }
23209         
23210         if(this.submenu){
23211             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23212             
23213             if(this.pos == 'left'){
23214                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23215             }
23216         }
23217         
23218         return cfg;
23219     },
23220     
23221     initEvents : function() 
23222     {
23223         this.el.on('mouseover', this.onMouseOver, this);
23224         this.el.on('mouseout', this.onMouseOut, this);
23225         
23226         this.el.select('a', true).first().on('click', this.onClick, this);
23227         
23228     },
23229     
23230     onClick : function(e)
23231     {
23232         if(this.preventDefault){
23233             e.preventDefault();
23234         }
23235         
23236         this.fireEvent("click", this, e);
23237     },
23238     
23239     onMouseOver : function(e)
23240     {
23241         if(this.submenu && this.pos == 'left'){
23242             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23243         }
23244         
23245         this.fireEvent("mouseover", this, e);
23246     },
23247     
23248     onMouseOut : function(e)
23249     {
23250         this.fireEvent("mouseout", this, e);
23251     }
23252 });
23253
23254  
23255
23256  /*
23257  * - LGPL
23258  *
23259  * menu separator
23260  * 
23261  */
23262 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23263
23264 /**
23265  * @class Roo.bootstrap.menu.Separator
23266  * @extends Roo.bootstrap.Component
23267  * Bootstrap Separator class
23268  * 
23269  * @constructor
23270  * Create a new Separator
23271  * @param {Object} config The config object
23272  */
23273
23274
23275 Roo.bootstrap.menu.Separator = function(config){
23276     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23277 };
23278
23279 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23280     
23281     getAutoCreate : function(){
23282         var cfg = {
23283             tag : 'li',
23284             cls: 'divider'
23285         };
23286         
23287         return cfg;
23288     }
23289    
23290 });
23291
23292  
23293
23294  /*
23295  * - LGPL
23296  *
23297  * Tooltip
23298  * 
23299  */
23300
23301 /**
23302  * @class Roo.bootstrap.Tooltip
23303  * Bootstrap Tooltip class
23304  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23305  * to determine which dom element triggers the tooltip.
23306  * 
23307  * It needs to add support for additional attributes like tooltip-position
23308  * 
23309  * @constructor
23310  * Create a new Toolti
23311  * @param {Object} config The config object
23312  */
23313
23314 Roo.bootstrap.Tooltip = function(config){
23315     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23316 };
23317
23318 Roo.apply(Roo.bootstrap.Tooltip, {
23319     /**
23320      * @function init initialize tooltip monitoring.
23321      * @static
23322      */
23323     currentEl : false,
23324     currentTip : false,
23325     currentRegion : false,
23326     
23327     //  init : delay?
23328     
23329     init : function()
23330     {
23331         Roo.get(document).on('mouseover', this.enter ,this);
23332         Roo.get(document).on('mouseout', this.leave, this);
23333          
23334         
23335         this.currentTip = new Roo.bootstrap.Tooltip();
23336     },
23337     
23338     enter : function(ev)
23339     {
23340         var dom = ev.getTarget();
23341         
23342         //Roo.log(['enter',dom]);
23343         var el = Roo.fly(dom);
23344         if (this.currentEl) {
23345             //Roo.log(dom);
23346             //Roo.log(this.currentEl);
23347             //Roo.log(this.currentEl.contains(dom));
23348             if (this.currentEl == el) {
23349                 return;
23350             }
23351             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23352                 return;
23353             }
23354
23355         }
23356         
23357         if (this.currentTip.el) {
23358             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23359         }    
23360         //Roo.log(ev);
23361         var bindEl = el;
23362         
23363         // you can not look for children, as if el is the body.. then everythign is the child..
23364         if (!el.attr('tooltip')) { //
23365             if (!el.select("[tooltip]").elements.length) {
23366                 return;
23367             }
23368             // is the mouse over this child...?
23369             bindEl = el.select("[tooltip]").first();
23370             var xy = ev.getXY();
23371             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23372                 //Roo.log("not in region.");
23373                 return;
23374             }
23375             //Roo.log("child element over..");
23376             
23377         }
23378         this.currentEl = bindEl;
23379         this.currentTip.bind(bindEl);
23380         this.currentRegion = Roo.lib.Region.getRegion(dom);
23381         this.currentTip.enter();
23382         
23383     },
23384     leave : function(ev)
23385     {
23386         var dom = ev.getTarget();
23387         //Roo.log(['leave',dom]);
23388         if (!this.currentEl) {
23389             return;
23390         }
23391         
23392         
23393         if (dom != this.currentEl.dom) {
23394             return;
23395         }
23396         var xy = ev.getXY();
23397         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23398             return;
23399         }
23400         // only activate leave if mouse cursor is outside... bounding box..
23401         
23402         
23403         
23404         
23405         if (this.currentTip) {
23406             this.currentTip.leave();
23407         }
23408         //Roo.log('clear currentEl');
23409         this.currentEl = false;
23410         
23411         
23412     },
23413     alignment : {
23414         'left' : ['r-l', [-2,0], 'right'],
23415         'right' : ['l-r', [2,0], 'left'],
23416         'bottom' : ['t-b', [0,2], 'top'],
23417         'top' : [ 'b-t', [0,-2], 'bottom']
23418     }
23419     
23420 });
23421
23422
23423 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23424     
23425     
23426     bindEl : false,
23427     
23428     delay : null, // can be { show : 300 , hide: 500}
23429     
23430     timeout : null,
23431     
23432     hoverState : null, //???
23433     
23434     placement : 'bottom', 
23435     
23436     getAutoCreate : function(){
23437     
23438         var cfg = {
23439            cls : 'tooltip',
23440            role : 'tooltip',
23441            cn : [
23442                 {
23443                     cls : 'tooltip-arrow'
23444                 },
23445                 {
23446                     cls : 'tooltip-inner'
23447                 }
23448            ]
23449         };
23450         
23451         return cfg;
23452     },
23453     bind : function(el)
23454     {
23455         this.bindEl = el;
23456     },
23457       
23458     
23459     enter : function () {
23460        
23461         if (this.timeout != null) {
23462             clearTimeout(this.timeout);
23463         }
23464         
23465         this.hoverState = 'in';
23466          //Roo.log("enter - show");
23467         if (!this.delay || !this.delay.show) {
23468             this.show();
23469             return;
23470         }
23471         var _t = this;
23472         this.timeout = setTimeout(function () {
23473             if (_t.hoverState == 'in') {
23474                 _t.show();
23475             }
23476         }, this.delay.show);
23477     },
23478     leave : function()
23479     {
23480         clearTimeout(this.timeout);
23481     
23482         this.hoverState = 'out';
23483          if (!this.delay || !this.delay.hide) {
23484             this.hide();
23485             return;
23486         }
23487        
23488         var _t = this;
23489         this.timeout = setTimeout(function () {
23490             //Roo.log("leave - timeout");
23491             
23492             if (_t.hoverState == 'out') {
23493                 _t.hide();
23494                 Roo.bootstrap.Tooltip.currentEl = false;
23495             }
23496         }, delay);
23497     },
23498     
23499     show : function ()
23500     {
23501         if (!this.el) {
23502             this.render(document.body);
23503         }
23504         // set content.
23505         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23506         
23507         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23508         
23509         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23510         
23511         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23512         
23513         var placement = typeof this.placement == 'function' ?
23514             this.placement.call(this, this.el, on_el) :
23515             this.placement;
23516             
23517         var autoToken = /\s?auto?\s?/i;
23518         var autoPlace = autoToken.test(placement);
23519         if (autoPlace) {
23520             placement = placement.replace(autoToken, '') || 'top';
23521         }
23522         
23523         //this.el.detach()
23524         //this.el.setXY([0,0]);
23525         this.el.show();
23526         //this.el.dom.style.display='block';
23527         
23528         //this.el.appendTo(on_el);
23529         
23530         var p = this.getPosition();
23531         var box = this.el.getBox();
23532         
23533         if (autoPlace) {
23534             // fixme..
23535         }
23536         
23537         var align = Roo.bootstrap.Tooltip.alignment[placement];
23538         
23539         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23540         
23541         if(placement == 'top' || placement == 'bottom'){
23542             if(xy[0] < 0){
23543                 placement = 'right';
23544             }
23545             
23546             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23547                 placement = 'left';
23548             }
23549         }
23550         
23551         align = Roo.bootstrap.Tooltip.alignment[placement];
23552         
23553         this.el.alignTo(this.bindEl, align[0],align[1]);
23554         //var arrow = this.el.select('.arrow',true).first();
23555         //arrow.set(align[2], 
23556         
23557         this.el.addClass(placement);
23558         
23559         this.el.addClass('in fade');
23560         
23561         this.hoverState = null;
23562         
23563         if (this.el.hasClass('fade')) {
23564             // fade it?
23565         }
23566         
23567     },
23568     hide : function()
23569     {
23570          
23571         if (!this.el) {
23572             return;
23573         }
23574         //this.el.setXY([0,0]);
23575         this.el.removeClass('in');
23576         //this.el.hide();
23577         
23578     }
23579     
23580 });
23581  
23582
23583  /*
23584  * - LGPL
23585  *
23586  * Location Picker
23587  * 
23588  */
23589
23590 /**
23591  * @class Roo.bootstrap.LocationPicker
23592  * @extends Roo.bootstrap.Component
23593  * Bootstrap LocationPicker class
23594  * @cfg {Number} latitude Position when init default 0
23595  * @cfg {Number} longitude Position when init default 0
23596  * @cfg {Number} zoom default 15
23597  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23598  * @cfg {Boolean} mapTypeControl default false
23599  * @cfg {Boolean} disableDoubleClickZoom default false
23600  * @cfg {Boolean} scrollwheel default true
23601  * @cfg {Boolean} streetViewControl default false
23602  * @cfg {Number} radius default 0
23603  * @cfg {String} locationName
23604  * @cfg {Boolean} draggable default true
23605  * @cfg {Boolean} enableAutocomplete default false
23606  * @cfg {Boolean} enableReverseGeocode default true
23607  * @cfg {String} markerTitle
23608  * 
23609  * @constructor
23610  * Create a new LocationPicker
23611  * @param {Object} config The config object
23612  */
23613
23614
23615 Roo.bootstrap.LocationPicker = function(config){
23616     
23617     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23618     
23619     this.addEvents({
23620         /**
23621          * @event initial
23622          * Fires when the picker initialized.
23623          * @param {Roo.bootstrap.LocationPicker} this
23624          * @param {Google Location} location
23625          */
23626         initial : true,
23627         /**
23628          * @event positionchanged
23629          * Fires when the picker position changed.
23630          * @param {Roo.bootstrap.LocationPicker} this
23631          * @param {Google Location} location
23632          */
23633         positionchanged : true,
23634         /**
23635          * @event resize
23636          * Fires when the map resize.
23637          * @param {Roo.bootstrap.LocationPicker} this
23638          */
23639         resize : true,
23640         /**
23641          * @event show
23642          * Fires when the map show.
23643          * @param {Roo.bootstrap.LocationPicker} this
23644          */
23645         show : true,
23646         /**
23647          * @event hide
23648          * Fires when the map hide.
23649          * @param {Roo.bootstrap.LocationPicker} this
23650          */
23651         hide : true,
23652         /**
23653          * @event mapClick
23654          * Fires when click the map.
23655          * @param {Roo.bootstrap.LocationPicker} this
23656          * @param {Map event} e
23657          */
23658         mapClick : true,
23659         /**
23660          * @event mapRightClick
23661          * Fires when right click the map.
23662          * @param {Roo.bootstrap.LocationPicker} this
23663          * @param {Map event} e
23664          */
23665         mapRightClick : true,
23666         /**
23667          * @event markerClick
23668          * Fires when click the marker.
23669          * @param {Roo.bootstrap.LocationPicker} this
23670          * @param {Map event} e
23671          */
23672         markerClick : true,
23673         /**
23674          * @event markerRightClick
23675          * Fires when right click the marker.
23676          * @param {Roo.bootstrap.LocationPicker} this
23677          * @param {Map event} e
23678          */
23679         markerRightClick : true,
23680         /**
23681          * @event OverlayViewDraw
23682          * Fires when OverlayView Draw
23683          * @param {Roo.bootstrap.LocationPicker} this
23684          */
23685         OverlayViewDraw : true,
23686         /**
23687          * @event OverlayViewOnAdd
23688          * Fires when OverlayView Draw
23689          * @param {Roo.bootstrap.LocationPicker} this
23690          */
23691         OverlayViewOnAdd : true,
23692         /**
23693          * @event OverlayViewOnRemove
23694          * Fires when OverlayView Draw
23695          * @param {Roo.bootstrap.LocationPicker} this
23696          */
23697         OverlayViewOnRemove : true,
23698         /**
23699          * @event OverlayViewShow
23700          * Fires when OverlayView Draw
23701          * @param {Roo.bootstrap.LocationPicker} this
23702          * @param {Pixel} cpx
23703          */
23704         OverlayViewShow : true,
23705         /**
23706          * @event OverlayViewHide
23707          * Fires when OverlayView Draw
23708          * @param {Roo.bootstrap.LocationPicker} this
23709          */
23710         OverlayViewHide : true,
23711         /**
23712          * @event loadexception
23713          * Fires when load google lib failed.
23714          * @param {Roo.bootstrap.LocationPicker} this
23715          */
23716         loadexception : true
23717     });
23718         
23719 };
23720
23721 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23722     
23723     gMapContext: false,
23724     
23725     latitude: 0,
23726     longitude: 0,
23727     zoom: 15,
23728     mapTypeId: false,
23729     mapTypeControl: false,
23730     disableDoubleClickZoom: false,
23731     scrollwheel: true,
23732     streetViewControl: false,
23733     radius: 0,
23734     locationName: '',
23735     draggable: true,
23736     enableAutocomplete: false,
23737     enableReverseGeocode: true,
23738     markerTitle: '',
23739     
23740     getAutoCreate: function()
23741     {
23742
23743         var cfg = {
23744             tag: 'div',
23745             cls: 'roo-location-picker'
23746         };
23747         
23748         return cfg
23749     },
23750     
23751     initEvents: function(ct, position)
23752     {       
23753         if(!this.el.getWidth() || this.isApplied()){
23754             return;
23755         }
23756         
23757         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23758         
23759         this.initial();
23760     },
23761     
23762     initial: function()
23763     {
23764         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
23765             this.fireEvent('loadexception', this);
23766             return;
23767         }
23768         
23769         if(!this.mapTypeId){
23770             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23771         }
23772         
23773         this.gMapContext = this.GMapContext();
23774         
23775         this.initOverlayView();
23776         
23777         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23778         
23779         var _this = this;
23780                 
23781         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23782             _this.setPosition(_this.gMapContext.marker.position);
23783         });
23784         
23785         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23786             _this.fireEvent('mapClick', this, event);
23787             
23788         });
23789
23790         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23791             _this.fireEvent('mapRightClick', this, event);
23792             
23793         });
23794         
23795         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23796             _this.fireEvent('markerClick', this, event);
23797             
23798         });
23799
23800         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23801             _this.fireEvent('markerRightClick', this, event);
23802             
23803         });
23804         
23805         this.setPosition(this.gMapContext.location);
23806         
23807         this.fireEvent('initial', this, this.gMapContext.location);
23808     },
23809     
23810     initOverlayView: function()
23811     {
23812         var _this = this;
23813         
23814         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23815             
23816             draw: function()
23817             {
23818                 _this.fireEvent('OverlayViewDraw', _this);
23819             },
23820             
23821             onAdd: function()
23822             {
23823                 _this.fireEvent('OverlayViewOnAdd', _this);
23824             },
23825             
23826             onRemove: function()
23827             {
23828                 _this.fireEvent('OverlayViewOnRemove', _this);
23829             },
23830             
23831             show: function(cpx)
23832             {
23833                 _this.fireEvent('OverlayViewShow', _this, cpx);
23834             },
23835             
23836             hide: function()
23837             {
23838                 _this.fireEvent('OverlayViewHide', _this);
23839             }
23840             
23841         });
23842     },
23843     
23844     fromLatLngToContainerPixel: function(event)
23845     {
23846         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23847     },
23848     
23849     isApplied: function() 
23850     {
23851         return this.getGmapContext() == false ? false : true;
23852     },
23853     
23854     getGmapContext: function() 
23855     {
23856         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
23857     },
23858     
23859     GMapContext: function() 
23860     {
23861         var position = new google.maps.LatLng(this.latitude, this.longitude);
23862         
23863         var _map = new google.maps.Map(this.el.dom, {
23864             center: position,
23865             zoom: this.zoom,
23866             mapTypeId: this.mapTypeId,
23867             mapTypeControl: this.mapTypeControl,
23868             disableDoubleClickZoom: this.disableDoubleClickZoom,
23869             scrollwheel: this.scrollwheel,
23870             streetViewControl: this.streetViewControl,
23871             locationName: this.locationName,
23872             draggable: this.draggable,
23873             enableAutocomplete: this.enableAutocomplete,
23874             enableReverseGeocode: this.enableReverseGeocode
23875         });
23876         
23877         var _marker = new google.maps.Marker({
23878             position: position,
23879             map: _map,
23880             title: this.markerTitle,
23881             draggable: this.draggable
23882         });
23883         
23884         return {
23885             map: _map,
23886             marker: _marker,
23887             circle: null,
23888             location: position,
23889             radius: this.radius,
23890             locationName: this.locationName,
23891             addressComponents: {
23892                 formatted_address: null,
23893                 addressLine1: null,
23894                 addressLine2: null,
23895                 streetName: null,
23896                 streetNumber: null,
23897                 city: null,
23898                 district: null,
23899                 state: null,
23900                 stateOrProvince: null
23901             },
23902             settings: this,
23903             domContainer: this.el.dom,
23904             geodecoder: new google.maps.Geocoder()
23905         };
23906     },
23907     
23908     drawCircle: function(center, radius, options) 
23909     {
23910         if (this.gMapContext.circle != null) {
23911             this.gMapContext.circle.setMap(null);
23912         }
23913         if (radius > 0) {
23914             radius *= 1;
23915             options = Roo.apply({}, options, {
23916                 strokeColor: "#0000FF",
23917                 strokeOpacity: .35,
23918                 strokeWeight: 2,
23919                 fillColor: "#0000FF",
23920                 fillOpacity: .2
23921             });
23922             
23923             options.map = this.gMapContext.map;
23924             options.radius = radius;
23925             options.center = center;
23926             this.gMapContext.circle = new google.maps.Circle(options);
23927             return this.gMapContext.circle;
23928         }
23929         
23930         return null;
23931     },
23932     
23933     setPosition: function(location) 
23934     {
23935         this.gMapContext.location = location;
23936         this.gMapContext.marker.setPosition(location);
23937         this.gMapContext.map.panTo(location);
23938         this.drawCircle(location, this.gMapContext.radius, {});
23939         
23940         var _this = this;
23941         
23942         if (this.gMapContext.settings.enableReverseGeocode) {
23943             this.gMapContext.geodecoder.geocode({
23944                 latLng: this.gMapContext.location
23945             }, function(results, status) {
23946                 
23947                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23948                     _this.gMapContext.locationName = results[0].formatted_address;
23949                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23950                     
23951                     _this.fireEvent('positionchanged', this, location);
23952                 }
23953             });
23954             
23955             return;
23956         }
23957         
23958         this.fireEvent('positionchanged', this, location);
23959     },
23960     
23961     resize: function()
23962     {
23963         google.maps.event.trigger(this.gMapContext.map, "resize");
23964         
23965         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23966         
23967         this.fireEvent('resize', this);
23968     },
23969     
23970     setPositionByLatLng: function(latitude, longitude)
23971     {
23972         this.setPosition(new google.maps.LatLng(latitude, longitude));
23973     },
23974     
23975     getCurrentPosition: function() 
23976     {
23977         return {
23978             latitude: this.gMapContext.location.lat(),
23979             longitude: this.gMapContext.location.lng()
23980         };
23981     },
23982     
23983     getAddressName: function() 
23984     {
23985         return this.gMapContext.locationName;
23986     },
23987     
23988     getAddressComponents: function() 
23989     {
23990         return this.gMapContext.addressComponents;
23991     },
23992     
23993     address_component_from_google_geocode: function(address_components) 
23994     {
23995         var result = {};
23996         
23997         for (var i = 0; i < address_components.length; i++) {
23998             var component = address_components[i];
23999             if (component.types.indexOf("postal_code") >= 0) {
24000                 result.postalCode = component.short_name;
24001             } else if (component.types.indexOf("street_number") >= 0) {
24002                 result.streetNumber = component.short_name;
24003             } else if (component.types.indexOf("route") >= 0) {
24004                 result.streetName = component.short_name;
24005             } else if (component.types.indexOf("neighborhood") >= 0) {
24006                 result.city = component.short_name;
24007             } else if (component.types.indexOf("locality") >= 0) {
24008                 result.city = component.short_name;
24009             } else if (component.types.indexOf("sublocality") >= 0) {
24010                 result.district = component.short_name;
24011             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24012                 result.stateOrProvince = component.short_name;
24013             } else if (component.types.indexOf("country") >= 0) {
24014                 result.country = component.short_name;
24015             }
24016         }
24017         
24018         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24019         result.addressLine2 = "";
24020         return result;
24021     },
24022     
24023     setZoomLevel: function(zoom)
24024     {
24025         this.gMapContext.map.setZoom(zoom);
24026     },
24027     
24028     show: function()
24029     {
24030         if(!this.el){
24031             return;
24032         }
24033         
24034         this.el.show();
24035         
24036         this.resize();
24037         
24038         this.fireEvent('show', this);
24039     },
24040     
24041     hide: function()
24042     {
24043         if(!this.el){
24044             return;
24045         }
24046         
24047         this.el.hide();
24048         
24049         this.fireEvent('hide', this);
24050     }
24051     
24052 });
24053
24054 Roo.apply(Roo.bootstrap.LocationPicker, {
24055     
24056     OverlayView : function(map, options)
24057     {
24058         options = options || {};
24059         
24060         this.setMap(map);
24061     }
24062     
24063     
24064 });/*
24065  * - LGPL
24066  *
24067  * Alert
24068  * 
24069  */
24070
24071 /**
24072  * @class Roo.bootstrap.Alert
24073  * @extends Roo.bootstrap.Component
24074  * Bootstrap Alert class
24075  * @cfg {String} title The title of alert
24076  * @cfg {String} html The content of alert
24077  * @cfg {String} weight (  success | info | warning | danger )
24078  * @cfg {String} faicon font-awesomeicon
24079  * 
24080  * @constructor
24081  * Create a new alert
24082  * @param {Object} config The config object
24083  */
24084
24085
24086 Roo.bootstrap.Alert = function(config){
24087     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24088     
24089 };
24090
24091 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24092     
24093     title: '',
24094     html: '',
24095     weight: false,
24096     faicon: false,
24097     
24098     getAutoCreate : function()
24099     {
24100         
24101         var cfg = {
24102             tag : 'div',
24103             cls : 'alert',
24104             cn : [
24105                 {
24106                     tag : 'i',
24107                     cls : 'roo-alert-icon'
24108                     
24109                 },
24110                 {
24111                     tag : 'b',
24112                     cls : 'roo-alert-title',
24113                     html : this.title
24114                 },
24115                 {
24116                     tag : 'span',
24117                     cls : 'roo-alert-text',
24118                     html : this.html
24119                 }
24120             ]
24121         };
24122         
24123         if(this.faicon){
24124             cfg.cn[0].cls += ' fa ' + this.faicon;
24125         }
24126         
24127         if(this.weight){
24128             cfg.cls += ' alert-' + this.weight;
24129         }
24130         
24131         return cfg;
24132     },
24133     
24134     initEvents: function() 
24135     {
24136         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24137     },
24138     
24139     setTitle : function(str)
24140     {
24141         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24142     },
24143     
24144     setText : function(str)
24145     {
24146         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24147     },
24148     
24149     setWeight : function(weight)
24150     {
24151         if(this.weight){
24152             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24153         }
24154         
24155         this.weight = weight;
24156         
24157         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24158     },
24159     
24160     setIcon : function(icon)
24161     {
24162         if(this.faicon){
24163             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24164         }
24165         
24166         this.faicon = icon;
24167         
24168         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24169     },
24170     
24171     hide: function() 
24172     {
24173         this.el.hide();   
24174     },
24175     
24176     show: function() 
24177     {  
24178         this.el.show();   
24179     }
24180     
24181 });
24182
24183  
24184 /*
24185 * Licence: LGPL
24186 */
24187
24188 /**
24189  * @class Roo.bootstrap.UploadCropbox
24190  * @extends Roo.bootstrap.Component
24191  * Bootstrap UploadCropbox class
24192  * @cfg {String} emptyText show when image has been loaded
24193  * @cfg {String} rotateNotify show when image too small to rotate
24194  * @cfg {Number} errorTimeout default 3000
24195  * @cfg {Number} minWidth default 300
24196  * @cfg {Number} minHeight default 300
24197  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24198  * @cfg {Boolean} isDocument (true|false) default false
24199  * @cfg {String} url action url
24200  * @cfg {String} paramName default 'imageUpload'
24201  * @cfg {String} method default POST
24202  * @cfg {Boolean} loadMask (true|false) default true
24203  * @cfg {Boolean} loadingText default 'Loading...'
24204  * 
24205  * @constructor
24206  * Create a new UploadCropbox
24207  * @param {Object} config The config object
24208  */
24209
24210 Roo.bootstrap.UploadCropbox = function(config){
24211     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24212     
24213     this.addEvents({
24214         /**
24215          * @event beforeselectfile
24216          * Fire before select file
24217          * @param {Roo.bootstrap.UploadCropbox} this
24218          */
24219         "beforeselectfile" : true,
24220         /**
24221          * @event initial
24222          * Fire after initEvent
24223          * @param {Roo.bootstrap.UploadCropbox} this
24224          */
24225         "initial" : true,
24226         /**
24227          * @event crop
24228          * Fire after initEvent
24229          * @param {Roo.bootstrap.UploadCropbox} this
24230          * @param {String} data
24231          */
24232         "crop" : true,
24233         /**
24234          * @event prepare
24235          * Fire when preparing the file data
24236          * @param {Roo.bootstrap.UploadCropbox} this
24237          * @param {Object} file
24238          */
24239         "prepare" : true,
24240         /**
24241          * @event exception
24242          * Fire when get exception
24243          * @param {Roo.bootstrap.UploadCropbox} this
24244          * @param {XMLHttpRequest} xhr
24245          */
24246         "exception" : true,
24247         /**
24248          * @event beforeloadcanvas
24249          * Fire before load the canvas
24250          * @param {Roo.bootstrap.UploadCropbox} this
24251          * @param {String} src
24252          */
24253         "beforeloadcanvas" : true,
24254         /**
24255          * @event trash
24256          * Fire when trash image
24257          * @param {Roo.bootstrap.UploadCropbox} this
24258          */
24259         "trash" : true,
24260         /**
24261          * @event download
24262          * Fire when download the image
24263          * @param {Roo.bootstrap.UploadCropbox} this
24264          */
24265         "download" : true,
24266         /**
24267          * @event footerbuttonclick
24268          * Fire when footerbuttonclick
24269          * @param {Roo.bootstrap.UploadCropbox} this
24270          * @param {String} type
24271          */
24272         "footerbuttonclick" : true,
24273         /**
24274          * @event resize
24275          * Fire when resize
24276          * @param {Roo.bootstrap.UploadCropbox} this
24277          */
24278         "resize" : true,
24279         /**
24280          * @event rotate
24281          * Fire when rotate the image
24282          * @param {Roo.bootstrap.UploadCropbox} this
24283          * @param {String} pos
24284          */
24285         "rotate" : true,
24286         /**
24287          * @event inspect
24288          * Fire when inspect the file
24289          * @param {Roo.bootstrap.UploadCropbox} this
24290          * @param {Object} file
24291          */
24292         "inspect" : true,
24293         /**
24294          * @event upload
24295          * Fire when xhr upload the file
24296          * @param {Roo.bootstrap.UploadCropbox} this
24297          * @param {Object} data
24298          */
24299         "upload" : true,
24300         /**
24301          * @event arrange
24302          * Fire when arrange the file data
24303          * @param {Roo.bootstrap.UploadCropbox} this
24304          * @param {Object} formData
24305          */
24306         "arrange" : true
24307     });
24308     
24309     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24310 };
24311
24312 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24313     
24314     emptyText : 'Click to upload image',
24315     rotateNotify : 'Image is too small to rotate',
24316     errorTimeout : 3000,
24317     scale : 0,
24318     baseScale : 1,
24319     rotate : 0,
24320     dragable : false,
24321     pinching : false,
24322     mouseX : 0,
24323     mouseY : 0,
24324     cropData : false,
24325     minWidth : 300,
24326     minHeight : 300,
24327     file : false,
24328     exif : {},
24329     baseRotate : 1,
24330     cropType : 'image/jpeg',
24331     buttons : false,
24332     canvasLoaded : false,
24333     isDocument : false,
24334     method : 'POST',
24335     paramName : 'imageUpload',
24336     loadMask : true,
24337     loadingText : 'Loading...',
24338     maskEl : false,
24339     
24340     getAutoCreate : function()
24341     {
24342         var cfg = {
24343             tag : 'div',
24344             cls : 'roo-upload-cropbox',
24345             cn : [
24346                 {
24347                     tag : 'input',
24348                     cls : 'roo-upload-cropbox-selector',
24349                     type : 'file'
24350                 },
24351                 {
24352                     tag : 'div',
24353                     cls : 'roo-upload-cropbox-body',
24354                     style : 'cursor:pointer',
24355                     cn : [
24356                         {
24357                             tag : 'div',
24358                             cls : 'roo-upload-cropbox-preview'
24359                         },
24360                         {
24361                             tag : 'div',
24362                             cls : 'roo-upload-cropbox-thumb'
24363                         },
24364                         {
24365                             tag : 'div',
24366                             cls : 'roo-upload-cropbox-empty-notify',
24367                             html : this.emptyText
24368                         },
24369                         {
24370                             tag : 'div',
24371                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24372                             html : this.rotateNotify
24373                         }
24374                     ]
24375                 },
24376                 {
24377                     tag : 'div',
24378                     cls : 'roo-upload-cropbox-footer',
24379                     cn : {
24380                         tag : 'div',
24381                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24382                         cn : []
24383                     }
24384                 }
24385             ]
24386         };
24387         
24388         return cfg;
24389     },
24390     
24391     onRender : function(ct, position)
24392     {
24393         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24394         
24395         if (this.buttons.length) {
24396             
24397             Roo.each(this.buttons, function(bb) {
24398                 
24399                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24400                 
24401                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24402                 
24403             }, this);
24404         }
24405         
24406         if(this.loadMask){
24407             this.maskEl = this.el;
24408         }
24409     },
24410     
24411     initEvents : function()
24412     {
24413         this.urlAPI = (window.createObjectURL && window) || 
24414                                 (window.URL && URL.revokeObjectURL && URL) || 
24415                                 (window.webkitURL && webkitURL);
24416                         
24417         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24418         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24419         
24420         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24421         this.selectorEl.hide();
24422         
24423         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24424         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24425         
24426         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24427         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24428         this.thumbEl.hide();
24429         
24430         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24431         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24432         
24433         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24434         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24435         this.errorEl.hide();
24436         
24437         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24438         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24439         this.footerEl.hide();
24440         
24441         this.setThumbBoxSize();
24442         
24443         this.bind();
24444         
24445         this.resize();
24446         
24447         this.fireEvent('initial', this);
24448     },
24449
24450     bind : function()
24451     {
24452         var _this = this;
24453         
24454         window.addEventListener("resize", function() { _this.resize(); } );
24455         
24456         this.bodyEl.on('click', this.beforeSelectFile, this);
24457         
24458         if(Roo.isTouch){
24459             this.bodyEl.on('touchstart', this.onTouchStart, this);
24460             this.bodyEl.on('touchmove', this.onTouchMove, this);
24461             this.bodyEl.on('touchend', this.onTouchEnd, this);
24462         }
24463         
24464         if(!Roo.isTouch){
24465             this.bodyEl.on('mousedown', this.onMouseDown, this);
24466             this.bodyEl.on('mousemove', this.onMouseMove, this);
24467             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24468             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24469             Roo.get(document).on('mouseup', this.onMouseUp, this);
24470         }
24471         
24472         this.selectorEl.on('change', this.onFileSelected, this);
24473     },
24474     
24475     reset : function()
24476     {    
24477         this.scale = 0;
24478         this.baseScale = 1;
24479         this.rotate = 0;
24480         this.baseRotate = 1;
24481         this.dragable = false;
24482         this.pinching = false;
24483         this.mouseX = 0;
24484         this.mouseY = 0;
24485         this.cropData = false;
24486         this.notifyEl.dom.innerHTML = this.emptyText;
24487         
24488         this.selectorEl.dom.value = '';
24489         
24490     },
24491     
24492     resize : function()
24493     {
24494         if(this.fireEvent('resize', this) != false){
24495             this.setThumbBoxPosition();
24496             this.setCanvasPosition();
24497         }
24498     },
24499     
24500     onFooterButtonClick : function(e, el, o, type)
24501     {
24502         switch (type) {
24503             case 'rotate-left' :
24504                 this.onRotateLeft(e);
24505                 break;
24506             case 'rotate-right' :
24507                 this.onRotateRight(e);
24508                 break;
24509             case 'picture' :
24510                 this.beforeSelectFile(e);
24511                 break;
24512             case 'trash' :
24513                 this.trash(e);
24514                 break;
24515             case 'crop' :
24516                 this.crop(e);
24517                 break;
24518             case 'download' :
24519                 this.download(e);
24520                 break;
24521             default :
24522                 break;
24523         }
24524         
24525         this.fireEvent('footerbuttonclick', this, type);
24526     },
24527     
24528     beforeSelectFile : function(e)
24529     {
24530         e.preventDefault();
24531         
24532         if(this.fireEvent('beforeselectfile', this) != false){
24533             this.selectorEl.dom.click();
24534         }
24535     },
24536     
24537     onFileSelected : function(e)
24538     {
24539         e.preventDefault();
24540         
24541         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24542             return;
24543         }
24544         
24545         var file = this.selectorEl.dom.files[0];
24546         
24547         if(this.fireEvent('inspect', this, file) != false){
24548             this.prepare(file);
24549         }
24550         
24551     },
24552     
24553     trash : function(e)
24554     {
24555         this.fireEvent('trash', this);
24556     },
24557     
24558     download : function(e)
24559     {
24560         this.fireEvent('download', this);
24561     },
24562     
24563     loadCanvas : function(src)
24564     {   
24565         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24566             
24567             this.reset();
24568             
24569             this.imageEl = document.createElement('img');
24570             
24571             var _this = this;
24572             
24573             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24574             
24575             this.imageEl.src = src;
24576         }
24577     },
24578     
24579     onLoadCanvas : function()
24580     {   
24581         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24582         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24583         
24584         this.bodyEl.un('click', this.beforeSelectFile, this);
24585         
24586         this.notifyEl.hide();
24587         this.thumbEl.show();
24588         this.footerEl.show();
24589         
24590         this.baseRotateLevel();
24591         
24592         if(this.isDocument){
24593             this.setThumbBoxSize();
24594         }
24595         
24596         this.setThumbBoxPosition();
24597         
24598         this.baseScaleLevel();
24599         
24600         this.draw();
24601         
24602         this.resize();
24603         
24604         this.canvasLoaded = true;
24605         
24606         if(this.loadMask){
24607             this.maskEl.unmask();
24608         }
24609         
24610     },
24611     
24612     setCanvasPosition : function()
24613     {   
24614         if(!this.canvasEl){
24615             return;
24616         }
24617         
24618         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24619         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24620         
24621         this.previewEl.setLeft(pw);
24622         this.previewEl.setTop(ph);
24623         
24624     },
24625     
24626     onMouseDown : function(e)
24627     {   
24628         e.stopEvent();
24629         
24630         this.dragable = true;
24631         this.pinching = false;
24632         
24633         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24634             this.dragable = false;
24635             return;
24636         }
24637         
24638         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24639         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24640         
24641     },
24642     
24643     onMouseMove : function(e)
24644     {   
24645         e.stopEvent();
24646         
24647         if(!this.canvasLoaded){
24648             return;
24649         }
24650         
24651         if (!this.dragable){
24652             return;
24653         }
24654         
24655         var minX = Math.ceil(this.thumbEl.getLeft(true));
24656         var minY = Math.ceil(this.thumbEl.getTop(true));
24657         
24658         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24659         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24660         
24661         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24662         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24663         
24664         x = x - this.mouseX;
24665         y = y - this.mouseY;
24666         
24667         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24668         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24669         
24670         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24671         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24672         
24673         this.previewEl.setLeft(bgX);
24674         this.previewEl.setTop(bgY);
24675         
24676         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24677         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24678     },
24679     
24680     onMouseUp : function(e)
24681     {   
24682         e.stopEvent();
24683         
24684         this.dragable = false;
24685     },
24686     
24687     onMouseWheel : function(e)
24688     {   
24689         e.stopEvent();
24690         
24691         this.startScale = this.scale;
24692         
24693         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24694         
24695         if(!this.zoomable()){
24696             this.scale = this.startScale;
24697             return;
24698         }
24699         
24700         this.draw();
24701         
24702         return;
24703     },
24704     
24705     zoomable : function()
24706     {
24707         var minScale = this.thumbEl.getWidth() / this.minWidth;
24708         
24709         if(this.minWidth < this.minHeight){
24710             minScale = this.thumbEl.getHeight() / this.minHeight;
24711         }
24712         
24713         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24714         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24715         
24716         if(
24717                 this.isDocument &&
24718                 (this.rotate == 0 || this.rotate == 180) && 
24719                 (
24720                     width > this.imageEl.OriginWidth || 
24721                     height > this.imageEl.OriginHeight ||
24722                     (width < this.minWidth && height < this.minHeight)
24723                 )
24724         ){
24725             return false;
24726         }
24727         
24728         if(
24729                 this.isDocument &&
24730                 (this.rotate == 90 || this.rotate == 270) && 
24731                 (
24732                     width > this.imageEl.OriginWidth || 
24733                     height > this.imageEl.OriginHeight ||
24734                     (width < this.minHeight && height < this.minWidth)
24735                 )
24736         ){
24737             return false;
24738         }
24739         
24740         if(
24741                 !this.isDocument &&
24742                 (this.rotate == 0 || this.rotate == 180) && 
24743                 (
24744                     width < this.minWidth || 
24745                     width > this.imageEl.OriginWidth || 
24746                     height < this.minHeight || 
24747                     height > this.imageEl.OriginHeight
24748                 )
24749         ){
24750             return false;
24751         }
24752         
24753         if(
24754                 !this.isDocument &&
24755                 (this.rotate == 90 || this.rotate == 270) && 
24756                 (
24757                     width < this.minHeight || 
24758                     width > this.imageEl.OriginWidth || 
24759                     height < this.minWidth || 
24760                     height > this.imageEl.OriginHeight
24761                 )
24762         ){
24763             return false;
24764         }
24765         
24766         return true;
24767         
24768     },
24769     
24770     onRotateLeft : function(e)
24771     {   
24772         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24773             
24774             var minScale = this.thumbEl.getWidth() / this.minWidth;
24775             
24776             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24777             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24778             
24779             this.startScale = this.scale;
24780             
24781             while (this.getScaleLevel() < minScale){
24782             
24783                 this.scale = this.scale + 1;
24784                 
24785                 if(!this.zoomable()){
24786                     break;
24787                 }
24788                 
24789                 if(
24790                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24791                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24792                 ){
24793                     continue;
24794                 }
24795                 
24796                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24797
24798                 this.draw();
24799                 
24800                 return;
24801             }
24802             
24803             this.scale = this.startScale;
24804             
24805             this.onRotateFail();
24806             
24807             return false;
24808         }
24809         
24810         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24811
24812         if(this.isDocument){
24813             this.setThumbBoxSize();
24814             this.setThumbBoxPosition();
24815             this.setCanvasPosition();
24816         }
24817         
24818         this.draw();
24819         
24820         this.fireEvent('rotate', this, 'left');
24821         
24822     },
24823     
24824     onRotateRight : function(e)
24825     {
24826         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24827             
24828             var minScale = this.thumbEl.getWidth() / this.minWidth;
24829         
24830             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24831             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24832             
24833             this.startScale = this.scale;
24834             
24835             while (this.getScaleLevel() < minScale){
24836             
24837                 this.scale = this.scale + 1;
24838                 
24839                 if(!this.zoomable()){
24840                     break;
24841                 }
24842                 
24843                 if(
24844                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24845                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24846                 ){
24847                     continue;
24848                 }
24849                 
24850                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24851
24852                 this.draw();
24853                 
24854                 return;
24855             }
24856             
24857             this.scale = this.startScale;
24858             
24859             this.onRotateFail();
24860             
24861             return false;
24862         }
24863         
24864         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24865
24866         if(this.isDocument){
24867             this.setThumbBoxSize();
24868             this.setThumbBoxPosition();
24869             this.setCanvasPosition();
24870         }
24871         
24872         this.draw();
24873         
24874         this.fireEvent('rotate', this, 'right');
24875     },
24876     
24877     onRotateFail : function()
24878     {
24879         this.errorEl.show(true);
24880         
24881         var _this = this;
24882         
24883         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24884     },
24885     
24886     draw : function()
24887     {
24888         this.previewEl.dom.innerHTML = '';
24889         
24890         var canvasEl = document.createElement("canvas");
24891         
24892         var contextEl = canvasEl.getContext("2d");
24893         
24894         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24895         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24896         var center = this.imageEl.OriginWidth / 2;
24897         
24898         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24899             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24900             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24901             center = this.imageEl.OriginHeight / 2;
24902         }
24903         
24904         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24905         
24906         contextEl.translate(center, center);
24907         contextEl.rotate(this.rotate * Math.PI / 180);
24908
24909         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24910         
24911         this.canvasEl = document.createElement("canvas");
24912         
24913         this.contextEl = this.canvasEl.getContext("2d");
24914         
24915         switch (this.rotate) {
24916             case 0 :
24917                 
24918                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24919                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24920                 
24921                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24922                 
24923                 break;
24924             case 90 : 
24925                 
24926                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24927                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24928                 
24929                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24930                     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);
24931                     break;
24932                 }
24933                 
24934                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24935                 
24936                 break;
24937             case 180 :
24938                 
24939                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24940                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24941                 
24942                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24943                     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);
24944                     break;
24945                 }
24946                 
24947                 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);
24948                 
24949                 break;
24950             case 270 :
24951                 
24952                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24953                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24954         
24955                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24956                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24957                     break;
24958                 }
24959                 
24960                 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);
24961                 
24962                 break;
24963             default : 
24964                 break;
24965         }
24966         
24967         this.previewEl.appendChild(this.canvasEl);
24968         
24969         this.setCanvasPosition();
24970     },
24971     
24972     crop : function()
24973     {
24974         if(!this.canvasLoaded){
24975             return;
24976         }
24977         
24978         var imageCanvas = document.createElement("canvas");
24979         
24980         var imageContext = imageCanvas.getContext("2d");
24981         
24982         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24983         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24984         
24985         var center = imageCanvas.width / 2;
24986         
24987         imageContext.translate(center, center);
24988         
24989         imageContext.rotate(this.rotate * Math.PI / 180);
24990         
24991         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24992         
24993         var canvas = document.createElement("canvas");
24994         
24995         var context = canvas.getContext("2d");
24996                 
24997         canvas.width = this.minWidth;
24998         canvas.height = this.minHeight;
24999
25000         switch (this.rotate) {
25001             case 0 :
25002                 
25003                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25004                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25005                 
25006                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25007                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25008                 
25009                 var targetWidth = this.minWidth - 2 * x;
25010                 var targetHeight = this.minHeight - 2 * y;
25011                 
25012                 var scale = 1;
25013                 
25014                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25015                     scale = targetWidth / width;
25016                 }
25017                 
25018                 if(x > 0 && y == 0){
25019                     scale = targetHeight / height;
25020                 }
25021                 
25022                 if(x > 0 && y > 0){
25023                     scale = targetWidth / width;
25024                     
25025                     if(width < height){
25026                         scale = targetHeight / height;
25027                     }
25028                 }
25029                 
25030                 context.scale(scale, scale);
25031                 
25032                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25033                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25034
25035                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25036                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25037
25038                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25039                 
25040                 break;
25041             case 90 : 
25042                 
25043                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25044                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25045                 
25046                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25047                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25048                 
25049                 var targetWidth = this.minWidth - 2 * x;
25050                 var targetHeight = this.minHeight - 2 * y;
25051                 
25052                 var scale = 1;
25053                 
25054                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25055                     scale = targetWidth / width;
25056                 }
25057                 
25058                 if(x > 0 && y == 0){
25059                     scale = targetHeight / height;
25060                 }
25061                 
25062                 if(x > 0 && y > 0){
25063                     scale = targetWidth / width;
25064                     
25065                     if(width < height){
25066                         scale = targetHeight / height;
25067                     }
25068                 }
25069                 
25070                 context.scale(scale, scale);
25071                 
25072                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25073                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25074
25075                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25076                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25077                 
25078                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25079                 
25080                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25081                 
25082                 break;
25083             case 180 :
25084                 
25085                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25086                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25087                 
25088                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25089                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25090                 
25091                 var targetWidth = this.minWidth - 2 * x;
25092                 var targetHeight = this.minHeight - 2 * y;
25093                 
25094                 var scale = 1;
25095                 
25096                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25097                     scale = targetWidth / width;
25098                 }
25099                 
25100                 if(x > 0 && y == 0){
25101                     scale = targetHeight / height;
25102                 }
25103                 
25104                 if(x > 0 && y > 0){
25105                     scale = targetWidth / width;
25106                     
25107                     if(width < height){
25108                         scale = targetHeight / height;
25109                     }
25110                 }
25111                 
25112                 context.scale(scale, scale);
25113                 
25114                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25115                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25116
25117                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25118                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25119
25120                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25121                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25122                 
25123                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25124                 
25125                 break;
25126             case 270 :
25127                 
25128                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25129                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25130                 
25131                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25132                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25133                 
25134                 var targetWidth = this.minWidth - 2 * x;
25135                 var targetHeight = this.minHeight - 2 * y;
25136                 
25137                 var scale = 1;
25138                 
25139                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25140                     scale = targetWidth / width;
25141                 }
25142                 
25143                 if(x > 0 && y == 0){
25144                     scale = targetHeight / height;
25145                 }
25146                 
25147                 if(x > 0 && y > 0){
25148                     scale = targetWidth / width;
25149                     
25150                     if(width < height){
25151                         scale = targetHeight / height;
25152                     }
25153                 }
25154                 
25155                 context.scale(scale, scale);
25156                 
25157                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25158                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25159
25160                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25161                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25162                 
25163                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25164                 
25165                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25166                 
25167                 break;
25168             default : 
25169                 break;
25170         }
25171         
25172         this.cropData = canvas.toDataURL(this.cropType);
25173         
25174         if(this.fireEvent('crop', this, this.cropData) !== false){
25175             this.process(this.file, this.cropData);
25176         }
25177         
25178         return;
25179         
25180     },
25181     
25182     setThumbBoxSize : function()
25183     {
25184         var width, height;
25185         
25186         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25187             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25188             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25189             
25190             this.minWidth = width;
25191             this.minHeight = height;
25192             
25193             if(this.rotate == 90 || this.rotate == 270){
25194                 this.minWidth = height;
25195                 this.minHeight = width;
25196             }
25197         }
25198         
25199         height = 300;
25200         width = Math.ceil(this.minWidth * height / this.minHeight);
25201         
25202         if(this.minWidth > this.minHeight){
25203             width = 300;
25204             height = Math.ceil(this.minHeight * width / this.minWidth);
25205         }
25206         
25207         this.thumbEl.setStyle({
25208             width : width + 'px',
25209             height : height + 'px'
25210         });
25211
25212         return;
25213             
25214     },
25215     
25216     setThumbBoxPosition : function()
25217     {
25218         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25219         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25220         
25221         this.thumbEl.setLeft(x);
25222         this.thumbEl.setTop(y);
25223         
25224     },
25225     
25226     baseRotateLevel : function()
25227     {
25228         this.baseRotate = 1;
25229         
25230         if(
25231                 typeof(this.exif) != 'undefined' &&
25232                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25233                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25234         ){
25235             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25236         }
25237         
25238         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25239         
25240     },
25241     
25242     baseScaleLevel : function()
25243     {
25244         var width, height;
25245         
25246         if(this.isDocument){
25247             
25248             if(this.baseRotate == 6 || this.baseRotate == 8){
25249             
25250                 height = this.thumbEl.getHeight();
25251                 this.baseScale = height / this.imageEl.OriginWidth;
25252
25253                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25254                     width = this.thumbEl.getWidth();
25255                     this.baseScale = width / this.imageEl.OriginHeight;
25256                 }
25257
25258                 return;
25259             }
25260
25261             height = this.thumbEl.getHeight();
25262             this.baseScale = height / this.imageEl.OriginHeight;
25263
25264             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25265                 width = this.thumbEl.getWidth();
25266                 this.baseScale = width / this.imageEl.OriginWidth;
25267             }
25268
25269             return;
25270         }
25271         
25272         if(this.baseRotate == 6 || this.baseRotate == 8){
25273             
25274             width = this.thumbEl.getHeight();
25275             this.baseScale = width / this.imageEl.OriginHeight;
25276             
25277             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25278                 height = this.thumbEl.getWidth();
25279                 this.baseScale = height / this.imageEl.OriginHeight;
25280             }
25281             
25282             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25283                 height = this.thumbEl.getWidth();
25284                 this.baseScale = height / this.imageEl.OriginHeight;
25285                 
25286                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25287                     width = this.thumbEl.getHeight();
25288                     this.baseScale = width / this.imageEl.OriginWidth;
25289                 }
25290             }
25291             
25292             return;
25293         }
25294         
25295         width = this.thumbEl.getWidth();
25296         this.baseScale = width / this.imageEl.OriginWidth;
25297         
25298         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25299             height = this.thumbEl.getHeight();
25300             this.baseScale = height / this.imageEl.OriginHeight;
25301         }
25302         
25303         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25304             
25305             height = this.thumbEl.getHeight();
25306             this.baseScale = height / this.imageEl.OriginHeight;
25307             
25308             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25309                 width = this.thumbEl.getWidth();
25310                 this.baseScale = width / this.imageEl.OriginWidth;
25311             }
25312             
25313         }
25314         
25315         return;
25316     },
25317     
25318     getScaleLevel : function()
25319     {
25320         return this.baseScale * Math.pow(1.1, this.scale);
25321     },
25322     
25323     onTouchStart : function(e)
25324     {
25325         if(!this.canvasLoaded){
25326             this.beforeSelectFile(e);
25327             return;
25328         }
25329         
25330         var touches = e.browserEvent.touches;
25331         
25332         if(!touches){
25333             return;
25334         }
25335         
25336         if(touches.length == 1){
25337             this.onMouseDown(e);
25338             return;
25339         }
25340         
25341         if(touches.length != 2){
25342             return;
25343         }
25344         
25345         var coords = [];
25346         
25347         for(var i = 0, finger; finger = touches[i]; i++){
25348             coords.push(finger.pageX, finger.pageY);
25349         }
25350         
25351         var x = Math.pow(coords[0] - coords[2], 2);
25352         var y = Math.pow(coords[1] - coords[3], 2);
25353         
25354         this.startDistance = Math.sqrt(x + y);
25355         
25356         this.startScale = this.scale;
25357         
25358         this.pinching = true;
25359         this.dragable = false;
25360         
25361     },
25362     
25363     onTouchMove : function(e)
25364     {
25365         if(!this.pinching && !this.dragable){
25366             return;
25367         }
25368         
25369         var touches = e.browserEvent.touches;
25370         
25371         if(!touches){
25372             return;
25373         }
25374         
25375         if(this.dragable){
25376             this.onMouseMove(e);
25377             return;
25378         }
25379         
25380         var coords = [];
25381         
25382         for(var i = 0, finger; finger = touches[i]; i++){
25383             coords.push(finger.pageX, finger.pageY);
25384         }
25385         
25386         var x = Math.pow(coords[0] - coords[2], 2);
25387         var y = Math.pow(coords[1] - coords[3], 2);
25388         
25389         this.endDistance = Math.sqrt(x + y);
25390         
25391         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25392         
25393         if(!this.zoomable()){
25394             this.scale = this.startScale;
25395             return;
25396         }
25397         
25398         this.draw();
25399         
25400     },
25401     
25402     onTouchEnd : function(e)
25403     {
25404         this.pinching = false;
25405         this.dragable = false;
25406         
25407     },
25408     
25409     process : function(file, crop)
25410     {
25411         if(this.loadMask){
25412             this.maskEl.mask(this.loadingText);
25413         }
25414         
25415         this.xhr = new XMLHttpRequest();
25416         
25417         file.xhr = this.xhr;
25418
25419         this.xhr.open(this.method, this.url, true);
25420         
25421         var headers = {
25422             "Accept": "application/json",
25423             "Cache-Control": "no-cache",
25424             "X-Requested-With": "XMLHttpRequest"
25425         };
25426         
25427         for (var headerName in headers) {
25428             var headerValue = headers[headerName];
25429             if (headerValue) {
25430                 this.xhr.setRequestHeader(headerName, headerValue);
25431             }
25432         }
25433         
25434         var _this = this;
25435         
25436         this.xhr.onload = function()
25437         {
25438             _this.xhrOnLoad(_this.xhr);
25439         }
25440         
25441         this.xhr.onerror = function()
25442         {
25443             _this.xhrOnError(_this.xhr);
25444         }
25445         
25446         var formData = new FormData();
25447
25448         formData.append('returnHTML', 'NO');
25449         
25450         if(crop){
25451             formData.append('crop', crop);
25452         }
25453         
25454         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25455             formData.append(this.paramName, file, file.name);
25456         }
25457         
25458         if(typeof(file.filename) != 'undefined'){
25459             formData.append('filename', file.filename);
25460         }
25461         
25462         if(typeof(file.mimetype) != 'undefined'){
25463             formData.append('mimetype', file.mimetype);
25464         }
25465         
25466         if(this.fireEvent('arrange', this, formData) != false){
25467             this.xhr.send(formData);
25468         };
25469     },
25470     
25471     xhrOnLoad : function(xhr)
25472     {
25473         if(this.loadMask){
25474             this.maskEl.unmask();
25475         }
25476         
25477         if (xhr.readyState !== 4) {
25478             this.fireEvent('exception', this, xhr);
25479             return;
25480         }
25481
25482         var response = Roo.decode(xhr.responseText);
25483         
25484         if(!response.success){
25485             this.fireEvent('exception', this, xhr);
25486             return;
25487         }
25488         
25489         var response = Roo.decode(xhr.responseText);
25490         
25491         this.fireEvent('upload', this, response);
25492         
25493     },
25494     
25495     xhrOnError : function()
25496     {
25497         if(this.loadMask){
25498             this.maskEl.unmask();
25499         }
25500         
25501         Roo.log('xhr on error');
25502         
25503         var response = Roo.decode(xhr.responseText);
25504           
25505         Roo.log(response);
25506         
25507     },
25508     
25509     prepare : function(file)
25510     {   
25511         if(this.loadMask){
25512             this.maskEl.mask(this.loadingText);
25513         }
25514         
25515         this.file = false;
25516         this.exif = {};
25517         
25518         if(typeof(file) === 'string'){
25519             this.loadCanvas(file);
25520             return;
25521         }
25522         
25523         if(!file || !this.urlAPI){
25524             return;
25525         }
25526         
25527         this.file = file;
25528         this.cropType = file.type;
25529         
25530         var _this = this;
25531         
25532         if(this.fireEvent('prepare', this, this.file) != false){
25533             
25534             var reader = new FileReader();
25535             
25536             reader.onload = function (e) {
25537                 if (e.target.error) {
25538                     Roo.log(e.target.error);
25539                     return;
25540                 }
25541                 
25542                 var buffer = e.target.result,
25543                     dataView = new DataView(buffer),
25544                     offset = 2,
25545                     maxOffset = dataView.byteLength - 4,
25546                     markerBytes,
25547                     markerLength;
25548                 
25549                 if (dataView.getUint16(0) === 0xffd8) {
25550                     while (offset < maxOffset) {
25551                         markerBytes = dataView.getUint16(offset);
25552                         
25553                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25554                             markerLength = dataView.getUint16(offset + 2) + 2;
25555                             if (offset + markerLength > dataView.byteLength) {
25556                                 Roo.log('Invalid meta data: Invalid segment size.');
25557                                 break;
25558                             }
25559                             
25560                             if(markerBytes == 0xffe1){
25561                                 _this.parseExifData(
25562                                     dataView,
25563                                     offset,
25564                                     markerLength
25565                                 );
25566                             }
25567                             
25568                             offset += markerLength;
25569                             
25570                             continue;
25571                         }
25572                         
25573                         break;
25574                     }
25575                     
25576                 }
25577                 
25578                 var url = _this.urlAPI.createObjectURL(_this.file);
25579                 
25580                 _this.loadCanvas(url);
25581                 
25582                 return;
25583             }
25584             
25585             reader.readAsArrayBuffer(this.file);
25586             
25587         }
25588         
25589     },
25590     
25591     parseExifData : function(dataView, offset, length)
25592     {
25593         var tiffOffset = offset + 10,
25594             littleEndian,
25595             dirOffset;
25596     
25597         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25598             // No Exif data, might be XMP data instead
25599             return;
25600         }
25601         
25602         // Check for the ASCII code for "Exif" (0x45786966):
25603         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25604             // No Exif data, might be XMP data instead
25605             return;
25606         }
25607         if (tiffOffset + 8 > dataView.byteLength) {
25608             Roo.log('Invalid Exif data: Invalid segment size.');
25609             return;
25610         }
25611         // Check for the two null bytes:
25612         if (dataView.getUint16(offset + 8) !== 0x0000) {
25613             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25614             return;
25615         }
25616         // Check the byte alignment:
25617         switch (dataView.getUint16(tiffOffset)) {
25618         case 0x4949:
25619             littleEndian = true;
25620             break;
25621         case 0x4D4D:
25622             littleEndian = false;
25623             break;
25624         default:
25625             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25626             return;
25627         }
25628         // Check for the TIFF tag marker (0x002A):
25629         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25630             Roo.log('Invalid Exif data: Missing TIFF marker.');
25631             return;
25632         }
25633         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25634         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25635         
25636         this.parseExifTags(
25637             dataView,
25638             tiffOffset,
25639             tiffOffset + dirOffset,
25640             littleEndian
25641         );
25642     },
25643     
25644     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25645     {
25646         var tagsNumber,
25647             dirEndOffset,
25648             i;
25649         if (dirOffset + 6 > dataView.byteLength) {
25650             Roo.log('Invalid Exif data: Invalid directory offset.');
25651             return;
25652         }
25653         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25654         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25655         if (dirEndOffset + 4 > dataView.byteLength) {
25656             Roo.log('Invalid Exif data: Invalid directory size.');
25657             return;
25658         }
25659         for (i = 0; i < tagsNumber; i += 1) {
25660             this.parseExifTag(
25661                 dataView,
25662                 tiffOffset,
25663                 dirOffset + 2 + 12 * i, // tag offset
25664                 littleEndian
25665             );
25666         }
25667         // Return the offset to the next directory:
25668         return dataView.getUint32(dirEndOffset, littleEndian);
25669     },
25670     
25671     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25672     {
25673         var tag = dataView.getUint16(offset, littleEndian);
25674         
25675         this.exif[tag] = this.getExifValue(
25676             dataView,
25677             tiffOffset,
25678             offset,
25679             dataView.getUint16(offset + 2, littleEndian), // tag type
25680             dataView.getUint32(offset + 4, littleEndian), // tag length
25681             littleEndian
25682         );
25683     },
25684     
25685     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25686     {
25687         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25688             tagSize,
25689             dataOffset,
25690             values,
25691             i,
25692             str,
25693             c;
25694     
25695         if (!tagType) {
25696             Roo.log('Invalid Exif data: Invalid tag type.');
25697             return;
25698         }
25699         
25700         tagSize = tagType.size * length;
25701         // Determine if the value is contained in the dataOffset bytes,
25702         // or if the value at the dataOffset is a pointer to the actual data:
25703         dataOffset = tagSize > 4 ?
25704                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25705         if (dataOffset + tagSize > dataView.byteLength) {
25706             Roo.log('Invalid Exif data: Invalid data offset.');
25707             return;
25708         }
25709         if (length === 1) {
25710             return tagType.getValue(dataView, dataOffset, littleEndian);
25711         }
25712         values = [];
25713         for (i = 0; i < length; i += 1) {
25714             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25715         }
25716         
25717         if (tagType.ascii) {
25718             str = '';
25719             // Concatenate the chars:
25720             for (i = 0; i < values.length; i += 1) {
25721                 c = values[i];
25722                 // Ignore the terminating NULL byte(s):
25723                 if (c === '\u0000') {
25724                     break;
25725                 }
25726                 str += c;
25727             }
25728             return str;
25729         }
25730         return values;
25731     }
25732     
25733 });
25734
25735 Roo.apply(Roo.bootstrap.UploadCropbox, {
25736     tags : {
25737         'Orientation': 0x0112
25738     },
25739     
25740     Orientation: {
25741             1: 0, //'top-left',
25742 //            2: 'top-right',
25743             3: 180, //'bottom-right',
25744 //            4: 'bottom-left',
25745 //            5: 'left-top',
25746             6: 90, //'right-top',
25747 //            7: 'right-bottom',
25748             8: 270 //'left-bottom'
25749     },
25750     
25751     exifTagTypes : {
25752         // byte, 8-bit unsigned int:
25753         1: {
25754             getValue: function (dataView, dataOffset) {
25755                 return dataView.getUint8(dataOffset);
25756             },
25757             size: 1
25758         },
25759         // ascii, 8-bit byte:
25760         2: {
25761             getValue: function (dataView, dataOffset) {
25762                 return String.fromCharCode(dataView.getUint8(dataOffset));
25763             },
25764             size: 1,
25765             ascii: true
25766         },
25767         // short, 16 bit int:
25768         3: {
25769             getValue: function (dataView, dataOffset, littleEndian) {
25770                 return dataView.getUint16(dataOffset, littleEndian);
25771             },
25772             size: 2
25773         },
25774         // long, 32 bit int:
25775         4: {
25776             getValue: function (dataView, dataOffset, littleEndian) {
25777                 return dataView.getUint32(dataOffset, littleEndian);
25778             },
25779             size: 4
25780         },
25781         // rational = two long values, first is numerator, second is denominator:
25782         5: {
25783             getValue: function (dataView, dataOffset, littleEndian) {
25784                 return dataView.getUint32(dataOffset, littleEndian) /
25785                     dataView.getUint32(dataOffset + 4, littleEndian);
25786             },
25787             size: 8
25788         },
25789         // slong, 32 bit signed int:
25790         9: {
25791             getValue: function (dataView, dataOffset, littleEndian) {
25792                 return dataView.getInt32(dataOffset, littleEndian);
25793             },
25794             size: 4
25795         },
25796         // srational, two slongs, first is numerator, second is denominator:
25797         10: {
25798             getValue: function (dataView, dataOffset, littleEndian) {
25799                 return dataView.getInt32(dataOffset, littleEndian) /
25800                     dataView.getInt32(dataOffset + 4, littleEndian);
25801             },
25802             size: 8
25803         }
25804     },
25805     
25806     footer : {
25807         STANDARD : [
25808             {
25809                 tag : 'div',
25810                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25811                 action : 'rotate-left',
25812                 cn : [
25813                     {
25814                         tag : 'button',
25815                         cls : 'btn btn-default',
25816                         html : '<i class="fa fa-undo"></i>'
25817                     }
25818                 ]
25819             },
25820             {
25821                 tag : 'div',
25822                 cls : 'btn-group roo-upload-cropbox-picture',
25823                 action : 'picture',
25824                 cn : [
25825                     {
25826                         tag : 'button',
25827                         cls : 'btn btn-default',
25828                         html : '<i class="fa fa-picture-o"></i>'
25829                     }
25830                 ]
25831             },
25832             {
25833                 tag : 'div',
25834                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25835                 action : 'rotate-right',
25836                 cn : [
25837                     {
25838                         tag : 'button',
25839                         cls : 'btn btn-default',
25840                         html : '<i class="fa fa-repeat"></i>'
25841                     }
25842                 ]
25843             }
25844         ],
25845         DOCUMENT : [
25846             {
25847                 tag : 'div',
25848                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25849                 action : 'rotate-left',
25850                 cn : [
25851                     {
25852                         tag : 'button',
25853                         cls : 'btn btn-default',
25854                         html : '<i class="fa fa-undo"></i>'
25855                     }
25856                 ]
25857             },
25858             {
25859                 tag : 'div',
25860                 cls : 'btn-group roo-upload-cropbox-download',
25861                 action : 'download',
25862                 cn : [
25863                     {
25864                         tag : 'button',
25865                         cls : 'btn btn-default',
25866                         html : '<i class="fa fa-download"></i>'
25867                     }
25868                 ]
25869             },
25870             {
25871                 tag : 'div',
25872                 cls : 'btn-group roo-upload-cropbox-crop',
25873                 action : 'crop',
25874                 cn : [
25875                     {
25876                         tag : 'button',
25877                         cls : 'btn btn-default',
25878                         html : '<i class="fa fa-crop"></i>'
25879                     }
25880                 ]
25881             },
25882             {
25883                 tag : 'div',
25884                 cls : 'btn-group roo-upload-cropbox-trash',
25885                 action : 'trash',
25886                 cn : [
25887                     {
25888                         tag : 'button',
25889                         cls : 'btn btn-default',
25890                         html : '<i class="fa fa-trash"></i>'
25891                     }
25892                 ]
25893             },
25894             {
25895                 tag : 'div',
25896                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25897                 action : 'rotate-right',
25898                 cn : [
25899                     {
25900                         tag : 'button',
25901                         cls : 'btn btn-default',
25902                         html : '<i class="fa fa-repeat"></i>'
25903                     }
25904                 ]
25905             }
25906         ],
25907         ROTATOR : [
25908             {
25909                 tag : 'div',
25910                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25911                 action : 'rotate-left',
25912                 cn : [
25913                     {
25914                         tag : 'button',
25915                         cls : 'btn btn-default',
25916                         html : '<i class="fa fa-undo"></i>'
25917                     }
25918                 ]
25919             },
25920             {
25921                 tag : 'div',
25922                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25923                 action : 'rotate-right',
25924                 cn : [
25925                     {
25926                         tag : 'button',
25927                         cls : 'btn btn-default',
25928                         html : '<i class="fa fa-repeat"></i>'
25929                     }
25930                 ]
25931             }
25932         ]
25933     }
25934 });
25935
25936 /*
25937 * Licence: LGPL
25938 */
25939
25940 /**
25941  * @class Roo.bootstrap.DocumentManager
25942  * @extends Roo.bootstrap.Component
25943  * Bootstrap DocumentManager class
25944  * @cfg {String} paramName default 'imageUpload'
25945  * @cfg {String} method default POST
25946  * @cfg {String} url action url
25947  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
25948  * @cfg {Boolean} multiple multiple upload default true
25949  * @cfg {Number} thumbSize default 300
25950  * @cfg {String} fieldLabel
25951  * @cfg {Number} labelWidth default 4
25952  * @cfg {String} labelAlign (left|top) default left
25953  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
25954  * 
25955  * @constructor
25956  * Create a new DocumentManager
25957  * @param {Object} config The config object
25958  */
25959
25960 Roo.bootstrap.DocumentManager = function(config){
25961     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25962     
25963     this.addEvents({
25964         /**
25965          * @event initial
25966          * Fire when initial the DocumentManager
25967          * @param {Roo.bootstrap.DocumentManager} this
25968          */
25969         "initial" : true,
25970         /**
25971          * @event inspect
25972          * inspect selected file
25973          * @param {Roo.bootstrap.DocumentManager} this
25974          * @param {File} file
25975          */
25976         "inspect" : true,
25977         /**
25978          * @event exception
25979          * Fire when xhr load exception
25980          * @param {Roo.bootstrap.DocumentManager} this
25981          * @param {XMLHttpRequest} xhr
25982          */
25983         "exception" : true,
25984         /**
25985          * @event prepare
25986          * prepare the form data
25987          * @param {Roo.bootstrap.DocumentManager} this
25988          * @param {Object} formData
25989          */
25990         "prepare" : true,
25991         /**
25992          * @event remove
25993          * Fire when remove the file
25994          * @param {Roo.bootstrap.DocumentManager} this
25995          * @param {Object} file
25996          */
25997         "remove" : true,
25998         /**
25999          * @event refresh
26000          * Fire after refresh the file
26001          * @param {Roo.bootstrap.DocumentManager} this
26002          */
26003         "refresh" : true,
26004         /**
26005          * @event click
26006          * Fire after click the image
26007          * @param {Roo.bootstrap.DocumentManager} this
26008          * @param {Object} file
26009          */
26010         "click" : true,
26011         /**
26012          * @event edit
26013          * Fire when upload a image and editable set to true
26014          * @param {Roo.bootstrap.DocumentManager} this
26015          * @param {Object} file
26016          */
26017         "edit" : true,
26018         /**
26019          * @event beforeselectfile
26020          * Fire before select file
26021          * @param {Roo.bootstrap.DocumentManager} this
26022          */
26023         "beforeselectfile" : true,
26024         /**
26025          * @event process
26026          * Fire before process file
26027          * @param {Roo.bootstrap.DocumentManager} this
26028          * @param {Object} file
26029          */
26030         "process" : true
26031         
26032     });
26033 };
26034
26035 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26036     
26037     boxes : 0,
26038     inputName : '',
26039     thumbSize : 300,
26040     multiple : true,
26041     files : [],
26042     method : 'POST',
26043     url : '',
26044     paramName : 'imageUpload',
26045     fieldLabel : '',
26046     labelWidth : 4,
26047     labelAlign : 'left',
26048     editable : true,
26049     delegates : [],
26050     
26051     getAutoCreate : function()
26052     {   
26053         var managerWidget = {
26054             tag : 'div',
26055             cls : 'roo-document-manager',
26056             cn : [
26057                 {
26058                     tag : 'input',
26059                     cls : 'roo-document-manager-selector',
26060                     type : 'file'
26061                 },
26062                 {
26063                     tag : 'div',
26064                     cls : 'roo-document-manager-uploader',
26065                     cn : [
26066                         {
26067                             tag : 'div',
26068                             cls : 'roo-document-manager-upload-btn',
26069                             html : '<i class="fa fa-plus"></i>'
26070                         }
26071                     ]
26072                     
26073                 }
26074             ]
26075         };
26076         
26077         var content = [
26078             {
26079                 tag : 'div',
26080                 cls : 'column col-md-12',
26081                 cn : managerWidget
26082             }
26083         ];
26084         
26085         if(this.fieldLabel.length){
26086             
26087             content = [
26088                 {
26089                     tag : 'div',
26090                     cls : 'column col-md-12',
26091                     html : this.fieldLabel
26092                 },
26093                 {
26094                     tag : 'div',
26095                     cls : 'column col-md-12',
26096                     cn : managerWidget
26097                 }
26098             ];
26099
26100             if(this.labelAlign == 'left'){
26101                 content = [
26102                     {
26103                         tag : 'div',
26104                         cls : 'column col-md-' + this.labelWidth,
26105                         html : this.fieldLabel
26106                     },
26107                     {
26108                         tag : 'div',
26109                         cls : 'column col-md-' + (12 - this.labelWidth),
26110                         cn : managerWidget
26111                     }
26112                 ];
26113                 
26114             }
26115         }
26116         
26117         var cfg = {
26118             tag : 'div',
26119             cls : 'row clearfix',
26120             cn : content
26121         };
26122         
26123         return cfg;
26124         
26125     },
26126     
26127     initEvents : function()
26128     {
26129         this.managerEl = this.el.select('.roo-document-manager', true).first();
26130         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26131         
26132         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26133         this.selectorEl.hide();
26134         
26135         if(this.multiple){
26136             this.selectorEl.attr('multiple', 'multiple');
26137         }
26138         
26139         this.selectorEl.on('change', this.onFileSelected, this);
26140         
26141         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26142         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26143         
26144         this.uploader.on('click', this.onUploaderClick, this);
26145         
26146         this.renderProgressDialog();
26147         
26148         var _this = this;
26149         
26150         window.addEventListener("resize", function() { _this.refresh(); } );
26151         
26152         this.fireEvent('initial', this);
26153     },
26154     
26155     renderProgressDialog : function()
26156     {
26157         var _this = this;
26158         
26159         this.progressDialog = new Roo.bootstrap.Modal({
26160             cls : 'roo-document-manager-progress-dialog',
26161             allow_close : false,
26162             title : '',
26163             buttons : [
26164                 {
26165                     name  :'cancel',
26166                     weight : 'danger',
26167                     html : 'Cancel'
26168                 }
26169             ], 
26170             listeners : { 
26171                 btnclick : function() {
26172                     _this.uploadCancel();
26173                     this.hide();
26174                 }
26175             }
26176         });
26177          
26178         this.progressDialog.render(Roo.get(document.body));
26179          
26180         this.progress = new Roo.bootstrap.Progress({
26181             cls : 'roo-document-manager-progress',
26182             active : true,
26183             striped : true
26184         });
26185         
26186         this.progress.render(this.progressDialog.getChildContainer());
26187         
26188         this.progressBar = new Roo.bootstrap.ProgressBar({
26189             cls : 'roo-document-manager-progress-bar',
26190             aria_valuenow : 0,
26191             aria_valuemin : 0,
26192             aria_valuemax : 12,
26193             panel : 'success'
26194         });
26195         
26196         this.progressBar.render(this.progress.getChildContainer());
26197     },
26198     
26199     onUploaderClick : function(e)
26200     {
26201         e.preventDefault();
26202      
26203         if(this.fireEvent('beforeselectfile', this) != false){
26204             this.selectorEl.dom.click();
26205         }
26206         
26207     },
26208     
26209     onFileSelected : function(e)
26210     {
26211         e.preventDefault();
26212         
26213         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26214             return;
26215         }
26216         
26217         Roo.each(this.selectorEl.dom.files, function(file){
26218             if(this.fireEvent('inspect', this, file) != false){
26219                 this.files.push(file);
26220             }
26221         }, this);
26222         
26223         this.queue();
26224         
26225     },
26226     
26227     queue : function()
26228     {
26229         this.selectorEl.dom.value = '';
26230         
26231         if(!this.files.length){
26232             return;
26233         }
26234         
26235         if(this.boxes > 0 && this.files.length > this.boxes){
26236             this.files = this.files.slice(0, this.boxes);
26237         }
26238         
26239         this.uploader.show();
26240         
26241         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26242             this.uploader.hide();
26243         }
26244         
26245         var _this = this;
26246         
26247         var files = [];
26248         
26249         var docs = [];
26250         
26251         Roo.each(this.files, function(file){
26252             
26253             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26254                 var f = this.renderPreview(file);
26255                 files.push(f);
26256                 return;
26257             }
26258             
26259             if(file.type.indexOf('image') != -1){
26260                 this.delegates.push(
26261                     (function(){
26262                         _this.process(file);
26263                     }).createDelegate(this)
26264                 );
26265         
26266                 return;
26267             }
26268             
26269             docs.push(
26270                 (function(){
26271                     _this.process(file);
26272                 }).createDelegate(this)
26273             );
26274             
26275         }, this);
26276         
26277         this.files = files;
26278         
26279         this.delegates = this.delegates.concat(docs);
26280         
26281         if(!this.delegates.length){
26282             this.refresh();
26283             return;
26284         }
26285         
26286         this.progressBar.aria_valuemax = this.delegates.length;
26287         
26288         this.arrange();
26289         
26290         return;
26291     },
26292     
26293     arrange : function()
26294     {
26295         if(!this.delegates.length){
26296             this.progressDialog.hide();
26297             this.refresh();
26298             return;
26299         }
26300         
26301         var delegate = this.delegates.shift();
26302         
26303         this.progressDialog.show();
26304         
26305         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26306         
26307         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26308         
26309         delegate();
26310     },
26311     
26312     refresh : function()
26313     {
26314         this.uploader.show();
26315         
26316         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26317             this.uploader.hide();
26318         }
26319         
26320         Roo.isTouch ? this.closable(false) : this.closable(true);
26321         
26322         this.fireEvent('refresh', this);
26323     },
26324     
26325     onRemove : function(e, el, o)
26326     {
26327         e.preventDefault();
26328         
26329         this.fireEvent('remove', this, o);
26330         
26331     },
26332     
26333     remove : function(o)
26334     {
26335         var files = [];
26336         
26337         Roo.each(this.files, function(file){
26338             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26339                 files.push(file);
26340                 return;
26341             }
26342
26343             o.target.remove();
26344
26345         }, this);
26346         
26347         this.files = files;
26348         
26349         this.refresh();
26350     },
26351     
26352     clear : function()
26353     {
26354         Roo.each(this.files, function(file){
26355             if(!file.target){
26356                 return;
26357             }
26358             
26359             file.target.remove();
26360
26361         }, this);
26362         
26363         this.files = [];
26364         
26365         this.refresh();
26366     },
26367     
26368     onClick : function(e, el, o)
26369     {
26370         e.preventDefault();
26371         
26372         this.fireEvent('click', this, o);
26373         
26374     },
26375     
26376     closable : function(closable)
26377     {
26378         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26379             
26380             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26381             
26382             if(closable){
26383                 el.show();
26384                 return;
26385             }
26386             
26387             el.hide();
26388             
26389         }, this);
26390     },
26391     
26392     xhrOnLoad : function(xhr)
26393     {
26394         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26395             el.remove();
26396         }, this);
26397         
26398         if (xhr.readyState !== 4) {
26399             this.arrange();
26400             this.fireEvent('exception', this, xhr);
26401             return;
26402         }
26403
26404         var response = Roo.decode(xhr.responseText);
26405         
26406         if(!response.success){
26407             this.arrange();
26408             this.fireEvent('exception', this, xhr);
26409             return;
26410         }
26411         
26412         var file = this.renderPreview(response.data);
26413         
26414         this.files.push(file);
26415         
26416         this.arrange();
26417         
26418     },
26419     
26420     xhrOnError : function()
26421     {
26422         Roo.log('xhr on error');
26423         
26424         var response = Roo.decode(xhr.responseText);
26425           
26426         Roo.log(response);
26427         
26428         this.arrange();
26429     },
26430     
26431     process : function(file)
26432     {
26433         if(this.fireEvent('process', this, file) !== false){
26434             if(this.editable && file.type.indexOf('image') != -1){
26435                 this.fireEvent('edit', this, file);
26436                 return;
26437             }
26438
26439             this.uploadStart(file, false);
26440
26441             return;
26442         }
26443         
26444     },
26445     
26446     uploadStart : function(file, crop)
26447     {
26448         this.xhr = new XMLHttpRequest();
26449         
26450         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26451             this.arrange();
26452             return;
26453         }
26454         
26455         file.xhr = this.xhr;
26456             
26457         this.managerEl.createChild({
26458             tag : 'div',
26459             cls : 'roo-document-manager-loading',
26460             cn : [
26461                 {
26462                     tag : 'div',
26463                     tooltip : file.name,
26464                     cls : 'roo-document-manager-thumb',
26465                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26466                 }
26467             ]
26468
26469         });
26470
26471         this.xhr.open(this.method, this.url, true);
26472         
26473         var headers = {
26474             "Accept": "application/json",
26475             "Cache-Control": "no-cache",
26476             "X-Requested-With": "XMLHttpRequest"
26477         };
26478         
26479         for (var headerName in headers) {
26480             var headerValue = headers[headerName];
26481             if (headerValue) {
26482                 this.xhr.setRequestHeader(headerName, headerValue);
26483             }
26484         }
26485         
26486         var _this = this;
26487         
26488         this.xhr.onload = function()
26489         {
26490             _this.xhrOnLoad(_this.xhr);
26491         }
26492         
26493         this.xhr.onerror = function()
26494         {
26495             _this.xhrOnError(_this.xhr);
26496         }
26497         
26498         var formData = new FormData();
26499
26500         formData.append('returnHTML', 'NO');
26501         
26502         if(crop){
26503             formData.append('crop', crop);
26504         }
26505         
26506         formData.append(this.paramName, file, file.name);
26507         
26508         if(this.fireEvent('prepare', this, formData) != false){
26509             this.xhr.send(formData);
26510         };
26511     },
26512     
26513     uploadCancel : function()
26514     {
26515         this.xhr.abort();
26516         
26517         this.delegates = [];
26518         
26519         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26520             el.remove();
26521         }, this);
26522         
26523         this.arrange();
26524     },
26525     
26526     renderPreview : function(file)
26527     {
26528         if(typeof(file.target) != 'undefined' && file.target){
26529             return file;
26530         }
26531         
26532         var previewEl = this.managerEl.createChild({
26533             tag : 'div',
26534             cls : 'roo-document-manager-preview',
26535             cn : [
26536                 {
26537                     tag : 'div',
26538                     tooltip : file.filename,
26539                     cls : 'roo-document-manager-thumb',
26540                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26541                 },
26542                 {
26543                     tag : 'button',
26544                     cls : 'close',
26545                     html : '<i class="fa fa-times-circle"></i>'
26546                 }
26547             ]
26548         });
26549
26550         var close = previewEl.select('button.close', true).first();
26551
26552         close.on('click', this.onRemove, this, file);
26553
26554         file.target = previewEl;
26555
26556         var image = previewEl.select('img', true).first();
26557         
26558         var _this = this;
26559         
26560         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26561         
26562         image.on('click', this.onClick, this, file);
26563         
26564         return file;
26565         
26566     },
26567     
26568     onPreviewLoad : function(file, image)
26569     {
26570         if(typeof(file.target) == 'undefined' || !file.target){
26571             return;
26572         }
26573         
26574         var width = image.dom.naturalWidth || image.dom.width;
26575         var height = image.dom.naturalHeight || image.dom.height;
26576         
26577         if(width > height){
26578             file.target.addClass('wide');
26579             return;
26580         }
26581         
26582         file.target.addClass('tall');
26583         return;
26584         
26585     },
26586     
26587     uploadFromSource : function(file, crop)
26588     {
26589         this.xhr = new XMLHttpRequest();
26590         
26591         this.managerEl.createChild({
26592             tag : 'div',
26593             cls : 'roo-document-manager-loading',
26594             cn : [
26595                 {
26596                     tag : 'div',
26597                     tooltip : file.name,
26598                     cls : 'roo-document-manager-thumb',
26599                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26600                 }
26601             ]
26602
26603         });
26604
26605         this.xhr.open(this.method, this.url, true);
26606         
26607         var headers = {
26608             "Accept": "application/json",
26609             "Cache-Control": "no-cache",
26610             "X-Requested-With": "XMLHttpRequest"
26611         };
26612         
26613         for (var headerName in headers) {
26614             var headerValue = headers[headerName];
26615             if (headerValue) {
26616                 this.xhr.setRequestHeader(headerName, headerValue);
26617             }
26618         }
26619         
26620         var _this = this;
26621         
26622         this.xhr.onload = function()
26623         {
26624             _this.xhrOnLoad(_this.xhr);
26625         }
26626         
26627         this.xhr.onerror = function()
26628         {
26629             _this.xhrOnError(_this.xhr);
26630         }
26631         
26632         var formData = new FormData();
26633
26634         formData.append('returnHTML', 'NO');
26635         
26636         formData.append('crop', crop);
26637         
26638         if(typeof(file.filename) != 'undefined'){
26639             formData.append('filename', file.filename);
26640         }
26641         
26642         if(typeof(file.mimetype) != 'undefined'){
26643             formData.append('mimetype', file.mimetype);
26644         }
26645         
26646         if(this.fireEvent('prepare', this, formData) != false){
26647             this.xhr.send(formData);
26648         };
26649     }
26650 });
26651
26652 /*
26653 * Licence: LGPL
26654 */
26655
26656 /**
26657  * @class Roo.bootstrap.DocumentViewer
26658  * @extends Roo.bootstrap.Component
26659  * Bootstrap DocumentViewer class
26660  * 
26661  * @constructor
26662  * Create a new DocumentViewer
26663  * @param {Object} config The config object
26664  */
26665
26666 Roo.bootstrap.DocumentViewer = function(config){
26667     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26668     
26669     this.addEvents({
26670         /**
26671          * @event initial
26672          * Fire after initEvent
26673          * @param {Roo.bootstrap.DocumentViewer} this
26674          */
26675         "initial" : true,
26676         /**
26677          * @event click
26678          * Fire after click
26679          * @param {Roo.bootstrap.DocumentViewer} this
26680          */
26681         "click" : true,
26682         /**
26683          * @event trash
26684          * Fire after trash button
26685          * @param {Roo.bootstrap.DocumentViewer} this
26686          */
26687         "trash" : true
26688         
26689     });
26690 };
26691
26692 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
26693     
26694     getAutoCreate : function()
26695     {
26696         var cfg = {
26697             tag : 'div',
26698             cls : 'roo-document-viewer',
26699             cn : [
26700                 {
26701                     tag : 'div',
26702                     cls : 'roo-document-viewer-body',
26703                     cn : [
26704                         {
26705                             tag : 'div',
26706                             cls : 'roo-document-viewer-thumb',
26707                             cn : [
26708                                 {
26709                                     tag : 'img',
26710                                     cls : 'roo-document-viewer-image'
26711                                 }
26712                             ]
26713                         }
26714                     ]
26715                 },
26716                 {
26717                     tag : 'div',
26718                     cls : 'roo-document-viewer-footer',
26719                     cn : {
26720                         tag : 'div',
26721                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26722                         cn : [
26723                             {
26724                                 tag : 'div',
26725                                 cls : 'btn-group',
26726                                 cn : [
26727                                     {
26728                                         tag : 'button',
26729                                         cls : 'btn btn-default roo-document-viewer-trash',
26730                                         html : '<i class="fa fa-trash"></i>'
26731                                     }
26732                                 ]
26733                             }
26734                         ]
26735                     }
26736                 }
26737             ]
26738         };
26739         
26740         return cfg;
26741     },
26742     
26743     initEvents : function()
26744     {
26745         
26746         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26747         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26748         
26749         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26750         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26751         
26752         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26753         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26754         
26755         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26756         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26757         
26758         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26759         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26760         
26761         this.bodyEl.on('click', this.onClick, this);
26762         
26763         this.trashBtn.on('click', this.onTrash, this);
26764         
26765     },
26766     
26767     initial : function()
26768     {
26769 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26770         
26771         
26772         this.fireEvent('initial', this);
26773         
26774     },
26775     
26776     onClick : function(e)
26777     {
26778         e.preventDefault();
26779         
26780         this.fireEvent('click', this);
26781     },
26782     
26783     onTrash : function(e)
26784     {
26785         e.preventDefault();
26786         
26787         this.fireEvent('trash', this);
26788     }
26789     
26790 });
26791 /*
26792  * - LGPL
26793  *
26794  * nav progress bar
26795  * 
26796  */
26797
26798 /**
26799  * @class Roo.bootstrap.NavProgressBar
26800  * @extends Roo.bootstrap.Component
26801  * Bootstrap NavProgressBar class
26802  * 
26803  * @constructor
26804  * Create a new nav progress bar
26805  * @param {Object} config The config object
26806  */
26807
26808 Roo.bootstrap.NavProgressBar = function(config){
26809     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26810
26811     this.bullets = this.bullets || [];
26812    
26813 //    Roo.bootstrap.NavProgressBar.register(this);
26814      this.addEvents({
26815         /**
26816              * @event changed
26817              * Fires when the active item changes
26818              * @param {Roo.bootstrap.NavProgressBar} this
26819              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26820              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
26821          */
26822         'changed': true
26823      });
26824     
26825 };
26826
26827 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
26828     
26829     bullets : [],
26830     barItems : [],
26831     
26832     getAutoCreate : function()
26833     {
26834         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26835         
26836         cfg = {
26837             tag : 'div',
26838             cls : 'roo-navigation-bar-group',
26839             cn : [
26840                 {
26841                     tag : 'div',
26842                     cls : 'roo-navigation-top-bar'
26843                 },
26844                 {
26845                     tag : 'div',
26846                     cls : 'roo-navigation-bullets-bar',
26847                     cn : [
26848                         {
26849                             tag : 'ul',
26850                             cls : 'roo-navigation-bar'
26851                         }
26852                     ]
26853                 },
26854                 
26855                 {
26856                     tag : 'div',
26857                     cls : 'roo-navigation-bottom-bar'
26858                 }
26859             ]
26860             
26861         };
26862         
26863         return cfg;
26864         
26865     },
26866     
26867     initEvents: function() 
26868     {
26869         
26870     },
26871     
26872     onRender : function(ct, position) 
26873     {
26874         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26875         
26876         if(this.bullets.length){
26877             Roo.each(this.bullets, function(b){
26878                this.addItem(b);
26879             }, this);
26880         }
26881         
26882         this.format();
26883         
26884     },
26885     
26886     addItem : function(cfg)
26887     {
26888         var item = new Roo.bootstrap.NavProgressItem(cfg);
26889         
26890         item.parentId = this.id;
26891         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
26892         
26893         if(cfg.html){
26894             var top = new Roo.bootstrap.Element({
26895                 tag : 'div',
26896                 cls : 'roo-navigation-bar-text'
26897             });
26898             
26899             var bottom = new Roo.bootstrap.Element({
26900                 tag : 'div',
26901                 cls : 'roo-navigation-bar-text'
26902             });
26903             
26904             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
26905             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
26906             
26907             var topText = new Roo.bootstrap.Element({
26908                 tag : 'span',
26909                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
26910             });
26911             
26912             var bottomText = new Roo.bootstrap.Element({
26913                 tag : 'span',
26914                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
26915             });
26916             
26917             topText.onRender(top.el, null);
26918             bottomText.onRender(bottom.el, null);
26919             
26920             item.topEl = top;
26921             item.bottomEl = bottom;
26922         }
26923         
26924         this.barItems.push(item);
26925         
26926         return item;
26927     },
26928     
26929     getActive : function()
26930     {
26931         var active = false;
26932         
26933         Roo.each(this.barItems, function(v){
26934             
26935             if (!v.isActive()) {
26936                 return;
26937             }
26938             
26939             active = v;
26940             return false;
26941             
26942         });
26943         
26944         return active;
26945     },
26946     
26947     setActiveItem : function(item)
26948     {
26949         var prev = false;
26950         
26951         Roo.each(this.barItems, function(v){
26952             if (v.rid == item.rid) {
26953                 return ;
26954             }
26955             
26956             if (v.isActive()) {
26957                 v.setActive(false);
26958                 prev = v;
26959             }
26960         });
26961
26962         item.setActive(true);
26963         
26964         this.fireEvent('changed', this, item, prev);
26965     },
26966     
26967     getBarItem: function(rid)
26968     {
26969         var ret = false;
26970         
26971         Roo.each(this.barItems, function(e) {
26972             if (e.rid != rid) {
26973                 return;
26974             }
26975             
26976             ret =  e;
26977             return false;
26978         });
26979         
26980         return ret;
26981     },
26982     
26983     indexOfItem : function(item)
26984     {
26985         var index = false;
26986         
26987         Roo.each(this.barItems, function(v, i){
26988             
26989             if (v.rid != item.rid) {
26990                 return;
26991             }
26992             
26993             index = i;
26994             return false
26995         });
26996         
26997         return index;
26998     },
26999     
27000     setActiveNext : function()
27001     {
27002         var i = this.indexOfItem(this.getActive());
27003         
27004         if (i > this.barItems.length) {
27005             return;
27006         }
27007         
27008         this.setActiveItem(this.barItems[i+1]);
27009     },
27010     
27011     setActivePrev : function()
27012     {
27013         var i = this.indexOfItem(this.getActive());
27014         
27015         if (i  < 1) {
27016             return;
27017         }
27018         
27019         this.setActiveItem(this.barItems[i-1]);
27020     },
27021     
27022     format : function()
27023     {
27024         if(!this.barItems.length){
27025             return;
27026         }
27027      
27028         var width = 100 / this.barItems.length;
27029         
27030         Roo.each(this.barItems, function(i){
27031             i.el.setStyle('width', width + '%');
27032             i.topEl.el.setStyle('width', width + '%');
27033             i.bottomEl.el.setStyle('width', width + '%');
27034         }, this);
27035         
27036     }
27037     
27038 });
27039 /*
27040  * - LGPL
27041  *
27042  * Nav Progress Item
27043  * 
27044  */
27045
27046 /**
27047  * @class Roo.bootstrap.NavProgressItem
27048  * @extends Roo.bootstrap.Component
27049  * Bootstrap NavProgressItem class
27050  * @cfg {String} rid the reference id
27051  * @cfg {Boolean} active (true|false) Is item active default false
27052  * @cfg {Boolean} disabled (true|false) Is item active default false
27053  * @cfg {String} html
27054  * @cfg {String} position (top|bottom) text position default bottom
27055  * @cfg {String} icon show icon instead of number
27056  * 
27057  * @constructor
27058  * Create a new NavProgressItem
27059  * @param {Object} config The config object
27060  */
27061 Roo.bootstrap.NavProgressItem = function(config){
27062     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27063     this.addEvents({
27064         // raw events
27065         /**
27066          * @event click
27067          * The raw click event for the entire grid.
27068          * @param {Roo.bootstrap.NavProgressItem} this
27069          * @param {Roo.EventObject} e
27070          */
27071         "click" : true
27072     });
27073    
27074 };
27075
27076 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27077     
27078     rid : '',
27079     active : false,
27080     disabled : false,
27081     html : '',
27082     position : 'bottom',
27083     icon : false,
27084     
27085     getAutoCreate : function()
27086     {
27087         var iconCls = 'roo-navigation-bar-item-icon';
27088         
27089         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27090         
27091         var cfg = {
27092             tag: 'li',
27093             cls: 'roo-navigation-bar-item',
27094             cn : [
27095                 {
27096                     tag : 'i',
27097                     cls : iconCls
27098                 }
27099             ]
27100         }
27101         
27102         if(this.active){
27103             cfg.cls += ' active';
27104         }
27105         if(this.disabled){
27106             cfg.cls += ' disabled';
27107         }
27108         
27109         return cfg;
27110     },
27111     
27112     disable : function()
27113     {
27114         this.setDisabled(true);
27115     },
27116     
27117     enable : function()
27118     {
27119         this.setDisabled(false);
27120     },
27121     
27122     initEvents: function() 
27123     {
27124         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27125         
27126         this.iconEl.on('click', this.onClick, this);
27127     },
27128     
27129     onClick : function(e)
27130     {
27131         e.preventDefault();
27132         
27133         if(this.disabled){
27134             return;
27135         }
27136         
27137         if(this.fireEvent('click', this, e) === false){
27138             return;
27139         };
27140         
27141         this.parent().setActiveItem(this);
27142     },
27143     
27144     isActive: function () 
27145     {
27146         return this.active;
27147     },
27148     
27149     setActive : function(state)
27150     {
27151         if(this.active == state){
27152             return;
27153         }
27154         
27155         this.active = state;
27156         
27157         if (state) {
27158             this.el.addClass('active');
27159             return;
27160         }
27161         
27162         this.el.removeClass('active');
27163         
27164         return;
27165     },
27166     
27167     setDisabled : function(state)
27168     {
27169         if(this.disabled == state){
27170             return;
27171         }
27172         
27173         this.disabled = state;
27174         
27175         if (state) {
27176             this.el.addClass('disabled');
27177             return;
27178         }
27179         
27180         this.el.removeClass('disabled');
27181     },
27182     
27183     tooltipEl : function()
27184     {
27185         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27186     }
27187 });
27188  
27189
27190