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         this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1969
1970         this.el.on("mouseover", this.onMouseOver, this);
1971         this.el.on("mouseout", this.onMouseOut, this);
1972         
1973         
1974     },
1975     findTargetItem : function(e){
1976         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1977         if(!t){
1978             return false;
1979         }
1980         //Roo.log(t);         Roo.log(t.id);
1981         if(t && t.id){
1982             //Roo.log(this.menuitems);
1983             return this.menuitems.get(t.id);
1984             
1985             //return this.items.get(t.menuItemId);
1986         }
1987         
1988         return false;
1989     },
1990     onClick : function(e){
1991         Roo.log("menu.onClick");
1992         var t = this.findTargetItem(e);
1993         if(!t || t.isContainer){
1994             return;
1995         }
1996         Roo.log(e);
1997         /*
1998         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1999             if(t == this.activeItem && t.shouldDeactivate(e)){
2000                 this.activeItem.deactivate();
2001                 delete this.activeItem;
2002                 return;
2003             }
2004             if(t.canActivate){
2005                 this.setActiveItem(t, true);
2006             }
2007             return;
2008             
2009             
2010         }
2011         */
2012        
2013         Roo.log('pass click event');
2014         
2015         t.onClick(e);
2016         
2017         this.fireEvent("click", this, t, e);
2018         
2019         this.hide();
2020     },
2021      onMouseOver : function(e){
2022         var t  = this.findTargetItem(e);
2023         //Roo.log(t);
2024         //if(t){
2025         //    if(t.canActivate && !t.disabled){
2026         //        this.setActiveItem(t, true);
2027         //    }
2028         //}
2029         
2030         this.fireEvent("mouseover", this, e, t);
2031     },
2032     isVisible : function(){
2033         return !this.hidden;
2034     },
2035      onMouseOut : function(e){
2036         var t  = this.findTargetItem(e);
2037         
2038         //if(t ){
2039         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2040         //        this.activeItem.deactivate();
2041         //        delete this.activeItem;
2042         //    }
2043         //}
2044         this.fireEvent("mouseout", this, e, t);
2045     },
2046     
2047     
2048     /**
2049      * Displays this menu relative to another element
2050      * @param {String/HTMLElement/Roo.Element} element The element to align to
2051      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2052      * the element (defaults to this.defaultAlign)
2053      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2054      */
2055     show : function(el, pos, parentMenu){
2056         this.parentMenu = parentMenu;
2057         if(!this.el){
2058             this.render();
2059         }
2060         this.fireEvent("beforeshow", this);
2061         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2062     },
2063      /**
2064      * Displays this menu at a specific xy position
2065      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2066      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2067      */
2068     showAt : function(xy, parentMenu, /* private: */_e){
2069         this.parentMenu = parentMenu;
2070         if(!this.el){
2071             this.render();
2072         }
2073         if(_e !== false){
2074             this.fireEvent("beforeshow", this);
2075             //xy = this.el.adjustForConstraints(xy);
2076         }
2077         
2078         //this.el.show();
2079         this.hideMenuItems();
2080         this.hidden = false;
2081         this.triggerEl.addClass('open');
2082         
2083         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2084             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2085         }
2086         
2087         this.el.setXY(xy);
2088         this.focus();
2089         this.fireEvent("show", this);
2090     },
2091     
2092     focus : function(){
2093         return;
2094         if(!this.hidden){
2095             this.doFocus.defer(50, this);
2096         }
2097     },
2098
2099     doFocus : function(){
2100         if(!this.hidden){
2101             this.focusEl.focus();
2102         }
2103     },
2104
2105     /**
2106      * Hides this menu and optionally all parent menus
2107      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2108      */
2109     hide : function(deep){
2110         
2111         this.hideMenuItems();
2112         if(this.el && this.isVisible()){
2113             this.fireEvent("beforehide", this);
2114             if(this.activeItem){
2115                 this.activeItem.deactivate();
2116                 this.activeItem = null;
2117             }
2118             this.triggerEl.removeClass('open');;
2119             this.hidden = true;
2120             this.fireEvent("hide", this);
2121         }
2122         if(deep === true && this.parentMenu){
2123             this.parentMenu.hide(true);
2124         }
2125     },
2126     
2127     onTriggerPress  : function(e)
2128     {
2129         
2130         Roo.log('trigger press');
2131         //Roo.log(e.getTarget());
2132        // Roo.log(this.triggerEl.dom);
2133         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2134             return;
2135         }
2136         
2137         if (this.isVisible()) {
2138             Roo.log('hide');
2139             this.hide();
2140         } else {
2141             Roo.log('show');
2142             this.show(this.triggerEl, false, false);
2143         }
2144         
2145         e.stopEvent();
2146     },
2147     
2148          
2149        
2150     
2151     hideMenuItems : function()
2152     {
2153         //$(backdrop).remove()
2154         Roo.select('.open',true).each(function(aa) {
2155             
2156             aa.removeClass('open');
2157           //var parent = getParent($(this))
2158           //var relatedTarget = { relatedTarget: this }
2159           
2160            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2161           //if (e.isDefaultPrevented()) return
2162            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2163         })
2164     },
2165     addxtypeChild : function (tree, cntr) {
2166         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2167           
2168         this.menuitems.add(comp);
2169         return comp;
2170
2171     },
2172     getEl : function()
2173     {
2174         Roo.log(this.el);
2175         return this.el;
2176     }
2177 });
2178
2179  
2180  /*
2181  * - LGPL
2182  *
2183  * menu item
2184  * 
2185  */
2186
2187
2188 /**
2189  * @class Roo.bootstrap.MenuItem
2190  * @extends Roo.bootstrap.Component
2191  * Bootstrap MenuItem class
2192  * @cfg {String} html the menu label
2193  * @cfg {String} href the link
2194  * @cfg {Boolean} preventDefault (true | false) default true
2195  * @cfg {Boolean} isContainer (true | false) default false
2196  * 
2197  * 
2198  * @constructor
2199  * Create a new MenuItem
2200  * @param {Object} config The config object
2201  */
2202
2203
2204 Roo.bootstrap.MenuItem = function(config){
2205     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2206     this.addEvents({
2207         // raw events
2208         /**
2209          * @event click
2210          * The raw click event for the entire grid.
2211          * @param {Roo.bootstrap.MenuItem} this
2212          * @param {Roo.EventObject} e
2213          */
2214         "click" : true
2215     });
2216 };
2217
2218 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2219     
2220     href : false,
2221     html : false,
2222     preventDefault: true,
2223     isContainer : false,
2224     
2225     getAutoCreate : function(){
2226         
2227         if(this.isContainer){
2228             return {
2229                 tag: 'li',
2230                 cls: 'dropdown-menu-item'
2231             };
2232         }
2233         
2234         var cfg= {
2235             tag: 'li',
2236             cls: 'dropdown-menu-item',
2237             cn: [
2238                     {
2239                         tag : 'a',
2240                         href : '#',
2241                         html : 'Link'
2242                     }
2243                 ]
2244         };
2245         if (this.parent().type == 'treeview') {
2246             cfg.cls = 'treeview-menu';
2247         }
2248         
2249         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2250         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2251         return cfg;
2252     },
2253     
2254     initEvents: function() {
2255         
2256         //this.el.select('a').on('click', this.onClick, this);
2257         
2258     },
2259     onClick : function(e)
2260     {
2261         Roo.log('item on click ');
2262         //if(this.preventDefault){
2263         //    e.preventDefault();
2264         //}
2265         //this.parent().hideMenuItems();
2266         
2267         this.fireEvent('click', this, e);
2268     },
2269     getEl : function()
2270     {
2271         return this.el;
2272     }
2273 });
2274
2275  
2276
2277  /*
2278  * - LGPL
2279  *
2280  * menu separator
2281  * 
2282  */
2283
2284
2285 /**
2286  * @class Roo.bootstrap.MenuSeparator
2287  * @extends Roo.bootstrap.Component
2288  * Bootstrap MenuSeparator class
2289  * 
2290  * @constructor
2291  * Create a new MenuItem
2292  * @param {Object} config The config object
2293  */
2294
2295
2296 Roo.bootstrap.MenuSeparator = function(config){
2297     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2298 };
2299
2300 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2301     
2302     getAutoCreate : function(){
2303         var cfg = {
2304             cls: 'divider',
2305             tag : 'li'
2306         };
2307         
2308         return cfg;
2309     }
2310    
2311 });
2312
2313  
2314
2315  
2316 /*
2317 * Licence: LGPL
2318 */
2319
2320 /**
2321  * @class Roo.bootstrap.Modal
2322  * @extends Roo.bootstrap.Component
2323  * Bootstrap Modal class
2324  * @cfg {String} title Title of dialog
2325  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2326  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2327  * @cfg {Boolean} specificTitle default false
2328  * @cfg {Array} buttons Array of buttons or standard button set..
2329  * @cfg {String} buttonPosition (left|right|center) default right
2330  * @cfg {Boolean} animate default true
2331  * @cfg {Boolean} allow_close default true
2332  * 
2333  * @constructor
2334  * Create a new Modal Dialog
2335  * @param {Object} config The config object
2336  */
2337
2338 Roo.bootstrap.Modal = function(config){
2339     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2340     this.addEvents({
2341         // raw events
2342         /**
2343          * @event btnclick
2344          * The raw btnclick event for the button
2345          * @param {Roo.EventObject} e
2346          */
2347         "btnclick" : true
2348     });
2349     this.buttons = this.buttons || [];
2350      
2351     if (this.tmpl) {
2352         this.tmpl = Roo.factory(this.tmpl);
2353     }
2354     
2355 };
2356
2357 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2358     
2359     title : 'test dialog',
2360    
2361     buttons : false,
2362     
2363     // set on load...
2364      
2365     html: false,
2366     
2367     tmp: false,
2368     
2369     specificTitle: false,
2370     
2371     buttonPosition: 'right',
2372     
2373     allow_close : true,
2374     
2375     animate : true,
2376     
2377     
2378      // private
2379     bodyEl:  false,
2380     footerEl:  false,
2381     titleEl:  false,
2382     closeEl:  false,
2383     
2384     
2385     onRender : function(ct, position)
2386     {
2387         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2388      
2389         if(!this.el){
2390             var cfg = Roo.apply({},  this.getAutoCreate());
2391             cfg.id = Roo.id();
2392             //if(!cfg.name){
2393             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2394             //}
2395             //if (!cfg.name.length) {
2396             //    delete cfg.name;
2397            // }
2398             if (this.cls) {
2399                 cfg.cls += ' ' + this.cls;
2400             }
2401             if (this.style) {
2402                 cfg.style = this.style;
2403             }
2404             this.el = Roo.get(document.body).createChild(cfg, position);
2405         }
2406         //var type = this.el.dom.type;
2407         
2408         
2409         
2410         
2411         if(this.tabIndex !== undefined){
2412             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2413         }
2414         
2415         
2416         this.bodyEl = this.el.select('.modal-body',true).first();
2417         this.closeEl = this.el.select('.modal-header .close', true).first();
2418         this.footerEl = this.el.select('.modal-footer',true).first();
2419         this.titleEl = this.el.select('.modal-title',true).first();
2420         
2421         
2422          
2423         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2424         this.maskEl.enableDisplayMode("block");
2425         this.maskEl.hide();
2426         //this.el.addClass("x-dlg-modal");
2427     
2428         if (this.buttons.length) {
2429             Roo.each(this.buttons, function(bb) {
2430                 var b = Roo.apply({}, bb);
2431                 b.xns = b.xns || Roo.bootstrap;
2432                 b.xtype = b.xtype || 'Button';
2433                 if (typeof(b.listeners) == 'undefined') {
2434                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2435                 }
2436                 
2437                 var btn = Roo.factory(b);
2438                 
2439                 btn.onRender(this.el.select('.modal-footer div').first());
2440                 
2441             },this);
2442         }
2443         // render the children.
2444         var nitems = [];
2445         
2446         if(typeof(this.items) != 'undefined'){
2447             var items = this.items;
2448             delete this.items;
2449
2450             for(var i =0;i < items.length;i++) {
2451                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2452             }
2453         }
2454         
2455         this.items = nitems;
2456         
2457         // where are these used - they used to be body/close/footer
2458         
2459        
2460         this.initEvents();
2461         //this.el.addClass([this.fieldClass, this.cls]);
2462         
2463     },
2464     
2465     getAutoCreate : function(){
2466         
2467         
2468         var bdy = {
2469                 cls : 'modal-body',
2470                 html : this.html || ''
2471         };
2472         
2473         var title = {
2474             tag: 'h4',
2475             cls : 'modal-title',
2476             html : this.title
2477         };
2478         
2479         if(this.specificTitle){
2480             title = this.title;
2481             
2482         };
2483         
2484         var header = [];
2485         if (this.allow_close) {
2486             header.push({
2487                 tag: 'button',
2488                 cls : 'close',
2489                 html : '&times'
2490             });
2491         }
2492         header.push(title);
2493         
2494         var modal = {
2495             cls: "modal",
2496             style : 'display: none',
2497             cn : [
2498                 {
2499                     cls: "modal-dialog",
2500                     cn : [
2501                         {
2502                             cls : "modal-content",
2503                             cn : [
2504                                 {
2505                                     cls : 'modal-header',
2506                                     cn : header
2507                                 },
2508                                 bdy,
2509                                 {
2510                                     cls : 'modal-footer',
2511                                     cn : [
2512                                         {
2513                                             tag: 'div',
2514                                             cls: 'btn-' + this.buttonPosition
2515                                         }
2516                                     ]
2517                                     
2518                                 }
2519                                 
2520                                 
2521                             ]
2522                             
2523                         }
2524                     ]
2525                         
2526                 }
2527             ]
2528         };
2529         
2530         if(this.animate){
2531             modal.cls += ' fade';
2532         }
2533         
2534         return modal;
2535           
2536     },
2537     getChildContainer : function() {
2538          
2539          return this.bodyEl;
2540         
2541     },
2542     getButtonContainer : function() {
2543          return this.el.select('.modal-footer div',true).first();
2544         
2545     },
2546     initEvents : function()
2547     {
2548         if (this.allow_close) {
2549             this.closeEl.on('click', this.hide, this);
2550         }
2551         
2552         var _this = this;
2553         
2554         window.addEventListener("resize", function() { _this.resize(); } );
2555
2556     },
2557     
2558     resize : function()
2559     {
2560         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2561     },
2562     
2563     show : function() {
2564         
2565         if (!this.rendered) {
2566             this.render();
2567         }
2568         
2569         this.el.setStyle('display', 'block');
2570         
2571         if(this.animate){
2572             var _this = this;
2573             (function(){ _this.el.addClass('in'); }).defer(50);
2574         }else{
2575             this.el.addClass('in');
2576         }
2577         
2578         // not sure how we can show data in here.. 
2579         //if (this.tmpl) {
2580         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2581         //}
2582         
2583         Roo.get(document.body).addClass("x-body-masked");
2584         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2585         this.maskEl.show();
2586         this.el.setStyle('zIndex', '10001');
2587        
2588         this.fireEvent('show', this);
2589         
2590         
2591     },
2592     hide : function()
2593     {
2594         this.maskEl.hide();
2595         Roo.get(document.body).removeClass("x-body-masked");
2596         this.el.removeClass('in');
2597         
2598         if(this.animate){
2599             var _this = this;
2600             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2601         }else{
2602             this.el.setStyle('display', 'none');
2603         }
2604         
2605         this.fireEvent('hide', this);
2606     },
2607     
2608     addButton : function(str, cb)
2609     {
2610          
2611         
2612         var b = Roo.apply({}, { html : str } );
2613         b.xns = b.xns || Roo.bootstrap;
2614         b.xtype = b.xtype || 'Button';
2615         if (typeof(b.listeners) == 'undefined') {
2616             b.listeners = { click : cb.createDelegate(this)  };
2617         }
2618         
2619         var btn = Roo.factory(b);
2620            
2621         btn.onRender(this.el.select('.modal-footer div').first());
2622         
2623         return btn;   
2624        
2625     },
2626     
2627     setDefaultButton : function(btn)
2628     {
2629         //this.el.select('.modal-footer').()
2630     },
2631     resizeTo: function(w,h)
2632     {
2633         // skip..
2634     },
2635     setContentSize  : function(w, h)
2636     {
2637         
2638     },
2639     onButtonClick: function(btn,e)
2640     {
2641         //Roo.log([a,b,c]);
2642         this.fireEvent('btnclick', btn.name, e);
2643     },
2644      /**
2645      * Set the title of the Dialog
2646      * @param {String} str new Title
2647      */
2648     setTitle: function(str) {
2649         this.titleEl.dom.innerHTML = str;    
2650     },
2651     /**
2652      * Set the body of the Dialog
2653      * @param {String} str new Title
2654      */
2655     setBody: function(str) {
2656         this.bodyEl.dom.innerHTML = str;    
2657     },
2658     /**
2659      * Set the body of the Dialog using the template
2660      * @param {Obj} data - apply this data to the template and replace the body contents.
2661      */
2662     applyBody: function(obj)
2663     {
2664         if (!this.tmpl) {
2665             Roo.log("Error - using apply Body without a template");
2666             //code
2667         }
2668         this.tmpl.overwrite(this.bodyEl, obj);
2669     }
2670     
2671 });
2672
2673
2674 Roo.apply(Roo.bootstrap.Modal,  {
2675     /**
2676          * Button config that displays a single OK button
2677          * @type Object
2678          */
2679         OK :  [{
2680             name : 'ok',
2681             weight : 'primary',
2682             html : 'OK'
2683         }], 
2684         /**
2685          * Button config that displays Yes and No buttons
2686          * @type Object
2687          */
2688         YESNO : [
2689             {
2690                 name  : 'no',
2691                 html : 'No'
2692             },
2693             {
2694                 name  :'yes',
2695                 weight : 'primary',
2696                 html : 'Yes'
2697             }
2698         ],
2699         
2700         /**
2701          * Button config that displays OK and Cancel buttons
2702          * @type Object
2703          */
2704         OKCANCEL : [
2705             {
2706                name : 'cancel',
2707                 html : 'Cancel'
2708             },
2709             {
2710                 name : 'ok',
2711                 weight : 'primary',
2712                 html : 'OK'
2713             }
2714         ],
2715         /**
2716          * Button config that displays Yes, No and Cancel buttons
2717          * @type Object
2718          */
2719         YESNOCANCEL : [
2720             {
2721                 name : 'yes',
2722                 weight : 'primary',
2723                 html : 'Yes'
2724             },
2725             {
2726                 name : 'no',
2727                 html : 'No'
2728             },
2729             {
2730                 name : 'cancel',
2731                 html : 'Cancel'
2732             }
2733         ]
2734 });
2735  
2736  /*
2737  * - LGPL
2738  *
2739  * messagebox - can be used as a replace
2740  * 
2741  */
2742 /**
2743  * @class Roo.MessageBox
2744  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2745  * Example usage:
2746  *<pre><code>
2747 // Basic alert:
2748 Roo.Msg.alert('Status', 'Changes saved successfully.');
2749
2750 // Prompt for user data:
2751 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2752     if (btn == 'ok'){
2753         // process text value...
2754     }
2755 });
2756
2757 // Show a dialog using config options:
2758 Roo.Msg.show({
2759    title:'Save Changes?',
2760    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2761    buttons: Roo.Msg.YESNOCANCEL,
2762    fn: processResult,
2763    animEl: 'elId'
2764 });
2765 </code></pre>
2766  * @singleton
2767  */
2768 Roo.bootstrap.MessageBox = function(){
2769     var dlg, opt, mask, waitTimer;
2770     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2771     var buttons, activeTextEl, bwidth;
2772
2773     
2774     // private
2775     var handleButton = function(button){
2776         dlg.hide();
2777         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2778     };
2779
2780     // private
2781     var handleHide = function(){
2782         if(opt && opt.cls){
2783             dlg.el.removeClass(opt.cls);
2784         }
2785         //if(waitTimer){
2786         //    Roo.TaskMgr.stop(waitTimer);
2787         //    waitTimer = null;
2788         //}
2789     };
2790
2791     // private
2792     var updateButtons = function(b){
2793         var width = 0;
2794         if(!b){
2795             buttons["ok"].hide();
2796             buttons["cancel"].hide();
2797             buttons["yes"].hide();
2798             buttons["no"].hide();
2799             //dlg.footer.dom.style.display = 'none';
2800             return width;
2801         }
2802         dlg.footerEl.dom.style.display = '';
2803         for(var k in buttons){
2804             if(typeof buttons[k] != "function"){
2805                 if(b[k]){
2806                     buttons[k].show();
2807                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2808                     width += buttons[k].el.getWidth()+15;
2809                 }else{
2810                     buttons[k].hide();
2811                 }
2812             }
2813         }
2814         return width;
2815     };
2816
2817     // private
2818     var handleEsc = function(d, k, e){
2819         if(opt && opt.closable !== false){
2820             dlg.hide();
2821         }
2822         if(e){
2823             e.stopEvent();
2824         }
2825     };
2826
2827     return {
2828         /**
2829          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2830          * @return {Roo.BasicDialog} The BasicDialog element
2831          */
2832         getDialog : function(){
2833            if(!dlg){
2834                 dlg = new Roo.bootstrap.Modal( {
2835                     //draggable: true,
2836                     //resizable:false,
2837                     //constraintoviewport:false,
2838                     //fixedcenter:true,
2839                     //collapsible : false,
2840                     //shim:true,
2841                     //modal: true,
2842                   //  width:400,
2843                   //  height:100,
2844                     //buttonAlign:"center",
2845                     closeClick : function(){
2846                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2847                             handleButton("no");
2848                         }else{
2849                             handleButton("cancel");
2850                         }
2851                     }
2852                 });
2853                 dlg.render();
2854                 dlg.on("hide", handleHide);
2855                 mask = dlg.mask;
2856                 //dlg.addKeyListener(27, handleEsc);
2857                 buttons = {};
2858                 this.buttons = buttons;
2859                 var bt = this.buttonText;
2860                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2861                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2862                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2863                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2864                 Roo.log(buttons)
2865                 bodyEl = dlg.bodyEl.createChild({
2866
2867                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2868                         '<textarea class="roo-mb-textarea"></textarea>' +
2869                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2870                 });
2871                 msgEl = bodyEl.dom.firstChild;
2872                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2873                 textboxEl.enableDisplayMode();
2874                 textboxEl.addKeyListener([10,13], function(){
2875                     if(dlg.isVisible() && opt && opt.buttons){
2876                         if(opt.buttons.ok){
2877                             handleButton("ok");
2878                         }else if(opt.buttons.yes){
2879                             handleButton("yes");
2880                         }
2881                     }
2882                 });
2883                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2884                 textareaEl.enableDisplayMode();
2885                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2886                 progressEl.enableDisplayMode();
2887                 var pf = progressEl.dom.firstChild;
2888                 if (pf) {
2889                     pp = Roo.get(pf.firstChild);
2890                     pp.setHeight(pf.offsetHeight);
2891                 }
2892                 
2893             }
2894             return dlg;
2895         },
2896
2897         /**
2898          * Updates the message box body text
2899          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2900          * the XHTML-compliant non-breaking space character '&amp;#160;')
2901          * @return {Roo.MessageBox} This message box
2902          */
2903         updateText : function(text){
2904             if(!dlg.isVisible() && !opt.width){
2905                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2906             }
2907             msgEl.innerHTML = text || '&#160;';
2908       
2909             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2910             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2911             var w = Math.max(
2912                     Math.min(opt.width || cw , this.maxWidth), 
2913                     Math.max(opt.minWidth || this.minWidth, bwidth)
2914             );
2915             if(opt.prompt){
2916                 activeTextEl.setWidth(w);
2917             }
2918             if(dlg.isVisible()){
2919                 dlg.fixedcenter = false;
2920             }
2921             // to big, make it scroll. = But as usual stupid IE does not support
2922             // !important..
2923             
2924             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2925                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2926                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2927             } else {
2928                 bodyEl.dom.style.height = '';
2929                 bodyEl.dom.style.overflowY = '';
2930             }
2931             if (cw > w) {
2932                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2933             } else {
2934                 bodyEl.dom.style.overflowX = '';
2935             }
2936             
2937             dlg.setContentSize(w, bodyEl.getHeight());
2938             if(dlg.isVisible()){
2939                 dlg.fixedcenter = true;
2940             }
2941             return this;
2942         },
2943
2944         /**
2945          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2946          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2947          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2948          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2949          * @return {Roo.MessageBox} This message box
2950          */
2951         updateProgress : function(value, text){
2952             if(text){
2953                 this.updateText(text);
2954             }
2955             if (pp) { // weird bug on my firefox - for some reason this is not defined
2956                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2957             }
2958             return this;
2959         },        
2960
2961         /**
2962          * Returns true if the message box is currently displayed
2963          * @return {Boolean} True if the message box is visible, else false
2964          */
2965         isVisible : function(){
2966             return dlg && dlg.isVisible();  
2967         },
2968
2969         /**
2970          * Hides the message box if it is displayed
2971          */
2972         hide : function(){
2973             if(this.isVisible()){
2974                 dlg.hide();
2975             }  
2976         },
2977
2978         /**
2979          * Displays a new message box, or reinitializes an existing message box, based on the config options
2980          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2981          * The following config object properties are supported:
2982          * <pre>
2983 Property    Type             Description
2984 ----------  ---------------  ------------------------------------------------------------------------------------
2985 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2986                                    closes (defaults to undefined)
2987 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2988                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2989 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2990                                    progress and wait dialogs will ignore this property and always hide the
2991                                    close button as they can only be closed programmatically.
2992 cls               String           A custom CSS class to apply to the message box element
2993 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2994                                    displayed (defaults to 75)
2995 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2996                                    function will be btn (the name of the button that was clicked, if applicable,
2997                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2998                                    Progress and wait dialogs will ignore this option since they do not respond to
2999                                    user actions and can only be closed programmatically, so any required function
3000                                    should be called by the same code after it closes the dialog.
3001 icon              String           A CSS class that provides a background image to be used as an icon for
3002                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3003 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3004 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3005 modal             Boolean          False to allow user interaction with the page while the message box is
3006                                    displayed (defaults to true)
3007 msg               String           A string that will replace the existing message box body text (defaults
3008                                    to the XHTML-compliant non-breaking space character '&#160;')
3009 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3010 progress          Boolean          True to display a progress bar (defaults to false)
3011 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3012 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3013 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3014 title             String           The title text
3015 value             String           The string value to set into the active textbox element if displayed
3016 wait              Boolean          True to display a progress bar (defaults to false)
3017 width             Number           The width of the dialog in pixels
3018 </pre>
3019          *
3020          * Example usage:
3021          * <pre><code>
3022 Roo.Msg.show({
3023    title: 'Address',
3024    msg: 'Please enter your address:',
3025    width: 300,
3026    buttons: Roo.MessageBox.OKCANCEL,
3027    multiline: true,
3028    fn: saveAddress,
3029    animEl: 'addAddressBtn'
3030 });
3031 </code></pre>
3032          * @param {Object} config Configuration options
3033          * @return {Roo.MessageBox} This message box
3034          */
3035         show : function(options)
3036         {
3037             
3038             // this causes nightmares if you show one dialog after another
3039             // especially on callbacks..
3040              
3041             if(this.isVisible()){
3042                 
3043                 this.hide();
3044                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3045                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3046                 Roo.log("New Dialog Message:" +  options.msg )
3047                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3048                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3049                 
3050             }
3051             var d = this.getDialog();
3052             opt = options;
3053             d.setTitle(opt.title || "&#160;");
3054             d.closeEl.setDisplayed(opt.closable !== false);
3055             activeTextEl = textboxEl;
3056             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3057             if(opt.prompt){
3058                 if(opt.multiline){
3059                     textboxEl.hide();
3060                     textareaEl.show();
3061                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3062                         opt.multiline : this.defaultTextHeight);
3063                     activeTextEl = textareaEl;
3064                 }else{
3065                     textboxEl.show();
3066                     textareaEl.hide();
3067                 }
3068             }else{
3069                 textboxEl.hide();
3070                 textareaEl.hide();
3071             }
3072             progressEl.setDisplayed(opt.progress === true);
3073             this.updateProgress(0);
3074             activeTextEl.dom.value = opt.value || "";
3075             if(opt.prompt){
3076                 dlg.setDefaultButton(activeTextEl);
3077             }else{
3078                 var bs = opt.buttons;
3079                 var db = null;
3080                 if(bs && bs.ok){
3081                     db = buttons["ok"];
3082                 }else if(bs && bs.yes){
3083                     db = buttons["yes"];
3084                 }
3085                 dlg.setDefaultButton(db);
3086             }
3087             bwidth = updateButtons(opt.buttons);
3088             this.updateText(opt.msg);
3089             if(opt.cls){
3090                 d.el.addClass(opt.cls);
3091             }
3092             d.proxyDrag = opt.proxyDrag === true;
3093             d.modal = opt.modal !== false;
3094             d.mask = opt.modal !== false ? mask : false;
3095             if(!d.isVisible()){
3096                 // force it to the end of the z-index stack so it gets a cursor in FF
3097                 document.body.appendChild(dlg.el.dom);
3098                 d.animateTarget = null;
3099                 d.show(options.animEl);
3100             }
3101             return this;
3102         },
3103
3104         /**
3105          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3106          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3107          * and closing the message box when the process is complete.
3108          * @param {String} title The title bar text
3109          * @param {String} msg The message box body text
3110          * @return {Roo.MessageBox} This message box
3111          */
3112         progress : function(title, msg){
3113             this.show({
3114                 title : title,
3115                 msg : msg,
3116                 buttons: false,
3117                 progress:true,
3118                 closable:false,
3119                 minWidth: this.minProgressWidth,
3120                 modal : true
3121             });
3122             return this;
3123         },
3124
3125         /**
3126          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3127          * If a callback function is passed it will be called after the user clicks the button, and the
3128          * id of the button that was clicked will be passed as the only parameter to the callback
3129          * (could also be the top-right close button).
3130          * @param {String} title The title bar text
3131          * @param {String} msg The message box body text
3132          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3133          * @param {Object} scope (optional) The scope of the callback function
3134          * @return {Roo.MessageBox} This message box
3135          */
3136         alert : function(title, msg, fn, scope){
3137             this.show({
3138                 title : title,
3139                 msg : msg,
3140                 buttons: this.OK,
3141                 fn: fn,
3142                 scope : scope,
3143                 modal : true
3144             });
3145             return this;
3146         },
3147
3148         /**
3149          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3150          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3151          * You are responsible for closing the message box when the process is complete.
3152          * @param {String} msg The message box body text
3153          * @param {String} title (optional) The title bar text
3154          * @return {Roo.MessageBox} This message box
3155          */
3156         wait : function(msg, title){
3157             this.show({
3158                 title : title,
3159                 msg : msg,
3160                 buttons: false,
3161                 closable:false,
3162                 progress:true,
3163                 modal:true,
3164                 width:300,
3165                 wait:true
3166             });
3167             waitTimer = Roo.TaskMgr.start({
3168                 run: function(i){
3169                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3170                 },
3171                 interval: 1000
3172             });
3173             return this;
3174         },
3175
3176         /**
3177          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3178          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3179          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3180          * @param {String} title The title bar text
3181          * @param {String} msg The message box body text
3182          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3183          * @param {Object} scope (optional) The scope of the callback function
3184          * @return {Roo.MessageBox} This message box
3185          */
3186         confirm : function(title, msg, fn, scope){
3187             this.show({
3188                 title : title,
3189                 msg : msg,
3190                 buttons: this.YESNO,
3191                 fn: fn,
3192                 scope : scope,
3193                 modal : true
3194             });
3195             return this;
3196         },
3197
3198         /**
3199          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3200          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3201          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3202          * (could also be the top-right close button) and the text that was entered will be passed as the two
3203          * parameters to the callback.
3204          * @param {String} title The title bar text
3205          * @param {String} msg The message box body text
3206          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3207          * @param {Object} scope (optional) The scope of the callback function
3208          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3209          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3210          * @return {Roo.MessageBox} This message box
3211          */
3212         prompt : function(title, msg, fn, scope, multiline){
3213             this.show({
3214                 title : title,
3215                 msg : msg,
3216                 buttons: this.OKCANCEL,
3217                 fn: fn,
3218                 minWidth:250,
3219                 scope : scope,
3220                 prompt:true,
3221                 multiline: multiline,
3222                 modal : true
3223             });
3224             return this;
3225         },
3226
3227         /**
3228          * Button config that displays a single OK button
3229          * @type Object
3230          */
3231         OK : {ok:true},
3232         /**
3233          * Button config that displays Yes and No buttons
3234          * @type Object
3235          */
3236         YESNO : {yes:true, no:true},
3237         /**
3238          * Button config that displays OK and Cancel buttons
3239          * @type Object
3240          */
3241         OKCANCEL : {ok:true, cancel:true},
3242         /**
3243          * Button config that displays Yes, No and Cancel buttons
3244          * @type Object
3245          */
3246         YESNOCANCEL : {yes:true, no:true, cancel:true},
3247
3248         /**
3249          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3250          * @type Number
3251          */
3252         defaultTextHeight : 75,
3253         /**
3254          * The maximum width in pixels of the message box (defaults to 600)
3255          * @type Number
3256          */
3257         maxWidth : 600,
3258         /**
3259          * The minimum width in pixels of the message box (defaults to 100)
3260          * @type Number
3261          */
3262         minWidth : 100,
3263         /**
3264          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3265          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3266          * @type Number
3267          */
3268         minProgressWidth : 250,
3269         /**
3270          * An object containing the default button text strings that can be overriden for localized language support.
3271          * Supported properties are: ok, cancel, yes and no.
3272          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3273          * @type Object
3274          */
3275         buttonText : {
3276             ok : "OK",
3277             cancel : "Cancel",
3278             yes : "Yes",
3279             no : "No"
3280         }
3281     };
3282 }();
3283
3284 /**
3285  * Shorthand for {@link Roo.MessageBox}
3286  */
3287 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3288 Roo.Msg = Roo.Msg || Roo.MessageBox;
3289 /*
3290  * - LGPL
3291  *
3292  * navbar
3293  * 
3294  */
3295
3296 /**
3297  * @class Roo.bootstrap.Navbar
3298  * @extends Roo.bootstrap.Component
3299  * Bootstrap Navbar class
3300
3301  * @constructor
3302  * Create a new Navbar
3303  * @param {Object} config The config object
3304  */
3305
3306
3307 Roo.bootstrap.Navbar = function(config){
3308     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3309     
3310 };
3311
3312 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3313     
3314     
3315    
3316     // private
3317     navItems : false,
3318     loadMask : false,
3319     
3320     
3321     getAutoCreate : function(){
3322         
3323         
3324         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3325         
3326     },
3327     
3328     initEvents :function ()
3329     {
3330         //Roo.log(this.el.select('.navbar-toggle',true));
3331         this.el.select('.navbar-toggle',true).on('click', function() {
3332            // Roo.log('click');
3333             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3334         }, this);
3335         
3336         var mark = {
3337             tag: "div",
3338             cls:"x-dlg-mask"
3339         }
3340         
3341         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3342         
3343         var size = this.el.getSize();
3344         this.maskEl.setSize(size.width, size.height);
3345         this.maskEl.enableDisplayMode("block");
3346         this.maskEl.hide();
3347         
3348         if(this.loadMask){
3349             this.maskEl.show();
3350         }
3351     },
3352     
3353     
3354     getChildContainer : function()
3355     {
3356         if (this.el.select('.collapse').getCount()) {
3357             return this.el.select('.collapse',true).first();
3358         }
3359         
3360         return this.el;
3361     },
3362     
3363     mask : function()
3364     {
3365         this.maskEl.show();
3366     },
3367     
3368     unmask : function()
3369     {
3370         this.maskEl.hide();
3371     } 
3372     
3373     
3374     
3375     
3376 });
3377
3378
3379
3380  
3381
3382  /*
3383  * - LGPL
3384  *
3385  * navbar
3386  * 
3387  */
3388
3389 /**
3390  * @class Roo.bootstrap.NavSimplebar
3391  * @extends Roo.bootstrap.Navbar
3392  * Bootstrap Sidebar class
3393  *
3394  * @cfg {Boolean} inverse is inverted color
3395  * 
3396  * @cfg {String} type (nav | pills | tabs)
3397  * @cfg {Boolean} arrangement stacked | justified
3398  * @cfg {String} align (left | right) alignment
3399  * 
3400  * @cfg {Boolean} main (true|false) main nav bar? default false
3401  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3402  * 
3403  * @cfg {String} tag (header|footer|nav|div) default is nav 
3404
3405  * 
3406  * 
3407  * 
3408  * @constructor
3409  * Create a new Sidebar
3410  * @param {Object} config The config object
3411  */
3412
3413
3414 Roo.bootstrap.NavSimplebar = function(config){
3415     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3416 };
3417
3418 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3419     
3420     inverse: false,
3421     
3422     type: false,
3423     arrangement: '',
3424     align : false,
3425     
3426     
3427     
3428     main : false,
3429     
3430     
3431     tag : false,
3432     
3433     
3434     getAutoCreate : function(){
3435         
3436         
3437         var cfg = {
3438             tag : this.tag || 'div',
3439             cls : 'navbar'
3440         };
3441           
3442         
3443         cfg.cn = [
3444             {
3445                 cls: 'nav',
3446                 tag : 'ul'
3447             }
3448         ];
3449         
3450          
3451         this.type = this.type || 'nav';
3452         if (['tabs','pills'].indexOf(this.type)!==-1) {
3453             cfg.cn[0].cls += ' nav-' + this.type
3454         
3455         
3456         } else {
3457             if (this.type!=='nav') {
3458                 Roo.log('nav type must be nav/tabs/pills')
3459             }
3460             cfg.cn[0].cls += ' navbar-nav'
3461         }
3462         
3463         
3464         
3465         
3466         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3467             cfg.cn[0].cls += ' nav-' + this.arrangement;
3468         }
3469         
3470         
3471         if (this.align === 'right') {
3472             cfg.cn[0].cls += ' navbar-right';
3473         }
3474         
3475         if (this.inverse) {
3476             cfg.cls += ' navbar-inverse';
3477             
3478         }
3479         
3480         
3481         return cfg;
3482     
3483         
3484     }
3485     
3486     
3487     
3488 });
3489
3490
3491
3492  
3493
3494  
3495        /*
3496  * - LGPL
3497  *
3498  * navbar
3499  * 
3500  */
3501
3502 /**
3503  * @class Roo.bootstrap.NavHeaderbar
3504  * @extends Roo.bootstrap.NavSimplebar
3505  * Bootstrap Sidebar class
3506  *
3507  * @cfg {String} brand what is brand
3508  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3509  * @cfg {String} brand_href href of the brand
3510  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3511  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3512  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3513  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3514  * 
3515  * @constructor
3516  * Create a new Sidebar
3517  * @param {Object} config The config object
3518  */
3519
3520
3521 Roo.bootstrap.NavHeaderbar = function(config){
3522     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3523       
3524 };
3525
3526 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3527     
3528     position: '',
3529     brand: '',
3530     brand_href: false,
3531     srButton : true,
3532     autohide : false,
3533     desktopCenter : false,
3534    
3535     
3536     getAutoCreate : function(){
3537         
3538         var   cfg = {
3539             tag: this.nav || 'nav',
3540             cls: 'navbar',
3541             role: 'navigation',
3542             cn: []
3543         };
3544         
3545         var cn = cfg.cn;
3546         if (this.desktopCenter) {
3547             cn.push({cls : 'container', cn : []});
3548             cn = cn[0].cn;
3549         }
3550         
3551         if(this.srButton){
3552             cn.push({
3553                 tag: 'div',
3554                 cls: 'navbar-header',
3555                 cn: [
3556                     {
3557                         tag: 'button',
3558                         type: 'button',
3559                         cls: 'navbar-toggle',
3560                         'data-toggle': 'collapse',
3561                         cn: [
3562                             {
3563                                 tag: 'span',
3564                                 cls: 'sr-only',
3565                                 html: 'Toggle navigation'
3566                             },
3567                             {
3568                                 tag: 'span',
3569                                 cls: 'icon-bar'
3570                             },
3571                             {
3572                                 tag: 'span',
3573                                 cls: 'icon-bar'
3574                             },
3575                             {
3576                                 tag: 'span',
3577                                 cls: 'icon-bar'
3578                             }
3579                         ]
3580                     }
3581                 ]
3582             });
3583         }
3584         
3585         cn.push({
3586             tag: 'div',
3587             cls: 'collapse navbar-collapse',
3588             cn : []
3589         });
3590         
3591         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3592         
3593         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3594             cfg.cls += ' navbar-' + this.position;
3595             
3596             // tag can override this..
3597             
3598             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3599         }
3600         
3601         if (this.brand !== '') {
3602             cn[0].cn.push({
3603                 tag: 'a',
3604                 href: this.brand_href ? this.brand_href : '#',
3605                 cls: 'navbar-brand',
3606                 cn: [
3607                 this.brand
3608                 ]
3609             });
3610         }
3611         
3612         if(this.main){
3613             cfg.cls += ' main-nav';
3614         }
3615         
3616         
3617         return cfg;
3618
3619         
3620     },
3621     getHeaderChildContainer : function()
3622     {
3623         if (this.el.select('.navbar-header').getCount()) {
3624             return this.el.select('.navbar-header',true).first();
3625         }
3626         
3627         return this.getChildContainer();
3628     },
3629     
3630     
3631     initEvents : function()
3632     {
3633         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3634         
3635         if (this.autohide) {
3636             
3637             var prevScroll = 0;
3638             var ft = this.el;
3639             
3640             Roo.get(document).on('scroll',function(e) {
3641                 var ns = Roo.get(document).getScroll().top;
3642                 var os = prevScroll;
3643                 prevScroll = ns;
3644                 
3645                 if(ns > os){
3646                     ft.removeClass('slideDown');
3647                     ft.addClass('slideUp');
3648                     return;
3649                 }
3650                 ft.removeClass('slideUp');
3651                 ft.addClass('slideDown');
3652                  
3653               
3654           },this);
3655         }
3656     }    
3657     
3658 });
3659
3660
3661
3662  
3663
3664  /*
3665  * - LGPL
3666  *
3667  * navbar
3668  * 
3669  */
3670
3671 /**
3672  * @class Roo.bootstrap.NavSidebar
3673  * @extends Roo.bootstrap.Navbar
3674  * Bootstrap Sidebar class
3675  * 
3676  * @constructor
3677  * Create a new Sidebar
3678  * @param {Object} config The config object
3679  */
3680
3681
3682 Roo.bootstrap.NavSidebar = function(config){
3683     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3684 };
3685
3686 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3687     
3688     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3689     
3690     getAutoCreate : function(){
3691         
3692         
3693         return  {
3694             tag: 'div',
3695             cls: 'sidebar sidebar-nav'
3696         };
3697     
3698         
3699     }
3700     
3701     
3702     
3703 });
3704
3705
3706
3707  
3708
3709  /*
3710  * - LGPL
3711  *
3712  * nav group
3713  * 
3714  */
3715
3716 /**
3717  * @class Roo.bootstrap.NavGroup
3718  * @extends Roo.bootstrap.Component
3719  * Bootstrap NavGroup class
3720  * @cfg {String} align (left|right)
3721  * @cfg {Boolean} inverse
3722  * @cfg {String} type (nav|pills|tab) default nav
3723  * @cfg {String} navId - reference Id for navbar.
3724
3725  * 
3726  * @constructor
3727  * Create a new nav group
3728  * @param {Object} config The config object
3729  */
3730
3731 Roo.bootstrap.NavGroup = function(config){
3732     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3733     this.navItems = [];
3734    
3735     Roo.bootstrap.NavGroup.register(this);
3736      this.addEvents({
3737         /**
3738              * @event changed
3739              * Fires when the active item changes
3740              * @param {Roo.bootstrap.NavGroup} this
3741              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3742              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3743          */
3744         'changed': true
3745      });
3746     
3747 };
3748
3749 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3750     
3751     align: '',
3752     inverse: false,
3753     form: false,
3754     type: 'nav',
3755     navId : '',
3756     // private
3757     
3758     navItems : false, 
3759     
3760     getAutoCreate : function()
3761     {
3762         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3763         
3764         cfg = {
3765             tag : 'ul',
3766             cls: 'nav' 
3767         }
3768         
3769         if (['tabs','pills'].indexOf(this.type)!==-1) {
3770             cfg.cls += ' nav-' + this.type
3771         } else {
3772             if (this.type!=='nav') {
3773                 Roo.log('nav type must be nav/tabs/pills')
3774             }
3775             cfg.cls += ' navbar-nav'
3776         }
3777         
3778         if (this.parent().sidebar) {
3779             cfg = {
3780                 tag: 'ul',
3781                 cls: 'dashboard-menu sidebar-menu'
3782             }
3783             
3784             return cfg;
3785         }
3786         
3787         if (this.form === true) {
3788             cfg = {
3789                 tag: 'form',
3790                 cls: 'navbar-form'
3791             }
3792             
3793             if (this.align === 'right') {
3794                 cfg.cls += ' navbar-right';
3795             } else {
3796                 cfg.cls += ' navbar-left';
3797             }
3798         }
3799         
3800         if (this.align === 'right') {
3801             cfg.cls += ' navbar-right';
3802         }
3803         
3804         if (this.inverse) {
3805             cfg.cls += ' navbar-inverse';
3806             
3807         }
3808         
3809         
3810         return cfg;
3811     },
3812     /**
3813     * sets the active Navigation item
3814     * @param {Roo.bootstrap.NavItem} the new current navitem
3815     */
3816     setActiveItem : function(item)
3817     {
3818         var prev = false;
3819         Roo.each(this.navItems, function(v){
3820             if (v == item) {
3821                 return ;
3822             }
3823             if (v.isActive()) {
3824                 v.setActive(false, true);
3825                 prev = v;
3826                 
3827             }
3828             
3829         });
3830
3831         item.setActive(true, true);
3832         this.fireEvent('changed', this, item, prev);
3833         
3834         
3835     },
3836     /**
3837     * gets the active Navigation item
3838     * @return {Roo.bootstrap.NavItem} the current navitem
3839     */
3840     getActive : function()
3841     {
3842         
3843         var prev = false;
3844         Roo.each(this.navItems, function(v){
3845             
3846             if (v.isActive()) {
3847                 prev = v;
3848                 
3849             }
3850             
3851         });
3852         return prev;
3853     },
3854     
3855     indexOfNav : function()
3856     {
3857         
3858         var prev = false;
3859         Roo.each(this.navItems, function(v,i){
3860             
3861             if (v.isActive()) {
3862                 prev = i;
3863                 
3864             }
3865             
3866         });
3867         return prev;
3868     },
3869     /**
3870     * adds a Navigation item
3871     * @param {Roo.bootstrap.NavItem} the navitem to add
3872     */
3873     addItem : function(cfg)
3874     {
3875         var cn = new Roo.bootstrap.NavItem(cfg);
3876         this.register(cn);
3877         cn.parentId = this.id;
3878         cn.onRender(this.el, null);
3879         return cn;
3880     },
3881     /**
3882     * register a Navigation item
3883     * @param {Roo.bootstrap.NavItem} the navitem to add
3884     */
3885     register : function(item)
3886     {
3887         this.navItems.push( item);
3888         item.navId = this.navId;
3889     
3890     },
3891     
3892     /**
3893     * clear all the Navigation item
3894     */
3895    
3896     clearAll : function()
3897     {
3898         this.navItems = [];
3899         this.el.dom.innerHTML = '';
3900     },
3901     
3902     getNavItem: function(tabId)
3903     {
3904         var ret = false;
3905         Roo.each(this.navItems, function(e) {
3906             if (e.tabId == tabId) {
3907                ret =  e;
3908                return false;
3909             }
3910             return true;
3911             
3912         });
3913         return ret;
3914     },
3915     
3916     setActiveNext : function()
3917     {
3918         var i = this.indexOfNav(this.getActive());
3919         if (i > this.navItems.length) {
3920             return;
3921         }
3922         this.setActiveItem(this.navItems[i+1]);
3923     },
3924     setActivePrev : function()
3925     {
3926         var i = this.indexOfNav(this.getActive());
3927         if (i  < 1) {
3928             return;
3929         }
3930         this.setActiveItem(this.navItems[i-1]);
3931     },
3932     clearWasActive : function(except) {
3933         Roo.each(this.navItems, function(e) {
3934             if (e.tabId != except.tabId && e.was_active) {
3935                e.was_active = false;
3936                return false;
3937             }
3938             return true;
3939             
3940         });
3941     },
3942     getWasActive : function ()
3943     {
3944         var r = false;
3945         Roo.each(this.navItems, function(e) {
3946             if (e.was_active) {
3947                r = e;
3948                return false;
3949             }
3950             return true;
3951             
3952         });
3953         return r;
3954     }
3955     
3956     
3957 });
3958
3959  
3960 Roo.apply(Roo.bootstrap.NavGroup, {
3961     
3962     groups: {},
3963      /**
3964     * register a Navigation Group
3965     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3966     */
3967     register : function(navgrp)
3968     {
3969         this.groups[navgrp.navId] = navgrp;
3970         
3971     },
3972     /**
3973     * fetch a Navigation Group based on the navigation ID
3974     * @param {string} the navgroup to add
3975     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3976     */
3977     get: function(navId) {
3978         if (typeof(this.groups[navId]) == 'undefined') {
3979             return false;
3980             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3981         }
3982         return this.groups[navId] ;
3983     }
3984     
3985     
3986     
3987 });
3988
3989  /*
3990  * - LGPL
3991  *
3992  * row
3993  * 
3994  */
3995
3996 /**
3997  * @class Roo.bootstrap.NavItem
3998  * @extends Roo.bootstrap.Component
3999  * Bootstrap Navbar.NavItem class
4000  * @cfg {String} href  link to
4001  * @cfg {String} html content of button
4002  * @cfg {String} badge text inside badge
4003  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4004  * @cfg {String} glyphicon name of glyphicon
4005  * @cfg {String} icon name of font awesome icon
4006  * @cfg {Boolean} active Is item active
4007  * @cfg {Boolean} disabled Is item disabled
4008  
4009  * @cfg {Boolean} preventDefault (true | false) default false
4010  * @cfg {String} tabId the tab that this item activates.
4011  * @cfg {String} tagtype (a|span) render as a href or span?
4012  * @cfg {Boolean} animateRef (true|false) link to element default false  
4013   
4014  * @constructor
4015  * Create a new Navbar Item
4016  * @param {Object} config The config object
4017  */
4018 Roo.bootstrap.NavItem = function(config){
4019     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4020     this.addEvents({
4021         // raw events
4022         /**
4023          * @event click
4024          * The raw click event for the entire grid.
4025          * @param {Roo.EventObject} e
4026          */
4027         "click" : true,
4028          /**
4029             * @event changed
4030             * Fires when the active item active state changes
4031             * @param {Roo.bootstrap.NavItem} this
4032             * @param {boolean} state the new state
4033              
4034          */
4035         'changed': true,
4036         /**
4037             * @event scrollto
4038             * Fires when scroll to element
4039             * @param {Roo.bootstrap.NavItem} this
4040             * @param {Object} options
4041             * @param {Roo.EventObject} e
4042              
4043          */
4044         'scrollto': true
4045     });
4046    
4047 };
4048
4049 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4050     
4051     href: false,
4052     html: '',
4053     badge: '',
4054     icon: false,
4055     glyphicon: false,
4056     active: false,
4057     preventDefault : false,
4058     tabId : false,
4059     tagtype : 'a',
4060     disabled : false,
4061     animateRef : false,
4062     was_active : false,
4063     
4064     getAutoCreate : function(){
4065          
4066         var cfg = {
4067             tag: 'li',
4068             cls: 'nav-item'
4069             
4070         }
4071         if (this.active) {
4072             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4073         }
4074         if (this.disabled) {
4075             cfg.cls += ' disabled';
4076         }
4077         
4078         if (this.href || this.html || this.glyphicon || this.icon) {
4079             cfg.cn = [
4080                 {
4081                     tag: this.tagtype,
4082                     href : this.href || "#",
4083                     html: this.html || ''
4084                 }
4085             ];
4086             
4087             if (this.icon) {
4088                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4089             }
4090
4091             if(this.glyphicon) {
4092                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4093             }
4094             
4095             if (this.menu) {
4096                 
4097                 cfg.cn[0].html += " <span class='caret'></span>";
4098              
4099             }
4100             
4101             if (this.badge !== '') {
4102                  
4103                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4104             }
4105         }
4106         
4107         
4108         
4109         return cfg;
4110     },
4111     initEvents: function() 
4112     {
4113         if (typeof (this.menu) != 'undefined') {
4114             this.menu.parentType = this.xtype;
4115             this.menu.triggerEl = this.el;
4116             this.menu = this.addxtype(Roo.apply({}, this.menu));
4117         }
4118         
4119         this.el.select('a',true).on('click', this.onClick, this);
4120         
4121         if(this.tagtype == 'span'){
4122             this.el.select('span',true).on('click', this.onClick, this);
4123         }
4124        
4125         // at this point parent should be available..
4126         this.parent().register(this);
4127     },
4128     
4129     onClick : function(e)
4130     {
4131         if(
4132                 this.preventDefault || 
4133                 this.href == '#' 
4134         ){
4135             
4136             e.preventDefault();
4137         }
4138         
4139         if (this.disabled) {
4140             return;
4141         }
4142         
4143         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4144         if (tg && tg.transition) {
4145             Roo.log("waiting for the transitionend");
4146             return;
4147         }
4148         
4149         
4150         
4151         //Roo.log("fire event clicked");
4152         if(this.fireEvent('click', this, e) === false){
4153             return;
4154         };
4155         
4156         if(this.tagtype == 'span'){
4157             return;
4158         }
4159         
4160         //Roo.log(this.href);
4161         var ael = this.el.select('a',true).first();
4162         //Roo.log(ael);
4163         
4164         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4165             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4166             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4167                 return; // ignore... - it's a 'hash' to another page.
4168             }
4169             
4170             e.preventDefault();
4171             this.scrollToElement(e);
4172         }
4173         
4174         
4175         var p =  this.parent();
4176    
4177         if (['tabs','pills'].indexOf(p.type)!==-1) {
4178             if (typeof(p.setActiveItem) !== 'undefined') {
4179                 p.setActiveItem(this);
4180             }
4181         }
4182         
4183         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4184         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4185             // remove the collapsed menu expand...
4186             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4187         }
4188     },
4189     
4190     isActive: function () {
4191         return this.active
4192     },
4193     setActive : function(state, fire, is_was_active)
4194     {
4195         if (this.active && !state && this.navId) {
4196             this.was_active = true;
4197             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4198             if (nv) {
4199                 nv.clearWasActive(this);
4200             }
4201             
4202         }
4203         this.active = state;
4204         
4205         if (!state ) {
4206             this.el.removeClass('active');
4207         } else if (!this.el.hasClass('active')) {
4208             this.el.addClass('active');
4209         }
4210         if (fire) {
4211             this.fireEvent('changed', this, state);
4212         }
4213         
4214         // show a panel if it's registered and related..
4215         
4216         if (!this.navId || !this.tabId || !state || is_was_active) {
4217             return;
4218         }
4219         
4220         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4221         if (!tg) {
4222             return;
4223         }
4224         var pan = tg.getPanelByName(this.tabId);
4225         if (!pan) {
4226             return;
4227         }
4228         // if we can not flip to new panel - go back to old nav highlight..
4229         if (false == tg.showPanel(pan)) {
4230             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4231             if (nv) {
4232                 var onav = nv.getWasActive();
4233                 if (onav) {
4234                     onav.setActive(true, false, true);
4235                 }
4236             }
4237             
4238         }
4239         
4240         
4241         
4242     },
4243      // this should not be here...
4244     setDisabled : function(state)
4245     {
4246         this.disabled = state;
4247         if (!state ) {
4248             this.el.removeClass('disabled');
4249         } else if (!this.el.hasClass('disabled')) {
4250             this.el.addClass('disabled');
4251         }
4252         
4253     },
4254     
4255     /**
4256      * Fetch the element to display the tooltip on.
4257      * @return {Roo.Element} defaults to this.el
4258      */
4259     tooltipEl : function()
4260     {
4261         return this.el.select('' + this.tagtype + '', true).first();
4262     },
4263     
4264     scrollToElement : function(e)
4265     {
4266         var c = document.body;
4267         
4268         /*
4269          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4270          */
4271         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4272             c = document.documentElement;
4273         }
4274         
4275         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4276         
4277         if(!target){
4278             return;
4279         }
4280
4281         var o = target.calcOffsetsTo(c);
4282         
4283         var options = {
4284             target : target,
4285             value : o[1]
4286         }
4287         
4288         this.fireEvent('scrollto', this, options, e);
4289         
4290         Roo.get(c).scrollTo('top', options.value, true);
4291         
4292         return;
4293     }
4294 });
4295  
4296
4297  /*
4298  * - LGPL
4299  *
4300  * sidebar item
4301  *
4302  *  li
4303  *    <span> icon </span>
4304  *    <span> text </span>
4305  *    <span>badge </span>
4306  */
4307
4308 /**
4309  * @class Roo.bootstrap.NavSidebarItem
4310  * @extends Roo.bootstrap.NavItem
4311  * Bootstrap Navbar.NavSidebarItem class
4312  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4313  * @constructor
4314  * Create a new Navbar Button
4315  * @param {Object} config The config object
4316  */
4317 Roo.bootstrap.NavSidebarItem = function(config){
4318     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4319     this.addEvents({
4320         // raw events
4321         /**
4322          * @event click
4323          * The raw click event for the entire grid.
4324          * @param {Roo.EventObject} e
4325          */
4326         "click" : true,
4327          /**
4328             * @event changed
4329             * Fires when the active item active state changes
4330             * @param {Roo.bootstrap.NavSidebarItem} this
4331             * @param {boolean} state the new state
4332              
4333          */
4334         'changed': true
4335     });
4336    
4337 };
4338
4339 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4340     
4341     badgeWeight : 'default',
4342     
4343     getAutoCreate : function(){
4344         
4345         
4346         var a = {
4347                 tag: 'a',
4348                 href : this.href || '#',
4349                 cls: '',
4350                 html : '',
4351                 cn : []
4352         };
4353         var cfg = {
4354             tag: 'li',
4355             cls: '',
4356             cn: [ a ]
4357         };
4358         var span = {
4359             tag: 'span',
4360             html : this.html || ''
4361         };
4362         
4363         
4364         if (this.active) {
4365             cfg.cls += ' active';
4366         }
4367         
4368         if (this.disabled) {
4369             cfg.cls += ' disabled';
4370         }
4371         
4372         // left icon..
4373         if (this.glyphicon || this.icon) {
4374             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4375             a.cn.push({ tag : 'i', cls : c }) ;
4376         }
4377         // html..
4378         a.cn.push(span);
4379         // then badge..
4380         if (this.badge !== '') {
4381             
4382             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4383         }
4384         // fi
4385         if (this.menu) {
4386             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4387             a.cls += 'dropdown-toggle treeview' ;
4388             
4389         }
4390         
4391         
4392         
4393         return cfg;
4394          
4395            
4396     },
4397     
4398     initEvents : function()
4399     { 
4400         this.el.on('click', this.onClick, this);
4401        
4402     
4403         if(this.badge !== ''){
4404  
4405             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4406         }
4407         
4408     },
4409     
4410     onClick : function(e)
4411     {
4412         if(this.disabled){
4413             e.preventDefault();
4414             return;
4415         }
4416         
4417         if(this.preventDefault){
4418             e.preventDefault();
4419         }
4420         
4421         this.fireEvent('click', this);
4422     },
4423     
4424     disable : function()
4425     {
4426         this.setDisabled(true);
4427     },
4428     
4429     enable : function()
4430     {
4431         this.setDisabled(false);
4432     },
4433     
4434     setDisabled : function(state)
4435     {
4436         if(this.disabled == state){
4437             return;
4438         }
4439         
4440         this.disabled = state;
4441         
4442         if (state) {
4443             this.el.addClass('disabled');
4444             return;
4445         }
4446         
4447         this.el.removeClass('disabled');
4448         
4449         return;
4450     },
4451     
4452     setActive : function(state)
4453     {
4454         if(this.active == state){
4455             return;
4456         }
4457         
4458         this.active = state;
4459         
4460         if (state) {
4461             this.el.addClass('active');
4462             return;
4463         }
4464         
4465         this.el.removeClass('active');
4466         
4467         return;
4468     },
4469     
4470     isActive: function () 
4471     {
4472         return this.active;
4473     },
4474     
4475     setBadge : function(str)
4476     {
4477         if(!this.badgeEl){
4478             return;
4479         }
4480         
4481         this.badgeEl.dom.innerHTML = str;
4482     }
4483     
4484    
4485      
4486  
4487 });
4488  
4489
4490  /*
4491  * - LGPL
4492  *
4493  * row
4494  * 
4495  */
4496
4497 /**
4498  * @class Roo.bootstrap.Row
4499  * @extends Roo.bootstrap.Component
4500  * Bootstrap Row class (contains columns...)
4501  * 
4502  * @constructor
4503  * Create a new Row
4504  * @param {Object} config The config object
4505  */
4506
4507 Roo.bootstrap.Row = function(config){
4508     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4509 };
4510
4511 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4512     
4513     getAutoCreate : function(){
4514        return {
4515             cls: 'row clearfix'
4516        };
4517     }
4518     
4519     
4520 });
4521
4522  
4523
4524  /*
4525  * - LGPL
4526  *
4527  * element
4528  * 
4529  */
4530
4531 /**
4532  * @class Roo.bootstrap.Element
4533  * @extends Roo.bootstrap.Component
4534  * Bootstrap Element class
4535  * @cfg {String} html contents of the element
4536  * @cfg {String} tag tag of the element
4537  * @cfg {String} cls class of the element
4538  * @cfg {Boolean} preventDefault (true|false) default false
4539  * @cfg {Boolean} clickable (true|false) default false
4540  * 
4541  * @constructor
4542  * Create a new Element
4543  * @param {Object} config The config object
4544  */
4545
4546 Roo.bootstrap.Element = function(config){
4547     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4548     
4549     this.addEvents({
4550         // raw events
4551         /**
4552          * @event click
4553          * When a element is chick
4554          * @param {Roo.bootstrap.Element} this
4555          * @param {Roo.EventObject} e
4556          */
4557         "click" : true
4558     });
4559 };
4560
4561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4562     
4563     tag: 'div',
4564     cls: '',
4565     html: '',
4566     preventDefault: false, 
4567     clickable: false,
4568     
4569     getAutoCreate : function(){
4570         
4571         var cfg = {
4572             tag: this.tag,
4573             cls: this.cls,
4574             html: this.html
4575         }
4576         
4577         return cfg;
4578     },
4579     
4580     initEvents: function() 
4581     {
4582         Roo.bootstrap.Element.superclass.initEvents.call(this);
4583         
4584         if(this.clickable){
4585             this.el.on('click', this.onClick, this);
4586         }
4587         
4588     },
4589     
4590     onClick : function(e)
4591     {
4592         if(this.preventDefault){
4593             e.preventDefault();
4594         }
4595         
4596         this.fireEvent('click', this, e);
4597     },
4598     
4599     getValue : function()
4600     {
4601         return this.el.dom.innerHTML;
4602     },
4603     
4604     setValue : function(value)
4605     {
4606         this.el.dom.innerHTML = value;
4607     }
4608    
4609 });
4610
4611  
4612
4613  /*
4614  * - LGPL
4615  *
4616  * pagination
4617  * 
4618  */
4619
4620 /**
4621  * @class Roo.bootstrap.Pagination
4622  * @extends Roo.bootstrap.Component
4623  * Bootstrap Pagination class
4624  * @cfg {String} size xs | sm | md | lg
4625  * @cfg {Boolean} inverse false | true
4626  * 
4627  * @constructor
4628  * Create a new Pagination
4629  * @param {Object} config The config object
4630  */
4631
4632 Roo.bootstrap.Pagination = function(config){
4633     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4634 };
4635
4636 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4637     
4638     cls: false,
4639     size: false,
4640     inverse: false,
4641     
4642     getAutoCreate : function(){
4643         var cfg = {
4644             tag: 'ul',
4645                 cls: 'pagination'
4646         };
4647         if (this.inverse) {
4648             cfg.cls += ' inverse';
4649         }
4650         if (this.html) {
4651             cfg.html=this.html;
4652         }
4653         if (this.cls) {
4654             cfg.cls += " " + this.cls;
4655         }
4656         return cfg;
4657     }
4658    
4659 });
4660
4661  
4662
4663  /*
4664  * - LGPL
4665  *
4666  * Pagination item
4667  * 
4668  */
4669
4670
4671 /**
4672  * @class Roo.bootstrap.PaginationItem
4673  * @extends Roo.bootstrap.Component
4674  * Bootstrap PaginationItem class
4675  * @cfg {String} html text
4676  * @cfg {String} href the link
4677  * @cfg {Boolean} preventDefault (true | false) default true
4678  * @cfg {Boolean} active (true | false) default false
4679  * @cfg {Boolean} disabled default false
4680  * 
4681  * 
4682  * @constructor
4683  * Create a new PaginationItem
4684  * @param {Object} config The config object
4685  */
4686
4687
4688 Roo.bootstrap.PaginationItem = function(config){
4689     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4690     this.addEvents({
4691         // raw events
4692         /**
4693          * @event click
4694          * The raw click event for the entire grid.
4695          * @param {Roo.EventObject} e
4696          */
4697         "click" : true
4698     });
4699 };
4700
4701 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4702     
4703     href : false,
4704     html : false,
4705     preventDefault: true,
4706     active : false,
4707     cls : false,
4708     disabled: false,
4709     
4710     getAutoCreate : function(){
4711         var cfg= {
4712             tag: 'li',
4713             cn: [
4714                 {
4715                     tag : 'a',
4716                     href : this.href ? this.href : '#',
4717                     html : this.html ? this.html : ''
4718                 }
4719             ]
4720         };
4721         
4722         if(this.cls){
4723             cfg.cls = this.cls;
4724         }
4725         
4726         if(this.disabled){
4727             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4728         }
4729         
4730         if(this.active){
4731             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4732         }
4733         
4734         return cfg;
4735     },
4736     
4737     initEvents: function() {
4738         
4739         this.el.on('click', this.onClick, this);
4740         
4741     },
4742     onClick : function(e)
4743     {
4744         Roo.log('PaginationItem on click ');
4745         if(this.preventDefault){
4746             e.preventDefault();
4747         }
4748         
4749         if(this.disabled){
4750             return;
4751         }
4752         
4753         this.fireEvent('click', this, e);
4754     }
4755    
4756 });
4757
4758  
4759
4760  /*
4761  * - LGPL
4762  *
4763  * slider
4764  * 
4765  */
4766
4767
4768 /**
4769  * @class Roo.bootstrap.Slider
4770  * @extends Roo.bootstrap.Component
4771  * Bootstrap Slider class
4772  *    
4773  * @constructor
4774  * Create a new Slider
4775  * @param {Object} config The config object
4776  */
4777
4778 Roo.bootstrap.Slider = function(config){
4779     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4780 };
4781
4782 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4783     
4784     getAutoCreate : function(){
4785         
4786         var cfg = {
4787             tag: 'div',
4788             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4789             cn: [
4790                 {
4791                     tag: 'a',
4792                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4793                 }
4794             ]
4795         }
4796         
4797         return cfg;
4798     }
4799    
4800 });
4801
4802  /*
4803  * Based on:
4804  * Ext JS Library 1.1.1
4805  * Copyright(c) 2006-2007, Ext JS, LLC.
4806  *
4807  * Originally Released Under LGPL - original licence link has changed is not relivant.
4808  *
4809  * Fork - LGPL
4810  * <script type="text/javascript">
4811  */
4812  
4813
4814 /**
4815  * @class Roo.grid.ColumnModel
4816  * @extends Roo.util.Observable
4817  * This is the default implementation of a ColumnModel used by the Grid. It defines
4818  * the columns in the grid.
4819  * <br>Usage:<br>
4820  <pre><code>
4821  var colModel = new Roo.grid.ColumnModel([
4822         {header: "Ticker", width: 60, sortable: true, locked: true},
4823         {header: "Company Name", width: 150, sortable: true},
4824         {header: "Market Cap.", width: 100, sortable: true},
4825         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4826         {header: "Employees", width: 100, sortable: true, resizable: false}
4827  ]);
4828  </code></pre>
4829  * <p>
4830  
4831  * The config options listed for this class are options which may appear in each
4832  * individual column definition.
4833  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4834  * @constructor
4835  * @param {Object} config An Array of column config objects. See this class's
4836  * config objects for details.
4837 */
4838 Roo.grid.ColumnModel = function(config){
4839         /**
4840      * The config passed into the constructor
4841      */
4842     this.config = config;
4843     this.lookup = {};
4844
4845     // if no id, create one
4846     // if the column does not have a dataIndex mapping,
4847     // map it to the order it is in the config
4848     for(var i = 0, len = config.length; i < len; i++){
4849         var c = config[i];
4850         if(typeof c.dataIndex == "undefined"){
4851             c.dataIndex = i;
4852         }
4853         if(typeof c.renderer == "string"){
4854             c.renderer = Roo.util.Format[c.renderer];
4855         }
4856         if(typeof c.id == "undefined"){
4857             c.id = Roo.id();
4858         }
4859         if(c.editor && c.editor.xtype){
4860             c.editor  = Roo.factory(c.editor, Roo.grid);
4861         }
4862         if(c.editor && c.editor.isFormField){
4863             c.editor = new Roo.grid.GridEditor(c.editor);
4864         }
4865         this.lookup[c.id] = c;
4866     }
4867
4868     /**
4869      * The width of columns which have no width specified (defaults to 100)
4870      * @type Number
4871      */
4872     this.defaultWidth = 100;
4873
4874     /**
4875      * Default sortable of columns which have no sortable specified (defaults to false)
4876      * @type Boolean
4877      */
4878     this.defaultSortable = false;
4879
4880     this.addEvents({
4881         /**
4882              * @event widthchange
4883              * Fires when the width of a column changes.
4884              * @param {ColumnModel} this
4885              * @param {Number} columnIndex The column index
4886              * @param {Number} newWidth The new width
4887              */
4888             "widthchange": true,
4889         /**
4890              * @event headerchange
4891              * Fires when the text of a header changes.
4892              * @param {ColumnModel} this
4893              * @param {Number} columnIndex The column index
4894              * @param {Number} newText The new header text
4895              */
4896             "headerchange": true,
4897         /**
4898              * @event hiddenchange
4899              * Fires when a column is hidden or "unhidden".
4900              * @param {ColumnModel} this
4901              * @param {Number} columnIndex The column index
4902              * @param {Boolean} hidden true if hidden, false otherwise
4903              */
4904             "hiddenchange": true,
4905             /**
4906          * @event columnmoved
4907          * Fires when a column is moved.
4908          * @param {ColumnModel} this
4909          * @param {Number} oldIndex
4910          * @param {Number} newIndex
4911          */
4912         "columnmoved" : true,
4913         /**
4914          * @event columlockchange
4915          * Fires when a column's locked state is changed
4916          * @param {ColumnModel} this
4917          * @param {Number} colIndex
4918          * @param {Boolean} locked true if locked
4919          */
4920         "columnlockchange" : true
4921     });
4922     Roo.grid.ColumnModel.superclass.constructor.call(this);
4923 };
4924 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4925     /**
4926      * @cfg {String} header The header text to display in the Grid view.
4927      */
4928     /**
4929      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4930      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4931      * specified, the column's index is used as an index into the Record's data Array.
4932      */
4933     /**
4934      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4935      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4936      */
4937     /**
4938      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4939      * Defaults to the value of the {@link #defaultSortable} property.
4940      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4941      */
4942     /**
4943      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4944      */
4945     /**
4946      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4947      */
4948     /**
4949      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4950      */
4951     /**
4952      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4953      */
4954     /**
4955      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4956      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4957      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4958      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4959      */
4960        /**
4961      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4962      */
4963     /**
4964      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4965      */
4966     /**
4967      * @cfg {String} cursor (Optional)
4968      */
4969     /**
4970      * @cfg {String} tooltip (Optional)
4971      */
4972     /**
4973      * Returns the id of the column at the specified index.
4974      * @param {Number} index The column index
4975      * @return {String} the id
4976      */
4977     getColumnId : function(index){
4978         return this.config[index].id;
4979     },
4980
4981     /**
4982      * Returns the column for a specified id.
4983      * @param {String} id The column id
4984      * @return {Object} the column
4985      */
4986     getColumnById : function(id){
4987         return this.lookup[id];
4988     },
4989
4990     
4991     /**
4992      * Returns the column for a specified dataIndex.
4993      * @param {String} dataIndex The column dataIndex
4994      * @return {Object|Boolean} the column or false if not found
4995      */
4996     getColumnByDataIndex: function(dataIndex){
4997         var index = this.findColumnIndex(dataIndex);
4998         return index > -1 ? this.config[index] : false;
4999     },
5000     
5001     /**
5002      * Returns the index for a specified column id.
5003      * @param {String} id The column id
5004      * @return {Number} the index, or -1 if not found
5005      */
5006     getIndexById : function(id){
5007         for(var i = 0, len = this.config.length; i < len; i++){
5008             if(this.config[i].id == id){
5009                 return i;
5010             }
5011         }
5012         return -1;
5013     },
5014     
5015     /**
5016      * Returns the index for a specified column dataIndex.
5017      * @param {String} dataIndex The column dataIndex
5018      * @return {Number} the index, or -1 if not found
5019      */
5020     
5021     findColumnIndex : function(dataIndex){
5022         for(var i = 0, len = this.config.length; i < len; i++){
5023             if(this.config[i].dataIndex == dataIndex){
5024                 return i;
5025             }
5026         }
5027         return -1;
5028     },
5029     
5030     
5031     moveColumn : function(oldIndex, newIndex){
5032         var c = this.config[oldIndex];
5033         this.config.splice(oldIndex, 1);
5034         this.config.splice(newIndex, 0, c);
5035         this.dataMap = null;
5036         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5037     },
5038
5039     isLocked : function(colIndex){
5040         return this.config[colIndex].locked === true;
5041     },
5042
5043     setLocked : function(colIndex, value, suppressEvent){
5044         if(this.isLocked(colIndex) == value){
5045             return;
5046         }
5047         this.config[colIndex].locked = value;
5048         if(!suppressEvent){
5049             this.fireEvent("columnlockchange", this, colIndex, value);
5050         }
5051     },
5052
5053     getTotalLockedWidth : function(){
5054         var totalWidth = 0;
5055         for(var i = 0; i < this.config.length; i++){
5056             if(this.isLocked(i) && !this.isHidden(i)){
5057                 this.totalWidth += this.getColumnWidth(i);
5058             }
5059         }
5060         return totalWidth;
5061     },
5062
5063     getLockedCount : function(){
5064         for(var i = 0, len = this.config.length; i < len; i++){
5065             if(!this.isLocked(i)){
5066                 return i;
5067             }
5068         }
5069     },
5070
5071     /**
5072      * Returns the number of columns.
5073      * @return {Number}
5074      */
5075     getColumnCount : function(visibleOnly){
5076         if(visibleOnly === true){
5077             var c = 0;
5078             for(var i = 0, len = this.config.length; i < len; i++){
5079                 if(!this.isHidden(i)){
5080                     c++;
5081                 }
5082             }
5083             return c;
5084         }
5085         return this.config.length;
5086     },
5087
5088     /**
5089      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5090      * @param {Function} fn
5091      * @param {Object} scope (optional)
5092      * @return {Array} result
5093      */
5094     getColumnsBy : function(fn, scope){
5095         var r = [];
5096         for(var i = 0, len = this.config.length; i < len; i++){
5097             var c = this.config[i];
5098             if(fn.call(scope||this, c, i) === true){
5099                 r[r.length] = c;
5100             }
5101         }
5102         return r;
5103     },
5104
5105     /**
5106      * Returns true if the specified column is sortable.
5107      * @param {Number} col The column index
5108      * @return {Boolean}
5109      */
5110     isSortable : function(col){
5111         if(typeof this.config[col].sortable == "undefined"){
5112             return this.defaultSortable;
5113         }
5114         return this.config[col].sortable;
5115     },
5116
5117     /**
5118      * Returns the rendering (formatting) function defined for the column.
5119      * @param {Number} col The column index.
5120      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5121      */
5122     getRenderer : function(col){
5123         if(!this.config[col].renderer){
5124             return Roo.grid.ColumnModel.defaultRenderer;
5125         }
5126         return this.config[col].renderer;
5127     },
5128
5129     /**
5130      * Sets the rendering (formatting) function for a column.
5131      * @param {Number} col The column index
5132      * @param {Function} fn The function to use to process the cell's raw data
5133      * to return HTML markup for the grid view. The render function is called with
5134      * the following parameters:<ul>
5135      * <li>Data value.</li>
5136      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5137      * <li>css A CSS style string to apply to the table cell.</li>
5138      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5139      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5140      * <li>Row index</li>
5141      * <li>Column index</li>
5142      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5143      */
5144     setRenderer : function(col, fn){
5145         this.config[col].renderer = fn;
5146     },
5147
5148     /**
5149      * Returns the width for the specified column.
5150      * @param {Number} col The column index
5151      * @return {Number}
5152      */
5153     getColumnWidth : function(col){
5154         return this.config[col].width * 1 || this.defaultWidth;
5155     },
5156
5157     /**
5158      * Sets the width for a column.
5159      * @param {Number} col The column index
5160      * @param {Number} width The new width
5161      */
5162     setColumnWidth : function(col, width, suppressEvent){
5163         this.config[col].width = width;
5164         this.totalWidth = null;
5165         if(!suppressEvent){
5166              this.fireEvent("widthchange", this, col, width);
5167         }
5168     },
5169
5170     /**
5171      * Returns the total width of all columns.
5172      * @param {Boolean} includeHidden True to include hidden column widths
5173      * @return {Number}
5174      */
5175     getTotalWidth : function(includeHidden){
5176         if(!this.totalWidth){
5177             this.totalWidth = 0;
5178             for(var i = 0, len = this.config.length; i < len; i++){
5179                 if(includeHidden || !this.isHidden(i)){
5180                     this.totalWidth += this.getColumnWidth(i);
5181                 }
5182             }
5183         }
5184         return this.totalWidth;
5185     },
5186
5187     /**
5188      * Returns the header for the specified column.
5189      * @param {Number} col The column index
5190      * @return {String}
5191      */
5192     getColumnHeader : function(col){
5193         return this.config[col].header;
5194     },
5195
5196     /**
5197      * Sets the header for a column.
5198      * @param {Number} col The column index
5199      * @param {String} header The new header
5200      */
5201     setColumnHeader : function(col, header){
5202         this.config[col].header = header;
5203         this.fireEvent("headerchange", this, col, header);
5204     },
5205
5206     /**
5207      * Returns the tooltip for the specified column.
5208      * @param {Number} col The column index
5209      * @return {String}
5210      */
5211     getColumnTooltip : function(col){
5212             return this.config[col].tooltip;
5213     },
5214     /**
5215      * Sets the tooltip for a column.
5216      * @param {Number} col The column index
5217      * @param {String} tooltip The new tooltip
5218      */
5219     setColumnTooltip : function(col, tooltip){
5220             this.config[col].tooltip = tooltip;
5221     },
5222
5223     /**
5224      * Returns the dataIndex for the specified column.
5225      * @param {Number} col The column index
5226      * @return {Number}
5227      */
5228     getDataIndex : function(col){
5229         return this.config[col].dataIndex;
5230     },
5231
5232     /**
5233      * Sets the dataIndex for a column.
5234      * @param {Number} col The column index
5235      * @param {Number} dataIndex The new dataIndex
5236      */
5237     setDataIndex : function(col, dataIndex){
5238         this.config[col].dataIndex = dataIndex;
5239     },
5240
5241     
5242     
5243     /**
5244      * Returns true if the cell is editable.
5245      * @param {Number} colIndex The column index
5246      * @param {Number} rowIndex The row index
5247      * @return {Boolean}
5248      */
5249     isCellEditable : function(colIndex, rowIndex){
5250         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5251     },
5252
5253     /**
5254      * Returns the editor defined for the cell/column.
5255      * return false or null to disable editing.
5256      * @param {Number} colIndex The column index
5257      * @param {Number} rowIndex The row index
5258      * @return {Object}
5259      */
5260     getCellEditor : function(colIndex, rowIndex){
5261         return this.config[colIndex].editor;
5262     },
5263
5264     /**
5265      * Sets if a column is editable.
5266      * @param {Number} col The column index
5267      * @param {Boolean} editable True if the column is editable
5268      */
5269     setEditable : function(col, editable){
5270         this.config[col].editable = editable;
5271     },
5272
5273
5274     /**
5275      * Returns true if the column is hidden.
5276      * @param {Number} colIndex The column index
5277      * @return {Boolean}
5278      */
5279     isHidden : function(colIndex){
5280         return this.config[colIndex].hidden;
5281     },
5282
5283
5284     /**
5285      * Returns true if the column width cannot be changed
5286      */
5287     isFixed : function(colIndex){
5288         return this.config[colIndex].fixed;
5289     },
5290
5291     /**
5292      * Returns true if the column can be resized
5293      * @return {Boolean}
5294      */
5295     isResizable : function(colIndex){
5296         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5297     },
5298     /**
5299      * Sets if a column is hidden.
5300      * @param {Number} colIndex The column index
5301      * @param {Boolean} hidden True if the column is hidden
5302      */
5303     setHidden : function(colIndex, hidden){
5304         this.config[colIndex].hidden = hidden;
5305         this.totalWidth = null;
5306         this.fireEvent("hiddenchange", this, colIndex, hidden);
5307     },
5308
5309     /**
5310      * Sets the editor for a column.
5311      * @param {Number} col The column index
5312      * @param {Object} editor The editor object
5313      */
5314     setEditor : function(col, editor){
5315         this.config[col].editor = editor;
5316     }
5317 });
5318
5319 Roo.grid.ColumnModel.defaultRenderer = function(value){
5320         if(typeof value == "string" && value.length < 1){
5321             return "&#160;";
5322         }
5323         return value;
5324 };
5325
5326 // Alias for backwards compatibility
5327 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5328 /*
5329  * Based on:
5330  * Ext JS Library 1.1.1
5331  * Copyright(c) 2006-2007, Ext JS, LLC.
5332  *
5333  * Originally Released Under LGPL - original licence link has changed is not relivant.
5334  *
5335  * Fork - LGPL
5336  * <script type="text/javascript">
5337  */
5338  
5339 /**
5340  * @class Roo.LoadMask
5341  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5342  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5343  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5344  * element's UpdateManager load indicator and will be destroyed after the initial load.
5345  * @constructor
5346  * Create a new LoadMask
5347  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5348  * @param {Object} config The config object
5349  */
5350 Roo.LoadMask = function(el, config){
5351     this.el = Roo.get(el);
5352     Roo.apply(this, config);
5353     if(this.store){
5354         this.store.on('beforeload', this.onBeforeLoad, this);
5355         this.store.on('load', this.onLoad, this);
5356         this.store.on('loadexception', this.onLoadException, this);
5357         this.removeMask = false;
5358     }else{
5359         var um = this.el.getUpdateManager();
5360         um.showLoadIndicator = false; // disable the default indicator
5361         um.on('beforeupdate', this.onBeforeLoad, this);
5362         um.on('update', this.onLoad, this);
5363         um.on('failure', this.onLoad, this);
5364         this.removeMask = true;
5365     }
5366 };
5367
5368 Roo.LoadMask.prototype = {
5369     /**
5370      * @cfg {Boolean} removeMask
5371      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5372      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5373      */
5374     /**
5375      * @cfg {String} msg
5376      * The text to display in a centered loading message box (defaults to 'Loading...')
5377      */
5378     msg : 'Loading...',
5379     /**
5380      * @cfg {String} msgCls
5381      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5382      */
5383     msgCls : 'x-mask-loading',
5384
5385     /**
5386      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5387      * @type Boolean
5388      */
5389     disabled: false,
5390
5391     /**
5392      * Disables the mask to prevent it from being displayed
5393      */
5394     disable : function(){
5395        this.disabled = true;
5396     },
5397
5398     /**
5399      * Enables the mask so that it can be displayed
5400      */
5401     enable : function(){
5402         this.disabled = false;
5403     },
5404     
5405     onLoadException : function()
5406     {
5407         Roo.log(arguments);
5408         
5409         if (typeof(arguments[3]) != 'undefined') {
5410             Roo.MessageBox.alert("Error loading",arguments[3]);
5411         } 
5412         /*
5413         try {
5414             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5415                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5416             }   
5417         } catch(e) {
5418             
5419         }
5420         */
5421     
5422         
5423         
5424         this.el.unmask(this.removeMask);
5425     },
5426     // private
5427     onLoad : function()
5428     {
5429         this.el.unmask(this.removeMask);
5430     },
5431
5432     // private
5433     onBeforeLoad : function(){
5434         if(!this.disabled){
5435             this.el.mask(this.msg, this.msgCls);
5436         }
5437     },
5438
5439     // private
5440     destroy : function(){
5441         if(this.store){
5442             this.store.un('beforeload', this.onBeforeLoad, this);
5443             this.store.un('load', this.onLoad, this);
5444             this.store.un('loadexception', this.onLoadException, this);
5445         }else{
5446             var um = this.el.getUpdateManager();
5447             um.un('beforeupdate', this.onBeforeLoad, this);
5448             um.un('update', this.onLoad, this);
5449             um.un('failure', this.onLoad, this);
5450         }
5451     }
5452 };/*
5453  * - LGPL
5454  *
5455  * table
5456  * 
5457  */
5458
5459 /**
5460  * @class Roo.bootstrap.Table
5461  * @extends Roo.bootstrap.Component
5462  * Bootstrap Table class
5463  * @cfg {String} cls table class
5464  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5465  * @cfg {String} bgcolor Specifies the background color for a table
5466  * @cfg {Number} border Specifies whether the table cells should have borders or not
5467  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5468  * @cfg {Number} cellspacing Specifies the space between cells
5469  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5470  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5471  * @cfg {String} sortable Specifies that the table should be sortable
5472  * @cfg {String} summary Specifies a summary of the content of a table
5473  * @cfg {Number} width Specifies the width of a table
5474  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5475  * 
5476  * @cfg {boolean} striped Should the rows be alternative striped
5477  * @cfg {boolean} bordered Add borders to the table
5478  * @cfg {boolean} hover Add hover highlighting
5479  * @cfg {boolean} condensed Format condensed
5480  * @cfg {boolean} responsive Format condensed
5481  * @cfg {Boolean} loadMask (true|false) default false
5482  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5483  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5484  * @cfg {Boolean} rowSelection (true|false) default false
5485  * @cfg {Boolean} cellSelection (true|false) default false
5486  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5487  
5488  * 
5489  * @constructor
5490  * Create a new Table
5491  * @param {Object} config The config object
5492  */
5493
5494 Roo.bootstrap.Table = function(config){
5495     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5496     
5497     // BC...
5498     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5499     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5500     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5501     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5502     
5503     
5504     if (this.sm) {
5505         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5506         this.sm = this.selModel;
5507         this.sm.xmodule = this.xmodule || false;
5508     }
5509     if (this.cm && typeof(this.cm.config) == 'undefined') {
5510         this.colModel = new Roo.grid.ColumnModel(this.cm);
5511         this.cm = this.colModel;
5512         this.cm.xmodule = this.xmodule || false;
5513     }
5514     if (this.store) {
5515         this.store= Roo.factory(this.store, Roo.data);
5516         this.ds = this.store;
5517         this.ds.xmodule = this.xmodule || false;
5518          
5519     }
5520     if (this.footer && this.store) {
5521         this.footer.dataSource = this.ds;
5522         this.footer = Roo.factory(this.footer);
5523     }
5524     
5525     /** @private */
5526     this.addEvents({
5527         /**
5528          * @event cellclick
5529          * Fires when a cell is clicked
5530          * @param {Roo.bootstrap.Table} this
5531          * @param {Roo.Element} el
5532          * @param {Number} rowIndex
5533          * @param {Number} columnIndex
5534          * @param {Roo.EventObject} e
5535          */
5536         "cellclick" : true,
5537         /**
5538          * @event celldblclick
5539          * Fires when a cell is double clicked
5540          * @param {Roo.bootstrap.Table} this
5541          * @param {Roo.Element} el
5542          * @param {Number} rowIndex
5543          * @param {Number} columnIndex
5544          * @param {Roo.EventObject} e
5545          */
5546         "celldblclick" : true,
5547         /**
5548          * @event rowclick
5549          * Fires when a row is clicked
5550          * @param {Roo.bootstrap.Table} this
5551          * @param {Roo.Element} el
5552          * @param {Number} rowIndex
5553          * @param {Roo.EventObject} e
5554          */
5555         "rowclick" : true,
5556         /**
5557          * @event rowdblclick
5558          * Fires when a row is double clicked
5559          * @param {Roo.bootstrap.Table} this
5560          * @param {Roo.Element} el
5561          * @param {Number} rowIndex
5562          * @param {Roo.EventObject} e
5563          */
5564         "rowdblclick" : true,
5565         /**
5566          * @event mouseover
5567          * Fires when a mouseover occur
5568          * @param {Roo.bootstrap.Table} this
5569          * @param {Roo.Element} el
5570          * @param {Number} rowIndex
5571          * @param {Number} columnIndex
5572          * @param {Roo.EventObject} e
5573          */
5574         "mouseover" : true,
5575         /**
5576          * @event mouseout
5577          * Fires when a mouseout occur
5578          * @param {Roo.bootstrap.Table} this
5579          * @param {Roo.Element} el
5580          * @param {Number} rowIndex
5581          * @param {Number} columnIndex
5582          * @param {Roo.EventObject} e
5583          */
5584         "mouseout" : true,
5585         /**
5586          * @event rowclass
5587          * Fires when a row is rendered, so you can change add a style to it.
5588          * @param {Roo.bootstrap.Table} this
5589          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5590          */
5591         'rowclass' : true,
5592           /**
5593          * @event rowsrendered
5594          * Fires when all the  rows have been rendered
5595          * @param {Roo.bootstrap.Table} this
5596          */
5597         'rowsrendered' : true
5598         
5599     });
5600 };
5601
5602 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5603     
5604     cls: false,
5605     align: false,
5606     bgcolor: false,
5607     border: false,
5608     cellpadding: false,
5609     cellspacing: false,
5610     frame: false,
5611     rules: false,
5612     sortable: false,
5613     summary: false,
5614     width: false,
5615     striped : false,
5616     bordered: false,
5617     hover:  false,
5618     condensed : false,
5619     responsive : false,
5620     sm : false,
5621     cm : false,
5622     store : false,
5623     loadMask : false,
5624     footerShow : true,
5625     headerShow : true,
5626   
5627     rowSelection : false,
5628     cellSelection : false,
5629     layout : false,
5630     
5631     // Roo.Element - the tbody
5632     mainBody: false, 
5633     
5634     getAutoCreate : function(){
5635         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5636         
5637         cfg = {
5638             tag: 'table',
5639             cls : 'table',
5640             cn : []
5641         }
5642             
5643         if (this.striped) {
5644             cfg.cls += ' table-striped';
5645         }
5646         
5647         if (this.hover) {
5648             cfg.cls += ' table-hover';
5649         }
5650         if (this.bordered) {
5651             cfg.cls += ' table-bordered';
5652         }
5653         if (this.condensed) {
5654             cfg.cls += ' table-condensed';
5655         }
5656         if (this.responsive) {
5657             cfg.cls += ' table-responsive';
5658         }
5659         
5660         if (this.cls) {
5661             cfg.cls+=  ' ' +this.cls;
5662         }
5663         
5664         // this lot should be simplifed...
5665         
5666         if (this.align) {
5667             cfg.align=this.align;
5668         }
5669         if (this.bgcolor) {
5670             cfg.bgcolor=this.bgcolor;
5671         }
5672         if (this.border) {
5673             cfg.border=this.border;
5674         }
5675         if (this.cellpadding) {
5676             cfg.cellpadding=this.cellpadding;
5677         }
5678         if (this.cellspacing) {
5679             cfg.cellspacing=this.cellspacing;
5680         }
5681         if (this.frame) {
5682             cfg.frame=this.frame;
5683         }
5684         if (this.rules) {
5685             cfg.rules=this.rules;
5686         }
5687         if (this.sortable) {
5688             cfg.sortable=this.sortable;
5689         }
5690         if (this.summary) {
5691             cfg.summary=this.summary;
5692         }
5693         if (this.width) {
5694             cfg.width=this.width;
5695         }
5696         if (this.layout) {
5697             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5698         }
5699         
5700         if(this.store || this.cm){
5701             if(this.headerShow){
5702                 cfg.cn.push(this.renderHeader());
5703             }
5704             
5705             cfg.cn.push(this.renderBody());
5706             
5707             if(this.footerShow){
5708                 cfg.cn.push(this.renderFooter());
5709             }
5710             
5711             cfg.cls+=  ' TableGrid';
5712         }
5713         
5714         return { cn : [ cfg ] };
5715     },
5716     
5717     initEvents : function()
5718     {   
5719         if(!this.store || !this.cm){
5720             return;
5721         }
5722         
5723         //Roo.log('initEvents with ds!!!!');
5724         
5725         this.mainBody = this.el.select('tbody', true).first();
5726         
5727         
5728         var _this = this;
5729         
5730         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5731             e.on('click', _this.sort, _this);
5732         });
5733         
5734         this.el.on("click", this.onClick, this);
5735         this.el.on("dblclick", this.onDblClick, this);
5736         
5737         // why is this done????? = it breaks dialogs??
5738         //this.parent().el.setStyle('position', 'relative');
5739         
5740         
5741         if (this.footer) {
5742             this.footer.parentId = this.id;
5743             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5744         }
5745         
5746         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5747         
5748         this.store.on('load', this.onLoad, this);
5749         this.store.on('beforeload', this.onBeforeLoad, this);
5750         this.store.on('update', this.onUpdate, this);
5751         this.store.on('add', this.onAdd, this);
5752         
5753     },
5754     
5755     onMouseover : function(e, el)
5756     {
5757         var cell = Roo.get(el);
5758         
5759         if(!cell){
5760             return;
5761         }
5762         
5763         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5764             cell = cell.findParent('td', false, true);
5765         }
5766         
5767         var row = cell.findParent('tr', false, true);
5768         var cellIndex = cell.dom.cellIndex;
5769         var rowIndex = row.dom.rowIndex - 1; // start from 0
5770         
5771         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5772         
5773     },
5774     
5775     onMouseout : function(e, el)
5776     {
5777         var cell = Roo.get(el);
5778         
5779         if(!cell){
5780             return;
5781         }
5782         
5783         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5784             cell = cell.findParent('td', false, true);
5785         }
5786         
5787         var row = cell.findParent('tr', false, true);
5788         var cellIndex = cell.dom.cellIndex;
5789         var rowIndex = row.dom.rowIndex - 1; // start from 0
5790         
5791         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5792         
5793     },
5794     
5795     onClick : function(e, el)
5796     {
5797         var cell = Roo.get(el);
5798         
5799         if(!cell || (!this.cellSelection && !this.rowSelection)){
5800             return;
5801         }
5802         
5803         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5804             cell = cell.findParent('td', false, true);
5805         }
5806         
5807         if(!cell || typeof(cell) == 'undefined'){
5808             return;
5809         }
5810         
5811         var row = cell.findParent('tr', false, true);
5812         
5813         if(!row || typeof(row) == 'undefined'){
5814             return;
5815         }
5816         
5817         var cellIndex = cell.dom.cellIndex;
5818         var rowIndex = this.getRowIndex(row);
5819         
5820         // why??? - should these not be based on SelectionModel?
5821         if(this.cellSelection){
5822             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5823         }
5824         
5825         if(this.rowSelection){
5826             this.fireEvent('rowclick', this, row, rowIndex, e);
5827         }
5828         
5829         
5830     },
5831     
5832     onDblClick : function(e,el)
5833     {
5834         var cell = Roo.get(el);
5835         
5836         if(!cell || (!this.CellSelection && !this.RowSelection)){
5837             return;
5838         }
5839         
5840         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5841             cell = cell.findParent('td', false, true);
5842         }
5843         
5844         if(!cell || typeof(cell) == 'undefined'){
5845             return;
5846         }
5847         
5848         var row = cell.findParent('tr', false, true);
5849         
5850         if(!row || typeof(row) == 'undefined'){
5851             return;
5852         }
5853         
5854         var cellIndex = cell.dom.cellIndex;
5855         var rowIndex = this.getRowIndex(row);
5856         
5857         if(this.CellSelection){
5858             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5859         }
5860         
5861         if(this.RowSelection){
5862             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5863         }
5864     },
5865     
5866     sort : function(e,el)
5867     {
5868         var col = Roo.get(el);
5869         
5870         if(!col.hasClass('sortable')){
5871             return;
5872         }
5873         
5874         var sort = col.attr('sort');
5875         var dir = 'ASC';
5876         
5877         if(col.hasClass('glyphicon-arrow-up')){
5878             dir = 'DESC';
5879         }
5880         
5881         this.store.sortInfo = {field : sort, direction : dir};
5882         
5883         if (this.footer) {
5884             Roo.log("calling footer first");
5885             this.footer.onClick('first');
5886         } else {
5887         
5888             this.store.load({ params : { start : 0 } });
5889         }
5890     },
5891     
5892     renderHeader : function()
5893     {
5894         var header = {
5895             tag: 'thead',
5896             cn : []
5897         };
5898         
5899         var cm = this.cm;
5900         
5901         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5902             
5903             var config = cm.config[i];
5904             
5905             var c = {
5906                 tag: 'th',
5907                 style : '',
5908                 html: cm.getColumnHeader(i)
5909             };
5910             
5911             var hh = '';
5912             
5913             if(typeof(config.lgHeader) != 'undefined'){
5914                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5915             }
5916             
5917             if(typeof(config.mdHeader) != 'undefined'){
5918                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5919             }
5920             
5921             if(typeof(config.smHeader) != 'undefined'){
5922                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5923             }
5924             
5925             if(typeof(config.xsHeader) != 'undefined'){
5926                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5927             }
5928             
5929             if(hh.length){
5930                 c.html = hh;
5931             }
5932             
5933             if(typeof(config.tooltip) != 'undefined'){
5934                 c.tooltip = config.tooltip;
5935             }
5936             
5937             if(typeof(config.colspan) != 'undefined'){
5938                 c.colspan = config.colspan;
5939             }
5940             
5941             if(typeof(config.hidden) != 'undefined' && config.hidden){
5942                 c.style += ' display:none;';
5943             }
5944             
5945             if(typeof(config.dataIndex) != 'undefined'){
5946                 c.sort = config.dataIndex;
5947             }
5948             
5949             if(typeof(config.sortable) != 'undefined' && config.sortable){
5950                 c.cls = 'sortable';
5951             }
5952             
5953             if(typeof(config.align) != 'undefined' && config.align.length){
5954                 c.style += ' text-align:' + config.align + ';';
5955             }
5956             
5957             if(typeof(config.width) != 'undefined'){
5958                 c.style += ' width:' + config.width + 'px;';
5959             }
5960             
5961             if(typeof(config.cls) != 'undefined'){
5962                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5963             }
5964             
5965             header.cn.push(c)
5966         }
5967         
5968         return header;
5969     },
5970     
5971     renderBody : function()
5972     {
5973         var body = {
5974             tag: 'tbody',
5975             cn : [
5976                 {
5977                     tag: 'tr',
5978                     cn : [
5979                         {
5980                             tag : 'td',
5981                             colspan :  this.cm.getColumnCount()
5982                         }
5983                     ]
5984                 }
5985             ]
5986         };
5987         
5988         return body;
5989     },
5990     
5991     renderFooter : function()
5992     {
5993         var footer = {
5994             tag: 'tfoot',
5995             cn : [
5996                 {
5997                     tag: 'tr',
5998                     cn : [
5999                         {
6000                             tag : 'td',
6001                             colspan :  this.cm.getColumnCount()
6002                         }
6003                     ]
6004                 }
6005             ]
6006         };
6007         
6008         return footer;
6009     },
6010     
6011     
6012     
6013     onLoad : function()
6014     {
6015         Roo.log('ds onload');
6016         this.clear();
6017         
6018         var _this = this;
6019         var cm = this.cm;
6020         var ds = this.store;
6021         
6022         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6023             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6024             
6025             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6026                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6027             }
6028             
6029             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6030                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6031             }
6032         });
6033         
6034         var tbody =  this.mainBody;
6035               
6036         if(ds.getCount() > 0){
6037             ds.data.each(function(d,rowIndex){
6038                 var row =  this.renderRow(cm, ds, rowIndex);
6039                 
6040                 tbody.createChild(row);
6041                 
6042                 var _this = this;
6043                 
6044                 if(row.cellObjects.length){
6045                     Roo.each(row.cellObjects, function(r){
6046                         _this.renderCellObject(r);
6047                     })
6048                 }
6049                 
6050             }, this);
6051         }
6052         
6053         Roo.each(this.el.select('tbody td', true).elements, function(e){
6054             e.on('mouseover', _this.onMouseover, _this);
6055         });
6056         
6057         Roo.each(this.el.select('tbody td', true).elements, function(e){
6058             e.on('mouseout', _this.onMouseout, _this);
6059         });
6060         this.fireEvent('rowsrendered', this);
6061         //if(this.loadMask){
6062         //    this.maskEl.hide();
6063         //}
6064     },
6065     
6066     
6067     onUpdate : function(ds,record)
6068     {
6069         this.refreshRow(record);
6070     },
6071     
6072     onRemove : function(ds, record, index, isUpdate){
6073         if(isUpdate !== true){
6074             this.fireEvent("beforerowremoved", this, index, record);
6075         }
6076         var bt = this.mainBody.dom;
6077         
6078         var rows = this.el.select('tbody > tr', true).elements;
6079         
6080         if(typeof(rows[index]) != 'undefined'){
6081             bt.removeChild(rows[index].dom);
6082         }
6083         
6084 //        if(bt.rows[index]){
6085 //            bt.removeChild(bt.rows[index]);
6086 //        }
6087         
6088         if(isUpdate !== true){
6089             //this.stripeRows(index);
6090             //this.syncRowHeights(index, index);
6091             //this.layout();
6092             this.fireEvent("rowremoved", this, index, record);
6093         }
6094     },
6095     
6096     onAdd : function(ds, records, rowIndex)
6097     {
6098         //Roo.log('on Add called');
6099         // - note this does not handle multiple adding very well..
6100         var bt = this.mainBody.dom;
6101         for (var i =0 ; i < records.length;i++) {
6102             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6103             //Roo.log(records[i]);
6104             //Roo.log(this.store.getAt(rowIndex+i));
6105             this.insertRow(this.store, rowIndex + i, false);
6106             return;
6107         }
6108         
6109     },
6110     
6111     
6112     refreshRow : function(record){
6113         var ds = this.store, index;
6114         if(typeof record == 'number'){
6115             index = record;
6116             record = ds.getAt(index);
6117         }else{
6118             index = ds.indexOf(record);
6119         }
6120         this.insertRow(ds, index, true);
6121         this.onRemove(ds, record, index+1, true);
6122         //this.syncRowHeights(index, index);
6123         //this.layout();
6124         this.fireEvent("rowupdated", this, index, record);
6125     },
6126     
6127     insertRow : function(dm, rowIndex, isUpdate){
6128         
6129         if(!isUpdate){
6130             this.fireEvent("beforerowsinserted", this, rowIndex);
6131         }
6132             //var s = this.getScrollState();
6133         var row = this.renderRow(this.cm, this.store, rowIndex);
6134         // insert before rowIndex..
6135         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6136         
6137         var _this = this;
6138                 
6139         if(row.cellObjects.length){
6140             Roo.each(row.cellObjects, function(r){
6141                 _this.renderCellObject(r);
6142             })
6143         }
6144             
6145         if(!isUpdate){
6146             this.fireEvent("rowsinserted", this, rowIndex);
6147             //this.syncRowHeights(firstRow, lastRow);
6148             //this.stripeRows(firstRow);
6149             //this.layout();
6150         }
6151         
6152     },
6153     
6154     
6155     getRowDom : function(rowIndex)
6156     {
6157         var rows = this.el.select('tbody > tr', true).elements;
6158         
6159         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6160         
6161     },
6162     // returns the object tree for a tr..
6163   
6164     
6165     renderRow : function(cm, ds, rowIndex) 
6166     {
6167         
6168         var d = ds.getAt(rowIndex);
6169         
6170         var row = {
6171             tag : 'tr',
6172             cn : []
6173         };
6174             
6175         var cellObjects = [];
6176         
6177         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6178             var config = cm.config[i];
6179             
6180             var renderer = cm.getRenderer(i);
6181             var value = '';
6182             var id = false;
6183             
6184             if(typeof(renderer) !== 'undefined'){
6185                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6186             }
6187             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6188             // and are rendered into the cells after the row is rendered - using the id for the element.
6189             
6190             if(typeof(value) === 'object'){
6191                 id = Roo.id();
6192                 cellObjects.push({
6193                     container : id,
6194                     cfg : value 
6195                 })
6196             }
6197             
6198             var rowcfg = {
6199                 record: d,
6200                 rowIndex : rowIndex,
6201                 colIndex : i,
6202                 rowClass : ''
6203             }
6204
6205             this.fireEvent('rowclass', this, rowcfg);
6206             
6207             var td = {
6208                 tag: 'td',
6209                 cls : rowcfg.rowClass,
6210                 style: '',
6211                 html: (typeof(value) === 'object') ? '' : value
6212             };
6213             
6214             if (id) {
6215                 td.id = id;
6216             }
6217             
6218             if(typeof(config.colspan) != 'undefined'){
6219                 td.colspan = config.colspan;
6220             }
6221             
6222             if(typeof(config.hidden) != 'undefined' && config.hidden){
6223                 td.style += ' display:none;';
6224             }
6225             
6226             if(typeof(config.align) != 'undefined' && config.align.length){
6227                 td.style += ' text-align:' + config.align + ';';
6228             }
6229             
6230             if(typeof(config.width) != 'undefined'){
6231                 td.style += ' width:' +  config.width + 'px;';
6232             }
6233             
6234             if(typeof(config.cursor) != 'undefined'){
6235                 td.style += ' cursor:' +  config.cursor + ';';
6236             }
6237             
6238             if(typeof(config.cls) != 'undefined'){
6239                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6240             }
6241              
6242             row.cn.push(td);
6243            
6244         }
6245         
6246         row.cellObjects = cellObjects;
6247         
6248         return row;
6249           
6250     },
6251     
6252     
6253     
6254     onBeforeLoad : function()
6255     {
6256         //Roo.log('ds onBeforeLoad');
6257         
6258         //this.clear();
6259         
6260         //if(this.loadMask){
6261         //    this.maskEl.show();
6262         //}
6263     },
6264      /**
6265      * Remove all rows
6266      */
6267     clear : function()
6268     {
6269         this.el.select('tbody', true).first().dom.innerHTML = '';
6270     },
6271     /**
6272      * Show or hide a row.
6273      * @param {Number} rowIndex to show or hide
6274      * @param {Boolean} state hide
6275      */
6276     setRowVisibility : function(rowIndex, state)
6277     {
6278         var bt = this.mainBody.dom;
6279         
6280         var rows = this.el.select('tbody > tr', true).elements;
6281         
6282         if(typeof(rows[rowIndex]) == 'undefined'){
6283             return;
6284         }
6285         rows[rowIndex].dom.style.display = state ? '' : 'none';
6286     },
6287     
6288     
6289     getSelectionModel : function(){
6290         if(!this.selModel){
6291             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6292         }
6293         return this.selModel;
6294     },
6295     /*
6296      * Render the Roo.bootstrap object from renderder
6297      */
6298     renderCellObject : function(r)
6299     {
6300         var _this = this;
6301         
6302         var t = r.cfg.render(r.container);
6303         
6304         if(r.cfg.cn){
6305             Roo.each(r.cfg.cn, function(c){
6306                 var child = {
6307                     container: t.getChildContainer(),
6308                     cfg: c
6309                 }
6310                 _this.renderCellObject(child);
6311             })
6312         }
6313     },
6314     
6315     getRowIndex : function(row)
6316     {
6317         var rowIndex = -1;
6318         
6319         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6320             if(el != row){
6321                 return;
6322             }
6323             
6324             rowIndex = index;
6325         });
6326         
6327         return rowIndex;
6328     }
6329    
6330 });
6331
6332  
6333
6334  /*
6335  * - LGPL
6336  *
6337  * table cell
6338  * 
6339  */
6340
6341 /**
6342  * @class Roo.bootstrap.TableCell
6343  * @extends Roo.bootstrap.Component
6344  * Bootstrap TableCell class
6345  * @cfg {String} html cell contain text
6346  * @cfg {String} cls cell class
6347  * @cfg {String} tag cell tag (td|th) default td
6348  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6349  * @cfg {String} align Aligns the content in a cell
6350  * @cfg {String} axis Categorizes cells
6351  * @cfg {String} bgcolor Specifies the background color of a cell
6352  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6353  * @cfg {Number} colspan Specifies the number of columns a cell should span
6354  * @cfg {String} headers Specifies one or more header cells a cell is related to
6355  * @cfg {Number} height Sets the height of a cell
6356  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6357  * @cfg {Number} rowspan Sets the number of rows a cell should span
6358  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6359  * @cfg {String} valign Vertical aligns the content in a cell
6360  * @cfg {Number} width Specifies the width of a cell
6361  * 
6362  * @constructor
6363  * Create a new TableCell
6364  * @param {Object} config The config object
6365  */
6366
6367 Roo.bootstrap.TableCell = function(config){
6368     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6369 };
6370
6371 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6372     
6373     html: false,
6374     cls: false,
6375     tag: false,
6376     abbr: false,
6377     align: false,
6378     axis: false,
6379     bgcolor: false,
6380     charoff: false,
6381     colspan: false,
6382     headers: false,
6383     height: false,
6384     nowrap: false,
6385     rowspan: false,
6386     scope: false,
6387     valign: false,
6388     width: false,
6389     
6390     
6391     getAutoCreate : function(){
6392         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6393         
6394         cfg = {
6395             tag: 'td'
6396         }
6397         
6398         if(this.tag){
6399             cfg.tag = this.tag;
6400         }
6401         
6402         if (this.html) {
6403             cfg.html=this.html
6404         }
6405         if (this.cls) {
6406             cfg.cls=this.cls
6407         }
6408         if (this.abbr) {
6409             cfg.abbr=this.abbr
6410         }
6411         if (this.align) {
6412             cfg.align=this.align
6413         }
6414         if (this.axis) {
6415             cfg.axis=this.axis
6416         }
6417         if (this.bgcolor) {
6418             cfg.bgcolor=this.bgcolor
6419         }
6420         if (this.charoff) {
6421             cfg.charoff=this.charoff
6422         }
6423         if (this.colspan) {
6424             cfg.colspan=this.colspan
6425         }
6426         if (this.headers) {
6427             cfg.headers=this.headers
6428         }
6429         if (this.height) {
6430             cfg.height=this.height
6431         }
6432         if (this.nowrap) {
6433             cfg.nowrap=this.nowrap
6434         }
6435         if (this.rowspan) {
6436             cfg.rowspan=this.rowspan
6437         }
6438         if (this.scope) {
6439             cfg.scope=this.scope
6440         }
6441         if (this.valign) {
6442             cfg.valign=this.valign
6443         }
6444         if (this.width) {
6445             cfg.width=this.width
6446         }
6447         
6448         
6449         return cfg;
6450     }
6451    
6452 });
6453
6454  
6455
6456  /*
6457  * - LGPL
6458  *
6459  * table row
6460  * 
6461  */
6462
6463 /**
6464  * @class Roo.bootstrap.TableRow
6465  * @extends Roo.bootstrap.Component
6466  * Bootstrap TableRow class
6467  * @cfg {String} cls row class
6468  * @cfg {String} align Aligns the content in a table row
6469  * @cfg {String} bgcolor Specifies a background color for a table row
6470  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6471  * @cfg {String} valign Vertical aligns the content in a table row
6472  * 
6473  * @constructor
6474  * Create a new TableRow
6475  * @param {Object} config The config object
6476  */
6477
6478 Roo.bootstrap.TableRow = function(config){
6479     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6480 };
6481
6482 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6483     
6484     cls: false,
6485     align: false,
6486     bgcolor: false,
6487     charoff: false,
6488     valign: false,
6489     
6490     getAutoCreate : function(){
6491         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6492         
6493         cfg = {
6494             tag: 'tr'
6495         }
6496             
6497         if(this.cls){
6498             cfg.cls = this.cls;
6499         }
6500         if(this.align){
6501             cfg.align = this.align;
6502         }
6503         if(this.bgcolor){
6504             cfg.bgcolor = this.bgcolor;
6505         }
6506         if(this.charoff){
6507             cfg.charoff = this.charoff;
6508         }
6509         if(this.valign){
6510             cfg.valign = this.valign;
6511         }
6512         
6513         return cfg;
6514     }
6515    
6516 });
6517
6518  
6519
6520  /*
6521  * - LGPL
6522  *
6523  * table body
6524  * 
6525  */
6526
6527 /**
6528  * @class Roo.bootstrap.TableBody
6529  * @extends Roo.bootstrap.Component
6530  * Bootstrap TableBody class
6531  * @cfg {String} cls element class
6532  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6533  * @cfg {String} align Aligns the content inside the element
6534  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6535  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6536  * 
6537  * @constructor
6538  * Create a new TableBody
6539  * @param {Object} config The config object
6540  */
6541
6542 Roo.bootstrap.TableBody = function(config){
6543     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6544 };
6545
6546 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6547     
6548     cls: false,
6549     tag: false,
6550     align: false,
6551     charoff: false,
6552     valign: false,
6553     
6554     getAutoCreate : function(){
6555         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6556         
6557         cfg = {
6558             tag: 'tbody'
6559         }
6560             
6561         if (this.cls) {
6562             cfg.cls=this.cls
6563         }
6564         if(this.tag){
6565             cfg.tag = this.tag;
6566         }
6567         
6568         if(this.align){
6569             cfg.align = this.align;
6570         }
6571         if(this.charoff){
6572             cfg.charoff = this.charoff;
6573         }
6574         if(this.valign){
6575             cfg.valign = this.valign;
6576         }
6577         
6578         return cfg;
6579     }
6580     
6581     
6582 //    initEvents : function()
6583 //    {
6584 //        
6585 //        if(!this.store){
6586 //            return;
6587 //        }
6588 //        
6589 //        this.store = Roo.factory(this.store, Roo.data);
6590 //        this.store.on('load', this.onLoad, this);
6591 //        
6592 //        this.store.load();
6593 //        
6594 //    },
6595 //    
6596 //    onLoad: function () 
6597 //    {   
6598 //        this.fireEvent('load', this);
6599 //    }
6600 //    
6601 //   
6602 });
6603
6604  
6605
6606  /*
6607  * Based on:
6608  * Ext JS Library 1.1.1
6609  * Copyright(c) 2006-2007, Ext JS, LLC.
6610  *
6611  * Originally Released Under LGPL - original licence link has changed is not relivant.
6612  *
6613  * Fork - LGPL
6614  * <script type="text/javascript">
6615  */
6616
6617 // as we use this in bootstrap.
6618 Roo.namespace('Roo.form');
6619  /**
6620  * @class Roo.form.Action
6621  * Internal Class used to handle form actions
6622  * @constructor
6623  * @param {Roo.form.BasicForm} el The form element or its id
6624  * @param {Object} config Configuration options
6625  */
6626
6627  
6628  
6629 // define the action interface
6630 Roo.form.Action = function(form, options){
6631     this.form = form;
6632     this.options = options || {};
6633 };
6634 /**
6635  * Client Validation Failed
6636  * @const 
6637  */
6638 Roo.form.Action.CLIENT_INVALID = 'client';
6639 /**
6640  * Server Validation Failed
6641  * @const 
6642  */
6643 Roo.form.Action.SERVER_INVALID = 'server';
6644  /**
6645  * Connect to Server Failed
6646  * @const 
6647  */
6648 Roo.form.Action.CONNECT_FAILURE = 'connect';
6649 /**
6650  * Reading Data from Server Failed
6651  * @const 
6652  */
6653 Roo.form.Action.LOAD_FAILURE = 'load';
6654
6655 Roo.form.Action.prototype = {
6656     type : 'default',
6657     failureType : undefined,
6658     response : undefined,
6659     result : undefined,
6660
6661     // interface method
6662     run : function(options){
6663
6664     },
6665
6666     // interface method
6667     success : function(response){
6668
6669     },
6670
6671     // interface method
6672     handleResponse : function(response){
6673
6674     },
6675
6676     // default connection failure
6677     failure : function(response){
6678         
6679         this.response = response;
6680         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6681         this.form.afterAction(this, false);
6682     },
6683
6684     processResponse : function(response){
6685         this.response = response;
6686         if(!response.responseText){
6687             return true;
6688         }
6689         this.result = this.handleResponse(response);
6690         return this.result;
6691     },
6692
6693     // utility functions used internally
6694     getUrl : function(appendParams){
6695         var url = this.options.url || this.form.url || this.form.el.dom.action;
6696         if(appendParams){
6697             var p = this.getParams();
6698             if(p){
6699                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6700             }
6701         }
6702         return url;
6703     },
6704
6705     getMethod : function(){
6706         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6707     },
6708
6709     getParams : function(){
6710         var bp = this.form.baseParams;
6711         var p = this.options.params;
6712         if(p){
6713             if(typeof p == "object"){
6714                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6715             }else if(typeof p == 'string' && bp){
6716                 p += '&' + Roo.urlEncode(bp);
6717             }
6718         }else if(bp){
6719             p = Roo.urlEncode(bp);
6720         }
6721         return p;
6722     },
6723
6724     createCallback : function(){
6725         return {
6726             success: this.success,
6727             failure: this.failure,
6728             scope: this,
6729             timeout: (this.form.timeout*1000),
6730             upload: this.form.fileUpload ? this.success : undefined
6731         };
6732     }
6733 };
6734
6735 Roo.form.Action.Submit = function(form, options){
6736     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6737 };
6738
6739 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6740     type : 'submit',
6741
6742     haveProgress : false,
6743     uploadComplete : false,
6744     
6745     // uploadProgress indicator.
6746     uploadProgress : function()
6747     {
6748         if (!this.form.progressUrl) {
6749             return;
6750         }
6751         
6752         if (!this.haveProgress) {
6753             Roo.MessageBox.progress("Uploading", "Uploading");
6754         }
6755         if (this.uploadComplete) {
6756            Roo.MessageBox.hide();
6757            return;
6758         }
6759         
6760         this.haveProgress = true;
6761    
6762         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6763         
6764         var c = new Roo.data.Connection();
6765         c.request({
6766             url : this.form.progressUrl,
6767             params: {
6768                 id : uid
6769             },
6770             method: 'GET',
6771             success : function(req){
6772                //console.log(data);
6773                 var rdata = false;
6774                 var edata;
6775                 try  {
6776                    rdata = Roo.decode(req.responseText)
6777                 } catch (e) {
6778                     Roo.log("Invalid data from server..");
6779                     Roo.log(edata);
6780                     return;
6781                 }
6782                 if (!rdata || !rdata.success) {
6783                     Roo.log(rdata);
6784                     Roo.MessageBox.alert(Roo.encode(rdata));
6785                     return;
6786                 }
6787                 var data = rdata.data;
6788                 
6789                 if (this.uploadComplete) {
6790                    Roo.MessageBox.hide();
6791                    return;
6792                 }
6793                    
6794                 if (data){
6795                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6796                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6797                     );
6798                 }
6799                 this.uploadProgress.defer(2000,this);
6800             },
6801        
6802             failure: function(data) {
6803                 Roo.log('progress url failed ');
6804                 Roo.log(data);
6805             },
6806             scope : this
6807         });
6808            
6809     },
6810     
6811     
6812     run : function()
6813     {
6814         // run get Values on the form, so it syncs any secondary forms.
6815         this.form.getValues();
6816         
6817         var o = this.options;
6818         var method = this.getMethod();
6819         var isPost = method == 'POST';
6820         if(o.clientValidation === false || this.form.isValid()){
6821             
6822             if (this.form.progressUrl) {
6823                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6824                     (new Date() * 1) + '' + Math.random());
6825                     
6826             } 
6827             
6828             
6829             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6830                 form:this.form.el.dom,
6831                 url:this.getUrl(!isPost),
6832                 method: method,
6833                 params:isPost ? this.getParams() : null,
6834                 isUpload: this.form.fileUpload
6835             }));
6836             
6837             this.uploadProgress();
6838
6839         }else if (o.clientValidation !== false){ // client validation failed
6840             this.failureType = Roo.form.Action.CLIENT_INVALID;
6841             this.form.afterAction(this, false);
6842         }
6843     },
6844
6845     success : function(response)
6846     {
6847         this.uploadComplete= true;
6848         if (this.haveProgress) {
6849             Roo.MessageBox.hide();
6850         }
6851         
6852         
6853         var result = this.processResponse(response);
6854         if(result === true || result.success){
6855             this.form.afterAction(this, true);
6856             return;
6857         }
6858         if(result.errors){
6859             this.form.markInvalid(result.errors);
6860             this.failureType = Roo.form.Action.SERVER_INVALID;
6861         }
6862         this.form.afterAction(this, false);
6863     },
6864     failure : function(response)
6865     {
6866         this.uploadComplete= true;
6867         if (this.haveProgress) {
6868             Roo.MessageBox.hide();
6869         }
6870         
6871         this.response = response;
6872         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6873         this.form.afterAction(this, false);
6874     },
6875     
6876     handleResponse : function(response){
6877         if(this.form.errorReader){
6878             var rs = this.form.errorReader.read(response);
6879             var errors = [];
6880             if(rs.records){
6881                 for(var i = 0, len = rs.records.length; i < len; i++) {
6882                     var r = rs.records[i];
6883                     errors[i] = r.data;
6884                 }
6885             }
6886             if(errors.length < 1){
6887                 errors = null;
6888             }
6889             return {
6890                 success : rs.success,
6891                 errors : errors
6892             };
6893         }
6894         var ret = false;
6895         try {
6896             ret = Roo.decode(response.responseText);
6897         } catch (e) {
6898             ret = {
6899                 success: false,
6900                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6901                 errors : []
6902             };
6903         }
6904         return ret;
6905         
6906     }
6907 });
6908
6909
6910 Roo.form.Action.Load = function(form, options){
6911     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6912     this.reader = this.form.reader;
6913 };
6914
6915 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6916     type : 'load',
6917
6918     run : function(){
6919         
6920         Roo.Ajax.request(Roo.apply(
6921                 this.createCallback(), {
6922                     method:this.getMethod(),
6923                     url:this.getUrl(false),
6924                     params:this.getParams()
6925         }));
6926     },
6927
6928     success : function(response){
6929         
6930         var result = this.processResponse(response);
6931         if(result === true || !result.success || !result.data){
6932             this.failureType = Roo.form.Action.LOAD_FAILURE;
6933             this.form.afterAction(this, false);
6934             return;
6935         }
6936         this.form.clearInvalid();
6937         this.form.setValues(result.data);
6938         this.form.afterAction(this, true);
6939     },
6940
6941     handleResponse : function(response){
6942         if(this.form.reader){
6943             var rs = this.form.reader.read(response);
6944             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6945             return {
6946                 success : rs.success,
6947                 data : data
6948             };
6949         }
6950         return Roo.decode(response.responseText);
6951     }
6952 });
6953
6954 Roo.form.Action.ACTION_TYPES = {
6955     'load' : Roo.form.Action.Load,
6956     'submit' : Roo.form.Action.Submit
6957 };/*
6958  * - LGPL
6959  *
6960  * form
6961  * 
6962  */
6963
6964 /**
6965  * @class Roo.bootstrap.Form
6966  * @extends Roo.bootstrap.Component
6967  * Bootstrap Form class
6968  * @cfg {String} method  GET | POST (default POST)
6969  * @cfg {String} labelAlign top | left (default top)
6970  * @cfg {String} align left  | right - for navbars
6971  * @cfg {Boolean} loadMask load mask when submit (default true)
6972
6973  * 
6974  * @constructor
6975  * Create a new Form
6976  * @param {Object} config The config object
6977  */
6978
6979
6980 Roo.bootstrap.Form = function(config){
6981     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6982     this.addEvents({
6983         /**
6984          * @event clientvalidation
6985          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6986          * @param {Form} this
6987          * @param {Boolean} valid true if the form has passed client-side validation
6988          */
6989         clientvalidation: true,
6990         /**
6991          * @event beforeaction
6992          * Fires before any action is performed. Return false to cancel the action.
6993          * @param {Form} this
6994          * @param {Action} action The action to be performed
6995          */
6996         beforeaction: true,
6997         /**
6998          * @event actionfailed
6999          * Fires when an action fails.
7000          * @param {Form} this
7001          * @param {Action} action The action that failed
7002          */
7003         actionfailed : true,
7004         /**
7005          * @event actioncomplete
7006          * Fires when an action is completed.
7007          * @param {Form} this
7008          * @param {Action} action The action that completed
7009          */
7010         actioncomplete : true
7011     });
7012     
7013 };
7014
7015 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7016       
7017      /**
7018      * @cfg {String} method
7019      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7020      */
7021     method : 'POST',
7022     /**
7023      * @cfg {String} url
7024      * The URL to use for form actions if one isn't supplied in the action options.
7025      */
7026     /**
7027      * @cfg {Boolean} fileUpload
7028      * Set to true if this form is a file upload.
7029      */
7030      
7031     /**
7032      * @cfg {Object} baseParams
7033      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7034      */
7035       
7036     /**
7037      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7038      */
7039     timeout: 30,
7040     /**
7041      * @cfg {Sting} align (left|right) for navbar forms
7042      */
7043     align : 'left',
7044
7045     // private
7046     activeAction : null,
7047  
7048     /**
7049      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7050      * element by passing it or its id or mask the form itself by passing in true.
7051      * @type Mixed
7052      */
7053     waitMsgTarget : false,
7054     
7055     loadMask : true,
7056     
7057     getAutoCreate : function(){
7058         
7059         var cfg = {
7060             tag: 'form',
7061             method : this.method || 'POST',
7062             id : this.id || Roo.id(),
7063             cls : ''
7064         }
7065         if (this.parent().xtype.match(/^Nav/)) {
7066             cfg.cls = 'navbar-form navbar-' + this.align;
7067             
7068         }
7069         
7070         if (this.labelAlign == 'left' ) {
7071             cfg.cls += ' form-horizontal';
7072         }
7073         
7074         
7075         return cfg;
7076     },
7077     initEvents : function()
7078     {
7079         this.el.on('submit', this.onSubmit, this);
7080         // this was added as random key presses on the form where triggering form submit.
7081         this.el.on('keypress', function(e) {
7082             if (e.getCharCode() != 13) {
7083                 return true;
7084             }
7085             // we might need to allow it for textareas.. and some other items.
7086             // check e.getTarget().
7087             
7088             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7089                 return true;
7090             }
7091         
7092             Roo.log("keypress blocked");
7093             
7094             e.preventDefault();
7095             return false;
7096         });
7097         
7098     },
7099     // private
7100     onSubmit : function(e){
7101         e.stopEvent();
7102     },
7103     
7104      /**
7105      * Returns true if client-side validation on the form is successful.
7106      * @return Boolean
7107      */
7108     isValid : function(){
7109         var items = this.getItems();
7110         var valid = true;
7111         items.each(function(f){
7112            if(!f.validate()){
7113                valid = false;
7114                
7115            }
7116         });
7117         return valid;
7118     },
7119     /**
7120      * Returns true if any fields in this form have changed since their original load.
7121      * @return Boolean
7122      */
7123     isDirty : function(){
7124         var dirty = false;
7125         var items = this.getItems();
7126         items.each(function(f){
7127            if(f.isDirty()){
7128                dirty = true;
7129                return false;
7130            }
7131            return true;
7132         });
7133         return dirty;
7134     },
7135      /**
7136      * Performs a predefined action (submit or load) or custom actions you define on this form.
7137      * @param {String} actionName The name of the action type
7138      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7139      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7140      * accept other config options):
7141      * <pre>
7142 Property          Type             Description
7143 ----------------  ---------------  ----------------------------------------------------------------------------------
7144 url               String           The url for the action (defaults to the form's url)
7145 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7146 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7147 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7148                                    validate the form on the client (defaults to false)
7149      * </pre>
7150      * @return {BasicForm} this
7151      */
7152     doAction : function(action, options){
7153         if(typeof action == 'string'){
7154             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7155         }
7156         if(this.fireEvent('beforeaction', this, action) !== false){
7157             this.beforeAction(action);
7158             action.run.defer(100, action);
7159         }
7160         return this;
7161     },
7162     
7163     // private
7164     beforeAction : function(action){
7165         var o = action.options;
7166         
7167         if(this.loadMask){
7168             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7169         }
7170         // not really supported yet.. ??
7171         
7172         //if(this.waitMsgTarget === true){
7173         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7174         //}else if(this.waitMsgTarget){
7175         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7176         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7177         //}else {
7178         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7179        // }
7180          
7181     },
7182
7183     // private
7184     afterAction : function(action, success){
7185         this.activeAction = null;
7186         var o = action.options;
7187         
7188         //if(this.waitMsgTarget === true){
7189             this.el.unmask();
7190         //}else if(this.waitMsgTarget){
7191         //    this.waitMsgTarget.unmask();
7192         //}else{
7193         //    Roo.MessageBox.updateProgress(1);
7194         //    Roo.MessageBox.hide();
7195        // }
7196         // 
7197         if(success){
7198             if(o.reset){
7199                 this.reset();
7200             }
7201             Roo.callback(o.success, o.scope, [this, action]);
7202             this.fireEvent('actioncomplete', this, action);
7203             
7204         }else{
7205             
7206             // failure condition..
7207             // we have a scenario where updates need confirming.
7208             // eg. if a locking scenario exists..
7209             // we look for { errors : { needs_confirm : true }} in the response.
7210             if (
7211                 (typeof(action.result) != 'undefined')  &&
7212                 (typeof(action.result.errors) != 'undefined')  &&
7213                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7214            ){
7215                 var _t = this;
7216                 Roo.log("not supported yet");
7217                  /*
7218                 
7219                 Roo.MessageBox.confirm(
7220                     "Change requires confirmation",
7221                     action.result.errorMsg,
7222                     function(r) {
7223                         if (r != 'yes') {
7224                             return;
7225                         }
7226                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7227                     }
7228                     
7229                 );
7230                 */
7231                 
7232                 
7233                 return;
7234             }
7235             
7236             Roo.callback(o.failure, o.scope, [this, action]);
7237             // show an error message if no failed handler is set..
7238             if (!this.hasListener('actionfailed')) {
7239                 Roo.log("need to add dialog support");
7240                 /*
7241                 Roo.MessageBox.alert("Error",
7242                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7243                         action.result.errorMsg :
7244                         "Saving Failed, please check your entries or try again"
7245                 );
7246                 */
7247             }
7248             
7249             this.fireEvent('actionfailed', this, action);
7250         }
7251         
7252     },
7253     /**
7254      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7255      * @param {String} id The value to search for
7256      * @return Field
7257      */
7258     findField : function(id){
7259         var items = this.getItems();
7260         var field = items.get(id);
7261         if(!field){
7262              items.each(function(f){
7263                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7264                     field = f;
7265                     return false;
7266                 }
7267                 return true;
7268             });
7269         }
7270         return field || null;
7271     },
7272      /**
7273      * Mark fields in this form invalid in bulk.
7274      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7275      * @return {BasicForm} this
7276      */
7277     markInvalid : function(errors){
7278         if(errors instanceof Array){
7279             for(var i = 0, len = errors.length; i < len; i++){
7280                 var fieldError = errors[i];
7281                 var f = this.findField(fieldError.id);
7282                 if(f){
7283                     f.markInvalid(fieldError.msg);
7284                 }
7285             }
7286         }else{
7287             var field, id;
7288             for(id in errors){
7289                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7290                     field.markInvalid(errors[id]);
7291                 }
7292             }
7293         }
7294         //Roo.each(this.childForms || [], function (f) {
7295         //    f.markInvalid(errors);
7296         //});
7297         
7298         return this;
7299     },
7300
7301     /**
7302      * Set values for fields in this form in bulk.
7303      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7304      * @return {BasicForm} this
7305      */
7306     setValues : function(values){
7307         if(values instanceof Array){ // array of objects
7308             for(var i = 0, len = values.length; i < len; i++){
7309                 var v = values[i];
7310                 var f = this.findField(v.id);
7311                 if(f){
7312                     f.setValue(v.value);
7313                     if(this.trackResetOnLoad){
7314                         f.originalValue = f.getValue();
7315                     }
7316                 }
7317             }
7318         }else{ // object hash
7319             var field, id;
7320             for(id in values){
7321                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7322                     
7323                     if (field.setFromData && 
7324                         field.valueField && 
7325                         field.displayField &&
7326                         // combos' with local stores can 
7327                         // be queried via setValue()
7328                         // to set their value..
7329                         (field.store && !field.store.isLocal)
7330                         ) {
7331                         // it's a combo
7332                         var sd = { };
7333                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7334                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7335                         field.setFromData(sd);
7336                         
7337                     } else {
7338                         field.setValue(values[id]);
7339                     }
7340                     
7341                     
7342                     if(this.trackResetOnLoad){
7343                         field.originalValue = field.getValue();
7344                     }
7345                 }
7346             }
7347         }
7348          
7349         //Roo.each(this.childForms || [], function (f) {
7350         //    f.setValues(values);
7351         //});
7352                 
7353         return this;
7354     },
7355
7356     /**
7357      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7358      * they are returned as an array.
7359      * @param {Boolean} asString
7360      * @return {Object}
7361      */
7362     getValues : function(asString){
7363         //if (this.childForms) {
7364             // copy values from the child forms
7365         //    Roo.each(this.childForms, function (f) {
7366         //        this.setValues(f.getValues());
7367         //    }, this);
7368         //}
7369         
7370         
7371         
7372         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7373         if(asString === true){
7374             return fs;
7375         }
7376         return Roo.urlDecode(fs);
7377     },
7378     
7379     /**
7380      * Returns the fields in this form as an object with key/value pairs. 
7381      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7382      * @return {Object}
7383      */
7384     getFieldValues : function(with_hidden)
7385     {
7386         var items = this.getItems();
7387         var ret = {};
7388         items.each(function(f){
7389             if (!f.getName()) {
7390                 return;
7391             }
7392             var v = f.getValue();
7393             if (f.inputType =='radio') {
7394                 if (typeof(ret[f.getName()]) == 'undefined') {
7395                     ret[f.getName()] = ''; // empty..
7396                 }
7397                 
7398                 if (!f.el.dom.checked) {
7399                     return;
7400                     
7401                 }
7402                 v = f.el.dom.value;
7403                 
7404             }
7405             
7406             // not sure if this supported any more..
7407             if ((typeof(v) == 'object') && f.getRawValue) {
7408                 v = f.getRawValue() ; // dates..
7409             }
7410             // combo boxes where name != hiddenName...
7411             if (f.name != f.getName()) {
7412                 ret[f.name] = f.getRawValue();
7413             }
7414             ret[f.getName()] = v;
7415         });
7416         
7417         return ret;
7418     },
7419
7420     /**
7421      * Clears all invalid messages in this form.
7422      * @return {BasicForm} this
7423      */
7424     clearInvalid : function(){
7425         var items = this.getItems();
7426         
7427         items.each(function(f){
7428            f.clearInvalid();
7429         });
7430         
7431         
7432         
7433         return this;
7434     },
7435
7436     /**
7437      * Resets this form.
7438      * @return {BasicForm} this
7439      */
7440     reset : function(){
7441         var items = this.getItems();
7442         items.each(function(f){
7443             f.reset();
7444         });
7445         
7446         Roo.each(this.childForms || [], function (f) {
7447             f.reset();
7448         });
7449        
7450         
7451         return this;
7452     },
7453     getItems : function()
7454     {
7455         var r=new Roo.util.MixedCollection(false, function(o){
7456             return o.id || (o.id = Roo.id());
7457         });
7458         var iter = function(el) {
7459             if (el.inputEl) {
7460                 r.add(el);
7461             }
7462             if (!el.items) {
7463                 return;
7464             }
7465             Roo.each(el.items,function(e) {
7466                 iter(e);
7467             });
7468             
7469             
7470         };
7471         
7472         iter(this);
7473         return r;
7474         
7475         
7476         
7477         
7478     }
7479     
7480 });
7481
7482  
7483 /*
7484  * Based on:
7485  * Ext JS Library 1.1.1
7486  * Copyright(c) 2006-2007, Ext JS, LLC.
7487  *
7488  * Originally Released Under LGPL - original licence link has changed is not relivant.
7489  *
7490  * Fork - LGPL
7491  * <script type="text/javascript">
7492  */
7493 /**
7494  * @class Roo.form.VTypes
7495  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7496  * @singleton
7497  */
7498 Roo.form.VTypes = function(){
7499     // closure these in so they are only created once.
7500     var alpha = /^[a-zA-Z_]+$/;
7501     var alphanum = /^[a-zA-Z0-9_]+$/;
7502     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7503     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7504
7505     // All these messages and functions are configurable
7506     return {
7507         /**
7508          * The function used to validate email addresses
7509          * @param {String} value The email address
7510          */
7511         'email' : function(v){
7512             return email.test(v);
7513         },
7514         /**
7515          * The error text to display when the email validation function returns false
7516          * @type String
7517          */
7518         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7519         /**
7520          * The keystroke filter mask to be applied on email input
7521          * @type RegExp
7522          */
7523         'emailMask' : /[a-z0-9_\.\-@]/i,
7524
7525         /**
7526          * The function used to validate URLs
7527          * @param {String} value The URL
7528          */
7529         'url' : function(v){
7530             return url.test(v);
7531         },
7532         /**
7533          * The error text to display when the url validation function returns false
7534          * @type String
7535          */
7536         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7537         
7538         /**
7539          * The function used to validate alpha values
7540          * @param {String} value The value
7541          */
7542         'alpha' : function(v){
7543             return alpha.test(v);
7544         },
7545         /**
7546          * The error text to display when the alpha validation function returns false
7547          * @type String
7548          */
7549         'alphaText' : 'This field should only contain letters and _',
7550         /**
7551          * The keystroke filter mask to be applied on alpha input
7552          * @type RegExp
7553          */
7554         'alphaMask' : /[a-z_]/i,
7555
7556         /**
7557          * The function used to validate alphanumeric values
7558          * @param {String} value The value
7559          */
7560         'alphanum' : function(v){
7561             return alphanum.test(v);
7562         },
7563         /**
7564          * The error text to display when the alphanumeric validation function returns false
7565          * @type String
7566          */
7567         'alphanumText' : 'This field should only contain letters, numbers and _',
7568         /**
7569          * The keystroke filter mask to be applied on alphanumeric input
7570          * @type RegExp
7571          */
7572         'alphanumMask' : /[a-z0-9_]/i
7573     };
7574 }();/*
7575  * - LGPL
7576  *
7577  * Input
7578  * 
7579  */
7580
7581 /**
7582  * @class Roo.bootstrap.Input
7583  * @extends Roo.bootstrap.Component
7584  * Bootstrap Input class
7585  * @cfg {Boolean} disabled is it disabled
7586  * @cfg {String} fieldLabel - the label associated
7587  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7588  * @cfg {String} name name of the input
7589  * @cfg {string} fieldLabel - the label associated
7590  * @cfg {string}  inputType - input / file submit ...
7591  * @cfg {string} placeholder - placeholder to put in text.
7592  * @cfg {string}  before - input group add on before
7593  * @cfg {string} after - input group add on after
7594  * @cfg {string} size - (lg|sm) or leave empty..
7595  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7596  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7597  * @cfg {Number} md colspan out of 12 for computer-sized screens
7598  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7599  * @cfg {string} value default value of the input
7600  * @cfg {Number} labelWidth set the width of label (0-12)
7601  * @cfg {String} labelAlign (top|left)
7602  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7603  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7604
7605  * @cfg {String} align (left|center|right) Default left
7606  * @cfg {Boolean} forceFeedback (true|false) Default false
7607  * 
7608  * 
7609  * 
7610  * 
7611  * @constructor
7612  * Create a new Input
7613  * @param {Object} config The config object
7614  */
7615
7616 Roo.bootstrap.Input = function(config){
7617     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7618    
7619         this.addEvents({
7620             /**
7621              * @event focus
7622              * Fires when this field receives input focus.
7623              * @param {Roo.form.Field} this
7624              */
7625             focus : true,
7626             /**
7627              * @event blur
7628              * Fires when this field loses input focus.
7629              * @param {Roo.form.Field} this
7630              */
7631             blur : true,
7632             /**
7633              * @event specialkey
7634              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7635              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7636              * @param {Roo.form.Field} this
7637              * @param {Roo.EventObject} e The event object
7638              */
7639             specialkey : true,
7640             /**
7641              * @event change
7642              * Fires just before the field blurs if the field value has changed.
7643              * @param {Roo.form.Field} this
7644              * @param {Mixed} newValue The new value
7645              * @param {Mixed} oldValue The original value
7646              */
7647             change : true,
7648             /**
7649              * @event invalid
7650              * Fires after the field has been marked as invalid.
7651              * @param {Roo.form.Field} this
7652              * @param {String} msg The validation message
7653              */
7654             invalid : true,
7655             /**
7656              * @event valid
7657              * Fires after the field has been validated with no errors.
7658              * @param {Roo.form.Field} this
7659              */
7660             valid : true,
7661              /**
7662              * @event keyup
7663              * Fires after the key up
7664              * @param {Roo.form.Field} this
7665              * @param {Roo.EventObject}  e The event Object
7666              */
7667             keyup : true
7668         });
7669 };
7670
7671 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7672      /**
7673      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7674       automatic validation (defaults to "keyup").
7675      */
7676     validationEvent : "keyup",
7677      /**
7678      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7679      */
7680     validateOnBlur : true,
7681     /**
7682      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7683      */
7684     validationDelay : 250,
7685      /**
7686      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7687      */
7688     focusClass : "x-form-focus",  // not needed???
7689     
7690        
7691     /**
7692      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7693      */
7694     invalidClass : "has-warning",
7695     
7696     /**
7697      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7698      */
7699     validClass : "has-success",
7700     
7701     /**
7702      * @cfg {Boolean} hasFeedback (true|false) default true
7703      */
7704     hasFeedback : true,
7705     
7706     /**
7707      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7708      */
7709     invalidFeedbackClass : "glyphicon-warning-sign",
7710     
7711     /**
7712      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7713      */
7714     validFeedbackClass : "glyphicon-ok",
7715     
7716     /**
7717      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7718      */
7719     selectOnFocus : false,
7720     
7721      /**
7722      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7723      */
7724     maskRe : null,
7725        /**
7726      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7727      */
7728     vtype : null,
7729     
7730       /**
7731      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7732      */
7733     disableKeyFilter : false,
7734     
7735        /**
7736      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7737      */
7738     disabled : false,
7739      /**
7740      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7741      */
7742     allowBlank : true,
7743     /**
7744      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7745      */
7746     blankText : "This field is required",
7747     
7748      /**
7749      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7750      */
7751     minLength : 0,
7752     /**
7753      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7754      */
7755     maxLength : Number.MAX_VALUE,
7756     /**
7757      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7758      */
7759     minLengthText : "The minimum length for this field is {0}",
7760     /**
7761      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7762      */
7763     maxLengthText : "The maximum length for this field is {0}",
7764   
7765     
7766     /**
7767      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7768      * If available, this function will be called only after the basic validators all return true, and will be passed the
7769      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7770      */
7771     validator : null,
7772     /**
7773      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7774      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7775      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7776      */
7777     regex : null,
7778     /**
7779      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7780      */
7781     regexText : "",
7782     
7783     autocomplete: false,
7784     
7785     
7786     fieldLabel : '',
7787     inputType : 'text',
7788     
7789     name : false,
7790     placeholder: false,
7791     before : false,
7792     after : false,
7793     size : false,
7794     hasFocus : false,
7795     preventMark: false,
7796     isFormField : true,
7797     value : '',
7798     labelWidth : 2,
7799     labelAlign : false,
7800     readOnly : false,
7801     align : false,
7802     formatedValue : false,
7803     forceFeedback : false,
7804     
7805     parentLabelAlign : function()
7806     {
7807         var parent = this;
7808         while (parent.parent()) {
7809             parent = parent.parent();
7810             if (typeof(parent.labelAlign) !='undefined') {
7811                 return parent.labelAlign;
7812             }
7813         }
7814         return 'left';
7815         
7816     },
7817     
7818     getAutoCreate : function(){
7819         
7820         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7821         
7822         var id = Roo.id();
7823         
7824         var cfg = {};
7825         
7826         if(this.inputType != 'hidden'){
7827             cfg.cls = 'form-group' //input-group
7828         }
7829         
7830         var input =  {
7831             tag: 'input',
7832             id : id,
7833             type : this.inputType,
7834             value : this.value,
7835             cls : 'form-control',
7836             placeholder : this.placeholder || '',
7837             autocomplete : this.autocomplete || 'new-password'
7838         };
7839         
7840         
7841         if(this.align){
7842             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7843         }
7844         
7845         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7846             input.maxLength = this.maxLength;
7847         }
7848         
7849         if (this.disabled) {
7850             input.disabled=true;
7851         }
7852         
7853         if (this.readOnly) {
7854             input.readonly=true;
7855         }
7856         
7857         if (this.name) {
7858             input.name = this.name;
7859         }
7860         if (this.size) {
7861             input.cls += ' input-' + this.size;
7862         }
7863         var settings=this;
7864         ['xs','sm','md','lg'].map(function(size){
7865             if (settings[size]) {
7866                 cfg.cls += ' col-' + size + '-' + settings[size];
7867             }
7868         });
7869         
7870         var inputblock = input;
7871         
7872         var feedback = {
7873             tag: 'span',
7874             cls: 'glyphicon form-control-feedback'
7875         };
7876             
7877         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7878             
7879             inputblock = {
7880                 cls : 'has-feedback',
7881                 cn :  [
7882                     input,
7883                     feedback
7884                 ] 
7885             };  
7886         }
7887         
7888         if (this.before || this.after) {
7889             
7890             inputblock = {
7891                 cls : 'input-group',
7892                 cn :  [] 
7893             };
7894             
7895             if (this.before && typeof(this.before) == 'string') {
7896                 
7897                 inputblock.cn.push({
7898                     tag :'span',
7899                     cls : 'roo-input-before input-group-addon',
7900                     html : this.before
7901                 });
7902             }
7903             if (this.before && typeof(this.before) == 'object') {
7904                 this.before = Roo.factory(this.before);
7905                 Roo.log(this.before);
7906                 inputblock.cn.push({
7907                     tag :'span',
7908                     cls : 'roo-input-before input-group-' +
7909                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7910                 });
7911             }
7912             
7913             inputblock.cn.push(input);
7914             
7915             if (this.after && typeof(this.after) == 'string') {
7916                 inputblock.cn.push({
7917                     tag :'span',
7918                     cls : 'roo-input-after input-group-addon',
7919                     html : this.after
7920                 });
7921             }
7922             if (this.after && typeof(this.after) == 'object') {
7923                 this.after = Roo.factory(this.after);
7924                 Roo.log(this.after);
7925                 inputblock.cn.push({
7926                     tag :'span',
7927                     cls : 'roo-input-after input-group-' +
7928                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7929                 });
7930             }
7931             
7932             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7933                 inputblock.cls += ' has-feedback';
7934                 inputblock.cn.push(feedback);
7935             }
7936         };
7937         
7938         if (align ==='left' && this.fieldLabel.length) {
7939                 Roo.log("left and has label");
7940                 cfg.cn = [
7941                     
7942                     {
7943                         tag: 'label',
7944                         'for' :  id,
7945                         cls : 'control-label col-sm-' + this.labelWidth,
7946                         html : this.fieldLabel
7947                         
7948                     },
7949                     {
7950                         cls : "col-sm-" + (12 - this.labelWidth), 
7951                         cn: [
7952                             inputblock
7953                         ]
7954                     }
7955                     
7956                 ];
7957         } else if ( this.fieldLabel.length) {
7958                 Roo.log(" label");
7959                  cfg.cn = [
7960                    
7961                     {
7962                         tag: 'label',
7963                         //cls : 'input-group-addon',
7964                         html : this.fieldLabel
7965                         
7966                     },
7967                     
7968                     inputblock
7969                     
7970                 ];
7971
7972         } else {
7973             
7974                 Roo.log(" no label && no align");
7975                 cfg.cn = [
7976                     
7977                         inputblock
7978                     
7979                 ];
7980                 
7981                 
7982         };
7983         Roo.log('input-parentType: ' + this.parentType);
7984         
7985         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7986            cfg.cls += ' navbar-form';
7987            Roo.log(cfg);
7988         }
7989         
7990         return cfg;
7991         
7992     },
7993     /**
7994      * return the real input element.
7995      */
7996     inputEl: function ()
7997     {
7998         return this.el.select('input.form-control',true).first();
7999     },
8000     
8001     tooltipEl : function()
8002     {
8003         return this.inputEl();
8004     },
8005     
8006     setDisabled : function(v)
8007     {
8008         var i  = this.inputEl().dom;
8009         if (!v) {
8010             i.removeAttribute('disabled');
8011             return;
8012             
8013         }
8014         i.setAttribute('disabled','true');
8015     },
8016     initEvents : function()
8017     {
8018           
8019         this.inputEl().on("keydown" , this.fireKey,  this);
8020         this.inputEl().on("focus", this.onFocus,  this);
8021         this.inputEl().on("blur", this.onBlur,  this);
8022         
8023         this.inputEl().relayEvent('keyup', this);
8024  
8025         // reference to original value for reset
8026         this.originalValue = this.getValue();
8027         //Roo.form.TextField.superclass.initEvents.call(this);
8028         if(this.validationEvent == 'keyup'){
8029             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8030             this.inputEl().on('keyup', this.filterValidation, this);
8031         }
8032         else if(this.validationEvent !== false){
8033             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8034         }
8035         
8036         if(this.selectOnFocus){
8037             this.on("focus", this.preFocus, this);
8038             
8039         }
8040         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8041             this.inputEl().on("keypress", this.filterKeys, this);
8042         }
8043        /* if(this.grow){
8044             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8045             this.el.on("click", this.autoSize,  this);
8046         }
8047         */
8048         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8049             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8050         }
8051         
8052         if (typeof(this.before) == 'object') {
8053             this.before.render(this.el.select('.roo-input-before',true).first());
8054         }
8055         if (typeof(this.after) == 'object') {
8056             this.after.render(this.el.select('.roo-input-after',true).first());
8057         }
8058         
8059         
8060     },
8061     filterValidation : function(e){
8062         if(!e.isNavKeyPress()){
8063             this.validationTask.delay(this.validationDelay);
8064         }
8065     },
8066      /**
8067      * Validates the field value
8068      * @return {Boolean} True if the value is valid, else false
8069      */
8070     validate : function(){
8071         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8072         if(this.disabled || this.validateValue(this.getRawValue())){
8073             this.markValid();
8074             return true;
8075         }
8076         
8077         this.markInvalid();
8078         return false;
8079     },
8080     
8081     
8082     /**
8083      * Validates a value according to the field's validation rules and marks the field as invalid
8084      * if the validation fails
8085      * @param {Mixed} value The value to validate
8086      * @return {Boolean} True if the value is valid, else false
8087      */
8088     validateValue : function(value){
8089         if(value.length < 1)  { // if it's blank
8090             if(this.allowBlank){
8091                 return true;
8092             }
8093             return false;
8094         }
8095         
8096         if(value.length < this.minLength){
8097             return false;
8098         }
8099         if(value.length > this.maxLength){
8100             return false;
8101         }
8102         if(this.vtype){
8103             var vt = Roo.form.VTypes;
8104             if(!vt[this.vtype](value, this)){
8105                 return false;
8106             }
8107         }
8108         if(typeof this.validator == "function"){
8109             var msg = this.validator(value);
8110             if(msg !== true){
8111                 return false;
8112             }
8113         }
8114         
8115         if(this.regex && !this.regex.test(value)){
8116             return false;
8117         }
8118         
8119         return true;
8120     },
8121
8122     
8123     
8124      // private
8125     fireKey : function(e){
8126         //Roo.log('field ' + e.getKey());
8127         if(e.isNavKeyPress()){
8128             this.fireEvent("specialkey", this, e);
8129         }
8130     },
8131     focus : function (selectText){
8132         if(this.rendered){
8133             this.inputEl().focus();
8134             if(selectText === true){
8135                 this.inputEl().dom.select();
8136             }
8137         }
8138         return this;
8139     } ,
8140     
8141     onFocus : function(){
8142         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8143            // this.el.addClass(this.focusClass);
8144         }
8145         if(!this.hasFocus){
8146             this.hasFocus = true;
8147             this.startValue = this.getValue();
8148             this.fireEvent("focus", this);
8149         }
8150     },
8151     
8152     beforeBlur : Roo.emptyFn,
8153
8154     
8155     // private
8156     onBlur : function(){
8157         this.beforeBlur();
8158         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8159             //this.el.removeClass(this.focusClass);
8160         }
8161         this.hasFocus = false;
8162         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8163             this.validate();
8164         }
8165         var v = this.getValue();
8166         if(String(v) !== String(this.startValue)){
8167             this.fireEvent('change', this, v, this.startValue);
8168         }
8169         this.fireEvent("blur", this);
8170     },
8171     
8172     /**
8173      * Resets the current field value to the originally loaded value and clears any validation messages
8174      */
8175     reset : function(){
8176         this.setValue(this.originalValue);
8177         this.validate();
8178     },
8179      /**
8180      * Returns the name of the field
8181      * @return {Mixed} name The name field
8182      */
8183     getName: function(){
8184         return this.name;
8185     },
8186      /**
8187      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8188      * @return {Mixed} value The field value
8189      */
8190     getValue : function(){
8191         
8192         var v = this.inputEl().getValue();
8193         
8194         return v;
8195     },
8196     /**
8197      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8198      * @return {Mixed} value The field value
8199      */
8200     getRawValue : function(){
8201         var v = this.inputEl().getValue();
8202         
8203         return v;
8204     },
8205     
8206     /**
8207      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8208      * @param {Mixed} value The value to set
8209      */
8210     setRawValue : function(v){
8211         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8212     },
8213     
8214     selectText : function(start, end){
8215         var v = this.getRawValue();
8216         if(v.length > 0){
8217             start = start === undefined ? 0 : start;
8218             end = end === undefined ? v.length : end;
8219             var d = this.inputEl().dom;
8220             if(d.setSelectionRange){
8221                 d.setSelectionRange(start, end);
8222             }else if(d.createTextRange){
8223                 var range = d.createTextRange();
8224                 range.moveStart("character", start);
8225                 range.moveEnd("character", v.length-end);
8226                 range.select();
8227             }
8228         }
8229     },
8230     
8231     /**
8232      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8233      * @param {Mixed} value The value to set
8234      */
8235     setValue : function(v){
8236         this.value = v;
8237         if(this.rendered){
8238             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8239             this.validate();
8240         }
8241     },
8242     
8243     /*
8244     processValue : function(value){
8245         if(this.stripCharsRe){
8246             var newValue = value.replace(this.stripCharsRe, '');
8247             if(newValue !== value){
8248                 this.setRawValue(newValue);
8249                 return newValue;
8250             }
8251         }
8252         return value;
8253     },
8254   */
8255     preFocus : function(){
8256         
8257         if(this.selectOnFocus){
8258             this.inputEl().dom.select();
8259         }
8260     },
8261     filterKeys : function(e){
8262         var k = e.getKey();
8263         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8264             return;
8265         }
8266         var c = e.getCharCode(), cc = String.fromCharCode(c);
8267         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8268             return;
8269         }
8270         if(!this.maskRe.test(cc)){
8271             e.stopEvent();
8272         }
8273     },
8274      /**
8275      * Clear any invalid styles/messages for this field
8276      */
8277     clearInvalid : function(){
8278         
8279         if(!this.el || this.preventMark){ // not rendered
8280             return;
8281         }
8282         this.el.removeClass(this.invalidClass);
8283         
8284         this.fireEvent('valid', this);
8285     },
8286     
8287      /**
8288      * Mark this field as valid
8289      */
8290     markValid : function(){
8291         if(!this.el  || this.preventMark){ // not rendered
8292             return;
8293         }
8294         
8295         this.el.removeClass([this.invalidClass, this.validClass]);
8296         
8297         var feedback = this.el.select('.form-control-feedback', true).first();
8298             
8299         if(feedback){
8300             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8301         }
8302
8303         if(this.disabled || this.allowBlank){
8304             return;
8305         }
8306         
8307         this.el.addClass(this.validClass);
8308         
8309         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8310             
8311             var feedback = this.el.select('.form-control-feedback', true).first();
8312             
8313             if(feedback){
8314                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8315                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8316             }
8317             
8318         }
8319         
8320         this.fireEvent('valid', this);
8321     },
8322     
8323      /**
8324      * Mark this field as invalid
8325      * @param {String} msg The validation message
8326      */
8327     markInvalid : function(msg){
8328         if(!this.el  || this.preventMark){ // not rendered
8329             return;
8330         }
8331         
8332         this.el.removeClass([this.invalidClass, this.validClass]);
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         }
8339
8340         if(this.disabled || this.allowBlank){
8341             return;
8342         }
8343         
8344         this.el.addClass(this.invalidClass);
8345         
8346         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8347             
8348             var feedback = this.el.select('.form-control-feedback', true).first();
8349             
8350             if(feedback){
8351                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8352                 
8353                 if(this.getValue().length || this.forceFeedback){
8354                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8355                 }
8356                 
8357             }
8358             
8359         }
8360         
8361         this.fireEvent('invalid', this, msg);
8362     },
8363     // private
8364     SafariOnKeyDown : function(event)
8365     {
8366         // this is a workaround for a password hang bug on chrome/ webkit.
8367         
8368         var isSelectAll = false;
8369         
8370         if(this.inputEl().dom.selectionEnd > 0){
8371             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8372         }
8373         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8374             event.preventDefault();
8375             this.setValue('');
8376             return;
8377         }
8378         
8379         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8380             
8381             event.preventDefault();
8382             // this is very hacky as keydown always get's upper case.
8383             //
8384             var cc = String.fromCharCode(event.getCharCode());
8385             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8386             
8387         }
8388     },
8389     adjustWidth : function(tag, w){
8390         tag = tag.toLowerCase();
8391         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8392             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8393                 if(tag == 'input'){
8394                     return w + 2;
8395                 }
8396                 if(tag == 'textarea'){
8397                     return w-2;
8398                 }
8399             }else if(Roo.isOpera){
8400                 if(tag == 'input'){
8401                     return w + 2;
8402                 }
8403                 if(tag == 'textarea'){
8404                     return w-2;
8405                 }
8406             }
8407         }
8408         return w;
8409     }
8410     
8411 });
8412
8413  
8414 /*
8415  * - LGPL
8416  *
8417  * Input
8418  * 
8419  */
8420
8421 /**
8422  * @class Roo.bootstrap.TextArea
8423  * @extends Roo.bootstrap.Input
8424  * Bootstrap TextArea class
8425  * @cfg {Number} cols Specifies the visible width of a text area
8426  * @cfg {Number} rows Specifies the visible number of lines in a text area
8427  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8428  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8429  * @cfg {string} html text
8430  * 
8431  * @constructor
8432  * Create a new TextArea
8433  * @param {Object} config The config object
8434  */
8435
8436 Roo.bootstrap.TextArea = function(config){
8437     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8438    
8439 };
8440
8441 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8442      
8443     cols : false,
8444     rows : 5,
8445     readOnly : false,
8446     warp : 'soft',
8447     resize : false,
8448     value: false,
8449     html: false,
8450     
8451     getAutoCreate : function(){
8452         
8453         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8454         
8455         var id = Roo.id();
8456         
8457         var cfg = {};
8458         
8459         var input =  {
8460             tag: 'textarea',
8461             id : id,
8462             warp : this.warp,
8463             rows : this.rows,
8464             value : this.value || '',
8465             html: this.html || '',
8466             cls : 'form-control',
8467             placeholder : this.placeholder || '' 
8468             
8469         };
8470         
8471         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8472             input.maxLength = this.maxLength;
8473         }
8474         
8475         if(this.resize){
8476             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8477         }
8478         
8479         if(this.cols){
8480             input.cols = this.cols;
8481         }
8482         
8483         if (this.readOnly) {
8484             input.readonly = true;
8485         }
8486         
8487         if (this.name) {
8488             input.name = this.name;
8489         }
8490         
8491         if (this.size) {
8492             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8493         }
8494         
8495         var settings=this;
8496         ['xs','sm','md','lg'].map(function(size){
8497             if (settings[size]) {
8498                 cfg.cls += ' col-' + size + '-' + settings[size];
8499             }
8500         });
8501         
8502         var inputblock = input;
8503         
8504         if(this.hasFeedback && !this.allowBlank){
8505             
8506             var feedback = {
8507                 tag: 'span',
8508                 cls: 'glyphicon form-control-feedback'
8509             };
8510
8511             inputblock = {
8512                 cls : 'has-feedback',
8513                 cn :  [
8514                     input,
8515                     feedback
8516                 ] 
8517             };  
8518         }
8519         
8520         
8521         if (this.before || this.after) {
8522             
8523             inputblock = {
8524                 cls : 'input-group',
8525                 cn :  [] 
8526             };
8527             if (this.before) {
8528                 inputblock.cn.push({
8529                     tag :'span',
8530                     cls : 'input-group-addon',
8531                     html : this.before
8532                 });
8533             }
8534             
8535             inputblock.cn.push(input);
8536             
8537             if(this.hasFeedback && !this.allowBlank){
8538                 inputblock.cls += ' has-feedback';
8539                 inputblock.cn.push(feedback);
8540             }
8541             
8542             if (this.after) {
8543                 inputblock.cn.push({
8544                     tag :'span',
8545                     cls : 'input-group-addon',
8546                     html : this.after
8547                 });
8548             }
8549             
8550         }
8551         
8552         if (align ==='left' && this.fieldLabel.length) {
8553                 Roo.log("left and has label");
8554                 cfg.cn = [
8555                     
8556                     {
8557                         tag: 'label',
8558                         'for' :  id,
8559                         cls : 'control-label col-sm-' + this.labelWidth,
8560                         html : this.fieldLabel
8561                         
8562                     },
8563                     {
8564                         cls : "col-sm-" + (12 - this.labelWidth), 
8565                         cn: [
8566                             inputblock
8567                         ]
8568                     }
8569                     
8570                 ];
8571         } else if ( this.fieldLabel.length) {
8572                 Roo.log(" label");
8573                  cfg.cn = [
8574                    
8575                     {
8576                         tag: 'label',
8577                         //cls : 'input-group-addon',
8578                         html : this.fieldLabel
8579                         
8580                     },
8581                     
8582                     inputblock
8583                     
8584                 ];
8585
8586         } else {
8587             
8588                    Roo.log(" no label && no align");
8589                 cfg.cn = [
8590                     
8591                         inputblock
8592                     
8593                 ];
8594                 
8595                 
8596         }
8597         
8598         if (this.disabled) {
8599             input.disabled=true;
8600         }
8601         
8602         return cfg;
8603         
8604     },
8605     /**
8606      * return the real textarea element.
8607      */
8608     inputEl: function ()
8609     {
8610         return this.el.select('textarea.form-control',true).first();
8611     }
8612 });
8613
8614  
8615 /*
8616  * - LGPL
8617  *
8618  * trigger field - base class for combo..
8619  * 
8620  */
8621  
8622 /**
8623  * @class Roo.bootstrap.TriggerField
8624  * @extends Roo.bootstrap.Input
8625  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8626  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8627  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8628  * for which you can provide a custom implementation.  For example:
8629  * <pre><code>
8630 var trigger = new Roo.bootstrap.TriggerField();
8631 trigger.onTriggerClick = myTriggerFn;
8632 trigger.applyTo('my-field');
8633 </code></pre>
8634  *
8635  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8636  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8637  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8638  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8639  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8640
8641  * @constructor
8642  * Create a new TriggerField.
8643  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8644  * to the base TextField)
8645  */
8646 Roo.bootstrap.TriggerField = function(config){
8647     this.mimicing = false;
8648     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8649 };
8650
8651 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8652     /**
8653      * @cfg {String} triggerClass A CSS class to apply to the trigger
8654      */
8655      /**
8656      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8657      */
8658     hideTrigger:false,
8659
8660     /**
8661      * @cfg {Boolean} removable (true|false) special filter default false
8662      */
8663     removable : false,
8664     
8665     /** @cfg {Boolean} grow @hide */
8666     /** @cfg {Number} growMin @hide */
8667     /** @cfg {Number} growMax @hide */
8668
8669     /**
8670      * @hide 
8671      * @method
8672      */
8673     autoSize: Roo.emptyFn,
8674     // private
8675     monitorTab : true,
8676     // private
8677     deferHeight : true,
8678
8679     
8680     actionMode : 'wrap',
8681     
8682     caret : false,
8683     
8684     
8685     getAutoCreate : function(){
8686        
8687         var align = this.labelAlign || this.parentLabelAlign();
8688         
8689         var id = Roo.id();
8690         
8691         var cfg = {
8692             cls: 'form-group' //input-group
8693         };
8694         
8695         
8696         var input =  {
8697             tag: 'input',
8698             id : id,
8699             type : this.inputType,
8700             cls : 'form-control',
8701             autocomplete: 'new-password',
8702             placeholder : this.placeholder || '' 
8703             
8704         };
8705         if (this.name) {
8706             input.name = this.name;
8707         }
8708         if (this.size) {
8709             input.cls += ' input-' + this.size;
8710         }
8711         
8712         if (this.disabled) {
8713             input.disabled=true;
8714         }
8715         
8716         var inputblock = input;
8717         
8718         if(this.hasFeedback && !this.allowBlank){
8719             
8720             var feedback = {
8721                 tag: 'span',
8722                 cls: 'glyphicon form-control-feedback'
8723             };
8724             
8725             if(this.removable && !this.editable && !this.tickable){
8726                 inputblock = {
8727                     cls : 'has-feedback',
8728                     cn :  [
8729                         inputblock,
8730                         {
8731                             tag: 'button',
8732                             html : 'x',
8733                             cls : 'roo-combo-removable-btn close'
8734                         },
8735                         feedback
8736                     ] 
8737                 };
8738             } else {
8739                 inputblock = {
8740                     cls : 'has-feedback',
8741                     cn :  [
8742                         inputblock,
8743                         feedback
8744                     ] 
8745                 };
8746             }
8747
8748         } else {
8749             if(this.removable && !this.editable && !this.tickable){
8750                 inputblock = {
8751                     cls : 'roo-removable',
8752                     cn :  [
8753                         inputblock,
8754                         {
8755                             tag: 'button',
8756                             html : 'x',
8757                             cls : 'roo-combo-removable-btn close'
8758                         }
8759                     ] 
8760                 };
8761             }
8762         }
8763         
8764         if (this.before || this.after) {
8765             
8766             inputblock = {
8767                 cls : 'input-group',
8768                 cn :  [] 
8769             };
8770             if (this.before) {
8771                 inputblock.cn.push({
8772                     tag :'span',
8773                     cls : 'input-group-addon',
8774                     html : this.before
8775                 });
8776             }
8777             
8778             inputblock.cn.push(input);
8779             
8780             if(this.hasFeedback && !this.allowBlank){
8781                 inputblock.cls += ' has-feedback';
8782                 inputblock.cn.push(feedback);
8783             }
8784             
8785             if (this.after) {
8786                 inputblock.cn.push({
8787                     tag :'span',
8788                     cls : 'input-group-addon',
8789                     html : this.after
8790                 });
8791             }
8792             
8793         };
8794         
8795         var box = {
8796             tag: 'div',
8797             cn: [
8798                 {
8799                     tag: 'input',
8800                     type : 'hidden',
8801                     cls: 'form-hidden-field'
8802                 },
8803                 inputblock
8804             ]
8805             
8806         };
8807         
8808         if(this.multiple){
8809             Roo.log('multiple');
8810             
8811             box = {
8812                 tag: 'div',
8813                 cn: [
8814                     {
8815                         tag: 'input',
8816                         type : 'hidden',
8817                         cls: 'form-hidden-field'
8818                     },
8819                     {
8820                         tag: 'ul',
8821                         cls: 'select2-choices',
8822                         cn:[
8823                             {
8824                                 tag: 'li',
8825                                 cls: 'select2-search-field',
8826                                 cn: [
8827
8828                                     inputblock
8829                                 ]
8830                             }
8831                         ]
8832                     }
8833                 ]
8834             }
8835         };
8836         
8837         var combobox = {
8838             cls: 'select2-container input-group',
8839             cn: [
8840                 box
8841 //                {
8842 //                    tag: 'ul',
8843 //                    cls: 'typeahead typeahead-long dropdown-menu',
8844 //                    style: 'display:none'
8845 //                }
8846             ]
8847         };
8848         
8849         if(!this.multiple && this.showToggleBtn){
8850             
8851             var caret = {
8852                         tag: 'span',
8853                         cls: 'caret'
8854              };
8855             if (this.caret != false) {
8856                 caret = {
8857                      tag: 'i',
8858                      cls: 'fa fa-' + this.caret
8859                 };
8860                 
8861             }
8862             
8863             combobox.cn.push({
8864                 tag :'span',
8865                 cls : 'input-group-addon btn dropdown-toggle',
8866                 cn : [
8867                     caret,
8868                     {
8869                         tag: 'span',
8870                         cls: 'combobox-clear',
8871                         cn  : [
8872                             {
8873                                 tag : 'i',
8874                                 cls: 'icon-remove'
8875                             }
8876                         ]
8877                     }
8878                 ]
8879
8880             })
8881         }
8882         
8883         if(this.multiple){
8884             combobox.cls += ' select2-container-multi';
8885         }
8886         
8887         if (align ==='left' && this.fieldLabel.length) {
8888             
8889                 Roo.log("left and has label");
8890                 cfg.cn = [
8891                     
8892                     {
8893                         tag: 'label',
8894                         'for' :  id,
8895                         cls : 'control-label col-sm-' + this.labelWidth,
8896                         html : this.fieldLabel
8897                         
8898                     },
8899                     {
8900                         cls : "col-sm-" + (12 - this.labelWidth), 
8901                         cn: [
8902                             combobox
8903                         ]
8904                     }
8905                     
8906                 ];
8907         } else if ( this.fieldLabel.length) {
8908                 Roo.log(" label");
8909                  cfg.cn = [
8910                    
8911                     {
8912                         tag: 'label',
8913                         //cls : 'input-group-addon',
8914                         html : this.fieldLabel
8915                         
8916                     },
8917                     
8918                     combobox
8919                     
8920                 ];
8921
8922         } else {
8923             
8924                 Roo.log(" no label && no align");
8925                 cfg = combobox
8926                      
8927                 
8928         }
8929          
8930         var settings=this;
8931         ['xs','sm','md','lg'].map(function(size){
8932             if (settings[size]) {
8933                 cfg.cls += ' col-' + size + '-' + settings[size];
8934             }
8935         });
8936         Roo.log(cfg);
8937         return cfg;
8938         
8939     },
8940     
8941     
8942     
8943     // private
8944     onResize : function(w, h){
8945 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8946 //        if(typeof w == 'number'){
8947 //            var x = w - this.trigger.getWidth();
8948 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8949 //            this.trigger.setStyle('left', x+'px');
8950 //        }
8951     },
8952
8953     // private
8954     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8955
8956     // private
8957     getResizeEl : function(){
8958         return this.inputEl();
8959     },
8960
8961     // private
8962     getPositionEl : function(){
8963         return this.inputEl();
8964     },
8965
8966     // private
8967     alignErrorIcon : function(){
8968         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8969     },
8970
8971     // private
8972     initEvents : function(){
8973         
8974         this.createList();
8975         
8976         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8977         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8978         if(!this.multiple && this.showToggleBtn){
8979             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8980             if(this.hideTrigger){
8981                 this.trigger.setDisplayed(false);
8982             }
8983             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8984         }
8985         
8986         if(this.multiple){
8987             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8988         }
8989         
8990         if(this.removable && !this.editable && !this.tickable){
8991             var close = this.closeTriggerEl();
8992             
8993             if(close){
8994                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8995                 close.on('click', this.removeBtnClick, this, close);
8996             }
8997         }
8998         
8999         //this.trigger.addClassOnOver('x-form-trigger-over');
9000         //this.trigger.addClassOnClick('x-form-trigger-click');
9001         
9002         //if(!this.width){
9003         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9004         //}
9005     },
9006     
9007     closeTriggerEl : function()
9008     {
9009         var close = this.el.select('.roo-combo-removable-btn', true).first();
9010         return close ? close : false;
9011     },
9012     
9013     removeBtnClick : function(e, h, el)
9014     {
9015         e.preventDefault();
9016         
9017         if(this.fireEvent("remove", this) !== false){
9018             this.reset();
9019         }
9020     },
9021     
9022     createList : function()
9023     {
9024         this.list = Roo.get(document.body).createChild({
9025             tag: 'ul',
9026             cls: 'typeahead typeahead-long dropdown-menu',
9027             style: 'display:none'
9028         });
9029         
9030         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9031         
9032     },
9033
9034     // private
9035     initTrigger : function(){
9036        
9037     },
9038
9039     // private
9040     onDestroy : function(){
9041         if(this.trigger){
9042             this.trigger.removeAllListeners();
9043           //  this.trigger.remove();
9044         }
9045         //if(this.wrap){
9046         //    this.wrap.remove();
9047         //}
9048         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9049     },
9050
9051     // private
9052     onFocus : function(){
9053         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9054         /*
9055         if(!this.mimicing){
9056             this.wrap.addClass('x-trigger-wrap-focus');
9057             this.mimicing = true;
9058             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9059             if(this.monitorTab){
9060                 this.el.on("keydown", this.checkTab, this);
9061             }
9062         }
9063         */
9064     },
9065
9066     // private
9067     checkTab : function(e){
9068         if(e.getKey() == e.TAB){
9069             this.triggerBlur();
9070         }
9071     },
9072
9073     // private
9074     onBlur : function(){
9075         // do nothing
9076     },
9077
9078     // private
9079     mimicBlur : function(e, t){
9080         /*
9081         if(!this.wrap.contains(t) && this.validateBlur()){
9082             this.triggerBlur();
9083         }
9084         */
9085     },
9086
9087     // private
9088     triggerBlur : function(){
9089         this.mimicing = false;
9090         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9091         if(this.monitorTab){
9092             this.el.un("keydown", this.checkTab, this);
9093         }
9094         //this.wrap.removeClass('x-trigger-wrap-focus');
9095         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9096     },
9097
9098     // private
9099     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9100     validateBlur : function(e, t){
9101         return true;
9102     },
9103
9104     // private
9105     onDisable : function(){
9106         this.inputEl().dom.disabled = true;
9107         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9108         //if(this.wrap){
9109         //    this.wrap.addClass('x-item-disabled');
9110         //}
9111     },
9112
9113     // private
9114     onEnable : function(){
9115         this.inputEl().dom.disabled = false;
9116         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9117         //if(this.wrap){
9118         //    this.el.removeClass('x-item-disabled');
9119         //}
9120     },
9121
9122     // private
9123     onShow : function(){
9124         var ae = this.getActionEl();
9125         
9126         if(ae){
9127             ae.dom.style.display = '';
9128             ae.dom.style.visibility = 'visible';
9129         }
9130     },
9131
9132     // private
9133     
9134     onHide : function(){
9135         var ae = this.getActionEl();
9136         ae.dom.style.display = 'none';
9137     },
9138
9139     /**
9140      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9141      * by an implementing function.
9142      * @method
9143      * @param {EventObject} e
9144      */
9145     onTriggerClick : Roo.emptyFn
9146 });
9147  /*
9148  * Based on:
9149  * Ext JS Library 1.1.1
9150  * Copyright(c) 2006-2007, Ext JS, LLC.
9151  *
9152  * Originally Released Under LGPL - original licence link has changed is not relivant.
9153  *
9154  * Fork - LGPL
9155  * <script type="text/javascript">
9156  */
9157
9158
9159 /**
9160  * @class Roo.data.SortTypes
9161  * @singleton
9162  * Defines the default sorting (casting?) comparison functions used when sorting data.
9163  */
9164 Roo.data.SortTypes = {
9165     /**
9166      * Default sort that does nothing
9167      * @param {Mixed} s The value being converted
9168      * @return {Mixed} The comparison value
9169      */
9170     none : function(s){
9171         return s;
9172     },
9173     
9174     /**
9175      * The regular expression used to strip tags
9176      * @type {RegExp}
9177      * @property
9178      */
9179     stripTagsRE : /<\/?[^>]+>/gi,
9180     
9181     /**
9182      * Strips all HTML tags to sort on text only
9183      * @param {Mixed} s The value being converted
9184      * @return {String} The comparison value
9185      */
9186     asText : function(s){
9187         return String(s).replace(this.stripTagsRE, "");
9188     },
9189     
9190     /**
9191      * Strips all HTML tags to sort on text only - Case insensitive
9192      * @param {Mixed} s The value being converted
9193      * @return {String} The comparison value
9194      */
9195     asUCText : function(s){
9196         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9197     },
9198     
9199     /**
9200      * Case insensitive string
9201      * @param {Mixed} s The value being converted
9202      * @return {String} The comparison value
9203      */
9204     asUCString : function(s) {
9205         return String(s).toUpperCase();
9206     },
9207     
9208     /**
9209      * Date sorting
9210      * @param {Mixed} s The value being converted
9211      * @return {Number} The comparison value
9212      */
9213     asDate : function(s) {
9214         if(!s){
9215             return 0;
9216         }
9217         if(s instanceof Date){
9218             return s.getTime();
9219         }
9220         return Date.parse(String(s));
9221     },
9222     
9223     /**
9224      * Float sorting
9225      * @param {Mixed} s The value being converted
9226      * @return {Float} The comparison value
9227      */
9228     asFloat : function(s) {
9229         var val = parseFloat(String(s).replace(/,/g, ""));
9230         if(isNaN(val)) val = 0;
9231         return val;
9232     },
9233     
9234     /**
9235      * Integer sorting
9236      * @param {Mixed} s The value being converted
9237      * @return {Number} The comparison value
9238      */
9239     asInt : function(s) {
9240         var val = parseInt(String(s).replace(/,/g, ""));
9241         if(isNaN(val)) val = 0;
9242         return val;
9243     }
9244 };/*
9245  * Based on:
9246  * Ext JS Library 1.1.1
9247  * Copyright(c) 2006-2007, Ext JS, LLC.
9248  *
9249  * Originally Released Under LGPL - original licence link has changed is not relivant.
9250  *
9251  * Fork - LGPL
9252  * <script type="text/javascript">
9253  */
9254
9255 /**
9256 * @class Roo.data.Record
9257  * Instances of this class encapsulate both record <em>definition</em> information, and record
9258  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9259  * to access Records cached in an {@link Roo.data.Store} object.<br>
9260  * <p>
9261  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9262  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9263  * objects.<br>
9264  * <p>
9265  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9266  * @constructor
9267  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9268  * {@link #create}. The parameters are the same.
9269  * @param {Array} data An associative Array of data values keyed by the field name.
9270  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9271  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9272  * not specified an integer id is generated.
9273  */
9274 Roo.data.Record = function(data, id){
9275     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9276     this.data = data;
9277 };
9278
9279 /**
9280  * Generate a constructor for a specific record layout.
9281  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9282  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9283  * Each field definition object may contain the following properties: <ul>
9284  * <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,
9285  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9286  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9287  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9288  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9289  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9290  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9291  * this may be omitted.</p></li>
9292  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9293  * <ul><li>auto (Default, implies no conversion)</li>
9294  * <li>string</li>
9295  * <li>int</li>
9296  * <li>float</li>
9297  * <li>boolean</li>
9298  * <li>date</li></ul></p></li>
9299  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9300  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9301  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9302  * by the Reader into an object that will be stored in the Record. It is passed the
9303  * following parameters:<ul>
9304  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9305  * </ul></p></li>
9306  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9307  * </ul>
9308  * <br>usage:<br><pre><code>
9309 var TopicRecord = Roo.data.Record.create(
9310     {name: 'title', mapping: 'topic_title'},
9311     {name: 'author', mapping: 'username'},
9312     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9313     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9314     {name: 'lastPoster', mapping: 'user2'},
9315     {name: 'excerpt', mapping: 'post_text'}
9316 );
9317
9318 var myNewRecord = new TopicRecord({
9319     title: 'Do my job please',
9320     author: 'noobie',
9321     totalPosts: 1,
9322     lastPost: new Date(),
9323     lastPoster: 'Animal',
9324     excerpt: 'No way dude!'
9325 });
9326 myStore.add(myNewRecord);
9327 </code></pre>
9328  * @method create
9329  * @static
9330  */
9331 Roo.data.Record.create = function(o){
9332     var f = function(){
9333         f.superclass.constructor.apply(this, arguments);
9334     };
9335     Roo.extend(f, Roo.data.Record);
9336     var p = f.prototype;
9337     p.fields = new Roo.util.MixedCollection(false, function(field){
9338         return field.name;
9339     });
9340     for(var i = 0, len = o.length; i < len; i++){
9341         p.fields.add(new Roo.data.Field(o[i]));
9342     }
9343     f.getField = function(name){
9344         return p.fields.get(name);  
9345     };
9346     return f;
9347 };
9348
9349 Roo.data.Record.AUTO_ID = 1000;
9350 Roo.data.Record.EDIT = 'edit';
9351 Roo.data.Record.REJECT = 'reject';
9352 Roo.data.Record.COMMIT = 'commit';
9353
9354 Roo.data.Record.prototype = {
9355     /**
9356      * Readonly flag - true if this record has been modified.
9357      * @type Boolean
9358      */
9359     dirty : false,
9360     editing : false,
9361     error: null,
9362     modified: null,
9363
9364     // private
9365     join : function(store){
9366         this.store = store;
9367     },
9368
9369     /**
9370      * Set the named field to the specified value.
9371      * @param {String} name The name of the field to set.
9372      * @param {Object} value The value to set the field to.
9373      */
9374     set : function(name, value){
9375         if(this.data[name] == value){
9376             return;
9377         }
9378         this.dirty = true;
9379         if(!this.modified){
9380             this.modified = {};
9381         }
9382         if(typeof this.modified[name] == 'undefined'){
9383             this.modified[name] = this.data[name];
9384         }
9385         this.data[name] = value;
9386         if(!this.editing && this.store){
9387             this.store.afterEdit(this);
9388         }       
9389     },
9390
9391     /**
9392      * Get the value of the named field.
9393      * @param {String} name The name of the field to get the value of.
9394      * @return {Object} The value of the field.
9395      */
9396     get : function(name){
9397         return this.data[name]; 
9398     },
9399
9400     // private
9401     beginEdit : function(){
9402         this.editing = true;
9403         this.modified = {}; 
9404     },
9405
9406     // private
9407     cancelEdit : function(){
9408         this.editing = false;
9409         delete this.modified;
9410     },
9411
9412     // private
9413     endEdit : function(){
9414         this.editing = false;
9415         if(this.dirty && this.store){
9416             this.store.afterEdit(this);
9417         }
9418     },
9419
9420     /**
9421      * Usually called by the {@link Roo.data.Store} which owns the Record.
9422      * Rejects all changes made to the Record since either creation, or the last commit operation.
9423      * Modified fields are reverted to their original values.
9424      * <p>
9425      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9426      * of reject operations.
9427      */
9428     reject : function(){
9429         var m = this.modified;
9430         for(var n in m){
9431             if(typeof m[n] != "function"){
9432                 this.data[n] = m[n];
9433             }
9434         }
9435         this.dirty = false;
9436         delete this.modified;
9437         this.editing = false;
9438         if(this.store){
9439             this.store.afterReject(this);
9440         }
9441     },
9442
9443     /**
9444      * Usually called by the {@link Roo.data.Store} which owns the Record.
9445      * Commits all changes made to the Record since either creation, or the last commit operation.
9446      * <p>
9447      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9448      * of commit operations.
9449      */
9450     commit : function(){
9451         this.dirty = false;
9452         delete this.modified;
9453         this.editing = false;
9454         if(this.store){
9455             this.store.afterCommit(this);
9456         }
9457     },
9458
9459     // private
9460     hasError : function(){
9461         return this.error != null;
9462     },
9463
9464     // private
9465     clearError : function(){
9466         this.error = null;
9467     },
9468
9469     /**
9470      * Creates a copy of this record.
9471      * @param {String} id (optional) A new record id if you don't want to use this record's id
9472      * @return {Record}
9473      */
9474     copy : function(newId) {
9475         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9476     }
9477 };/*
9478  * Based on:
9479  * Ext JS Library 1.1.1
9480  * Copyright(c) 2006-2007, Ext JS, LLC.
9481  *
9482  * Originally Released Under LGPL - original licence link has changed is not relivant.
9483  *
9484  * Fork - LGPL
9485  * <script type="text/javascript">
9486  */
9487
9488
9489
9490 /**
9491  * @class Roo.data.Store
9492  * @extends Roo.util.Observable
9493  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9494  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9495  * <p>
9496  * 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
9497  * has no knowledge of the format of the data returned by the Proxy.<br>
9498  * <p>
9499  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9500  * instances from the data object. These records are cached and made available through accessor functions.
9501  * @constructor
9502  * Creates a new Store.
9503  * @param {Object} config A config object containing the objects needed for the Store to access data,
9504  * and read the data into Records.
9505  */
9506 Roo.data.Store = function(config){
9507     this.data = new Roo.util.MixedCollection(false);
9508     this.data.getKey = function(o){
9509         return o.id;
9510     };
9511     this.baseParams = {};
9512     // private
9513     this.paramNames = {
9514         "start" : "start",
9515         "limit" : "limit",
9516         "sort" : "sort",
9517         "dir" : "dir",
9518         "multisort" : "_multisort"
9519     };
9520
9521     if(config && config.data){
9522         this.inlineData = config.data;
9523         delete config.data;
9524     }
9525
9526     Roo.apply(this, config);
9527     
9528     if(this.reader){ // reader passed
9529         this.reader = Roo.factory(this.reader, Roo.data);
9530         this.reader.xmodule = this.xmodule || false;
9531         if(!this.recordType){
9532             this.recordType = this.reader.recordType;
9533         }
9534         if(this.reader.onMetaChange){
9535             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9536         }
9537     }
9538
9539     if(this.recordType){
9540         this.fields = this.recordType.prototype.fields;
9541     }
9542     this.modified = [];
9543
9544     this.addEvents({
9545         /**
9546          * @event datachanged
9547          * Fires when the data cache has changed, and a widget which is using this Store
9548          * as a Record cache should refresh its view.
9549          * @param {Store} this
9550          */
9551         datachanged : true,
9552         /**
9553          * @event metachange
9554          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9555          * @param {Store} this
9556          * @param {Object} meta The JSON metadata
9557          */
9558         metachange : true,
9559         /**
9560          * @event add
9561          * Fires when Records have been added to the Store
9562          * @param {Store} this
9563          * @param {Roo.data.Record[]} records The array of Records added
9564          * @param {Number} index The index at which the record(s) were added
9565          */
9566         add : true,
9567         /**
9568          * @event remove
9569          * Fires when a Record has been removed from the Store
9570          * @param {Store} this
9571          * @param {Roo.data.Record} record The Record that was removed
9572          * @param {Number} index The index at which the record was removed
9573          */
9574         remove : true,
9575         /**
9576          * @event update
9577          * Fires when a Record has been updated
9578          * @param {Store} this
9579          * @param {Roo.data.Record} record The Record that was updated
9580          * @param {String} operation The update operation being performed.  Value may be one of:
9581          * <pre><code>
9582  Roo.data.Record.EDIT
9583  Roo.data.Record.REJECT
9584  Roo.data.Record.COMMIT
9585          * </code></pre>
9586          */
9587         update : true,
9588         /**
9589          * @event clear
9590          * Fires when the data cache has been cleared.
9591          * @param {Store} this
9592          */
9593         clear : true,
9594         /**
9595          * @event beforeload
9596          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9597          * the load action will be canceled.
9598          * @param {Store} this
9599          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9600          */
9601         beforeload : true,
9602         /**
9603          * @event beforeloadadd
9604          * Fires after a new set of Records has been loaded.
9605          * @param {Store} this
9606          * @param {Roo.data.Record[]} records The Records that were loaded
9607          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9608          */
9609         beforeloadadd : true,
9610         /**
9611          * @event load
9612          * Fires after a new set of Records has been loaded, before they are added to the store.
9613          * @param {Store} this
9614          * @param {Roo.data.Record[]} records The Records that were loaded
9615          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9616          * @params {Object} return from reader
9617          */
9618         load : true,
9619         /**
9620          * @event loadexception
9621          * Fires if an exception occurs in the Proxy during loading.
9622          * Called with the signature of the Proxy's "loadexception" event.
9623          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9624          * 
9625          * @param {Proxy} 
9626          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9627          * @param {Object} load options 
9628          * @param {Object} jsonData from your request (normally this contains the Exception)
9629          */
9630         loadexception : true
9631     });
9632     
9633     if(this.proxy){
9634         this.proxy = Roo.factory(this.proxy, Roo.data);
9635         this.proxy.xmodule = this.xmodule || false;
9636         this.relayEvents(this.proxy,  ["loadexception"]);
9637     }
9638     this.sortToggle = {};
9639     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9640
9641     Roo.data.Store.superclass.constructor.call(this);
9642
9643     if(this.inlineData){
9644         this.loadData(this.inlineData);
9645         delete this.inlineData;
9646     }
9647 };
9648
9649 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9650      /**
9651     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9652     * without a remote query - used by combo/forms at present.
9653     */
9654     
9655     /**
9656     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9657     */
9658     /**
9659     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9660     */
9661     /**
9662     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9663     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9664     */
9665     /**
9666     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9667     * on any HTTP request
9668     */
9669     /**
9670     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9671     */
9672     /**
9673     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9674     */
9675     multiSort: false,
9676     /**
9677     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9678     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9679     */
9680     remoteSort : false,
9681
9682     /**
9683     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9684      * loaded or when a record is removed. (defaults to false).
9685     */
9686     pruneModifiedRecords : false,
9687
9688     // private
9689     lastOptions : null,
9690
9691     /**
9692      * Add Records to the Store and fires the add event.
9693      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9694      */
9695     add : function(records){
9696         records = [].concat(records);
9697         for(var i = 0, len = records.length; i < len; i++){
9698             records[i].join(this);
9699         }
9700         var index = this.data.length;
9701         this.data.addAll(records);
9702         this.fireEvent("add", this, records, index);
9703     },
9704
9705     /**
9706      * Remove a Record from the Store and fires the remove event.
9707      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9708      */
9709     remove : function(record){
9710         var index = this.data.indexOf(record);
9711         this.data.removeAt(index);
9712         if(this.pruneModifiedRecords){
9713             this.modified.remove(record);
9714         }
9715         this.fireEvent("remove", this, record, index);
9716     },
9717
9718     /**
9719      * Remove all Records from the Store and fires the clear event.
9720      */
9721     removeAll : function(){
9722         this.data.clear();
9723         if(this.pruneModifiedRecords){
9724             this.modified = [];
9725         }
9726         this.fireEvent("clear", this);
9727     },
9728
9729     /**
9730      * Inserts Records to the Store at the given index and fires the add event.
9731      * @param {Number} index The start index at which to insert the passed Records.
9732      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9733      */
9734     insert : function(index, records){
9735         records = [].concat(records);
9736         for(var i = 0, len = records.length; i < len; i++){
9737             this.data.insert(index, records[i]);
9738             records[i].join(this);
9739         }
9740         this.fireEvent("add", this, records, index);
9741     },
9742
9743     /**
9744      * Get the index within the cache of the passed Record.
9745      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9746      * @return {Number} The index of the passed Record. Returns -1 if not found.
9747      */
9748     indexOf : function(record){
9749         return this.data.indexOf(record);
9750     },
9751
9752     /**
9753      * Get the index within the cache of the Record with the passed id.
9754      * @param {String} id The id of the Record to find.
9755      * @return {Number} The index of the Record. Returns -1 if not found.
9756      */
9757     indexOfId : function(id){
9758         return this.data.indexOfKey(id);
9759     },
9760
9761     /**
9762      * Get the Record with the specified id.
9763      * @param {String} id The id of the Record to find.
9764      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9765      */
9766     getById : function(id){
9767         return this.data.key(id);
9768     },
9769
9770     /**
9771      * Get the Record at the specified index.
9772      * @param {Number} index The index of the Record to find.
9773      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9774      */
9775     getAt : function(index){
9776         return this.data.itemAt(index);
9777     },
9778
9779     /**
9780      * Returns a range of Records between specified indices.
9781      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9782      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9783      * @return {Roo.data.Record[]} An array of Records
9784      */
9785     getRange : function(start, end){
9786         return this.data.getRange(start, end);
9787     },
9788
9789     // private
9790     storeOptions : function(o){
9791         o = Roo.apply({}, o);
9792         delete o.callback;
9793         delete o.scope;
9794         this.lastOptions = o;
9795     },
9796
9797     /**
9798      * Loads the Record cache from the configured Proxy using the configured Reader.
9799      * <p>
9800      * If using remote paging, then the first load call must specify the <em>start</em>
9801      * and <em>limit</em> properties in the options.params property to establish the initial
9802      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9803      * <p>
9804      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9805      * and this call will return before the new data has been loaded. Perform any post-processing
9806      * in a callback function, or in a "load" event handler.</strong>
9807      * <p>
9808      * @param {Object} options An object containing properties which control loading options:<ul>
9809      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9810      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9811      * passed the following arguments:<ul>
9812      * <li>r : Roo.data.Record[]</li>
9813      * <li>options: Options object from the load call</li>
9814      * <li>success: Boolean success indicator</li></ul></li>
9815      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9816      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9817      * </ul>
9818      */
9819     load : function(options){
9820         options = options || {};
9821         if(this.fireEvent("beforeload", this, options) !== false){
9822             this.storeOptions(options);
9823             var p = Roo.apply(options.params || {}, this.baseParams);
9824             // if meta was not loaded from remote source.. try requesting it.
9825             if (!this.reader.metaFromRemote) {
9826                 p._requestMeta = 1;
9827             }
9828             if(this.sortInfo && this.remoteSort){
9829                 var pn = this.paramNames;
9830                 p[pn["sort"]] = this.sortInfo.field;
9831                 p[pn["dir"]] = this.sortInfo.direction;
9832             }
9833             if (this.multiSort) {
9834                 var pn = this.paramNames;
9835                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9836             }
9837             
9838             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9839         }
9840     },
9841
9842     /**
9843      * Reloads the Record cache from the configured Proxy using the configured Reader and
9844      * the options from the last load operation performed.
9845      * @param {Object} options (optional) An object containing properties which may override the options
9846      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9847      * the most recently used options are reused).
9848      */
9849     reload : function(options){
9850         this.load(Roo.applyIf(options||{}, this.lastOptions));
9851     },
9852
9853     // private
9854     // Called as a callback by the Reader during a load operation.
9855     loadRecords : function(o, options, success){
9856         if(!o || success === false){
9857             if(success !== false){
9858                 this.fireEvent("load", this, [], options, o);
9859             }
9860             if(options.callback){
9861                 options.callback.call(options.scope || this, [], options, false);
9862             }
9863             return;
9864         }
9865         // if data returned failure - throw an exception.
9866         if (o.success === false) {
9867             // show a message if no listener is registered.
9868             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9869                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9870             }
9871             // loadmask wil be hooked into this..
9872             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9873             return;
9874         }
9875         var r = o.records, t = o.totalRecords || r.length;
9876         
9877         this.fireEvent("beforeloadadd", this, r, options, o);
9878         
9879         if(!options || options.add !== true){
9880             if(this.pruneModifiedRecords){
9881                 this.modified = [];
9882             }
9883             for(var i = 0, len = r.length; i < len; i++){
9884                 r[i].join(this);
9885             }
9886             if(this.snapshot){
9887                 this.data = this.snapshot;
9888                 delete this.snapshot;
9889             }
9890             this.data.clear();
9891             this.data.addAll(r);
9892             this.totalLength = t;
9893             this.applySort();
9894             this.fireEvent("datachanged", this);
9895         }else{
9896             this.totalLength = Math.max(t, this.data.length+r.length);
9897             this.add(r);
9898         }
9899         this.fireEvent("load", this, r, options, o);
9900         if(options.callback){
9901             options.callback.call(options.scope || this, r, options, true);
9902         }
9903     },
9904
9905
9906     /**
9907      * Loads data from a passed data block. A Reader which understands the format of the data
9908      * must have been configured in the constructor.
9909      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9910      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9911      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9912      */
9913     loadData : function(o, append){
9914         var r = this.reader.readRecords(o);
9915         this.loadRecords(r, {add: append}, true);
9916     },
9917
9918     /**
9919      * Gets the number of cached records.
9920      * <p>
9921      * <em>If using paging, this may not be the total size of the dataset. If the data object
9922      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9923      * the data set size</em>
9924      */
9925     getCount : function(){
9926         return this.data.length || 0;
9927     },
9928
9929     /**
9930      * Gets the total number of records in the dataset as returned by the server.
9931      * <p>
9932      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9933      * the dataset size</em>
9934      */
9935     getTotalCount : function(){
9936         return this.totalLength || 0;
9937     },
9938
9939     /**
9940      * Returns the sort state of the Store as an object with two properties:
9941      * <pre><code>
9942  field {String} The name of the field by which the Records are sorted
9943  direction {String} The sort order, "ASC" or "DESC"
9944      * </code></pre>
9945      */
9946     getSortState : function(){
9947         return this.sortInfo;
9948     },
9949
9950     // private
9951     applySort : function(){
9952         if(this.sortInfo && !this.remoteSort){
9953             var s = this.sortInfo, f = s.field;
9954             var st = this.fields.get(f).sortType;
9955             var fn = function(r1, r2){
9956                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9957                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9958             };
9959             this.data.sort(s.direction, fn);
9960             if(this.snapshot && this.snapshot != this.data){
9961                 this.snapshot.sort(s.direction, fn);
9962             }
9963         }
9964     },
9965
9966     /**
9967      * Sets the default sort column and order to be used by the next load operation.
9968      * @param {String} fieldName The name of the field to sort by.
9969      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9970      */
9971     setDefaultSort : function(field, dir){
9972         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9973     },
9974
9975     /**
9976      * Sort the Records.
9977      * If remote sorting is used, the sort is performed on the server, and the cache is
9978      * reloaded. If local sorting is used, the cache is sorted internally.
9979      * @param {String} fieldName The name of the field to sort by.
9980      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9981      */
9982     sort : function(fieldName, dir){
9983         var f = this.fields.get(fieldName);
9984         if(!dir){
9985             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9986             
9987             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9988                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9989             }else{
9990                 dir = f.sortDir;
9991             }
9992         }
9993         this.sortToggle[f.name] = dir;
9994         this.sortInfo = {field: f.name, direction: dir};
9995         if(!this.remoteSort){
9996             this.applySort();
9997             this.fireEvent("datachanged", this);
9998         }else{
9999             this.load(this.lastOptions);
10000         }
10001     },
10002
10003     /**
10004      * Calls the specified function for each of the Records in the cache.
10005      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10006      * Returning <em>false</em> aborts and exits the iteration.
10007      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10008      */
10009     each : function(fn, scope){
10010         this.data.each(fn, scope);
10011     },
10012
10013     /**
10014      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10015      * (e.g., during paging).
10016      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10017      */
10018     getModifiedRecords : function(){
10019         return this.modified;
10020     },
10021
10022     // private
10023     createFilterFn : function(property, value, anyMatch){
10024         if(!value.exec){ // not a regex
10025             value = String(value);
10026             if(value.length == 0){
10027                 return false;
10028             }
10029             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10030         }
10031         return function(r){
10032             return value.test(r.data[property]);
10033         };
10034     },
10035
10036     /**
10037      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10038      * @param {String} property A field on your records
10039      * @param {Number} start The record index to start at (defaults to 0)
10040      * @param {Number} end The last record index to include (defaults to length - 1)
10041      * @return {Number} The sum
10042      */
10043     sum : function(property, start, end){
10044         var rs = this.data.items, v = 0;
10045         start = start || 0;
10046         end = (end || end === 0) ? end : rs.length-1;
10047
10048         for(var i = start; i <= end; i++){
10049             v += (rs[i].data[property] || 0);
10050         }
10051         return v;
10052     },
10053
10054     /**
10055      * Filter the records by a specified property.
10056      * @param {String} field A field on your records
10057      * @param {String/RegExp} value Either a string that the field
10058      * should start with or a RegExp to test against the field
10059      * @param {Boolean} anyMatch True to match any part not just the beginning
10060      */
10061     filter : function(property, value, anyMatch){
10062         var fn = this.createFilterFn(property, value, anyMatch);
10063         return fn ? this.filterBy(fn) : this.clearFilter();
10064     },
10065
10066     /**
10067      * Filter by a function. The specified function will be called with each
10068      * record in this data source. If the function returns true the record is included,
10069      * otherwise it is filtered.
10070      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10071      * @param {Object} scope (optional) The scope of the function (defaults to this)
10072      */
10073     filterBy : function(fn, scope){
10074         this.snapshot = this.snapshot || this.data;
10075         this.data = this.queryBy(fn, scope||this);
10076         this.fireEvent("datachanged", this);
10077     },
10078
10079     /**
10080      * Query the records by a specified property.
10081      * @param {String} field A field on your records
10082      * @param {String/RegExp} value Either a string that the field
10083      * should start with or a RegExp to test against the field
10084      * @param {Boolean} anyMatch True to match any part not just the beginning
10085      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10086      */
10087     query : function(property, value, anyMatch){
10088         var fn = this.createFilterFn(property, value, anyMatch);
10089         return fn ? this.queryBy(fn) : this.data.clone();
10090     },
10091
10092     /**
10093      * Query by a function. The specified function will be called with each
10094      * record in this data source. If the function returns true the record is included
10095      * in the results.
10096      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10097      * @param {Object} scope (optional) The scope of the function (defaults to this)
10098       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10099      **/
10100     queryBy : function(fn, scope){
10101         var data = this.snapshot || this.data;
10102         return data.filterBy(fn, scope||this);
10103     },
10104
10105     /**
10106      * Collects unique values for a particular dataIndex from this store.
10107      * @param {String} dataIndex The property to collect
10108      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10109      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10110      * @return {Array} An array of the unique values
10111      **/
10112     collect : function(dataIndex, allowNull, bypassFilter){
10113         var d = (bypassFilter === true && this.snapshot) ?
10114                 this.snapshot.items : this.data.items;
10115         var v, sv, r = [], l = {};
10116         for(var i = 0, len = d.length; i < len; i++){
10117             v = d[i].data[dataIndex];
10118             sv = String(v);
10119             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10120                 l[sv] = true;
10121                 r[r.length] = v;
10122             }
10123         }
10124         return r;
10125     },
10126
10127     /**
10128      * Revert to a view of the Record cache with no filtering applied.
10129      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10130      */
10131     clearFilter : function(suppressEvent){
10132         if(this.snapshot && this.snapshot != this.data){
10133             this.data = this.snapshot;
10134             delete this.snapshot;
10135             if(suppressEvent !== true){
10136                 this.fireEvent("datachanged", this);
10137             }
10138         }
10139     },
10140
10141     // private
10142     afterEdit : function(record){
10143         if(this.modified.indexOf(record) == -1){
10144             this.modified.push(record);
10145         }
10146         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10147     },
10148     
10149     // private
10150     afterReject : function(record){
10151         this.modified.remove(record);
10152         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10153     },
10154
10155     // private
10156     afterCommit : function(record){
10157         this.modified.remove(record);
10158         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10159     },
10160
10161     /**
10162      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10163      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10164      */
10165     commitChanges : function(){
10166         var m = this.modified.slice(0);
10167         this.modified = [];
10168         for(var i = 0, len = m.length; i < len; i++){
10169             m[i].commit();
10170         }
10171     },
10172
10173     /**
10174      * Cancel outstanding changes on all changed records.
10175      */
10176     rejectChanges : function(){
10177         var m = this.modified.slice(0);
10178         this.modified = [];
10179         for(var i = 0, len = m.length; i < len; i++){
10180             m[i].reject();
10181         }
10182     },
10183
10184     onMetaChange : function(meta, rtype, o){
10185         this.recordType = rtype;
10186         this.fields = rtype.prototype.fields;
10187         delete this.snapshot;
10188         this.sortInfo = meta.sortInfo || this.sortInfo;
10189         this.modified = [];
10190         this.fireEvent('metachange', this, this.reader.meta);
10191     },
10192     
10193     moveIndex : function(data, type)
10194     {
10195         var index = this.indexOf(data);
10196         
10197         var newIndex = index + type;
10198         
10199         this.remove(data);
10200         
10201         this.insert(newIndex, data);
10202         
10203     }
10204 });/*
10205  * Based on:
10206  * Ext JS Library 1.1.1
10207  * Copyright(c) 2006-2007, Ext JS, LLC.
10208  *
10209  * Originally Released Under LGPL - original licence link has changed is not relivant.
10210  *
10211  * Fork - LGPL
10212  * <script type="text/javascript">
10213  */
10214
10215 /**
10216  * @class Roo.data.SimpleStore
10217  * @extends Roo.data.Store
10218  * Small helper class to make creating Stores from Array data easier.
10219  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10220  * @cfg {Array} fields An array of field definition objects, or field name strings.
10221  * @cfg {Array} data The multi-dimensional array of data
10222  * @constructor
10223  * @param {Object} config
10224  */
10225 Roo.data.SimpleStore = function(config){
10226     Roo.data.SimpleStore.superclass.constructor.call(this, {
10227         isLocal : true,
10228         reader: new Roo.data.ArrayReader({
10229                 id: config.id
10230             },
10231             Roo.data.Record.create(config.fields)
10232         ),
10233         proxy : new Roo.data.MemoryProxy(config.data)
10234     });
10235     this.load();
10236 };
10237 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10238  * Based on:
10239  * Ext JS Library 1.1.1
10240  * Copyright(c) 2006-2007, Ext JS, LLC.
10241  *
10242  * Originally Released Under LGPL - original licence link has changed is not relivant.
10243  *
10244  * Fork - LGPL
10245  * <script type="text/javascript">
10246  */
10247
10248 /**
10249 /**
10250  * @extends Roo.data.Store
10251  * @class Roo.data.JsonStore
10252  * Small helper class to make creating Stores for JSON data easier. <br/>
10253 <pre><code>
10254 var store = new Roo.data.JsonStore({
10255     url: 'get-images.php',
10256     root: 'images',
10257     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10258 });
10259 </code></pre>
10260  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10261  * JsonReader and HttpProxy (unless inline data is provided).</b>
10262  * @cfg {Array} fields An array of field definition objects, or field name strings.
10263  * @constructor
10264  * @param {Object} config
10265  */
10266 Roo.data.JsonStore = function(c){
10267     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10268         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10269         reader: new Roo.data.JsonReader(c, c.fields)
10270     }));
10271 };
10272 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10273  * Based on:
10274  * Ext JS Library 1.1.1
10275  * Copyright(c) 2006-2007, Ext JS, LLC.
10276  *
10277  * Originally Released Under LGPL - original licence link has changed is not relivant.
10278  *
10279  * Fork - LGPL
10280  * <script type="text/javascript">
10281  */
10282
10283  
10284 Roo.data.Field = function(config){
10285     if(typeof config == "string"){
10286         config = {name: config};
10287     }
10288     Roo.apply(this, config);
10289     
10290     if(!this.type){
10291         this.type = "auto";
10292     }
10293     
10294     var st = Roo.data.SortTypes;
10295     // named sortTypes are supported, here we look them up
10296     if(typeof this.sortType == "string"){
10297         this.sortType = st[this.sortType];
10298     }
10299     
10300     // set default sortType for strings and dates
10301     if(!this.sortType){
10302         switch(this.type){
10303             case "string":
10304                 this.sortType = st.asUCString;
10305                 break;
10306             case "date":
10307                 this.sortType = st.asDate;
10308                 break;
10309             default:
10310                 this.sortType = st.none;
10311         }
10312     }
10313
10314     // define once
10315     var stripRe = /[\$,%]/g;
10316
10317     // prebuilt conversion function for this field, instead of
10318     // switching every time we're reading a value
10319     if(!this.convert){
10320         var cv, dateFormat = this.dateFormat;
10321         switch(this.type){
10322             case "":
10323             case "auto":
10324             case undefined:
10325                 cv = function(v){ return v; };
10326                 break;
10327             case "string":
10328                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10329                 break;
10330             case "int":
10331                 cv = function(v){
10332                     return v !== undefined && v !== null && v !== '' ?
10333                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10334                     };
10335                 break;
10336             case "float":
10337                 cv = function(v){
10338                     return v !== undefined && v !== null && v !== '' ?
10339                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10340                     };
10341                 break;
10342             case "bool":
10343             case "boolean":
10344                 cv = function(v){ return v === true || v === "true" || v == 1; };
10345                 break;
10346             case "date":
10347                 cv = function(v){
10348                     if(!v){
10349                         return '';
10350                     }
10351                     if(v instanceof Date){
10352                         return v;
10353                     }
10354                     if(dateFormat){
10355                         if(dateFormat == "timestamp"){
10356                             return new Date(v*1000);
10357                         }
10358                         return Date.parseDate(v, dateFormat);
10359                     }
10360                     var parsed = Date.parse(v);
10361                     return parsed ? new Date(parsed) : null;
10362                 };
10363              break;
10364             
10365         }
10366         this.convert = cv;
10367     }
10368 };
10369
10370 Roo.data.Field.prototype = {
10371     dateFormat: null,
10372     defaultValue: "",
10373     mapping: null,
10374     sortType : null,
10375     sortDir : "ASC"
10376 };/*
10377  * Based on:
10378  * Ext JS Library 1.1.1
10379  * Copyright(c) 2006-2007, Ext JS, LLC.
10380  *
10381  * Originally Released Under LGPL - original licence link has changed is not relivant.
10382  *
10383  * Fork - LGPL
10384  * <script type="text/javascript">
10385  */
10386  
10387 // Base class for reading structured data from a data source.  This class is intended to be
10388 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10389
10390 /**
10391  * @class Roo.data.DataReader
10392  * Base class for reading structured data from a data source.  This class is intended to be
10393  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10394  */
10395
10396 Roo.data.DataReader = function(meta, recordType){
10397     
10398     this.meta = meta;
10399     
10400     this.recordType = recordType instanceof Array ? 
10401         Roo.data.Record.create(recordType) : recordType;
10402 };
10403
10404 Roo.data.DataReader.prototype = {
10405      /**
10406      * Create an empty record
10407      * @param {Object} data (optional) - overlay some values
10408      * @return {Roo.data.Record} record created.
10409      */
10410     newRow :  function(d) {
10411         var da =  {};
10412         this.recordType.prototype.fields.each(function(c) {
10413             switch( c.type) {
10414                 case 'int' : da[c.name] = 0; break;
10415                 case 'date' : da[c.name] = new Date(); break;
10416                 case 'float' : da[c.name] = 0.0; break;
10417                 case 'boolean' : da[c.name] = false; break;
10418                 default : da[c.name] = ""; break;
10419             }
10420             
10421         });
10422         return new this.recordType(Roo.apply(da, d));
10423     }
10424     
10425 };/*
10426  * Based on:
10427  * Ext JS Library 1.1.1
10428  * Copyright(c) 2006-2007, Ext JS, LLC.
10429  *
10430  * Originally Released Under LGPL - original licence link has changed is not relivant.
10431  *
10432  * Fork - LGPL
10433  * <script type="text/javascript">
10434  */
10435
10436 /**
10437  * @class Roo.data.DataProxy
10438  * @extends Roo.data.Observable
10439  * This class is an abstract base class for implementations which provide retrieval of
10440  * unformatted data objects.<br>
10441  * <p>
10442  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10443  * (of the appropriate type which knows how to parse the data object) to provide a block of
10444  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10445  * <p>
10446  * Custom implementations must implement the load method as described in
10447  * {@link Roo.data.HttpProxy#load}.
10448  */
10449 Roo.data.DataProxy = function(){
10450     this.addEvents({
10451         /**
10452          * @event beforeload
10453          * Fires before a network request is made to retrieve a data object.
10454          * @param {Object} This DataProxy object.
10455          * @param {Object} params The params parameter to the load function.
10456          */
10457         beforeload : true,
10458         /**
10459          * @event load
10460          * Fires before the load method's callback is called.
10461          * @param {Object} This DataProxy object.
10462          * @param {Object} o The data object.
10463          * @param {Object} arg The callback argument object passed to the load function.
10464          */
10465         load : true,
10466         /**
10467          * @event loadexception
10468          * Fires if an Exception occurs during data retrieval.
10469          * @param {Object} This DataProxy object.
10470          * @param {Object} o The data object.
10471          * @param {Object} arg The callback argument object passed to the load function.
10472          * @param {Object} e The Exception.
10473          */
10474         loadexception : true
10475     });
10476     Roo.data.DataProxy.superclass.constructor.call(this);
10477 };
10478
10479 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10480
10481     /**
10482      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10483      */
10484 /*
10485  * Based on:
10486  * Ext JS Library 1.1.1
10487  * Copyright(c) 2006-2007, Ext JS, LLC.
10488  *
10489  * Originally Released Under LGPL - original licence link has changed is not relivant.
10490  *
10491  * Fork - LGPL
10492  * <script type="text/javascript">
10493  */
10494 /**
10495  * @class Roo.data.MemoryProxy
10496  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10497  * to the Reader when its load method is called.
10498  * @constructor
10499  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10500  */
10501 Roo.data.MemoryProxy = function(data){
10502     if (data.data) {
10503         data = data.data;
10504     }
10505     Roo.data.MemoryProxy.superclass.constructor.call(this);
10506     this.data = data;
10507 };
10508
10509 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10510     /**
10511      * Load data from the requested source (in this case an in-memory
10512      * data object passed to the constructor), read the data object into
10513      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10514      * process that block using the passed callback.
10515      * @param {Object} params This parameter is not used by the MemoryProxy class.
10516      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10517      * object into a block of Roo.data.Records.
10518      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10519      * The function must be passed <ul>
10520      * <li>The Record block object</li>
10521      * <li>The "arg" argument from the load function</li>
10522      * <li>A boolean success indicator</li>
10523      * </ul>
10524      * @param {Object} scope The scope in which to call the callback
10525      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10526      */
10527     load : function(params, reader, callback, scope, arg){
10528         params = params || {};
10529         var result;
10530         try {
10531             result = reader.readRecords(this.data);
10532         }catch(e){
10533             this.fireEvent("loadexception", this, arg, null, e);
10534             callback.call(scope, null, arg, false);
10535             return;
10536         }
10537         callback.call(scope, result, arg, true);
10538     },
10539     
10540     // private
10541     update : function(params, records){
10542         
10543     }
10544 });/*
10545  * Based on:
10546  * Ext JS Library 1.1.1
10547  * Copyright(c) 2006-2007, Ext JS, LLC.
10548  *
10549  * Originally Released Under LGPL - original licence link has changed is not relivant.
10550  *
10551  * Fork - LGPL
10552  * <script type="text/javascript">
10553  */
10554 /**
10555  * @class Roo.data.HttpProxy
10556  * @extends Roo.data.DataProxy
10557  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10558  * configured to reference a certain URL.<br><br>
10559  * <p>
10560  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10561  * from which the running page was served.<br><br>
10562  * <p>
10563  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10564  * <p>
10565  * Be aware that to enable the browser to parse an XML document, the server must set
10566  * the Content-Type header in the HTTP response to "text/xml".
10567  * @constructor
10568  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10569  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10570  * will be used to make the request.
10571  */
10572 Roo.data.HttpProxy = function(conn){
10573     Roo.data.HttpProxy.superclass.constructor.call(this);
10574     // is conn a conn config or a real conn?
10575     this.conn = conn;
10576     this.useAjax = !conn || !conn.events;
10577   
10578 };
10579
10580 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10581     // thse are take from connection...
10582     
10583     /**
10584      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10585      */
10586     /**
10587      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10588      * extra parameters to each request made by this object. (defaults to undefined)
10589      */
10590     /**
10591      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10592      *  to each request made by this object. (defaults to undefined)
10593      */
10594     /**
10595      * @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)
10596      */
10597     /**
10598      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10599      */
10600      /**
10601      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10602      * @type Boolean
10603      */
10604   
10605
10606     /**
10607      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10608      * @type Boolean
10609      */
10610     /**
10611      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10612      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10613      * a finer-grained basis than the DataProxy events.
10614      */
10615     getConnection : function(){
10616         return this.useAjax ? Roo.Ajax : this.conn;
10617     },
10618
10619     /**
10620      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10621      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10622      * process that block using the passed callback.
10623      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10624      * for the request to the remote server.
10625      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10626      * object into a block of Roo.data.Records.
10627      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10628      * The function must be passed <ul>
10629      * <li>The Record block object</li>
10630      * <li>The "arg" argument from the load function</li>
10631      * <li>A boolean success indicator</li>
10632      * </ul>
10633      * @param {Object} scope The scope in which to call the callback
10634      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10635      */
10636     load : function(params, reader, callback, scope, arg){
10637         if(this.fireEvent("beforeload", this, params) !== false){
10638             var  o = {
10639                 params : params || {},
10640                 request: {
10641                     callback : callback,
10642                     scope : scope,
10643                     arg : arg
10644                 },
10645                 reader: reader,
10646                 callback : this.loadResponse,
10647                 scope: this
10648             };
10649             if(this.useAjax){
10650                 Roo.applyIf(o, this.conn);
10651                 if(this.activeRequest){
10652                     Roo.Ajax.abort(this.activeRequest);
10653                 }
10654                 this.activeRequest = Roo.Ajax.request(o);
10655             }else{
10656                 this.conn.request(o);
10657             }
10658         }else{
10659             callback.call(scope||this, null, arg, false);
10660         }
10661     },
10662
10663     // private
10664     loadResponse : function(o, success, response){
10665         delete this.activeRequest;
10666         if(!success){
10667             this.fireEvent("loadexception", this, o, response);
10668             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10669             return;
10670         }
10671         var result;
10672         try {
10673             result = o.reader.read(response);
10674         }catch(e){
10675             this.fireEvent("loadexception", this, o, response, e);
10676             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10677             return;
10678         }
10679         
10680         this.fireEvent("load", this, o, o.request.arg);
10681         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10682     },
10683
10684     // private
10685     update : function(dataSet){
10686
10687     },
10688
10689     // private
10690     updateResponse : function(dataSet){
10691
10692     }
10693 });/*
10694  * Based on:
10695  * Ext JS Library 1.1.1
10696  * Copyright(c) 2006-2007, Ext JS, LLC.
10697  *
10698  * Originally Released Under LGPL - original licence link has changed is not relivant.
10699  *
10700  * Fork - LGPL
10701  * <script type="text/javascript">
10702  */
10703
10704 /**
10705  * @class Roo.data.ScriptTagProxy
10706  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10707  * other than the originating domain of the running page.<br><br>
10708  * <p>
10709  * <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
10710  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10711  * <p>
10712  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10713  * source code that is used as the source inside a &lt;script> tag.<br><br>
10714  * <p>
10715  * In order for the browser to process the returned data, the server must wrap the data object
10716  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10717  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10718  * depending on whether the callback name was passed:
10719  * <p>
10720  * <pre><code>
10721 boolean scriptTag = false;
10722 String cb = request.getParameter("callback");
10723 if (cb != null) {
10724     scriptTag = true;
10725     response.setContentType("text/javascript");
10726 } else {
10727     response.setContentType("application/x-json");
10728 }
10729 Writer out = response.getWriter();
10730 if (scriptTag) {
10731     out.write(cb + "(");
10732 }
10733 out.print(dataBlock.toJsonString());
10734 if (scriptTag) {
10735     out.write(");");
10736 }
10737 </pre></code>
10738  *
10739  * @constructor
10740  * @param {Object} config A configuration object.
10741  */
10742 Roo.data.ScriptTagProxy = function(config){
10743     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10744     Roo.apply(this, config);
10745     this.head = document.getElementsByTagName("head")[0];
10746 };
10747
10748 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10749
10750 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10751     /**
10752      * @cfg {String} url The URL from which to request the data object.
10753      */
10754     /**
10755      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10756      */
10757     timeout : 30000,
10758     /**
10759      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10760      * the server the name of the callback function set up by the load call to process the returned data object.
10761      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10762      * javascript output which calls this named function passing the data object as its only parameter.
10763      */
10764     callbackParam : "callback",
10765     /**
10766      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10767      * name to the request.
10768      */
10769     nocache : true,
10770
10771     /**
10772      * Load data from the configured URL, read the data object into
10773      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10774      * process that block using the passed callback.
10775      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10776      * for the request to the remote server.
10777      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10778      * object into a block of Roo.data.Records.
10779      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10780      * The function must be passed <ul>
10781      * <li>The Record block object</li>
10782      * <li>The "arg" argument from the load function</li>
10783      * <li>A boolean success indicator</li>
10784      * </ul>
10785      * @param {Object} scope The scope in which to call the callback
10786      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10787      */
10788     load : function(params, reader, callback, scope, arg){
10789         if(this.fireEvent("beforeload", this, params) !== false){
10790
10791             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10792
10793             var url = this.url;
10794             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10795             if(this.nocache){
10796                 url += "&_dc=" + (new Date().getTime());
10797             }
10798             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10799             var trans = {
10800                 id : transId,
10801                 cb : "stcCallback"+transId,
10802                 scriptId : "stcScript"+transId,
10803                 params : params,
10804                 arg : arg,
10805                 url : url,
10806                 callback : callback,
10807                 scope : scope,
10808                 reader : reader
10809             };
10810             var conn = this;
10811
10812             window[trans.cb] = function(o){
10813                 conn.handleResponse(o, trans);
10814             };
10815
10816             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10817
10818             if(this.autoAbort !== false){
10819                 this.abort();
10820             }
10821
10822             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10823
10824             var script = document.createElement("script");
10825             script.setAttribute("src", url);
10826             script.setAttribute("type", "text/javascript");
10827             script.setAttribute("id", trans.scriptId);
10828             this.head.appendChild(script);
10829
10830             this.trans = trans;
10831         }else{
10832             callback.call(scope||this, null, arg, false);
10833         }
10834     },
10835
10836     // private
10837     isLoading : function(){
10838         return this.trans ? true : false;
10839     },
10840
10841     /**
10842      * Abort the current server request.
10843      */
10844     abort : function(){
10845         if(this.isLoading()){
10846             this.destroyTrans(this.trans);
10847         }
10848     },
10849
10850     // private
10851     destroyTrans : function(trans, isLoaded){
10852         this.head.removeChild(document.getElementById(trans.scriptId));
10853         clearTimeout(trans.timeoutId);
10854         if(isLoaded){
10855             window[trans.cb] = undefined;
10856             try{
10857                 delete window[trans.cb];
10858             }catch(e){}
10859         }else{
10860             // if hasn't been loaded, wait for load to remove it to prevent script error
10861             window[trans.cb] = function(){
10862                 window[trans.cb] = undefined;
10863                 try{
10864                     delete window[trans.cb];
10865                 }catch(e){}
10866             };
10867         }
10868     },
10869
10870     // private
10871     handleResponse : function(o, trans){
10872         this.trans = false;
10873         this.destroyTrans(trans, true);
10874         var result;
10875         try {
10876             result = trans.reader.readRecords(o);
10877         }catch(e){
10878             this.fireEvent("loadexception", this, o, trans.arg, e);
10879             trans.callback.call(trans.scope||window, null, trans.arg, false);
10880             return;
10881         }
10882         this.fireEvent("load", this, o, trans.arg);
10883         trans.callback.call(trans.scope||window, result, trans.arg, true);
10884     },
10885
10886     // private
10887     handleFailure : function(trans){
10888         this.trans = false;
10889         this.destroyTrans(trans, false);
10890         this.fireEvent("loadexception", this, null, trans.arg);
10891         trans.callback.call(trans.scope||window, null, trans.arg, false);
10892     }
10893 });/*
10894  * Based on:
10895  * Ext JS Library 1.1.1
10896  * Copyright(c) 2006-2007, Ext JS, LLC.
10897  *
10898  * Originally Released Under LGPL - original licence link has changed is not relivant.
10899  *
10900  * Fork - LGPL
10901  * <script type="text/javascript">
10902  */
10903
10904 /**
10905  * @class Roo.data.JsonReader
10906  * @extends Roo.data.DataReader
10907  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10908  * based on mappings in a provided Roo.data.Record constructor.
10909  * 
10910  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10911  * in the reply previously. 
10912  * 
10913  * <p>
10914  * Example code:
10915  * <pre><code>
10916 var RecordDef = Roo.data.Record.create([
10917     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10918     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10919 ]);
10920 var myReader = new Roo.data.JsonReader({
10921     totalProperty: "results",    // The property which contains the total dataset size (optional)
10922     root: "rows",                // The property which contains an Array of row objects
10923     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10924 }, RecordDef);
10925 </code></pre>
10926  * <p>
10927  * This would consume a JSON file like this:
10928  * <pre><code>
10929 { 'results': 2, 'rows': [
10930     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10931     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10932 }
10933 </code></pre>
10934  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10935  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10936  * paged from the remote server.
10937  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10938  * @cfg {String} root name of the property which contains the Array of row objects.
10939  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10940  * @cfg {Array} fields Array of field definition objects
10941  * @constructor
10942  * Create a new JsonReader
10943  * @param {Object} meta Metadata configuration options
10944  * @param {Object} recordType Either an Array of field definition objects,
10945  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10946  */
10947 Roo.data.JsonReader = function(meta, recordType){
10948     
10949     meta = meta || {};
10950     // set some defaults:
10951     Roo.applyIf(meta, {
10952         totalProperty: 'total',
10953         successProperty : 'success',
10954         root : 'data',
10955         id : 'id'
10956     });
10957     
10958     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10959 };
10960 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10961     
10962     /**
10963      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10964      * Used by Store query builder to append _requestMeta to params.
10965      * 
10966      */
10967     metaFromRemote : false,
10968     /**
10969      * This method is only used by a DataProxy which has retrieved data from a remote server.
10970      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10971      * @return {Object} data A data block which is used by an Roo.data.Store object as
10972      * a cache of Roo.data.Records.
10973      */
10974     read : function(response){
10975         var json = response.responseText;
10976        
10977         var o = /* eval:var:o */ eval("("+json+")");
10978         if(!o) {
10979             throw {message: "JsonReader.read: Json object not found"};
10980         }
10981         
10982         if(o.metaData){
10983             
10984             delete this.ef;
10985             this.metaFromRemote = true;
10986             this.meta = o.metaData;
10987             this.recordType = Roo.data.Record.create(o.metaData.fields);
10988             this.onMetaChange(this.meta, this.recordType, o);
10989         }
10990         return this.readRecords(o);
10991     },
10992
10993     // private function a store will implement
10994     onMetaChange : function(meta, recordType, o){
10995
10996     },
10997
10998     /**
10999          * @ignore
11000          */
11001     simpleAccess: function(obj, subsc) {
11002         return obj[subsc];
11003     },
11004
11005         /**
11006          * @ignore
11007          */
11008     getJsonAccessor: function(){
11009         var re = /[\[\.]/;
11010         return function(expr) {
11011             try {
11012                 return(re.test(expr))
11013                     ? new Function("obj", "return obj." + expr)
11014                     : function(obj){
11015                         return obj[expr];
11016                     };
11017             } catch(e){}
11018             return Roo.emptyFn;
11019         };
11020     }(),
11021
11022     /**
11023      * Create a data block containing Roo.data.Records from an XML document.
11024      * @param {Object} o An object which contains an Array of row objects in the property specified
11025      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11026      * which contains the total size of the dataset.
11027      * @return {Object} data A data block which is used by an Roo.data.Store object as
11028      * a cache of Roo.data.Records.
11029      */
11030     readRecords : function(o){
11031         /**
11032          * After any data loads, the raw JSON data is available for further custom processing.
11033          * @type Object
11034          */
11035         this.o = o;
11036         var s = this.meta, Record = this.recordType,
11037             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11038
11039 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11040         if (!this.ef) {
11041             if(s.totalProperty) {
11042                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11043                 }
11044                 if(s.successProperty) {
11045                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11046                 }
11047                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11048                 if (s.id) {
11049                         var g = this.getJsonAccessor(s.id);
11050                         this.getId = function(rec) {
11051                                 var r = g(rec);  
11052                                 return (r === undefined || r === "") ? null : r;
11053                         };
11054                 } else {
11055                         this.getId = function(){return null;};
11056                 }
11057             this.ef = [];
11058             for(var jj = 0; jj < fl; jj++){
11059                 f = fi[jj];
11060                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11061                 this.ef[jj] = this.getJsonAccessor(map);
11062             }
11063         }
11064
11065         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11066         if(s.totalProperty){
11067             var vt = parseInt(this.getTotal(o), 10);
11068             if(!isNaN(vt)){
11069                 totalRecords = vt;
11070             }
11071         }
11072         if(s.successProperty){
11073             var vs = this.getSuccess(o);
11074             if(vs === false || vs === 'false'){
11075                 success = false;
11076             }
11077         }
11078         var records = [];
11079         for(var i = 0; i < c; i++){
11080                 var n = root[i];
11081             var values = {};
11082             var id = this.getId(n);
11083             for(var j = 0; j < fl; j++){
11084                 f = fi[j];
11085             var v = this.ef[j](n);
11086             if (!f.convert) {
11087                 Roo.log('missing convert for ' + f.name);
11088                 Roo.log(f);
11089                 continue;
11090             }
11091             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11092             }
11093             var record = new Record(values, id);
11094             record.json = n;
11095             records[i] = record;
11096         }
11097         return {
11098             raw : o,
11099             success : success,
11100             records : records,
11101             totalRecords : totalRecords
11102         };
11103     }
11104 });/*
11105  * Based on:
11106  * Ext JS Library 1.1.1
11107  * Copyright(c) 2006-2007, Ext JS, LLC.
11108  *
11109  * Originally Released Under LGPL - original licence link has changed is not relivant.
11110  *
11111  * Fork - LGPL
11112  * <script type="text/javascript">
11113  */
11114
11115 /**
11116  * @class Roo.data.ArrayReader
11117  * @extends Roo.data.DataReader
11118  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11119  * Each element of that Array represents a row of data fields. The
11120  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11121  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11122  * <p>
11123  * Example code:.
11124  * <pre><code>
11125 var RecordDef = Roo.data.Record.create([
11126     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11127     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11128 ]);
11129 var myReader = new Roo.data.ArrayReader({
11130     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11131 }, RecordDef);
11132 </code></pre>
11133  * <p>
11134  * This would consume an Array like this:
11135  * <pre><code>
11136 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11137   </code></pre>
11138  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11139  * @constructor
11140  * Create a new JsonReader
11141  * @param {Object} meta Metadata configuration options.
11142  * @param {Object} recordType Either an Array of field definition objects
11143  * as specified to {@link Roo.data.Record#create},
11144  * or an {@link Roo.data.Record} object
11145  * created using {@link Roo.data.Record#create}.
11146  */
11147 Roo.data.ArrayReader = function(meta, recordType){
11148     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11149 };
11150
11151 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11152     /**
11153      * Create a data block containing Roo.data.Records from an XML document.
11154      * @param {Object} o An Array of row objects which represents the dataset.
11155      * @return {Object} data A data block which is used by an Roo.data.Store object as
11156      * a cache of Roo.data.Records.
11157      */
11158     readRecords : function(o){
11159         var sid = this.meta ? this.meta.id : null;
11160         var recordType = this.recordType, fields = recordType.prototype.fields;
11161         var records = [];
11162         var root = o;
11163             for(var i = 0; i < root.length; i++){
11164                     var n = root[i];
11165                 var values = {};
11166                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11167                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11168                 var f = fields.items[j];
11169                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11170                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11171                 v = f.convert(v);
11172                 values[f.name] = v;
11173             }
11174                 var record = new recordType(values, id);
11175                 record.json = n;
11176                 records[records.length] = record;
11177             }
11178             return {
11179                 records : records,
11180                 totalRecords : records.length
11181             };
11182     }
11183 });/*
11184  * - LGPL
11185  * * 
11186  */
11187
11188 /**
11189  * @class Roo.bootstrap.ComboBox
11190  * @extends Roo.bootstrap.TriggerField
11191  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11192  * @cfg {Boolean} append (true|false) default false
11193  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11194  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11195  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11196  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11197  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11198  * @cfg {Boolean} animate default true
11199  * @cfg {Boolean} emptyResultText only for touch device
11200  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11201  * @constructor
11202  * Create a new ComboBox.
11203  * @param {Object} config Configuration options
11204  */
11205 Roo.bootstrap.ComboBox = function(config){
11206     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11207     this.addEvents({
11208         /**
11209          * @event expand
11210          * Fires when the dropdown list is expanded
11211              * @param {Roo.bootstrap.ComboBox} combo This combo box
11212              */
11213         'expand' : true,
11214         /**
11215          * @event collapse
11216          * Fires when the dropdown list is collapsed
11217              * @param {Roo.bootstrap.ComboBox} combo This combo box
11218              */
11219         'collapse' : true,
11220         /**
11221          * @event beforeselect
11222          * Fires before a list item is selected. Return false to cancel the selection.
11223              * @param {Roo.bootstrap.ComboBox} combo This combo box
11224              * @param {Roo.data.Record} record The data record returned from the underlying store
11225              * @param {Number} index The index of the selected item in the dropdown list
11226              */
11227         'beforeselect' : true,
11228         /**
11229          * @event select
11230          * Fires when a list item is selected
11231              * @param {Roo.bootstrap.ComboBox} combo This combo box
11232              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11233              * @param {Number} index The index of the selected item in the dropdown list
11234              */
11235         'select' : true,
11236         /**
11237          * @event beforequery
11238          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11239          * The event object passed has these properties:
11240              * @param {Roo.bootstrap.ComboBox} combo This combo box
11241              * @param {String} query The query
11242              * @param {Boolean} forceAll true to force "all" query
11243              * @param {Boolean} cancel true to cancel the query
11244              * @param {Object} e The query event object
11245              */
11246         'beforequery': true,
11247          /**
11248          * @event add
11249          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11250              * @param {Roo.bootstrap.ComboBox} combo This combo box
11251              */
11252         'add' : true,
11253         /**
11254          * @event edit
11255          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11256              * @param {Roo.bootstrap.ComboBox} combo This combo box
11257              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11258              */
11259         'edit' : true,
11260         /**
11261          * @event remove
11262          * Fires when the remove value from the combobox array
11263              * @param {Roo.bootstrap.ComboBox} combo This combo box
11264              */
11265         'remove' : true,
11266         /**
11267          * @event specialfilter
11268          * Fires when specialfilter
11269             * @param {Roo.bootstrap.ComboBox} combo This combo box
11270             */
11271         'specialfilter' : true
11272         
11273     });
11274     
11275     this.item = [];
11276     this.tickItems = [];
11277     
11278     this.selectedIndex = -1;
11279     if(this.mode == 'local'){
11280         if(config.queryDelay === undefined){
11281             this.queryDelay = 10;
11282         }
11283         if(config.minChars === undefined){
11284             this.minChars = 0;
11285         }
11286     }
11287 };
11288
11289 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11290      
11291     /**
11292      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11293      * rendering into an Roo.Editor, defaults to false)
11294      */
11295     /**
11296      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11297      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11298      */
11299     /**
11300      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11301      */
11302     /**
11303      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11304      * the dropdown list (defaults to undefined, with no header element)
11305      */
11306
11307      /**
11308      * @cfg {String/Roo.Template} tpl The template to use to render the output
11309      */
11310      
11311      /**
11312      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11313      */
11314     listWidth: undefined,
11315     /**
11316      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11317      * mode = 'remote' or 'text' if mode = 'local')
11318      */
11319     displayField: undefined,
11320     
11321     /**
11322      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11323      * mode = 'remote' or 'value' if mode = 'local'). 
11324      * Note: use of a valueField requires the user make a selection
11325      * in order for a value to be mapped.
11326      */
11327     valueField: undefined,
11328     
11329     
11330     /**
11331      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11332      * field's data value (defaults to the underlying DOM element's name)
11333      */
11334     hiddenName: undefined,
11335     /**
11336      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11337      */
11338     listClass: '',
11339     /**
11340      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11341      */
11342     selectedClass: 'active',
11343     
11344     /**
11345      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11346      */
11347     shadow:'sides',
11348     /**
11349      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11350      * anchor positions (defaults to 'tl-bl')
11351      */
11352     listAlign: 'tl-bl?',
11353     /**
11354      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11355      */
11356     maxHeight: 300,
11357     /**
11358      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11359      * query specified by the allQuery config option (defaults to 'query')
11360      */
11361     triggerAction: 'query',
11362     /**
11363      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11364      * (defaults to 4, does not apply if editable = false)
11365      */
11366     minChars : 4,
11367     /**
11368      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11369      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11370      */
11371     typeAhead: false,
11372     /**
11373      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11374      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11375      */
11376     queryDelay: 500,
11377     /**
11378      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11379      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11380      */
11381     pageSize: 0,
11382     /**
11383      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11384      * when editable = true (defaults to false)
11385      */
11386     selectOnFocus:false,
11387     /**
11388      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11389      */
11390     queryParam: 'query',
11391     /**
11392      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11393      * when mode = 'remote' (defaults to 'Loading...')
11394      */
11395     loadingText: 'Loading...',
11396     /**
11397      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11398      */
11399     resizable: false,
11400     /**
11401      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11402      */
11403     handleHeight : 8,
11404     /**
11405      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11406      * traditional select (defaults to true)
11407      */
11408     editable: true,
11409     /**
11410      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11411      */
11412     allQuery: '',
11413     /**
11414      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11415      */
11416     mode: 'remote',
11417     /**
11418      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11419      * listWidth has a higher value)
11420      */
11421     minListWidth : 70,
11422     /**
11423      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11424      * allow the user to set arbitrary text into the field (defaults to false)
11425      */
11426     forceSelection:false,
11427     /**
11428      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11429      * if typeAhead = true (defaults to 250)
11430      */
11431     typeAheadDelay : 250,
11432     /**
11433      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11434      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11435      */
11436     valueNotFoundText : undefined,
11437     /**
11438      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11439      */
11440     blockFocus : false,
11441     
11442     /**
11443      * @cfg {Boolean} disableClear Disable showing of clear button.
11444      */
11445     disableClear : false,
11446     /**
11447      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11448      */
11449     alwaysQuery : false,
11450     
11451     /**
11452      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11453      */
11454     multiple : false,
11455     
11456     /**
11457      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11458      */
11459     invalidClass : "has-warning",
11460     
11461     /**
11462      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11463      */
11464     validClass : "has-success",
11465     
11466     /**
11467      * @cfg {Boolean} specialFilter (true|false) special filter default false
11468      */
11469     specialFilter : false,
11470     
11471     /**
11472      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11473      */
11474     mobileTouchView : true,
11475     
11476     //private
11477     addicon : false,
11478     editicon: false,
11479     
11480     page: 0,
11481     hasQuery: false,
11482     append: false,
11483     loadNext: false,
11484     autoFocus : true,
11485     tickable : false,
11486     btnPosition : 'right',
11487     triggerList : true,
11488     showToggleBtn : true,
11489     animate : true,
11490     emptyResultText: 'Empty',
11491     triggerText : 'Select',
11492     
11493     // element that contains real text value.. (when hidden is used..)
11494     
11495     getAutoCreate : function()
11496     {
11497         var cfg = false;
11498         
11499         /*
11500          * Touch Devices
11501          */
11502         
11503         if(Roo.isTouch && this.mobileTouchView){
11504             cfg = this.getAutoCreateTouchView();
11505             return cfg;;
11506         }
11507         
11508         /*
11509          *  Normal ComboBox
11510          */
11511         if(!this.tickable){
11512             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11513             return cfg;
11514         }
11515         
11516         /*
11517          *  ComboBox with tickable selections
11518          */
11519              
11520         var align = this.labelAlign || this.parentLabelAlign();
11521         
11522         cfg = {
11523             cls : 'form-group roo-combobox-tickable' //input-group
11524         };
11525         
11526         var buttons = {
11527             tag : 'div',
11528             cls : 'tickable-buttons',
11529             cn : [
11530                 {
11531                     tag : 'button',
11532                     type : 'button',
11533                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11534                     html : this.triggerText
11535                 },
11536                 {
11537                     tag : 'button',
11538                     type : 'button',
11539                     name : 'ok',
11540                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11541                     html : 'Done'
11542                 },
11543                 {
11544                     tag : 'button',
11545                     type : 'button',
11546                     name : 'cancel',
11547                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11548                     html : 'Cancel'
11549                 }
11550             ]
11551         };
11552         
11553         if(this.editable){
11554             buttons.cn.unshift({
11555                 tag: 'input',
11556                 cls: 'select2-search-field-input'
11557             });
11558         }
11559         
11560         var _this = this;
11561         
11562         Roo.each(buttons.cn, function(c){
11563             if (_this.size) {
11564                 c.cls += ' btn-' + _this.size;
11565             }
11566
11567             if (_this.disabled) {
11568                 c.disabled = true;
11569             }
11570         });
11571         
11572         var box = {
11573             tag: 'div',
11574             cn: [
11575                 {
11576                     tag: 'input',
11577                     type : 'hidden',
11578                     cls: 'form-hidden-field'
11579                 },
11580                 {
11581                     tag: 'ul',
11582                     cls: 'select2-choices',
11583                     cn:[
11584                         {
11585                             tag: 'li',
11586                             cls: 'select2-search-field',
11587                             cn: [
11588
11589                                 buttons
11590                             ]
11591                         }
11592                     ]
11593                 }
11594             ]
11595         }
11596         
11597         var combobox = {
11598             cls: 'select2-container input-group select2-container-multi',
11599             cn: [
11600                 box
11601 //                {
11602 //                    tag: 'ul',
11603 //                    cls: 'typeahead typeahead-long dropdown-menu',
11604 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11605 //                }
11606             ]
11607         };
11608         
11609         if(this.hasFeedback && !this.allowBlank){
11610             
11611             var feedback = {
11612                 tag: 'span',
11613                 cls: 'glyphicon form-control-feedback'
11614             };
11615
11616             combobox.cn.push(feedback);
11617         }
11618         
11619         if (align ==='left' && this.fieldLabel.length) {
11620             
11621                 Roo.log("left and has label");
11622                 cfg.cn = [
11623                     
11624                     {
11625                         tag: 'label',
11626                         'for' :  id,
11627                         cls : 'control-label col-sm-' + this.labelWidth,
11628                         html : this.fieldLabel
11629                         
11630                     },
11631                     {
11632                         cls : "col-sm-" + (12 - this.labelWidth), 
11633                         cn: [
11634                             combobox
11635                         ]
11636                     }
11637                     
11638                 ];
11639         } else if ( this.fieldLabel.length) {
11640                 Roo.log(" label");
11641                  cfg.cn = [
11642                    
11643                     {
11644                         tag: 'label',
11645                         //cls : 'input-group-addon',
11646                         html : this.fieldLabel
11647                         
11648                     },
11649                     
11650                     combobox
11651                     
11652                 ];
11653
11654         } else {
11655             
11656                 Roo.log(" no label && no align");
11657                 cfg = combobox
11658                      
11659                 
11660         }
11661          
11662         var settings=this;
11663         ['xs','sm','md','lg'].map(function(size){
11664             if (settings[size]) {
11665                 cfg.cls += ' col-' + size + '-' + settings[size];
11666             }
11667         });
11668         
11669         return cfg;
11670         
11671     },
11672     
11673     _initEventsCalled : false,
11674     
11675     // private
11676     initEvents: function()
11677     {
11678         
11679         if (this._initEventsCalled) { // as we call render... prevent looping...
11680             return;
11681         }
11682         this._initEventsCalled = true;
11683         
11684         if (!this.store) {
11685             throw "can not find store for combo";
11686         }
11687         
11688         this.store = Roo.factory(this.store, Roo.data);
11689         
11690         // if we are building from html. then this element is so complex, that we can not really
11691         // use the rendered HTML.
11692         // so we have to trash and replace the previous code.
11693         if (Roo.XComponent.build_from_html) {
11694             
11695             // remove this element....
11696             var e = this.el.dom, k=0;
11697             while (e ) { e = e.previousSibling;  ++k;}
11698
11699             this.el.remove();
11700             
11701             this.el=false;
11702             this.rendered = false;
11703             
11704             this.render(this.parent().getChildContainer(true), k);
11705             
11706             
11707             
11708         }
11709         
11710         
11711         /*
11712          * Touch Devices
11713          */
11714         
11715         if(Roo.isTouch && this.mobileTouchView){
11716             this.initTouchView();
11717             return;
11718         }
11719         
11720         if(this.tickable){
11721             this.initTickableEvents();
11722             return;
11723         }
11724         
11725         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11726         
11727         if(this.hiddenName){
11728             
11729             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11730             
11731             this.hiddenField.dom.value =
11732                 this.hiddenValue !== undefined ? this.hiddenValue :
11733                 this.value !== undefined ? this.value : '';
11734
11735             // prevent input submission
11736             this.el.dom.removeAttribute('name');
11737             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11738              
11739              
11740         }
11741         //if(Roo.isGecko){
11742         //    this.el.dom.setAttribute('autocomplete', 'off');
11743         //}
11744         
11745         var cls = 'x-combo-list';
11746         
11747         //this.list = new Roo.Layer({
11748         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11749         //});
11750         
11751         var _this = this;
11752         
11753         (function(){
11754             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11755             _this.list.setWidth(lw);
11756         }).defer(100);
11757         
11758         this.list.on('mouseover', this.onViewOver, this);
11759         this.list.on('mousemove', this.onViewMove, this);
11760         
11761         this.list.on('scroll', this.onViewScroll, this);
11762         
11763         /*
11764         this.list.swallowEvent('mousewheel');
11765         this.assetHeight = 0;
11766
11767         if(this.title){
11768             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11769             this.assetHeight += this.header.getHeight();
11770         }
11771
11772         this.innerList = this.list.createChild({cls:cls+'-inner'});
11773         this.innerList.on('mouseover', this.onViewOver, this);
11774         this.innerList.on('mousemove', this.onViewMove, this);
11775         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11776         
11777         if(this.allowBlank && !this.pageSize && !this.disableClear){
11778             this.footer = this.list.createChild({cls:cls+'-ft'});
11779             this.pageTb = new Roo.Toolbar(this.footer);
11780            
11781         }
11782         if(this.pageSize){
11783             this.footer = this.list.createChild({cls:cls+'-ft'});
11784             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11785                     {pageSize: this.pageSize});
11786             
11787         }
11788         
11789         if (this.pageTb && this.allowBlank && !this.disableClear) {
11790             var _this = this;
11791             this.pageTb.add(new Roo.Toolbar.Fill(), {
11792                 cls: 'x-btn-icon x-btn-clear',
11793                 text: '&#160;',
11794                 handler: function()
11795                 {
11796                     _this.collapse();
11797                     _this.clearValue();
11798                     _this.onSelect(false, -1);
11799                 }
11800             });
11801         }
11802         if (this.footer) {
11803             this.assetHeight += this.footer.getHeight();
11804         }
11805         */
11806             
11807         if(!this.tpl){
11808             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11809         }
11810
11811         this.view = new Roo.View(this.list, this.tpl, {
11812             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11813         });
11814         //this.view.wrapEl.setDisplayed(false);
11815         this.view.on('click', this.onViewClick, this);
11816         
11817         
11818         
11819         this.store.on('beforeload', this.onBeforeLoad, this);
11820         this.store.on('load', this.onLoad, this);
11821         this.store.on('loadexception', this.onLoadException, this);
11822         /*
11823         if(this.resizable){
11824             this.resizer = new Roo.Resizable(this.list,  {
11825                pinned:true, handles:'se'
11826             });
11827             this.resizer.on('resize', function(r, w, h){
11828                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11829                 this.listWidth = w;
11830                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11831                 this.restrictHeight();
11832             }, this);
11833             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11834         }
11835         */
11836         if(!this.editable){
11837             this.editable = true;
11838             this.setEditable(false);
11839         }
11840         
11841         /*
11842         
11843         if (typeof(this.events.add.listeners) != 'undefined') {
11844             
11845             this.addicon = this.wrap.createChild(
11846                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11847        
11848             this.addicon.on('click', function(e) {
11849                 this.fireEvent('add', this);
11850             }, this);
11851         }
11852         if (typeof(this.events.edit.listeners) != 'undefined') {
11853             
11854             this.editicon = this.wrap.createChild(
11855                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11856             if (this.addicon) {
11857                 this.editicon.setStyle('margin-left', '40px');
11858             }
11859             this.editicon.on('click', function(e) {
11860                 
11861                 // we fire even  if inothing is selected..
11862                 this.fireEvent('edit', this, this.lastData );
11863                 
11864             }, this);
11865         }
11866         */
11867         
11868         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11869             "up" : function(e){
11870                 this.inKeyMode = true;
11871                 this.selectPrev();
11872             },
11873
11874             "down" : function(e){
11875                 if(!this.isExpanded()){
11876                     this.onTriggerClick();
11877                 }else{
11878                     this.inKeyMode = true;
11879                     this.selectNext();
11880                 }
11881             },
11882
11883             "enter" : function(e){
11884 //                this.onViewClick();
11885                 //return true;
11886                 this.collapse();
11887                 
11888                 if(this.fireEvent("specialkey", this, e)){
11889                     this.onViewClick(false);
11890                 }
11891                 
11892                 return true;
11893             },
11894
11895             "esc" : function(e){
11896                 this.collapse();
11897             },
11898
11899             "tab" : function(e){
11900                 this.collapse();
11901                 
11902                 if(this.fireEvent("specialkey", this, e)){
11903                     this.onViewClick(false);
11904                 }
11905                 
11906                 return true;
11907             },
11908
11909             scope : this,
11910
11911             doRelay : function(foo, bar, hname){
11912                 if(hname == 'down' || this.scope.isExpanded()){
11913                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11914                 }
11915                 return true;
11916             },
11917
11918             forceKeyDown: true
11919         });
11920         
11921         
11922         this.queryDelay = Math.max(this.queryDelay || 10,
11923                 this.mode == 'local' ? 10 : 250);
11924         
11925         
11926         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11927         
11928         if(this.typeAhead){
11929             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11930         }
11931         if(this.editable !== false){
11932             this.inputEl().on("keyup", this.onKeyUp, this);
11933         }
11934         if(this.forceSelection){
11935             this.inputEl().on('blur', this.doForce, this);
11936         }
11937         
11938         if(this.multiple){
11939             this.choices = this.el.select('ul.select2-choices', true).first();
11940             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11941         }
11942     },
11943     
11944     initTickableEvents: function()
11945     {   
11946         this.createList();
11947         
11948         if(this.hiddenName){
11949             
11950             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11951             
11952             this.hiddenField.dom.value =
11953                 this.hiddenValue !== undefined ? this.hiddenValue :
11954                 this.value !== undefined ? this.value : '';
11955
11956             // prevent input submission
11957             this.el.dom.removeAttribute('name');
11958             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11959              
11960              
11961         }
11962         
11963 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11964         
11965         this.choices = this.el.select('ul.select2-choices', true).first();
11966         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11967         if(this.triggerList){
11968             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11969         }
11970          
11971         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11972         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11973         
11974         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11975         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11976         
11977         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11978         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11979         
11980         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11981         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11982         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11983         
11984         this.okBtn.hide();
11985         this.cancelBtn.hide();
11986         
11987         var _this = this;
11988         
11989         (function(){
11990             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11991             _this.list.setWidth(lw);
11992         }).defer(100);
11993         
11994         this.list.on('mouseover', this.onViewOver, this);
11995         this.list.on('mousemove', this.onViewMove, this);
11996         
11997         this.list.on('scroll', this.onViewScroll, this);
11998         
11999         if(!this.tpl){
12000             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>';
12001         }
12002
12003         this.view = new Roo.View(this.list, this.tpl, {
12004             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12005         });
12006         
12007         //this.view.wrapEl.setDisplayed(false);
12008         this.view.on('click', this.onViewClick, this);
12009         
12010         
12011         
12012         this.store.on('beforeload', this.onBeforeLoad, this);
12013         this.store.on('load', this.onLoad, this);
12014         this.store.on('loadexception', this.onLoadException, this);
12015         
12016         if(this.editable){
12017             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12018                 "up" : function(e){
12019                     this.inKeyMode = true;
12020                     this.selectPrev();
12021                 },
12022
12023                 "down" : function(e){
12024                     this.inKeyMode = true;
12025                     this.selectNext();
12026                 },
12027
12028                 "enter" : function(e){
12029                     if(this.fireEvent("specialkey", this, e)){
12030                         this.onViewClick(false);
12031                     }
12032                     
12033                     return true;
12034                 },
12035
12036                 "esc" : function(e){
12037                     this.onTickableFooterButtonClick(e, false, false);
12038                 },
12039
12040                 "tab" : function(e){
12041                     this.fireEvent("specialkey", this, e);
12042                     
12043                     this.onTickableFooterButtonClick(e, false, false);
12044                     
12045                     return true;
12046                 },
12047
12048                 scope : this,
12049
12050                 doRelay : function(e, fn, key){
12051                     if(this.scope.isExpanded()){
12052                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12053                     }
12054                     return true;
12055                 },
12056
12057                 forceKeyDown: true
12058             });
12059         }
12060         
12061         this.queryDelay = Math.max(this.queryDelay || 10,
12062                 this.mode == 'local' ? 10 : 250);
12063         
12064         
12065         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12066         
12067         if(this.typeAhead){
12068             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12069         }
12070         
12071         if(this.editable !== false){
12072             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12073         }
12074         
12075     },
12076
12077     onDestroy : function(){
12078         if(this.view){
12079             this.view.setStore(null);
12080             this.view.el.removeAllListeners();
12081             this.view.el.remove();
12082             this.view.purgeListeners();
12083         }
12084         if(this.list){
12085             this.list.dom.innerHTML  = '';
12086         }
12087         
12088         if(this.store){
12089             this.store.un('beforeload', this.onBeforeLoad, this);
12090             this.store.un('load', this.onLoad, this);
12091             this.store.un('loadexception', this.onLoadException, this);
12092         }
12093         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12094     },
12095
12096     // private
12097     fireKey : function(e){
12098         if(e.isNavKeyPress() && !this.list.isVisible()){
12099             this.fireEvent("specialkey", this, e);
12100         }
12101     },
12102
12103     // private
12104     onResize: function(w, h){
12105 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12106 //        
12107 //        if(typeof w != 'number'){
12108 //            // we do not handle it!?!?
12109 //            return;
12110 //        }
12111 //        var tw = this.trigger.getWidth();
12112 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12113 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12114 //        var x = w - tw;
12115 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12116 //            
12117 //        //this.trigger.setStyle('left', x+'px');
12118 //        
12119 //        if(this.list && this.listWidth === undefined){
12120 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12121 //            this.list.setWidth(lw);
12122 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12123 //        }
12124         
12125     
12126         
12127     },
12128
12129     /**
12130      * Allow or prevent the user from directly editing the field text.  If false is passed,
12131      * the user will only be able to select from the items defined in the dropdown list.  This method
12132      * is the runtime equivalent of setting the 'editable' config option at config time.
12133      * @param {Boolean} value True to allow the user to directly edit the field text
12134      */
12135     setEditable : function(value){
12136         if(value == this.editable){
12137             return;
12138         }
12139         this.editable = value;
12140         if(!value){
12141             this.inputEl().dom.setAttribute('readOnly', true);
12142             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12143             this.inputEl().addClass('x-combo-noedit');
12144         }else{
12145             this.inputEl().dom.setAttribute('readOnly', false);
12146             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12147             this.inputEl().removeClass('x-combo-noedit');
12148         }
12149     },
12150
12151     // private
12152     
12153     onBeforeLoad : function(combo,opts){
12154         if(!this.hasFocus){
12155             return;
12156         }
12157          if (!opts.add) {
12158             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12159          }
12160         this.restrictHeight();
12161         this.selectedIndex = -1;
12162     },
12163
12164     // private
12165     onLoad : function(){
12166         
12167         this.hasQuery = false;
12168         
12169         if(!this.hasFocus){
12170             return;
12171         }
12172         
12173         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12174             this.loading.hide();
12175         }
12176              
12177         if(this.store.getCount() > 0){
12178             this.expand();
12179             this.restrictHeight();
12180             if(this.lastQuery == this.allQuery){
12181                 if(this.editable && !this.tickable){
12182                     this.inputEl().dom.select();
12183                 }
12184                 
12185                 if(
12186                     !this.selectByValue(this.value, true) &&
12187                     this.autoFocus && 
12188                     (
12189                         !this.store.lastOptions ||
12190                         typeof(this.store.lastOptions.add) == 'undefined' || 
12191                         this.store.lastOptions.add != true
12192                     )
12193                 ){
12194                     this.select(0, true);
12195                 }
12196             }else{
12197                 if(this.autoFocus){
12198                     this.selectNext();
12199                 }
12200                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12201                     this.taTask.delay(this.typeAheadDelay);
12202                 }
12203             }
12204         }else{
12205             this.onEmptyResults();
12206         }
12207         
12208         //this.el.focus();
12209     },
12210     // private
12211     onLoadException : function()
12212     {
12213         this.hasQuery = false;
12214         
12215         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12216             this.loading.hide();
12217         }
12218         
12219         if(this.tickable && this.editable){
12220             return;
12221         }
12222         
12223         this.collapse();
12224         
12225         Roo.log(this.store.reader.jsonData);
12226         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12227             // fixme
12228             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12229         }
12230         
12231         
12232     },
12233     // private
12234     onTypeAhead : function(){
12235         if(this.store.getCount() > 0){
12236             var r = this.store.getAt(0);
12237             var newValue = r.data[this.displayField];
12238             var len = newValue.length;
12239             var selStart = this.getRawValue().length;
12240             
12241             if(selStart != len){
12242                 this.setRawValue(newValue);
12243                 this.selectText(selStart, newValue.length);
12244             }
12245         }
12246     },
12247
12248     // private
12249     onSelect : function(record, index){
12250         
12251         if(this.fireEvent('beforeselect', this, record, index) !== false){
12252         
12253             this.setFromData(index > -1 ? record.data : false);
12254             
12255             this.collapse();
12256             this.fireEvent('select', this, record, index);
12257         }
12258     },
12259
12260     /**
12261      * Returns the currently selected field value or empty string if no value is set.
12262      * @return {String} value The selected value
12263      */
12264     getValue : function(){
12265         
12266         if(this.multiple){
12267             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12268         }
12269         
12270         if(this.valueField){
12271             return typeof this.value != 'undefined' ? this.value : '';
12272         }else{
12273             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12274         }
12275     },
12276
12277     /**
12278      * Clears any text/value currently set in the field
12279      */
12280     clearValue : function(){
12281         if(this.hiddenField){
12282             this.hiddenField.dom.value = '';
12283         }
12284         this.value = '';
12285         this.setRawValue('');
12286         this.lastSelectionText = '';
12287         this.lastData = false;
12288         
12289         var close = this.closeTriggerEl();
12290         
12291         if(close){
12292             close.hide();
12293         }
12294         
12295     },
12296
12297     /**
12298      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12299      * will be displayed in the field.  If the value does not match the data value of an existing item,
12300      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12301      * Otherwise the field will be blank (although the value will still be set).
12302      * @param {String} value The value to match
12303      */
12304     setValue : function(v){
12305         if(this.multiple){
12306             this.syncValue();
12307             return;
12308         }
12309         
12310         var text = v;
12311         if(this.valueField){
12312             var r = this.findRecord(this.valueField, v);
12313             if(r){
12314                 text = r.data[this.displayField];
12315             }else if(this.valueNotFoundText !== undefined){
12316                 text = this.valueNotFoundText;
12317             }
12318         }
12319         this.lastSelectionText = text;
12320         if(this.hiddenField){
12321             this.hiddenField.dom.value = v;
12322         }
12323         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12324         this.value = v;
12325         
12326         var close = this.closeTriggerEl();
12327         
12328         if(close){
12329             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12330         }
12331     },
12332     /**
12333      * @property {Object} the last set data for the element
12334      */
12335     
12336     lastData : false,
12337     /**
12338      * Sets the value of the field based on a object which is related to the record format for the store.
12339      * @param {Object} value the value to set as. or false on reset?
12340      */
12341     setFromData : function(o){
12342         
12343         if(this.multiple){
12344             this.addItem(o);
12345             return;
12346         }
12347             
12348         var dv = ''; // display value
12349         var vv = ''; // value value..
12350         this.lastData = o;
12351         if (this.displayField) {
12352             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12353         } else {
12354             // this is an error condition!!!
12355             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12356         }
12357         
12358         if(this.valueField){
12359             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12360         }
12361         
12362         var close = this.closeTriggerEl();
12363         
12364         if(close){
12365             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12366         }
12367         
12368         if(this.hiddenField){
12369             this.hiddenField.dom.value = vv;
12370             
12371             this.lastSelectionText = dv;
12372             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12373             this.value = vv;
12374             return;
12375         }
12376         // no hidden field.. - we store the value in 'value', but still display
12377         // display field!!!!
12378         this.lastSelectionText = dv;
12379         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12380         this.value = vv;
12381         
12382         
12383         
12384     },
12385     // private
12386     reset : function(){
12387         // overridden so that last data is reset..
12388         
12389         if(this.multiple){
12390             this.clearItem();
12391             return;
12392         }
12393         
12394         this.setValue(this.originalValue);
12395         this.clearInvalid();
12396         this.lastData = false;
12397         if (this.view) {
12398             this.view.clearSelections();
12399         }
12400     },
12401     // private
12402     findRecord : function(prop, value){
12403         var record;
12404         if(this.store.getCount() > 0){
12405             this.store.each(function(r){
12406                 if(r.data[prop] == value){
12407                     record = r;
12408                     return false;
12409                 }
12410                 return true;
12411             });
12412         }
12413         return record;
12414     },
12415     
12416     getName: function()
12417     {
12418         // returns hidden if it's set..
12419         if (!this.rendered) {return ''};
12420         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12421         
12422     },
12423     // private
12424     onViewMove : function(e, t){
12425         this.inKeyMode = false;
12426     },
12427
12428     // private
12429     onViewOver : function(e, t){
12430         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12431             return;
12432         }
12433         var item = this.view.findItemFromChild(t);
12434         
12435         if(item){
12436             var index = this.view.indexOf(item);
12437             this.select(index, false);
12438         }
12439     },
12440
12441     // private
12442     onViewClick : function(view, doFocus, el, e)
12443     {
12444         var index = this.view.getSelectedIndexes()[0];
12445         
12446         var r = this.store.getAt(index);
12447         
12448         if(this.tickable){
12449             
12450             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12451                 return;
12452             }
12453             
12454             var rm = false;
12455             var _this = this;
12456             
12457             Roo.each(this.tickItems, function(v,k){
12458                 
12459                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12460                     _this.tickItems.splice(k, 1);
12461                     
12462                     if(typeof(e) == 'undefined' && view == false){
12463                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12464                     }
12465                     
12466                     rm = true;
12467                     return;
12468                 }
12469             });
12470             
12471             if(rm){
12472                 return;
12473             }
12474             
12475             this.tickItems.push(r.data);
12476             
12477             if(typeof(e) == 'undefined' && view == false){
12478                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12479             }
12480                     
12481             return;
12482         }
12483         
12484         if(r){
12485             this.onSelect(r, index);
12486         }
12487         if(doFocus !== false && !this.blockFocus){
12488             this.inputEl().focus();
12489         }
12490     },
12491
12492     // private
12493     restrictHeight : function(){
12494         //this.innerList.dom.style.height = '';
12495         //var inner = this.innerList.dom;
12496         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12497         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12498         //this.list.beginUpdate();
12499         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12500         this.list.alignTo(this.inputEl(), this.listAlign);
12501         this.list.alignTo(this.inputEl(), this.listAlign);
12502         //this.list.endUpdate();
12503     },
12504
12505     // private
12506     onEmptyResults : function(){
12507         
12508         if(this.tickable && this.editable){
12509             this.restrictHeight();
12510             return;
12511         }
12512         
12513         this.collapse();
12514     },
12515
12516     /**
12517      * Returns true if the dropdown list is expanded, else false.
12518      */
12519     isExpanded : function(){
12520         return this.list.isVisible();
12521     },
12522
12523     /**
12524      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12525      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12526      * @param {String} value The data value of the item to select
12527      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12528      * selected item if it is not currently in view (defaults to true)
12529      * @return {Boolean} True if the value matched an item in the list, else false
12530      */
12531     selectByValue : function(v, scrollIntoView){
12532         if(v !== undefined && v !== null){
12533             var r = this.findRecord(this.valueField || this.displayField, v);
12534             if(r){
12535                 this.select(this.store.indexOf(r), scrollIntoView);
12536                 return true;
12537             }
12538         }
12539         return false;
12540     },
12541
12542     /**
12543      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12544      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12545      * @param {Number} index The zero-based index of the list item to select
12546      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12547      * selected item if it is not currently in view (defaults to true)
12548      */
12549     select : function(index, scrollIntoView){
12550         this.selectedIndex = index;
12551         this.view.select(index);
12552         if(scrollIntoView !== false){
12553             var el = this.view.getNode(index);
12554             /*
12555              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12556              */
12557             if(el){
12558                 this.list.scrollChildIntoView(el, false);
12559             }
12560         }
12561     },
12562
12563     // private
12564     selectNext : function(){
12565         var ct = this.store.getCount();
12566         if(ct > 0){
12567             if(this.selectedIndex == -1){
12568                 this.select(0);
12569             }else if(this.selectedIndex < ct-1){
12570                 this.select(this.selectedIndex+1);
12571             }
12572         }
12573     },
12574
12575     // private
12576     selectPrev : function(){
12577         var ct = this.store.getCount();
12578         if(ct > 0){
12579             if(this.selectedIndex == -1){
12580                 this.select(0);
12581             }else if(this.selectedIndex != 0){
12582                 this.select(this.selectedIndex-1);
12583             }
12584         }
12585     },
12586
12587     // private
12588     onKeyUp : function(e){
12589         if(this.editable !== false && !e.isSpecialKey()){
12590             this.lastKey = e.getKey();
12591             this.dqTask.delay(this.queryDelay);
12592         }
12593     },
12594
12595     // private
12596     validateBlur : function(){
12597         return !this.list || !this.list.isVisible();   
12598     },
12599
12600     // private
12601     initQuery : function(){
12602         
12603         var v = this.getRawValue();
12604         
12605         if(this.tickable && this.editable){
12606             v = this.tickableInputEl().getValue();
12607         }
12608         
12609         this.doQuery(v);
12610     },
12611
12612     // private
12613     doForce : function(){
12614         if(this.inputEl().dom.value.length > 0){
12615             this.inputEl().dom.value =
12616                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12617              
12618         }
12619     },
12620
12621     /**
12622      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12623      * query allowing the query action to be canceled if needed.
12624      * @param {String} query The SQL query to execute
12625      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12626      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12627      * saved in the current store (defaults to false)
12628      */
12629     doQuery : function(q, forceAll){
12630         
12631         if(q === undefined || q === null){
12632             q = '';
12633         }
12634         var qe = {
12635             query: q,
12636             forceAll: forceAll,
12637             combo: this,
12638             cancel:false
12639         };
12640         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12641             return false;
12642         }
12643         q = qe.query;
12644         
12645         forceAll = qe.forceAll;
12646         if(forceAll === true || (q.length >= this.minChars)){
12647             
12648             this.hasQuery = true;
12649             
12650             if(this.lastQuery != q || this.alwaysQuery){
12651                 this.lastQuery = q;
12652                 if(this.mode == 'local'){
12653                     this.selectedIndex = -1;
12654                     if(forceAll){
12655                         this.store.clearFilter();
12656                     }else{
12657                         
12658                         if(this.specialFilter){
12659                             this.fireEvent('specialfilter', this);
12660                             this.onLoad();
12661                             return;
12662                         }
12663                         
12664                         this.store.filter(this.displayField, q);
12665                     }
12666                     
12667                     this.store.fireEvent("datachanged", this.store);
12668                     
12669                     this.onLoad();
12670                     
12671                     
12672                 }else{
12673                     
12674                     this.store.baseParams[this.queryParam] = q;
12675                     
12676                     var options = {params : this.getParams(q)};
12677                     
12678                     if(this.loadNext){
12679                         options.add = true;
12680                         options.params.start = this.page * this.pageSize;
12681                     }
12682                     
12683                     this.store.load(options);
12684                     
12685                     /*
12686                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12687                      *  we should expand the list on onLoad
12688                      *  so command out it
12689                      */
12690 //                    this.expand();
12691                 }
12692             }else{
12693                 this.selectedIndex = -1;
12694                 this.onLoad();   
12695             }
12696         }
12697         
12698         this.loadNext = false;
12699     },
12700     
12701     // private
12702     getParams : function(q){
12703         var p = {};
12704         //p[this.queryParam] = q;
12705         
12706         if(this.pageSize){
12707             p.start = 0;
12708             p.limit = this.pageSize;
12709         }
12710         return p;
12711     },
12712
12713     /**
12714      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12715      */
12716     collapse : function(){
12717         if(!this.isExpanded()){
12718             return;
12719         }
12720         
12721         this.list.hide();
12722         
12723         if(this.tickable){
12724             this.hasFocus = false;
12725             this.okBtn.hide();
12726             this.cancelBtn.hide();
12727             this.trigger.show();
12728             
12729             if(this.editable){
12730                 this.tickableInputEl().dom.value = '';
12731                 this.tickableInputEl().blur();
12732             }
12733             
12734         }
12735         
12736         Roo.get(document).un('mousedown', this.collapseIf, this);
12737         Roo.get(document).un('mousewheel', this.collapseIf, this);
12738         if (!this.editable) {
12739             Roo.get(document).un('keydown', this.listKeyPress, this);
12740         }
12741         this.fireEvent('collapse', this);
12742     },
12743
12744     // private
12745     collapseIf : function(e){
12746         var in_combo  = e.within(this.el);
12747         var in_list =  e.within(this.list);
12748         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12749         
12750         if (in_combo || in_list || is_list) {
12751             //e.stopPropagation();
12752             return;
12753         }
12754         
12755         if(this.tickable){
12756             this.onTickableFooterButtonClick(e, false, false);
12757         }
12758
12759         this.collapse();
12760         
12761     },
12762
12763     /**
12764      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12765      */
12766     expand : function(){
12767        
12768         if(this.isExpanded() || !this.hasFocus){
12769             return;
12770         }
12771         
12772         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12773         this.list.setWidth(lw);
12774         
12775         
12776          Roo.log('expand');
12777         
12778         this.list.show();
12779         
12780         this.restrictHeight();
12781         
12782         if(this.tickable){
12783             
12784             this.tickItems = Roo.apply([], this.item);
12785             
12786             this.okBtn.show();
12787             this.cancelBtn.show();
12788             this.trigger.hide();
12789             
12790             if(this.editable){
12791                 this.tickableInputEl().focus();
12792             }
12793             
12794         }
12795         
12796         Roo.get(document).on('mousedown', this.collapseIf, this);
12797         Roo.get(document).on('mousewheel', this.collapseIf, this);
12798         if (!this.editable) {
12799             Roo.get(document).on('keydown', this.listKeyPress, this);
12800         }
12801         
12802         this.fireEvent('expand', this);
12803     },
12804
12805     // private
12806     // Implements the default empty TriggerField.onTriggerClick function
12807     onTriggerClick : function(e)
12808     {
12809         Roo.log('trigger click');
12810         
12811         if(this.disabled || !this.triggerList){
12812             return;
12813         }
12814         
12815         this.page = 0;
12816         this.loadNext = false;
12817         
12818         if(this.isExpanded()){
12819             this.collapse();
12820             if (!this.blockFocus) {
12821                 this.inputEl().focus();
12822             }
12823             
12824         }else {
12825             this.hasFocus = true;
12826             if(this.triggerAction == 'all') {
12827                 this.doQuery(this.allQuery, true);
12828             } else {
12829                 this.doQuery(this.getRawValue());
12830             }
12831             if (!this.blockFocus) {
12832                 this.inputEl().focus();
12833             }
12834         }
12835     },
12836     
12837     onTickableTriggerClick : function(e)
12838     {
12839         if(this.disabled){
12840             return;
12841         }
12842         
12843         this.page = 0;
12844         this.loadNext = false;
12845         this.hasFocus = true;
12846         
12847         if(this.triggerAction == 'all') {
12848             this.doQuery(this.allQuery, true);
12849         } else {
12850             this.doQuery(this.getRawValue());
12851         }
12852     },
12853     
12854     onSearchFieldClick : function(e)
12855     {
12856         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12857             this.onTickableFooterButtonClick(e, false, false);
12858             return;
12859         }
12860         
12861         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12862             return;
12863         }
12864         
12865         this.page = 0;
12866         this.loadNext = false;
12867         this.hasFocus = true;
12868         
12869         if(this.triggerAction == 'all') {
12870             this.doQuery(this.allQuery, true);
12871         } else {
12872             this.doQuery(this.getRawValue());
12873         }
12874     },
12875     
12876     listKeyPress : function(e)
12877     {
12878         //Roo.log('listkeypress');
12879         // scroll to first matching element based on key pres..
12880         if (e.isSpecialKey()) {
12881             return false;
12882         }
12883         var k = String.fromCharCode(e.getKey()).toUpperCase();
12884         //Roo.log(k);
12885         var match  = false;
12886         var csel = this.view.getSelectedNodes();
12887         var cselitem = false;
12888         if (csel.length) {
12889             var ix = this.view.indexOf(csel[0]);
12890             cselitem  = this.store.getAt(ix);
12891             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12892                 cselitem = false;
12893             }
12894             
12895         }
12896         
12897         this.store.each(function(v) { 
12898             if (cselitem) {
12899                 // start at existing selection.
12900                 if (cselitem.id == v.id) {
12901                     cselitem = false;
12902                 }
12903                 return true;
12904             }
12905                 
12906             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12907                 match = this.store.indexOf(v);
12908                 return false;
12909             }
12910             return true;
12911         }, this);
12912         
12913         if (match === false) {
12914             return true; // no more action?
12915         }
12916         // scroll to?
12917         this.view.select(match);
12918         var sn = Roo.get(this.view.getSelectedNodes()[0])
12919         sn.scrollIntoView(sn.dom.parentNode, false);
12920     },
12921     
12922     onViewScroll : function(e, t){
12923         
12924         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){
12925             return;
12926         }
12927         
12928         this.hasQuery = true;
12929         
12930         this.loading = this.list.select('.loading', true).first();
12931         
12932         if(this.loading === null){
12933             this.list.createChild({
12934                 tag: 'div',
12935                 cls: 'loading select2-more-results select2-active',
12936                 html: 'Loading more results...'
12937             })
12938             
12939             this.loading = this.list.select('.loading', true).first();
12940             
12941             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12942             
12943             this.loading.hide();
12944         }
12945         
12946         this.loading.show();
12947         
12948         var _combo = this;
12949         
12950         this.page++;
12951         this.loadNext = true;
12952         
12953         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12954         
12955         return;
12956     },
12957     
12958     addItem : function(o)
12959     {   
12960         var dv = ''; // display value
12961         
12962         if (this.displayField) {
12963             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12964         } else {
12965             // this is an error condition!!!
12966             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12967         }
12968         
12969         if(!dv.length){
12970             return;
12971         }
12972         
12973         var choice = this.choices.createChild({
12974             tag: 'li',
12975             cls: 'select2-search-choice',
12976             cn: [
12977                 {
12978                     tag: 'div',
12979                     html: dv
12980                 },
12981                 {
12982                     tag: 'a',
12983                     href: '#',
12984                     cls: 'select2-search-choice-close',
12985                     tabindex: '-1'
12986                 }
12987             ]
12988             
12989         }, this.searchField);
12990         
12991         var close = choice.select('a.select2-search-choice-close', true).first()
12992         
12993         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12994         
12995         this.item.push(o);
12996         
12997         this.lastData = o;
12998         
12999         this.syncValue();
13000         
13001         this.inputEl().dom.value = '';
13002         
13003         this.validate();
13004     },
13005     
13006     onRemoveItem : function(e, _self, o)
13007     {
13008         e.preventDefault();
13009         
13010         this.lastItem = Roo.apply([], this.item);
13011         
13012         var index = this.item.indexOf(o.data) * 1;
13013         
13014         if( index < 0){
13015             Roo.log('not this item?!');
13016             return;
13017         }
13018         
13019         this.item.splice(index, 1);
13020         o.item.remove();
13021         
13022         this.syncValue();
13023         
13024         this.fireEvent('remove', this, e);
13025         
13026         this.validate();
13027         
13028     },
13029     
13030     syncValue : function()
13031     {
13032         if(!this.item.length){
13033             this.clearValue();
13034             return;
13035         }
13036             
13037         var value = [];
13038         var _this = this;
13039         Roo.each(this.item, function(i){
13040             if(_this.valueField){
13041                 value.push(i[_this.valueField]);
13042                 return;
13043             }
13044
13045             value.push(i);
13046         });
13047
13048         this.value = value.join(',');
13049
13050         if(this.hiddenField){
13051             this.hiddenField.dom.value = this.value;
13052         }
13053         
13054         this.store.fireEvent("datachanged", this.store);
13055     },
13056     
13057     clearItem : function()
13058     {
13059         if(!this.multiple){
13060             return;
13061         }
13062         
13063         this.item = [];
13064         
13065         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13066            c.remove();
13067         });
13068         
13069         this.syncValue();
13070         
13071         this.validate();
13072         
13073         if(this.tickable && !Roo.isTouch){
13074             this.view.refresh();
13075         }
13076     },
13077     
13078     inputEl: function ()
13079     {
13080         if(Roo.isTouch && this.mobileTouchView){
13081             return this.el.select('input.form-control',true).first();
13082         }
13083         
13084         if(this.tickable){
13085             return this.searchField;
13086         }
13087         
13088         return this.el.select('input.form-control',true).first();
13089     },
13090     
13091     
13092     onTickableFooterButtonClick : function(e, btn, el)
13093     {
13094         e.preventDefault();
13095         
13096         this.lastItem = Roo.apply([], this.item);
13097         
13098         if(btn && btn.name == 'cancel'){
13099             this.tickItems = Roo.apply([], this.item);
13100             this.collapse();
13101             return;
13102         }
13103         
13104         this.clearItem();
13105         
13106         var _this = this;
13107         
13108         Roo.each(this.tickItems, function(o){
13109             _this.addItem(o);
13110         });
13111         
13112         this.collapse();
13113         
13114     },
13115     
13116     validate : function()
13117     {
13118         var v = this.getRawValue();
13119         
13120         if(this.multiple){
13121             v = this.getValue();
13122         }
13123         
13124         if(this.disabled || this.allowBlank || v.length){
13125             this.markValid();
13126             return true;
13127         }
13128         
13129         this.markInvalid();
13130         return false;
13131     },
13132     
13133     tickableInputEl : function()
13134     {
13135         if(!this.tickable || !this.editable){
13136             return this.inputEl();
13137         }
13138         
13139         return this.inputEl().select('.select2-search-field-input', true).first();
13140     },
13141     
13142     
13143     getAutoCreateTouchView : function()
13144     {
13145         var id = Roo.id();
13146         
13147         var cfg = {
13148             cls: 'form-group' //input-group
13149         };
13150         
13151         var input =  {
13152             tag: 'input',
13153             id : id,
13154             type : this.inputType,
13155             cls : 'form-control x-combo-noedit',
13156             autocomplete: 'new-password',
13157             placeholder : this.placeholder || '',
13158             readonly : true
13159         };
13160         
13161         if (this.name) {
13162             input.name = this.name;
13163         }
13164         
13165         if (this.size) {
13166             input.cls += ' input-' + this.size;
13167         }
13168         
13169         if (this.disabled) {
13170             input.disabled = true;
13171         }
13172         
13173         var inputblock = {
13174             cls : '',
13175             cn : [
13176                 input
13177             ]
13178         };
13179         
13180         if(this.before){
13181             inputblock.cls += ' input-group';
13182             
13183             inputblock.cn.unshift({
13184                 tag :'span',
13185                 cls : 'input-group-addon',
13186                 html : this.before
13187             });
13188         }
13189         
13190         if(this.removable && !this.multiple){
13191             inputblock.cls += ' roo-removable';
13192             
13193             inputblock.cn.push({
13194                 tag: 'button',
13195                 html : 'x',
13196                 cls : 'roo-combo-removable-btn close'
13197             });
13198         }
13199
13200         if(this.hasFeedback && !this.allowBlank){
13201             
13202             inputblock.cls += ' has-feedback';
13203             
13204             inputblock.cn.push({
13205                 tag: 'span',
13206                 cls: 'glyphicon form-control-feedback'
13207             });
13208             
13209         }
13210         
13211         if (this.after) {
13212             
13213             inputblock.cls += (this.before) ? '' : ' input-group';
13214             
13215             inputblock.cn.push({
13216                 tag :'span',
13217                 cls : 'input-group-addon',
13218                 html : this.after
13219             });
13220         }
13221
13222         var box = {
13223             tag: 'div',
13224             cn: [
13225                 {
13226                     tag: 'input',
13227                     type : 'hidden',
13228                     cls: 'form-hidden-field'
13229                 },
13230                 inputblock
13231             ]
13232             
13233         };
13234         
13235         if(this.multiple){
13236             box = {
13237                 tag: 'div',
13238                 cn: [
13239                     {
13240                         tag: 'input',
13241                         type : 'hidden',
13242                         cls: 'form-hidden-field'
13243                     },
13244                     {
13245                         tag: 'ul',
13246                         cls: 'select2-choices',
13247                         cn:[
13248                             {
13249                                 tag: 'li',
13250                                 cls: 'select2-search-field',
13251                                 cn: [
13252
13253                                     inputblock
13254                                 ]
13255                             }
13256                         ]
13257                     }
13258                 ]
13259             }
13260         };
13261         
13262         var combobox = {
13263             cls: 'select2-container input-group',
13264             cn: [
13265                 box
13266             ]
13267         };
13268         
13269         if(this.multiple){
13270             combobox.cls += ' select2-container-multi';
13271         }
13272         
13273         var align = this.labelAlign || this.parentLabelAlign();
13274         
13275         cfg.cn = combobox;
13276         
13277         if(this.fieldLabel.length){
13278             
13279             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13280             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13281             
13282             cfg.cn = [
13283                 {
13284                     tag: 'label',
13285                     cls : 'control-label ' + lw,
13286                     html : this.fieldLabel
13287
13288                 },
13289                 {
13290                     cls : cw, 
13291                     cn: [
13292                         combobox
13293                     ]
13294                 }
13295             ];
13296         }
13297         
13298         var settings = this;
13299         
13300         ['xs','sm','md','lg'].map(function(size){
13301             if (settings[size]) {
13302                 cfg.cls += ' col-' + size + '-' + settings[size];
13303             }
13304         });
13305         
13306         return cfg;
13307     },
13308     
13309     initTouchView : function()
13310     {
13311         this.renderTouchView();
13312         
13313         this.touchViewEl.on('scroll', function(){
13314             this.el.dom.scrollTop = 0;
13315         }, this);
13316         
13317         this.inputEl().on("click", this.showTouchView, this);
13318         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13319         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13320         
13321         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13322         
13323         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13324         this.store.on('load', this.onTouchViewLoad, this);
13325         this.store.on('loadexception', this.onTouchViewLoadException, this);
13326         
13327         if(this.hiddenName){
13328             
13329             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13330             
13331             this.hiddenField.dom.value =
13332                 this.hiddenValue !== undefined ? this.hiddenValue :
13333                 this.value !== undefined ? this.value : '';
13334         
13335             this.el.dom.removeAttribute('name');
13336             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13337         }
13338         
13339         if(this.multiple){
13340             this.choices = this.el.select('ul.select2-choices', true).first();
13341             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13342         }
13343         
13344         if(this.removable && !this.multiple){
13345             var close = this.closeTriggerEl();
13346             if(close){
13347                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13348                 close.on('click', this.removeBtnClick, this, close);
13349             }
13350         }
13351         
13352         return;
13353         
13354         
13355     },
13356     
13357     renderTouchView : function()
13358     {
13359         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13360         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13361         
13362         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13363         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13364         
13365         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13366         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13367         this.touchViewBodyEl.setStyle('overflow', 'auto');
13368         
13369         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13370         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13371         
13372         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13373         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13374         
13375     },
13376     
13377     showTouchView : function()
13378     {
13379         this.touchViewHeaderEl.hide();
13380
13381         if(this.fieldLabel.length){
13382             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13383             this.touchViewHeaderEl.show();
13384         }
13385
13386         this.touchViewEl.show();
13387
13388         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13389         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13390
13391         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13392
13393         if(this.fieldLabel.length){
13394             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13395         }
13396         
13397         this.touchViewBodyEl.setHeight(bodyHeight);
13398
13399         if(this.animate){
13400             var _this = this;
13401             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13402         }else{
13403             this.touchViewEl.addClass('in');
13404         }
13405
13406         this.doTouchViewQuery();
13407         
13408     },
13409     
13410     hideTouchView : function()
13411     {
13412         this.touchViewEl.removeClass('in');
13413
13414         if(this.animate){
13415             var _this = this;
13416             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13417         }else{
13418             this.touchViewEl.setStyle('display', 'none');
13419         }
13420         
13421     },
13422     
13423     setTouchViewValue : function()
13424     {
13425         if(this.multiple){
13426             this.clearItem();
13427         
13428             var _this = this;
13429
13430             Roo.each(this.tickItems, function(o){
13431                 this.addItem(o);
13432             }, this);
13433         }
13434         
13435         this.hideTouchView();
13436     },
13437     
13438     doTouchViewQuery : function()
13439     {
13440         var qe = {
13441             query: '',
13442             forceAll: true,
13443             combo: this,
13444             cancel:false
13445         };
13446         
13447         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13448             return false;
13449         }
13450         
13451         if(!this.alwaysQuery || this.mode == 'local'){
13452             this.onTouchViewLoad();
13453             return;
13454         }
13455         
13456         this.store.load();
13457     },
13458     
13459     onTouchViewBeforeLoad : function(combo,opts)
13460     {
13461         return;
13462     },
13463
13464     // private
13465     onTouchViewLoad : function()
13466     {
13467         if(this.store.getCount() < 1){
13468             this.onTouchViewEmptyResults();
13469             return;
13470         }
13471         
13472         this.clearTouchView();
13473         
13474         var rawValue = this.getRawValue();
13475         
13476         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13477         
13478         this.tickItems = [];
13479         
13480         this.store.data.each(function(d, rowIndex){
13481             var row = this.touchViewListGroup.createChild(template);
13482             
13483             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13484                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13485             }
13486             
13487             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13488                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13489             }
13490             
13491             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13492                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13493                 this.tickItems.push(d.data);
13494             }
13495             
13496             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13497             
13498         }, this);
13499         
13500         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13501         
13502         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13503
13504         if(this.fieldLabel.length){
13505             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13506         }
13507
13508         var listHeight = this.touchViewListGroup.getHeight();
13509         
13510         var _this = this;
13511         
13512         if(firstChecked && listHeight > bodyHeight){
13513             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13514         }
13515         
13516     },
13517     
13518     onTouchViewLoadException : function()
13519     {
13520         this.hideTouchView();
13521     },
13522     
13523     onTouchViewEmptyResults : function()
13524     {
13525         this.clearTouchView();
13526         
13527         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13528         
13529         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13530         
13531     },
13532     
13533     clearTouchView : function()
13534     {
13535         this.touchViewListGroup.dom.innerHTML = '';
13536     },
13537     
13538     onTouchViewClick : function(e, el, o)
13539     {
13540         e.preventDefault();
13541         
13542         var row = o.row;
13543         var rowIndex = o.rowIndex;
13544         
13545         var r = this.store.getAt(rowIndex);
13546         
13547         if(!this.multiple){
13548             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13549                 c.dom.removeAttribute('checked');
13550             }, this);
13551             
13552             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13553         
13554             this.setFromData(r.data);
13555             
13556             var close = this.closeTriggerEl();
13557         
13558             if(close){
13559                 close.show();
13560             }
13561
13562             this.hideTouchView();
13563             
13564             this.fireEvent('select', this, r, rowIndex);
13565             
13566             return;
13567         }
13568         
13569         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13570             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13571             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13572             return;
13573         }
13574         
13575         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13576         this.addItem(r.data);
13577         this.tickItems.push(r.data);
13578         
13579     }
13580     
13581
13582     /** 
13583     * @cfg {Boolean} grow 
13584     * @hide 
13585     */
13586     /** 
13587     * @cfg {Number} growMin 
13588     * @hide 
13589     */
13590     /** 
13591     * @cfg {Number} growMax 
13592     * @hide 
13593     */
13594     /**
13595      * @hide
13596      * @method autoSize
13597      */
13598 });
13599
13600 Roo.apply(Roo.bootstrap.ComboBox,  {
13601     
13602     header : {
13603         tag: 'div',
13604         cls: 'modal-header',
13605         cn: [
13606             {
13607                 tag: 'h4',
13608                 cls: 'modal-title'
13609             }
13610         ]
13611     },
13612     
13613     body : {
13614         tag: 'div',
13615         cls: 'modal-body',
13616         cn: [
13617             {
13618                 tag: 'ul',
13619                 cls: 'list-group'
13620             }
13621         ]
13622     },
13623     
13624     listItemRadio : {
13625         tag: 'li',
13626         cls: 'list-group-item',
13627         cn: [
13628             {
13629                 tag: 'span',
13630                 cls: 'roo-combobox-list-group-item-value'
13631             },
13632             {
13633                 tag: 'div',
13634                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13635                 cn: [
13636                     {
13637                         tag: 'input',
13638                         type: 'radio'
13639                     },
13640                     {
13641                         tag: 'label'
13642                     }
13643                 ]
13644             }
13645         ]
13646     },
13647     
13648     listItemCheckbox : {
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 checkbox-inline checkbox checkbox-info',
13659                 cn: [
13660                     {
13661                         tag: 'input',
13662                         type: 'checkbox'
13663                     },
13664                     {
13665                         tag: 'label'
13666                     }
13667                 ]
13668             }
13669         ]
13670     },
13671     
13672     emptyResult : {
13673         tag: 'div',
13674         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13675     },
13676     
13677     footer : {
13678         tag: 'div',
13679         cls: 'modal-footer',
13680         cn: [
13681             {
13682                 tag: 'div',
13683                 cls: 'row',
13684                 cn: [
13685                     {
13686                         tag: 'div',
13687                         cls: 'col-xs-6 text-left',
13688                         cn: {
13689                             tag: 'button',
13690                             cls: 'btn btn-danger roo-touch-view-cancel',
13691                             html: 'Cancel'
13692                         }
13693                     },
13694                     {
13695                         tag: 'div',
13696                         cls: 'col-xs-6 text-right',
13697                         cn: {
13698                             tag: 'button',
13699                             cls: 'btn btn-success roo-touch-view-ok',
13700                             html: 'OK'
13701                         }
13702                     }
13703                 ]
13704             }
13705         ]
13706         
13707     }
13708 });
13709
13710 Roo.apply(Roo.bootstrap.ComboBox,  {
13711     
13712     touchViewTemplate : {
13713         tag: 'div',
13714         cls: 'modal fade roo-combobox-touch-view',
13715         cn: [
13716             {
13717                 tag: 'div',
13718                 cls: 'modal-dialog',
13719                 cn: [
13720                     {
13721                         tag: 'div',
13722                         cls: 'modal-content',
13723                         cn: [
13724                             Roo.bootstrap.ComboBox.header,
13725                             Roo.bootstrap.ComboBox.body,
13726                             Roo.bootstrap.ComboBox.footer
13727                         ]
13728                     }
13729                 ]
13730             }
13731         ]
13732     }
13733 });/*
13734  * Based on:
13735  * Ext JS Library 1.1.1
13736  * Copyright(c) 2006-2007, Ext JS, LLC.
13737  *
13738  * Originally Released Under LGPL - original licence link has changed is not relivant.
13739  *
13740  * Fork - LGPL
13741  * <script type="text/javascript">
13742  */
13743
13744 /**
13745  * @class Roo.View
13746  * @extends Roo.util.Observable
13747  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13748  * This class also supports single and multi selection modes. <br>
13749  * Create a data model bound view:
13750  <pre><code>
13751  var store = new Roo.data.Store(...);
13752
13753  var view = new Roo.View({
13754     el : "my-element",
13755     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13756  
13757     singleSelect: true,
13758     selectedClass: "ydataview-selected",
13759     store: store
13760  });
13761
13762  // listen for node click?
13763  view.on("click", function(vw, index, node, e){
13764  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13765  });
13766
13767  // load XML data
13768  dataModel.load("foobar.xml");
13769  </code></pre>
13770  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13771  * <br><br>
13772  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13773  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13774  * 
13775  * Note: old style constructor is still suported (container, template, config)
13776  * 
13777  * @constructor
13778  * Create a new View
13779  * @param {Object} config The config object
13780  * 
13781  */
13782 Roo.View = function(config, depreciated_tpl, depreciated_config){
13783     
13784     this.parent = false;
13785     
13786     if (typeof(depreciated_tpl) == 'undefined') {
13787         // new way.. - universal constructor.
13788         Roo.apply(this, config);
13789         this.el  = Roo.get(this.el);
13790     } else {
13791         // old format..
13792         this.el  = Roo.get(config);
13793         this.tpl = depreciated_tpl;
13794         Roo.apply(this, depreciated_config);
13795     }
13796     this.wrapEl  = this.el.wrap().wrap();
13797     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13798     
13799     
13800     if(typeof(this.tpl) == "string"){
13801         this.tpl = new Roo.Template(this.tpl);
13802     } else {
13803         // support xtype ctors..
13804         this.tpl = new Roo.factory(this.tpl, Roo);
13805     }
13806     
13807     
13808     this.tpl.compile();
13809     
13810     /** @private */
13811     this.addEvents({
13812         /**
13813          * @event beforeclick
13814          * Fires before a click is processed. Returns false to cancel the default action.
13815          * @param {Roo.View} this
13816          * @param {Number} index The index of the target node
13817          * @param {HTMLElement} node The target node
13818          * @param {Roo.EventObject} e The raw event object
13819          */
13820             "beforeclick" : true,
13821         /**
13822          * @event click
13823          * Fires when a template node is clicked.
13824          * @param {Roo.View} this
13825          * @param {Number} index The index of the target node
13826          * @param {HTMLElement} node The target node
13827          * @param {Roo.EventObject} e The raw event object
13828          */
13829             "click" : true,
13830         /**
13831          * @event dblclick
13832          * Fires when a template node is double clicked.
13833          * @param {Roo.View} this
13834          * @param {Number} index The index of the target node
13835          * @param {HTMLElement} node The target node
13836          * @param {Roo.EventObject} e The raw event object
13837          */
13838             "dblclick" : true,
13839         /**
13840          * @event contextmenu
13841          * Fires when a template node is right clicked.
13842          * @param {Roo.View} this
13843          * @param {Number} index The index of the target node
13844          * @param {HTMLElement} node The target node
13845          * @param {Roo.EventObject} e The raw event object
13846          */
13847             "contextmenu" : true,
13848         /**
13849          * @event selectionchange
13850          * Fires when the selected nodes change.
13851          * @param {Roo.View} this
13852          * @param {Array} selections Array of the selected nodes
13853          */
13854             "selectionchange" : true,
13855     
13856         /**
13857          * @event beforeselect
13858          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13859          * @param {Roo.View} this
13860          * @param {HTMLElement} node The node to be selected
13861          * @param {Array} selections Array of currently selected nodes
13862          */
13863             "beforeselect" : true,
13864         /**
13865          * @event preparedata
13866          * Fires on every row to render, to allow you to change the data.
13867          * @param {Roo.View} this
13868          * @param {Object} data to be rendered (change this)
13869          */
13870           "preparedata" : true
13871           
13872           
13873         });
13874
13875
13876
13877     this.el.on({
13878         "click": this.onClick,
13879         "dblclick": this.onDblClick,
13880         "contextmenu": this.onContextMenu,
13881         scope:this
13882     });
13883
13884     this.selections = [];
13885     this.nodes = [];
13886     this.cmp = new Roo.CompositeElementLite([]);
13887     if(this.store){
13888         this.store = Roo.factory(this.store, Roo.data);
13889         this.setStore(this.store, true);
13890     }
13891     
13892     if ( this.footer && this.footer.xtype) {
13893            
13894          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13895         
13896         this.footer.dataSource = this.store;
13897         this.footer.container = fctr;
13898         this.footer = Roo.factory(this.footer, Roo);
13899         fctr.insertFirst(this.el);
13900         
13901         // this is a bit insane - as the paging toolbar seems to detach the el..
13902 //        dom.parentNode.parentNode.parentNode
13903          // they get detached?
13904     }
13905     
13906     
13907     Roo.View.superclass.constructor.call(this);
13908     
13909     
13910 };
13911
13912 Roo.extend(Roo.View, Roo.util.Observable, {
13913     
13914      /**
13915      * @cfg {Roo.data.Store} store Data store to load data from.
13916      */
13917     store : false,
13918     
13919     /**
13920      * @cfg {String|Roo.Element} el The container element.
13921      */
13922     el : '',
13923     
13924     /**
13925      * @cfg {String|Roo.Template} tpl The template used by this View 
13926      */
13927     tpl : false,
13928     /**
13929      * @cfg {String} dataName the named area of the template to use as the data area
13930      *                          Works with domtemplates roo-name="name"
13931      */
13932     dataName: false,
13933     /**
13934      * @cfg {String} selectedClass The css class to add to selected nodes
13935      */
13936     selectedClass : "x-view-selected",
13937      /**
13938      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13939      */
13940     emptyText : "",
13941     
13942     /**
13943      * @cfg {String} text to display on mask (default Loading)
13944      */
13945     mask : false,
13946     /**
13947      * @cfg {Boolean} multiSelect Allow multiple selection
13948      */
13949     multiSelect : false,
13950     /**
13951      * @cfg {Boolean} singleSelect Allow single selection
13952      */
13953     singleSelect:  false,
13954     
13955     /**
13956      * @cfg {Boolean} toggleSelect - selecting 
13957      */
13958     toggleSelect : false,
13959     
13960     /**
13961      * @cfg {Boolean} tickable - selecting 
13962      */
13963     tickable : false,
13964     
13965     /**
13966      * Returns the element this view is bound to.
13967      * @return {Roo.Element}
13968      */
13969     getEl : function(){
13970         return this.wrapEl;
13971     },
13972     
13973     
13974
13975     /**
13976      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13977      */
13978     refresh : function(){
13979         //Roo.log('refresh');
13980         var t = this.tpl;
13981         
13982         // if we are using something like 'domtemplate', then
13983         // the what gets used is:
13984         // t.applySubtemplate(NAME, data, wrapping data..)
13985         // the outer template then get' applied with
13986         //     the store 'extra data'
13987         // and the body get's added to the
13988         //      roo-name="data" node?
13989         //      <span class='roo-tpl-{name}'></span> ?????
13990         
13991         
13992         
13993         this.clearSelections();
13994         this.el.update("");
13995         var html = [];
13996         var records = this.store.getRange();
13997         if(records.length < 1) {
13998             
13999             // is this valid??  = should it render a template??
14000             
14001             this.el.update(this.emptyText);
14002             return;
14003         }
14004         var el = this.el;
14005         if (this.dataName) {
14006             this.el.update(t.apply(this.store.meta)); //????
14007             el = this.el.child('.roo-tpl-' + this.dataName);
14008         }
14009         
14010         for(var i = 0, len = records.length; i < len; i++){
14011             var data = this.prepareData(records[i].data, i, records[i]);
14012             this.fireEvent("preparedata", this, data, i, records[i]);
14013             
14014             var d = Roo.apply({}, data);
14015             
14016             if(this.tickable){
14017                 Roo.apply(d, {'roo-id' : Roo.id()});
14018                 
14019                 var _this = this;
14020             
14021                 Roo.each(this.parent.item, function(item){
14022                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14023                         return;
14024                     }
14025                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14026                 });
14027             }
14028             
14029             html[html.length] = Roo.util.Format.trim(
14030                 this.dataName ?
14031                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14032                     t.apply(d)
14033             );
14034         }
14035         
14036         
14037         
14038         el.update(html.join(""));
14039         this.nodes = el.dom.childNodes;
14040         this.updateIndexes(0);
14041     },
14042     
14043
14044     /**
14045      * Function to override to reformat the data that is sent to
14046      * the template for each node.
14047      * DEPRICATED - use the preparedata event handler.
14048      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14049      * a JSON object for an UpdateManager bound view).
14050      */
14051     prepareData : function(data, index, record)
14052     {
14053         this.fireEvent("preparedata", this, data, index, record);
14054         return data;
14055     },
14056
14057     onUpdate : function(ds, record){
14058         // Roo.log('on update');   
14059         this.clearSelections();
14060         var index = this.store.indexOf(record);
14061         var n = this.nodes[index];
14062         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14063         n.parentNode.removeChild(n);
14064         this.updateIndexes(index, index);
14065     },
14066
14067     
14068     
14069 // --------- FIXME     
14070     onAdd : function(ds, records, index)
14071     {
14072         //Roo.log(['on Add', ds, records, index] );        
14073         this.clearSelections();
14074         if(this.nodes.length == 0){
14075             this.refresh();
14076             return;
14077         }
14078         var n = this.nodes[index];
14079         for(var i = 0, len = records.length; i < len; i++){
14080             var d = this.prepareData(records[i].data, i, records[i]);
14081             if(n){
14082                 this.tpl.insertBefore(n, d);
14083             }else{
14084                 
14085                 this.tpl.append(this.el, d);
14086             }
14087         }
14088         this.updateIndexes(index);
14089     },
14090
14091     onRemove : function(ds, record, index){
14092        // Roo.log('onRemove');
14093         this.clearSelections();
14094         var el = this.dataName  ?
14095             this.el.child('.roo-tpl-' + this.dataName) :
14096             this.el; 
14097         
14098         el.dom.removeChild(this.nodes[index]);
14099         this.updateIndexes(index);
14100     },
14101
14102     /**
14103      * Refresh an individual node.
14104      * @param {Number} index
14105      */
14106     refreshNode : function(index){
14107         this.onUpdate(this.store, this.store.getAt(index));
14108     },
14109
14110     updateIndexes : function(startIndex, endIndex){
14111         var ns = this.nodes;
14112         startIndex = startIndex || 0;
14113         endIndex = endIndex || ns.length - 1;
14114         for(var i = startIndex; i <= endIndex; i++){
14115             ns[i].nodeIndex = i;
14116         }
14117     },
14118
14119     /**
14120      * Changes the data store this view uses and refresh the view.
14121      * @param {Store} store
14122      */
14123     setStore : function(store, initial){
14124         if(!initial && this.store){
14125             this.store.un("datachanged", this.refresh);
14126             this.store.un("add", this.onAdd);
14127             this.store.un("remove", this.onRemove);
14128             this.store.un("update", this.onUpdate);
14129             this.store.un("clear", this.refresh);
14130             this.store.un("beforeload", this.onBeforeLoad);
14131             this.store.un("load", this.onLoad);
14132             this.store.un("loadexception", this.onLoad);
14133         }
14134         if(store){
14135           
14136             store.on("datachanged", this.refresh, this);
14137             store.on("add", this.onAdd, this);
14138             store.on("remove", this.onRemove, this);
14139             store.on("update", this.onUpdate, this);
14140             store.on("clear", this.refresh, this);
14141             store.on("beforeload", this.onBeforeLoad, this);
14142             store.on("load", this.onLoad, this);
14143             store.on("loadexception", this.onLoad, this);
14144         }
14145         
14146         if(store){
14147             this.refresh();
14148         }
14149     },
14150     /**
14151      * onbeforeLoad - masks the loading area.
14152      *
14153      */
14154     onBeforeLoad : function(store,opts)
14155     {
14156          //Roo.log('onBeforeLoad');   
14157         if (!opts.add) {
14158             this.el.update("");
14159         }
14160         this.el.mask(this.mask ? this.mask : "Loading" ); 
14161     },
14162     onLoad : function ()
14163     {
14164         this.el.unmask();
14165     },
14166     
14167
14168     /**
14169      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14170      * @param {HTMLElement} node
14171      * @return {HTMLElement} The template node
14172      */
14173     findItemFromChild : function(node){
14174         var el = this.dataName  ?
14175             this.el.child('.roo-tpl-' + this.dataName,true) :
14176             this.el.dom; 
14177         
14178         if(!node || node.parentNode == el){
14179                     return node;
14180             }
14181             var p = node.parentNode;
14182             while(p && p != el){
14183             if(p.parentNode == el){
14184                 return p;
14185             }
14186             p = p.parentNode;
14187         }
14188             return null;
14189     },
14190
14191     /** @ignore */
14192     onClick : function(e){
14193         var item = this.findItemFromChild(e.getTarget());
14194         if(item){
14195             var index = this.indexOf(item);
14196             if(this.onItemClick(item, index, e) !== false){
14197                 this.fireEvent("click", this, index, item, e);
14198             }
14199         }else{
14200             this.clearSelections();
14201         }
14202     },
14203
14204     /** @ignore */
14205     onContextMenu : function(e){
14206         var item = this.findItemFromChild(e.getTarget());
14207         if(item){
14208             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14209         }
14210     },
14211
14212     /** @ignore */
14213     onDblClick : function(e){
14214         var item = this.findItemFromChild(e.getTarget());
14215         if(item){
14216             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14217         }
14218     },
14219
14220     onItemClick : function(item, index, e)
14221     {
14222         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14223             return false;
14224         }
14225         if (this.toggleSelect) {
14226             var m = this.isSelected(item) ? 'unselect' : 'select';
14227             //Roo.log(m);
14228             var _t = this;
14229             _t[m](item, true, false);
14230             return true;
14231         }
14232         if(this.multiSelect || this.singleSelect){
14233             if(this.multiSelect && e.shiftKey && this.lastSelection){
14234                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14235             }else{
14236                 this.select(item, this.multiSelect && e.ctrlKey);
14237                 this.lastSelection = item;
14238             }
14239             
14240             if(!this.tickable){
14241                 e.preventDefault();
14242             }
14243             
14244         }
14245         return true;
14246     },
14247
14248     /**
14249      * Get the number of selected nodes.
14250      * @return {Number}
14251      */
14252     getSelectionCount : function(){
14253         return this.selections.length;
14254     },
14255
14256     /**
14257      * Get the currently selected nodes.
14258      * @return {Array} An array of HTMLElements
14259      */
14260     getSelectedNodes : function(){
14261         return this.selections;
14262     },
14263
14264     /**
14265      * Get the indexes of the selected nodes.
14266      * @return {Array}
14267      */
14268     getSelectedIndexes : function(){
14269         var indexes = [], s = this.selections;
14270         for(var i = 0, len = s.length; i < len; i++){
14271             indexes.push(s[i].nodeIndex);
14272         }
14273         return indexes;
14274     },
14275
14276     /**
14277      * Clear all selections
14278      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14279      */
14280     clearSelections : function(suppressEvent){
14281         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14282             this.cmp.elements = this.selections;
14283             this.cmp.removeClass(this.selectedClass);
14284             this.selections = [];
14285             if(!suppressEvent){
14286                 this.fireEvent("selectionchange", this, this.selections);
14287             }
14288         }
14289     },
14290
14291     /**
14292      * Returns true if the passed node is selected
14293      * @param {HTMLElement/Number} node The node or node index
14294      * @return {Boolean}
14295      */
14296     isSelected : function(node){
14297         var s = this.selections;
14298         if(s.length < 1){
14299             return false;
14300         }
14301         node = this.getNode(node);
14302         return s.indexOf(node) !== -1;
14303     },
14304
14305     /**
14306      * Selects nodes.
14307      * @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
14308      * @param {Boolean} keepExisting (optional) true to keep existing selections
14309      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14310      */
14311     select : function(nodeInfo, keepExisting, suppressEvent){
14312         if(nodeInfo instanceof Array){
14313             if(!keepExisting){
14314                 this.clearSelections(true);
14315             }
14316             for(var i = 0, len = nodeInfo.length; i < len; i++){
14317                 this.select(nodeInfo[i], true, true);
14318             }
14319             return;
14320         } 
14321         var node = this.getNode(nodeInfo);
14322         if(!node || this.isSelected(node)){
14323             return; // already selected.
14324         }
14325         if(!keepExisting){
14326             this.clearSelections(true);
14327         }
14328         
14329         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14330             Roo.fly(node).addClass(this.selectedClass);
14331             this.selections.push(node);
14332             if(!suppressEvent){
14333                 this.fireEvent("selectionchange", this, this.selections);
14334             }
14335         }
14336         
14337         
14338     },
14339       /**
14340      * Unselects nodes.
14341      * @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
14342      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14343      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14344      */
14345     unselect : function(nodeInfo, keepExisting, suppressEvent)
14346     {
14347         if(nodeInfo instanceof Array){
14348             Roo.each(this.selections, function(s) {
14349                 this.unselect(s, nodeInfo);
14350             }, this);
14351             return;
14352         }
14353         var node = this.getNode(nodeInfo);
14354         if(!node || !this.isSelected(node)){
14355             //Roo.log("not selected");
14356             return; // not selected.
14357         }
14358         // fireevent???
14359         var ns = [];
14360         Roo.each(this.selections, function(s) {
14361             if (s == node ) {
14362                 Roo.fly(node).removeClass(this.selectedClass);
14363
14364                 return;
14365             }
14366             ns.push(s);
14367         },this);
14368         
14369         this.selections= ns;
14370         this.fireEvent("selectionchange", this, this.selections);
14371     },
14372
14373     /**
14374      * Gets a template node.
14375      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14376      * @return {HTMLElement} The node or null if it wasn't found
14377      */
14378     getNode : function(nodeInfo){
14379         if(typeof nodeInfo == "string"){
14380             return document.getElementById(nodeInfo);
14381         }else if(typeof nodeInfo == "number"){
14382             return this.nodes[nodeInfo];
14383         }
14384         return nodeInfo;
14385     },
14386
14387     /**
14388      * Gets a range template nodes.
14389      * @param {Number} startIndex
14390      * @param {Number} endIndex
14391      * @return {Array} An array of nodes
14392      */
14393     getNodes : function(start, end){
14394         var ns = this.nodes;
14395         start = start || 0;
14396         end = typeof end == "undefined" ? ns.length - 1 : end;
14397         var nodes = [];
14398         if(start <= end){
14399             for(var i = start; i <= end; i++){
14400                 nodes.push(ns[i]);
14401             }
14402         } else{
14403             for(var i = start; i >= end; i--){
14404                 nodes.push(ns[i]);
14405             }
14406         }
14407         return nodes;
14408     },
14409
14410     /**
14411      * Finds the index of the passed node
14412      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14413      * @return {Number} The index of the node or -1
14414      */
14415     indexOf : function(node){
14416         node = this.getNode(node);
14417         if(typeof node.nodeIndex == "number"){
14418             return node.nodeIndex;
14419         }
14420         var ns = this.nodes;
14421         for(var i = 0, len = ns.length; i < len; i++){
14422             if(ns[i] == node){
14423                 return i;
14424             }
14425         }
14426         return -1;
14427     }
14428 });
14429 /*
14430  * - LGPL
14431  *
14432  * based on jquery fullcalendar
14433  * 
14434  */
14435
14436 Roo.bootstrap = Roo.bootstrap || {};
14437 /**
14438  * @class Roo.bootstrap.Calendar
14439  * @extends Roo.bootstrap.Component
14440  * Bootstrap Calendar class
14441  * @cfg {Boolean} loadMask (true|false) default false
14442  * @cfg {Object} header generate the user specific header of the calendar, default false
14443
14444  * @constructor
14445  * Create a new Container
14446  * @param {Object} config The config object
14447  */
14448
14449
14450
14451 Roo.bootstrap.Calendar = function(config){
14452     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14453      this.addEvents({
14454         /**
14455              * @event select
14456              * Fires when a date is selected
14457              * @param {DatePicker} this
14458              * @param {Date} date The selected date
14459              */
14460         'select': true,
14461         /**
14462              * @event monthchange
14463              * Fires when the displayed month changes 
14464              * @param {DatePicker} this
14465              * @param {Date} date The selected month
14466              */
14467         'monthchange': true,
14468         /**
14469              * @event evententer
14470              * Fires when mouse over an event
14471              * @param {Calendar} this
14472              * @param {event} Event
14473              */
14474         'evententer': true,
14475         /**
14476              * @event eventleave
14477              * Fires when the mouse leaves an
14478              * @param {Calendar} this
14479              * @param {event}
14480              */
14481         'eventleave': true,
14482         /**
14483              * @event eventclick
14484              * Fires when the mouse click an
14485              * @param {Calendar} this
14486              * @param {event}
14487              */
14488         'eventclick': true
14489         
14490     });
14491
14492 };
14493
14494 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14495     
14496      /**
14497      * @cfg {Number} startDay
14498      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14499      */
14500     startDay : 0,
14501     
14502     loadMask : false,
14503     
14504     header : false,
14505       
14506     getAutoCreate : function(){
14507         
14508         
14509         var fc_button = function(name, corner, style, content ) {
14510             return Roo.apply({},{
14511                 tag : 'span',
14512                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14513                          (corner.length ?
14514                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14515                             ''
14516                         ),
14517                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14518                 unselectable: 'on'
14519             });
14520         };
14521         
14522         var header = {};
14523         
14524         if(!this.header){
14525             header = {
14526                 tag : 'table',
14527                 cls : 'fc-header',
14528                 style : 'width:100%',
14529                 cn : [
14530                     {
14531                         tag: 'tr',
14532                         cn : [
14533                             {
14534                                 tag : 'td',
14535                                 cls : 'fc-header-left',
14536                                 cn : [
14537                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14538                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14539                                     { tag: 'span', cls: 'fc-header-space' },
14540                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14541
14542
14543                                 ]
14544                             },
14545
14546                             {
14547                                 tag : 'td',
14548                                 cls : 'fc-header-center',
14549                                 cn : [
14550                                     {
14551                                         tag: 'span',
14552                                         cls: 'fc-header-title',
14553                                         cn : {
14554                                             tag: 'H2',
14555                                             html : 'month / year'
14556                                         }
14557                                     }
14558
14559                                 ]
14560                             },
14561                             {
14562                                 tag : 'td',
14563                                 cls : 'fc-header-right',
14564                                 cn : [
14565                               /*      fc_button('month', 'left', '', 'month' ),
14566                                     fc_button('week', '', '', 'week' ),
14567                                     fc_button('day', 'right', '', 'day' )
14568                                 */    
14569
14570                                 ]
14571                             }
14572
14573                         ]
14574                     }
14575                 ]
14576             };
14577         }
14578         
14579         header = this.header;
14580         
14581        
14582         var cal_heads = function() {
14583             var ret = [];
14584             // fixme - handle this.
14585             
14586             for (var i =0; i < Date.dayNames.length; i++) {
14587                 var d = Date.dayNames[i];
14588                 ret.push({
14589                     tag: 'th',
14590                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14591                     html : d.substring(0,3)
14592                 });
14593                 
14594             }
14595             ret[0].cls += ' fc-first';
14596             ret[6].cls += ' fc-last';
14597             return ret;
14598         };
14599         var cal_cell = function(n) {
14600             return  {
14601                 tag: 'td',
14602                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14603                 cn : [
14604                     {
14605                         cn : [
14606                             {
14607                                 cls: 'fc-day-number',
14608                                 html: 'D'
14609                             },
14610                             {
14611                                 cls: 'fc-day-content',
14612                              
14613                                 cn : [
14614                                      {
14615                                         style: 'position: relative;' // height: 17px;
14616                                     }
14617                                 ]
14618                             }
14619                             
14620                             
14621                         ]
14622                     }
14623                 ]
14624                 
14625             }
14626         };
14627         var cal_rows = function() {
14628             
14629             var ret = [];
14630             for (var r = 0; r < 6; r++) {
14631                 var row= {
14632                     tag : 'tr',
14633                     cls : 'fc-week',
14634                     cn : []
14635                 };
14636                 
14637                 for (var i =0; i < Date.dayNames.length; i++) {
14638                     var d = Date.dayNames[i];
14639                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14640
14641                 }
14642                 row.cn[0].cls+=' fc-first';
14643                 row.cn[0].cn[0].style = 'min-height:90px';
14644                 row.cn[6].cls+=' fc-last';
14645                 ret.push(row);
14646                 
14647             }
14648             ret[0].cls += ' fc-first';
14649             ret[4].cls += ' fc-prev-last';
14650             ret[5].cls += ' fc-last';
14651             return ret;
14652             
14653         };
14654         
14655         var cal_table = {
14656             tag: 'table',
14657             cls: 'fc-border-separate',
14658             style : 'width:100%',
14659             cellspacing  : 0,
14660             cn : [
14661                 { 
14662                     tag: 'thead',
14663                     cn : [
14664                         { 
14665                             tag: 'tr',
14666                             cls : 'fc-first fc-last',
14667                             cn : cal_heads()
14668                         }
14669                     ]
14670                 },
14671                 { 
14672                     tag: 'tbody',
14673                     cn : cal_rows()
14674                 }
14675                   
14676             ]
14677         };
14678          
14679          var cfg = {
14680             cls : 'fc fc-ltr',
14681             cn : [
14682                 header,
14683                 {
14684                     cls : 'fc-content',
14685                     style : "position: relative;",
14686                     cn : [
14687                         {
14688                             cls : 'fc-view fc-view-month fc-grid',
14689                             style : 'position: relative',
14690                             unselectable : 'on',
14691                             cn : [
14692                                 {
14693                                     cls : 'fc-event-container',
14694                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14695                                 },
14696                                 cal_table
14697                             ]
14698                         }
14699                     ]
14700     
14701                 }
14702            ] 
14703             
14704         };
14705         
14706          
14707         
14708         return cfg;
14709     },
14710     
14711     
14712     initEvents : function()
14713     {
14714         if(!this.store){
14715             throw "can not find store for calendar";
14716         }
14717         
14718         var mark = {
14719             tag: "div",
14720             cls:"x-dlg-mask",
14721             style: "text-align:center",
14722             cn: [
14723                 {
14724                     tag: "div",
14725                     style: "background-color:white;width:50%;margin:250 auto",
14726                     cn: [
14727                         {
14728                             tag: "img",
14729                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14730                         },
14731                         {
14732                             tag: "span",
14733                             html: "Loading"
14734                         }
14735                         
14736                     ]
14737                 }
14738             ]
14739         }
14740         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14741         
14742         var size = this.el.select('.fc-content', true).first().getSize();
14743         this.maskEl.setSize(size.width, size.height);
14744         this.maskEl.enableDisplayMode("block");
14745         if(!this.loadMask){
14746             this.maskEl.hide();
14747         }
14748         
14749         this.store = Roo.factory(this.store, Roo.data);
14750         this.store.on('load', this.onLoad, this);
14751         this.store.on('beforeload', this.onBeforeLoad, this);
14752         
14753         this.resize();
14754         
14755         this.cells = this.el.select('.fc-day',true);
14756         //Roo.log(this.cells);
14757         this.textNodes = this.el.query('.fc-day-number');
14758         this.cells.addClassOnOver('fc-state-hover');
14759         
14760         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14761         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14762         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14763         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14764         
14765         this.on('monthchange', this.onMonthChange, this);
14766         
14767         this.update(new Date().clearTime());
14768     },
14769     
14770     resize : function() {
14771         var sz  = this.el.getSize();
14772         
14773         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14774         this.el.select('.fc-day-content div',true).setHeight(34);
14775     },
14776     
14777     
14778     // private
14779     showPrevMonth : function(e){
14780         this.update(this.activeDate.add("mo", -1));
14781     },
14782     showToday : function(e){
14783         this.update(new Date().clearTime());
14784     },
14785     // private
14786     showNextMonth : function(e){
14787         this.update(this.activeDate.add("mo", 1));
14788     },
14789
14790     // private
14791     showPrevYear : function(){
14792         this.update(this.activeDate.add("y", -1));
14793     },
14794
14795     // private
14796     showNextYear : function(){
14797         this.update(this.activeDate.add("y", 1));
14798     },
14799
14800     
14801    // private
14802     update : function(date)
14803     {
14804         var vd = this.activeDate;
14805         this.activeDate = date;
14806 //        if(vd && this.el){
14807 //            var t = date.getTime();
14808 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14809 //                Roo.log('using add remove');
14810 //                
14811 //                this.fireEvent('monthchange', this, date);
14812 //                
14813 //                this.cells.removeClass("fc-state-highlight");
14814 //                this.cells.each(function(c){
14815 //                   if(c.dateValue == t){
14816 //                       c.addClass("fc-state-highlight");
14817 //                       setTimeout(function(){
14818 //                            try{c.dom.firstChild.focus();}catch(e){}
14819 //                       }, 50);
14820 //                       return false;
14821 //                   }
14822 //                   return true;
14823 //                });
14824 //                return;
14825 //            }
14826 //        }
14827         
14828         var days = date.getDaysInMonth();
14829         
14830         var firstOfMonth = date.getFirstDateOfMonth();
14831         var startingPos = firstOfMonth.getDay()-this.startDay;
14832         
14833         if(startingPos < this.startDay){
14834             startingPos += 7;
14835         }
14836         
14837         var pm = date.add(Date.MONTH, -1);
14838         var prevStart = pm.getDaysInMonth()-startingPos;
14839 //        
14840         this.cells = this.el.select('.fc-day',true);
14841         this.textNodes = this.el.query('.fc-day-number');
14842         this.cells.addClassOnOver('fc-state-hover');
14843         
14844         var cells = this.cells.elements;
14845         var textEls = this.textNodes;
14846         
14847         Roo.each(cells, function(cell){
14848             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14849         });
14850         
14851         days += startingPos;
14852
14853         // convert everything to numbers so it's fast
14854         var day = 86400000;
14855         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14856         //Roo.log(d);
14857         //Roo.log(pm);
14858         //Roo.log(prevStart);
14859         
14860         var today = new Date().clearTime().getTime();
14861         var sel = date.clearTime().getTime();
14862         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14863         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14864         var ddMatch = this.disabledDatesRE;
14865         var ddText = this.disabledDatesText;
14866         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14867         var ddaysText = this.disabledDaysText;
14868         var format = this.format;
14869         
14870         var setCellClass = function(cal, cell){
14871             cell.row = 0;
14872             cell.events = [];
14873             cell.more = [];
14874             //Roo.log('set Cell Class');
14875             cell.title = "";
14876             var t = d.getTime();
14877             
14878             //Roo.log(d);
14879             
14880             cell.dateValue = t;
14881             if(t == today){
14882                 cell.className += " fc-today";
14883                 cell.className += " fc-state-highlight";
14884                 cell.title = cal.todayText;
14885             }
14886             if(t == sel){
14887                 // disable highlight in other month..
14888                 //cell.className += " fc-state-highlight";
14889                 
14890             }
14891             // disabling
14892             if(t < min) {
14893                 cell.className = " fc-state-disabled";
14894                 cell.title = cal.minText;
14895                 return;
14896             }
14897             if(t > max) {
14898                 cell.className = " fc-state-disabled";
14899                 cell.title = cal.maxText;
14900                 return;
14901             }
14902             if(ddays){
14903                 if(ddays.indexOf(d.getDay()) != -1){
14904                     cell.title = ddaysText;
14905                     cell.className = " fc-state-disabled";
14906                 }
14907             }
14908             if(ddMatch && format){
14909                 var fvalue = d.dateFormat(format);
14910                 if(ddMatch.test(fvalue)){
14911                     cell.title = ddText.replace("%0", fvalue);
14912                     cell.className = " fc-state-disabled";
14913                 }
14914             }
14915             
14916             if (!cell.initialClassName) {
14917                 cell.initialClassName = cell.dom.className;
14918             }
14919             
14920             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14921         };
14922
14923         var i = 0;
14924         
14925         for(; i < startingPos; i++) {
14926             textEls[i].innerHTML = (++prevStart);
14927             d.setDate(d.getDate()+1);
14928             
14929             cells[i].className = "fc-past fc-other-month";
14930             setCellClass(this, cells[i]);
14931         }
14932         
14933         var intDay = 0;
14934         
14935         for(; i < days; i++){
14936             intDay = i - startingPos + 1;
14937             textEls[i].innerHTML = (intDay);
14938             d.setDate(d.getDate()+1);
14939             
14940             cells[i].className = ''; // "x-date-active";
14941             setCellClass(this, cells[i]);
14942         }
14943         var extraDays = 0;
14944         
14945         for(; i < 42; i++) {
14946             textEls[i].innerHTML = (++extraDays);
14947             d.setDate(d.getDate()+1);
14948             
14949             cells[i].className = "fc-future fc-other-month";
14950             setCellClass(this, cells[i]);
14951         }
14952         
14953         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14954         
14955         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14956         
14957         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14958         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14959         
14960         if(totalRows != 6){
14961             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14962             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14963         }
14964         
14965         this.fireEvent('monthchange', this, date);
14966         
14967         
14968         /*
14969         if(!this.internalRender){
14970             var main = this.el.dom.firstChild;
14971             var w = main.offsetWidth;
14972             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14973             Roo.fly(main).setWidth(w);
14974             this.internalRender = true;
14975             // opera does not respect the auto grow header center column
14976             // then, after it gets a width opera refuses to recalculate
14977             // without a second pass
14978             if(Roo.isOpera && !this.secondPass){
14979                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14980                 this.secondPass = true;
14981                 this.update.defer(10, this, [date]);
14982             }
14983         }
14984         */
14985         
14986     },
14987     
14988     findCell : function(dt) {
14989         dt = dt.clearTime().getTime();
14990         var ret = false;
14991         this.cells.each(function(c){
14992             //Roo.log("check " +c.dateValue + '?=' + dt);
14993             if(c.dateValue == dt){
14994                 ret = c;
14995                 return false;
14996             }
14997             return true;
14998         });
14999         
15000         return ret;
15001     },
15002     
15003     findCells : function(ev) {
15004         var s = ev.start.clone().clearTime().getTime();
15005        // Roo.log(s);
15006         var e= ev.end.clone().clearTime().getTime();
15007        // Roo.log(e);
15008         var ret = [];
15009         this.cells.each(function(c){
15010              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15011             
15012             if(c.dateValue > e){
15013                 return ;
15014             }
15015             if(c.dateValue < s){
15016                 return ;
15017             }
15018             ret.push(c);
15019         });
15020         
15021         return ret;    
15022     },
15023     
15024 //    findBestRow: function(cells)
15025 //    {
15026 //        var ret = 0;
15027 //        
15028 //        for (var i =0 ; i < cells.length;i++) {
15029 //            ret  = Math.max(cells[i].rows || 0,ret);
15030 //        }
15031 //        return ret;
15032 //        
15033 //    },
15034     
15035     
15036     addItem : function(ev)
15037     {
15038         // look for vertical location slot in
15039         var cells = this.findCells(ev);
15040         
15041 //        ev.row = this.findBestRow(cells);
15042         
15043         // work out the location.
15044         
15045         var crow = false;
15046         var rows = [];
15047         for(var i =0; i < cells.length; i++) {
15048             
15049             cells[i].row = cells[0].row;
15050             
15051             if(i == 0){
15052                 cells[i].row = cells[i].row + 1;
15053             }
15054             
15055             if (!crow) {
15056                 crow = {
15057                     start : cells[i],
15058                     end :  cells[i]
15059                 };
15060                 continue;
15061             }
15062             if (crow.start.getY() == cells[i].getY()) {
15063                 // on same row.
15064                 crow.end = cells[i];
15065                 continue;
15066             }
15067             // different row.
15068             rows.push(crow);
15069             crow = {
15070                 start: cells[i],
15071                 end : cells[i]
15072             };
15073             
15074         }
15075         
15076         rows.push(crow);
15077         ev.els = [];
15078         ev.rows = rows;
15079         ev.cells = cells;
15080         
15081         cells[0].events.push(ev);
15082         
15083         this.calevents.push(ev);
15084     },
15085     
15086     clearEvents: function() {
15087         
15088         if(!this.calevents){
15089             return;
15090         }
15091         
15092         Roo.each(this.cells.elements, function(c){
15093             c.row = 0;
15094             c.events = [];
15095             c.more = [];
15096         });
15097         
15098         Roo.each(this.calevents, function(e) {
15099             Roo.each(e.els, function(el) {
15100                 el.un('mouseenter' ,this.onEventEnter, this);
15101                 el.un('mouseleave' ,this.onEventLeave, this);
15102                 el.remove();
15103             },this);
15104         },this);
15105         
15106         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15107             e.remove();
15108         });
15109         
15110     },
15111     
15112     renderEvents: function()
15113     {   
15114         var _this = this;
15115         
15116         this.cells.each(function(c) {
15117             
15118             if(c.row < 5){
15119                 return;
15120             }
15121             
15122             var ev = c.events;
15123             
15124             var r = 4;
15125             if(c.row != c.events.length){
15126                 r = 4 - (4 - (c.row - c.events.length));
15127             }
15128             
15129             c.events = ev.slice(0, r);
15130             c.more = ev.slice(r);
15131             
15132             if(c.more.length && c.more.length == 1){
15133                 c.events.push(c.more.pop());
15134             }
15135             
15136             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15137             
15138         });
15139             
15140         this.cells.each(function(c) {
15141             
15142             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15143             
15144             
15145             for (var e = 0; e < c.events.length; e++){
15146                 var ev = c.events[e];
15147                 var rows = ev.rows;
15148                 
15149                 for(var i = 0; i < rows.length; i++) {
15150                 
15151                     // how many rows should it span..
15152
15153                     var  cfg = {
15154                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15155                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15156
15157                         unselectable : "on",
15158                         cn : [
15159                             {
15160                                 cls: 'fc-event-inner',
15161                                 cn : [
15162     //                                {
15163     //                                  tag:'span',
15164     //                                  cls: 'fc-event-time',
15165     //                                  html : cells.length > 1 ? '' : ev.time
15166     //                                },
15167                                     {
15168                                       tag:'span',
15169                                       cls: 'fc-event-title',
15170                                       html : String.format('{0}', ev.title)
15171                                     }
15172
15173
15174                                 ]
15175                             },
15176                             {
15177                                 cls: 'ui-resizable-handle ui-resizable-e',
15178                                 html : '&nbsp;&nbsp;&nbsp'
15179                             }
15180
15181                         ]
15182                     };
15183
15184                     if (i == 0) {
15185                         cfg.cls += ' fc-event-start';
15186                     }
15187                     if ((i+1) == rows.length) {
15188                         cfg.cls += ' fc-event-end';
15189                     }
15190
15191                     var ctr = _this.el.select('.fc-event-container',true).first();
15192                     var cg = ctr.createChild(cfg);
15193
15194                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15195                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15196
15197                     var r = (c.more.length) ? 1 : 0;
15198                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15199                     cg.setWidth(ebox.right - sbox.x -2);
15200
15201                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15202                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15203                     cg.on('click', _this.onEventClick, _this, ev);
15204
15205                     ev.els.push(cg);
15206                     
15207                 }
15208                 
15209             }
15210             
15211             
15212             if(c.more.length){
15213                 var  cfg = {
15214                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15215                     style : 'position: absolute',
15216                     unselectable : "on",
15217                     cn : [
15218                         {
15219                             cls: 'fc-event-inner',
15220                             cn : [
15221                                 {
15222                                   tag:'span',
15223                                   cls: 'fc-event-title',
15224                                   html : 'More'
15225                                 }
15226
15227
15228                             ]
15229                         },
15230                         {
15231                             cls: 'ui-resizable-handle ui-resizable-e',
15232                             html : '&nbsp;&nbsp;&nbsp'
15233                         }
15234
15235                     ]
15236                 };
15237
15238                 var ctr = _this.el.select('.fc-event-container',true).first();
15239                 var cg = ctr.createChild(cfg);
15240
15241                 var sbox = c.select('.fc-day-content',true).first().getBox();
15242                 var ebox = c.select('.fc-day-content',true).first().getBox();
15243                 //Roo.log(cg);
15244                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15245                 cg.setWidth(ebox.right - sbox.x -2);
15246
15247                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15248                 
15249             }
15250             
15251         });
15252         
15253         
15254         
15255     },
15256     
15257     onEventEnter: function (e, el,event,d) {
15258         this.fireEvent('evententer', this, el, event);
15259     },
15260     
15261     onEventLeave: function (e, el,event,d) {
15262         this.fireEvent('eventleave', this, el, event);
15263     },
15264     
15265     onEventClick: function (e, el,event,d) {
15266         this.fireEvent('eventclick', this, el, event);
15267     },
15268     
15269     onMonthChange: function () {
15270         this.store.load();
15271     },
15272     
15273     onMoreEventClick: function(e, el, more)
15274     {
15275         var _this = this;
15276         
15277         this.calpopover.placement = 'right';
15278         this.calpopover.setTitle('More');
15279         
15280         this.calpopover.setContent('');
15281         
15282         var ctr = this.calpopover.el.select('.popover-content', true).first();
15283         
15284         Roo.each(more, function(m){
15285             var cfg = {
15286                 cls : 'fc-event-hori fc-event-draggable',
15287                 html : m.title
15288             }
15289             var cg = ctr.createChild(cfg);
15290             
15291             cg.on('click', _this.onEventClick, _this, m);
15292         });
15293         
15294         this.calpopover.show(el);
15295         
15296         
15297     },
15298     
15299     onLoad: function () 
15300     {   
15301         this.calevents = [];
15302         var cal = this;
15303         
15304         if(this.store.getCount() > 0){
15305             this.store.data.each(function(d){
15306                cal.addItem({
15307                     id : d.data.id,
15308                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15309                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15310                     time : d.data.start_time,
15311                     title : d.data.title,
15312                     description : d.data.description,
15313                     venue : d.data.venue
15314                 });
15315             });
15316         }
15317         
15318         this.renderEvents();
15319         
15320         if(this.calevents.length && this.loadMask){
15321             this.maskEl.hide();
15322         }
15323     },
15324     
15325     onBeforeLoad: function()
15326     {
15327         this.clearEvents();
15328         if(this.loadMask){
15329             this.maskEl.show();
15330         }
15331     }
15332 });
15333
15334  
15335  /*
15336  * - LGPL
15337  *
15338  * element
15339  * 
15340  */
15341
15342 /**
15343  * @class Roo.bootstrap.Popover
15344  * @extends Roo.bootstrap.Component
15345  * Bootstrap Popover class
15346  * @cfg {String} html contents of the popover   (or false to use children..)
15347  * @cfg {String} title of popover (or false to hide)
15348  * @cfg {String} placement how it is placed
15349  * @cfg {String} trigger click || hover (or false to trigger manually)
15350  * @cfg {String} over what (parent or false to trigger manually.)
15351  * @cfg {Number} delay - delay before showing
15352  
15353  * @constructor
15354  * Create a new Popover
15355  * @param {Object} config The config object
15356  */
15357
15358 Roo.bootstrap.Popover = function(config){
15359     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15360 };
15361
15362 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15363     
15364     title: 'Fill in a title',
15365     html: false,
15366     
15367     placement : 'right',
15368     trigger : 'hover', // hover
15369     
15370     delay : 0,
15371     
15372     over: 'parent',
15373     
15374     can_build_overlaid : false,
15375     
15376     getChildContainer : function()
15377     {
15378         return this.el.select('.popover-content',true).first();
15379     },
15380     
15381     getAutoCreate : function(){
15382          Roo.log('make popover?');
15383         var cfg = {
15384            cls : 'popover roo-dynamic',
15385            style: 'display:block',
15386            cn : [
15387                 {
15388                     cls : 'arrow'
15389                 },
15390                 {
15391                     cls : 'popover-inner',
15392                     cn : [
15393                         {
15394                             tag: 'h3',
15395                             cls: 'popover-title',
15396                             html : this.title
15397                         },
15398                         {
15399                             cls : 'popover-content',
15400                             html : this.html
15401                         }
15402                     ]
15403                     
15404                 }
15405            ]
15406         };
15407         
15408         return cfg;
15409     },
15410     setTitle: function(str)
15411     {
15412         this.title = str;
15413         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15414     },
15415     setContent: function(str)
15416     {
15417         this.html = str;
15418         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15419     },
15420     // as it get's added to the bottom of the page.
15421     onRender : function(ct, position)
15422     {
15423         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15424         if(!this.el){
15425             var cfg = Roo.apply({},  this.getAutoCreate());
15426             cfg.id = Roo.id();
15427             
15428             if (this.cls) {
15429                 cfg.cls += ' ' + this.cls;
15430             }
15431             if (this.style) {
15432                 cfg.style = this.style;
15433             }
15434             Roo.log("adding to ")
15435             this.el = Roo.get(document.body).createChild(cfg, position);
15436             Roo.log(this.el);
15437         }
15438         this.initEvents();
15439     },
15440     
15441     initEvents : function()
15442     {
15443         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15444         this.el.enableDisplayMode('block');
15445         this.el.hide();
15446         if (this.over === false) {
15447             return; 
15448         }
15449         if (this.triggers === false) {
15450             return;
15451         }
15452         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15453         var triggers = this.trigger ? this.trigger.split(' ') : [];
15454         Roo.each(triggers, function(trigger) {
15455         
15456             if (trigger == 'click') {
15457                 on_el.on('click', this.toggle, this);
15458             } else if (trigger != 'manual') {
15459                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15460                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15461       
15462                 on_el.on(eventIn  ,this.enter, this);
15463                 on_el.on(eventOut, this.leave, this);
15464             }
15465         }, this);
15466         
15467     },
15468     
15469     
15470     // private
15471     timeout : null,
15472     hoverState : null,
15473     
15474     toggle : function () {
15475         this.hoverState == 'in' ? this.leave() : this.enter();
15476     },
15477     
15478     enter : function () {
15479        
15480     
15481         clearTimeout(this.timeout);
15482     
15483         this.hoverState = 'in';
15484     
15485         if (!this.delay || !this.delay.show) {
15486             this.show();
15487             return;
15488         }
15489         var _t = this;
15490         this.timeout = setTimeout(function () {
15491             if (_t.hoverState == 'in') {
15492                 _t.show();
15493             }
15494         }, this.delay.show)
15495     },
15496     leave : function() {
15497         clearTimeout(this.timeout);
15498     
15499         this.hoverState = 'out';
15500     
15501         if (!this.delay || !this.delay.hide) {
15502             this.hide();
15503             return;
15504         }
15505         var _t = this;
15506         this.timeout = setTimeout(function () {
15507             if (_t.hoverState == 'out') {
15508                 _t.hide();
15509             }
15510         }, this.delay.hide)
15511     },
15512     
15513     show : function (on_el)
15514     {
15515         if (!on_el) {
15516             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15517         }
15518         // set content.
15519         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15520         if (this.html !== false) {
15521             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15522         }
15523         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15524         if (!this.title.length) {
15525             this.el.select('.popover-title',true).hide();
15526         }
15527         
15528         var placement = typeof this.placement == 'function' ?
15529             this.placement.call(this, this.el, on_el) :
15530             this.placement;
15531             
15532         var autoToken = /\s?auto?\s?/i;
15533         var autoPlace = autoToken.test(placement);
15534         if (autoPlace) {
15535             placement = placement.replace(autoToken, '') || 'top';
15536         }
15537         
15538         //this.el.detach()
15539         //this.el.setXY([0,0]);
15540         this.el.show();
15541         this.el.dom.style.display='block';
15542         this.el.addClass(placement);
15543         
15544         //this.el.appendTo(on_el);
15545         
15546         var p = this.getPosition();
15547         var box = this.el.getBox();
15548         
15549         if (autoPlace) {
15550             // fixme..
15551         }
15552         var align = Roo.bootstrap.Popover.alignment[placement];
15553         this.el.alignTo(on_el, align[0],align[1]);
15554         //var arrow = this.el.select('.arrow',true).first();
15555         //arrow.set(align[2], 
15556         
15557         this.el.addClass('in');
15558         
15559         
15560         if (this.el.hasClass('fade')) {
15561             // fade it?
15562         }
15563         
15564     },
15565     hide : function()
15566     {
15567         this.el.setXY([0,0]);
15568         this.el.removeClass('in');
15569         this.el.hide();
15570         this.hoverState = null;
15571         
15572     }
15573     
15574 });
15575
15576 Roo.bootstrap.Popover.alignment = {
15577     'left' : ['r-l', [-10,0], 'right'],
15578     'right' : ['l-r', [10,0], 'left'],
15579     'bottom' : ['t-b', [0,10], 'top'],
15580     'top' : [ 'b-t', [0,-10], 'bottom']
15581 };
15582
15583  /*
15584  * - LGPL
15585  *
15586  * Progress
15587  * 
15588  */
15589
15590 /**
15591  * @class Roo.bootstrap.Progress
15592  * @extends Roo.bootstrap.Component
15593  * Bootstrap Progress class
15594  * @cfg {Boolean} striped striped of the progress bar
15595  * @cfg {Boolean} active animated of the progress bar
15596  * 
15597  * 
15598  * @constructor
15599  * Create a new Progress
15600  * @param {Object} config The config object
15601  */
15602
15603 Roo.bootstrap.Progress = function(config){
15604     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15605 };
15606
15607 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15608     
15609     striped : false,
15610     active: false,
15611     
15612     getAutoCreate : function(){
15613         var cfg = {
15614             tag: 'div',
15615             cls: 'progress'
15616         };
15617         
15618         
15619         if(this.striped){
15620             cfg.cls += ' progress-striped';
15621         }
15622       
15623         if(this.active){
15624             cfg.cls += ' active';
15625         }
15626         
15627         
15628         return cfg;
15629     }
15630    
15631 });
15632
15633  
15634
15635  /*
15636  * - LGPL
15637  *
15638  * ProgressBar
15639  * 
15640  */
15641
15642 /**
15643  * @class Roo.bootstrap.ProgressBar
15644  * @extends Roo.bootstrap.Component
15645  * Bootstrap ProgressBar class
15646  * @cfg {Number} aria_valuenow aria-value now
15647  * @cfg {Number} aria_valuemin aria-value min
15648  * @cfg {Number} aria_valuemax aria-value max
15649  * @cfg {String} label label for the progress bar
15650  * @cfg {String} panel (success | info | warning | danger )
15651  * @cfg {String} role role of the progress bar
15652  * @cfg {String} sr_only text
15653  * 
15654  * 
15655  * @constructor
15656  * Create a new ProgressBar
15657  * @param {Object} config The config object
15658  */
15659
15660 Roo.bootstrap.ProgressBar = function(config){
15661     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15662 };
15663
15664 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15665     
15666     aria_valuenow : 0,
15667     aria_valuemin : 0,
15668     aria_valuemax : 100,
15669     label : false,
15670     panel : false,
15671     role : false,
15672     sr_only: false,
15673     
15674     getAutoCreate : function()
15675     {
15676         
15677         var cfg = {
15678             tag: 'div',
15679             cls: 'progress-bar',
15680             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15681         };
15682         
15683         if(this.sr_only){
15684             cfg.cn = {
15685                 tag: 'span',
15686                 cls: 'sr-only',
15687                 html: this.sr_only
15688             }
15689         }
15690         
15691         if(this.role){
15692             cfg.role = this.role;
15693         }
15694         
15695         if(this.aria_valuenow){
15696             cfg['aria-valuenow'] = this.aria_valuenow;
15697         }
15698         
15699         if(this.aria_valuemin){
15700             cfg['aria-valuemin'] = this.aria_valuemin;
15701         }
15702         
15703         if(this.aria_valuemax){
15704             cfg['aria-valuemax'] = this.aria_valuemax;
15705         }
15706         
15707         if(this.label && !this.sr_only){
15708             cfg.html = this.label;
15709         }
15710         
15711         if(this.panel){
15712             cfg.cls += ' progress-bar-' + this.panel;
15713         }
15714         
15715         return cfg;
15716     },
15717     
15718     update : function(aria_valuenow)
15719     {
15720         this.aria_valuenow = aria_valuenow;
15721         
15722         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15723     }
15724    
15725 });
15726
15727  
15728
15729  /*
15730  * - LGPL
15731  *
15732  * column
15733  * 
15734  */
15735
15736 /**
15737  * @class Roo.bootstrap.TabGroup
15738  * @extends Roo.bootstrap.Column
15739  * Bootstrap Column class
15740  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15741  * @cfg {Boolean} carousel true to make the group behave like a carousel
15742  * @cfg {Number} bullets show the panel pointer.. default 0
15743  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15744  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15745  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15746  * 
15747  * @constructor
15748  * Create a new TabGroup
15749  * @param {Object} config The config object
15750  */
15751
15752 Roo.bootstrap.TabGroup = function(config){
15753     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15754     if (!this.navId) {
15755         this.navId = Roo.id();
15756     }
15757     this.tabs = [];
15758     Roo.bootstrap.TabGroup.register(this);
15759     
15760 };
15761
15762 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15763     
15764     carousel : false,
15765     transition : false,
15766     bullets : 0,
15767     timer : 0,
15768     autoslide : false,
15769     slideFn : false,
15770     slideOnTouch : false,
15771     
15772     getAutoCreate : function()
15773     {
15774         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15775         
15776         cfg.cls += ' tab-content';
15777         
15778         Roo.log('get auto create...............');
15779         
15780         if (this.carousel) {
15781             cfg.cls += ' carousel slide';
15782             
15783             cfg.cn = [{
15784                cls : 'carousel-inner'
15785             }];
15786         
15787             if(this.bullets > 0 && !Roo.isTouch){
15788                 
15789                 var bullets = {
15790                     cls : 'carousel-bullets',
15791                     cn : []
15792                 };
15793                 
15794                 if(this.bullets_cls){
15795                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15796                 }
15797                 
15798                 for (var i = 0; i < this.bullets; i++){
15799                     bullets.cn.push({
15800                         cls : 'bullet bullet-' + i
15801                     });
15802                 }
15803                 
15804                 bullets.cn.push({
15805                     cls : 'clear'
15806                 });
15807                 
15808                 cfg.cn[0].cn = bullets;
15809             }
15810         }
15811         
15812         return cfg;
15813     },
15814     
15815     initEvents:  function()
15816     {
15817         Roo.log('-------- init events on tab group ---------');
15818         
15819         if(this.bullets > 0 && !Roo.isTouch){
15820             this.initBullet();
15821         }
15822         
15823         Roo.log(this);
15824         
15825         if(Roo.isTouch && this.slideOnTouch){
15826             this.el.on("touchstart", this.onTouchStart, this);
15827         }
15828         
15829         if(this.autoslide){
15830             var _this = this;
15831             
15832             this.slideFn = window.setInterval(function() {
15833                 _this.showPanelNext();
15834             }, this.timer);
15835         }
15836         
15837     },
15838     
15839     onTouchStart : function(e, el, o)
15840     {
15841         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15842             return;
15843         }
15844         
15845         this.showPanelNext();
15846     },
15847     
15848     getChildContainer : function()
15849     {
15850         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15851     },
15852     
15853     /**
15854     * register a Navigation item
15855     * @param {Roo.bootstrap.NavItem} the navitem to add
15856     */
15857     register : function(item)
15858     {
15859         this.tabs.push( item);
15860         item.navId = this.navId; // not really needed..
15861     
15862     },
15863     
15864     getActivePanel : function()
15865     {
15866         var r = false;
15867         Roo.each(this.tabs, function(t) {
15868             if (t.active) {
15869                 r = t;
15870                 return false;
15871             }
15872             return null;
15873         });
15874         return r;
15875         
15876     },
15877     getPanelByName : function(n)
15878     {
15879         var r = false;
15880         Roo.each(this.tabs, function(t) {
15881             if (t.tabId == n) {
15882                 r = t;
15883                 return false;
15884             }
15885             return null;
15886         });
15887         return r;
15888     },
15889     indexOfPanel : function(p)
15890     {
15891         var r = false;
15892         Roo.each(this.tabs, function(t,i) {
15893             if (t.tabId == p.tabId) {
15894                 r = i;
15895                 return false;
15896             }
15897             return null;
15898         });
15899         return r;
15900     },
15901     /**
15902      * show a specific panel
15903      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15904      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15905      */
15906     showPanel : function (pan)
15907     {
15908         if(this.transition){
15909             Roo.log("waiting for the transitionend");
15910             return;
15911         }
15912         
15913         if (typeof(pan) == 'number') {
15914             pan = this.tabs[pan];
15915         }
15916         if (typeof(pan) == 'string') {
15917             pan = this.getPanelByName(pan);
15918         }
15919         if (pan.tabId == this.getActivePanel().tabId) {
15920             return true;
15921         }
15922         var cur = this.getActivePanel();
15923         
15924         if (false === cur.fireEvent('beforedeactivate')) {
15925             return false;
15926         }
15927         
15928         if(this.bullets > 0 && !Roo.isTouch){
15929             this.setActiveBullet(this.indexOfPanel(pan));
15930         }
15931         
15932         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15933             
15934             this.transition = true;
15935             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15936             var lr = dir == 'next' ? 'left' : 'right';
15937             pan.el.addClass(dir); // or prev
15938             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15939             cur.el.addClass(lr); // or right
15940             pan.el.addClass(lr);
15941             
15942             var _this = this;
15943             cur.el.on('transitionend', function() {
15944                 Roo.log("trans end?");
15945                 
15946                 pan.el.removeClass([lr,dir]);
15947                 pan.setActive(true);
15948                 
15949                 cur.el.removeClass([lr]);
15950                 cur.setActive(false);
15951                 
15952                 _this.transition = false;
15953                 
15954             }, this, { single:  true } );
15955             
15956             return true;
15957         }
15958         
15959         cur.setActive(false);
15960         pan.setActive(true);
15961         
15962         return true;
15963         
15964     },
15965     showPanelNext : function()
15966     {
15967         var i = this.indexOfPanel(this.getActivePanel());
15968         
15969         if (i >= this.tabs.length - 1 && !this.autoslide) {
15970             return;
15971         }
15972         
15973         if (i >= this.tabs.length - 1 && this.autoslide) {
15974             i = -1;
15975         }
15976         
15977         this.showPanel(this.tabs[i+1]);
15978     },
15979     
15980     showPanelPrev : function()
15981     {
15982         var i = this.indexOfPanel(this.getActivePanel());
15983         
15984         if (i  < 1 && !this.autoslide) {
15985             return;
15986         }
15987         
15988         if (i < 1 && this.autoslide) {
15989             i = this.tabs.length;
15990         }
15991         
15992         this.showPanel(this.tabs[i-1]);
15993     },
15994     
15995     initBullet : function()
15996     {
15997         if(Roo.isTouch){
15998             return;
15999         }
16000         
16001         var _this = this;
16002         
16003         for (var i = 0; i < this.bullets; i++){
16004             var bullet = this.el.select('.bullet-' + i, true).first();
16005
16006             if(!bullet){
16007                 continue;
16008             }
16009
16010             bullet.on('click', (function(e, el, o, ii, t){
16011
16012                 e.preventDefault();
16013
16014                 _this.showPanel(ii);
16015
16016                 if(_this.autoslide && _this.slideFn){
16017                     clearInterval(_this.slideFn);
16018                     _this.slideFn = window.setInterval(function() {
16019                         _this.showPanelNext();
16020                     }, _this.timer);
16021                 }
16022
16023             }).createDelegate(this, [i, bullet], true));
16024         }
16025     },
16026     
16027     setActiveBullet : function(i)
16028     {
16029         if(Roo.isTouch){
16030             return;
16031         }
16032         
16033         Roo.each(this.el.select('.bullet', true).elements, function(el){
16034             el.removeClass('selected');
16035         });
16036
16037         var bullet = this.el.select('.bullet-' + i, true).first();
16038         
16039         if(!bullet){
16040             return;
16041         }
16042         
16043         bullet.addClass('selected');
16044     }
16045     
16046     
16047   
16048 });
16049
16050  
16051
16052  
16053  
16054 Roo.apply(Roo.bootstrap.TabGroup, {
16055     
16056     groups: {},
16057      /**
16058     * register a Navigation Group
16059     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16060     */
16061     register : function(navgrp)
16062     {
16063         this.groups[navgrp.navId] = navgrp;
16064         
16065     },
16066     /**
16067     * fetch a Navigation Group based on the navigation ID
16068     * if one does not exist , it will get created.
16069     * @param {string} the navgroup to add
16070     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16071     */
16072     get: function(navId) {
16073         if (typeof(this.groups[navId]) == 'undefined') {
16074             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16075         }
16076         return this.groups[navId] ;
16077     }
16078     
16079     
16080     
16081 });
16082
16083  /*
16084  * - LGPL
16085  *
16086  * TabPanel
16087  * 
16088  */
16089
16090 /**
16091  * @class Roo.bootstrap.TabPanel
16092  * @extends Roo.bootstrap.Component
16093  * Bootstrap TabPanel class
16094  * @cfg {Boolean} active panel active
16095  * @cfg {String} html panel content
16096  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16097  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16098  * 
16099  * 
16100  * @constructor
16101  * Create a new TabPanel
16102  * @param {Object} config The config object
16103  */
16104
16105 Roo.bootstrap.TabPanel = function(config){
16106     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16107     this.addEvents({
16108         /**
16109              * @event changed
16110              * Fires when the active status changes
16111              * @param {Roo.bootstrap.TabPanel} this
16112              * @param {Boolean} state the new state
16113             
16114          */
16115         'changed': true,
16116         /**
16117              * @event beforedeactivate
16118              * Fires before a tab is de-activated - can be used to do validation on a form.
16119              * @param {Roo.bootstrap.TabPanel} this
16120              * @return {Boolean} false if there is an error
16121             
16122          */
16123         'beforedeactivate': true
16124      });
16125     
16126     this.tabId = this.tabId || Roo.id();
16127   
16128 };
16129
16130 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16131     
16132     active: false,
16133     html: false,
16134     tabId: false,
16135     navId : false,
16136     
16137     getAutoCreate : function(){
16138         var cfg = {
16139             tag: 'div',
16140             // item is needed for carousel - not sure if it has any effect otherwise
16141             cls: 'tab-pane item',
16142             html: this.html || ''
16143         };
16144         
16145         if(this.active){
16146             cfg.cls += ' active';
16147         }
16148         
16149         if(this.tabId){
16150             cfg.tabId = this.tabId;
16151         }
16152         
16153         
16154         return cfg;
16155     },
16156     
16157     initEvents:  function()
16158     {
16159         Roo.log('-------- init events on tab panel ---------');
16160         
16161         var p = this.parent();
16162         this.navId = this.navId || p.navId;
16163         
16164         if (typeof(this.navId) != 'undefined') {
16165             // not really needed.. but just in case.. parent should be a NavGroup.
16166             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16167             Roo.log(['register', tg, this]);
16168             tg.register(this);
16169             
16170             var i = tg.tabs.length - 1;
16171             
16172             if(this.active && tg.bullets > 0 && i < tg.bullets){
16173                 tg.setActiveBullet(i);
16174             }
16175         }
16176         
16177     },
16178     
16179     
16180     onRender : function(ct, position)
16181     {
16182        // Roo.log("Call onRender: " + this.xtype);
16183         
16184         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16185         
16186         
16187         
16188         
16189         
16190     },
16191     
16192     setActive: function(state)
16193     {
16194         Roo.log("panel - set active " + this.tabId + "=" + state);
16195         
16196         this.active = state;
16197         if (!state) {
16198             this.el.removeClass('active');
16199             
16200         } else  if (!this.el.hasClass('active')) {
16201             this.el.addClass('active');
16202         }
16203         
16204         this.fireEvent('changed', this, state);
16205     }
16206     
16207     
16208 });
16209  
16210
16211  
16212
16213  /*
16214  * - LGPL
16215  *
16216  * DateField
16217  * 
16218  */
16219
16220 /**
16221  * @class Roo.bootstrap.DateField
16222  * @extends Roo.bootstrap.Input
16223  * Bootstrap DateField class
16224  * @cfg {Number} weekStart default 0
16225  * @cfg {String} viewMode default empty, (months|years)
16226  * @cfg {String} minViewMode default empty, (months|years)
16227  * @cfg {Number} startDate default -Infinity
16228  * @cfg {Number} endDate default Infinity
16229  * @cfg {Boolean} todayHighlight default false
16230  * @cfg {Boolean} todayBtn default false
16231  * @cfg {Boolean} calendarWeeks default false
16232  * @cfg {Object} daysOfWeekDisabled default empty
16233  * @cfg {Boolean} singleMode default false (true | false)
16234  * 
16235  * @cfg {Boolean} keyboardNavigation default true
16236  * @cfg {String} language default en
16237  * 
16238  * @constructor
16239  * Create a new DateField
16240  * @param {Object} config The config object
16241  */
16242
16243 Roo.bootstrap.DateField = function(config){
16244     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16245      this.addEvents({
16246             /**
16247              * @event show
16248              * Fires when this field show.
16249              * @param {Roo.bootstrap.DateField} this
16250              * @param {Mixed} date The date value
16251              */
16252             show : true,
16253             /**
16254              * @event show
16255              * Fires when this field hide.
16256              * @param {Roo.bootstrap.DateField} this
16257              * @param {Mixed} date The date value
16258              */
16259             hide : true,
16260             /**
16261              * @event select
16262              * Fires when select a date.
16263              * @param {Roo.bootstrap.DateField} this
16264              * @param {Mixed} date The date value
16265              */
16266             select : true
16267         });
16268 };
16269
16270 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16271     
16272     /**
16273      * @cfg {String} format
16274      * The default date format string which can be overriden for localization support.  The format must be
16275      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16276      */
16277     format : "m/d/y",
16278     /**
16279      * @cfg {String} altFormats
16280      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16281      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16282      */
16283     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16284     
16285     weekStart : 0,
16286     
16287     viewMode : '',
16288     
16289     minViewMode : '',
16290     
16291     todayHighlight : false,
16292     
16293     todayBtn: false,
16294     
16295     language: 'en',
16296     
16297     keyboardNavigation: true,
16298     
16299     calendarWeeks: false,
16300     
16301     startDate: -Infinity,
16302     
16303     endDate: Infinity,
16304     
16305     daysOfWeekDisabled: [],
16306     
16307     _events: [],
16308     
16309     singleMode : false,
16310     
16311     UTCDate: function()
16312     {
16313         return new Date(Date.UTC.apply(Date, arguments));
16314     },
16315     
16316     UTCToday: function()
16317     {
16318         var today = new Date();
16319         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16320     },
16321     
16322     getDate: function() {
16323             var d = this.getUTCDate();
16324             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16325     },
16326     
16327     getUTCDate: function() {
16328             return this.date;
16329     },
16330     
16331     setDate: function(d) {
16332             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16333     },
16334     
16335     setUTCDate: function(d) {
16336             this.date = d;
16337             this.setValue(this.formatDate(this.date));
16338     },
16339         
16340     onRender: function(ct, position)
16341     {
16342         
16343         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16344         
16345         this.language = this.language || 'en';
16346         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16347         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16348         
16349         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16350         this.format = this.format || 'm/d/y';
16351         this.isInline = false;
16352         this.isInput = true;
16353         this.component = this.el.select('.add-on', true).first() || false;
16354         this.component = (this.component && this.component.length === 0) ? false : this.component;
16355         this.hasInput = this.component && this.inputEL().length;
16356         
16357         if (typeof(this.minViewMode === 'string')) {
16358             switch (this.minViewMode) {
16359                 case 'months':
16360                     this.minViewMode = 1;
16361                     break;
16362                 case 'years':
16363                     this.minViewMode = 2;
16364                     break;
16365                 default:
16366                     this.minViewMode = 0;
16367                     break;
16368             }
16369         }
16370         
16371         if (typeof(this.viewMode === 'string')) {
16372             switch (this.viewMode) {
16373                 case 'months':
16374                     this.viewMode = 1;
16375                     break;
16376                 case 'years':
16377                     this.viewMode = 2;
16378                     break;
16379                 default:
16380                     this.viewMode = 0;
16381                     break;
16382             }
16383         }
16384                 
16385         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16386         
16387 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16388         
16389         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16390         
16391         this.picker().on('mousedown', this.onMousedown, this);
16392         this.picker().on('click', this.onClick, this);
16393         
16394         this.picker().addClass('datepicker-dropdown');
16395         
16396         this.startViewMode = this.viewMode;
16397         
16398         if(this.singleMode){
16399             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16400                 v.setVisibilityMode(Roo.Element.DISPLAY)
16401                 v.hide();
16402             });
16403             
16404             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16405                 v.setStyle('width', '189px');
16406             });
16407         }
16408         
16409         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16410             if(!this.calendarWeeks){
16411                 v.remove();
16412                 return;
16413             }
16414             
16415             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16416             v.attr('colspan', function(i, val){
16417                 return parseInt(val) + 1;
16418             });
16419         })
16420                         
16421         
16422         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16423         
16424         this.setStartDate(this.startDate);
16425         this.setEndDate(this.endDate);
16426         
16427         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16428         
16429         this.fillDow();
16430         this.fillMonths();
16431         this.update();
16432         this.showMode();
16433         
16434         if(this.isInline) {
16435             this.show();
16436         }
16437     },
16438     
16439     picker : function()
16440     {
16441         return this.pickerEl;
16442 //        return this.el.select('.datepicker', true).first();
16443     },
16444     
16445     fillDow: function()
16446     {
16447         var dowCnt = this.weekStart;
16448         
16449         var dow = {
16450             tag: 'tr',
16451             cn: [
16452                 
16453             ]
16454         };
16455         
16456         if(this.calendarWeeks){
16457             dow.cn.push({
16458                 tag: 'th',
16459                 cls: 'cw',
16460                 html: '&nbsp;'
16461             })
16462         }
16463         
16464         while (dowCnt < this.weekStart + 7) {
16465             dow.cn.push({
16466                 tag: 'th',
16467                 cls: 'dow',
16468                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16469             });
16470         }
16471         
16472         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16473     },
16474     
16475     fillMonths: function()
16476     {    
16477         var i = 0;
16478         var months = this.picker().select('>.datepicker-months td', true).first();
16479         
16480         months.dom.innerHTML = '';
16481         
16482         while (i < 12) {
16483             var month = {
16484                 tag: 'span',
16485                 cls: 'month',
16486                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16487             }
16488             
16489             months.createChild(month);
16490         }
16491         
16492     },
16493     
16494     update: function()
16495     {
16496         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;
16497         
16498         if (this.date < this.startDate) {
16499             this.viewDate = new Date(this.startDate);
16500         } else if (this.date > this.endDate) {
16501             this.viewDate = new Date(this.endDate);
16502         } else {
16503             this.viewDate = new Date(this.date);
16504         }
16505         
16506         this.fill();
16507     },
16508     
16509     fill: function() 
16510     {
16511         var d = new Date(this.viewDate),
16512                 year = d.getUTCFullYear(),
16513                 month = d.getUTCMonth(),
16514                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16515                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16516                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16517                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16518                 currentDate = this.date && this.date.valueOf(),
16519                 today = this.UTCToday();
16520         
16521         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16522         
16523 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16524         
16525 //        this.picker.select('>tfoot th.today').
16526 //                                              .text(dates[this.language].today)
16527 //                                              .toggle(this.todayBtn !== false);
16528     
16529         this.updateNavArrows();
16530         this.fillMonths();
16531                                                 
16532         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16533         
16534         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16535          
16536         prevMonth.setUTCDate(day);
16537         
16538         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16539         
16540         var nextMonth = new Date(prevMonth);
16541         
16542         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16543         
16544         nextMonth = nextMonth.valueOf();
16545         
16546         var fillMonths = false;
16547         
16548         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16549         
16550         while(prevMonth.valueOf() < nextMonth) {
16551             var clsName = '';
16552             
16553             if (prevMonth.getUTCDay() === this.weekStart) {
16554                 if(fillMonths){
16555                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16556                 }
16557                     
16558                 fillMonths = {
16559                     tag: 'tr',
16560                     cn: []
16561                 };
16562                 
16563                 if(this.calendarWeeks){
16564                     // ISO 8601: First week contains first thursday.
16565                     // ISO also states week starts on Monday, but we can be more abstract here.
16566                     var
16567                     // Start of current week: based on weekstart/current date
16568                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16569                     // Thursday of this week
16570                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16571                     // First Thursday of year, year from thursday
16572                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16573                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16574                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16575                     
16576                     fillMonths.cn.push({
16577                         tag: 'td',
16578                         cls: 'cw',
16579                         html: calWeek
16580                     });
16581                 }
16582             }
16583             
16584             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16585                 clsName += ' old';
16586             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16587                 clsName += ' new';
16588             }
16589             if (this.todayHighlight &&
16590                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16591                 prevMonth.getUTCMonth() == today.getMonth() &&
16592                 prevMonth.getUTCDate() == today.getDate()) {
16593                 clsName += ' today';
16594             }
16595             
16596             if (currentDate && prevMonth.valueOf() === currentDate) {
16597                 clsName += ' active';
16598             }
16599             
16600             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16601                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16602                     clsName += ' disabled';
16603             }
16604             
16605             fillMonths.cn.push({
16606                 tag: 'td',
16607                 cls: 'day ' + clsName,
16608                 html: prevMonth.getDate()
16609             })
16610             
16611             prevMonth.setDate(prevMonth.getDate()+1);
16612         }
16613           
16614         var currentYear = this.date && this.date.getUTCFullYear();
16615         var currentMonth = this.date && this.date.getUTCMonth();
16616         
16617         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16618         
16619         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16620             v.removeClass('active');
16621             
16622             if(currentYear === year && k === currentMonth){
16623                 v.addClass('active');
16624             }
16625             
16626             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16627                 v.addClass('disabled');
16628             }
16629             
16630         });
16631         
16632         
16633         year = parseInt(year/10, 10) * 10;
16634         
16635         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16636         
16637         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16638         
16639         year -= 1;
16640         for (var i = -1; i < 11; i++) {
16641             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16642                 tag: 'span',
16643                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16644                 html: year
16645             })
16646             
16647             year += 1;
16648         }
16649     },
16650     
16651     showMode: function(dir) 
16652     {
16653         if (dir) {
16654             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16655         }
16656         
16657         Roo.each(this.picker().select('>div',true).elements, function(v){
16658             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16659             v.hide();
16660         });
16661         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16662     },
16663     
16664     place: function()
16665     {
16666         if(this.isInline) return;
16667         
16668         this.picker().removeClass(['bottom', 'top']);
16669         
16670         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16671             /*
16672              * place to the top of element!
16673              *
16674              */
16675             
16676             this.picker().addClass('top');
16677             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16678             
16679             return;
16680         }
16681         
16682         this.picker().addClass('bottom');
16683         
16684         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16685     },
16686     
16687     parseDate : function(value)
16688     {
16689         if(!value || value instanceof Date){
16690             return value;
16691         }
16692         var v = Date.parseDate(value, this.format);
16693         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16694             v = Date.parseDate(value, 'Y-m-d');
16695         }
16696         if(!v && this.altFormats){
16697             if(!this.altFormatsArray){
16698                 this.altFormatsArray = this.altFormats.split("|");
16699             }
16700             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16701                 v = Date.parseDate(value, this.altFormatsArray[i]);
16702             }
16703         }
16704         return v;
16705     },
16706     
16707     formatDate : function(date, fmt)
16708     {   
16709         return (!date || !(date instanceof Date)) ?
16710         date : date.dateFormat(fmt || this.format);
16711     },
16712     
16713     onFocus : function()
16714     {
16715         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16716         this.show();
16717     },
16718     
16719     onBlur : function()
16720     {
16721         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16722         
16723         var d = this.inputEl().getValue();
16724         
16725         this.setValue(d);
16726                 
16727         this.hide();
16728     },
16729     
16730     show : function()
16731     {
16732         this.picker().show();
16733         this.update();
16734         this.place();
16735         
16736         this.fireEvent('show', this, this.date);
16737     },
16738     
16739     hide : function()
16740     {
16741         if(this.isInline) return;
16742         this.picker().hide();
16743         this.viewMode = this.startViewMode;
16744         this.showMode();
16745         
16746         this.fireEvent('hide', this, this.date);
16747         
16748     },
16749     
16750     onMousedown: function(e)
16751     {
16752         e.stopPropagation();
16753         e.preventDefault();
16754     },
16755     
16756     keyup: function(e)
16757     {
16758         Roo.bootstrap.DateField.superclass.keyup.call(this);
16759         this.update();
16760     },
16761
16762     setValue: function(v)
16763     {
16764         
16765         // v can be a string or a date..
16766         
16767         
16768         var d = new Date(this.parseDate(v) ).clearTime();
16769         
16770         if(isNaN(d.getTime())){
16771             this.date = this.viewDate = '';
16772             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16773             return;
16774         }
16775         
16776         v = this.formatDate(d);
16777         
16778         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16779         
16780         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16781      
16782         this.update();
16783
16784         this.fireEvent('select', this, this.date);
16785         
16786     },
16787     
16788     getValue: function()
16789     {
16790         return this.formatDate(this.date);
16791     },
16792     
16793     fireKey: function(e)
16794     {
16795         if (!this.picker().isVisible()){
16796             if (e.keyCode == 27) // allow escape to hide and re-show picker
16797                 this.show();
16798             return;
16799         }
16800         
16801         var dateChanged = false,
16802         dir, day, month,
16803         newDate, newViewDate;
16804         
16805         switch(e.keyCode){
16806             case 27: // escape
16807                 this.hide();
16808                 e.preventDefault();
16809                 break;
16810             case 37: // left
16811             case 39: // right
16812                 if (!this.keyboardNavigation) break;
16813                 dir = e.keyCode == 37 ? -1 : 1;
16814                 
16815                 if (e.ctrlKey){
16816                     newDate = this.moveYear(this.date, dir);
16817                     newViewDate = this.moveYear(this.viewDate, dir);
16818                 } else if (e.shiftKey){
16819                     newDate = this.moveMonth(this.date, dir);
16820                     newViewDate = this.moveMonth(this.viewDate, dir);
16821                 } else {
16822                     newDate = new Date(this.date);
16823                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16824                     newViewDate = new Date(this.viewDate);
16825                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16826                 }
16827                 if (this.dateWithinRange(newDate)){
16828                     this.date = newDate;
16829                     this.viewDate = newViewDate;
16830                     this.setValue(this.formatDate(this.date));
16831 //                    this.update();
16832                     e.preventDefault();
16833                     dateChanged = true;
16834                 }
16835                 break;
16836             case 38: // up
16837             case 40: // down
16838                 if (!this.keyboardNavigation) break;
16839                 dir = e.keyCode == 38 ? -1 : 1;
16840                 if (e.ctrlKey){
16841                     newDate = this.moveYear(this.date, dir);
16842                     newViewDate = this.moveYear(this.viewDate, dir);
16843                 } else if (e.shiftKey){
16844                     newDate = this.moveMonth(this.date, dir);
16845                     newViewDate = this.moveMonth(this.viewDate, dir);
16846                 } else {
16847                     newDate = new Date(this.date);
16848                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16849                     newViewDate = new Date(this.viewDate);
16850                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16851                 }
16852                 if (this.dateWithinRange(newDate)){
16853                     this.date = newDate;
16854                     this.viewDate = newViewDate;
16855                     this.setValue(this.formatDate(this.date));
16856 //                    this.update();
16857                     e.preventDefault();
16858                     dateChanged = true;
16859                 }
16860                 break;
16861             case 13: // enter
16862                 this.setValue(this.formatDate(this.date));
16863                 this.hide();
16864                 e.preventDefault();
16865                 break;
16866             case 9: // tab
16867                 this.setValue(this.formatDate(this.date));
16868                 this.hide();
16869                 break;
16870             case 16: // shift
16871             case 17: // ctrl
16872             case 18: // alt
16873                 break;
16874             default :
16875                 this.hide();
16876                 
16877         }
16878     },
16879     
16880     
16881     onClick: function(e) 
16882     {
16883         e.stopPropagation();
16884         e.preventDefault();
16885         
16886         var target = e.getTarget();
16887         
16888         if(target.nodeName.toLowerCase() === 'i'){
16889             target = Roo.get(target).dom.parentNode;
16890         }
16891         
16892         var nodeName = target.nodeName;
16893         var className = target.className;
16894         var html = target.innerHTML;
16895         //Roo.log(nodeName);
16896         
16897         switch(nodeName.toLowerCase()) {
16898             case 'th':
16899                 switch(className) {
16900                     case 'switch':
16901                         this.showMode(1);
16902                         break;
16903                     case 'prev':
16904                     case 'next':
16905                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16906                         switch(this.viewMode){
16907                                 case 0:
16908                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16909                                         break;
16910                                 case 1:
16911                                 case 2:
16912                                         this.viewDate = this.moveYear(this.viewDate, dir);
16913                                         break;
16914                         }
16915                         this.fill();
16916                         break;
16917                     case 'today':
16918                         var date = new Date();
16919                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16920 //                        this.fill()
16921                         this.setValue(this.formatDate(this.date));
16922                         
16923                         this.hide();
16924                         break;
16925                 }
16926                 break;
16927             case 'span':
16928                 if (className.indexOf('disabled') < 0) {
16929                     this.viewDate.setUTCDate(1);
16930                     if (className.indexOf('month') > -1) {
16931                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16932                     } else {
16933                         var year = parseInt(html, 10) || 0;
16934                         this.viewDate.setUTCFullYear(year);
16935                         
16936                     }
16937                     
16938                     if(this.singleMode){
16939                         this.setValue(this.formatDate(this.viewDate));
16940                         this.hide();
16941                         return;
16942                     }
16943                     
16944                     this.showMode(-1);
16945                     this.fill();
16946                 }
16947                 break;
16948                 
16949             case 'td':
16950                 //Roo.log(className);
16951                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16952                     var day = parseInt(html, 10) || 1;
16953                     var year = this.viewDate.getUTCFullYear(),
16954                         month = this.viewDate.getUTCMonth();
16955
16956                     if (className.indexOf('old') > -1) {
16957                         if(month === 0 ){
16958                             month = 11;
16959                             year -= 1;
16960                         }else{
16961                             month -= 1;
16962                         }
16963                     } else if (className.indexOf('new') > -1) {
16964                         if (month == 11) {
16965                             month = 0;
16966                             year += 1;
16967                         } else {
16968                             month += 1;
16969                         }
16970                     }
16971                     //Roo.log([year,month,day]);
16972                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16973                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16974 //                    this.fill();
16975                     //Roo.log(this.formatDate(this.date));
16976                     this.setValue(this.formatDate(this.date));
16977                     this.hide();
16978                 }
16979                 break;
16980         }
16981     },
16982     
16983     setStartDate: function(startDate)
16984     {
16985         this.startDate = startDate || -Infinity;
16986         if (this.startDate !== -Infinity) {
16987             this.startDate = this.parseDate(this.startDate);
16988         }
16989         this.update();
16990         this.updateNavArrows();
16991     },
16992
16993     setEndDate: function(endDate)
16994     {
16995         this.endDate = endDate || Infinity;
16996         if (this.endDate !== Infinity) {
16997             this.endDate = this.parseDate(this.endDate);
16998         }
16999         this.update();
17000         this.updateNavArrows();
17001     },
17002     
17003     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17004     {
17005         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17006         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17007             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17008         }
17009         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17010             return parseInt(d, 10);
17011         });
17012         this.update();
17013         this.updateNavArrows();
17014     },
17015     
17016     updateNavArrows: function() 
17017     {
17018         if(this.singleMode){
17019             return;
17020         }
17021         
17022         var d = new Date(this.viewDate),
17023         year = d.getUTCFullYear(),
17024         month = d.getUTCMonth();
17025         
17026         Roo.each(this.picker().select('.prev', true).elements, function(v){
17027             v.show();
17028             switch (this.viewMode) {
17029                 case 0:
17030
17031                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17032                         v.hide();
17033                     }
17034                     break;
17035                 case 1:
17036                 case 2:
17037                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17038                         v.hide();
17039                     }
17040                     break;
17041             }
17042         });
17043         
17044         Roo.each(this.picker().select('.next', true).elements, function(v){
17045             v.show();
17046             switch (this.viewMode) {
17047                 case 0:
17048
17049                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17050                         v.hide();
17051                     }
17052                     break;
17053                 case 1:
17054                 case 2:
17055                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17056                         v.hide();
17057                     }
17058                     break;
17059             }
17060         })
17061     },
17062     
17063     moveMonth: function(date, dir)
17064     {
17065         if (!dir) return date;
17066         var new_date = new Date(date.valueOf()),
17067         day = new_date.getUTCDate(),
17068         month = new_date.getUTCMonth(),
17069         mag = Math.abs(dir),
17070         new_month, test;
17071         dir = dir > 0 ? 1 : -1;
17072         if (mag == 1){
17073             test = dir == -1
17074             // If going back one month, make sure month is not current month
17075             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17076             ? function(){
17077                 return new_date.getUTCMonth() == month;
17078             }
17079             // If going forward one month, make sure month is as expected
17080             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17081             : function(){
17082                 return new_date.getUTCMonth() != new_month;
17083             };
17084             new_month = month + dir;
17085             new_date.setUTCMonth(new_month);
17086             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17087             if (new_month < 0 || new_month > 11)
17088                 new_month = (new_month + 12) % 12;
17089         } else {
17090             // For magnitudes >1, move one month at a time...
17091             for (var i=0; i<mag; i++)
17092                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17093                 new_date = this.moveMonth(new_date, dir);
17094             // ...then reset the day, keeping it in the new month
17095             new_month = new_date.getUTCMonth();
17096             new_date.setUTCDate(day);
17097             test = function(){
17098                 return new_month != new_date.getUTCMonth();
17099             };
17100         }
17101         // Common date-resetting loop -- if date is beyond end of month, make it
17102         // end of month
17103         while (test()){
17104             new_date.setUTCDate(--day);
17105             new_date.setUTCMonth(new_month);
17106         }
17107         return new_date;
17108     },
17109
17110     moveYear: function(date, dir)
17111     {
17112         return this.moveMonth(date, dir*12);
17113     },
17114
17115     dateWithinRange: function(date)
17116     {
17117         return date >= this.startDate && date <= this.endDate;
17118     },
17119
17120     
17121     remove: function() 
17122     {
17123         this.picker().remove();
17124     }
17125    
17126 });
17127
17128 Roo.apply(Roo.bootstrap.DateField,  {
17129     
17130     head : {
17131         tag: 'thead',
17132         cn: [
17133         {
17134             tag: 'tr',
17135             cn: [
17136             {
17137                 tag: 'th',
17138                 cls: 'prev',
17139                 html: '<i class="fa fa-arrow-left"/>'
17140             },
17141             {
17142                 tag: 'th',
17143                 cls: 'switch',
17144                 colspan: '5'
17145             },
17146             {
17147                 tag: 'th',
17148                 cls: 'next',
17149                 html: '<i class="fa fa-arrow-right"/>'
17150             }
17151
17152             ]
17153         }
17154         ]
17155     },
17156     
17157     content : {
17158         tag: 'tbody',
17159         cn: [
17160         {
17161             tag: 'tr',
17162             cn: [
17163             {
17164                 tag: 'td',
17165                 colspan: '7'
17166             }
17167             ]
17168         }
17169         ]
17170     },
17171     
17172     footer : {
17173         tag: 'tfoot',
17174         cn: [
17175         {
17176             tag: 'tr',
17177             cn: [
17178             {
17179                 tag: 'th',
17180                 colspan: '7',
17181                 cls: 'today'
17182             }
17183                     
17184             ]
17185         }
17186         ]
17187     },
17188     
17189     dates:{
17190         en: {
17191             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17192             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17193             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17194             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17195             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17196             today: "Today"
17197         }
17198     },
17199     
17200     modes: [
17201     {
17202         clsName: 'days',
17203         navFnc: 'Month',
17204         navStep: 1
17205     },
17206     {
17207         clsName: 'months',
17208         navFnc: 'FullYear',
17209         navStep: 1
17210     },
17211     {
17212         clsName: 'years',
17213         navFnc: 'FullYear',
17214         navStep: 10
17215     }]
17216 });
17217
17218 Roo.apply(Roo.bootstrap.DateField,  {
17219   
17220     template : {
17221         tag: 'div',
17222         cls: 'datepicker dropdown-menu roo-dynamic',
17223         cn: [
17224         {
17225             tag: 'div',
17226             cls: 'datepicker-days',
17227             cn: [
17228             {
17229                 tag: 'table',
17230                 cls: 'table-condensed',
17231                 cn:[
17232                 Roo.bootstrap.DateField.head,
17233                 {
17234                     tag: 'tbody'
17235                 },
17236                 Roo.bootstrap.DateField.footer
17237                 ]
17238             }
17239             ]
17240         },
17241         {
17242             tag: 'div',
17243             cls: 'datepicker-months',
17244             cn: [
17245             {
17246                 tag: 'table',
17247                 cls: 'table-condensed',
17248                 cn:[
17249                 Roo.bootstrap.DateField.head,
17250                 Roo.bootstrap.DateField.content,
17251                 Roo.bootstrap.DateField.footer
17252                 ]
17253             }
17254             ]
17255         },
17256         {
17257             tag: 'div',
17258             cls: 'datepicker-years',
17259             cn: [
17260             {
17261                 tag: 'table',
17262                 cls: 'table-condensed',
17263                 cn:[
17264                 Roo.bootstrap.DateField.head,
17265                 Roo.bootstrap.DateField.content,
17266                 Roo.bootstrap.DateField.footer
17267                 ]
17268             }
17269             ]
17270         }
17271         ]
17272     }
17273 });
17274
17275  
17276
17277  /*
17278  * - LGPL
17279  *
17280  * TimeField
17281  * 
17282  */
17283
17284 /**
17285  * @class Roo.bootstrap.TimeField
17286  * @extends Roo.bootstrap.Input
17287  * Bootstrap DateField class
17288  * 
17289  * 
17290  * @constructor
17291  * Create a new TimeField
17292  * @param {Object} config The config object
17293  */
17294
17295 Roo.bootstrap.TimeField = function(config){
17296     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17297     this.addEvents({
17298             /**
17299              * @event show
17300              * Fires when this field show.
17301              * @param {Roo.bootstrap.DateField} thisthis
17302              * @param {Mixed} date The date value
17303              */
17304             show : true,
17305             /**
17306              * @event show
17307              * Fires when this field hide.
17308              * @param {Roo.bootstrap.DateField} this
17309              * @param {Mixed} date The date value
17310              */
17311             hide : true,
17312             /**
17313              * @event select
17314              * Fires when select a date.
17315              * @param {Roo.bootstrap.DateField} this
17316              * @param {Mixed} date The date value
17317              */
17318             select : true
17319         });
17320 };
17321
17322 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17323     
17324     /**
17325      * @cfg {String} format
17326      * The default time format string which can be overriden for localization support.  The format must be
17327      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17328      */
17329     format : "H:i",
17330        
17331     onRender: function(ct, position)
17332     {
17333         
17334         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17335                 
17336         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17337         
17338         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17339         
17340         this.pop = this.picker().select('>.datepicker-time',true).first();
17341         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17342         
17343         this.picker().on('mousedown', this.onMousedown, this);
17344         this.picker().on('click', this.onClick, this);
17345         
17346         this.picker().addClass('datepicker-dropdown');
17347     
17348         this.fillTime();
17349         this.update();
17350             
17351         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17352         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17353         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17354         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17355         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17356         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17357
17358     },
17359     
17360     fireKey: function(e){
17361         if (!this.picker().isVisible()){
17362             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17363                 this.show();
17364             }
17365             return;
17366         }
17367
17368         e.preventDefault();
17369         
17370         switch(e.keyCode){
17371             case 27: // escape
17372                 this.hide();
17373                 break;
17374             case 37: // left
17375             case 39: // right
17376                 this.onTogglePeriod();
17377                 break;
17378             case 38: // up
17379                 this.onIncrementMinutes();
17380                 break;
17381             case 40: // down
17382                 this.onDecrementMinutes();
17383                 break;
17384             case 13: // enter
17385             case 9: // tab
17386                 this.setTime();
17387                 break;
17388         }
17389     },
17390     
17391     onClick: function(e) {
17392         e.stopPropagation();
17393         e.preventDefault();
17394     },
17395     
17396     picker : function()
17397     {
17398         return this.el.select('.datepicker', true).first();
17399     },
17400     
17401     fillTime: function()
17402     {    
17403         var time = this.pop.select('tbody', true).first();
17404         
17405         time.dom.innerHTML = '';
17406         
17407         time.createChild({
17408             tag: 'tr',
17409             cn: [
17410                 {
17411                     tag: 'td',
17412                     cn: [
17413                         {
17414                             tag: 'a',
17415                             href: '#',
17416                             cls: 'btn',
17417                             cn: [
17418                                 {
17419                                     tag: 'span',
17420                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17421                                 }
17422                             ]
17423                         } 
17424                     ]
17425                 },
17426                 {
17427                     tag: 'td',
17428                     cls: 'separator'
17429                 },
17430                 {
17431                     tag: 'td',
17432                     cn: [
17433                         {
17434                             tag: 'a',
17435                             href: '#',
17436                             cls: 'btn',
17437                             cn: [
17438                                 {
17439                                     tag: 'span',
17440                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17441                                 }
17442                             ]
17443                         }
17444                     ]
17445                 },
17446                 {
17447                     tag: 'td',
17448                     cls: 'separator'
17449                 }
17450             ]
17451         });
17452         
17453         time.createChild({
17454             tag: 'tr',
17455             cn: [
17456                 {
17457                     tag: 'td',
17458                     cn: [
17459                         {
17460                             tag: 'span',
17461                             cls: 'timepicker-hour',
17462                             html: '00'
17463                         }  
17464                     ]
17465                 },
17466                 {
17467                     tag: 'td',
17468                     cls: 'separator',
17469                     html: ':'
17470                 },
17471                 {
17472                     tag: 'td',
17473                     cn: [
17474                         {
17475                             tag: 'span',
17476                             cls: 'timepicker-minute',
17477                             html: '00'
17478                         }  
17479                     ]
17480                 },
17481                 {
17482                     tag: 'td',
17483                     cls: 'separator'
17484                 },
17485                 {
17486                     tag: 'td',
17487                     cn: [
17488                         {
17489                             tag: 'button',
17490                             type: 'button',
17491                             cls: 'btn btn-primary period',
17492                             html: 'AM'
17493                             
17494                         }
17495                     ]
17496                 }
17497             ]
17498         });
17499         
17500         time.createChild({
17501             tag: 'tr',
17502             cn: [
17503                 {
17504                     tag: 'td',
17505                     cn: [
17506                         {
17507                             tag: 'a',
17508                             href: '#',
17509                             cls: 'btn',
17510                             cn: [
17511                                 {
17512                                     tag: 'span',
17513                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17514                                 }
17515                             ]
17516                         }
17517                     ]
17518                 },
17519                 {
17520                     tag: 'td',
17521                     cls: 'separator'
17522                 },
17523                 {
17524                     tag: 'td',
17525                     cn: [
17526                         {
17527                             tag: 'a',
17528                             href: '#',
17529                             cls: 'btn',
17530                             cn: [
17531                                 {
17532                                     tag: 'span',
17533                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17534                                 }
17535                             ]
17536                         }
17537                     ]
17538                 },
17539                 {
17540                     tag: 'td',
17541                     cls: 'separator'
17542                 }
17543             ]
17544         });
17545         
17546     },
17547     
17548     update: function()
17549     {
17550         
17551         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17552         
17553         this.fill();
17554     },
17555     
17556     fill: function() 
17557     {
17558         var hours = this.time.getHours();
17559         var minutes = this.time.getMinutes();
17560         var period = 'AM';
17561         
17562         if(hours > 11){
17563             period = 'PM';
17564         }
17565         
17566         if(hours == 0){
17567             hours = 12;
17568         }
17569         
17570         
17571         if(hours > 12){
17572             hours = hours - 12;
17573         }
17574         
17575         if(hours < 10){
17576             hours = '0' + hours;
17577         }
17578         
17579         if(minutes < 10){
17580             minutes = '0' + minutes;
17581         }
17582         
17583         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17584         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17585         this.pop.select('button', true).first().dom.innerHTML = period;
17586         
17587     },
17588     
17589     place: function()
17590     {   
17591         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17592         
17593         var cls = ['bottom'];
17594         
17595         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17596             cls.pop();
17597             cls.push('top');
17598         }
17599         
17600         cls.push('right');
17601         
17602         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17603             cls.pop();
17604             cls.push('left');
17605         }
17606         
17607         this.picker().addClass(cls.join('-'));
17608         
17609         var _this = this;
17610         
17611         Roo.each(cls, function(c){
17612             if(c == 'bottom'){
17613                 _this.picker().setTop(_this.inputEl().getHeight());
17614                 return;
17615             }
17616             if(c == 'top'){
17617                 _this.picker().setTop(0 - _this.picker().getHeight());
17618                 return;
17619             }
17620             
17621             if(c == 'left'){
17622                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17623                 return;
17624             }
17625             if(c == 'right'){
17626                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17627                 return;
17628             }
17629         });
17630         
17631     },
17632   
17633     onFocus : function()
17634     {
17635         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17636         this.show();
17637     },
17638     
17639     onBlur : function()
17640     {
17641         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17642         this.hide();
17643     },
17644     
17645     show : function()
17646     {
17647         this.picker().show();
17648         this.pop.show();
17649         this.update();
17650         this.place();
17651         
17652         this.fireEvent('show', this, this.date);
17653     },
17654     
17655     hide : function()
17656     {
17657         this.picker().hide();
17658         this.pop.hide();
17659         
17660         this.fireEvent('hide', this, this.date);
17661     },
17662     
17663     setTime : function()
17664     {
17665         this.hide();
17666         this.setValue(this.time.format(this.format));
17667         
17668         this.fireEvent('select', this, this.date);
17669         
17670         
17671     },
17672     
17673     onMousedown: function(e){
17674         e.stopPropagation();
17675         e.preventDefault();
17676     },
17677     
17678     onIncrementHours: function()
17679     {
17680         Roo.log('onIncrementHours');
17681         this.time = this.time.add(Date.HOUR, 1);
17682         this.update();
17683         
17684     },
17685     
17686     onDecrementHours: function()
17687     {
17688         Roo.log('onDecrementHours');
17689         this.time = this.time.add(Date.HOUR, -1);
17690         this.update();
17691     },
17692     
17693     onIncrementMinutes: function()
17694     {
17695         Roo.log('onIncrementMinutes');
17696         this.time = this.time.add(Date.MINUTE, 1);
17697         this.update();
17698     },
17699     
17700     onDecrementMinutes: function()
17701     {
17702         Roo.log('onDecrementMinutes');
17703         this.time = this.time.add(Date.MINUTE, -1);
17704         this.update();
17705     },
17706     
17707     onTogglePeriod: function()
17708     {
17709         Roo.log('onTogglePeriod');
17710         this.time = this.time.add(Date.HOUR, 12);
17711         this.update();
17712     }
17713     
17714    
17715 });
17716
17717 Roo.apply(Roo.bootstrap.TimeField,  {
17718     
17719     content : {
17720         tag: 'tbody',
17721         cn: [
17722             {
17723                 tag: 'tr',
17724                 cn: [
17725                 {
17726                     tag: 'td',
17727                     colspan: '7'
17728                 }
17729                 ]
17730             }
17731         ]
17732     },
17733     
17734     footer : {
17735         tag: 'tfoot',
17736         cn: [
17737             {
17738                 tag: 'tr',
17739                 cn: [
17740                 {
17741                     tag: 'th',
17742                     colspan: '7',
17743                     cls: '',
17744                     cn: [
17745                         {
17746                             tag: 'button',
17747                             cls: 'btn btn-info ok',
17748                             html: 'OK'
17749                         }
17750                     ]
17751                 }
17752
17753                 ]
17754             }
17755         ]
17756     }
17757 });
17758
17759 Roo.apply(Roo.bootstrap.TimeField,  {
17760   
17761     template : {
17762         tag: 'div',
17763         cls: 'datepicker dropdown-menu',
17764         cn: [
17765             {
17766                 tag: 'div',
17767                 cls: 'datepicker-time',
17768                 cn: [
17769                 {
17770                     tag: 'table',
17771                     cls: 'table-condensed',
17772                     cn:[
17773                     Roo.bootstrap.TimeField.content,
17774                     Roo.bootstrap.TimeField.footer
17775                     ]
17776                 }
17777                 ]
17778             }
17779         ]
17780     }
17781 });
17782
17783  
17784
17785  /*
17786  * - LGPL
17787  *
17788  * MonthField
17789  * 
17790  */
17791
17792 /**
17793  * @class Roo.bootstrap.MonthField
17794  * @extends Roo.bootstrap.Input
17795  * Bootstrap MonthField class
17796  * 
17797  * @cfg {String} language default en
17798  * 
17799  * @constructor
17800  * Create a new MonthField
17801  * @param {Object} config The config object
17802  */
17803
17804 Roo.bootstrap.MonthField = function(config){
17805     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17806     
17807     this.addEvents({
17808         /**
17809          * @event show
17810          * Fires when this field show.
17811          * @param {Roo.bootstrap.MonthField} this
17812          * @param {Mixed} date The date value
17813          */
17814         show : true,
17815         /**
17816          * @event show
17817          * Fires when this field hide.
17818          * @param {Roo.bootstrap.MonthField} this
17819          * @param {Mixed} date The date value
17820          */
17821         hide : true,
17822         /**
17823          * @event select
17824          * Fires when select a date.
17825          * @param {Roo.bootstrap.MonthField} this
17826          * @param {String} oldvalue The old value
17827          * @param {String} newvalue The new value
17828          */
17829         select : true
17830     });
17831 };
17832
17833 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17834     
17835     onRender: function(ct, position)
17836     {
17837         
17838         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17839         
17840         this.language = this.language || 'en';
17841         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17842         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17843         
17844         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17845         this.isInline = false;
17846         this.isInput = true;
17847         this.component = this.el.select('.add-on', true).first() || false;
17848         this.component = (this.component && this.component.length === 0) ? false : this.component;
17849         this.hasInput = this.component && this.inputEL().length;
17850         
17851         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17852         
17853         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17854         
17855         this.picker().on('mousedown', this.onMousedown, this);
17856         this.picker().on('click', this.onClick, this);
17857         
17858         this.picker().addClass('datepicker-dropdown');
17859         
17860         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17861             v.setStyle('width', '189px');
17862         });
17863         
17864         this.fillMonths();
17865         
17866         this.update();
17867         
17868         if(this.isInline) {
17869             this.show();
17870         }
17871         
17872     },
17873     
17874     setValue: function(v, suppressEvent)
17875     {   
17876         var o = this.getValue();
17877         
17878         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17879         
17880         this.update();
17881
17882         if(suppressEvent !== true){
17883             this.fireEvent('select', this, o, v);
17884         }
17885         
17886     },
17887     
17888     getValue: function()
17889     {
17890         return this.value;
17891     },
17892     
17893     onClick: function(e) 
17894     {
17895         e.stopPropagation();
17896         e.preventDefault();
17897         
17898         var target = e.getTarget();
17899         
17900         if(target.nodeName.toLowerCase() === 'i'){
17901             target = Roo.get(target).dom.parentNode;
17902         }
17903         
17904         var nodeName = target.nodeName;
17905         var className = target.className;
17906         var html = target.innerHTML;
17907         
17908         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17909             return;
17910         }
17911         
17912         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17913         
17914         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17915         
17916         this.hide();
17917                         
17918     },
17919     
17920     picker : function()
17921     {
17922         return this.pickerEl;
17923     },
17924     
17925     fillMonths: function()
17926     {    
17927         var i = 0;
17928         var months = this.picker().select('>.datepicker-months td', true).first();
17929         
17930         months.dom.innerHTML = '';
17931         
17932         while (i < 12) {
17933             var month = {
17934                 tag: 'span',
17935                 cls: 'month',
17936                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17937             }
17938             
17939             months.createChild(month);
17940         }
17941         
17942     },
17943     
17944     update: function()
17945     {
17946         var _this = this;
17947         
17948         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17949             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17950         }
17951         
17952         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17953             e.removeClass('active');
17954             
17955             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17956                 e.addClass('active');
17957             }
17958         })
17959     },
17960     
17961     place: function()
17962     {
17963         if(this.isInline) return;
17964         
17965         this.picker().removeClass(['bottom', 'top']);
17966         
17967         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17968             /*
17969              * place to the top of element!
17970              *
17971              */
17972             
17973             this.picker().addClass('top');
17974             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17975             
17976             return;
17977         }
17978         
17979         this.picker().addClass('bottom');
17980         
17981         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17982     },
17983     
17984     onFocus : function()
17985     {
17986         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17987         this.show();
17988     },
17989     
17990     onBlur : function()
17991     {
17992         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17993         
17994         var d = this.inputEl().getValue();
17995         
17996         this.setValue(d);
17997                 
17998         this.hide();
17999     },
18000     
18001     show : function()
18002     {
18003         this.picker().show();
18004         this.picker().select('>.datepicker-months', true).first().show();
18005         this.update();
18006         this.place();
18007         
18008         this.fireEvent('show', this, this.date);
18009     },
18010     
18011     hide : function()
18012     {
18013         if(this.isInline) return;
18014         this.picker().hide();
18015         this.fireEvent('hide', this, this.date);
18016         
18017     },
18018     
18019     onMousedown: function(e)
18020     {
18021         e.stopPropagation();
18022         e.preventDefault();
18023     },
18024     
18025     keyup: function(e)
18026     {
18027         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18028         this.update();
18029     },
18030
18031     fireKey: function(e)
18032     {
18033         if (!this.picker().isVisible()){
18034             if (e.keyCode == 27) // allow escape to hide and re-show picker
18035                 this.show();
18036             return;
18037         }
18038         
18039         var dir;
18040         
18041         switch(e.keyCode){
18042             case 27: // escape
18043                 this.hide();
18044                 e.preventDefault();
18045                 break;
18046             case 37: // left
18047             case 39: // right
18048                 dir = e.keyCode == 37 ? -1 : 1;
18049                 
18050                 this.vIndex = this.vIndex + dir;
18051                 
18052                 if(this.vIndex < 0){
18053                     this.vIndex = 0;
18054                 }
18055                 
18056                 if(this.vIndex > 11){
18057                     this.vIndex = 11;
18058                 }
18059                 
18060                 if(isNaN(this.vIndex)){
18061                     this.vIndex = 0;
18062                 }
18063                 
18064                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18065                 
18066                 break;
18067             case 38: // up
18068             case 40: // down
18069                 
18070                 dir = e.keyCode == 38 ? -1 : 1;
18071                 
18072                 this.vIndex = this.vIndex + dir * 4;
18073                 
18074                 if(this.vIndex < 0){
18075                     this.vIndex = 0;
18076                 }
18077                 
18078                 if(this.vIndex > 11){
18079                     this.vIndex = 11;
18080                 }
18081                 
18082                 if(isNaN(this.vIndex)){
18083                     this.vIndex = 0;
18084                 }
18085                 
18086                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18087                 break;
18088                 
18089             case 13: // enter
18090                 
18091                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18092                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18093                 }
18094                 
18095                 this.hide();
18096                 e.preventDefault();
18097                 break;
18098             case 9: // tab
18099                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18100                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18101                 }
18102                 this.hide();
18103                 break;
18104             case 16: // shift
18105             case 17: // ctrl
18106             case 18: // alt
18107                 break;
18108             default :
18109                 this.hide();
18110                 
18111         }
18112     },
18113     
18114     remove: function() 
18115     {
18116         this.picker().remove();
18117     }
18118    
18119 });
18120
18121 Roo.apply(Roo.bootstrap.MonthField,  {
18122     
18123     content : {
18124         tag: 'tbody',
18125         cn: [
18126         {
18127             tag: 'tr',
18128             cn: [
18129             {
18130                 tag: 'td',
18131                 colspan: '7'
18132             }
18133             ]
18134         }
18135         ]
18136     },
18137     
18138     dates:{
18139         en: {
18140             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18141             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18142         }
18143     }
18144 });
18145
18146 Roo.apply(Roo.bootstrap.MonthField,  {
18147   
18148     template : {
18149         tag: 'div',
18150         cls: 'datepicker dropdown-menu roo-dynamic',
18151         cn: [
18152             {
18153                 tag: 'div',
18154                 cls: 'datepicker-months',
18155                 cn: [
18156                 {
18157                     tag: 'table',
18158                     cls: 'table-condensed',
18159                     cn:[
18160                         Roo.bootstrap.DateField.content
18161                     ]
18162                 }
18163                 ]
18164             }
18165         ]
18166     }
18167 });
18168
18169  
18170
18171  
18172  /*
18173  * - LGPL
18174  *
18175  * CheckBox
18176  * 
18177  */
18178
18179 /**
18180  * @class Roo.bootstrap.CheckBox
18181  * @extends Roo.bootstrap.Input
18182  * Bootstrap CheckBox class
18183  * 
18184  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18185  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18186  * @cfg {String} boxLabel The text that appears beside the checkbox
18187  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18188  * @cfg {Boolean} checked initnal the element
18189  * @cfg {Boolean} inline inline the element (default false)
18190  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18191  * 
18192  * @constructor
18193  * Create a new CheckBox
18194  * @param {Object} config The config object
18195  */
18196
18197 Roo.bootstrap.CheckBox = function(config){
18198     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18199    
18200     this.addEvents({
18201         /**
18202         * @event check
18203         * Fires when the element is checked or unchecked.
18204         * @param {Roo.bootstrap.CheckBox} this This input
18205         * @param {Boolean} checked The new checked value
18206         */
18207        check : true
18208     });
18209     
18210 };
18211
18212 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18213   
18214     inputType: 'checkbox',
18215     inputValue: 1,
18216     valueOff: 0,
18217     boxLabel: false,
18218     checked: false,
18219     weight : false,
18220     inline: false,
18221     
18222     getAutoCreate : function()
18223     {
18224         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18225         
18226         var id = Roo.id();
18227         
18228         var cfg = {};
18229         
18230         cfg.cls = 'form-group ' + this.inputType; //input-group
18231         
18232         if(this.inline){
18233             cfg.cls += ' ' + this.inputType + '-inline';
18234         }
18235         
18236         var input =  {
18237             tag: 'input',
18238             id : id,
18239             type : this.inputType,
18240             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18241             cls : 'roo-' + this.inputType, //'form-box',
18242             placeholder : this.placeholder || ''
18243             
18244         };
18245         
18246         if (this.weight) { // Validity check?
18247             cfg.cls += " " + this.inputType + "-" + this.weight;
18248         }
18249         
18250         if (this.disabled) {
18251             input.disabled=true;
18252         }
18253         
18254         if(this.checked){
18255             input.checked = this.checked;
18256         }
18257         
18258         if (this.name) {
18259             input.name = this.name;
18260         }
18261         
18262         if (this.size) {
18263             input.cls += ' input-' + this.size;
18264         }
18265         
18266         var settings=this;
18267         
18268         ['xs','sm','md','lg'].map(function(size){
18269             if (settings[size]) {
18270                 cfg.cls += ' col-' + size + '-' + settings[size];
18271             }
18272         });
18273         
18274         var inputblock = input;
18275          
18276         if (this.before || this.after) {
18277             
18278             inputblock = {
18279                 cls : 'input-group',
18280                 cn :  [] 
18281             };
18282             
18283             if (this.before) {
18284                 inputblock.cn.push({
18285                     tag :'span',
18286                     cls : 'input-group-addon',
18287                     html : this.before
18288                 });
18289             }
18290             
18291             inputblock.cn.push(input);
18292             
18293             if (this.after) {
18294                 inputblock.cn.push({
18295                     tag :'span',
18296                     cls : 'input-group-addon',
18297                     html : this.after
18298                 });
18299             }
18300             
18301         }
18302         
18303         if (align ==='left' && this.fieldLabel.length) {
18304                 Roo.log("left and has label");
18305                 cfg.cn = [
18306                     
18307                     {
18308                         tag: 'label',
18309                         'for' :  id,
18310                         cls : 'control-label col-md-' + this.labelWidth,
18311                         html : this.fieldLabel
18312                         
18313                     },
18314                     {
18315                         cls : "col-md-" + (12 - this.labelWidth), 
18316                         cn: [
18317                             inputblock
18318                         ]
18319                     }
18320                     
18321                 ];
18322         } else if ( this.fieldLabel.length) {
18323                 Roo.log(" label");
18324                 cfg.cn = [
18325                    
18326                     {
18327                         tag: this.boxLabel ? 'span' : 'label',
18328                         'for': id,
18329                         cls: 'control-label box-input-label',
18330                         //cls : 'input-group-addon',
18331                         html : this.fieldLabel
18332                         
18333                     },
18334                     
18335                     inputblock
18336                     
18337                 ];
18338
18339         } else {
18340             
18341                 Roo.log(" no label && no align");
18342                 cfg.cn = [  inputblock ] ;
18343                 
18344                 
18345         }
18346         if(this.boxLabel){
18347              var boxLabelCfg = {
18348                 tag: 'label',
18349                 //'for': id, // box label is handled by onclick - so no for...
18350                 cls: 'box-label',
18351                 html: this.boxLabel
18352             }
18353             
18354             if(this.tooltip){
18355                 boxLabelCfg.tooltip = this.tooltip;
18356             }
18357              
18358             cfg.cn.push(boxLabelCfg);
18359         }
18360         
18361         
18362        
18363         return cfg;
18364         
18365     },
18366     
18367     /**
18368      * return the real input element.
18369      */
18370     inputEl: function ()
18371     {
18372         return this.el.select('input.roo-' + this.inputType,true).first();
18373     },
18374     
18375     labelEl: function()
18376     {
18377         return this.el.select('label.control-label',true).first();
18378     },
18379     /* depricated... */
18380     
18381     label: function()
18382     {
18383         return this.labelEl();
18384     },
18385     
18386     initEvents : function()
18387     {
18388 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18389         
18390         this.inputEl().on('click', this.onClick,  this);
18391         
18392         if (this.boxLabel) { 
18393             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18394         }
18395         
18396         this.startValue = this.getValue();
18397         
18398         if(this.groupId){
18399             Roo.bootstrap.CheckBox.register(this);
18400         }
18401     },
18402     
18403     onClick : function()
18404     {   
18405         this.setChecked(!this.checked);
18406     },
18407     
18408     setChecked : function(state,suppressEvent)
18409     {
18410         this.startValue = this.getValue();
18411         
18412         if(this.inputType == 'radio'){
18413             
18414             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18415                 e.dom.checked = false;
18416             });
18417             
18418             this.inputEl().dom.checked = true;
18419             
18420             this.inputEl().dom.value = this.inputValue;
18421             
18422             if(suppressEvent !== true){
18423                 this.fireEvent('check', this, true);
18424             }
18425             
18426             this.validate();
18427             
18428             return;
18429         }
18430         
18431         this.checked = state;
18432         
18433         this.inputEl().dom.checked = state;
18434         
18435         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18436         
18437         if(suppressEvent !== true){
18438             this.fireEvent('check', this, state);
18439         }
18440         
18441         this.validate();
18442     },
18443     
18444     getValue : function()
18445     {
18446         if(this.inputType == 'radio'){
18447             return this.getGroupValue();
18448         }
18449         
18450         return this.inputEl().getValue();
18451         
18452     },
18453     
18454     getGroupValue : function()
18455     {
18456         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18457             return '';
18458         }
18459         
18460         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18461     },
18462     
18463     setValue : function(v,suppressEvent)
18464     {
18465         if(this.inputType == 'radio'){
18466             this.setGroupValue(v, suppressEvent);
18467             return;
18468         }
18469         
18470         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18471         
18472         this.validate();
18473     },
18474     
18475     setGroupValue : function(v, suppressEvent)
18476     {
18477         this.startValue = this.getValue();
18478         
18479         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18480             e.dom.checked = false;
18481             
18482             if(e.dom.value == v){
18483                 e.dom.checked = true;
18484             }
18485         });
18486         
18487         if(suppressEvent !== true){
18488             this.fireEvent('check', this, true);
18489         }
18490
18491         this.validate();
18492         
18493         return;
18494     },
18495     
18496     validate : function()
18497     {
18498         if(
18499                 this.disabled || 
18500                 (this.inputType == 'radio' && this.validateRadio()) ||
18501                 (this.inputType == 'checkbox' && this.validateCheckbox())
18502         ){
18503             this.markValid();
18504             return true;
18505         }
18506         
18507         this.markInvalid();
18508         return false;
18509     },
18510     
18511     validateRadio : function()
18512     {
18513         var valid = false;
18514         
18515         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18516             if(!e.dom.checked){
18517                 return;
18518             }
18519             
18520             valid = true;
18521             
18522             return false;
18523         });
18524         
18525         return valid;
18526     },
18527     
18528     validateCheckbox : function()
18529     {
18530         if(!this.groupId){
18531             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18532         }
18533         
18534         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18535         
18536         if(!group){
18537             return false;
18538         }
18539         
18540         var r = false;
18541         
18542         for(var i in group){
18543             if(r){
18544                 break;
18545             }
18546             
18547             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18548         }
18549         
18550         return r;
18551     },
18552     
18553     /**
18554      * Mark this field as valid
18555      */
18556     markValid : function()
18557     {
18558         if(this.allowBlank){
18559             return;
18560         }
18561         
18562         var _this = this;
18563         
18564         this.fireEvent('valid', this);
18565         
18566         if(this.inputType == 'radio'){
18567             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18568                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18569                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18570             });
18571             
18572             return;
18573         }
18574         
18575         if(!this.groupId){
18576             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18577             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18578             return;
18579         }
18580         
18581         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18582             
18583         if(!group){
18584             return;
18585         }
18586         
18587         for(var i in group){
18588             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18589             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18590         }
18591     },
18592     
18593      /**
18594      * Mark this field as invalid
18595      * @param {String} msg The validation message
18596      */
18597     markInvalid : function(msg)
18598     {
18599         if(this.allowBlank){
18600             return;
18601         }
18602         
18603         var _this = this;
18604         
18605         this.fireEvent('invalid', this, msg);
18606         
18607         if(this.inputType == 'radio'){
18608             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18609                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18610                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18611             });
18612             
18613             return;
18614         }
18615         
18616         if(!this.groupId){
18617             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18618             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18619             return;
18620         }
18621         
18622         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18623             
18624         if(!group){
18625             return;
18626         }
18627         
18628         for(var i in group){
18629             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18630             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18631         }
18632         
18633     }
18634     
18635 });
18636
18637 Roo.apply(Roo.bootstrap.CheckBox, {
18638     
18639     groups: {},
18640     
18641      /**
18642     * register a CheckBox Group
18643     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18644     */
18645     register : function(checkbox)
18646     {
18647         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18648             this.groups[checkbox.groupId] = {};
18649         }
18650         
18651         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18652             return;
18653         }
18654         
18655         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18656         
18657     },
18658     /**
18659     * fetch a CheckBox Group based on the group ID
18660     * @param {string} the group ID
18661     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18662     */
18663     get: function(groupId) {
18664         if (typeof(this.groups[groupId]) == 'undefined') {
18665             return false;
18666         }
18667         
18668         return this.groups[groupId] ;
18669     }
18670     
18671     
18672 });
18673 /*
18674  * - LGPL
18675  *
18676  * Radio
18677  *
18678  *
18679  * not inline
18680  *<div class="radio">
18681   <label>
18682     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18683     Option one is this and that&mdash;be sure to include why it's great
18684   </label>
18685 </div>
18686  *
18687  *
18688  *inline
18689  *<span>
18690  *<label class="radio-inline">fieldLabel</label>
18691  *<label class="radio-inline">
18692   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18693 </label>
18694 <span>
18695  * 
18696  * 
18697  */
18698
18699 /**
18700  * @class Roo.bootstrap.Radio
18701  * @extends Roo.bootstrap.CheckBox
18702  * Bootstrap Radio class
18703
18704  * @constructor
18705  * Create a new Radio
18706  * @param {Object} config The config object
18707  */
18708
18709 Roo.bootstrap.Radio = function(config){
18710     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18711    
18712 };
18713
18714 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18715     
18716     inputType: 'radio',
18717     inputValue: '',
18718     valueOff: '',
18719     
18720     getAutoCreate : function()
18721     {
18722         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18723         align = align || 'left'; // default...
18724         
18725         
18726         
18727         var id = Roo.id();
18728         
18729         var cfg = {
18730                 tag : this.inline ? 'span' : 'div',
18731                 cls : '',
18732                 cn : []
18733         };
18734         
18735         var inline = this.inline ? ' radio-inline' : '';
18736         
18737         var lbl = {
18738                 tag: 'label' ,
18739                 // does not need for, as we wrap the input with it..
18740                 'for' : id,
18741                 cls : 'control-label box-label' + inline,
18742                 cn : []
18743         };
18744         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18745         
18746         var fieldLabel = {
18747             tag: 'label' ,
18748             //cls : 'control-label' + inline,
18749             html : this.fieldLabel,
18750             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18751         };
18752         
18753  
18754         
18755         
18756         var input =  {
18757             tag: 'input',
18758             id : id,
18759             type : this.inputType,
18760             //value : (!this.checked) ? this.valueOff : this.inputValue,
18761             value : this.inputValue,
18762             cls : 'roo-radio',
18763             placeholder : this.placeholder || '' // ?? needed????
18764             
18765         };
18766         if (this.weight) { // Validity check?
18767             input.cls += " radio-" + this.weight;
18768         }
18769         if (this.disabled) {
18770             input.disabled=true;
18771         }
18772         
18773         if(this.checked){
18774             input.checked = this.checked;
18775         }
18776         
18777         if (this.name) {
18778             input.name = this.name;
18779         }
18780         
18781         if (this.size) {
18782             input.cls += ' input-' + this.size;
18783         }
18784         
18785         //?? can span's inline have a width??
18786         
18787         var settings=this;
18788         ['xs','sm','md','lg'].map(function(size){
18789             if (settings[size]) {
18790                 cfg.cls += ' col-' + size + '-' + settings[size];
18791             }
18792         });
18793         
18794         var inputblock = input;
18795         
18796         if (this.before || this.after) {
18797             
18798             inputblock = {
18799                 cls : 'input-group',
18800                 tag : 'span',
18801                 cn :  [] 
18802             };
18803             if (this.before) {
18804                 inputblock.cn.push({
18805                     tag :'span',
18806                     cls : 'input-group-addon',
18807                     html : this.before
18808                 });
18809             }
18810             inputblock.cn.push(input);
18811             if (this.after) {
18812                 inputblock.cn.push({
18813                     tag :'span',
18814                     cls : 'input-group-addon',
18815                     html : this.after
18816                 });
18817             }
18818             
18819         };
18820         
18821         
18822         if (this.fieldLabel && this.fieldLabel.length) {
18823             cfg.cn.push(fieldLabel);
18824         }
18825        
18826         // normal bootstrap puts the input inside the label.
18827         // however with our styled version - it has to go after the input.
18828        
18829         //lbl.cn.push(inputblock);
18830         
18831         var lblwrap =  {
18832             tag: 'span',
18833             cls: 'radio' + inline,
18834             cn: [
18835                 inputblock,
18836                 lbl
18837             ]
18838         };
18839         
18840         cfg.cn.push( lblwrap);
18841         
18842         if(this.boxLabel){
18843             lbl.cn.push({
18844                 tag: 'span',
18845                 html: this.boxLabel
18846             })
18847         }
18848          
18849         
18850         return cfg;
18851         
18852     },
18853     
18854     initEvents : function()
18855     {
18856 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18857         
18858         this.inputEl().on('click', this.onClick,  this);
18859         if (this.boxLabel) {
18860             Roo.log('find label')
18861             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18862         }
18863         
18864     },
18865     
18866     inputEl: function ()
18867     {
18868         return this.el.select('input.roo-radio',true).first();
18869     },
18870     onClick : function()
18871     {   
18872         Roo.log("click");
18873         this.setChecked(true);
18874     },
18875     
18876     setChecked : function(state,suppressEvent)
18877     {
18878         if(state){
18879             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18880                 v.dom.checked = false;
18881             });
18882         }
18883         Roo.log(this.inputEl().dom);
18884         this.checked = state;
18885         this.inputEl().dom.checked = state;
18886         
18887         if(suppressEvent !== true){
18888             this.fireEvent('check', this, state);
18889         }
18890         
18891         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18892         
18893     },
18894     
18895     getGroupValue : function()
18896     {
18897         var value = '';
18898         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18899             if(v.dom.checked == true){
18900                 value = v.dom.value;
18901             }
18902         });
18903         
18904         return value;
18905     },
18906     
18907     /**
18908      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18909      * @return {Mixed} value The field value
18910      */
18911     getValue : function(){
18912         return this.getGroupValue();
18913     }
18914     
18915 });
18916
18917  
18918 //<script type="text/javascript">
18919
18920 /*
18921  * Based  Ext JS Library 1.1.1
18922  * Copyright(c) 2006-2007, Ext JS, LLC.
18923  * LGPL
18924  *
18925  */
18926  
18927 /**
18928  * @class Roo.HtmlEditorCore
18929  * @extends Roo.Component
18930  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18931  *
18932  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18933  */
18934
18935 Roo.HtmlEditorCore = function(config){
18936     
18937     
18938     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18939     
18940     
18941     this.addEvents({
18942         /**
18943          * @event initialize
18944          * Fires when the editor is fully initialized (including the iframe)
18945          * @param {Roo.HtmlEditorCore} this
18946          */
18947         initialize: true,
18948         /**
18949          * @event activate
18950          * Fires when the editor is first receives the focus. Any insertion must wait
18951          * until after this event.
18952          * @param {Roo.HtmlEditorCore} this
18953          */
18954         activate: true,
18955          /**
18956          * @event beforesync
18957          * Fires before the textarea is updated with content from the editor iframe. Return false
18958          * to cancel the sync.
18959          * @param {Roo.HtmlEditorCore} this
18960          * @param {String} html
18961          */
18962         beforesync: true,
18963          /**
18964          * @event beforepush
18965          * Fires before the iframe editor is updated with content from the textarea. Return false
18966          * to cancel the push.
18967          * @param {Roo.HtmlEditorCore} this
18968          * @param {String} html
18969          */
18970         beforepush: true,
18971          /**
18972          * @event sync
18973          * Fires when the textarea is updated with content from the editor iframe.
18974          * @param {Roo.HtmlEditorCore} this
18975          * @param {String} html
18976          */
18977         sync: true,
18978          /**
18979          * @event push
18980          * Fires when the iframe editor is updated with content from the textarea.
18981          * @param {Roo.HtmlEditorCore} this
18982          * @param {String} html
18983          */
18984         push: true,
18985         
18986         /**
18987          * @event editorevent
18988          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18989          * @param {Roo.HtmlEditorCore} this
18990          */
18991         editorevent: true
18992         
18993     });
18994     
18995     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18996     
18997     // defaults : white / black...
18998     this.applyBlacklists();
18999     
19000     
19001     
19002 };
19003
19004
19005 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19006
19007
19008      /**
19009      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19010      */
19011     
19012     owner : false,
19013     
19014      /**
19015      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19016      *                        Roo.resizable.
19017      */
19018     resizable : false,
19019      /**
19020      * @cfg {Number} height (in pixels)
19021      */   
19022     height: 300,
19023    /**
19024      * @cfg {Number} width (in pixels)
19025      */   
19026     width: 500,
19027     
19028     /**
19029      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19030      * 
19031      */
19032     stylesheets: false,
19033     
19034     // id of frame..
19035     frameId: false,
19036     
19037     // private properties
19038     validationEvent : false,
19039     deferHeight: true,
19040     initialized : false,
19041     activated : false,
19042     sourceEditMode : false,
19043     onFocus : Roo.emptyFn,
19044     iframePad:3,
19045     hideMode:'offsets',
19046     
19047     clearUp: true,
19048     
19049     // blacklist + whitelisted elements..
19050     black: false,
19051     white: false,
19052      
19053     
19054
19055     /**
19056      * Protected method that will not generally be called directly. It
19057      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19058      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19059      */
19060     getDocMarkup : function(){
19061         // body styles..
19062         var st = '';
19063         
19064         // inherit styels from page...?? 
19065         if (this.stylesheets === false) {
19066             
19067             Roo.get(document.head).select('style').each(function(node) {
19068                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19069             });
19070             
19071             Roo.get(document.head).select('link').each(function(node) { 
19072                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19073             });
19074             
19075         } else if (!this.stylesheets.length) {
19076                 // simple..
19077                 st = '<style type="text/css">' +
19078                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19079                    '</style>';
19080         } else { 
19081             
19082         }
19083         
19084         st +=  '<style type="text/css">' +
19085             'IMG { cursor: pointer } ' +
19086         '</style>';
19087
19088         
19089         return '<html><head>' + st  +
19090             //<style type="text/css">' +
19091             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19092             //'</style>' +
19093             ' </head><body class="roo-htmleditor-body"></body></html>';
19094     },
19095
19096     // private
19097     onRender : function(ct, position)
19098     {
19099         var _t = this;
19100         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19101         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19102         
19103         
19104         this.el.dom.style.border = '0 none';
19105         this.el.dom.setAttribute('tabIndex', -1);
19106         this.el.addClass('x-hidden hide');
19107         
19108         
19109         
19110         if(Roo.isIE){ // fix IE 1px bogus margin
19111             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19112         }
19113        
19114         
19115         this.frameId = Roo.id();
19116         
19117          
19118         
19119         var iframe = this.owner.wrap.createChild({
19120             tag: 'iframe',
19121             cls: 'form-control', // bootstrap..
19122             id: this.frameId,
19123             name: this.frameId,
19124             frameBorder : 'no',
19125             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19126         }, this.el
19127         );
19128         
19129         
19130         this.iframe = iframe.dom;
19131
19132          this.assignDocWin();
19133         
19134         this.doc.designMode = 'on';
19135        
19136         this.doc.open();
19137         this.doc.write(this.getDocMarkup());
19138         this.doc.close();
19139
19140         
19141         var task = { // must defer to wait for browser to be ready
19142             run : function(){
19143                 //console.log("run task?" + this.doc.readyState);
19144                 this.assignDocWin();
19145                 if(this.doc.body || this.doc.readyState == 'complete'){
19146                     try {
19147                         this.doc.designMode="on";
19148                     } catch (e) {
19149                         return;
19150                     }
19151                     Roo.TaskMgr.stop(task);
19152                     this.initEditor.defer(10, this);
19153                 }
19154             },
19155             interval : 10,
19156             duration: 10000,
19157             scope: this
19158         };
19159         Roo.TaskMgr.start(task);
19160
19161     },
19162
19163     // private
19164     onResize : function(w, h)
19165     {
19166          Roo.log('resize: ' +w + ',' + h );
19167         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19168         if(!this.iframe){
19169             return;
19170         }
19171         if(typeof w == 'number'){
19172             
19173             this.iframe.style.width = w + 'px';
19174         }
19175         if(typeof h == 'number'){
19176             
19177             this.iframe.style.height = h + 'px';
19178             if(this.doc){
19179                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19180             }
19181         }
19182         
19183     },
19184
19185     /**
19186      * Toggles the editor between standard and source edit mode.
19187      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19188      */
19189     toggleSourceEdit : function(sourceEditMode){
19190         
19191         this.sourceEditMode = sourceEditMode === true;
19192         
19193         if(this.sourceEditMode){
19194  
19195             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19196             
19197         }else{
19198             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19199             //this.iframe.className = '';
19200             this.deferFocus();
19201         }
19202         //this.setSize(this.owner.wrap.getSize());
19203         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19204     },
19205
19206     
19207   
19208
19209     /**
19210      * Protected method that will not generally be called directly. If you need/want
19211      * custom HTML cleanup, this is the method you should override.
19212      * @param {String} html The HTML to be cleaned
19213      * return {String} The cleaned HTML
19214      */
19215     cleanHtml : function(html){
19216         html = String(html);
19217         if(html.length > 5){
19218             if(Roo.isSafari){ // strip safari nonsense
19219                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19220             }
19221         }
19222         if(html == '&nbsp;'){
19223             html = '';
19224         }
19225         return html;
19226     },
19227
19228     /**
19229      * HTML Editor -> Textarea
19230      * Protected method that will not generally be called directly. Syncs the contents
19231      * of the editor iframe with the textarea.
19232      */
19233     syncValue : function(){
19234         if(this.initialized){
19235             var bd = (this.doc.body || this.doc.documentElement);
19236             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19237             var html = bd.innerHTML;
19238             if(Roo.isSafari){
19239                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19240                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19241                 if(m && m[1]){
19242                     html = '<div style="'+m[0]+'">' + html + '</div>';
19243                 }
19244             }
19245             html = this.cleanHtml(html);
19246             // fix up the special chars.. normaly like back quotes in word...
19247             // however we do not want to do this with chinese..
19248             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19249                 var cc = b.charCodeAt();
19250                 if (
19251                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19252                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19253                     (cc >= 0xf900 && cc < 0xfb00 )
19254                 ) {
19255                         return b;
19256                 }
19257                 return "&#"+cc+";" 
19258             });
19259             if(this.owner.fireEvent('beforesync', this, html) !== false){
19260                 this.el.dom.value = html;
19261                 this.owner.fireEvent('sync', this, html);
19262             }
19263         }
19264     },
19265
19266     /**
19267      * Protected method that will not generally be called directly. Pushes the value of the textarea
19268      * into the iframe editor.
19269      */
19270     pushValue : function(){
19271         if(this.initialized){
19272             var v = this.el.dom.value.trim();
19273             
19274 //            if(v.length < 1){
19275 //                v = '&#160;';
19276 //            }
19277             
19278             if(this.owner.fireEvent('beforepush', this, v) !== false){
19279                 var d = (this.doc.body || this.doc.documentElement);
19280                 d.innerHTML = v;
19281                 this.cleanUpPaste();
19282                 this.el.dom.value = d.innerHTML;
19283                 this.owner.fireEvent('push', this, v);
19284             }
19285         }
19286     },
19287
19288     // private
19289     deferFocus : function(){
19290         this.focus.defer(10, this);
19291     },
19292
19293     // doc'ed in Field
19294     focus : function(){
19295         if(this.win && !this.sourceEditMode){
19296             this.win.focus();
19297         }else{
19298             this.el.focus();
19299         }
19300     },
19301     
19302     assignDocWin: function()
19303     {
19304         var iframe = this.iframe;
19305         
19306          if(Roo.isIE){
19307             this.doc = iframe.contentWindow.document;
19308             this.win = iframe.contentWindow;
19309         } else {
19310 //            if (!Roo.get(this.frameId)) {
19311 //                return;
19312 //            }
19313 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19314 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19315             
19316             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19317                 return;
19318             }
19319             
19320             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19321             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19322         }
19323     },
19324     
19325     // private
19326     initEditor : function(){
19327         //console.log("INIT EDITOR");
19328         this.assignDocWin();
19329         
19330         
19331         
19332         this.doc.designMode="on";
19333         this.doc.open();
19334         this.doc.write(this.getDocMarkup());
19335         this.doc.close();
19336         
19337         var dbody = (this.doc.body || this.doc.documentElement);
19338         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19339         // this copies styles from the containing element into thsi one..
19340         // not sure why we need all of this..
19341         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19342         
19343         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19344         //ss['background-attachment'] = 'fixed'; // w3c
19345         dbody.bgProperties = 'fixed'; // ie
19346         //Roo.DomHelper.applyStyles(dbody, ss);
19347         Roo.EventManager.on(this.doc, {
19348             //'mousedown': this.onEditorEvent,
19349             'mouseup': this.onEditorEvent,
19350             'dblclick': this.onEditorEvent,
19351             'click': this.onEditorEvent,
19352             'keyup': this.onEditorEvent,
19353             buffer:100,
19354             scope: this
19355         });
19356         if(Roo.isGecko){
19357             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19358         }
19359         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19360             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19361         }
19362         this.initialized = true;
19363
19364         this.owner.fireEvent('initialize', this);
19365         this.pushValue();
19366     },
19367
19368     // private
19369     onDestroy : function(){
19370         
19371         
19372         
19373         if(this.rendered){
19374             
19375             //for (var i =0; i < this.toolbars.length;i++) {
19376             //    // fixme - ask toolbars for heights?
19377             //    this.toolbars[i].onDestroy();
19378            // }
19379             
19380             //this.wrap.dom.innerHTML = '';
19381             //this.wrap.remove();
19382         }
19383     },
19384
19385     // private
19386     onFirstFocus : function(){
19387         
19388         this.assignDocWin();
19389         
19390         
19391         this.activated = true;
19392          
19393     
19394         if(Roo.isGecko){ // prevent silly gecko errors
19395             this.win.focus();
19396             var s = this.win.getSelection();
19397             if(!s.focusNode || s.focusNode.nodeType != 3){
19398                 var r = s.getRangeAt(0);
19399                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19400                 r.collapse(true);
19401                 this.deferFocus();
19402             }
19403             try{
19404                 this.execCmd('useCSS', true);
19405                 this.execCmd('styleWithCSS', false);
19406             }catch(e){}
19407         }
19408         this.owner.fireEvent('activate', this);
19409     },
19410
19411     // private
19412     adjustFont: function(btn){
19413         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19414         //if(Roo.isSafari){ // safari
19415         //    adjust *= 2;
19416        // }
19417         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19418         if(Roo.isSafari){ // safari
19419             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19420             v =  (v < 10) ? 10 : v;
19421             v =  (v > 48) ? 48 : v;
19422             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19423             
19424         }
19425         
19426         
19427         v = Math.max(1, v+adjust);
19428         
19429         this.execCmd('FontSize', v  );
19430     },
19431
19432     onEditorEvent : function(e)
19433     {
19434         this.owner.fireEvent('editorevent', this, e);
19435       //  this.updateToolbar();
19436         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19437     },
19438
19439     insertTag : function(tg)
19440     {
19441         // could be a bit smarter... -> wrap the current selected tRoo..
19442         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19443             
19444             range = this.createRange(this.getSelection());
19445             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19446             wrappingNode.appendChild(range.extractContents());
19447             range.insertNode(wrappingNode);
19448
19449             return;
19450             
19451             
19452             
19453         }
19454         this.execCmd("formatblock",   tg);
19455         
19456     },
19457     
19458     insertText : function(txt)
19459     {
19460         
19461         
19462         var range = this.createRange();
19463         range.deleteContents();
19464                //alert(Sender.getAttribute('label'));
19465                
19466         range.insertNode(this.doc.createTextNode(txt));
19467     } ,
19468     
19469      
19470
19471     /**
19472      * Executes a Midas editor command on the editor document and performs necessary focus and
19473      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19474      * @param {String} cmd The Midas command
19475      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19476      */
19477     relayCmd : function(cmd, value){
19478         this.win.focus();
19479         this.execCmd(cmd, value);
19480         this.owner.fireEvent('editorevent', this);
19481         //this.updateToolbar();
19482         this.owner.deferFocus();
19483     },
19484
19485     /**
19486      * Executes a Midas editor command directly on the editor document.
19487      * For visual commands, you should use {@link #relayCmd} instead.
19488      * <b>This should only be called after the editor is initialized.</b>
19489      * @param {String} cmd The Midas command
19490      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19491      */
19492     execCmd : function(cmd, value){
19493         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19494         this.syncValue();
19495     },
19496  
19497  
19498    
19499     /**
19500      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19501      * to insert tRoo.
19502      * @param {String} text | dom node.. 
19503      */
19504     insertAtCursor : function(text)
19505     {
19506         
19507         
19508         
19509         if(!this.activated){
19510             return;
19511         }
19512         /*
19513         if(Roo.isIE){
19514             this.win.focus();
19515             var r = this.doc.selection.createRange();
19516             if(r){
19517                 r.collapse(true);
19518                 r.pasteHTML(text);
19519                 this.syncValue();
19520                 this.deferFocus();
19521             
19522             }
19523             return;
19524         }
19525         */
19526         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19527             this.win.focus();
19528             
19529             
19530             // from jquery ui (MIT licenced)
19531             var range, node;
19532             var win = this.win;
19533             
19534             if (win.getSelection && win.getSelection().getRangeAt) {
19535                 range = win.getSelection().getRangeAt(0);
19536                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19537                 range.insertNode(node);
19538             } else if (win.document.selection && win.document.selection.createRange) {
19539                 // no firefox support
19540                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19541                 win.document.selection.createRange().pasteHTML(txt);
19542             } else {
19543                 // no firefox support
19544                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19545                 this.execCmd('InsertHTML', txt);
19546             } 
19547             
19548             this.syncValue();
19549             
19550             this.deferFocus();
19551         }
19552     },
19553  // private
19554     mozKeyPress : function(e){
19555         if(e.ctrlKey){
19556             var c = e.getCharCode(), cmd;
19557           
19558             if(c > 0){
19559                 c = String.fromCharCode(c).toLowerCase();
19560                 switch(c){
19561                     case 'b':
19562                         cmd = 'bold';
19563                         break;
19564                     case 'i':
19565                         cmd = 'italic';
19566                         break;
19567                     
19568                     case 'u':
19569                         cmd = 'underline';
19570                         break;
19571                     
19572                     case 'v':
19573                         this.cleanUpPaste.defer(100, this);
19574                         return;
19575                         
19576                 }
19577                 if(cmd){
19578                     this.win.focus();
19579                     this.execCmd(cmd);
19580                     this.deferFocus();
19581                     e.preventDefault();
19582                 }
19583                 
19584             }
19585         }
19586     },
19587
19588     // private
19589     fixKeys : function(){ // load time branching for fastest keydown performance
19590         if(Roo.isIE){
19591             return function(e){
19592                 var k = e.getKey(), r;
19593                 if(k == e.TAB){
19594                     e.stopEvent();
19595                     r = this.doc.selection.createRange();
19596                     if(r){
19597                         r.collapse(true);
19598                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19599                         this.deferFocus();
19600                     }
19601                     return;
19602                 }
19603                 
19604                 if(k == e.ENTER){
19605                     r = this.doc.selection.createRange();
19606                     if(r){
19607                         var target = r.parentElement();
19608                         if(!target || target.tagName.toLowerCase() != 'li'){
19609                             e.stopEvent();
19610                             r.pasteHTML('<br />');
19611                             r.collapse(false);
19612                             r.select();
19613                         }
19614                     }
19615                 }
19616                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19617                     this.cleanUpPaste.defer(100, this);
19618                     return;
19619                 }
19620                 
19621                 
19622             };
19623         }else if(Roo.isOpera){
19624             return function(e){
19625                 var k = e.getKey();
19626                 if(k == e.TAB){
19627                     e.stopEvent();
19628                     this.win.focus();
19629                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19630                     this.deferFocus();
19631                 }
19632                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19633                     this.cleanUpPaste.defer(100, this);
19634                     return;
19635                 }
19636                 
19637             };
19638         }else if(Roo.isSafari){
19639             return function(e){
19640                 var k = e.getKey();
19641                 
19642                 if(k == e.TAB){
19643                     e.stopEvent();
19644                     this.execCmd('InsertText','\t');
19645                     this.deferFocus();
19646                     return;
19647                 }
19648                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19649                     this.cleanUpPaste.defer(100, this);
19650                     return;
19651                 }
19652                 
19653              };
19654         }
19655     }(),
19656     
19657     getAllAncestors: function()
19658     {
19659         var p = this.getSelectedNode();
19660         var a = [];
19661         if (!p) {
19662             a.push(p); // push blank onto stack..
19663             p = this.getParentElement();
19664         }
19665         
19666         
19667         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19668             a.push(p);
19669             p = p.parentNode;
19670         }
19671         a.push(this.doc.body);
19672         return a;
19673     },
19674     lastSel : false,
19675     lastSelNode : false,
19676     
19677     
19678     getSelection : function() 
19679     {
19680         this.assignDocWin();
19681         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19682     },
19683     
19684     getSelectedNode: function() 
19685     {
19686         // this may only work on Gecko!!!
19687         
19688         // should we cache this!!!!
19689         
19690         
19691         
19692          
19693         var range = this.createRange(this.getSelection()).cloneRange();
19694         
19695         if (Roo.isIE) {
19696             var parent = range.parentElement();
19697             while (true) {
19698                 var testRange = range.duplicate();
19699                 testRange.moveToElementText(parent);
19700                 if (testRange.inRange(range)) {
19701                     break;
19702                 }
19703                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19704                     break;
19705                 }
19706                 parent = parent.parentElement;
19707             }
19708             return parent;
19709         }
19710         
19711         // is ancestor a text element.
19712         var ac =  range.commonAncestorContainer;
19713         if (ac.nodeType == 3) {
19714             ac = ac.parentNode;
19715         }
19716         
19717         var ar = ac.childNodes;
19718          
19719         var nodes = [];
19720         var other_nodes = [];
19721         var has_other_nodes = false;
19722         for (var i=0;i<ar.length;i++) {
19723             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19724                 continue;
19725             }
19726             // fullly contained node.
19727             
19728             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19729                 nodes.push(ar[i]);
19730                 continue;
19731             }
19732             
19733             // probably selected..
19734             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19735                 other_nodes.push(ar[i]);
19736                 continue;
19737             }
19738             // outer..
19739             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19740                 continue;
19741             }
19742             
19743             
19744             has_other_nodes = true;
19745         }
19746         if (!nodes.length && other_nodes.length) {
19747             nodes= other_nodes;
19748         }
19749         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19750             return false;
19751         }
19752         
19753         return nodes[0];
19754     },
19755     createRange: function(sel)
19756     {
19757         // this has strange effects when using with 
19758         // top toolbar - not sure if it's a great idea.
19759         //this.editor.contentWindow.focus();
19760         if (typeof sel != "undefined") {
19761             try {
19762                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19763             } catch(e) {
19764                 return this.doc.createRange();
19765             }
19766         } else {
19767             return this.doc.createRange();
19768         }
19769     },
19770     getParentElement: function()
19771     {
19772         
19773         this.assignDocWin();
19774         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19775         
19776         var range = this.createRange(sel);
19777          
19778         try {
19779             var p = range.commonAncestorContainer;
19780             while (p.nodeType == 3) { // text node
19781                 p = p.parentNode;
19782             }
19783             return p;
19784         } catch (e) {
19785             return null;
19786         }
19787     
19788     },
19789     /***
19790      *
19791      * Range intersection.. the hard stuff...
19792      *  '-1' = before
19793      *  '0' = hits..
19794      *  '1' = after.
19795      *         [ -- selected range --- ]
19796      *   [fail]                        [fail]
19797      *
19798      *    basically..
19799      *      if end is before start or  hits it. fail.
19800      *      if start is after end or hits it fail.
19801      *
19802      *   if either hits (but other is outside. - then it's not 
19803      *   
19804      *    
19805      **/
19806     
19807     
19808     // @see http://www.thismuchiknow.co.uk/?p=64.
19809     rangeIntersectsNode : function(range, node)
19810     {
19811         var nodeRange = node.ownerDocument.createRange();
19812         try {
19813             nodeRange.selectNode(node);
19814         } catch (e) {
19815             nodeRange.selectNodeContents(node);
19816         }
19817     
19818         var rangeStartRange = range.cloneRange();
19819         rangeStartRange.collapse(true);
19820     
19821         var rangeEndRange = range.cloneRange();
19822         rangeEndRange.collapse(false);
19823     
19824         var nodeStartRange = nodeRange.cloneRange();
19825         nodeStartRange.collapse(true);
19826     
19827         var nodeEndRange = nodeRange.cloneRange();
19828         nodeEndRange.collapse(false);
19829     
19830         return rangeStartRange.compareBoundaryPoints(
19831                  Range.START_TO_START, nodeEndRange) == -1 &&
19832                rangeEndRange.compareBoundaryPoints(
19833                  Range.START_TO_START, nodeStartRange) == 1;
19834         
19835          
19836     },
19837     rangeCompareNode : function(range, node)
19838     {
19839         var nodeRange = node.ownerDocument.createRange();
19840         try {
19841             nodeRange.selectNode(node);
19842         } catch (e) {
19843             nodeRange.selectNodeContents(node);
19844         }
19845         
19846         
19847         range.collapse(true);
19848     
19849         nodeRange.collapse(true);
19850      
19851         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19852         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19853          
19854         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19855         
19856         var nodeIsBefore   =  ss == 1;
19857         var nodeIsAfter    = ee == -1;
19858         
19859         if (nodeIsBefore && nodeIsAfter)
19860             return 0; // outer
19861         if (!nodeIsBefore && nodeIsAfter)
19862             return 1; //right trailed.
19863         
19864         if (nodeIsBefore && !nodeIsAfter)
19865             return 2;  // left trailed.
19866         // fully contined.
19867         return 3;
19868     },
19869
19870     // private? - in a new class?
19871     cleanUpPaste :  function()
19872     {
19873         // cleans up the whole document..
19874         Roo.log('cleanuppaste');
19875         
19876         this.cleanUpChildren(this.doc.body);
19877         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19878         if (clean != this.doc.body.innerHTML) {
19879             this.doc.body.innerHTML = clean;
19880         }
19881         
19882     },
19883     
19884     cleanWordChars : function(input) {// change the chars to hex code
19885         var he = Roo.HtmlEditorCore;
19886         
19887         var output = input;
19888         Roo.each(he.swapCodes, function(sw) { 
19889             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19890             
19891             output = output.replace(swapper, sw[1]);
19892         });
19893         
19894         return output;
19895     },
19896     
19897     
19898     cleanUpChildren : function (n)
19899     {
19900         if (!n.childNodes.length) {
19901             return;
19902         }
19903         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19904            this.cleanUpChild(n.childNodes[i]);
19905         }
19906     },
19907     
19908     
19909         
19910     
19911     cleanUpChild : function (node)
19912     {
19913         var ed = this;
19914         //console.log(node);
19915         if (node.nodeName == "#text") {
19916             // clean up silly Windows -- stuff?
19917             return; 
19918         }
19919         if (node.nodeName == "#comment") {
19920             node.parentNode.removeChild(node);
19921             // clean up silly Windows -- stuff?
19922             return; 
19923         }
19924         var lcname = node.tagName.toLowerCase();
19925         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19926         // whitelist of tags..
19927         
19928         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19929             // remove node.
19930             node.parentNode.removeChild(node);
19931             return;
19932             
19933         }
19934         
19935         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19936         
19937         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19938         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19939         
19940         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19941         //    remove_keep_children = true;
19942         //}
19943         
19944         if (remove_keep_children) {
19945             this.cleanUpChildren(node);
19946             // inserts everything just before this node...
19947             while (node.childNodes.length) {
19948                 var cn = node.childNodes[0];
19949                 node.removeChild(cn);
19950                 node.parentNode.insertBefore(cn, node);
19951             }
19952             node.parentNode.removeChild(node);
19953             return;
19954         }
19955         
19956         if (!node.attributes || !node.attributes.length) {
19957             this.cleanUpChildren(node);
19958             return;
19959         }
19960         
19961         function cleanAttr(n,v)
19962         {
19963             
19964             if (v.match(/^\./) || v.match(/^\//)) {
19965                 return;
19966             }
19967             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19968                 return;
19969             }
19970             if (v.match(/^#/)) {
19971                 return;
19972             }
19973 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19974             node.removeAttribute(n);
19975             
19976         }
19977         
19978         var cwhite = this.cwhite;
19979         var cblack = this.cblack;
19980             
19981         function cleanStyle(n,v)
19982         {
19983             if (v.match(/expression/)) { //XSS?? should we even bother..
19984                 node.removeAttribute(n);
19985                 return;
19986             }
19987             
19988             var parts = v.split(/;/);
19989             var clean = [];
19990             
19991             Roo.each(parts, function(p) {
19992                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19993                 if (!p.length) {
19994                     return true;
19995                 }
19996                 var l = p.split(':').shift().replace(/\s+/g,'');
19997                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19998                 
19999                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20000 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20001                     //node.removeAttribute(n);
20002                     return true;
20003                 }
20004                 //Roo.log()
20005                 // only allow 'c whitelisted system attributes'
20006                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20007 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20008                     //node.removeAttribute(n);
20009                     return true;
20010                 }
20011                 
20012                 
20013                  
20014                 
20015                 clean.push(p);
20016                 return true;
20017             });
20018             if (clean.length) { 
20019                 node.setAttribute(n, clean.join(';'));
20020             } else {
20021                 node.removeAttribute(n);
20022             }
20023             
20024         }
20025         
20026         
20027         for (var i = node.attributes.length-1; i > -1 ; i--) {
20028             var a = node.attributes[i];
20029             //console.log(a);
20030             
20031             if (a.name.toLowerCase().substr(0,2)=='on')  {
20032                 node.removeAttribute(a.name);
20033                 continue;
20034             }
20035             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20036                 node.removeAttribute(a.name);
20037                 continue;
20038             }
20039             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20040                 cleanAttr(a.name,a.value); // fixme..
20041                 continue;
20042             }
20043             if (a.name == 'style') {
20044                 cleanStyle(a.name,a.value);
20045                 continue;
20046             }
20047             /// clean up MS crap..
20048             // tecnically this should be a list of valid class'es..
20049             
20050             
20051             if (a.name == 'class') {
20052                 if (a.value.match(/^Mso/)) {
20053                     node.className = '';
20054                 }
20055                 
20056                 if (a.value.match(/body/)) {
20057                     node.className = '';
20058                 }
20059                 continue;
20060             }
20061             
20062             // style cleanup!?
20063             // class cleanup?
20064             
20065         }
20066         
20067         
20068         this.cleanUpChildren(node);
20069         
20070         
20071     },
20072     
20073     /**
20074      * Clean up MS wordisms...
20075      */
20076     cleanWord : function(node)
20077     {
20078         
20079         
20080         if (!node) {
20081             this.cleanWord(this.doc.body);
20082             return;
20083         }
20084         if (node.nodeName == "#text") {
20085             // clean up silly Windows -- stuff?
20086             return; 
20087         }
20088         if (node.nodeName == "#comment") {
20089             node.parentNode.removeChild(node);
20090             // clean up silly Windows -- stuff?
20091             return; 
20092         }
20093         
20094         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20095             node.parentNode.removeChild(node);
20096             return;
20097         }
20098         
20099         // remove - but keep children..
20100         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20101             while (node.childNodes.length) {
20102                 var cn = node.childNodes[0];
20103                 node.removeChild(cn);
20104                 node.parentNode.insertBefore(cn, node);
20105             }
20106             node.parentNode.removeChild(node);
20107             this.iterateChildren(node, this.cleanWord);
20108             return;
20109         }
20110         // clean styles
20111         if (node.className.length) {
20112             
20113             var cn = node.className.split(/\W+/);
20114             var cna = [];
20115             Roo.each(cn, function(cls) {
20116                 if (cls.match(/Mso[a-zA-Z]+/)) {
20117                     return;
20118                 }
20119                 cna.push(cls);
20120             });
20121             node.className = cna.length ? cna.join(' ') : '';
20122             if (!cna.length) {
20123                 node.removeAttribute("class");
20124             }
20125         }
20126         
20127         if (node.hasAttribute("lang")) {
20128             node.removeAttribute("lang");
20129         }
20130         
20131         if (node.hasAttribute("style")) {
20132             
20133             var styles = node.getAttribute("style").split(";");
20134             var nstyle = [];
20135             Roo.each(styles, function(s) {
20136                 if (!s.match(/:/)) {
20137                     return;
20138                 }
20139                 var kv = s.split(":");
20140                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20141                     return;
20142                 }
20143                 // what ever is left... we allow.
20144                 nstyle.push(s);
20145             });
20146             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20147             if (!nstyle.length) {
20148                 node.removeAttribute('style');
20149             }
20150         }
20151         this.iterateChildren(node, this.cleanWord);
20152         
20153         
20154         
20155     },
20156     /**
20157      * iterateChildren of a Node, calling fn each time, using this as the scole..
20158      * @param {DomNode} node node to iterate children of.
20159      * @param {Function} fn method of this class to call on each item.
20160      */
20161     iterateChildren : function(node, fn)
20162     {
20163         if (!node.childNodes.length) {
20164                 return;
20165         }
20166         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20167            fn.call(this, node.childNodes[i])
20168         }
20169     },
20170     
20171     
20172     /**
20173      * cleanTableWidths.
20174      *
20175      * Quite often pasting from word etc.. results in tables with column and widths.
20176      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20177      *
20178      */
20179     cleanTableWidths : function(node)
20180     {
20181          
20182          
20183         if (!node) {
20184             this.cleanTableWidths(this.doc.body);
20185             return;
20186         }
20187         
20188         // ignore list...
20189         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20190             return; 
20191         }
20192         Roo.log(node.tagName);
20193         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20194             this.iterateChildren(node, this.cleanTableWidths);
20195             return;
20196         }
20197         if (node.hasAttribute('width')) {
20198             node.removeAttribute('width');
20199         }
20200         
20201          
20202         if (node.hasAttribute("style")) {
20203             // pretty basic...
20204             
20205             var styles = node.getAttribute("style").split(";");
20206             var nstyle = [];
20207             Roo.each(styles, function(s) {
20208                 if (!s.match(/:/)) {
20209                     return;
20210                 }
20211                 var kv = s.split(":");
20212                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20213                     return;
20214                 }
20215                 // what ever is left... we allow.
20216                 nstyle.push(s);
20217             });
20218             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20219             if (!nstyle.length) {
20220                 node.removeAttribute('style');
20221             }
20222         }
20223         
20224         this.iterateChildren(node, this.cleanTableWidths);
20225         
20226         
20227     },
20228     
20229     
20230     
20231     
20232     domToHTML : function(currentElement, depth, nopadtext) {
20233         
20234         depth = depth || 0;
20235         nopadtext = nopadtext || false;
20236     
20237         if (!currentElement) {
20238             return this.domToHTML(this.doc.body);
20239         }
20240         
20241         //Roo.log(currentElement);
20242         var j;
20243         var allText = false;
20244         var nodeName = currentElement.nodeName;
20245         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20246         
20247         if  (nodeName == '#text') {
20248             
20249             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20250         }
20251         
20252         
20253         var ret = '';
20254         if (nodeName != 'BODY') {
20255              
20256             var i = 0;
20257             // Prints the node tagName, such as <A>, <IMG>, etc
20258             if (tagName) {
20259                 var attr = [];
20260                 for(i = 0; i < currentElement.attributes.length;i++) {
20261                     // quoting?
20262                     var aname = currentElement.attributes.item(i).name;
20263                     if (!currentElement.attributes.item(i).value.length) {
20264                         continue;
20265                     }
20266                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20267                 }
20268                 
20269                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20270             } 
20271             else {
20272                 
20273                 // eack
20274             }
20275         } else {
20276             tagName = false;
20277         }
20278         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20279             return ret;
20280         }
20281         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20282             nopadtext = true;
20283         }
20284         
20285         
20286         // Traverse the tree
20287         i = 0;
20288         var currentElementChild = currentElement.childNodes.item(i);
20289         var allText = true;
20290         var innerHTML  = '';
20291         lastnode = '';
20292         while (currentElementChild) {
20293             // Formatting code (indent the tree so it looks nice on the screen)
20294             var nopad = nopadtext;
20295             if (lastnode == 'SPAN') {
20296                 nopad  = true;
20297             }
20298             // text
20299             if  (currentElementChild.nodeName == '#text') {
20300                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20301                 toadd = nopadtext ? toadd : toadd.trim();
20302                 if (!nopad && toadd.length > 80) {
20303                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20304                 }
20305                 innerHTML  += toadd;
20306                 
20307                 i++;
20308                 currentElementChild = currentElement.childNodes.item(i);
20309                 lastNode = '';
20310                 continue;
20311             }
20312             allText = false;
20313             
20314             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20315                 
20316             // Recursively traverse the tree structure of the child node
20317             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20318             lastnode = currentElementChild.nodeName;
20319             i++;
20320             currentElementChild=currentElement.childNodes.item(i);
20321         }
20322         
20323         ret += innerHTML;
20324         
20325         if (!allText) {
20326                 // The remaining code is mostly for formatting the tree
20327             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20328         }
20329         
20330         
20331         if (tagName) {
20332             ret+= "</"+tagName+">";
20333         }
20334         return ret;
20335         
20336     },
20337         
20338     applyBlacklists : function()
20339     {
20340         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20341         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20342         
20343         this.white = [];
20344         this.black = [];
20345         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20346             if (b.indexOf(tag) > -1) {
20347                 return;
20348             }
20349             this.white.push(tag);
20350             
20351         }, this);
20352         
20353         Roo.each(w, function(tag) {
20354             if (b.indexOf(tag) > -1) {
20355                 return;
20356             }
20357             if (this.white.indexOf(tag) > -1) {
20358                 return;
20359             }
20360             this.white.push(tag);
20361             
20362         }, this);
20363         
20364         
20365         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20366             if (w.indexOf(tag) > -1) {
20367                 return;
20368             }
20369             this.black.push(tag);
20370             
20371         }, this);
20372         
20373         Roo.each(b, function(tag) {
20374             if (w.indexOf(tag) > -1) {
20375                 return;
20376             }
20377             if (this.black.indexOf(tag) > -1) {
20378                 return;
20379             }
20380             this.black.push(tag);
20381             
20382         }, this);
20383         
20384         
20385         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20386         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20387         
20388         this.cwhite = [];
20389         this.cblack = [];
20390         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20391             if (b.indexOf(tag) > -1) {
20392                 return;
20393             }
20394             this.cwhite.push(tag);
20395             
20396         }, this);
20397         
20398         Roo.each(w, function(tag) {
20399             if (b.indexOf(tag) > -1) {
20400                 return;
20401             }
20402             if (this.cwhite.indexOf(tag) > -1) {
20403                 return;
20404             }
20405             this.cwhite.push(tag);
20406             
20407         }, this);
20408         
20409         
20410         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20411             if (w.indexOf(tag) > -1) {
20412                 return;
20413             }
20414             this.cblack.push(tag);
20415             
20416         }, this);
20417         
20418         Roo.each(b, function(tag) {
20419             if (w.indexOf(tag) > -1) {
20420                 return;
20421             }
20422             if (this.cblack.indexOf(tag) > -1) {
20423                 return;
20424             }
20425             this.cblack.push(tag);
20426             
20427         }, this);
20428     },
20429     
20430     setStylesheets : function(stylesheets)
20431     {
20432         if(typeof(stylesheets) == 'string'){
20433             Roo.get(this.iframe.contentDocument.head).createChild({
20434                 tag : 'link',
20435                 rel : 'stylesheet',
20436                 type : 'text/css',
20437                 href : stylesheets
20438             });
20439             
20440             return;
20441         }
20442         var _this = this;
20443      
20444         Roo.each(stylesheets, function(s) {
20445             if(!s.length){
20446                 return;
20447             }
20448             
20449             Roo.get(_this.iframe.contentDocument.head).createChild({
20450                 tag : 'link',
20451                 rel : 'stylesheet',
20452                 type : 'text/css',
20453                 href : s
20454             });
20455         });
20456
20457         
20458     },
20459     
20460     removeStylesheets : function()
20461     {
20462         var _this = this;
20463         
20464         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20465             s.remove();
20466         });
20467     }
20468     
20469     // hide stuff that is not compatible
20470     /**
20471      * @event blur
20472      * @hide
20473      */
20474     /**
20475      * @event change
20476      * @hide
20477      */
20478     /**
20479      * @event focus
20480      * @hide
20481      */
20482     /**
20483      * @event specialkey
20484      * @hide
20485      */
20486     /**
20487      * @cfg {String} fieldClass @hide
20488      */
20489     /**
20490      * @cfg {String} focusClass @hide
20491      */
20492     /**
20493      * @cfg {String} autoCreate @hide
20494      */
20495     /**
20496      * @cfg {String} inputType @hide
20497      */
20498     /**
20499      * @cfg {String} invalidClass @hide
20500      */
20501     /**
20502      * @cfg {String} invalidText @hide
20503      */
20504     /**
20505      * @cfg {String} msgFx @hide
20506      */
20507     /**
20508      * @cfg {String} validateOnBlur @hide
20509      */
20510 });
20511
20512 Roo.HtmlEditorCore.white = [
20513         'area', 'br', 'img', 'input', 'hr', 'wbr',
20514         
20515        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20516        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20517        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20518        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20519        'table',   'ul',         'xmp', 
20520        
20521        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20522       'thead',   'tr', 
20523      
20524       'dir', 'menu', 'ol', 'ul', 'dl',
20525        
20526       'embed',  'object'
20527 ];
20528
20529
20530 Roo.HtmlEditorCore.black = [
20531     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20532         'applet', // 
20533         'base',   'basefont', 'bgsound', 'blink',  'body', 
20534         'frame',  'frameset', 'head',    'html',   'ilayer', 
20535         'iframe', 'layer',  'link',     'meta',    'object',   
20536         'script', 'style' ,'title',  'xml' // clean later..
20537 ];
20538 Roo.HtmlEditorCore.clean = [
20539     'script', 'style', 'title', 'xml'
20540 ];
20541 Roo.HtmlEditorCore.remove = [
20542     'font'
20543 ];
20544 // attributes..
20545
20546 Roo.HtmlEditorCore.ablack = [
20547     'on'
20548 ];
20549     
20550 Roo.HtmlEditorCore.aclean = [ 
20551     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20552 ];
20553
20554 // protocols..
20555 Roo.HtmlEditorCore.pwhite= [
20556         'http',  'https',  'mailto'
20557 ];
20558
20559 // white listed style attributes.
20560 Roo.HtmlEditorCore.cwhite= [
20561       //  'text-align', /// default is to allow most things..
20562       
20563          
20564 //        'font-size'//??
20565 ];
20566
20567 // black listed style attributes.
20568 Roo.HtmlEditorCore.cblack= [
20569       //  'font-size' -- this can be set by the project 
20570 ];
20571
20572
20573 Roo.HtmlEditorCore.swapCodes   =[ 
20574     [    8211, "--" ], 
20575     [    8212, "--" ], 
20576     [    8216,  "'" ],  
20577     [    8217, "'" ],  
20578     [    8220, '"' ],  
20579     [    8221, '"' ],  
20580     [    8226, "*" ],  
20581     [    8230, "..." ]
20582 ]; 
20583
20584     /*
20585  * - LGPL
20586  *
20587  * HtmlEditor
20588  * 
20589  */
20590
20591 /**
20592  * @class Roo.bootstrap.HtmlEditor
20593  * @extends Roo.bootstrap.TextArea
20594  * Bootstrap HtmlEditor class
20595
20596  * @constructor
20597  * Create a new HtmlEditor
20598  * @param {Object} config The config object
20599  */
20600
20601 Roo.bootstrap.HtmlEditor = function(config){
20602     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20603     if (!this.toolbars) {
20604         this.toolbars = [];
20605     }
20606     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20607     this.addEvents({
20608             /**
20609              * @event initialize
20610              * Fires when the editor is fully initialized (including the iframe)
20611              * @param {HtmlEditor} this
20612              */
20613             initialize: true,
20614             /**
20615              * @event activate
20616              * Fires when the editor is first receives the focus. Any insertion must wait
20617              * until after this event.
20618              * @param {HtmlEditor} this
20619              */
20620             activate: true,
20621              /**
20622              * @event beforesync
20623              * Fires before the textarea is updated with content from the editor iframe. Return false
20624              * to cancel the sync.
20625              * @param {HtmlEditor} this
20626              * @param {String} html
20627              */
20628             beforesync: true,
20629              /**
20630              * @event beforepush
20631              * Fires before the iframe editor is updated with content from the textarea. Return false
20632              * to cancel the push.
20633              * @param {HtmlEditor} this
20634              * @param {String} html
20635              */
20636             beforepush: true,
20637              /**
20638              * @event sync
20639              * Fires when the textarea is updated with content from the editor iframe.
20640              * @param {HtmlEditor} this
20641              * @param {String} html
20642              */
20643             sync: true,
20644              /**
20645              * @event push
20646              * Fires when the iframe editor is updated with content from the textarea.
20647              * @param {HtmlEditor} this
20648              * @param {String} html
20649              */
20650             push: true,
20651              /**
20652              * @event editmodechange
20653              * Fires when the editor switches edit modes
20654              * @param {HtmlEditor} this
20655              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20656              */
20657             editmodechange: true,
20658             /**
20659              * @event editorevent
20660              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20661              * @param {HtmlEditor} this
20662              */
20663             editorevent: true,
20664             /**
20665              * @event firstfocus
20666              * Fires when on first focus - needed by toolbars..
20667              * @param {HtmlEditor} this
20668              */
20669             firstfocus: true,
20670             /**
20671              * @event autosave
20672              * Auto save the htmlEditor value as a file into Events
20673              * @param {HtmlEditor} this
20674              */
20675             autosave: true,
20676             /**
20677              * @event savedpreview
20678              * preview the saved version of htmlEditor
20679              * @param {HtmlEditor} this
20680              */
20681             savedpreview: true
20682         });
20683 };
20684
20685
20686 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20687     
20688     
20689       /**
20690      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20691      */
20692     toolbars : false,
20693    
20694      /**
20695      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20696      *                        Roo.resizable.
20697      */
20698     resizable : false,
20699      /**
20700      * @cfg {Number} height (in pixels)
20701      */   
20702     height: 300,
20703    /**
20704      * @cfg {Number} width (in pixels)
20705      */   
20706     width: false,
20707     
20708     /**
20709      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20710      * 
20711      */
20712     stylesheets: false,
20713     
20714     // id of frame..
20715     frameId: false,
20716     
20717     // private properties
20718     validationEvent : false,
20719     deferHeight: true,
20720     initialized : false,
20721     activated : false,
20722     
20723     onFocus : Roo.emptyFn,
20724     iframePad:3,
20725     hideMode:'offsets',
20726     
20727     
20728     tbContainer : false,
20729     
20730     toolbarContainer :function() {
20731         return this.wrap.select('.x-html-editor-tb',true).first();
20732     },
20733
20734     /**
20735      * Protected method that will not generally be called directly. It
20736      * is called when the editor creates its toolbar. Override this method if you need to
20737      * add custom toolbar buttons.
20738      * @param {HtmlEditor} editor
20739      */
20740     createToolbar : function(){
20741         
20742         Roo.log("create toolbars");
20743         
20744         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20745         this.toolbars[0].render(this.toolbarContainer());
20746         
20747         return;
20748         
20749 //        if (!editor.toolbars || !editor.toolbars.length) {
20750 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20751 //        }
20752 //        
20753 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20754 //            editor.toolbars[i] = Roo.factory(
20755 //                    typeof(editor.toolbars[i]) == 'string' ?
20756 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20757 //                Roo.bootstrap.HtmlEditor);
20758 //            editor.toolbars[i].init(editor);
20759 //        }
20760     },
20761
20762      
20763     // private
20764     onRender : function(ct, position)
20765     {
20766        // Roo.log("Call onRender: " + this.xtype);
20767         var _t = this;
20768         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20769       
20770         this.wrap = this.inputEl().wrap({
20771             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20772         });
20773         
20774         this.editorcore.onRender(ct, position);
20775          
20776         if (this.resizable) {
20777             this.resizeEl = new Roo.Resizable(this.wrap, {
20778                 pinned : true,
20779                 wrap: true,
20780                 dynamic : true,
20781                 minHeight : this.height,
20782                 height: this.height,
20783                 handles : this.resizable,
20784                 width: this.width,
20785                 listeners : {
20786                     resize : function(r, w, h) {
20787                         _t.onResize(w,h); // -something
20788                     }
20789                 }
20790             });
20791             
20792         }
20793         this.createToolbar(this);
20794        
20795         
20796         if(!this.width && this.resizable){
20797             this.setSize(this.wrap.getSize());
20798         }
20799         if (this.resizeEl) {
20800             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20801             // should trigger onReize..
20802         }
20803         
20804     },
20805
20806     // private
20807     onResize : function(w, h)
20808     {
20809         Roo.log('resize: ' +w + ',' + h );
20810         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20811         var ew = false;
20812         var eh = false;
20813         
20814         if(this.inputEl() ){
20815             if(typeof w == 'number'){
20816                 var aw = w - this.wrap.getFrameWidth('lr');
20817                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20818                 ew = aw;
20819             }
20820             if(typeof h == 'number'){
20821                  var tbh = -11;  // fixme it needs to tool bar size!
20822                 for (var i =0; i < this.toolbars.length;i++) {
20823                     // fixme - ask toolbars for heights?
20824                     tbh += this.toolbars[i].el.getHeight();
20825                     //if (this.toolbars[i].footer) {
20826                     //    tbh += this.toolbars[i].footer.el.getHeight();
20827                     //}
20828                 }
20829               
20830                 
20831                 
20832                 
20833                 
20834                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20835                 ah -= 5; // knock a few pixes off for look..
20836                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20837                 var eh = ah;
20838             }
20839         }
20840         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20841         this.editorcore.onResize(ew,eh);
20842         
20843     },
20844
20845     /**
20846      * Toggles the editor between standard and source edit mode.
20847      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20848      */
20849     toggleSourceEdit : function(sourceEditMode)
20850     {
20851         this.editorcore.toggleSourceEdit(sourceEditMode);
20852         
20853         if(this.editorcore.sourceEditMode){
20854             Roo.log('editor - showing textarea');
20855             
20856 //            Roo.log('in');
20857 //            Roo.log(this.syncValue());
20858             this.syncValue();
20859             this.inputEl().removeClass(['hide', 'x-hidden']);
20860             this.inputEl().dom.removeAttribute('tabIndex');
20861             this.inputEl().focus();
20862         }else{
20863             Roo.log('editor - hiding textarea');
20864 //            Roo.log('out')
20865 //            Roo.log(this.pushValue()); 
20866             this.pushValue();
20867             
20868             this.inputEl().addClass(['hide', 'x-hidden']);
20869             this.inputEl().dom.setAttribute('tabIndex', -1);
20870             //this.deferFocus();
20871         }
20872          
20873         if(this.resizable){
20874             this.setSize(this.wrap.getSize());
20875         }
20876         
20877         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20878     },
20879  
20880     // private (for BoxComponent)
20881     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20882
20883     // private (for BoxComponent)
20884     getResizeEl : function(){
20885         return this.wrap;
20886     },
20887
20888     // private (for BoxComponent)
20889     getPositionEl : function(){
20890         return this.wrap;
20891     },
20892
20893     // private
20894     initEvents : function(){
20895         this.originalValue = this.getValue();
20896     },
20897
20898 //    /**
20899 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20900 //     * @method
20901 //     */
20902 //    markInvalid : Roo.emptyFn,
20903 //    /**
20904 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20905 //     * @method
20906 //     */
20907 //    clearInvalid : Roo.emptyFn,
20908
20909     setValue : function(v){
20910         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20911         this.editorcore.pushValue();
20912     },
20913
20914      
20915     // private
20916     deferFocus : function(){
20917         this.focus.defer(10, this);
20918     },
20919
20920     // doc'ed in Field
20921     focus : function(){
20922         this.editorcore.focus();
20923         
20924     },
20925       
20926
20927     // private
20928     onDestroy : function(){
20929         
20930         
20931         
20932         if(this.rendered){
20933             
20934             for (var i =0; i < this.toolbars.length;i++) {
20935                 // fixme - ask toolbars for heights?
20936                 this.toolbars[i].onDestroy();
20937             }
20938             
20939             this.wrap.dom.innerHTML = '';
20940             this.wrap.remove();
20941         }
20942     },
20943
20944     // private
20945     onFirstFocus : function(){
20946         //Roo.log("onFirstFocus");
20947         this.editorcore.onFirstFocus();
20948          for (var i =0; i < this.toolbars.length;i++) {
20949             this.toolbars[i].onFirstFocus();
20950         }
20951         
20952     },
20953     
20954     // private
20955     syncValue : function()
20956     {   
20957         this.editorcore.syncValue();
20958     },
20959     
20960     pushValue : function()
20961     {   
20962         this.editorcore.pushValue();
20963     }
20964      
20965     
20966     // hide stuff that is not compatible
20967     /**
20968      * @event blur
20969      * @hide
20970      */
20971     /**
20972      * @event change
20973      * @hide
20974      */
20975     /**
20976      * @event focus
20977      * @hide
20978      */
20979     /**
20980      * @event specialkey
20981      * @hide
20982      */
20983     /**
20984      * @cfg {String} fieldClass @hide
20985      */
20986     /**
20987      * @cfg {String} focusClass @hide
20988      */
20989     /**
20990      * @cfg {String} autoCreate @hide
20991      */
20992     /**
20993      * @cfg {String} inputType @hide
20994      */
20995     /**
20996      * @cfg {String} invalidClass @hide
20997      */
20998     /**
20999      * @cfg {String} invalidText @hide
21000      */
21001     /**
21002      * @cfg {String} msgFx @hide
21003      */
21004     /**
21005      * @cfg {String} validateOnBlur @hide
21006      */
21007 });
21008  
21009     
21010    
21011    
21012    
21013       
21014 Roo.namespace('Roo.bootstrap.htmleditor');
21015 /**
21016  * @class Roo.bootstrap.HtmlEditorToolbar1
21017  * Basic Toolbar
21018  * 
21019  * Usage:
21020  *
21021  new Roo.bootstrap.HtmlEditor({
21022     ....
21023     toolbars : [
21024         new Roo.bootstrap.HtmlEditorToolbar1({
21025             disable : { fonts: 1 , format: 1, ..., ... , ...],
21026             btns : [ .... ]
21027         })
21028     }
21029      
21030  * 
21031  * @cfg {Object} disable List of elements to disable..
21032  * @cfg {Array} btns List of additional buttons.
21033  * 
21034  * 
21035  * NEEDS Extra CSS? 
21036  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21037  */
21038  
21039 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21040 {
21041     
21042     Roo.apply(this, config);
21043     
21044     // default disabled, based on 'good practice'..
21045     this.disable = this.disable || {};
21046     Roo.applyIf(this.disable, {
21047         fontSize : true,
21048         colors : true,
21049         specialElements : true
21050     });
21051     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21052     
21053     this.editor = config.editor;
21054     this.editorcore = config.editor.editorcore;
21055     
21056     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21057     
21058     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21059     // dont call parent... till later.
21060 }
21061 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21062      
21063     bar : true,
21064     
21065     editor : false,
21066     editorcore : false,
21067     
21068     
21069     formats : [
21070         "p" ,  
21071         "h1","h2","h3","h4","h5","h6", 
21072         "pre", "code", 
21073         "abbr", "acronym", "address", "cite", "samp", "var",
21074         'div','span'
21075     ],
21076     
21077     onRender : function(ct, position)
21078     {
21079        // Roo.log("Call onRender: " + this.xtype);
21080         
21081        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21082        Roo.log(this.el);
21083        this.el.dom.style.marginBottom = '0';
21084        var _this = this;
21085        var editorcore = this.editorcore;
21086        var editor= this.editor;
21087        
21088        var children = [];
21089        var btn = function(id,cmd , toggle, handler){
21090        
21091             var  event = toggle ? 'toggle' : 'click';
21092        
21093             var a = {
21094                 size : 'sm',
21095                 xtype: 'Button',
21096                 xns: Roo.bootstrap,
21097                 glyphicon : id,
21098                 cmd : id || cmd,
21099                 enableToggle:toggle !== false,
21100                 //html : 'submit'
21101                 pressed : toggle ? false : null,
21102                 listeners : {}
21103             };
21104             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21105                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21106             };
21107             children.push(a);
21108             return a;
21109        }
21110         
21111         var style = {
21112                 xtype: 'Button',
21113                 size : 'sm',
21114                 xns: Roo.bootstrap,
21115                 glyphicon : 'font',
21116                 //html : 'submit'
21117                 menu : {
21118                     xtype: 'Menu',
21119                     xns: Roo.bootstrap,
21120                     items:  []
21121                 }
21122         };
21123         Roo.each(this.formats, function(f) {
21124             style.menu.items.push({
21125                 xtype :'MenuItem',
21126                 xns: Roo.bootstrap,
21127                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21128                 tagname : f,
21129                 listeners : {
21130                     click : function()
21131                     {
21132                         editorcore.insertTag(this.tagname);
21133                         editor.focus();
21134                     }
21135                 }
21136                 
21137             });
21138         });
21139          children.push(style);   
21140             
21141             
21142         btn('bold',false,true);
21143         btn('italic',false,true);
21144         btn('align-left', 'justifyleft',true);
21145         btn('align-center', 'justifycenter',true);
21146         btn('align-right' , 'justifyright',true);
21147         btn('link', false, false, function(btn) {
21148             //Roo.log("create link?");
21149             var url = prompt(this.createLinkText, this.defaultLinkValue);
21150             if(url && url != 'http:/'+'/'){
21151                 this.editorcore.relayCmd('createlink', url);
21152             }
21153         }),
21154         btn('list','insertunorderedlist',true);
21155         btn('pencil', false,true, function(btn){
21156                 Roo.log(this);
21157                 
21158                 this.toggleSourceEdit(btn.pressed);
21159         });
21160         /*
21161         var cog = {
21162                 xtype: 'Button',
21163                 size : 'sm',
21164                 xns: Roo.bootstrap,
21165                 glyphicon : 'cog',
21166                 //html : 'submit'
21167                 menu : {
21168                     xtype: 'Menu',
21169                     xns: Roo.bootstrap,
21170                     items:  []
21171                 }
21172         };
21173         
21174         cog.menu.items.push({
21175             xtype :'MenuItem',
21176             xns: Roo.bootstrap,
21177             html : Clean styles,
21178             tagname : f,
21179             listeners : {
21180                 click : function()
21181                 {
21182                     editorcore.insertTag(this.tagname);
21183                     editor.focus();
21184                 }
21185             }
21186             
21187         });
21188        */
21189         
21190          
21191        this.xtype = 'NavSimplebar';
21192         
21193         for(var i=0;i< children.length;i++) {
21194             
21195             this.buttons.add(this.addxtypeChild(children[i]));
21196             
21197         }
21198         
21199         editor.on('editorevent', this.updateToolbar, this);
21200     },
21201     onBtnClick : function(id)
21202     {
21203        this.editorcore.relayCmd(id);
21204        this.editorcore.focus();
21205     },
21206     
21207     /**
21208      * Protected method that will not generally be called directly. It triggers
21209      * a toolbar update by reading the markup state of the current selection in the editor.
21210      */
21211     updateToolbar: function(){
21212
21213         if(!this.editorcore.activated){
21214             this.editor.onFirstFocus(); // is this neeed?
21215             return;
21216         }
21217
21218         var btns = this.buttons; 
21219         var doc = this.editorcore.doc;
21220         btns.get('bold').setActive(doc.queryCommandState('bold'));
21221         btns.get('italic').setActive(doc.queryCommandState('italic'));
21222         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21223         
21224         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21225         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21226         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21227         
21228         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21229         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21230          /*
21231         
21232         var ans = this.editorcore.getAllAncestors();
21233         if (this.formatCombo) {
21234             
21235             
21236             var store = this.formatCombo.store;
21237             this.formatCombo.setValue("");
21238             for (var i =0; i < ans.length;i++) {
21239                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21240                     // select it..
21241                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21242                     break;
21243                 }
21244             }
21245         }
21246         
21247         
21248         
21249         // hides menus... - so this cant be on a menu...
21250         Roo.bootstrap.MenuMgr.hideAll();
21251         */
21252         Roo.bootstrap.MenuMgr.hideAll();
21253         //this.editorsyncValue();
21254     },
21255     onFirstFocus: function() {
21256         this.buttons.each(function(item){
21257            item.enable();
21258         });
21259     },
21260     toggleSourceEdit : function(sourceEditMode){
21261         
21262           
21263         if(sourceEditMode){
21264             Roo.log("disabling buttons");
21265            this.buttons.each( function(item){
21266                 if(item.cmd != 'pencil'){
21267                     item.disable();
21268                 }
21269             });
21270           
21271         }else{
21272             Roo.log("enabling buttons");
21273             if(this.editorcore.initialized){
21274                 this.buttons.each( function(item){
21275                     item.enable();
21276                 });
21277             }
21278             
21279         }
21280         Roo.log("calling toggole on editor");
21281         // tell the editor that it's been pressed..
21282         this.editor.toggleSourceEdit(sourceEditMode);
21283        
21284     }
21285 });
21286
21287
21288
21289
21290
21291 /**
21292  * @class Roo.bootstrap.Table.AbstractSelectionModel
21293  * @extends Roo.util.Observable
21294  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21295  * implemented by descendant classes.  This class should not be directly instantiated.
21296  * @constructor
21297  */
21298 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21299     this.locked = false;
21300     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21301 };
21302
21303
21304 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21305     /** @ignore Called by the grid automatically. Do not call directly. */
21306     init : function(grid){
21307         this.grid = grid;
21308         this.initEvents();
21309     },
21310
21311     /**
21312      * Locks the selections.
21313      */
21314     lock : function(){
21315         this.locked = true;
21316     },
21317
21318     /**
21319      * Unlocks the selections.
21320      */
21321     unlock : function(){
21322         this.locked = false;
21323     },
21324
21325     /**
21326      * Returns true if the selections are locked.
21327      * @return {Boolean}
21328      */
21329     isLocked : function(){
21330         return this.locked;
21331     }
21332 });
21333 /**
21334  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21335  * @class Roo.bootstrap.Table.RowSelectionModel
21336  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21337  * It supports multiple selections and keyboard selection/navigation. 
21338  * @constructor
21339  * @param {Object} config
21340  */
21341
21342 Roo.bootstrap.Table.RowSelectionModel = function(config){
21343     Roo.apply(this, config);
21344     this.selections = new Roo.util.MixedCollection(false, function(o){
21345         return o.id;
21346     });
21347
21348     this.last = false;
21349     this.lastActive = false;
21350
21351     this.addEvents({
21352         /**
21353              * @event selectionchange
21354              * Fires when the selection changes
21355              * @param {SelectionModel} this
21356              */
21357             "selectionchange" : true,
21358         /**
21359              * @event afterselectionchange
21360              * Fires after the selection changes (eg. by key press or clicking)
21361              * @param {SelectionModel} this
21362              */
21363             "afterselectionchange" : true,
21364         /**
21365              * @event beforerowselect
21366              * Fires when a row is selected being selected, return false to cancel.
21367              * @param {SelectionModel} this
21368              * @param {Number} rowIndex The selected index
21369              * @param {Boolean} keepExisting False if other selections will be cleared
21370              */
21371             "beforerowselect" : true,
21372         /**
21373              * @event rowselect
21374              * Fires when a row is selected.
21375              * @param {SelectionModel} this
21376              * @param {Number} rowIndex The selected index
21377              * @param {Roo.data.Record} r The record
21378              */
21379             "rowselect" : true,
21380         /**
21381              * @event rowdeselect
21382              * Fires when a row is deselected.
21383              * @param {SelectionModel} this
21384              * @param {Number} rowIndex The selected index
21385              */
21386         "rowdeselect" : true
21387     });
21388     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21389     this.locked = false;
21390 };
21391
21392 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21393     /**
21394      * @cfg {Boolean} singleSelect
21395      * True to allow selection of only one row at a time (defaults to false)
21396      */
21397     singleSelect : false,
21398
21399     // private
21400     initEvents : function(){
21401
21402         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21403             this.grid.on("mousedown", this.handleMouseDown, this);
21404         }else{ // allow click to work like normal
21405             this.grid.on("rowclick", this.handleDragableRowClick, this);
21406         }
21407
21408         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21409             "up" : function(e){
21410                 if(!e.shiftKey){
21411                     this.selectPrevious(e.shiftKey);
21412                 }else if(this.last !== false && this.lastActive !== false){
21413                     var last = this.last;
21414                     this.selectRange(this.last,  this.lastActive-1);
21415                     this.grid.getView().focusRow(this.lastActive);
21416                     if(last !== false){
21417                         this.last = last;
21418                     }
21419                 }else{
21420                     this.selectFirstRow();
21421                 }
21422                 this.fireEvent("afterselectionchange", this);
21423             },
21424             "down" : function(e){
21425                 if(!e.shiftKey){
21426                     this.selectNext(e.shiftKey);
21427                 }else if(this.last !== false && this.lastActive !== false){
21428                     var last = this.last;
21429                     this.selectRange(this.last,  this.lastActive+1);
21430                     this.grid.getView().focusRow(this.lastActive);
21431                     if(last !== false){
21432                         this.last = last;
21433                     }
21434                 }else{
21435                     this.selectFirstRow();
21436                 }
21437                 this.fireEvent("afterselectionchange", this);
21438             },
21439             scope: this
21440         });
21441
21442         var view = this.grid.view;
21443         view.on("refresh", this.onRefresh, this);
21444         view.on("rowupdated", this.onRowUpdated, this);
21445         view.on("rowremoved", this.onRemove, this);
21446     },
21447
21448     // private
21449     onRefresh : function(){
21450         var ds = this.grid.dataSource, i, v = this.grid.view;
21451         var s = this.selections;
21452         s.each(function(r){
21453             if((i = ds.indexOfId(r.id)) != -1){
21454                 v.onRowSelect(i);
21455             }else{
21456                 s.remove(r);
21457             }
21458         });
21459     },
21460
21461     // private
21462     onRemove : function(v, index, r){
21463         this.selections.remove(r);
21464     },
21465
21466     // private
21467     onRowUpdated : function(v, index, r){
21468         if(this.isSelected(r)){
21469             v.onRowSelect(index);
21470         }
21471     },
21472
21473     /**
21474      * Select records.
21475      * @param {Array} records The records to select
21476      * @param {Boolean} keepExisting (optional) True to keep existing selections
21477      */
21478     selectRecords : function(records, keepExisting){
21479         if(!keepExisting){
21480             this.clearSelections();
21481         }
21482         var ds = this.grid.dataSource;
21483         for(var i = 0, len = records.length; i < len; i++){
21484             this.selectRow(ds.indexOf(records[i]), true);
21485         }
21486     },
21487
21488     /**
21489      * Gets the number of selected rows.
21490      * @return {Number}
21491      */
21492     getCount : function(){
21493         return this.selections.length;
21494     },
21495
21496     /**
21497      * Selects the first row in the grid.
21498      */
21499     selectFirstRow : function(){
21500         this.selectRow(0);
21501     },
21502
21503     /**
21504      * Select the last row.
21505      * @param {Boolean} keepExisting (optional) True to keep existing selections
21506      */
21507     selectLastRow : function(keepExisting){
21508         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21509     },
21510
21511     /**
21512      * Selects the row immediately following the last selected row.
21513      * @param {Boolean} keepExisting (optional) True to keep existing selections
21514      */
21515     selectNext : function(keepExisting){
21516         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21517             this.selectRow(this.last+1, keepExisting);
21518             this.grid.getView().focusRow(this.last);
21519         }
21520     },
21521
21522     /**
21523      * Selects the row that precedes the last selected row.
21524      * @param {Boolean} keepExisting (optional) True to keep existing selections
21525      */
21526     selectPrevious : function(keepExisting){
21527         if(this.last){
21528             this.selectRow(this.last-1, keepExisting);
21529             this.grid.getView().focusRow(this.last);
21530         }
21531     },
21532
21533     /**
21534      * Returns the selected records
21535      * @return {Array} Array of selected records
21536      */
21537     getSelections : function(){
21538         return [].concat(this.selections.items);
21539     },
21540
21541     /**
21542      * Returns the first selected record.
21543      * @return {Record}
21544      */
21545     getSelected : function(){
21546         return this.selections.itemAt(0);
21547     },
21548
21549
21550     /**
21551      * Clears all selections.
21552      */
21553     clearSelections : function(fast){
21554         if(this.locked) return;
21555         if(fast !== true){
21556             var ds = this.grid.dataSource;
21557             var s = this.selections;
21558             s.each(function(r){
21559                 this.deselectRow(ds.indexOfId(r.id));
21560             }, this);
21561             s.clear();
21562         }else{
21563             this.selections.clear();
21564         }
21565         this.last = false;
21566     },
21567
21568
21569     /**
21570      * Selects all rows.
21571      */
21572     selectAll : function(){
21573         if(this.locked) return;
21574         this.selections.clear();
21575         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21576             this.selectRow(i, true);
21577         }
21578     },
21579
21580     /**
21581      * Returns True if there is a selection.
21582      * @return {Boolean}
21583      */
21584     hasSelection : function(){
21585         return this.selections.length > 0;
21586     },
21587
21588     /**
21589      * Returns True if the specified row is selected.
21590      * @param {Number/Record} record The record or index of the record to check
21591      * @return {Boolean}
21592      */
21593     isSelected : function(index){
21594         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21595         return (r && this.selections.key(r.id) ? true : false);
21596     },
21597
21598     /**
21599      * Returns True if the specified record id is selected.
21600      * @param {String} id The id of record to check
21601      * @return {Boolean}
21602      */
21603     isIdSelected : function(id){
21604         return (this.selections.key(id) ? true : false);
21605     },
21606
21607     // private
21608     handleMouseDown : function(e, t){
21609         var view = this.grid.getView(), rowIndex;
21610         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21611             return;
21612         };
21613         if(e.shiftKey && this.last !== false){
21614             var last = this.last;
21615             this.selectRange(last, rowIndex, e.ctrlKey);
21616             this.last = last; // reset the last
21617             view.focusRow(rowIndex);
21618         }else{
21619             var isSelected = this.isSelected(rowIndex);
21620             if(e.button !== 0 && isSelected){
21621                 view.focusRow(rowIndex);
21622             }else if(e.ctrlKey && isSelected){
21623                 this.deselectRow(rowIndex);
21624             }else if(!isSelected){
21625                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21626                 view.focusRow(rowIndex);
21627             }
21628         }
21629         this.fireEvent("afterselectionchange", this);
21630     },
21631     // private
21632     handleDragableRowClick :  function(grid, rowIndex, e) 
21633     {
21634         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21635             this.selectRow(rowIndex, false);
21636             grid.view.focusRow(rowIndex);
21637              this.fireEvent("afterselectionchange", this);
21638         }
21639     },
21640     
21641     /**
21642      * Selects multiple rows.
21643      * @param {Array} rows Array of the indexes of the row to select
21644      * @param {Boolean} keepExisting (optional) True to keep existing selections
21645      */
21646     selectRows : function(rows, keepExisting){
21647         if(!keepExisting){
21648             this.clearSelections();
21649         }
21650         for(var i = 0, len = rows.length; i < len; i++){
21651             this.selectRow(rows[i], true);
21652         }
21653     },
21654
21655     /**
21656      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21657      * @param {Number} startRow The index of the first row in the range
21658      * @param {Number} endRow The index of the last row in the range
21659      * @param {Boolean} keepExisting (optional) True to retain existing selections
21660      */
21661     selectRange : function(startRow, endRow, keepExisting){
21662         if(this.locked) return;
21663         if(!keepExisting){
21664             this.clearSelections();
21665         }
21666         if(startRow <= endRow){
21667             for(var i = startRow; i <= endRow; i++){
21668                 this.selectRow(i, true);
21669             }
21670         }else{
21671             for(var i = startRow; i >= endRow; i--){
21672                 this.selectRow(i, true);
21673             }
21674         }
21675     },
21676
21677     /**
21678      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21679      * @param {Number} startRow The index of the first row in the range
21680      * @param {Number} endRow The index of the last row in the range
21681      */
21682     deselectRange : function(startRow, endRow, preventViewNotify){
21683         if(this.locked) return;
21684         for(var i = startRow; i <= endRow; i++){
21685             this.deselectRow(i, preventViewNotify);
21686         }
21687     },
21688
21689     /**
21690      * Selects a row.
21691      * @param {Number} row The index of the row to select
21692      * @param {Boolean} keepExisting (optional) True to keep existing selections
21693      */
21694     selectRow : function(index, keepExisting, preventViewNotify){
21695         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21696         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21697             if(!keepExisting || this.singleSelect){
21698                 this.clearSelections();
21699             }
21700             var r = this.grid.dataSource.getAt(index);
21701             this.selections.add(r);
21702             this.last = this.lastActive = index;
21703             if(!preventViewNotify){
21704                 this.grid.getView().onRowSelect(index);
21705             }
21706             this.fireEvent("rowselect", this, index, r);
21707             this.fireEvent("selectionchange", this);
21708         }
21709     },
21710
21711     /**
21712      * Deselects a row.
21713      * @param {Number} row The index of the row to deselect
21714      */
21715     deselectRow : function(index, preventViewNotify){
21716         if(this.locked) return;
21717         if(this.last == index){
21718             this.last = false;
21719         }
21720         if(this.lastActive == index){
21721             this.lastActive = false;
21722         }
21723         var r = this.grid.dataSource.getAt(index);
21724         this.selections.remove(r);
21725         if(!preventViewNotify){
21726             this.grid.getView().onRowDeselect(index);
21727         }
21728         this.fireEvent("rowdeselect", this, index);
21729         this.fireEvent("selectionchange", this);
21730     },
21731
21732     // private
21733     restoreLast : function(){
21734         if(this._last){
21735             this.last = this._last;
21736         }
21737     },
21738
21739     // private
21740     acceptsNav : function(row, col, cm){
21741         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21742     },
21743
21744     // private
21745     onEditorKey : function(field, e){
21746         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21747         if(k == e.TAB){
21748             e.stopEvent();
21749             ed.completeEdit();
21750             if(e.shiftKey){
21751                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21752             }else{
21753                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21754             }
21755         }else if(k == e.ENTER && !e.ctrlKey){
21756             e.stopEvent();
21757             ed.completeEdit();
21758             if(e.shiftKey){
21759                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21760             }else{
21761                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21762             }
21763         }else if(k == e.ESC){
21764             ed.cancelEdit();
21765         }
21766         if(newCell){
21767             g.startEditing(newCell[0], newCell[1]);
21768         }
21769     }
21770 });/*
21771  * Based on:
21772  * Ext JS Library 1.1.1
21773  * Copyright(c) 2006-2007, Ext JS, LLC.
21774  *
21775  * Originally Released Under LGPL - original licence link has changed is not relivant.
21776  *
21777  * Fork - LGPL
21778  * <script type="text/javascript">
21779  */
21780  
21781 /**
21782  * @class Roo.bootstrap.PagingToolbar
21783  * @extends Roo.bootstrap.NavSimplebar
21784  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21785  * @constructor
21786  * Create a new PagingToolbar
21787  * @param {Object} config The config object
21788  * @param {Roo.data.Store} store
21789  */
21790 Roo.bootstrap.PagingToolbar = function(config)
21791 {
21792     // old args format still supported... - xtype is prefered..
21793         // created from xtype...
21794     
21795     this.ds = config.dataSource;
21796     
21797     if (config.store && !this.ds) {
21798         this.store= Roo.factory(config.store, Roo.data);
21799         this.ds = this.store;
21800         this.ds.xmodule = this.xmodule || false;
21801     }
21802     
21803     this.toolbarItems = [];
21804     if (config.items) {
21805         this.toolbarItems = config.items;
21806     }
21807     
21808     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21809     
21810     this.cursor = 0;
21811     
21812     if (this.ds) { 
21813         this.bind(this.ds);
21814     }
21815     
21816     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21817     
21818 };
21819
21820 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21821     /**
21822      * @cfg {Roo.data.Store} dataSource
21823      * The underlying data store providing the paged data
21824      */
21825     /**
21826      * @cfg {String/HTMLElement/Element} container
21827      * container The id or element that will contain the toolbar
21828      */
21829     /**
21830      * @cfg {Boolean} displayInfo
21831      * True to display the displayMsg (defaults to false)
21832      */
21833     /**
21834      * @cfg {Number} pageSize
21835      * The number of records to display per page (defaults to 20)
21836      */
21837     pageSize: 20,
21838     /**
21839      * @cfg {String} displayMsg
21840      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21841      */
21842     displayMsg : 'Displaying {0} - {1} of {2}',
21843     /**
21844      * @cfg {String} emptyMsg
21845      * The message to display when no records are found (defaults to "No data to display")
21846      */
21847     emptyMsg : 'No data to display',
21848     /**
21849      * Customizable piece of the default paging text (defaults to "Page")
21850      * @type String
21851      */
21852     beforePageText : "Page",
21853     /**
21854      * Customizable piece of the default paging text (defaults to "of %0")
21855      * @type String
21856      */
21857     afterPageText : "of {0}",
21858     /**
21859      * Customizable piece of the default paging text (defaults to "First Page")
21860      * @type String
21861      */
21862     firstText : "First Page",
21863     /**
21864      * Customizable piece of the default paging text (defaults to "Previous Page")
21865      * @type String
21866      */
21867     prevText : "Previous Page",
21868     /**
21869      * Customizable piece of the default paging text (defaults to "Next Page")
21870      * @type String
21871      */
21872     nextText : "Next Page",
21873     /**
21874      * Customizable piece of the default paging text (defaults to "Last Page")
21875      * @type String
21876      */
21877     lastText : "Last Page",
21878     /**
21879      * Customizable piece of the default paging text (defaults to "Refresh")
21880      * @type String
21881      */
21882     refreshText : "Refresh",
21883
21884     buttons : false,
21885     // private
21886     onRender : function(ct, position) 
21887     {
21888         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21889         this.navgroup.parentId = this.id;
21890         this.navgroup.onRender(this.el, null);
21891         // add the buttons to the navgroup
21892         
21893         if(this.displayInfo){
21894             Roo.log(this.el.select('ul.navbar-nav',true).first());
21895             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21896             this.displayEl = this.el.select('.x-paging-info', true).first();
21897 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21898 //            this.displayEl = navel.el.select('span',true).first();
21899         }
21900         
21901         var _this = this;
21902         
21903         if(this.buttons){
21904             Roo.each(_this.buttons, function(e){ // this might need to use render????
21905                Roo.factory(e).onRender(_this.el, null);
21906             });
21907         }
21908             
21909         Roo.each(_this.toolbarItems, function(e) {
21910             _this.navgroup.addItem(e);
21911         });
21912         
21913         
21914         this.first = this.navgroup.addItem({
21915             tooltip: this.firstText,
21916             cls: "prev",
21917             icon : 'fa fa-backward',
21918             disabled: true,
21919             preventDefault: true,
21920             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21921         });
21922         
21923         this.prev =  this.navgroup.addItem({
21924             tooltip: this.prevText,
21925             cls: "prev",
21926             icon : 'fa fa-step-backward',
21927             disabled: true,
21928             preventDefault: true,
21929             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21930         });
21931     //this.addSeparator();
21932         
21933         
21934         var field = this.navgroup.addItem( {
21935             tagtype : 'span',
21936             cls : 'x-paging-position',
21937             
21938             html : this.beforePageText  +
21939                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21940                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21941          } ); //?? escaped?
21942         
21943         this.field = field.el.select('input', true).first();
21944         this.field.on("keydown", this.onPagingKeydown, this);
21945         this.field.on("focus", function(){this.dom.select();});
21946     
21947     
21948         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21949         //this.field.setHeight(18);
21950         //this.addSeparator();
21951         this.next = this.navgroup.addItem({
21952             tooltip: this.nextText,
21953             cls: "next",
21954             html : ' <i class="fa fa-step-forward">',
21955             disabled: true,
21956             preventDefault: true,
21957             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21958         });
21959         this.last = this.navgroup.addItem({
21960             tooltip: this.lastText,
21961             icon : 'fa fa-forward',
21962             cls: "next",
21963             disabled: true,
21964             preventDefault: true,
21965             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21966         });
21967     //this.addSeparator();
21968         this.loading = this.navgroup.addItem({
21969             tooltip: this.refreshText,
21970             icon: 'fa fa-refresh',
21971             preventDefault: true,
21972             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21973         });
21974         
21975     },
21976
21977     // private
21978     updateInfo : function(){
21979         if(this.displayEl){
21980             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21981             var msg = count == 0 ?
21982                 this.emptyMsg :
21983                 String.format(
21984                     this.displayMsg,
21985                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21986                 );
21987             this.displayEl.update(msg);
21988         }
21989     },
21990
21991     // private
21992     onLoad : function(ds, r, o){
21993        this.cursor = o.params ? o.params.start : 0;
21994        var d = this.getPageData(),
21995             ap = d.activePage,
21996             ps = d.pages;
21997         
21998        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21999        this.field.dom.value = ap;
22000        this.first.setDisabled(ap == 1);
22001        this.prev.setDisabled(ap == 1);
22002        this.next.setDisabled(ap == ps);
22003        this.last.setDisabled(ap == ps);
22004        this.loading.enable();
22005        this.updateInfo();
22006     },
22007
22008     // private
22009     getPageData : function(){
22010         var total = this.ds.getTotalCount();
22011         return {
22012             total : total,
22013             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22014             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22015         };
22016     },
22017
22018     // private
22019     onLoadError : function(){
22020         this.loading.enable();
22021     },
22022
22023     // private
22024     onPagingKeydown : function(e){
22025         var k = e.getKey();
22026         var d = this.getPageData();
22027         if(k == e.RETURN){
22028             var v = this.field.dom.value, pageNum;
22029             if(!v || isNaN(pageNum = parseInt(v, 10))){
22030                 this.field.dom.value = d.activePage;
22031                 return;
22032             }
22033             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22034             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22035             e.stopEvent();
22036         }
22037         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))
22038         {
22039           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22040           this.field.dom.value = pageNum;
22041           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22042           e.stopEvent();
22043         }
22044         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22045         {
22046           var v = this.field.dom.value, pageNum; 
22047           var increment = (e.shiftKey) ? 10 : 1;
22048           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22049             increment *= -1;
22050           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22051             this.field.dom.value = d.activePage;
22052             return;
22053           }
22054           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22055           {
22056             this.field.dom.value = parseInt(v, 10) + increment;
22057             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22058             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22059           }
22060           e.stopEvent();
22061         }
22062     },
22063
22064     // private
22065     beforeLoad : function(){
22066         if(this.loading){
22067             this.loading.disable();
22068         }
22069     },
22070
22071     // private
22072     onClick : function(which){
22073         
22074         var ds = this.ds;
22075         if (!ds) {
22076             return;
22077         }
22078         
22079         switch(which){
22080             case "first":
22081                 ds.load({params:{start: 0, limit: this.pageSize}});
22082             break;
22083             case "prev":
22084                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22085             break;
22086             case "next":
22087                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22088             break;
22089             case "last":
22090                 var total = ds.getTotalCount();
22091                 var extra = total % this.pageSize;
22092                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22093                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22094             break;
22095             case "refresh":
22096                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22097             break;
22098         }
22099     },
22100
22101     /**
22102      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22103      * @param {Roo.data.Store} store The data store to unbind
22104      */
22105     unbind : function(ds){
22106         ds.un("beforeload", this.beforeLoad, this);
22107         ds.un("load", this.onLoad, this);
22108         ds.un("loadexception", this.onLoadError, this);
22109         ds.un("remove", this.updateInfo, this);
22110         ds.un("add", this.updateInfo, this);
22111         this.ds = undefined;
22112     },
22113
22114     /**
22115      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22116      * @param {Roo.data.Store} store The data store to bind
22117      */
22118     bind : function(ds){
22119         ds.on("beforeload", this.beforeLoad, this);
22120         ds.on("load", this.onLoad, this);
22121         ds.on("loadexception", this.onLoadError, this);
22122         ds.on("remove", this.updateInfo, this);
22123         ds.on("add", this.updateInfo, this);
22124         this.ds = ds;
22125     }
22126 });/*
22127  * - LGPL
22128  *
22129  * element
22130  * 
22131  */
22132
22133 /**
22134  * @class Roo.bootstrap.MessageBar
22135  * @extends Roo.bootstrap.Component
22136  * Bootstrap MessageBar class
22137  * @cfg {String} html contents of the MessageBar
22138  * @cfg {String} weight (info | success | warning | danger) default info
22139  * @cfg {String} beforeClass insert the bar before the given class
22140  * @cfg {Boolean} closable (true | false) default false
22141  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22142  * 
22143  * @constructor
22144  * Create a new Element
22145  * @param {Object} config The config object
22146  */
22147
22148 Roo.bootstrap.MessageBar = function(config){
22149     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22150 };
22151
22152 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22153     
22154     html: '',
22155     weight: 'info',
22156     closable: false,
22157     fixed: false,
22158     beforeClass: 'bootstrap-sticky-wrap',
22159     
22160     getAutoCreate : function(){
22161         
22162         var cfg = {
22163             tag: 'div',
22164             cls: 'alert alert-dismissable alert-' + this.weight,
22165             cn: [
22166                 {
22167                     tag: 'span',
22168                     cls: 'message',
22169                     html: this.html || ''
22170                 }
22171             ]
22172         }
22173         
22174         if(this.fixed){
22175             cfg.cls += ' alert-messages-fixed';
22176         }
22177         
22178         if(this.closable){
22179             cfg.cn.push({
22180                 tag: 'button',
22181                 cls: 'close',
22182                 html: 'x'
22183             });
22184         }
22185         
22186         return cfg;
22187     },
22188     
22189     onRender : function(ct, position)
22190     {
22191         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22192         
22193         if(!this.el){
22194             var cfg = Roo.apply({},  this.getAutoCreate());
22195             cfg.id = Roo.id();
22196             
22197             if (this.cls) {
22198                 cfg.cls += ' ' + this.cls;
22199             }
22200             if (this.style) {
22201                 cfg.style = this.style;
22202             }
22203             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22204             
22205             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22206         }
22207         
22208         this.el.select('>button.close').on('click', this.hide, this);
22209         
22210     },
22211     
22212     show : function()
22213     {
22214         if (!this.rendered) {
22215             this.render();
22216         }
22217         
22218         this.el.show();
22219         
22220         this.fireEvent('show', this);
22221         
22222     },
22223     
22224     hide : function()
22225     {
22226         if (!this.rendered) {
22227             this.render();
22228         }
22229         
22230         this.el.hide();
22231         
22232         this.fireEvent('hide', this);
22233     },
22234     
22235     update : function()
22236     {
22237 //        var e = this.el.dom.firstChild;
22238 //        
22239 //        if(this.closable){
22240 //            e = e.nextSibling;
22241 //        }
22242 //        
22243 //        e.data = this.html || '';
22244
22245         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22246     }
22247    
22248 });
22249
22250  
22251
22252      /*
22253  * - LGPL
22254  *
22255  * Graph
22256  * 
22257  */
22258
22259
22260 /**
22261  * @class Roo.bootstrap.Graph
22262  * @extends Roo.bootstrap.Component
22263  * Bootstrap Graph class
22264 > Prameters
22265  -sm {number} sm 4
22266  -md {number} md 5
22267  @cfg {String} graphtype  bar | vbar | pie
22268  @cfg {number} g_x coodinator | centre x (pie)
22269  @cfg {number} g_y coodinator | centre y (pie)
22270  @cfg {number} g_r radius (pie)
22271  @cfg {number} g_height height of the chart (respected by all elements in the set)
22272  @cfg {number} g_width width of the chart (respected by all elements in the set)
22273  @cfg {Object} title The title of the chart
22274     
22275  -{Array}  values
22276  -opts (object) options for the chart 
22277      o {
22278      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22279      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22280      o vgutter (number)
22281      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.
22282      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22283      o to
22284      o stretch (boolean)
22285      o }
22286  -opts (object) options for the pie
22287      o{
22288      o cut
22289      o startAngle (number)
22290      o endAngle (number)
22291      } 
22292  *
22293  * @constructor
22294  * Create a new Input
22295  * @param {Object} config The config object
22296  */
22297
22298 Roo.bootstrap.Graph = function(config){
22299     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22300     
22301     this.addEvents({
22302         // img events
22303         /**
22304          * @event click
22305          * The img click event for the img.
22306          * @param {Roo.EventObject} e
22307          */
22308         "click" : true
22309     });
22310 };
22311
22312 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22313     
22314     sm: 4,
22315     md: 5,
22316     graphtype: 'bar',
22317     g_height: 250,
22318     g_width: 400,
22319     g_x: 50,
22320     g_y: 50,
22321     g_r: 30,
22322     opts:{
22323         //g_colors: this.colors,
22324         g_type: 'soft',
22325         g_gutter: '20%'
22326
22327     },
22328     title : false,
22329
22330     getAutoCreate : function(){
22331         
22332         var cfg = {
22333             tag: 'div',
22334             html : null
22335         }
22336         
22337         
22338         return  cfg;
22339     },
22340
22341     onRender : function(ct,position){
22342         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22343         this.raphael = Raphael(this.el.dom);
22344         
22345                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22346                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22347                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22348                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22349                 /*
22350                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22351                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22352                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22353                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22354                 
22355                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22356                 r.barchart(330, 10, 300, 220, data1);
22357                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22358                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22359                 */
22360                 
22361                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22362                 // r.barchart(30, 30, 560, 250,  xdata, {
22363                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22364                 //     axis : "0 0 1 1",
22365                 //     axisxlabels :  xdata
22366                 //     //yvalues : cols,
22367                    
22368                 // });
22369 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22370 //        
22371 //        this.load(null,xdata,{
22372 //                axis : "0 0 1 1",
22373 //                axisxlabels :  xdata
22374 //                });
22375
22376     },
22377
22378     load : function(graphtype,xdata,opts){
22379         this.raphael.clear();
22380         if(!graphtype) {
22381             graphtype = this.graphtype;
22382         }
22383         if(!opts){
22384             opts = this.opts;
22385         }
22386         var r = this.raphael,
22387             fin = function () {
22388                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22389             },
22390             fout = function () {
22391                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22392             },
22393             pfin = function() {
22394                 this.sector.stop();
22395                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22396
22397                 if (this.label) {
22398                     this.label[0].stop();
22399                     this.label[0].attr({ r: 7.5 });
22400                     this.label[1].attr({ "font-weight": 800 });
22401                 }
22402             },
22403             pfout = function() {
22404                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22405
22406                 if (this.label) {
22407                     this.label[0].animate({ r: 5 }, 500, "bounce");
22408                     this.label[1].attr({ "font-weight": 400 });
22409                 }
22410             };
22411
22412         switch(graphtype){
22413             case 'bar':
22414                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22415                 break;
22416             case 'hbar':
22417                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22418                 break;
22419             case 'pie':
22420 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22421 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22422 //            
22423                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22424                 
22425                 break;
22426
22427         }
22428         
22429         if(this.title){
22430             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22431         }
22432         
22433     },
22434     
22435     setTitle: function(o)
22436     {
22437         this.title = o;
22438     },
22439     
22440     initEvents: function() {
22441         
22442         if(!this.href){
22443             this.el.on('click', this.onClick, this);
22444         }
22445     },
22446     
22447     onClick : function(e)
22448     {
22449         Roo.log('img onclick');
22450         this.fireEvent('click', this, e);
22451     }
22452    
22453 });
22454
22455  
22456 /*
22457  * - LGPL
22458  *
22459  * numberBox
22460  * 
22461  */
22462 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22463
22464 /**
22465  * @class Roo.bootstrap.dash.NumberBox
22466  * @extends Roo.bootstrap.Component
22467  * Bootstrap NumberBox class
22468  * @cfg {String} headline Box headline
22469  * @cfg {String} content Box content
22470  * @cfg {String} icon Box icon
22471  * @cfg {String} footer Footer text
22472  * @cfg {String} fhref Footer href
22473  * 
22474  * @constructor
22475  * Create a new NumberBox
22476  * @param {Object} config The config object
22477  */
22478
22479
22480 Roo.bootstrap.dash.NumberBox = function(config){
22481     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22482     
22483 };
22484
22485 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22486     
22487     headline : '',
22488     content : '',
22489     icon : '',
22490     footer : '',
22491     fhref : '',
22492     ficon : '',
22493     
22494     getAutoCreate : function(){
22495         
22496         var cfg = {
22497             tag : 'div',
22498             cls : 'small-box ',
22499             cn : [
22500                 {
22501                     tag : 'div',
22502                     cls : 'inner',
22503                     cn :[
22504                         {
22505                             tag : 'h3',
22506                             cls : 'roo-headline',
22507                             html : this.headline
22508                         },
22509                         {
22510                             tag : 'p',
22511                             cls : 'roo-content',
22512                             html : this.content
22513                         }
22514                     ]
22515                 }
22516             ]
22517         }
22518         
22519         if(this.icon){
22520             cfg.cn.push({
22521                 tag : 'div',
22522                 cls : 'icon',
22523                 cn :[
22524                     {
22525                         tag : 'i',
22526                         cls : 'ion ' + this.icon
22527                     }
22528                 ]
22529             });
22530         }
22531         
22532         if(this.footer){
22533             var footer = {
22534                 tag : 'a',
22535                 cls : 'small-box-footer',
22536                 href : this.fhref || '#',
22537                 html : this.footer
22538             };
22539             
22540             cfg.cn.push(footer);
22541             
22542         }
22543         
22544         return  cfg;
22545     },
22546
22547     onRender : function(ct,position){
22548         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22549
22550
22551        
22552                 
22553     },
22554
22555     setHeadline: function (value)
22556     {
22557         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22558     },
22559     
22560     setFooter: function (value, href)
22561     {
22562         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22563         
22564         if(href){
22565             this.el.select('a.small-box-footer',true).first().attr('href', href);
22566         }
22567         
22568     },
22569
22570     setContent: function (value)
22571     {
22572         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22573     },
22574
22575     initEvents: function() 
22576     {   
22577         
22578     }
22579     
22580 });
22581
22582  
22583 /*
22584  * - LGPL
22585  *
22586  * TabBox
22587  * 
22588  */
22589 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22590
22591 /**
22592  * @class Roo.bootstrap.dash.TabBox
22593  * @extends Roo.bootstrap.Component
22594  * Bootstrap TabBox class
22595  * @cfg {String} title Title of the TabBox
22596  * @cfg {String} icon Icon of the TabBox
22597  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22598  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22599  * 
22600  * @constructor
22601  * Create a new TabBox
22602  * @param {Object} config The config object
22603  */
22604
22605
22606 Roo.bootstrap.dash.TabBox = function(config){
22607     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22608     this.addEvents({
22609         // raw events
22610         /**
22611          * @event addpane
22612          * When a pane is added
22613          * @param {Roo.bootstrap.dash.TabPane} pane
22614          */
22615         "addpane" : true,
22616         /**
22617          * @event activatepane
22618          * When a pane is activated
22619          * @param {Roo.bootstrap.dash.TabPane} pane
22620          */
22621         "activatepane" : true
22622         
22623          
22624     });
22625     
22626     this.panes = [];
22627 };
22628
22629 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22630
22631     title : '',
22632     icon : false,
22633     showtabs : true,
22634     tabScrollable : false,
22635     
22636     getChildContainer : function()
22637     {
22638         return this.el.select('.tab-content', true).first();
22639     },
22640     
22641     getAutoCreate : function(){
22642         
22643         var header = {
22644             tag: 'li',
22645             cls: 'pull-left header',
22646             html: this.title,
22647             cn : []
22648         };
22649         
22650         if(this.icon){
22651             header.cn.push({
22652                 tag: 'i',
22653                 cls: 'fa ' + this.icon
22654             });
22655         }
22656         
22657         var h = {
22658             tag: 'ul',
22659             cls: 'nav nav-tabs pull-right',
22660             cn: [
22661                 header
22662             ]
22663         };
22664         
22665         if(this.tabScrollable){
22666             h = {
22667                 tag: 'div',
22668                 cls: 'tab-header',
22669                 cn: [
22670                     {
22671                         tag: 'ul',
22672                         cls: 'nav nav-tabs pull-right',
22673                         cn: [
22674                             header
22675                         ]
22676                     }
22677                 ]
22678             }
22679         }
22680         
22681         var cfg = {
22682             tag: 'div',
22683             cls: 'nav-tabs-custom',
22684             cn: [
22685                 h,
22686                 {
22687                     tag: 'div',
22688                     cls: 'tab-content no-padding',
22689                     cn: []
22690                 }
22691             ]
22692         }
22693
22694         return  cfg;
22695     },
22696     initEvents : function()
22697     {
22698         //Roo.log('add add pane handler');
22699         this.on('addpane', this.onAddPane, this);
22700     },
22701      /**
22702      * Updates the box title
22703      * @param {String} html to set the title to.
22704      */
22705     setTitle : function(value)
22706     {
22707         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22708     },
22709     onAddPane : function(pane)
22710     {
22711         this.panes.push(pane);
22712         //Roo.log('addpane');
22713         //Roo.log(pane);
22714         // tabs are rendere left to right..
22715         if(!this.showtabs){
22716             return;
22717         }
22718         
22719         var ctr = this.el.select('.nav-tabs', true).first();
22720          
22721          
22722         var existing = ctr.select('.nav-tab',true);
22723         var qty = existing.getCount();;
22724         
22725         
22726         var tab = ctr.createChild({
22727             tag : 'li',
22728             cls : 'nav-tab' + (qty ? '' : ' active'),
22729             cn : [
22730                 {
22731                     tag : 'a',
22732                     href:'#',
22733                     html : pane.title
22734                 }
22735             ]
22736         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22737         pane.tab = tab;
22738         
22739         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22740         if (!qty) {
22741             pane.el.addClass('active');
22742         }
22743         
22744                 
22745     },
22746     onTabClick : function(ev,un,ob,pane)
22747     {
22748         //Roo.log('tab - prev default');
22749         ev.preventDefault();
22750         
22751         
22752         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22753         pane.tab.addClass('active');
22754         //Roo.log(pane.title);
22755         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22756         // technically we should have a deactivate event.. but maybe add later.
22757         // and it should not de-activate the selected tab...
22758         this.fireEvent('activatepane', pane);
22759         pane.el.addClass('active');
22760         pane.fireEvent('activate');
22761         
22762         
22763     },
22764     
22765     getActivePane : function()
22766     {
22767         var r = false;
22768         Roo.each(this.panes, function(p) {
22769             if(p.el.hasClass('active')){
22770                 r = p;
22771                 return false;
22772             }
22773             
22774             return;
22775         });
22776         
22777         return r;
22778     }
22779     
22780     
22781 });
22782
22783  
22784 /*
22785  * - LGPL
22786  *
22787  * Tab pane
22788  * 
22789  */
22790 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22791 /**
22792  * @class Roo.bootstrap.TabPane
22793  * @extends Roo.bootstrap.Component
22794  * Bootstrap TabPane class
22795  * @cfg {Boolean} active (false | true) Default false
22796  * @cfg {String} title title of panel
22797
22798  * 
22799  * @constructor
22800  * Create a new TabPane
22801  * @param {Object} config The config object
22802  */
22803
22804 Roo.bootstrap.dash.TabPane = function(config){
22805     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22806     
22807     this.addEvents({
22808         // raw events
22809         /**
22810          * @event activate
22811          * When a pane is activated
22812          * @param {Roo.bootstrap.dash.TabPane} pane
22813          */
22814         "activate" : true
22815          
22816     });
22817 };
22818
22819 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22820     
22821     active : false,
22822     title : '',
22823     
22824     // the tabBox that this is attached to.
22825     tab : false,
22826      
22827     getAutoCreate : function() 
22828     {
22829         var cfg = {
22830             tag: 'div',
22831             cls: 'tab-pane'
22832         }
22833         
22834         if(this.active){
22835             cfg.cls += ' active';
22836         }
22837         
22838         return cfg;
22839     },
22840     initEvents  : function()
22841     {
22842         //Roo.log('trigger add pane handler');
22843         this.parent().fireEvent('addpane', this)
22844     },
22845     
22846      /**
22847      * Updates the tab title 
22848      * @param {String} html to set the title to.
22849      */
22850     setTitle: function(str)
22851     {
22852         if (!this.tab) {
22853             return;
22854         }
22855         this.title = str;
22856         this.tab.select('a', true).first().dom.innerHTML = str;
22857         
22858     }
22859     
22860     
22861     
22862 });
22863
22864  
22865
22866
22867  /*
22868  * - LGPL
22869  *
22870  * menu
22871  * 
22872  */
22873 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22874
22875 /**
22876  * @class Roo.bootstrap.menu.Menu
22877  * @extends Roo.bootstrap.Component
22878  * Bootstrap Menu class - container for Menu
22879  * @cfg {String} html Text of the menu
22880  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22881  * @cfg {String} icon Font awesome icon
22882  * @cfg {String} pos Menu align to (top | bottom) default bottom
22883  * 
22884  * 
22885  * @constructor
22886  * Create a new Menu
22887  * @param {Object} config The config object
22888  */
22889
22890
22891 Roo.bootstrap.menu.Menu = function(config){
22892     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22893     
22894     this.addEvents({
22895         /**
22896          * @event beforeshow
22897          * Fires before this menu is displayed
22898          * @param {Roo.bootstrap.menu.Menu} this
22899          */
22900         beforeshow : true,
22901         /**
22902          * @event beforehide
22903          * Fires before this menu is hidden
22904          * @param {Roo.bootstrap.menu.Menu} this
22905          */
22906         beforehide : true,
22907         /**
22908          * @event show
22909          * Fires after this menu is displayed
22910          * @param {Roo.bootstrap.menu.Menu} this
22911          */
22912         show : true,
22913         /**
22914          * @event hide
22915          * Fires after this menu is hidden
22916          * @param {Roo.bootstrap.menu.Menu} this
22917          */
22918         hide : true,
22919         /**
22920          * @event click
22921          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22922          * @param {Roo.bootstrap.menu.Menu} this
22923          * @param {Roo.EventObject} e
22924          */
22925         click : true
22926     });
22927     
22928 };
22929
22930 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22931     
22932     submenu : false,
22933     html : '',
22934     weight : 'default',
22935     icon : false,
22936     pos : 'bottom',
22937     
22938     
22939     getChildContainer : function() {
22940         if(this.isSubMenu){
22941             return this.el;
22942         }
22943         
22944         return this.el.select('ul.dropdown-menu', true).first();  
22945     },
22946     
22947     getAutoCreate : function()
22948     {
22949         var text = [
22950             {
22951                 tag : 'span',
22952                 cls : 'roo-menu-text',
22953                 html : this.html
22954             }
22955         ];
22956         
22957         if(this.icon){
22958             text.unshift({
22959                 tag : 'i',
22960                 cls : 'fa ' + this.icon
22961             })
22962         }
22963         
22964         
22965         var cfg = {
22966             tag : 'div',
22967             cls : 'btn-group',
22968             cn : [
22969                 {
22970                     tag : 'button',
22971                     cls : 'dropdown-button btn btn-' + this.weight,
22972                     cn : text
22973                 },
22974                 {
22975                     tag : 'button',
22976                     cls : 'dropdown-toggle btn btn-' + this.weight,
22977                     cn : [
22978                         {
22979                             tag : 'span',
22980                             cls : 'caret'
22981                         }
22982                     ]
22983                 },
22984                 {
22985                     tag : 'ul',
22986                     cls : 'dropdown-menu'
22987                 }
22988             ]
22989             
22990         };
22991         
22992         if(this.pos == 'top'){
22993             cfg.cls += ' dropup';
22994         }
22995         
22996         if(this.isSubMenu){
22997             cfg = {
22998                 tag : 'ul',
22999                 cls : 'dropdown-menu'
23000             }
23001         }
23002         
23003         return cfg;
23004     },
23005     
23006     onRender : function(ct, position)
23007     {
23008         this.isSubMenu = ct.hasClass('dropdown-submenu');
23009         
23010         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23011     },
23012     
23013     initEvents : function() 
23014     {
23015         if(this.isSubMenu){
23016             return;
23017         }
23018         
23019         this.hidden = true;
23020         
23021         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23022         this.triggerEl.on('click', this.onTriggerPress, this);
23023         
23024         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23025         this.buttonEl.on('click', this.onClick, this);
23026         
23027     },
23028     
23029     list : function()
23030     {
23031         if(this.isSubMenu){
23032             return this.el;
23033         }
23034         
23035         return this.el.select('ul.dropdown-menu', true).first();
23036     },
23037     
23038     onClick : function(e)
23039     {
23040         this.fireEvent("click", this, e);
23041     },
23042     
23043     onTriggerPress  : function(e)
23044     {   
23045         if (this.isVisible()) {
23046             this.hide();
23047         } else {
23048             this.show();
23049         }
23050     },
23051     
23052     isVisible : function(){
23053         return !this.hidden;
23054     },
23055     
23056     show : function()
23057     {
23058         this.fireEvent("beforeshow", this);
23059         
23060         this.hidden = false;
23061         this.el.addClass('open');
23062         
23063         Roo.get(document).on("mouseup", this.onMouseUp, this);
23064         
23065         this.fireEvent("show", this);
23066         
23067         
23068     },
23069     
23070     hide : function()
23071     {
23072         this.fireEvent("beforehide", this);
23073         
23074         this.hidden = true;
23075         this.el.removeClass('open');
23076         
23077         Roo.get(document).un("mouseup", this.onMouseUp);
23078         
23079         this.fireEvent("hide", this);
23080     },
23081     
23082     onMouseUp : function()
23083     {
23084         this.hide();
23085     }
23086     
23087 });
23088
23089  
23090  /*
23091  * - LGPL
23092  *
23093  * menu item
23094  * 
23095  */
23096 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23097
23098 /**
23099  * @class Roo.bootstrap.menu.Item
23100  * @extends Roo.bootstrap.Component
23101  * Bootstrap MenuItem class
23102  * @cfg {Boolean} submenu (true | false) default false
23103  * @cfg {String} html text of the item
23104  * @cfg {String} href the link
23105  * @cfg {Boolean} disable (true | false) default false
23106  * @cfg {Boolean} preventDefault (true | false) default true
23107  * @cfg {String} icon Font awesome icon
23108  * @cfg {String} pos Submenu align to (left | right) default right 
23109  * 
23110  * 
23111  * @constructor
23112  * Create a new Item
23113  * @param {Object} config The config object
23114  */
23115
23116
23117 Roo.bootstrap.menu.Item = function(config){
23118     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23119     this.addEvents({
23120         /**
23121          * @event mouseover
23122          * Fires when the mouse is hovering over this menu
23123          * @param {Roo.bootstrap.menu.Item} this
23124          * @param {Roo.EventObject} e
23125          */
23126         mouseover : true,
23127         /**
23128          * @event mouseout
23129          * Fires when the mouse exits this menu
23130          * @param {Roo.bootstrap.menu.Item} this
23131          * @param {Roo.EventObject} e
23132          */
23133         mouseout : true,
23134         // raw events
23135         /**
23136          * @event click
23137          * The raw click event for the entire grid.
23138          * @param {Roo.EventObject} e
23139          */
23140         click : true
23141     });
23142 };
23143
23144 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23145     
23146     submenu : false,
23147     href : '',
23148     html : '',
23149     preventDefault: true,
23150     disable : false,
23151     icon : false,
23152     pos : 'right',
23153     
23154     getAutoCreate : function()
23155     {
23156         var text = [
23157             {
23158                 tag : 'span',
23159                 cls : 'roo-menu-item-text',
23160                 html : this.html
23161             }
23162         ];
23163         
23164         if(this.icon){
23165             text.unshift({
23166                 tag : 'i',
23167                 cls : 'fa ' + this.icon
23168             })
23169         }
23170         
23171         var cfg = {
23172             tag : 'li',
23173             cn : [
23174                 {
23175                     tag : 'a',
23176                     href : this.href || '#',
23177                     cn : text
23178                 }
23179             ]
23180         };
23181         
23182         if(this.disable){
23183             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23184         }
23185         
23186         if(this.submenu){
23187             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23188             
23189             if(this.pos == 'left'){
23190                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23191             }
23192         }
23193         
23194         return cfg;
23195     },
23196     
23197     initEvents : function() 
23198     {
23199         this.el.on('mouseover', this.onMouseOver, this);
23200         this.el.on('mouseout', this.onMouseOut, this);
23201         
23202         this.el.select('a', true).first().on('click', this.onClick, this);
23203         
23204     },
23205     
23206     onClick : function(e)
23207     {
23208         if(this.preventDefault){
23209             e.preventDefault();
23210         }
23211         
23212         this.fireEvent("click", this, e);
23213     },
23214     
23215     onMouseOver : function(e)
23216     {
23217         if(this.submenu && this.pos == 'left'){
23218             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23219         }
23220         
23221         this.fireEvent("mouseover", this, e);
23222     },
23223     
23224     onMouseOut : function(e)
23225     {
23226         this.fireEvent("mouseout", this, e);
23227     }
23228 });
23229
23230  
23231
23232  /*
23233  * - LGPL
23234  *
23235  * menu separator
23236  * 
23237  */
23238 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23239
23240 /**
23241  * @class Roo.bootstrap.menu.Separator
23242  * @extends Roo.bootstrap.Component
23243  * Bootstrap Separator class
23244  * 
23245  * @constructor
23246  * Create a new Separator
23247  * @param {Object} config The config object
23248  */
23249
23250
23251 Roo.bootstrap.menu.Separator = function(config){
23252     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23253 };
23254
23255 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23256     
23257     getAutoCreate : function(){
23258         var cfg = {
23259             tag : 'li',
23260             cls: 'divider'
23261         };
23262         
23263         return cfg;
23264     }
23265    
23266 });
23267
23268  
23269
23270  /*
23271  * - LGPL
23272  *
23273  * Tooltip
23274  * 
23275  */
23276
23277 /**
23278  * @class Roo.bootstrap.Tooltip
23279  * Bootstrap Tooltip class
23280  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23281  * to determine which dom element triggers the tooltip.
23282  * 
23283  * It needs to add support for additional attributes like tooltip-position
23284  * 
23285  * @constructor
23286  * Create a new Toolti
23287  * @param {Object} config The config object
23288  */
23289
23290 Roo.bootstrap.Tooltip = function(config){
23291     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23292 };
23293
23294 Roo.apply(Roo.bootstrap.Tooltip, {
23295     /**
23296      * @function init initialize tooltip monitoring.
23297      * @static
23298      */
23299     currentEl : false,
23300     currentTip : false,
23301     currentRegion : false,
23302     
23303     //  init : delay?
23304     
23305     init : function()
23306     {
23307         Roo.get(document).on('mouseover', this.enter ,this);
23308         Roo.get(document).on('mouseout', this.leave, this);
23309          
23310         
23311         this.currentTip = new Roo.bootstrap.Tooltip();
23312     },
23313     
23314     enter : function(ev)
23315     {
23316         var dom = ev.getTarget();
23317         
23318         //Roo.log(['enter',dom]);
23319         var el = Roo.fly(dom);
23320         if (this.currentEl) {
23321             //Roo.log(dom);
23322             //Roo.log(this.currentEl);
23323             //Roo.log(this.currentEl.contains(dom));
23324             if (this.currentEl == el) {
23325                 return;
23326             }
23327             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23328                 return;
23329             }
23330
23331         }
23332         
23333         if (this.currentTip.el) {
23334             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23335         }    
23336         //Roo.log(ev);
23337         var bindEl = el;
23338         
23339         // you can not look for children, as if el is the body.. then everythign is the child..
23340         if (!el.attr('tooltip')) { //
23341             if (!el.select("[tooltip]").elements.length) {
23342                 return;
23343             }
23344             // is the mouse over this child...?
23345             bindEl = el.select("[tooltip]").first();
23346             var xy = ev.getXY();
23347             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23348                 //Roo.log("not in region.");
23349                 return;
23350             }
23351             //Roo.log("child element over..");
23352             
23353         }
23354         this.currentEl = bindEl;
23355         this.currentTip.bind(bindEl);
23356         this.currentRegion = Roo.lib.Region.getRegion(dom);
23357         this.currentTip.enter();
23358         
23359     },
23360     leave : function(ev)
23361     {
23362         var dom = ev.getTarget();
23363         //Roo.log(['leave',dom]);
23364         if (!this.currentEl) {
23365             return;
23366         }
23367         
23368         
23369         if (dom != this.currentEl.dom) {
23370             return;
23371         }
23372         var xy = ev.getXY();
23373         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23374             return;
23375         }
23376         // only activate leave if mouse cursor is outside... bounding box..
23377         
23378         
23379         
23380         
23381         if (this.currentTip) {
23382             this.currentTip.leave();
23383         }
23384         //Roo.log('clear currentEl');
23385         this.currentEl = false;
23386         
23387         
23388     },
23389     alignment : {
23390         'left' : ['r-l', [-2,0], 'right'],
23391         'right' : ['l-r', [2,0], 'left'],
23392         'bottom' : ['t-b', [0,2], 'top'],
23393         'top' : [ 'b-t', [0,-2], 'bottom']
23394     }
23395     
23396 });
23397
23398
23399 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23400     
23401     
23402     bindEl : false,
23403     
23404     delay : null, // can be { show : 300 , hide: 500}
23405     
23406     timeout : null,
23407     
23408     hoverState : null, //???
23409     
23410     placement : 'bottom', 
23411     
23412     getAutoCreate : function(){
23413     
23414         var cfg = {
23415            cls : 'tooltip',
23416            role : 'tooltip',
23417            cn : [
23418                 {
23419                     cls : 'tooltip-arrow'
23420                 },
23421                 {
23422                     cls : 'tooltip-inner'
23423                 }
23424            ]
23425         };
23426         
23427         return cfg;
23428     },
23429     bind : function(el)
23430     {
23431         this.bindEl = el;
23432     },
23433       
23434     
23435     enter : function () {
23436        
23437         if (this.timeout != null) {
23438             clearTimeout(this.timeout);
23439         }
23440         
23441         this.hoverState = 'in';
23442          //Roo.log("enter - show");
23443         if (!this.delay || !this.delay.show) {
23444             this.show();
23445             return;
23446         }
23447         var _t = this;
23448         this.timeout = setTimeout(function () {
23449             if (_t.hoverState == 'in') {
23450                 _t.show();
23451             }
23452         }, this.delay.show);
23453     },
23454     leave : function()
23455     {
23456         clearTimeout(this.timeout);
23457     
23458         this.hoverState = 'out';
23459          if (!this.delay || !this.delay.hide) {
23460             this.hide();
23461             return;
23462         }
23463        
23464         var _t = this;
23465         this.timeout = setTimeout(function () {
23466             //Roo.log("leave - timeout");
23467             
23468             if (_t.hoverState == 'out') {
23469                 _t.hide();
23470                 Roo.bootstrap.Tooltip.currentEl = false;
23471             }
23472         }, delay);
23473     },
23474     
23475     show : function ()
23476     {
23477         if (!this.el) {
23478             this.render(document.body);
23479         }
23480         // set content.
23481         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23482         
23483         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23484         
23485         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23486         
23487         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23488         
23489         var placement = typeof this.placement == 'function' ?
23490             this.placement.call(this, this.el, on_el) :
23491             this.placement;
23492             
23493         var autoToken = /\s?auto?\s?/i;
23494         var autoPlace = autoToken.test(placement);
23495         if (autoPlace) {
23496             placement = placement.replace(autoToken, '') || 'top';
23497         }
23498         
23499         //this.el.detach()
23500         //this.el.setXY([0,0]);
23501         this.el.show();
23502         //this.el.dom.style.display='block';
23503         
23504         //this.el.appendTo(on_el);
23505         
23506         var p = this.getPosition();
23507         var box = this.el.getBox();
23508         
23509         if (autoPlace) {
23510             // fixme..
23511         }
23512         
23513         var align = Roo.bootstrap.Tooltip.alignment[placement];
23514         
23515         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23516         
23517         if(placement == 'top' || placement == 'bottom'){
23518             if(xy[0] < 0){
23519                 placement = 'right';
23520             }
23521             
23522             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23523                 placement = 'left';
23524             }
23525         }
23526         
23527         align = Roo.bootstrap.Tooltip.alignment[placement];
23528         
23529         this.el.alignTo(this.bindEl, align[0],align[1]);
23530         //var arrow = this.el.select('.arrow',true).first();
23531         //arrow.set(align[2], 
23532         
23533         this.el.addClass(placement);
23534         
23535         this.el.addClass('in fade');
23536         
23537         this.hoverState = null;
23538         
23539         if (this.el.hasClass('fade')) {
23540             // fade it?
23541         }
23542         
23543     },
23544     hide : function()
23545     {
23546          
23547         if (!this.el) {
23548             return;
23549         }
23550         //this.el.setXY([0,0]);
23551         this.el.removeClass('in');
23552         //this.el.hide();
23553         
23554     }
23555     
23556 });
23557  
23558
23559  /*
23560  * - LGPL
23561  *
23562  * Location Picker
23563  * 
23564  */
23565
23566 /**
23567  * @class Roo.bootstrap.LocationPicker
23568  * @extends Roo.bootstrap.Component
23569  * Bootstrap LocationPicker class
23570  * @cfg {Number} latitude Position when init default 0
23571  * @cfg {Number} longitude Position when init default 0
23572  * @cfg {Number} zoom default 15
23573  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23574  * @cfg {Boolean} mapTypeControl default false
23575  * @cfg {Boolean} disableDoubleClickZoom default false
23576  * @cfg {Boolean} scrollwheel default true
23577  * @cfg {Boolean} streetViewControl default false
23578  * @cfg {Number} radius default 0
23579  * @cfg {String} locationName
23580  * @cfg {Boolean} draggable default true
23581  * @cfg {Boolean} enableAutocomplete default false
23582  * @cfg {Boolean} enableReverseGeocode default true
23583  * @cfg {String} markerTitle
23584  * 
23585  * @constructor
23586  * Create a new LocationPicker
23587  * @param {Object} config The config object
23588  */
23589
23590
23591 Roo.bootstrap.LocationPicker = function(config){
23592     
23593     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23594     
23595     this.addEvents({
23596         /**
23597          * @event initial
23598          * Fires when the picker initialized.
23599          * @param {Roo.bootstrap.LocationPicker} this
23600          * @param {Google Location} location
23601          */
23602         initial : true,
23603         /**
23604          * @event positionchanged
23605          * Fires when the picker position changed.
23606          * @param {Roo.bootstrap.LocationPicker} this
23607          * @param {Google Location} location
23608          */
23609         positionchanged : true,
23610         /**
23611          * @event resize
23612          * Fires when the map resize.
23613          * @param {Roo.bootstrap.LocationPicker} this
23614          */
23615         resize : true,
23616         /**
23617          * @event show
23618          * Fires when the map show.
23619          * @param {Roo.bootstrap.LocationPicker} this
23620          */
23621         show : true,
23622         /**
23623          * @event hide
23624          * Fires when the map hide.
23625          * @param {Roo.bootstrap.LocationPicker} this
23626          */
23627         hide : true,
23628         /**
23629          * @event mapClick
23630          * Fires when click the map.
23631          * @param {Roo.bootstrap.LocationPicker} this
23632          * @param {Map event} e
23633          */
23634         mapClick : true,
23635         /**
23636          * @event mapRightClick
23637          * Fires when right click the map.
23638          * @param {Roo.bootstrap.LocationPicker} this
23639          * @param {Map event} e
23640          */
23641         mapRightClick : true,
23642         /**
23643          * @event markerClick
23644          * Fires when click the marker.
23645          * @param {Roo.bootstrap.LocationPicker} this
23646          * @param {Map event} e
23647          */
23648         markerClick : true,
23649         /**
23650          * @event markerRightClick
23651          * Fires when right click the marker.
23652          * @param {Roo.bootstrap.LocationPicker} this
23653          * @param {Map event} e
23654          */
23655         markerRightClick : true,
23656         /**
23657          * @event OverlayViewDraw
23658          * Fires when OverlayView Draw
23659          * @param {Roo.bootstrap.LocationPicker} this
23660          */
23661         OverlayViewDraw : true,
23662         /**
23663          * @event OverlayViewOnAdd
23664          * Fires when OverlayView Draw
23665          * @param {Roo.bootstrap.LocationPicker} this
23666          */
23667         OverlayViewOnAdd : true,
23668         /**
23669          * @event OverlayViewOnRemove
23670          * Fires when OverlayView Draw
23671          * @param {Roo.bootstrap.LocationPicker} this
23672          */
23673         OverlayViewOnRemove : true,
23674         /**
23675          * @event OverlayViewShow
23676          * Fires when OverlayView Draw
23677          * @param {Roo.bootstrap.LocationPicker} this
23678          * @param {Pixel} cpx
23679          */
23680         OverlayViewShow : true,
23681         /**
23682          * @event OverlayViewHide
23683          * Fires when OverlayView Draw
23684          * @param {Roo.bootstrap.LocationPicker} this
23685          */
23686         OverlayViewHide : true
23687     });
23688         
23689 };
23690
23691 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23692     
23693     gMapContext: false,
23694     
23695     latitude: 0,
23696     longitude: 0,
23697     zoom: 15,
23698     mapTypeId: false,
23699     mapTypeControl: false,
23700     disableDoubleClickZoom: false,
23701     scrollwheel: true,
23702     streetViewControl: false,
23703     radius: 0,
23704     locationName: '',
23705     draggable: true,
23706     enableAutocomplete: false,
23707     enableReverseGeocode: true,
23708     markerTitle: '',
23709     
23710     getAutoCreate: function()
23711     {
23712
23713         var cfg = {
23714             tag: 'div',
23715             cls: 'roo-location-picker'
23716         };
23717         
23718         return cfg
23719     },
23720     
23721     initEvents: function(ct, position)
23722     {       
23723         if(!this.el.getWidth() || this.isApplied()){
23724             return;
23725         }
23726         
23727         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23728         
23729         this.initial();
23730     },
23731     
23732     initial: function()
23733     {
23734         if(!this.mapTypeId){
23735             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23736         }
23737         
23738         this.gMapContext = this.GMapContext();
23739         
23740         this.initOverlayView();
23741         
23742         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23743         
23744         var _this = this;
23745                 
23746         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23747             _this.setPosition(_this.gMapContext.marker.position);
23748         });
23749         
23750         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23751             _this.fireEvent('mapClick', this, event);
23752             
23753         });
23754
23755         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23756             _this.fireEvent('mapRightClick', this, event);
23757             
23758         });
23759         
23760         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23761             _this.fireEvent('markerClick', this, event);
23762             
23763         });
23764
23765         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23766             _this.fireEvent('markerRightClick', this, event);
23767             
23768         });
23769         
23770         this.setPosition(this.gMapContext.location);
23771         
23772         this.fireEvent('initial', this, this.gMapContext.location);
23773     },
23774     
23775     initOverlayView: function()
23776     {
23777         var _this = this;
23778         
23779         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23780             
23781             draw: function()
23782             {
23783                 _this.fireEvent('OverlayViewDraw', _this);
23784             },
23785             
23786             onAdd: function()
23787             {
23788                 _this.fireEvent('OverlayViewOnAdd', _this);
23789             },
23790             
23791             onRemove: function()
23792             {
23793                 _this.fireEvent('OverlayViewOnRemove', _this);
23794             },
23795             
23796             show: function(cpx)
23797             {
23798                 _this.fireEvent('OverlayViewShow', _this, cpx);
23799             },
23800             
23801             hide: function()
23802             {
23803                 _this.fireEvent('OverlayViewHide', _this);
23804             }
23805             
23806         });
23807     },
23808     
23809     fromLatLngToContainerPixel: function(event)
23810     {
23811         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23812     },
23813     
23814     isApplied: function() 
23815     {
23816         return this.getGmapContext() == false ? false : true;
23817     },
23818     
23819     getGmapContext: function() 
23820     {
23821         return this.gMapContext
23822     },
23823     
23824     GMapContext: function() 
23825     {
23826         var position = new google.maps.LatLng(this.latitude, this.longitude);
23827         
23828         var _map = new google.maps.Map(this.el.dom, {
23829             center: position,
23830             zoom: this.zoom,
23831             mapTypeId: this.mapTypeId,
23832             mapTypeControl: this.mapTypeControl,
23833             disableDoubleClickZoom: this.disableDoubleClickZoom,
23834             scrollwheel: this.scrollwheel,
23835             streetViewControl: this.streetViewControl,
23836             locationName: this.locationName,
23837             draggable: this.draggable,
23838             enableAutocomplete: this.enableAutocomplete,
23839             enableReverseGeocode: this.enableReverseGeocode
23840         });
23841         
23842         var _marker = new google.maps.Marker({
23843             position: position,
23844             map: _map,
23845             title: this.markerTitle,
23846             draggable: this.draggable
23847         });
23848         
23849         return {
23850             map: _map,
23851             marker: _marker,
23852             circle: null,
23853             location: position,
23854             radius: this.radius,
23855             locationName: this.locationName,
23856             addressComponents: {
23857                 formatted_address: null,
23858                 addressLine1: null,
23859                 addressLine2: null,
23860                 streetName: null,
23861                 streetNumber: null,
23862                 city: null,
23863                 district: null,
23864                 state: null,
23865                 stateOrProvince: null
23866             },
23867             settings: this,
23868             domContainer: this.el.dom,
23869             geodecoder: new google.maps.Geocoder()
23870         };
23871     },
23872     
23873     drawCircle: function(center, radius, options) 
23874     {
23875         if (this.gMapContext.circle != null) {
23876             this.gMapContext.circle.setMap(null);
23877         }
23878         if (radius > 0) {
23879             radius *= 1;
23880             options = Roo.apply({}, options, {
23881                 strokeColor: "#0000FF",
23882                 strokeOpacity: .35,
23883                 strokeWeight: 2,
23884                 fillColor: "#0000FF",
23885                 fillOpacity: .2
23886             });
23887             
23888             options.map = this.gMapContext.map;
23889             options.radius = radius;
23890             options.center = center;
23891             this.gMapContext.circle = new google.maps.Circle(options);
23892             return this.gMapContext.circle;
23893         }
23894         
23895         return null;
23896     },
23897     
23898     setPosition: function(location) 
23899     {
23900         this.gMapContext.location = location;
23901         this.gMapContext.marker.setPosition(location);
23902         this.gMapContext.map.panTo(location);
23903         this.drawCircle(location, this.gMapContext.radius, {});
23904         
23905         var _this = this;
23906         
23907         if (this.gMapContext.settings.enableReverseGeocode) {
23908             this.gMapContext.geodecoder.geocode({
23909                 latLng: this.gMapContext.location
23910             }, function(results, status) {
23911                 
23912                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23913                     _this.gMapContext.locationName = results[0].formatted_address;
23914                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23915                     
23916                     _this.fireEvent('positionchanged', this, location);
23917                 }
23918             });
23919             
23920             return;
23921         }
23922         
23923         this.fireEvent('positionchanged', this, location);
23924     },
23925     
23926     resize: function()
23927     {
23928         google.maps.event.trigger(this.gMapContext.map, "resize");
23929         
23930         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23931         
23932         this.fireEvent('resize', this);
23933     },
23934     
23935     setPositionByLatLng: function(latitude, longitude)
23936     {
23937         this.setPosition(new google.maps.LatLng(latitude, longitude));
23938     },
23939     
23940     getCurrentPosition: function() 
23941     {
23942         return {
23943             latitude: this.gMapContext.location.lat(),
23944             longitude: this.gMapContext.location.lng()
23945         };
23946     },
23947     
23948     getAddressName: function() 
23949     {
23950         return this.gMapContext.locationName;
23951     },
23952     
23953     getAddressComponents: function() 
23954     {
23955         return this.gMapContext.addressComponents;
23956     },
23957     
23958     address_component_from_google_geocode: function(address_components) 
23959     {
23960         var result = {};
23961         
23962         for (var i = 0; i < address_components.length; i++) {
23963             var component = address_components[i];
23964             if (component.types.indexOf("postal_code") >= 0) {
23965                 result.postalCode = component.short_name;
23966             } else if (component.types.indexOf("street_number") >= 0) {
23967                 result.streetNumber = component.short_name;
23968             } else if (component.types.indexOf("route") >= 0) {
23969                 result.streetName = component.short_name;
23970             } else if (component.types.indexOf("neighborhood") >= 0) {
23971                 result.city = component.short_name;
23972             } else if (component.types.indexOf("locality") >= 0) {
23973                 result.city = component.short_name;
23974             } else if (component.types.indexOf("sublocality") >= 0) {
23975                 result.district = component.short_name;
23976             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23977                 result.stateOrProvince = component.short_name;
23978             } else if (component.types.indexOf("country") >= 0) {
23979                 result.country = component.short_name;
23980             }
23981         }
23982         
23983         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23984         result.addressLine2 = "";
23985         return result;
23986     },
23987     
23988     setZoomLevel: function(zoom)
23989     {
23990         this.gMapContext.map.setZoom(zoom);
23991     },
23992     
23993     show: function()
23994     {
23995         if(!this.el){
23996             return;
23997         }
23998         
23999         this.el.show();
24000         
24001         this.resize();
24002         
24003         this.fireEvent('show', this);
24004     },
24005     
24006     hide: function()
24007     {
24008         if(!this.el){
24009             return;
24010         }
24011         
24012         this.el.hide();
24013         
24014         this.fireEvent('hide', this);
24015     }
24016     
24017 });
24018
24019 Roo.apply(Roo.bootstrap.LocationPicker, {
24020     
24021     OverlayView : function(map, options)
24022     {
24023         options = options || {};
24024         
24025         this.setMap(map);
24026     }
24027     
24028     
24029 });/*
24030  * - LGPL
24031  *
24032  * Alert
24033  * 
24034  */
24035
24036 /**
24037  * @class Roo.bootstrap.Alert
24038  * @extends Roo.bootstrap.Component
24039  * Bootstrap Alert class
24040  * @cfg {String} title The title of alert
24041  * @cfg {String} html The content of alert
24042  * @cfg {String} weight (  success | info | warning | danger )
24043  * @cfg {String} faicon font-awesomeicon
24044  * 
24045  * @constructor
24046  * Create a new alert
24047  * @param {Object} config The config object
24048  */
24049
24050
24051 Roo.bootstrap.Alert = function(config){
24052     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24053     
24054 };
24055
24056 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24057     
24058     title: '',
24059     html: '',
24060     weight: false,
24061     faicon: false,
24062     
24063     getAutoCreate : function()
24064     {
24065         
24066         var cfg = {
24067             tag : 'div',
24068             cls : 'alert',
24069             cn : [
24070                 {
24071                     tag : 'i',
24072                     cls : 'roo-alert-icon'
24073                     
24074                 },
24075                 {
24076                     tag : 'b',
24077                     cls : 'roo-alert-title',
24078                     html : this.title
24079                 },
24080                 {
24081                     tag : 'span',
24082                     cls : 'roo-alert-text',
24083                     html : this.html
24084                 }
24085             ]
24086         };
24087         
24088         if(this.faicon){
24089             cfg.cn[0].cls += ' fa ' + this.faicon;
24090         }
24091         
24092         if(this.weight){
24093             cfg.cls += ' alert-' + this.weight;
24094         }
24095         
24096         return cfg;
24097     },
24098     
24099     initEvents: function() 
24100     {
24101         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24102     },
24103     
24104     setTitle : function(str)
24105     {
24106         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24107     },
24108     
24109     setText : function(str)
24110     {
24111         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24112     },
24113     
24114     setWeight : function(weight)
24115     {
24116         if(this.weight){
24117             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24118         }
24119         
24120         this.weight = weight;
24121         
24122         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24123     },
24124     
24125     setIcon : function(icon)
24126     {
24127         if(this.faicon){
24128             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24129         }
24130         
24131         this.faicon = icon;
24132         
24133         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24134     },
24135     
24136     hide: function() 
24137     {
24138         this.el.hide();   
24139     },
24140     
24141     show: function() 
24142     {  
24143         this.el.show();   
24144     }
24145     
24146 });
24147
24148  
24149 /*
24150 * Licence: LGPL
24151 */
24152
24153 /**
24154  * @class Roo.bootstrap.UploadCropbox
24155  * @extends Roo.bootstrap.Component
24156  * Bootstrap UploadCropbox class
24157  * @cfg {String} emptyText show when image has been loaded
24158  * @cfg {String} rotateNotify show when image too small to rotate
24159  * @cfg {Number} errorTimeout default 3000
24160  * @cfg {Number} minWidth default 300
24161  * @cfg {Number} minHeight default 300
24162  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24163  * @cfg {Boolean} isDocument (true|false) default false
24164  * @cfg {String} url action url
24165  * @cfg {String} paramName default 'imageUpload'
24166  * @cfg {String} method default POST
24167  * 
24168  * @constructor
24169  * Create a new UploadCropbox
24170  * @param {Object} config The config object
24171  */
24172
24173 Roo.bootstrap.UploadCropbox = function(config){
24174     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24175     
24176     this.addEvents({
24177         /**
24178          * @event beforeselectfile
24179          * Fire before select file
24180          * @param {Roo.bootstrap.UploadCropbox} this
24181          */
24182         "beforeselectfile" : true,
24183         /**
24184          * @event initial
24185          * Fire after initEvent
24186          * @param {Roo.bootstrap.UploadCropbox} this
24187          */
24188         "initial" : true,
24189         /**
24190          * @event crop
24191          * Fire after initEvent
24192          * @param {Roo.bootstrap.UploadCropbox} this
24193          * @param {String} data
24194          */
24195         "crop" : true,
24196         /**
24197          * @event prepare
24198          * Fire when preparing the file data
24199          * @param {Roo.bootstrap.UploadCropbox} this
24200          * @param {Object} file
24201          */
24202         "prepare" : true,
24203         /**
24204          * @event exception
24205          * Fire when get exception
24206          * @param {Roo.bootstrap.UploadCropbox} this
24207          * @param {XMLHttpRequest} xhr
24208          */
24209         "exception" : true,
24210         /**
24211          * @event beforeloadcanvas
24212          * Fire before load the canvas
24213          * @param {Roo.bootstrap.UploadCropbox} this
24214          * @param {String} src
24215          */
24216         "beforeloadcanvas" : true,
24217         /**
24218          * @event trash
24219          * Fire when trash image
24220          * @param {Roo.bootstrap.UploadCropbox} this
24221          */
24222         "trash" : true,
24223         /**
24224          * @event download
24225          * Fire when download the image
24226          * @param {Roo.bootstrap.UploadCropbox} this
24227          */
24228         "download" : true,
24229         /**
24230          * @event footerbuttonclick
24231          * Fire when footerbuttonclick
24232          * @param {Roo.bootstrap.UploadCropbox} this
24233          * @param {String} type
24234          */
24235         "footerbuttonclick" : true,
24236         /**
24237          * @event resize
24238          * Fire when resize
24239          * @param {Roo.bootstrap.UploadCropbox} this
24240          */
24241         "resize" : true,
24242         /**
24243          * @event rotate
24244          * Fire when rotate the image
24245          * @param {Roo.bootstrap.UploadCropbox} this
24246          * @param {String} pos
24247          */
24248         "rotate" : true,
24249         /**
24250          * @event inspect
24251          * Fire when inspect the file
24252          * @param {Roo.bootstrap.UploadCropbox} this
24253          * @param {Object} file
24254          */
24255         "inspect" : true,
24256         /**
24257          * @event upload
24258          * Fire when xhr upload the file
24259          * @param {Roo.bootstrap.UploadCropbox} this
24260          * @param {Object} data
24261          */
24262         "upload" : true,
24263         /**
24264          * @event arrange
24265          * Fire when arrange the file data
24266          * @param {Roo.bootstrap.UploadCropbox} this
24267          * @param {Object} formData
24268          */
24269         "arrange" : true
24270     });
24271     
24272     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24273 };
24274
24275 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24276     
24277     emptyText : 'Click to upload image',
24278     rotateNotify : 'Image is too small to rotate',
24279     errorTimeout : 3000,
24280     scale : 0,
24281     baseScale : 1,
24282     rotate : 0,
24283     dragable : false,
24284     pinching : false,
24285     mouseX : 0,
24286     mouseY : 0,
24287     cropData : false,
24288     minWidth : 300,
24289     minHeight : 300,
24290     file : false,
24291     exif : {},
24292     baseRotate : 1,
24293     cropType : 'image/jpeg',
24294     buttons : false,
24295     canvasLoaded : false,
24296     isDocument : false,
24297     method : 'POST',
24298     paramName : 'imageUpload',
24299     
24300     getAutoCreate : function()
24301     {
24302         var cfg = {
24303             tag : 'div',
24304             cls : 'roo-upload-cropbox',
24305             cn : [
24306                 {
24307                     tag : 'input',
24308                     cls : 'roo-upload-cropbox-selector',
24309                     type : 'file'
24310                 },
24311                 {
24312                     tag : 'div',
24313                     cls : 'roo-upload-cropbox-body',
24314                     style : 'cursor:pointer',
24315                     cn : [
24316                         {
24317                             tag : 'div',
24318                             cls : 'roo-upload-cropbox-preview'
24319                         },
24320                         {
24321                             tag : 'div',
24322                             cls : 'roo-upload-cropbox-thumb'
24323                         },
24324                         {
24325                             tag : 'div',
24326                             cls : 'roo-upload-cropbox-empty-notify',
24327                             html : this.emptyText
24328                         },
24329                         {
24330                             tag : 'div',
24331                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24332                             html : this.rotateNotify
24333                         }
24334                     ]
24335                 },
24336                 {
24337                     tag : 'div',
24338                     cls : 'roo-upload-cropbox-footer',
24339                     cn : {
24340                         tag : 'div',
24341                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24342                         cn : []
24343                     }
24344                 }
24345             ]
24346         };
24347         
24348         return cfg;
24349     },
24350     
24351     onRender : function(ct, position)
24352     {
24353         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24354         
24355         if (this.buttons.length) {
24356             
24357             Roo.each(this.buttons, function(bb) {
24358                 
24359                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24360                 
24361                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24362                 
24363             }, this);
24364         }
24365     },
24366     
24367     initEvents : function()
24368     {
24369         this.urlAPI = (window.createObjectURL && window) || 
24370                                 (window.URL && URL.revokeObjectURL && URL) || 
24371                                 (window.webkitURL && webkitURL);
24372                         
24373         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24374         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24375         
24376         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24377         this.selectorEl.hide();
24378         
24379         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24380         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24381         
24382         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24383         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24384         this.thumbEl.hide();
24385         
24386         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24387         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24388         
24389         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24390         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24391         this.errorEl.hide();
24392         
24393         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24394         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24395         this.footerEl.hide();
24396         
24397         this.setThumbBoxSize();
24398         
24399         this.bind();
24400         
24401         this.resize();
24402         
24403         this.fireEvent('initial', this);
24404     },
24405
24406     bind : function()
24407     {
24408         var _this = this;
24409         
24410         window.addEventListener("resize", function() { _this.resize(); } );
24411         
24412         this.bodyEl.on('click', this.beforeSelectFile, this);
24413         
24414         if(Roo.isTouch){
24415             this.bodyEl.on('touchstart', this.onTouchStart, this);
24416             this.bodyEl.on('touchmove', this.onTouchMove, this);
24417             this.bodyEl.on('touchend', this.onTouchEnd, this);
24418         }
24419         
24420         if(!Roo.isTouch){
24421             this.bodyEl.on('mousedown', this.onMouseDown, this);
24422             this.bodyEl.on('mousemove', this.onMouseMove, this);
24423             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24424             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24425             Roo.get(document).on('mouseup', this.onMouseUp, this);
24426         }
24427         
24428         this.selectorEl.on('change', this.onFileSelected, this);
24429     },
24430     
24431     reset : function()
24432     {    
24433         this.scale = 0;
24434         this.baseScale = 1;
24435         this.rotate = 0;
24436         this.baseRotate = 1;
24437         this.dragable = false;
24438         this.pinching = false;
24439         this.mouseX = 0;
24440         this.mouseY = 0;
24441         this.cropData = false;
24442         this.notifyEl.dom.innerHTML = this.emptyText;
24443         
24444         this.selectorEl.dom.value = '';
24445         
24446     },
24447     
24448     resize : function()
24449     {
24450         if(this.fireEvent('resize', this) != false){
24451             this.setThumbBoxPosition();
24452             this.setCanvasPosition();
24453         }
24454     },
24455     
24456     onFooterButtonClick : function(e, el, o, type)
24457     {
24458         switch (type) {
24459             case 'rotate-left' :
24460                 this.onRotateLeft(e);
24461                 break;
24462             case 'rotate-right' :
24463                 this.onRotateRight(e);
24464                 break;
24465             case 'picture' :
24466                 this.beforeSelectFile(e);
24467                 break;
24468             case 'trash' :
24469                 this.trash(e);
24470                 break;
24471             case 'crop' :
24472                 this.crop(e);
24473                 break;
24474             case 'download' :
24475                 this.download(e);
24476                 break;
24477             default :
24478                 break;
24479         }
24480         
24481         this.fireEvent('footerbuttonclick', this, type);
24482     },
24483     
24484     beforeSelectFile : function(e)
24485     {
24486         e.preventDefault();
24487         
24488         if(this.fireEvent('beforeselectfile', this) != false){
24489             this.selectorEl.dom.click();
24490         }
24491     },
24492     
24493     onFileSelected : function(e)
24494     {
24495         e.preventDefault();
24496         
24497         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24498             return;
24499         }
24500         
24501         var file = this.selectorEl.dom.files[0];
24502         
24503         if(this.fireEvent('inspect', this, file) != false){
24504             this.prepare(file);
24505         }
24506         
24507     },
24508     
24509     trash : function(e)
24510     {
24511         this.fireEvent('trash', this);
24512     },
24513     
24514     download : function(e)
24515     {
24516         this.fireEvent('download', this);
24517     },
24518     
24519     loadCanvas : function(src)
24520     {   
24521         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24522             
24523             this.reset();
24524             
24525             this.imageEl = document.createElement('img');
24526             
24527             var _this = this;
24528             
24529             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24530             
24531             this.imageEl.src = src;
24532         }
24533     },
24534     
24535     onLoadCanvas : function()
24536     {   
24537         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24538         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24539         
24540         this.bodyEl.un('click', this.beforeSelectFile, this);
24541         
24542         this.notifyEl.hide();
24543         this.thumbEl.show();
24544         this.footerEl.show();
24545         
24546         this.baseRotateLevel();
24547         
24548         if(this.isDocument){
24549             this.setThumbBoxSize();
24550         }
24551         
24552         this.setThumbBoxPosition();
24553         
24554         this.baseScaleLevel();
24555         
24556         this.draw();
24557         
24558         this.resize();
24559         
24560         this.canvasLoaded = true;
24561         
24562     },
24563     
24564     setCanvasPosition : function()
24565     {   
24566         if(!this.canvasEl){
24567             return;
24568         }
24569         
24570         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24571         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24572         
24573         this.previewEl.setLeft(pw);
24574         this.previewEl.setTop(ph);
24575         
24576     },
24577     
24578     onMouseDown : function(e)
24579     {   
24580         e.stopEvent();
24581         
24582         this.dragable = true;
24583         this.pinching = false;
24584         
24585         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24586             this.dragable = false;
24587             return;
24588         }
24589         
24590         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24591         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24592         
24593     },
24594     
24595     onMouseMove : function(e)
24596     {   
24597         e.stopEvent();
24598         
24599         if(!this.canvasLoaded){
24600             return;
24601         }
24602         
24603         if (!this.dragable){
24604             return;
24605         }
24606         
24607         var minX = Math.ceil(this.thumbEl.getLeft(true));
24608         var minY = Math.ceil(this.thumbEl.getTop(true));
24609         
24610         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24611         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24612         
24613         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24614         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24615         
24616         x = x - this.mouseX;
24617         y = y - this.mouseY;
24618         
24619         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24620         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24621         
24622         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24623         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24624         
24625         this.previewEl.setLeft(bgX);
24626         this.previewEl.setTop(bgY);
24627         
24628         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24629         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24630     },
24631     
24632     onMouseUp : function(e)
24633     {   
24634         e.stopEvent();
24635         
24636         this.dragable = false;
24637     },
24638     
24639     onMouseWheel : function(e)
24640     {   
24641         e.stopEvent();
24642         
24643         this.startScale = this.scale;
24644         
24645         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24646         
24647         if(!this.zoomable()){
24648             this.scale = this.startScale;
24649             return;
24650         }
24651         
24652         this.draw();
24653         
24654         return;
24655     },
24656     
24657     zoomable : function()
24658     {
24659         var minScale = this.thumbEl.getWidth() / this.minWidth;
24660         
24661         if(this.minWidth < this.minHeight){
24662             minScale = this.thumbEl.getHeight() / this.minHeight;
24663         }
24664         
24665         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24666         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24667         
24668         if(
24669                 this.isDocument &&
24670                 (this.rotate == 0 || this.rotate == 180) && 
24671                 (
24672                     width > this.imageEl.OriginWidth || 
24673                     height > this.imageEl.OriginHeight ||
24674                     (width < this.minWidth && height < this.minHeight)
24675                 )
24676         ){
24677             return false;
24678         }
24679         
24680         if(
24681                 this.isDocument &&
24682                 (this.rotate == 90 || this.rotate == 270) && 
24683                 (
24684                     width > this.imageEl.OriginWidth || 
24685                     height > this.imageEl.OriginHeight ||
24686                     (width < this.minHeight && height < this.minWidth)
24687                 )
24688         ){
24689             return false;
24690         }
24691         
24692         if(
24693                 !this.isDocument &&
24694                 (this.rotate == 0 || this.rotate == 180) && 
24695                 (
24696                     width < this.minWidth || 
24697                     width > this.imageEl.OriginWidth || 
24698                     height < this.minHeight || 
24699                     height > this.imageEl.OriginHeight
24700                 )
24701         ){
24702             return false;
24703         }
24704         
24705         if(
24706                 !this.isDocument &&
24707                 (this.rotate == 90 || this.rotate == 270) && 
24708                 (
24709                     width < this.minHeight || 
24710                     width > this.imageEl.OriginWidth || 
24711                     height < this.minWidth || 
24712                     height > this.imageEl.OriginHeight
24713                 )
24714         ){
24715             return false;
24716         }
24717         
24718         return true;
24719         
24720     },
24721     
24722     onRotateLeft : function(e)
24723     {   
24724         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24725             
24726             var minScale = this.thumbEl.getWidth() / this.minWidth;
24727             
24728             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24729             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24730             
24731             this.startScale = this.scale;
24732             
24733             while (this.getScaleLevel() < minScale){
24734             
24735                 this.scale = this.scale + 1;
24736                 
24737                 if(!this.zoomable()){
24738                     break;
24739                 }
24740                 
24741                 if(
24742                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24743                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24744                 ){
24745                     continue;
24746                 }
24747                 
24748                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24749
24750                 this.draw();
24751                 
24752                 return;
24753             }
24754             
24755             this.scale = this.startScale;
24756             
24757             this.onRotateFail();
24758             
24759             return false;
24760         }
24761         
24762         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24763
24764         if(this.isDocument){
24765             this.setThumbBoxSize();
24766             this.setThumbBoxPosition();
24767             this.setCanvasPosition();
24768         }
24769         
24770         this.draw();
24771         
24772         this.fireEvent('rotate', this, 'left');
24773         
24774     },
24775     
24776     onRotateRight : function(e)
24777     {
24778         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24779             
24780             var minScale = this.thumbEl.getWidth() / this.minWidth;
24781         
24782             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24783             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24784             
24785             this.startScale = this.scale;
24786             
24787             while (this.getScaleLevel() < minScale){
24788             
24789                 this.scale = this.scale + 1;
24790                 
24791                 if(!this.zoomable()){
24792                     break;
24793                 }
24794                 
24795                 if(
24796                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24797                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24798                 ){
24799                     continue;
24800                 }
24801                 
24802                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24803
24804                 this.draw();
24805                 
24806                 return;
24807             }
24808             
24809             this.scale = this.startScale;
24810             
24811             this.onRotateFail();
24812             
24813             return false;
24814         }
24815         
24816         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24817
24818         if(this.isDocument){
24819             this.setThumbBoxSize();
24820             this.setThumbBoxPosition();
24821             this.setCanvasPosition();
24822         }
24823         
24824         this.draw();
24825         
24826         this.fireEvent('rotate', this, 'right');
24827     },
24828     
24829     onRotateFail : function()
24830     {
24831         this.errorEl.show(true);
24832         
24833         var _this = this;
24834         
24835         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24836     },
24837     
24838     draw : function()
24839     {
24840         this.previewEl.dom.innerHTML = '';
24841         
24842         var canvasEl = document.createElement("canvas");
24843         
24844         var contextEl = canvasEl.getContext("2d");
24845         
24846         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24847         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24848         var center = this.imageEl.OriginWidth / 2;
24849         
24850         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24851             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24852             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24853             center = this.imageEl.OriginHeight / 2;
24854         }
24855         
24856         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24857         
24858         contextEl.translate(center, center);
24859         contextEl.rotate(this.rotate * Math.PI / 180);
24860
24861         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24862         
24863         this.canvasEl = document.createElement("canvas");
24864         
24865         this.contextEl = this.canvasEl.getContext("2d");
24866         
24867         switch (this.rotate) {
24868             case 0 :
24869                 
24870                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24871                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24872                 
24873                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24874                 
24875                 break;
24876             case 90 : 
24877                 
24878                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24879                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24880                 
24881                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24882                     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);
24883                     break;
24884                 }
24885                 
24886                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24887                 
24888                 break;
24889             case 180 :
24890                 
24891                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24892                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24893                 
24894                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24895                     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);
24896                     break;
24897                 }
24898                 
24899                 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);
24900                 
24901                 break;
24902             case 270 :
24903                 
24904                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24905                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24906         
24907                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24908                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24909                     break;
24910                 }
24911                 
24912                 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);
24913                 
24914                 break;
24915             default : 
24916                 break;
24917         }
24918         
24919         this.previewEl.appendChild(this.canvasEl);
24920         
24921         this.setCanvasPosition();
24922     },
24923     
24924     crop : function()
24925     {
24926         if(!this.canvasLoaded){
24927             return;
24928         }
24929         
24930         var imageCanvas = document.createElement("canvas");
24931         
24932         var imageContext = imageCanvas.getContext("2d");
24933         
24934         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24935         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24936         
24937         var center = imageCanvas.width / 2;
24938         
24939         imageContext.translate(center, center);
24940         
24941         imageContext.rotate(this.rotate * Math.PI / 180);
24942         
24943         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24944         
24945         var canvas = document.createElement("canvas");
24946         
24947         var context = canvas.getContext("2d");
24948                 
24949         canvas.width = this.minWidth;
24950         canvas.height = this.minHeight;
24951
24952         switch (this.rotate) {
24953             case 0 :
24954                 
24955                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
24956                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
24957                 
24958                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24959                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24960                 
24961                 var targetWidth = this.minWidth - 2 * x;
24962                 var targetHeight = this.minHeight - 2 * y;
24963                 
24964                 var scale = 1;
24965                 
24966                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24967                     scale = targetWidth / width;
24968                 }
24969                 
24970                 if(x > 0 && y == 0){
24971                     scale = targetHeight / height;
24972                 }
24973                 
24974                 if(x > 0 && y > 0){
24975                     scale = targetWidth / width;
24976                     
24977                     if(width < height){
24978                         scale = targetHeight / height;
24979                     }
24980                 }
24981                 
24982                 context.scale(scale, scale);
24983                 
24984                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24985                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24986
24987                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24988                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24989
24990                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24991                 
24992                 break;
24993             case 90 : 
24994                 
24995                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
24996                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
24997                 
24998                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24999                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25000                 
25001                 var targetWidth = this.minWidth - 2 * x;
25002                 var targetHeight = this.minHeight - 2 * y;
25003                 
25004                 var scale = 1;
25005                 
25006                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25007                     scale = targetWidth / width;
25008                 }
25009                 
25010                 if(x > 0 && y == 0){
25011                     scale = targetHeight / height;
25012                 }
25013                 
25014                 if(x > 0 && y > 0){
25015                     scale = targetWidth / width;
25016                     
25017                     if(width < height){
25018                         scale = targetHeight / height;
25019                     }
25020                 }
25021                 
25022                 context.scale(scale, scale);
25023                 
25024                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25025                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25026
25027                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25028                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25029                 
25030                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25031                 
25032                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25033                 
25034                 break;
25035             case 180 :
25036                 
25037                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25038                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25039                 
25040                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25041                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25042                 
25043                 var targetWidth = this.minWidth - 2 * x;
25044                 var targetHeight = this.minHeight - 2 * y;
25045                 
25046                 var scale = 1;
25047                 
25048                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25049                     scale = targetWidth / width;
25050                 }
25051                 
25052                 if(x > 0 && y == 0){
25053                     scale = targetHeight / height;
25054                 }
25055                 
25056                 if(x > 0 && y > 0){
25057                     scale = targetWidth / width;
25058                     
25059                     if(width < height){
25060                         scale = targetHeight / height;
25061                     }
25062                 }
25063                 
25064                 context.scale(scale, scale);
25065                 
25066                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25067                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25068
25069                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25070                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25071
25072                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25073                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25074                 
25075                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25076                 
25077                 break;
25078             case 270 :
25079                 
25080                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25081                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25082                 
25083                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25084                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25085                 
25086                 var targetWidth = this.minWidth - 2 * x;
25087                 var targetHeight = this.minHeight - 2 * y;
25088                 
25089                 var scale = 1;
25090                 
25091                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25092                     scale = targetWidth / width;
25093                 }
25094                 
25095                 if(x > 0 && y == 0){
25096                     scale = targetHeight / height;
25097                 }
25098                 
25099                 if(x > 0 && y > 0){
25100                     scale = targetWidth / width;
25101                     
25102                     if(width < height){
25103                         scale = targetHeight / height;
25104                     }
25105                 }
25106                 
25107                 context.scale(scale, scale);
25108                 
25109                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25110                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25111
25112                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25113                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25114                 
25115                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25116                 
25117                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25118                 
25119                 break;
25120             default : 
25121                 break;
25122         }
25123         
25124         this.cropData = canvas.toDataURL(this.cropType);
25125         
25126         if(this.fireEvent('crop', this, this.cropData) !== false){
25127             this.process(this.file, this.cropData);
25128         }
25129         
25130         return;
25131         
25132     },
25133     
25134     setThumbBoxSize : function()
25135     {
25136         var width, height;
25137         
25138         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25139             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25140             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25141             
25142             this.minWidth = width;
25143             this.minHeight = height;
25144             
25145             if(this.rotate == 90 || this.rotate == 270){
25146                 this.minWidth = height;
25147                 this.minHeight = width;
25148             }
25149         }
25150         
25151         height = 300;
25152         width = Math.ceil(this.minWidth * height / this.minHeight);
25153         
25154         if(this.minWidth > this.minHeight){
25155             width = 300;
25156             height = Math.ceil(this.minHeight * width / this.minWidth);
25157         }
25158         
25159         this.thumbEl.setStyle({
25160             width : width + 'px',
25161             height : height + 'px'
25162         });
25163
25164         return;
25165             
25166     },
25167     
25168     setThumbBoxPosition : function()
25169     {
25170         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25171         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25172         
25173         this.thumbEl.setLeft(x);
25174         this.thumbEl.setTop(y);
25175         
25176     },
25177     
25178     baseRotateLevel : function()
25179     {
25180         this.baseRotate = 1;
25181         
25182         if(
25183                 typeof(this.exif) != 'undefined' &&
25184                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25185                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25186         ){
25187             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25188         }
25189         
25190         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25191         
25192     },
25193     
25194     baseScaleLevel : function()
25195     {
25196         var width, height;
25197         
25198         if(this.isDocument){
25199             
25200             if(this.baseRotate == 6 || this.baseRotate == 8){
25201             
25202                 height = this.thumbEl.getHeight();
25203                 this.baseScale = height / this.imageEl.OriginWidth;
25204
25205                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25206                     width = this.thumbEl.getWidth();
25207                     this.baseScale = width / this.imageEl.OriginHeight;
25208                 }
25209
25210                 return;
25211             }
25212
25213             height = this.thumbEl.getHeight();
25214             this.baseScale = height / this.imageEl.OriginHeight;
25215
25216             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25217                 width = this.thumbEl.getWidth();
25218                 this.baseScale = width / this.imageEl.OriginWidth;
25219             }
25220
25221             return;
25222         }
25223         
25224         if(this.baseRotate == 6 || this.baseRotate == 8){
25225             
25226             width = this.thumbEl.getHeight();
25227             this.baseScale = width / this.imageEl.OriginHeight;
25228             
25229             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25230                 height = this.thumbEl.getWidth();
25231                 this.baseScale = height / this.imageEl.OriginHeight;
25232             }
25233             
25234             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25235                 height = this.thumbEl.getWidth();
25236                 this.baseScale = height / this.imageEl.OriginHeight;
25237                 
25238                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25239                     width = this.thumbEl.getHeight();
25240                     this.baseScale = width / this.imageEl.OriginWidth;
25241                 }
25242             }
25243             
25244             return;
25245         }
25246         
25247         width = this.thumbEl.getWidth();
25248         this.baseScale = width / this.imageEl.OriginWidth;
25249         
25250         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25251             height = this.thumbEl.getHeight();
25252             this.baseScale = height / this.imageEl.OriginHeight;
25253         }
25254         
25255         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25256             
25257             height = this.thumbEl.getHeight();
25258             this.baseScale = height / this.imageEl.OriginHeight;
25259             
25260             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25261                 width = this.thumbEl.getWidth();
25262                 this.baseScale = width / this.imageEl.OriginWidth;
25263             }
25264             
25265         }
25266         
25267         return;
25268     },
25269     
25270     getScaleLevel : function()
25271     {
25272         return this.baseScale * Math.pow(1.1, this.scale);
25273     },
25274     
25275     onTouchStart : function(e)
25276     {
25277         if(!this.canvasLoaded){
25278             this.beforeSelectFile(e);
25279             return;
25280         }
25281         
25282         var touches = e.browserEvent.touches;
25283         
25284         if(!touches){
25285             return;
25286         }
25287         
25288         if(touches.length == 1){
25289             this.onMouseDown(e);
25290             return;
25291         }
25292         
25293         if(touches.length != 2){
25294             return;
25295         }
25296         
25297         var coords = [];
25298         
25299         for(var i = 0, finger; finger = touches[i]; i++){
25300             coords.push(finger.pageX, finger.pageY);
25301         }
25302         
25303         var x = Math.pow(coords[0] - coords[2], 2);
25304         var y = Math.pow(coords[1] - coords[3], 2);
25305         
25306         this.startDistance = Math.sqrt(x + y);
25307         
25308         this.startScale = this.scale;
25309         
25310         this.pinching = true;
25311         this.dragable = false;
25312         
25313     },
25314     
25315     onTouchMove : function(e)
25316     {
25317         if(!this.pinching && !this.dragable){
25318             return;
25319         }
25320         
25321         var touches = e.browserEvent.touches;
25322         
25323         if(!touches){
25324             return;
25325         }
25326         
25327         if(this.dragable){
25328             this.onMouseMove(e);
25329             return;
25330         }
25331         
25332         var coords = [];
25333         
25334         for(var i = 0, finger; finger = touches[i]; i++){
25335             coords.push(finger.pageX, finger.pageY);
25336         }
25337         
25338         var x = Math.pow(coords[0] - coords[2], 2);
25339         var y = Math.pow(coords[1] - coords[3], 2);
25340         
25341         this.endDistance = Math.sqrt(x + y);
25342         
25343         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25344         
25345         if(!this.zoomable()){
25346             this.scale = this.startScale;
25347             return;
25348         }
25349         
25350         this.draw();
25351         
25352     },
25353     
25354     onTouchEnd : function(e)
25355     {
25356         this.pinching = false;
25357         this.dragable = false;
25358         
25359     },
25360     
25361     process : function(file, crop)
25362     {
25363         this.xhr = new XMLHttpRequest();
25364         
25365         file.xhr = this.xhr;
25366
25367         this.xhr.open(this.method, this.url, true);
25368         
25369         var headers = {
25370             "Accept": "application/json",
25371             "Cache-Control": "no-cache",
25372             "X-Requested-With": "XMLHttpRequest"
25373         };
25374         
25375         for (var headerName in headers) {
25376             var headerValue = headers[headerName];
25377             if (headerValue) {
25378                 this.xhr.setRequestHeader(headerName, headerValue);
25379             }
25380         }
25381         
25382         var _this = this;
25383         
25384         this.xhr.onload = function()
25385         {
25386             _this.xhrOnLoad(_this.xhr);
25387         }
25388         
25389         this.xhr.onerror = function()
25390         {
25391             _this.xhrOnError(_this.xhr);
25392         }
25393         
25394         var formData = new FormData();
25395
25396         formData.append('returnHTML', 'NO');
25397         
25398         if(crop){
25399             formData.append('crop', crop);
25400         }
25401         
25402         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25403             formData.append(this.paramName, file, file.name);
25404         }
25405         
25406         if(typeof(file.filename) != 'undefined'){
25407             formData.append('filename', file.filename);
25408         }
25409         
25410         if(typeof(file.mimetype) != 'undefined'){
25411             formData.append('mimetype', file.mimetype);
25412         }
25413         
25414         if(this.fireEvent('arrange', this, formData) != false){
25415             this.xhr.send(formData);
25416         };
25417     },
25418     
25419     xhrOnLoad : function(xhr)
25420     {
25421         if (xhr.readyState !== 4) {
25422             this.fireEvent('exception', this, xhr);
25423             return;
25424         }
25425
25426         var response = Roo.decode(xhr.responseText);
25427         
25428         if(!response.success){
25429             this.fireEvent('exception', this, xhr);
25430             return;
25431         }
25432         
25433         var response = Roo.decode(xhr.responseText);
25434         
25435         this.fireEvent('upload', this, response);
25436         
25437     },
25438     
25439     xhrOnError : function()
25440     {
25441         Roo.log('xhr on error');
25442         
25443         var response = Roo.decode(xhr.responseText);
25444           
25445         Roo.log(response);
25446         
25447     },
25448     
25449     prepare : function(file)
25450     {   
25451         this.file = false;
25452         this.exif = {};
25453         
25454         if(typeof(file) === 'string'){
25455             this.loadCanvas(file);
25456             return;
25457         }
25458         
25459         if(!file || !this.urlAPI){
25460             return;
25461         }
25462         
25463         this.file = file;
25464         this.cropType = file.type;
25465         
25466         var _this = this;
25467         
25468         if(this.fireEvent('prepare', this, this.file) != false){
25469             
25470             var reader = new FileReader();
25471             
25472             reader.onload = function (e) {
25473                 if (e.target.error) {
25474                     Roo.log(e.target.error);
25475                     return;
25476                 }
25477                 
25478                 var buffer = e.target.result,
25479                     dataView = new DataView(buffer),
25480                     offset = 2,
25481                     maxOffset = dataView.byteLength - 4,
25482                     markerBytes,
25483                     markerLength;
25484                 
25485                 if (dataView.getUint16(0) === 0xffd8) {
25486                     while (offset < maxOffset) {
25487                         markerBytes = dataView.getUint16(offset);
25488                         
25489                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25490                             markerLength = dataView.getUint16(offset + 2) + 2;
25491                             if (offset + markerLength > dataView.byteLength) {
25492                                 Roo.log('Invalid meta data: Invalid segment size.');
25493                                 break;
25494                             }
25495                             
25496                             if(markerBytes == 0xffe1){
25497                                 _this.parseExifData(
25498                                     dataView,
25499                                     offset,
25500                                     markerLength
25501                                 );
25502                             }
25503                             
25504                             offset += markerLength;
25505                             
25506                             continue;
25507                         }
25508                         
25509                         break;
25510                     }
25511                     
25512                 }
25513                 
25514                 var url = _this.urlAPI.createObjectURL(_this.file);
25515                 
25516                 _this.loadCanvas(url);
25517                 
25518                 return;
25519             }
25520             
25521             reader.readAsArrayBuffer(this.file);
25522             
25523         }
25524         
25525     },
25526     
25527     parseExifData : function(dataView, offset, length)
25528     {
25529         var tiffOffset = offset + 10,
25530             littleEndian,
25531             dirOffset;
25532     
25533         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25534             // No Exif data, might be XMP data instead
25535             return;
25536         }
25537         
25538         // Check for the ASCII code for "Exif" (0x45786966):
25539         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25540             // No Exif data, might be XMP data instead
25541             return;
25542         }
25543         if (tiffOffset + 8 > dataView.byteLength) {
25544             Roo.log('Invalid Exif data: Invalid segment size.');
25545             return;
25546         }
25547         // Check for the two null bytes:
25548         if (dataView.getUint16(offset + 8) !== 0x0000) {
25549             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25550             return;
25551         }
25552         // Check the byte alignment:
25553         switch (dataView.getUint16(tiffOffset)) {
25554         case 0x4949:
25555             littleEndian = true;
25556             break;
25557         case 0x4D4D:
25558             littleEndian = false;
25559             break;
25560         default:
25561             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25562             return;
25563         }
25564         // Check for the TIFF tag marker (0x002A):
25565         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25566             Roo.log('Invalid Exif data: Missing TIFF marker.');
25567             return;
25568         }
25569         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25570         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25571         
25572         this.parseExifTags(
25573             dataView,
25574             tiffOffset,
25575             tiffOffset + dirOffset,
25576             littleEndian
25577         );
25578     },
25579     
25580     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25581     {
25582         var tagsNumber,
25583             dirEndOffset,
25584             i;
25585         if (dirOffset + 6 > dataView.byteLength) {
25586             Roo.log('Invalid Exif data: Invalid directory offset.');
25587             return;
25588         }
25589         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25590         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25591         if (dirEndOffset + 4 > dataView.byteLength) {
25592             Roo.log('Invalid Exif data: Invalid directory size.');
25593             return;
25594         }
25595         for (i = 0; i < tagsNumber; i += 1) {
25596             this.parseExifTag(
25597                 dataView,
25598                 tiffOffset,
25599                 dirOffset + 2 + 12 * i, // tag offset
25600                 littleEndian
25601             );
25602         }
25603         // Return the offset to the next directory:
25604         return dataView.getUint32(dirEndOffset, littleEndian);
25605     },
25606     
25607     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25608     {
25609         var tag = dataView.getUint16(offset, littleEndian);
25610         
25611         this.exif[tag] = this.getExifValue(
25612             dataView,
25613             tiffOffset,
25614             offset,
25615             dataView.getUint16(offset + 2, littleEndian), // tag type
25616             dataView.getUint32(offset + 4, littleEndian), // tag length
25617             littleEndian
25618         );
25619     },
25620     
25621     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25622     {
25623         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25624             tagSize,
25625             dataOffset,
25626             values,
25627             i,
25628             str,
25629             c;
25630     
25631         if (!tagType) {
25632             Roo.log('Invalid Exif data: Invalid tag type.');
25633             return;
25634         }
25635         
25636         tagSize = tagType.size * length;
25637         // Determine if the value is contained in the dataOffset bytes,
25638         // or if the value at the dataOffset is a pointer to the actual data:
25639         dataOffset = tagSize > 4 ?
25640                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25641         if (dataOffset + tagSize > dataView.byteLength) {
25642             Roo.log('Invalid Exif data: Invalid data offset.');
25643             return;
25644         }
25645         if (length === 1) {
25646             return tagType.getValue(dataView, dataOffset, littleEndian);
25647         }
25648         values = [];
25649         for (i = 0; i < length; i += 1) {
25650             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25651         }
25652         
25653         if (tagType.ascii) {
25654             str = '';
25655             // Concatenate the chars:
25656             for (i = 0; i < values.length; i += 1) {
25657                 c = values[i];
25658                 // Ignore the terminating NULL byte(s):
25659                 if (c === '\u0000') {
25660                     break;
25661                 }
25662                 str += c;
25663             }
25664             return str;
25665         }
25666         return values;
25667     }
25668     
25669 });
25670
25671 Roo.apply(Roo.bootstrap.UploadCropbox, {
25672     tags : {
25673         'Orientation': 0x0112
25674     },
25675     
25676     Orientation: {
25677             1: 0, //'top-left',
25678 //            2: 'top-right',
25679             3: 180, //'bottom-right',
25680 //            4: 'bottom-left',
25681 //            5: 'left-top',
25682             6: 90, //'right-top',
25683 //            7: 'right-bottom',
25684             8: 270 //'left-bottom'
25685     },
25686     
25687     exifTagTypes : {
25688         // byte, 8-bit unsigned int:
25689         1: {
25690             getValue: function (dataView, dataOffset) {
25691                 return dataView.getUint8(dataOffset);
25692             },
25693             size: 1
25694         },
25695         // ascii, 8-bit byte:
25696         2: {
25697             getValue: function (dataView, dataOffset) {
25698                 return String.fromCharCode(dataView.getUint8(dataOffset));
25699             },
25700             size: 1,
25701             ascii: true
25702         },
25703         // short, 16 bit int:
25704         3: {
25705             getValue: function (dataView, dataOffset, littleEndian) {
25706                 return dataView.getUint16(dataOffset, littleEndian);
25707             },
25708             size: 2
25709         },
25710         // long, 32 bit int:
25711         4: {
25712             getValue: function (dataView, dataOffset, littleEndian) {
25713                 return dataView.getUint32(dataOffset, littleEndian);
25714             },
25715             size: 4
25716         },
25717         // rational = two long values, first is numerator, second is denominator:
25718         5: {
25719             getValue: function (dataView, dataOffset, littleEndian) {
25720                 return dataView.getUint32(dataOffset, littleEndian) /
25721                     dataView.getUint32(dataOffset + 4, littleEndian);
25722             },
25723             size: 8
25724         },
25725         // slong, 32 bit signed int:
25726         9: {
25727             getValue: function (dataView, dataOffset, littleEndian) {
25728                 return dataView.getInt32(dataOffset, littleEndian);
25729             },
25730             size: 4
25731         },
25732         // srational, two slongs, first is numerator, second is denominator:
25733         10: {
25734             getValue: function (dataView, dataOffset, littleEndian) {
25735                 return dataView.getInt32(dataOffset, littleEndian) /
25736                     dataView.getInt32(dataOffset + 4, littleEndian);
25737             },
25738             size: 8
25739         }
25740     },
25741     
25742     footer : {
25743         STANDARD : [
25744             {
25745                 tag : 'div',
25746                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25747                 action : 'rotate-left',
25748                 cn : [
25749                     {
25750                         tag : 'button',
25751                         cls : 'btn btn-default',
25752                         html : '<i class="fa fa-undo"></i>'
25753                     }
25754                 ]
25755             },
25756             {
25757                 tag : 'div',
25758                 cls : 'btn-group roo-upload-cropbox-picture',
25759                 action : 'picture',
25760                 cn : [
25761                     {
25762                         tag : 'button',
25763                         cls : 'btn btn-default',
25764                         html : '<i class="fa fa-picture-o"></i>'
25765                     }
25766                 ]
25767             },
25768             {
25769                 tag : 'div',
25770                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25771                 action : 'rotate-right',
25772                 cn : [
25773                     {
25774                         tag : 'button',
25775                         cls : 'btn btn-default',
25776                         html : '<i class="fa fa-repeat"></i>'
25777                     }
25778                 ]
25779             }
25780         ],
25781         DOCUMENT : [
25782             {
25783                 tag : 'div',
25784                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25785                 action : 'rotate-left',
25786                 cn : [
25787                     {
25788                         tag : 'button',
25789                         cls : 'btn btn-default',
25790                         html : '<i class="fa fa-undo"></i>'
25791                     }
25792                 ]
25793             },
25794             {
25795                 tag : 'div',
25796                 cls : 'btn-group roo-upload-cropbox-download',
25797                 action : 'download',
25798                 cn : [
25799                     {
25800                         tag : 'button',
25801                         cls : 'btn btn-default',
25802                         html : '<i class="fa fa-download"></i>'
25803                     }
25804                 ]
25805             },
25806             {
25807                 tag : 'div',
25808                 cls : 'btn-group roo-upload-cropbox-crop',
25809                 action : 'crop',
25810                 cn : [
25811                     {
25812                         tag : 'button',
25813                         cls : 'btn btn-default',
25814                         html : '<i class="fa fa-crop"></i>'
25815                     }
25816                 ]
25817             },
25818             {
25819                 tag : 'div',
25820                 cls : 'btn-group roo-upload-cropbox-trash',
25821                 action : 'trash',
25822                 cn : [
25823                     {
25824                         tag : 'button',
25825                         cls : 'btn btn-default',
25826                         html : '<i class="fa fa-trash"></i>'
25827                     }
25828                 ]
25829             },
25830             {
25831                 tag : 'div',
25832                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25833                 action : 'rotate-right',
25834                 cn : [
25835                     {
25836                         tag : 'button',
25837                         cls : 'btn btn-default',
25838                         html : '<i class="fa fa-repeat"></i>'
25839                     }
25840                 ]
25841             }
25842         ],
25843         ROTATOR : [
25844             {
25845                 tag : 'div',
25846                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25847                 action : 'rotate-left',
25848                 cn : [
25849                     {
25850                         tag : 'button',
25851                         cls : 'btn btn-default',
25852                         html : '<i class="fa fa-undo"></i>'
25853                     }
25854                 ]
25855             },
25856             {
25857                 tag : 'div',
25858                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25859                 action : 'rotate-right',
25860                 cn : [
25861                     {
25862                         tag : 'button',
25863                         cls : 'btn btn-default',
25864                         html : '<i class="fa fa-repeat"></i>'
25865                     }
25866                 ]
25867             }
25868         ]
25869     }
25870 });
25871
25872 /*
25873 * Licence: LGPL
25874 */
25875
25876 /**
25877  * @class Roo.bootstrap.DocumentManager
25878  * @extends Roo.bootstrap.Component
25879  * Bootstrap DocumentManager class
25880  * @cfg {String} paramName default 'imageUpload'
25881  * @cfg {String} method default POST
25882  * @cfg {String} url action url
25883  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
25884  * @cfg {Boolean} multiple multiple upload default true
25885  * @cfg {Number} thumbSize default 300
25886  * @cfg {String} fieldLabel
25887  * @cfg {Number} labelWidth default 4
25888  * @cfg {String} labelAlign (left|top) default left
25889  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
25890  * 
25891  * @constructor
25892  * Create a new DocumentManager
25893  * @param {Object} config The config object
25894  */
25895
25896 Roo.bootstrap.DocumentManager = function(config){
25897     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25898     
25899     this.addEvents({
25900         /**
25901          * @event initial
25902          * Fire when initial the DocumentManager
25903          * @param {Roo.bootstrap.DocumentManager} this
25904          */
25905         "initial" : true,
25906         /**
25907          * @event inspect
25908          * inspect selected file
25909          * @param {Roo.bootstrap.DocumentManager} this
25910          * @param {File} file
25911          */
25912         "inspect" : true,
25913         /**
25914          * @event exception
25915          * Fire when xhr load exception
25916          * @param {Roo.bootstrap.DocumentManager} this
25917          * @param {XMLHttpRequest} xhr
25918          */
25919         "exception" : true,
25920         /**
25921          * @event prepare
25922          * prepare the form data
25923          * @param {Roo.bootstrap.DocumentManager} this
25924          * @param {Object} formData
25925          */
25926         "prepare" : true,
25927         /**
25928          * @event remove
25929          * Fire when remove the file
25930          * @param {Roo.bootstrap.DocumentManager} this
25931          * @param {Object} file
25932          */
25933         "remove" : true,
25934         /**
25935          * @event refresh
25936          * Fire after refresh the file
25937          * @param {Roo.bootstrap.DocumentManager} this
25938          */
25939         "refresh" : true,
25940         /**
25941          * @event click
25942          * Fire after click the image
25943          * @param {Roo.bootstrap.DocumentManager} this
25944          * @param {Object} file
25945          */
25946         "click" : true,
25947         /**
25948          * @event edit
25949          * Fire when upload a image and editable set to true
25950          * @param {Roo.bootstrap.DocumentManager} this
25951          * @param {Object} file
25952          */
25953         "edit" : true,
25954         /**
25955          * @event beforeselectfile
25956          * Fire before select file
25957          * @param {Roo.bootstrap.DocumentManager} this
25958          */
25959         "beforeselectfile" : true,
25960         /**
25961          * @event process
25962          * Fire before process file
25963          * @param {Roo.bootstrap.DocumentManager} this
25964          * @param {Object} file
25965          */
25966         "process" : true
25967         
25968     });
25969 };
25970
25971 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
25972     
25973     boxes : 0,
25974     inputName : '',
25975     thumbSize : 300,
25976     multiple : true,
25977     files : [],
25978     method : 'POST',
25979     url : '',
25980     paramName : 'imageUpload',
25981     fieldLabel : '',
25982     labelWidth : 4,
25983     labelAlign : 'left',
25984     editable : true,
25985     delegates : [],
25986     
25987     getAutoCreate : function()
25988     {   
25989         var managerWidget = {
25990             tag : 'div',
25991             cls : 'roo-document-manager',
25992             cn : [
25993                 {
25994                     tag : 'input',
25995                     cls : 'roo-document-manager-selector',
25996                     type : 'file'
25997                 },
25998                 {
25999                     tag : 'div',
26000                     cls : 'roo-document-manager-uploader',
26001                     cn : [
26002                         {
26003                             tag : 'div',
26004                             cls : 'roo-document-manager-upload-btn',
26005                             html : '<i class="fa fa-plus"></i>'
26006                         }
26007                     ]
26008                     
26009                 }
26010             ]
26011         };
26012         
26013         var content = [
26014             {
26015                 tag : 'div',
26016                 cls : 'column col-md-12',
26017                 cn : managerWidget
26018             }
26019         ];
26020         
26021         if(this.fieldLabel.length){
26022             
26023             content = [
26024                 {
26025                     tag : 'div',
26026                     cls : 'column col-md-12',
26027                     html : this.fieldLabel
26028                 },
26029                 {
26030                     tag : 'div',
26031                     cls : 'column col-md-12',
26032                     cn : managerWidget
26033                 }
26034             ];
26035
26036             if(this.labelAlign == 'left'){
26037                 content = [
26038                     {
26039                         tag : 'div',
26040                         cls : 'column col-md-' + this.labelWidth,
26041                         html : this.fieldLabel
26042                     },
26043                     {
26044                         tag : 'div',
26045                         cls : 'column col-md-' + (12 - this.labelWidth),
26046                         cn : managerWidget
26047                     }
26048                 ];
26049                 
26050             }
26051         }
26052         
26053         var cfg = {
26054             tag : 'div',
26055             cls : 'row clearfix',
26056             cn : content
26057         };
26058         
26059         return cfg;
26060         
26061     },
26062     
26063     initEvents : function()
26064     {
26065         this.managerEl = this.el.select('.roo-document-manager', true).first();
26066         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26067         
26068         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26069         this.selectorEl.hide();
26070         
26071         if(this.multiple){
26072             this.selectorEl.attr('multiple', 'multiple');
26073         }
26074         
26075         this.selectorEl.on('change', this.onFileSelected, this);
26076         
26077         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26078         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26079         
26080         this.uploader.on('click', this.onUploaderClick, this);
26081         
26082         this.renderProgressDialog();
26083         
26084         var _this = this;
26085         
26086         window.addEventListener("resize", function() { _this.refresh(); } );
26087         
26088         this.fireEvent('initial', this);
26089     },
26090     
26091     renderProgressDialog : function()
26092     {
26093         var _this = this;
26094         
26095         this.progressDialog = new Roo.bootstrap.Modal({
26096             cls : 'roo-document-manager-progress-dialog',
26097             allow_close : false,
26098             title : '',
26099             buttons : [
26100                 {
26101                     name  :'cancel',
26102                     weight : 'danger',
26103                     html : 'Cancel'
26104                 }
26105             ], 
26106             listeners : { 
26107                 btnclick : function() {
26108                     _this.uploadCancel();
26109                     this.hide();
26110                 }
26111             }
26112         });
26113          
26114         this.progressDialog.render(Roo.get(document.body));
26115          
26116         this.progress = new Roo.bootstrap.Progress({
26117             cls : 'roo-document-manager-progress',
26118             active : true,
26119             striped : true
26120         });
26121         
26122         this.progress.render(this.progressDialog.getChildContainer());
26123         
26124         this.progressBar = new Roo.bootstrap.ProgressBar({
26125             cls : 'roo-document-manager-progress-bar',
26126             aria_valuenow : 0,
26127             aria_valuemin : 0,
26128             aria_valuemax : 12,
26129             panel : 'success'
26130         });
26131         
26132         this.progressBar.render(this.progress.getChildContainer());
26133     },
26134     
26135     onUploaderClick : function(e)
26136     {
26137         e.preventDefault();
26138      
26139         if(this.fireEvent('beforeselectfile', this) != false){
26140             this.selectorEl.dom.click();
26141         }
26142         
26143     },
26144     
26145     onFileSelected : function(e)
26146     {
26147         e.preventDefault();
26148         
26149         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26150             return;
26151         }
26152         
26153         Roo.each(this.selectorEl.dom.files, function(file){
26154             if(this.fireEvent('inspect', this, file) != false){
26155                 this.files.push(file);
26156             }
26157         }, this);
26158         
26159         this.queue();
26160         
26161     },
26162     
26163     queue : function()
26164     {
26165         this.selectorEl.dom.value = '';
26166         
26167         if(!this.files.length){
26168             return;
26169         }
26170         
26171         if(this.boxes > 0 && this.files.length > this.boxes){
26172             this.files = this.files.slice(0, this.boxes);
26173         }
26174         
26175         this.uploader.show();
26176         
26177         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26178             this.uploader.hide();
26179         }
26180         
26181         var _this = this;
26182         
26183         var files = [];
26184         
26185         var docs = [];
26186         
26187         Roo.each(this.files, function(file){
26188             
26189             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26190                 var f = this.renderPreview(file);
26191                 files.push(f);
26192                 return;
26193             }
26194             
26195             if(file.type.indexOf('image') != -1){
26196                 this.delegates.push(
26197                     (function(){
26198                         _this.process(file);
26199                     }).createDelegate(this)
26200                 );
26201         
26202                 return;
26203             }
26204             
26205             docs.push(
26206                 (function(){
26207                     _this.process(file);
26208                 }).createDelegate(this)
26209             );
26210             
26211         }, this);
26212         
26213         this.files = files;
26214         
26215         this.delegates = this.delegates.concat(docs);
26216         
26217         if(!this.delegates.length){
26218             this.refresh();
26219             return;
26220         }
26221         
26222         this.progressBar.aria_valuemax = this.delegates.length;
26223         
26224         this.arrange();
26225         
26226         return;
26227     },
26228     
26229     arrange : function()
26230     {
26231         if(!this.delegates.length){
26232             this.progressDialog.hide();
26233             this.refresh();
26234             return;
26235         }
26236         
26237         var delegate = this.delegates.shift();
26238         
26239         this.progressDialog.show();
26240         
26241         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26242         
26243         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26244         
26245         delegate();
26246     },
26247     
26248     refresh : function()
26249     {
26250         this.uploader.show();
26251         
26252         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26253             this.uploader.hide();
26254         }
26255         
26256         Roo.isTouch ? this.closable(false) : this.closable(true);
26257         
26258         this.fireEvent('refresh', this);
26259     },
26260     
26261     onRemove : function(e, el, o)
26262     {
26263         e.preventDefault();
26264         
26265         this.fireEvent('remove', this, o);
26266         
26267     },
26268     
26269     remove : function(o)
26270     {
26271         var files = [];
26272         
26273         Roo.each(this.files, function(file){
26274             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26275                 files.push(file);
26276                 return;
26277             }
26278
26279             o.target.remove();
26280
26281         }, this);
26282         
26283         this.files = files;
26284         
26285         this.refresh();
26286     },
26287     
26288     clear : function()
26289     {
26290         Roo.each(this.files, function(file){
26291             if(!file.target){
26292                 return;
26293             }
26294             
26295             file.target.remove();
26296
26297         }, this);
26298         
26299         this.files = [];
26300         
26301         this.refresh();
26302     },
26303     
26304     onClick : function(e, el, o)
26305     {
26306         e.preventDefault();
26307         
26308         this.fireEvent('click', this, o);
26309         
26310     },
26311     
26312     closable : function(closable)
26313     {
26314         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26315             
26316             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26317             
26318             if(closable){
26319                 el.show();
26320                 return;
26321             }
26322             
26323             el.hide();
26324             
26325         }, this);
26326     },
26327     
26328     xhrOnLoad : function(xhr)
26329     {
26330         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26331             el.remove();
26332         }, this);
26333         
26334         if (xhr.readyState !== 4) {
26335             this.arrange();
26336             this.fireEvent('exception', this, xhr);
26337             return;
26338         }
26339
26340         var response = Roo.decode(xhr.responseText);
26341         
26342         if(!response.success){
26343             this.arrange();
26344             this.fireEvent('exception', this, xhr);
26345             return;
26346         }
26347         
26348         var file = this.renderPreview(response.data);
26349         
26350         this.files.push(file);
26351         
26352         this.arrange();
26353         
26354     },
26355     
26356     xhrOnError : function()
26357     {
26358         Roo.log('xhr on error');
26359         
26360         var response = Roo.decode(xhr.responseText);
26361           
26362         Roo.log(response);
26363         
26364         this.arrange();
26365     },
26366     
26367     process : function(file)
26368     {
26369         if(this.fireEvent('process', this, file) !== false){
26370             if(this.editable && file.type.indexOf('image') != -1){
26371                 this.fireEvent('edit', this, file);
26372                 return;
26373             }
26374
26375             this.uploadStart(file, false);
26376
26377             return;
26378         }
26379         
26380     },
26381     
26382     uploadStart : function(file, crop)
26383     {
26384         this.xhr = new XMLHttpRequest();
26385         
26386         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26387             this.arrange();
26388             return;
26389         }
26390         
26391         file.xhr = this.xhr;
26392             
26393         this.managerEl.createChild({
26394             tag : 'div',
26395             cls : 'roo-document-manager-loading',
26396             cn : [
26397                 {
26398                     tag : 'div',
26399                     tooltip : file.name,
26400                     cls : 'roo-document-manager-thumb',
26401                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26402                 }
26403             ]
26404
26405         });
26406
26407         this.xhr.open(this.method, this.url, true);
26408         
26409         var headers = {
26410             "Accept": "application/json",
26411             "Cache-Control": "no-cache",
26412             "X-Requested-With": "XMLHttpRequest"
26413         };
26414         
26415         for (var headerName in headers) {
26416             var headerValue = headers[headerName];
26417             if (headerValue) {
26418                 this.xhr.setRequestHeader(headerName, headerValue);
26419             }
26420         }
26421         
26422         var _this = this;
26423         
26424         this.xhr.onload = function()
26425         {
26426             _this.xhrOnLoad(_this.xhr);
26427         }
26428         
26429         this.xhr.onerror = function()
26430         {
26431             _this.xhrOnError(_this.xhr);
26432         }
26433         
26434         var formData = new FormData();
26435
26436         formData.append('returnHTML', 'NO');
26437         
26438         if(crop){
26439             formData.append('crop', crop);
26440         }
26441         
26442         formData.append(this.paramName, file, file.name);
26443         
26444         if(this.fireEvent('prepare', this, formData) != false){
26445             this.xhr.send(formData);
26446         };
26447     },
26448     
26449     uploadCancel : function()
26450     {
26451         this.xhr.abort();
26452         
26453         this.delegates = [];
26454         
26455         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26456             el.remove();
26457         }, this);
26458         
26459         this.arrange();
26460     },
26461     
26462     renderPreview : function(file)
26463     {
26464         if(typeof(file.target) != 'undefined' && file.target){
26465             return file;
26466         }
26467         
26468         var previewEl = this.managerEl.createChild({
26469             tag : 'div',
26470             cls : 'roo-document-manager-preview',
26471             cn : [
26472                 {
26473                     tag : 'div',
26474                     tooltip : file.filename,
26475                     cls : 'roo-document-manager-thumb',
26476                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26477                 },
26478                 {
26479                     tag : 'button',
26480                     cls : 'close',
26481                     html : '<i class="fa fa-times-circle"></i>'
26482                 }
26483             ]
26484         });
26485
26486         var close = previewEl.select('button.close', true).first();
26487
26488         close.on('click', this.onRemove, this, file);
26489
26490         file.target = previewEl;
26491
26492         var image = previewEl.select('img', true).first();
26493         
26494         var _this = this;
26495         
26496         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26497         
26498         image.on('click', this.onClick, this, file);
26499         
26500         return file;
26501         
26502     },
26503     
26504     onPreviewLoad : function(file, image)
26505     {
26506         if(typeof(file.target) == 'undefined' || !file.target){
26507             return;
26508         }
26509         
26510         var width = image.dom.naturalWidth || image.dom.width;
26511         var height = image.dom.naturalHeight || image.dom.height;
26512         
26513         if(width > height){
26514             file.target.addClass('wide');
26515             return;
26516         }
26517         
26518         file.target.addClass('tall');
26519         return;
26520         
26521     },
26522     
26523     uploadFromSource : function(file, crop)
26524     {
26525         this.xhr = new XMLHttpRequest();
26526         
26527         this.managerEl.createChild({
26528             tag : 'div',
26529             cls : 'roo-document-manager-loading',
26530             cn : [
26531                 {
26532                     tag : 'div',
26533                     tooltip : file.name,
26534                     cls : 'roo-document-manager-thumb',
26535                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26536                 }
26537             ]
26538
26539         });
26540
26541         this.xhr.open(this.method, this.url, true);
26542         
26543         var headers = {
26544             "Accept": "application/json",
26545             "Cache-Control": "no-cache",
26546             "X-Requested-With": "XMLHttpRequest"
26547         };
26548         
26549         for (var headerName in headers) {
26550             var headerValue = headers[headerName];
26551             if (headerValue) {
26552                 this.xhr.setRequestHeader(headerName, headerValue);
26553             }
26554         }
26555         
26556         var _this = this;
26557         
26558         this.xhr.onload = function()
26559         {
26560             _this.xhrOnLoad(_this.xhr);
26561         }
26562         
26563         this.xhr.onerror = function()
26564         {
26565             _this.xhrOnError(_this.xhr);
26566         }
26567         
26568         var formData = new FormData();
26569
26570         formData.append('returnHTML', 'NO');
26571         
26572         formData.append('crop', crop);
26573         
26574         if(typeof(file.filename) != 'undefined'){
26575             formData.append('filename', file.filename);
26576         }
26577         
26578         if(typeof(file.mimetype) != 'undefined'){
26579             formData.append('mimetype', file.mimetype);
26580         }
26581         
26582         if(this.fireEvent('prepare', this, formData) != false){
26583             this.xhr.send(formData);
26584         };
26585     }
26586 });
26587
26588 /*
26589 * Licence: LGPL
26590 */
26591
26592 /**
26593  * @class Roo.bootstrap.DocumentViewer
26594  * @extends Roo.bootstrap.Component
26595  * Bootstrap DocumentViewer class
26596  * 
26597  * @constructor
26598  * Create a new DocumentViewer
26599  * @param {Object} config The config object
26600  */
26601
26602 Roo.bootstrap.DocumentViewer = function(config){
26603     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26604     
26605     this.addEvents({
26606         /**
26607          * @event initial
26608          * Fire after initEvent
26609          * @param {Roo.bootstrap.DocumentViewer} this
26610          */
26611         "initial" : true,
26612         /**
26613          * @event click
26614          * Fire after click
26615          * @param {Roo.bootstrap.DocumentViewer} this
26616          */
26617         "click" : true,
26618         /**
26619          * @event trash
26620          * Fire after trash button
26621          * @param {Roo.bootstrap.DocumentViewer} this
26622          */
26623         "trash" : true
26624         
26625     });
26626 };
26627
26628 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
26629     
26630     getAutoCreate : function()
26631     {
26632         var cfg = {
26633             tag : 'div',
26634             cls : 'roo-document-viewer',
26635             cn : [
26636                 {
26637                     tag : 'div',
26638                     cls : 'roo-document-viewer-body',
26639                     cn : [
26640                         {
26641                             tag : 'div',
26642                             cls : 'roo-document-viewer-thumb',
26643                             cn : [
26644                                 {
26645                                     tag : 'img',
26646                                     cls : 'roo-document-viewer-image'
26647                                 }
26648                             ]
26649                         }
26650                     ]
26651                 },
26652                 {
26653                     tag : 'div',
26654                     cls : 'roo-document-viewer-footer',
26655                     cn : {
26656                         tag : 'div',
26657                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26658                         cn : [
26659                             {
26660                                 tag : 'div',
26661                                 cls : 'btn-group',
26662                                 cn : [
26663                                     {
26664                                         tag : 'button',
26665                                         cls : 'btn btn-default roo-document-viewer-trash',
26666                                         html : '<i class="fa fa-trash"></i>'
26667                                     }
26668                                 ]
26669                             }
26670                         ]
26671                     }
26672                 }
26673             ]
26674         };
26675         
26676         return cfg;
26677     },
26678     
26679     initEvents : function()
26680     {
26681         
26682         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26683         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26684         
26685         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26686         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26687         
26688         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26689         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26690         
26691         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26692         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26693         
26694         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26695         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26696         
26697         this.bodyEl.on('click', this.onClick, this);
26698         
26699         this.trashBtn.on('click', this.onTrash, this);
26700         
26701     },
26702     
26703     initial : function()
26704     {
26705 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26706         
26707         
26708         this.fireEvent('initial', this);
26709         
26710     },
26711     
26712     onClick : function(e)
26713     {
26714         e.preventDefault();
26715         
26716         this.fireEvent('click', this);
26717     },
26718     
26719     onTrash : function(e)
26720     {
26721         e.preventDefault();
26722         
26723         this.fireEvent('trash', this);
26724     }
26725     
26726 });
26727 /*
26728  * - LGPL
26729  *
26730  * nav progress bar
26731  * 
26732  */
26733
26734 /**
26735  * @class Roo.bootstrap.NavProgressBar
26736  * @extends Roo.bootstrap.Component
26737  * Bootstrap NavProgressBar class
26738  * 
26739  * @constructor
26740  * Create a new nav progress bar
26741  * @param {Object} config The config object
26742  */
26743
26744 Roo.bootstrap.NavProgressBar = function(config){
26745     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26746
26747     this.bullets = this.bullets || [];
26748    
26749 //    Roo.bootstrap.NavProgressBar.register(this);
26750      this.addEvents({
26751         /**
26752              * @event changed
26753              * Fires when the active item changes
26754              * @param {Roo.bootstrap.NavProgressBar} this
26755              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26756              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
26757          */
26758         'changed': true
26759      });
26760     
26761 };
26762
26763 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
26764     
26765     bullets : [],
26766     barItems : [],
26767     
26768     getAutoCreate : function()
26769     {
26770         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26771         
26772         cfg = {
26773             tag : 'div',
26774             cls : 'roo-navigation-bar-group',
26775             cn : [
26776                 {
26777                     tag : 'div',
26778                     cls : 'roo-navigation-top-bar'
26779                 },
26780                 {
26781                     tag : 'div',
26782                     cls : 'roo-navigation-bullets-bar',
26783                     cn : [
26784                         {
26785                             tag : 'ul',
26786                             cls : 'roo-navigation-bar'
26787                         }
26788                     ]
26789                 },
26790                 
26791                 {
26792                     tag : 'div',
26793                     cls : 'roo-navigation-bottom-bar'
26794                 }
26795             ]
26796             
26797         };
26798         
26799         return cfg;
26800         
26801     },
26802     
26803     initEvents: function() 
26804     {
26805         
26806     },
26807     
26808     onRender : function(ct, position) 
26809     {
26810         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26811         
26812         if(this.bullets.length){
26813             Roo.each(this.bullets, function(b){
26814                this.addItem(b);
26815             }, this);
26816         }
26817         
26818         this.format();
26819         
26820     },
26821     
26822     addItem : function(cfg)
26823     {
26824         var item = new Roo.bootstrap.NavProgressItem(cfg);
26825         
26826         item.parentId = this.id;
26827         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
26828         
26829         if(cfg.html){
26830             var top = new Roo.bootstrap.Element({
26831                 tag : 'div',
26832                 cls : 'roo-navigation-bar-text'
26833             });
26834             
26835             var bottom = new Roo.bootstrap.Element({
26836                 tag : 'div',
26837                 cls : 'roo-navigation-bar-text'
26838             });
26839             
26840             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
26841             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
26842             
26843             var topText = new Roo.bootstrap.Element({
26844                 tag : 'span',
26845                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
26846             });
26847             
26848             var bottomText = new Roo.bootstrap.Element({
26849                 tag : 'span',
26850                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
26851             });
26852             
26853             topText.onRender(top.el, null);
26854             bottomText.onRender(bottom.el, null);
26855             
26856             item.topEl = top;
26857             item.bottomEl = bottom;
26858         }
26859         
26860         this.barItems.push(item);
26861         
26862         return item;
26863     },
26864     
26865     getActive : function()
26866     {
26867         var active = false;
26868         
26869         Roo.each(this.barItems, function(v){
26870             
26871             if (!v.isActive()) {
26872                 return;
26873             }
26874             
26875             active = v;
26876             return false;
26877             
26878         });
26879         
26880         return active;
26881     },
26882     
26883     setActiveItem : function(item)
26884     {
26885         var prev = false;
26886         
26887         Roo.each(this.barItems, function(v){
26888             if (v.rid == item.rid) {
26889                 return ;
26890             }
26891             
26892             if (v.isActive()) {
26893                 v.setActive(false);
26894                 prev = v;
26895             }
26896         });
26897
26898         item.setActive(true);
26899         
26900         this.fireEvent('changed', this, item, prev);
26901     },
26902     
26903     getBarItem: function(rid)
26904     {
26905         var ret = false;
26906         
26907         Roo.each(this.barItems, function(e) {
26908             if (e.rid != rid) {
26909                 return;
26910             }
26911             
26912             ret =  e;
26913             return false;
26914         });
26915         
26916         return ret;
26917     },
26918     
26919     indexOfItem : function(item)
26920     {
26921         var index = false;
26922         
26923         Roo.each(this.barItems, function(v, i){
26924             
26925             if (v.rid != item.rid) {
26926                 return;
26927             }
26928             
26929             index = i;
26930             return false
26931         });
26932         
26933         return index;
26934     },
26935     
26936     setActiveNext : function()
26937     {
26938         var i = this.indexOfItem(this.getActive());
26939         
26940         if (i > this.barItems.length) {
26941             return;
26942         }
26943         
26944         this.setActiveItem(this.barItems[i+1]);
26945     },
26946     
26947     setActivePrev : function()
26948     {
26949         var i = this.indexOfItem(this.getActive());
26950         
26951         if (i  < 1) {
26952             return;
26953         }
26954         
26955         this.setActiveItem(this.barItems[i-1]);
26956     },
26957     
26958     format : function()
26959     {
26960         if(!this.barItems.length){
26961             return;
26962         }
26963      
26964         var width = 100 / this.barItems.length;
26965         
26966         Roo.each(this.barItems, function(i){
26967             i.el.setStyle('width', width + '%');
26968             i.topEl.el.setStyle('width', width + '%');
26969             i.bottomEl.el.setStyle('width', width + '%');
26970         }, this);
26971         
26972     }
26973     
26974 });
26975 /*
26976  * - LGPL
26977  *
26978  * Nav Progress Item
26979  * 
26980  */
26981
26982 /**
26983  * @class Roo.bootstrap.NavProgressItem
26984  * @extends Roo.bootstrap.Component
26985  * Bootstrap NavProgressItem class
26986  * @cfg {String} rid the reference id
26987  * @cfg {Boolean} active (true|false) Is item active default false
26988  * @cfg {Boolean} disabled (true|false) Is item active default false
26989  * @cfg {String} html
26990  * @cfg {String} position (top|bottom) text position default bottom
26991  * @cfg {String} icon show icon instead of number
26992  * 
26993  * @constructor
26994  * Create a new NavProgressItem
26995  * @param {Object} config The config object
26996  */
26997 Roo.bootstrap.NavProgressItem = function(config){
26998     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
26999     this.addEvents({
27000         // raw events
27001         /**
27002          * @event click
27003          * The raw click event for the entire grid.
27004          * @param {Roo.bootstrap.NavProgressItem} this
27005          * @param {Roo.EventObject} e
27006          */
27007         "click" : true
27008     });
27009    
27010 };
27011
27012 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27013     
27014     rid : '',
27015     active : false,
27016     disabled : false,
27017     html : '',
27018     position : 'bottom',
27019     icon : false,
27020     
27021     getAutoCreate : function()
27022     {
27023         var iconCls = 'roo-navigation-bar-item-icon';
27024         
27025         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27026         
27027         var cfg = {
27028             tag: 'li',
27029             cls: 'roo-navigation-bar-item',
27030             cn : [
27031                 {
27032                     tag : 'i',
27033                     cls : iconCls
27034                 }
27035             ]
27036         }
27037         
27038         if(this.active){
27039             cfg.cls += ' active';
27040         }
27041         if(this.disabled){
27042             cfg.cls += ' disabled';
27043         }
27044         
27045         return cfg;
27046     },
27047     
27048     disable : function()
27049     {
27050         this.setDisabled(true);
27051     },
27052     
27053     enable : function()
27054     {
27055         this.setDisabled(false);
27056     },
27057     
27058     initEvents: function() 
27059     {
27060         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27061         
27062         this.iconEl.on('click', this.onClick, this);
27063     },
27064     
27065     onClick : function(e)
27066     {
27067         e.preventDefault();
27068         
27069         if(this.disabled){
27070             return;
27071         }
27072         
27073         if(this.fireEvent('click', this, e) === false){
27074             return;
27075         };
27076         
27077         this.parent().setActiveItem(this);
27078     },
27079     
27080     isActive: function () 
27081     {
27082         return this.active;
27083     },
27084     
27085     setActive : function(state)
27086     {
27087         if(this.active == state){
27088             return;
27089         }
27090         
27091         this.active = state;
27092         
27093         if (state) {
27094             this.el.addClass('active');
27095             return;
27096         }
27097         
27098         this.el.removeClass('active');
27099         
27100         return;
27101     },
27102     
27103     setDisabled : function(state)
27104     {
27105         if(this.disabled == state){
27106             return;
27107         }
27108         
27109         this.disabled = state;
27110         
27111         if (state) {
27112             this.el.addClass('disabled');
27113             return;
27114         }
27115         
27116         this.el.removeClass('disabled');
27117     },
27118     
27119     tooltipEl : function()
27120     {
27121         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27122     }
27123 });
27124  
27125
27126