7f1f65476e39bb477d46043743aec4fc69d94c95
[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  * @constructor
4313  * Create a new Navbar Button
4314  * @param {Object} config The config object
4315  */
4316 Roo.bootstrap.NavSidebarItem = function(config){
4317     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4318     this.addEvents({
4319         // raw events
4320         /**
4321          * @event click
4322          * The raw click event for the entire grid.
4323          * @param {Roo.EventObject} e
4324          */
4325         "click" : true,
4326          /**
4327             * @event changed
4328             * Fires when the active item active state changes
4329             * @param {Roo.bootstrap.NavSidebarItem} this
4330             * @param {boolean} state the new state
4331              
4332          */
4333         'changed': true
4334     });
4335    
4336 };
4337
4338 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4339     
4340     
4341     getAutoCreate : function(){
4342         
4343         
4344         var a = {
4345                 tag: 'a',
4346                 href : this.href || '#',
4347                 cls: '',
4348                 html : '',
4349                 cn : []
4350         };
4351         var cfg = {
4352             tag: 'li',
4353             cls: '',
4354             cn: [ a ]
4355         }
4356         var span = {
4357             tag: 'span',
4358             html : this.html || ''
4359         }
4360         
4361         
4362         if (this.active) {
4363             cfg.cls += ' active';
4364         }
4365         
4366         // left icon..
4367         if (this.glyphicon || this.icon) {
4368             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4369             a.cn.push({ tag : 'i', cls : c }) ;
4370         }
4371         // html..
4372         a.cn.push(span);
4373         // then badge..
4374         if (this.badge !== '') {
4375             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
4376         }
4377         // fi
4378         if (this.menu) {
4379             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4380             a.cls += 'dropdown-toggle treeview' ;
4381             
4382         }
4383         
4384         
4385         
4386         return cfg;
4387          
4388            
4389     }
4390    
4391      
4392  
4393 });
4394  
4395
4396  /*
4397  * - LGPL
4398  *
4399  * row
4400  * 
4401  */
4402
4403 /**
4404  * @class Roo.bootstrap.Row
4405  * @extends Roo.bootstrap.Component
4406  * Bootstrap Row class (contains columns...)
4407  * 
4408  * @constructor
4409  * Create a new Row
4410  * @param {Object} config The config object
4411  */
4412
4413 Roo.bootstrap.Row = function(config){
4414     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4415 };
4416
4417 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4418     
4419     getAutoCreate : function(){
4420        return {
4421             cls: 'row clearfix'
4422        };
4423     }
4424     
4425     
4426 });
4427
4428  
4429
4430  /*
4431  * - LGPL
4432  *
4433  * element
4434  * 
4435  */
4436
4437 /**
4438  * @class Roo.bootstrap.Element
4439  * @extends Roo.bootstrap.Component
4440  * Bootstrap Element class
4441  * @cfg {String} html contents of the element
4442  * @cfg {String} tag tag of the element
4443  * @cfg {String} cls class of the element
4444  * @cfg {Boolean} preventDefault (true|false) default false
4445  * @cfg {Boolean} clickable (true|false) default false
4446  * 
4447  * @constructor
4448  * Create a new Element
4449  * @param {Object} config The config object
4450  */
4451
4452 Roo.bootstrap.Element = function(config){
4453     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4454     
4455     this.addEvents({
4456         // raw events
4457         /**
4458          * @event click
4459          * When a element is chick
4460          * @param {Roo.bootstrap.Element} this
4461          * @param {Roo.EventObject} e
4462          */
4463         "click" : true
4464     });
4465 };
4466
4467 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4468     
4469     tag: 'div',
4470     cls: '',
4471     html: '',
4472     preventDefault: false, 
4473     clickable: false,
4474     
4475     getAutoCreate : function(){
4476         
4477         var cfg = {
4478             tag: this.tag,
4479             cls: this.cls,
4480             html: this.html
4481         }
4482         
4483         return cfg;
4484     },
4485     
4486     initEvents: function() 
4487     {
4488         Roo.bootstrap.Element.superclass.initEvents.call(this);
4489         
4490         if(this.clickable){
4491             this.el.on('click', this.onClick, this);
4492         }
4493         
4494     },
4495     
4496     onClick : function(e)
4497     {
4498         if(this.preventDefault){
4499             e.preventDefault();
4500         }
4501         
4502         this.fireEvent('click', this, e);
4503     },
4504     
4505     getValue : function()
4506     {
4507         return this.el.dom.innerHTML;
4508     },
4509     
4510     setValue : function(value)
4511     {
4512         this.el.dom.innerHTML = value;
4513     }
4514    
4515 });
4516
4517  
4518
4519  /*
4520  * - LGPL
4521  *
4522  * pagination
4523  * 
4524  */
4525
4526 /**
4527  * @class Roo.bootstrap.Pagination
4528  * @extends Roo.bootstrap.Component
4529  * Bootstrap Pagination class
4530  * @cfg {String} size xs | sm | md | lg
4531  * @cfg {Boolean} inverse false | true
4532  * 
4533  * @constructor
4534  * Create a new Pagination
4535  * @param {Object} config The config object
4536  */
4537
4538 Roo.bootstrap.Pagination = function(config){
4539     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4540 };
4541
4542 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4543     
4544     cls: false,
4545     size: false,
4546     inverse: false,
4547     
4548     getAutoCreate : function(){
4549         var cfg = {
4550             tag: 'ul',
4551                 cls: 'pagination'
4552         };
4553         if (this.inverse) {
4554             cfg.cls += ' inverse';
4555         }
4556         if (this.html) {
4557             cfg.html=this.html;
4558         }
4559         if (this.cls) {
4560             cfg.cls += " " + this.cls;
4561         }
4562         return cfg;
4563     }
4564    
4565 });
4566
4567  
4568
4569  /*
4570  * - LGPL
4571  *
4572  * Pagination item
4573  * 
4574  */
4575
4576
4577 /**
4578  * @class Roo.bootstrap.PaginationItem
4579  * @extends Roo.bootstrap.Component
4580  * Bootstrap PaginationItem class
4581  * @cfg {String} html text
4582  * @cfg {String} href the link
4583  * @cfg {Boolean} preventDefault (true | false) default true
4584  * @cfg {Boolean} active (true | false) default false
4585  * @cfg {Boolean} disabled default false
4586  * 
4587  * 
4588  * @constructor
4589  * Create a new PaginationItem
4590  * @param {Object} config The config object
4591  */
4592
4593
4594 Roo.bootstrap.PaginationItem = function(config){
4595     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4596     this.addEvents({
4597         // raw events
4598         /**
4599          * @event click
4600          * The raw click event for the entire grid.
4601          * @param {Roo.EventObject} e
4602          */
4603         "click" : true
4604     });
4605 };
4606
4607 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4608     
4609     href : false,
4610     html : false,
4611     preventDefault: true,
4612     active : false,
4613     cls : false,
4614     disabled: false,
4615     
4616     getAutoCreate : function(){
4617         var cfg= {
4618             tag: 'li',
4619             cn: [
4620                 {
4621                     tag : 'a',
4622                     href : this.href ? this.href : '#',
4623                     html : this.html ? this.html : ''
4624                 }
4625             ]
4626         };
4627         
4628         if(this.cls){
4629             cfg.cls = this.cls;
4630         }
4631         
4632         if(this.disabled){
4633             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4634         }
4635         
4636         if(this.active){
4637             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4638         }
4639         
4640         return cfg;
4641     },
4642     
4643     initEvents: function() {
4644         
4645         this.el.on('click', this.onClick, this);
4646         
4647     },
4648     onClick : function(e)
4649     {
4650         Roo.log('PaginationItem on click ');
4651         if(this.preventDefault){
4652             e.preventDefault();
4653         }
4654         
4655         if(this.disabled){
4656             return;
4657         }
4658         
4659         this.fireEvent('click', this, e);
4660     }
4661    
4662 });
4663
4664  
4665
4666  /*
4667  * - LGPL
4668  *
4669  * slider
4670  * 
4671  */
4672
4673
4674 /**
4675  * @class Roo.bootstrap.Slider
4676  * @extends Roo.bootstrap.Component
4677  * Bootstrap Slider class
4678  *    
4679  * @constructor
4680  * Create a new Slider
4681  * @param {Object} config The config object
4682  */
4683
4684 Roo.bootstrap.Slider = function(config){
4685     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4686 };
4687
4688 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4689     
4690     getAutoCreate : function(){
4691         
4692         var cfg = {
4693             tag: 'div',
4694             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4695             cn: [
4696                 {
4697                     tag: 'a',
4698                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4699                 }
4700             ]
4701         }
4702         
4703         return cfg;
4704     }
4705    
4706 });
4707
4708  /*
4709  * Based on:
4710  * Ext JS Library 1.1.1
4711  * Copyright(c) 2006-2007, Ext JS, LLC.
4712  *
4713  * Originally Released Under LGPL - original licence link has changed is not relivant.
4714  *
4715  * Fork - LGPL
4716  * <script type="text/javascript">
4717  */
4718  
4719
4720 /**
4721  * @class Roo.grid.ColumnModel
4722  * @extends Roo.util.Observable
4723  * This is the default implementation of a ColumnModel used by the Grid. It defines
4724  * the columns in the grid.
4725  * <br>Usage:<br>
4726  <pre><code>
4727  var colModel = new Roo.grid.ColumnModel([
4728         {header: "Ticker", width: 60, sortable: true, locked: true},
4729         {header: "Company Name", width: 150, sortable: true},
4730         {header: "Market Cap.", width: 100, sortable: true},
4731         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4732         {header: "Employees", width: 100, sortable: true, resizable: false}
4733  ]);
4734  </code></pre>
4735  * <p>
4736  
4737  * The config options listed for this class are options which may appear in each
4738  * individual column definition.
4739  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4740  * @constructor
4741  * @param {Object} config An Array of column config objects. See this class's
4742  * config objects for details.
4743 */
4744 Roo.grid.ColumnModel = function(config){
4745         /**
4746      * The config passed into the constructor
4747      */
4748     this.config = config;
4749     this.lookup = {};
4750
4751     // if no id, create one
4752     // if the column does not have a dataIndex mapping,
4753     // map it to the order it is in the config
4754     for(var i = 0, len = config.length; i < len; i++){
4755         var c = config[i];
4756         if(typeof c.dataIndex == "undefined"){
4757             c.dataIndex = i;
4758         }
4759         if(typeof c.renderer == "string"){
4760             c.renderer = Roo.util.Format[c.renderer];
4761         }
4762         if(typeof c.id == "undefined"){
4763             c.id = Roo.id();
4764         }
4765         if(c.editor && c.editor.xtype){
4766             c.editor  = Roo.factory(c.editor, Roo.grid);
4767         }
4768         if(c.editor && c.editor.isFormField){
4769             c.editor = new Roo.grid.GridEditor(c.editor);
4770         }
4771         this.lookup[c.id] = c;
4772     }
4773
4774     /**
4775      * The width of columns which have no width specified (defaults to 100)
4776      * @type Number
4777      */
4778     this.defaultWidth = 100;
4779
4780     /**
4781      * Default sortable of columns which have no sortable specified (defaults to false)
4782      * @type Boolean
4783      */
4784     this.defaultSortable = false;
4785
4786     this.addEvents({
4787         /**
4788              * @event widthchange
4789              * Fires when the width of a column changes.
4790              * @param {ColumnModel} this
4791              * @param {Number} columnIndex The column index
4792              * @param {Number} newWidth The new width
4793              */
4794             "widthchange": true,
4795         /**
4796              * @event headerchange
4797              * Fires when the text of a header changes.
4798              * @param {ColumnModel} this
4799              * @param {Number} columnIndex The column index
4800              * @param {Number} newText The new header text
4801              */
4802             "headerchange": true,
4803         /**
4804              * @event hiddenchange
4805              * Fires when a column is hidden or "unhidden".
4806              * @param {ColumnModel} this
4807              * @param {Number} columnIndex The column index
4808              * @param {Boolean} hidden true if hidden, false otherwise
4809              */
4810             "hiddenchange": true,
4811             /**
4812          * @event columnmoved
4813          * Fires when a column is moved.
4814          * @param {ColumnModel} this
4815          * @param {Number} oldIndex
4816          * @param {Number} newIndex
4817          */
4818         "columnmoved" : true,
4819         /**
4820          * @event columlockchange
4821          * Fires when a column's locked state is changed
4822          * @param {ColumnModel} this
4823          * @param {Number} colIndex
4824          * @param {Boolean} locked true if locked
4825          */
4826         "columnlockchange" : true
4827     });
4828     Roo.grid.ColumnModel.superclass.constructor.call(this);
4829 };
4830 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4831     /**
4832      * @cfg {String} header The header text to display in the Grid view.
4833      */
4834     /**
4835      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4836      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4837      * specified, the column's index is used as an index into the Record's data Array.
4838      */
4839     /**
4840      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4841      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4842      */
4843     /**
4844      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4845      * Defaults to the value of the {@link #defaultSortable} property.
4846      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4847      */
4848     /**
4849      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4850      */
4851     /**
4852      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4853      */
4854     /**
4855      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4856      */
4857     /**
4858      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4859      */
4860     /**
4861      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4862      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4863      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4864      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4865      */
4866        /**
4867      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4868      */
4869     /**
4870      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4871      */
4872     /**
4873      * @cfg {String} cursor (Optional)
4874      */
4875     /**
4876      * @cfg {String} tooltip (Optional)
4877      */
4878     /**
4879      * Returns the id of the column at the specified index.
4880      * @param {Number} index The column index
4881      * @return {String} the id
4882      */
4883     getColumnId : function(index){
4884         return this.config[index].id;
4885     },
4886
4887     /**
4888      * Returns the column for a specified id.
4889      * @param {String} id The column id
4890      * @return {Object} the column
4891      */
4892     getColumnById : function(id){
4893         return this.lookup[id];
4894     },
4895
4896     
4897     /**
4898      * Returns the column for a specified dataIndex.
4899      * @param {String} dataIndex The column dataIndex
4900      * @return {Object|Boolean} the column or false if not found
4901      */
4902     getColumnByDataIndex: function(dataIndex){
4903         var index = this.findColumnIndex(dataIndex);
4904         return index > -1 ? this.config[index] : false;
4905     },
4906     
4907     /**
4908      * Returns the index for a specified column id.
4909      * @param {String} id The column id
4910      * @return {Number} the index, or -1 if not found
4911      */
4912     getIndexById : function(id){
4913         for(var i = 0, len = this.config.length; i < len; i++){
4914             if(this.config[i].id == id){
4915                 return i;
4916             }
4917         }
4918         return -1;
4919     },
4920     
4921     /**
4922      * Returns the index for a specified column dataIndex.
4923      * @param {String} dataIndex The column dataIndex
4924      * @return {Number} the index, or -1 if not found
4925      */
4926     
4927     findColumnIndex : function(dataIndex){
4928         for(var i = 0, len = this.config.length; i < len; i++){
4929             if(this.config[i].dataIndex == dataIndex){
4930                 return i;
4931             }
4932         }
4933         return -1;
4934     },
4935     
4936     
4937     moveColumn : function(oldIndex, newIndex){
4938         var c = this.config[oldIndex];
4939         this.config.splice(oldIndex, 1);
4940         this.config.splice(newIndex, 0, c);
4941         this.dataMap = null;
4942         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4943     },
4944
4945     isLocked : function(colIndex){
4946         return this.config[colIndex].locked === true;
4947     },
4948
4949     setLocked : function(colIndex, value, suppressEvent){
4950         if(this.isLocked(colIndex) == value){
4951             return;
4952         }
4953         this.config[colIndex].locked = value;
4954         if(!suppressEvent){
4955             this.fireEvent("columnlockchange", this, colIndex, value);
4956         }
4957     },
4958
4959     getTotalLockedWidth : function(){
4960         var totalWidth = 0;
4961         for(var i = 0; i < this.config.length; i++){
4962             if(this.isLocked(i) && !this.isHidden(i)){
4963                 this.totalWidth += this.getColumnWidth(i);
4964             }
4965         }
4966         return totalWidth;
4967     },
4968
4969     getLockedCount : function(){
4970         for(var i = 0, len = this.config.length; i < len; i++){
4971             if(!this.isLocked(i)){
4972                 return i;
4973             }
4974         }
4975     },
4976
4977     /**
4978      * Returns the number of columns.
4979      * @return {Number}
4980      */
4981     getColumnCount : function(visibleOnly){
4982         if(visibleOnly === true){
4983             var c = 0;
4984             for(var i = 0, len = this.config.length; i < len; i++){
4985                 if(!this.isHidden(i)){
4986                     c++;
4987                 }
4988             }
4989             return c;
4990         }
4991         return this.config.length;
4992     },
4993
4994     /**
4995      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4996      * @param {Function} fn
4997      * @param {Object} scope (optional)
4998      * @return {Array} result
4999      */
5000     getColumnsBy : function(fn, scope){
5001         var r = [];
5002         for(var i = 0, len = this.config.length; i < len; i++){
5003             var c = this.config[i];
5004             if(fn.call(scope||this, c, i) === true){
5005                 r[r.length] = c;
5006             }
5007         }
5008         return r;
5009     },
5010
5011     /**
5012      * Returns true if the specified column is sortable.
5013      * @param {Number} col The column index
5014      * @return {Boolean}
5015      */
5016     isSortable : function(col){
5017         if(typeof this.config[col].sortable == "undefined"){
5018             return this.defaultSortable;
5019         }
5020         return this.config[col].sortable;
5021     },
5022
5023     /**
5024      * Returns the rendering (formatting) function defined for the column.
5025      * @param {Number} col The column index.
5026      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5027      */
5028     getRenderer : function(col){
5029         if(!this.config[col].renderer){
5030             return Roo.grid.ColumnModel.defaultRenderer;
5031         }
5032         return this.config[col].renderer;
5033     },
5034
5035     /**
5036      * Sets the rendering (formatting) function for a column.
5037      * @param {Number} col The column index
5038      * @param {Function} fn The function to use to process the cell's raw data
5039      * to return HTML markup for the grid view. The render function is called with
5040      * the following parameters:<ul>
5041      * <li>Data value.</li>
5042      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5043      * <li>css A CSS style string to apply to the table cell.</li>
5044      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5045      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5046      * <li>Row index</li>
5047      * <li>Column index</li>
5048      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5049      */
5050     setRenderer : function(col, fn){
5051         this.config[col].renderer = fn;
5052     },
5053
5054     /**
5055      * Returns the width for the specified column.
5056      * @param {Number} col The column index
5057      * @return {Number}
5058      */
5059     getColumnWidth : function(col){
5060         return this.config[col].width * 1 || this.defaultWidth;
5061     },
5062
5063     /**
5064      * Sets the width for a column.
5065      * @param {Number} col The column index
5066      * @param {Number} width The new width
5067      */
5068     setColumnWidth : function(col, width, suppressEvent){
5069         this.config[col].width = width;
5070         this.totalWidth = null;
5071         if(!suppressEvent){
5072              this.fireEvent("widthchange", this, col, width);
5073         }
5074     },
5075
5076     /**
5077      * Returns the total width of all columns.
5078      * @param {Boolean} includeHidden True to include hidden column widths
5079      * @return {Number}
5080      */
5081     getTotalWidth : function(includeHidden){
5082         if(!this.totalWidth){
5083             this.totalWidth = 0;
5084             for(var i = 0, len = this.config.length; i < len; i++){
5085                 if(includeHidden || !this.isHidden(i)){
5086                     this.totalWidth += this.getColumnWidth(i);
5087                 }
5088             }
5089         }
5090         return this.totalWidth;
5091     },
5092
5093     /**
5094      * Returns the header for the specified column.
5095      * @param {Number} col The column index
5096      * @return {String}
5097      */
5098     getColumnHeader : function(col){
5099         return this.config[col].header;
5100     },
5101
5102     /**
5103      * Sets the header for a column.
5104      * @param {Number} col The column index
5105      * @param {String} header The new header
5106      */
5107     setColumnHeader : function(col, header){
5108         this.config[col].header = header;
5109         this.fireEvent("headerchange", this, col, header);
5110     },
5111
5112     /**
5113      * Returns the tooltip for the specified column.
5114      * @param {Number} col The column index
5115      * @return {String}
5116      */
5117     getColumnTooltip : function(col){
5118             return this.config[col].tooltip;
5119     },
5120     /**
5121      * Sets the tooltip for a column.
5122      * @param {Number} col The column index
5123      * @param {String} tooltip The new tooltip
5124      */
5125     setColumnTooltip : function(col, tooltip){
5126             this.config[col].tooltip = tooltip;
5127     },
5128
5129     /**
5130      * Returns the dataIndex for the specified column.
5131      * @param {Number} col The column index
5132      * @return {Number}
5133      */
5134     getDataIndex : function(col){
5135         return this.config[col].dataIndex;
5136     },
5137
5138     /**
5139      * Sets the dataIndex for a column.
5140      * @param {Number} col The column index
5141      * @param {Number} dataIndex The new dataIndex
5142      */
5143     setDataIndex : function(col, dataIndex){
5144         this.config[col].dataIndex = dataIndex;
5145     },
5146
5147     
5148     
5149     /**
5150      * Returns true if the cell is editable.
5151      * @param {Number} colIndex The column index
5152      * @param {Number} rowIndex The row index
5153      * @return {Boolean}
5154      */
5155     isCellEditable : function(colIndex, rowIndex){
5156         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5157     },
5158
5159     /**
5160      * Returns the editor defined for the cell/column.
5161      * return false or null to disable editing.
5162      * @param {Number} colIndex The column index
5163      * @param {Number} rowIndex The row index
5164      * @return {Object}
5165      */
5166     getCellEditor : function(colIndex, rowIndex){
5167         return this.config[colIndex].editor;
5168     },
5169
5170     /**
5171      * Sets if a column is editable.
5172      * @param {Number} col The column index
5173      * @param {Boolean} editable True if the column is editable
5174      */
5175     setEditable : function(col, editable){
5176         this.config[col].editable = editable;
5177     },
5178
5179
5180     /**
5181      * Returns true if the column is hidden.
5182      * @param {Number} colIndex The column index
5183      * @return {Boolean}
5184      */
5185     isHidden : function(colIndex){
5186         return this.config[colIndex].hidden;
5187     },
5188
5189
5190     /**
5191      * Returns true if the column width cannot be changed
5192      */
5193     isFixed : function(colIndex){
5194         return this.config[colIndex].fixed;
5195     },
5196
5197     /**
5198      * Returns true if the column can be resized
5199      * @return {Boolean}
5200      */
5201     isResizable : function(colIndex){
5202         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5203     },
5204     /**
5205      * Sets if a column is hidden.
5206      * @param {Number} colIndex The column index
5207      * @param {Boolean} hidden True if the column is hidden
5208      */
5209     setHidden : function(colIndex, hidden){
5210         this.config[colIndex].hidden = hidden;
5211         this.totalWidth = null;
5212         this.fireEvent("hiddenchange", this, colIndex, hidden);
5213     },
5214
5215     /**
5216      * Sets the editor for a column.
5217      * @param {Number} col The column index
5218      * @param {Object} editor The editor object
5219      */
5220     setEditor : function(col, editor){
5221         this.config[col].editor = editor;
5222     }
5223 });
5224
5225 Roo.grid.ColumnModel.defaultRenderer = function(value){
5226         if(typeof value == "string" && value.length < 1){
5227             return "&#160;";
5228         }
5229         return value;
5230 };
5231
5232 // Alias for backwards compatibility
5233 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5234 /*
5235  * Based on:
5236  * Ext JS Library 1.1.1
5237  * Copyright(c) 2006-2007, Ext JS, LLC.
5238  *
5239  * Originally Released Under LGPL - original licence link has changed is not relivant.
5240  *
5241  * Fork - LGPL
5242  * <script type="text/javascript">
5243  */
5244  
5245 /**
5246  * @class Roo.LoadMask
5247  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5248  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5249  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5250  * element's UpdateManager load indicator and will be destroyed after the initial load.
5251  * @constructor
5252  * Create a new LoadMask
5253  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5254  * @param {Object} config The config object
5255  */
5256 Roo.LoadMask = function(el, config){
5257     this.el = Roo.get(el);
5258     Roo.apply(this, config);
5259     if(this.store){
5260         this.store.on('beforeload', this.onBeforeLoad, this);
5261         this.store.on('load', this.onLoad, this);
5262         this.store.on('loadexception', this.onLoadException, this);
5263         this.removeMask = false;
5264     }else{
5265         var um = this.el.getUpdateManager();
5266         um.showLoadIndicator = false; // disable the default indicator
5267         um.on('beforeupdate', this.onBeforeLoad, this);
5268         um.on('update', this.onLoad, this);
5269         um.on('failure', this.onLoad, this);
5270         this.removeMask = true;
5271     }
5272 };
5273
5274 Roo.LoadMask.prototype = {
5275     /**
5276      * @cfg {Boolean} removeMask
5277      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5278      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5279      */
5280     /**
5281      * @cfg {String} msg
5282      * The text to display in a centered loading message box (defaults to 'Loading...')
5283      */
5284     msg : 'Loading...',
5285     /**
5286      * @cfg {String} msgCls
5287      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5288      */
5289     msgCls : 'x-mask-loading',
5290
5291     /**
5292      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5293      * @type Boolean
5294      */
5295     disabled: false,
5296
5297     /**
5298      * Disables the mask to prevent it from being displayed
5299      */
5300     disable : function(){
5301        this.disabled = true;
5302     },
5303
5304     /**
5305      * Enables the mask so that it can be displayed
5306      */
5307     enable : function(){
5308         this.disabled = false;
5309     },
5310     
5311     onLoadException : function()
5312     {
5313         Roo.log(arguments);
5314         
5315         if (typeof(arguments[3]) != 'undefined') {
5316             Roo.MessageBox.alert("Error loading",arguments[3]);
5317         } 
5318         /*
5319         try {
5320             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5321                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5322             }   
5323         } catch(e) {
5324             
5325         }
5326         */
5327     
5328         
5329         
5330         this.el.unmask(this.removeMask);
5331     },
5332     // private
5333     onLoad : function()
5334     {
5335         this.el.unmask(this.removeMask);
5336     },
5337
5338     // private
5339     onBeforeLoad : function(){
5340         if(!this.disabled){
5341             this.el.mask(this.msg, this.msgCls);
5342         }
5343     },
5344
5345     // private
5346     destroy : function(){
5347         if(this.store){
5348             this.store.un('beforeload', this.onBeforeLoad, this);
5349             this.store.un('load', this.onLoad, this);
5350             this.store.un('loadexception', this.onLoadException, this);
5351         }else{
5352             var um = this.el.getUpdateManager();
5353             um.un('beforeupdate', this.onBeforeLoad, this);
5354             um.un('update', this.onLoad, this);
5355             um.un('failure', this.onLoad, this);
5356         }
5357     }
5358 };/*
5359  * - LGPL
5360  *
5361  * table
5362  * 
5363  */
5364
5365 /**
5366  * @class Roo.bootstrap.Table
5367  * @extends Roo.bootstrap.Component
5368  * Bootstrap Table class
5369  * @cfg {String} cls table class
5370  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5371  * @cfg {String} bgcolor Specifies the background color for a table
5372  * @cfg {Number} border Specifies whether the table cells should have borders or not
5373  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5374  * @cfg {Number} cellspacing Specifies the space between cells
5375  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5376  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5377  * @cfg {String} sortable Specifies that the table should be sortable
5378  * @cfg {String} summary Specifies a summary of the content of a table
5379  * @cfg {Number} width Specifies the width of a table
5380  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5381  * 
5382  * @cfg {boolean} striped Should the rows be alternative striped
5383  * @cfg {boolean} bordered Add borders to the table
5384  * @cfg {boolean} hover Add hover highlighting
5385  * @cfg {boolean} condensed Format condensed
5386  * @cfg {boolean} responsive Format condensed
5387  * @cfg {Boolean} loadMask (true|false) default false
5388  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5389  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5390  * @cfg {Boolean} rowSelection (true|false) default false
5391  * @cfg {Boolean} cellSelection (true|false) default false
5392  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5393  
5394  * 
5395  * @constructor
5396  * Create a new Table
5397  * @param {Object} config The config object
5398  */
5399
5400 Roo.bootstrap.Table = function(config){
5401     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5402     
5403     // BC...
5404     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5405     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5406     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5407     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5408     
5409     
5410     if (this.sm) {
5411         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5412         this.sm = this.selModel;
5413         this.sm.xmodule = this.xmodule || false;
5414     }
5415     if (this.cm && typeof(this.cm.config) == 'undefined') {
5416         this.colModel = new Roo.grid.ColumnModel(this.cm);
5417         this.cm = this.colModel;
5418         this.cm.xmodule = this.xmodule || false;
5419     }
5420     if (this.store) {
5421         this.store= Roo.factory(this.store, Roo.data);
5422         this.ds = this.store;
5423         this.ds.xmodule = this.xmodule || false;
5424          
5425     }
5426     if (this.footer && this.store) {
5427         this.footer.dataSource = this.ds;
5428         this.footer = Roo.factory(this.footer);
5429     }
5430     
5431     /** @private */
5432     this.addEvents({
5433         /**
5434          * @event cellclick
5435          * Fires when a cell is clicked
5436          * @param {Roo.bootstrap.Table} this
5437          * @param {Roo.Element} el
5438          * @param {Number} rowIndex
5439          * @param {Number} columnIndex
5440          * @param {Roo.EventObject} e
5441          */
5442         "cellclick" : true,
5443         /**
5444          * @event celldblclick
5445          * Fires when a cell is double clicked
5446          * @param {Roo.bootstrap.Table} this
5447          * @param {Roo.Element} el
5448          * @param {Number} rowIndex
5449          * @param {Number} columnIndex
5450          * @param {Roo.EventObject} e
5451          */
5452         "celldblclick" : true,
5453         /**
5454          * @event rowclick
5455          * Fires when a row is clicked
5456          * @param {Roo.bootstrap.Table} this
5457          * @param {Roo.Element} el
5458          * @param {Number} rowIndex
5459          * @param {Roo.EventObject} e
5460          */
5461         "rowclick" : true,
5462         /**
5463          * @event rowdblclick
5464          * Fires when a row is double clicked
5465          * @param {Roo.bootstrap.Table} this
5466          * @param {Roo.Element} el
5467          * @param {Number} rowIndex
5468          * @param {Roo.EventObject} e
5469          */
5470         "rowdblclick" : true,
5471         /**
5472          * @event mouseover
5473          * Fires when a mouseover occur
5474          * @param {Roo.bootstrap.Table} this
5475          * @param {Roo.Element} el
5476          * @param {Number} rowIndex
5477          * @param {Number} columnIndex
5478          * @param {Roo.EventObject} e
5479          */
5480         "mouseover" : true,
5481         /**
5482          * @event mouseout
5483          * Fires when a mouseout occur
5484          * @param {Roo.bootstrap.Table} this
5485          * @param {Roo.Element} el
5486          * @param {Number} rowIndex
5487          * @param {Number} columnIndex
5488          * @param {Roo.EventObject} e
5489          */
5490         "mouseout" : true,
5491         /**
5492          * @event rowclass
5493          * Fires when a row is rendered, so you can change add a style to it.
5494          * @param {Roo.bootstrap.Table} this
5495          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5496          */
5497         'rowclass' : true,
5498           /**
5499          * @event rowsrendered
5500          * Fires when all the  rows have been rendered
5501          * @param {Roo.bootstrap.Table} this
5502          */
5503         'rowsrendered' : true
5504         
5505     });
5506 };
5507
5508 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5509     
5510     cls: false,
5511     align: false,
5512     bgcolor: false,
5513     border: false,
5514     cellpadding: false,
5515     cellspacing: false,
5516     frame: false,
5517     rules: false,
5518     sortable: false,
5519     summary: false,
5520     width: false,
5521     striped : false,
5522     bordered: false,
5523     hover:  false,
5524     condensed : false,
5525     responsive : false,
5526     sm : false,
5527     cm : false,
5528     store : false,
5529     loadMask : false,
5530     footerShow : true,
5531     headerShow : true,
5532   
5533     rowSelection : false,
5534     cellSelection : false,
5535     layout : false,
5536     
5537     // Roo.Element - the tbody
5538     mainBody: false, 
5539     
5540     getAutoCreate : function(){
5541         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5542         
5543         cfg = {
5544             tag: 'table',
5545             cls : 'table',
5546             cn : []
5547         }
5548             
5549         if (this.striped) {
5550             cfg.cls += ' table-striped';
5551         }
5552         
5553         if (this.hover) {
5554             cfg.cls += ' table-hover';
5555         }
5556         if (this.bordered) {
5557             cfg.cls += ' table-bordered';
5558         }
5559         if (this.condensed) {
5560             cfg.cls += ' table-condensed';
5561         }
5562         if (this.responsive) {
5563             cfg.cls += ' table-responsive';
5564         }
5565         
5566         if (this.cls) {
5567             cfg.cls+=  ' ' +this.cls;
5568         }
5569         
5570         // this lot should be simplifed...
5571         
5572         if (this.align) {
5573             cfg.align=this.align;
5574         }
5575         if (this.bgcolor) {
5576             cfg.bgcolor=this.bgcolor;
5577         }
5578         if (this.border) {
5579             cfg.border=this.border;
5580         }
5581         if (this.cellpadding) {
5582             cfg.cellpadding=this.cellpadding;
5583         }
5584         if (this.cellspacing) {
5585             cfg.cellspacing=this.cellspacing;
5586         }
5587         if (this.frame) {
5588             cfg.frame=this.frame;
5589         }
5590         if (this.rules) {
5591             cfg.rules=this.rules;
5592         }
5593         if (this.sortable) {
5594             cfg.sortable=this.sortable;
5595         }
5596         if (this.summary) {
5597             cfg.summary=this.summary;
5598         }
5599         if (this.width) {
5600             cfg.width=this.width;
5601         }
5602         if (this.layout) {
5603             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5604         }
5605         
5606         if(this.store || this.cm){
5607             if(this.headerShow){
5608                 cfg.cn.push(this.renderHeader());
5609             }
5610             
5611             cfg.cn.push(this.renderBody());
5612             
5613             if(this.footerShow){
5614                 cfg.cn.push(this.renderFooter());
5615             }
5616             
5617             cfg.cls+=  ' TableGrid';
5618         }
5619         
5620         return { cn : [ cfg ] };
5621     },
5622     
5623     initEvents : function()
5624     {   
5625         if(!this.store || !this.cm){
5626             return;
5627         }
5628         
5629         //Roo.log('initEvents with ds!!!!');
5630         
5631         this.mainBody = this.el.select('tbody', true).first();
5632         
5633         
5634         var _this = this;
5635         
5636         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5637             e.on('click', _this.sort, _this);
5638         });
5639         
5640         this.el.on("click", this.onClick, this);
5641         this.el.on("dblclick", this.onDblClick, this);
5642         
5643         // why is this done????? = it breaks dialogs??
5644         //this.parent().el.setStyle('position', 'relative');
5645         
5646         
5647         if (this.footer) {
5648             this.footer.parentId = this.id;
5649             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5650         }
5651         
5652         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5653         
5654         this.store.on('load', this.onLoad, this);
5655         this.store.on('beforeload', this.onBeforeLoad, this);
5656         this.store.on('update', this.onUpdate, this);
5657         this.store.on('add', this.onAdd, this);
5658         
5659     },
5660     
5661     onMouseover : function(e, el)
5662     {
5663         var cell = Roo.get(el);
5664         
5665         if(!cell){
5666             return;
5667         }
5668         
5669         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5670             cell = cell.findParent('td', false, true);
5671         }
5672         
5673         var row = cell.findParent('tr', false, true);
5674         var cellIndex = cell.dom.cellIndex;
5675         var rowIndex = row.dom.rowIndex - 1; // start from 0
5676         
5677         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5678         
5679     },
5680     
5681     onMouseout : function(e, el)
5682     {
5683         var cell = Roo.get(el);
5684         
5685         if(!cell){
5686             return;
5687         }
5688         
5689         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5690             cell = cell.findParent('td', false, true);
5691         }
5692         
5693         var row = cell.findParent('tr', false, true);
5694         var cellIndex = cell.dom.cellIndex;
5695         var rowIndex = row.dom.rowIndex - 1; // start from 0
5696         
5697         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5698         
5699     },
5700     
5701     onClick : function(e, el)
5702     {
5703         var cell = Roo.get(el);
5704         
5705         if(!cell || (!this.cellSelection && !this.rowSelection)){
5706             return;
5707         }
5708         
5709         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5710             cell = cell.findParent('td', false, true);
5711         }
5712         
5713         if(!cell || typeof(cell) == 'undefined'){
5714             return;
5715         }
5716         
5717         var row = cell.findParent('tr', false, true);
5718         
5719         if(!row || typeof(row) == 'undefined'){
5720             return;
5721         }
5722         
5723         var cellIndex = cell.dom.cellIndex;
5724         var rowIndex = this.getRowIndex(row);
5725         
5726         // why??? - should these not be based on SelectionModel?
5727         if(this.cellSelection){
5728             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5729         }
5730         
5731         if(this.rowSelection){
5732             this.fireEvent('rowclick', this, row, rowIndex, e);
5733         }
5734         
5735         
5736     },
5737     
5738     onDblClick : function(e,el)
5739     {
5740         var cell = Roo.get(el);
5741         
5742         if(!cell || (!this.CellSelection && !this.RowSelection)){
5743             return;
5744         }
5745         
5746         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5747             cell = cell.findParent('td', false, true);
5748         }
5749         
5750         if(!cell || typeof(cell) == 'undefined'){
5751             return;
5752         }
5753         
5754         var row = cell.findParent('tr', false, true);
5755         
5756         if(!row || typeof(row) == 'undefined'){
5757             return;
5758         }
5759         
5760         var cellIndex = cell.dom.cellIndex;
5761         var rowIndex = this.getRowIndex(row);
5762         
5763         if(this.CellSelection){
5764             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5765         }
5766         
5767         if(this.RowSelection){
5768             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5769         }
5770     },
5771     
5772     sort : function(e,el)
5773     {
5774         var col = Roo.get(el);
5775         
5776         if(!col.hasClass('sortable')){
5777             return;
5778         }
5779         
5780         var sort = col.attr('sort');
5781         var dir = 'ASC';
5782         
5783         if(col.hasClass('glyphicon-arrow-up')){
5784             dir = 'DESC';
5785         }
5786         
5787         this.store.sortInfo = {field : sort, direction : dir};
5788         
5789         if (this.footer) {
5790             Roo.log("calling footer first");
5791             this.footer.onClick('first');
5792         } else {
5793         
5794             this.store.load({ params : { start : 0 } });
5795         }
5796     },
5797     
5798     renderHeader : function()
5799     {
5800         var header = {
5801             tag: 'thead',
5802             cn : []
5803         };
5804         
5805         var cm = this.cm;
5806         
5807         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5808             
5809             var config = cm.config[i];
5810             
5811             var c = {
5812                 tag: 'th',
5813                 style : '',
5814                 html: cm.getColumnHeader(i)
5815             };
5816             
5817             var hh = '';
5818             
5819             if(typeof(config.lgHeader) != 'undefined'){
5820                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5821             }
5822             
5823             if(typeof(config.mdHeader) != 'undefined'){
5824                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5825             }
5826             
5827             if(typeof(config.smHeader) != 'undefined'){
5828                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5829             }
5830             
5831             if(typeof(config.xsHeader) != 'undefined'){
5832                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5833             }
5834             
5835             if(hh.length){
5836                 c.html = hh;
5837             }
5838             
5839             if(typeof(config.tooltip) != 'undefined'){
5840                 c.tooltip = config.tooltip;
5841             }
5842             
5843             if(typeof(config.colspan) != 'undefined'){
5844                 c.colspan = config.colspan;
5845             }
5846             
5847             if(typeof(config.hidden) != 'undefined' && config.hidden){
5848                 c.style += ' display:none;';
5849             }
5850             
5851             if(typeof(config.dataIndex) != 'undefined'){
5852                 c.sort = config.dataIndex;
5853             }
5854             
5855             if(typeof(config.sortable) != 'undefined' && config.sortable){
5856                 c.cls = 'sortable';
5857             }
5858             
5859             if(typeof(config.align) != 'undefined' && config.align.length){
5860                 c.style += ' text-align:' + config.align + ';';
5861             }
5862             
5863             if(typeof(config.width) != 'undefined'){
5864                 c.style += ' width:' + config.width + 'px;';
5865             }
5866             
5867             if(typeof(config.cls) != 'undefined'){
5868                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5869             }
5870             
5871             header.cn.push(c)
5872         }
5873         
5874         return header;
5875     },
5876     
5877     renderBody : function()
5878     {
5879         var body = {
5880             tag: 'tbody',
5881             cn : [
5882                 {
5883                     tag: 'tr',
5884                     cn : [
5885                         {
5886                             tag : 'td',
5887                             colspan :  this.cm.getColumnCount()
5888                         }
5889                     ]
5890                 }
5891             ]
5892         };
5893         
5894         return body;
5895     },
5896     
5897     renderFooter : function()
5898     {
5899         var footer = {
5900             tag: 'tfoot',
5901             cn : [
5902                 {
5903                     tag: 'tr',
5904                     cn : [
5905                         {
5906                             tag : 'td',
5907                             colspan :  this.cm.getColumnCount()
5908                         }
5909                     ]
5910                 }
5911             ]
5912         };
5913         
5914         return footer;
5915     },
5916     
5917     
5918     
5919     onLoad : function()
5920     {
5921         Roo.log('ds onload');
5922         this.clear();
5923         
5924         var _this = this;
5925         var cm = this.cm;
5926         var ds = this.store;
5927         
5928         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5929             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5930             
5931             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5932                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5933             }
5934             
5935             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5936                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5937             }
5938         });
5939         
5940         var tbody =  this.mainBody;
5941               
5942         if(ds.getCount() > 0){
5943             ds.data.each(function(d,rowIndex){
5944                 var row =  this.renderRow(cm, ds, rowIndex);
5945                 
5946                 tbody.createChild(row);
5947                 
5948                 var _this = this;
5949                 
5950                 if(row.cellObjects.length){
5951                     Roo.each(row.cellObjects, function(r){
5952                         _this.renderCellObject(r);
5953                     })
5954                 }
5955                 
5956             }, this);
5957         }
5958         
5959         Roo.each(this.el.select('tbody td', true).elements, function(e){
5960             e.on('mouseover', _this.onMouseover, _this);
5961         });
5962         
5963         Roo.each(this.el.select('tbody td', true).elements, function(e){
5964             e.on('mouseout', _this.onMouseout, _this);
5965         });
5966         this.fireEvent('rowsrendered', this);
5967         //if(this.loadMask){
5968         //    this.maskEl.hide();
5969         //}
5970     },
5971     
5972     
5973     onUpdate : function(ds,record)
5974     {
5975         this.refreshRow(record);
5976     },
5977     
5978     onRemove : function(ds, record, index, isUpdate){
5979         if(isUpdate !== true){
5980             this.fireEvent("beforerowremoved", this, index, record);
5981         }
5982         var bt = this.mainBody.dom;
5983         
5984         var rows = this.el.select('tbody > tr', true).elements;
5985         
5986         if(typeof(rows[index]) != 'undefined'){
5987             bt.removeChild(rows[index].dom);
5988         }
5989         
5990 //        if(bt.rows[index]){
5991 //            bt.removeChild(bt.rows[index]);
5992 //        }
5993         
5994         if(isUpdate !== true){
5995             //this.stripeRows(index);
5996             //this.syncRowHeights(index, index);
5997             //this.layout();
5998             this.fireEvent("rowremoved", this, index, record);
5999         }
6000     },
6001     
6002     onAdd : function(ds, records, rowIndex)
6003     {
6004         //Roo.log('on Add called');
6005         // - note this does not handle multiple adding very well..
6006         var bt = this.mainBody.dom;
6007         for (var i =0 ; i < records.length;i++) {
6008             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6009             //Roo.log(records[i]);
6010             //Roo.log(this.store.getAt(rowIndex+i));
6011             this.insertRow(this.store, rowIndex + i, false);
6012             return;
6013         }
6014         
6015     },
6016     
6017     
6018     refreshRow : function(record){
6019         var ds = this.store, index;
6020         if(typeof record == 'number'){
6021             index = record;
6022             record = ds.getAt(index);
6023         }else{
6024             index = ds.indexOf(record);
6025         }
6026         this.insertRow(ds, index, true);
6027         this.onRemove(ds, record, index+1, true);
6028         //this.syncRowHeights(index, index);
6029         //this.layout();
6030         this.fireEvent("rowupdated", this, index, record);
6031     },
6032     
6033     insertRow : function(dm, rowIndex, isUpdate){
6034         
6035         if(!isUpdate){
6036             this.fireEvent("beforerowsinserted", this, rowIndex);
6037         }
6038             //var s = this.getScrollState();
6039         var row = this.renderRow(this.cm, this.store, rowIndex);
6040         // insert before rowIndex..
6041         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6042         
6043         var _this = this;
6044                 
6045         if(row.cellObjects.length){
6046             Roo.each(row.cellObjects, function(r){
6047                 _this.renderCellObject(r);
6048             })
6049         }
6050             
6051         if(!isUpdate){
6052             this.fireEvent("rowsinserted", this, rowIndex);
6053             //this.syncRowHeights(firstRow, lastRow);
6054             //this.stripeRows(firstRow);
6055             //this.layout();
6056         }
6057         
6058     },
6059     
6060     
6061     getRowDom : function(rowIndex)
6062     {
6063         var rows = this.el.select('tbody > tr', true).elements;
6064         
6065         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6066         
6067     },
6068     // returns the object tree for a tr..
6069   
6070     
6071     renderRow : function(cm, ds, rowIndex) 
6072     {
6073         
6074         var d = ds.getAt(rowIndex);
6075         
6076         var row = {
6077             tag : 'tr',
6078             cn : []
6079         };
6080             
6081         var cellObjects = [];
6082         
6083         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6084             var config = cm.config[i];
6085             
6086             var renderer = cm.getRenderer(i);
6087             var value = '';
6088             var id = false;
6089             
6090             if(typeof(renderer) !== 'undefined'){
6091                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6092             }
6093             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6094             // and are rendered into the cells after the row is rendered - using the id for the element.
6095             
6096             if(typeof(value) === 'object'){
6097                 id = Roo.id();
6098                 cellObjects.push({
6099                     container : id,
6100                     cfg : value 
6101                 })
6102             }
6103             
6104             var rowcfg = {
6105                 record: d,
6106                 rowIndex : rowIndex,
6107                 colIndex : i,
6108                 rowClass : ''
6109             }
6110
6111             this.fireEvent('rowclass', this, rowcfg);
6112             
6113             var td = {
6114                 tag: 'td',
6115                 cls : rowcfg.rowClass,
6116                 style: '',
6117                 html: (typeof(value) === 'object') ? '' : value
6118             };
6119             
6120             if (id) {
6121                 td.id = id;
6122             }
6123             
6124             if(typeof(config.colspan) != 'undefined'){
6125                 td.colspan = config.colspan;
6126             }
6127             
6128             if(typeof(config.hidden) != 'undefined' && config.hidden){
6129                 td.style += ' display:none;';
6130             }
6131             
6132             if(typeof(config.align) != 'undefined' && config.align.length){
6133                 td.style += ' text-align:' + config.align + ';';
6134             }
6135             
6136             if(typeof(config.width) != 'undefined'){
6137                 td.style += ' width:' +  config.width + 'px;';
6138             }
6139             
6140             if(typeof(config.cursor) != 'undefined'){
6141                 td.style += ' cursor:' +  config.cursor + ';';
6142             }
6143             
6144             if(typeof(config.cls) != 'undefined'){
6145                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6146             }
6147              
6148             row.cn.push(td);
6149            
6150         }
6151         
6152         row.cellObjects = cellObjects;
6153         
6154         return row;
6155           
6156     },
6157     
6158     
6159     
6160     onBeforeLoad : function()
6161     {
6162         //Roo.log('ds onBeforeLoad');
6163         
6164         //this.clear();
6165         
6166         //if(this.loadMask){
6167         //    this.maskEl.show();
6168         //}
6169     },
6170      /**
6171      * Remove all rows
6172      */
6173     clear : function()
6174     {
6175         this.el.select('tbody', true).first().dom.innerHTML = '';
6176     },
6177     /**
6178      * Show or hide a row.
6179      * @param {Number} rowIndex to show or hide
6180      * @param {Boolean} state hide
6181      */
6182     setRowVisibility : function(rowIndex, state)
6183     {
6184         var bt = this.mainBody.dom;
6185         
6186         var rows = this.el.select('tbody > tr', true).elements;
6187         
6188         if(typeof(rows[rowIndex]) == 'undefined'){
6189             return;
6190         }
6191         rows[rowIndex].dom.style.display = state ? '' : 'none';
6192     },
6193     
6194     
6195     getSelectionModel : function(){
6196         if(!this.selModel){
6197             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6198         }
6199         return this.selModel;
6200     },
6201     /*
6202      * Render the Roo.bootstrap object from renderder
6203      */
6204     renderCellObject : function(r)
6205     {
6206         var _this = this;
6207         
6208         var t = r.cfg.render(r.container);
6209         
6210         if(r.cfg.cn){
6211             Roo.each(r.cfg.cn, function(c){
6212                 var child = {
6213                     container: t.getChildContainer(),
6214                     cfg: c
6215                 }
6216                 _this.renderCellObject(child);
6217             })
6218         }
6219     },
6220     
6221     getRowIndex : function(row)
6222     {
6223         var rowIndex = -1;
6224         
6225         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6226             if(el != row){
6227                 return;
6228             }
6229             
6230             rowIndex = index;
6231         });
6232         
6233         return rowIndex;
6234     }
6235    
6236 });
6237
6238  
6239
6240  /*
6241  * - LGPL
6242  *
6243  * table cell
6244  * 
6245  */
6246
6247 /**
6248  * @class Roo.bootstrap.TableCell
6249  * @extends Roo.bootstrap.Component
6250  * Bootstrap TableCell class
6251  * @cfg {String} html cell contain text
6252  * @cfg {String} cls cell class
6253  * @cfg {String} tag cell tag (td|th) default td
6254  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6255  * @cfg {String} align Aligns the content in a cell
6256  * @cfg {String} axis Categorizes cells
6257  * @cfg {String} bgcolor Specifies the background color of a cell
6258  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6259  * @cfg {Number} colspan Specifies the number of columns a cell should span
6260  * @cfg {String} headers Specifies one or more header cells a cell is related to
6261  * @cfg {Number} height Sets the height of a cell
6262  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6263  * @cfg {Number} rowspan Sets the number of rows a cell should span
6264  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6265  * @cfg {String} valign Vertical aligns the content in a cell
6266  * @cfg {Number} width Specifies the width of a cell
6267  * 
6268  * @constructor
6269  * Create a new TableCell
6270  * @param {Object} config The config object
6271  */
6272
6273 Roo.bootstrap.TableCell = function(config){
6274     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6275 };
6276
6277 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6278     
6279     html: false,
6280     cls: false,
6281     tag: false,
6282     abbr: false,
6283     align: false,
6284     axis: false,
6285     bgcolor: false,
6286     charoff: false,
6287     colspan: false,
6288     headers: false,
6289     height: false,
6290     nowrap: false,
6291     rowspan: false,
6292     scope: false,
6293     valign: false,
6294     width: false,
6295     
6296     
6297     getAutoCreate : function(){
6298         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6299         
6300         cfg = {
6301             tag: 'td'
6302         }
6303         
6304         if(this.tag){
6305             cfg.tag = this.tag;
6306         }
6307         
6308         if (this.html) {
6309             cfg.html=this.html
6310         }
6311         if (this.cls) {
6312             cfg.cls=this.cls
6313         }
6314         if (this.abbr) {
6315             cfg.abbr=this.abbr
6316         }
6317         if (this.align) {
6318             cfg.align=this.align
6319         }
6320         if (this.axis) {
6321             cfg.axis=this.axis
6322         }
6323         if (this.bgcolor) {
6324             cfg.bgcolor=this.bgcolor
6325         }
6326         if (this.charoff) {
6327             cfg.charoff=this.charoff
6328         }
6329         if (this.colspan) {
6330             cfg.colspan=this.colspan
6331         }
6332         if (this.headers) {
6333             cfg.headers=this.headers
6334         }
6335         if (this.height) {
6336             cfg.height=this.height
6337         }
6338         if (this.nowrap) {
6339             cfg.nowrap=this.nowrap
6340         }
6341         if (this.rowspan) {
6342             cfg.rowspan=this.rowspan
6343         }
6344         if (this.scope) {
6345             cfg.scope=this.scope
6346         }
6347         if (this.valign) {
6348             cfg.valign=this.valign
6349         }
6350         if (this.width) {
6351             cfg.width=this.width
6352         }
6353         
6354         
6355         return cfg;
6356     }
6357    
6358 });
6359
6360  
6361
6362  /*
6363  * - LGPL
6364  *
6365  * table row
6366  * 
6367  */
6368
6369 /**
6370  * @class Roo.bootstrap.TableRow
6371  * @extends Roo.bootstrap.Component
6372  * Bootstrap TableRow class
6373  * @cfg {String} cls row class
6374  * @cfg {String} align Aligns the content in a table row
6375  * @cfg {String} bgcolor Specifies a background color for a table row
6376  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6377  * @cfg {String} valign Vertical aligns the content in a table row
6378  * 
6379  * @constructor
6380  * Create a new TableRow
6381  * @param {Object} config The config object
6382  */
6383
6384 Roo.bootstrap.TableRow = function(config){
6385     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6386 };
6387
6388 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6389     
6390     cls: false,
6391     align: false,
6392     bgcolor: false,
6393     charoff: false,
6394     valign: false,
6395     
6396     getAutoCreate : function(){
6397         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6398         
6399         cfg = {
6400             tag: 'tr'
6401         }
6402             
6403         if(this.cls){
6404             cfg.cls = this.cls;
6405         }
6406         if(this.align){
6407             cfg.align = this.align;
6408         }
6409         if(this.bgcolor){
6410             cfg.bgcolor = this.bgcolor;
6411         }
6412         if(this.charoff){
6413             cfg.charoff = this.charoff;
6414         }
6415         if(this.valign){
6416             cfg.valign = this.valign;
6417         }
6418         
6419         return cfg;
6420     }
6421    
6422 });
6423
6424  
6425
6426  /*
6427  * - LGPL
6428  *
6429  * table body
6430  * 
6431  */
6432
6433 /**
6434  * @class Roo.bootstrap.TableBody
6435  * @extends Roo.bootstrap.Component
6436  * Bootstrap TableBody class
6437  * @cfg {String} cls element class
6438  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6439  * @cfg {String} align Aligns the content inside the element
6440  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6441  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6442  * 
6443  * @constructor
6444  * Create a new TableBody
6445  * @param {Object} config The config object
6446  */
6447
6448 Roo.bootstrap.TableBody = function(config){
6449     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6450 };
6451
6452 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6453     
6454     cls: false,
6455     tag: false,
6456     align: false,
6457     charoff: false,
6458     valign: false,
6459     
6460     getAutoCreate : function(){
6461         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6462         
6463         cfg = {
6464             tag: 'tbody'
6465         }
6466             
6467         if (this.cls) {
6468             cfg.cls=this.cls
6469         }
6470         if(this.tag){
6471             cfg.tag = this.tag;
6472         }
6473         
6474         if(this.align){
6475             cfg.align = this.align;
6476         }
6477         if(this.charoff){
6478             cfg.charoff = this.charoff;
6479         }
6480         if(this.valign){
6481             cfg.valign = this.valign;
6482         }
6483         
6484         return cfg;
6485     }
6486     
6487     
6488 //    initEvents : function()
6489 //    {
6490 //        
6491 //        if(!this.store){
6492 //            return;
6493 //        }
6494 //        
6495 //        this.store = Roo.factory(this.store, Roo.data);
6496 //        this.store.on('load', this.onLoad, this);
6497 //        
6498 //        this.store.load();
6499 //        
6500 //    },
6501 //    
6502 //    onLoad: function () 
6503 //    {   
6504 //        this.fireEvent('load', this);
6505 //    }
6506 //    
6507 //   
6508 });
6509
6510  
6511
6512  /*
6513  * Based on:
6514  * Ext JS Library 1.1.1
6515  * Copyright(c) 2006-2007, Ext JS, LLC.
6516  *
6517  * Originally Released Under LGPL - original licence link has changed is not relivant.
6518  *
6519  * Fork - LGPL
6520  * <script type="text/javascript">
6521  */
6522
6523 // as we use this in bootstrap.
6524 Roo.namespace('Roo.form');
6525  /**
6526  * @class Roo.form.Action
6527  * Internal Class used to handle form actions
6528  * @constructor
6529  * @param {Roo.form.BasicForm} el The form element or its id
6530  * @param {Object} config Configuration options
6531  */
6532
6533  
6534  
6535 // define the action interface
6536 Roo.form.Action = function(form, options){
6537     this.form = form;
6538     this.options = options || {};
6539 };
6540 /**
6541  * Client Validation Failed
6542  * @const 
6543  */
6544 Roo.form.Action.CLIENT_INVALID = 'client';
6545 /**
6546  * Server Validation Failed
6547  * @const 
6548  */
6549 Roo.form.Action.SERVER_INVALID = 'server';
6550  /**
6551  * Connect to Server Failed
6552  * @const 
6553  */
6554 Roo.form.Action.CONNECT_FAILURE = 'connect';
6555 /**
6556  * Reading Data from Server Failed
6557  * @const 
6558  */
6559 Roo.form.Action.LOAD_FAILURE = 'load';
6560
6561 Roo.form.Action.prototype = {
6562     type : 'default',
6563     failureType : undefined,
6564     response : undefined,
6565     result : undefined,
6566
6567     // interface method
6568     run : function(options){
6569
6570     },
6571
6572     // interface method
6573     success : function(response){
6574
6575     },
6576
6577     // interface method
6578     handleResponse : function(response){
6579
6580     },
6581
6582     // default connection failure
6583     failure : function(response){
6584         
6585         this.response = response;
6586         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6587         this.form.afterAction(this, false);
6588     },
6589
6590     processResponse : function(response){
6591         this.response = response;
6592         if(!response.responseText){
6593             return true;
6594         }
6595         this.result = this.handleResponse(response);
6596         return this.result;
6597     },
6598
6599     // utility functions used internally
6600     getUrl : function(appendParams){
6601         var url = this.options.url || this.form.url || this.form.el.dom.action;
6602         if(appendParams){
6603             var p = this.getParams();
6604             if(p){
6605                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6606             }
6607         }
6608         return url;
6609     },
6610
6611     getMethod : function(){
6612         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6613     },
6614
6615     getParams : function(){
6616         var bp = this.form.baseParams;
6617         var p = this.options.params;
6618         if(p){
6619             if(typeof p == "object"){
6620                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6621             }else if(typeof p == 'string' && bp){
6622                 p += '&' + Roo.urlEncode(bp);
6623             }
6624         }else if(bp){
6625             p = Roo.urlEncode(bp);
6626         }
6627         return p;
6628     },
6629
6630     createCallback : function(){
6631         return {
6632             success: this.success,
6633             failure: this.failure,
6634             scope: this,
6635             timeout: (this.form.timeout*1000),
6636             upload: this.form.fileUpload ? this.success : undefined
6637         };
6638     }
6639 };
6640
6641 Roo.form.Action.Submit = function(form, options){
6642     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6643 };
6644
6645 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6646     type : 'submit',
6647
6648     haveProgress : false,
6649     uploadComplete : false,
6650     
6651     // uploadProgress indicator.
6652     uploadProgress : function()
6653     {
6654         if (!this.form.progressUrl) {
6655             return;
6656         }
6657         
6658         if (!this.haveProgress) {
6659             Roo.MessageBox.progress("Uploading", "Uploading");
6660         }
6661         if (this.uploadComplete) {
6662            Roo.MessageBox.hide();
6663            return;
6664         }
6665         
6666         this.haveProgress = true;
6667    
6668         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6669         
6670         var c = new Roo.data.Connection();
6671         c.request({
6672             url : this.form.progressUrl,
6673             params: {
6674                 id : uid
6675             },
6676             method: 'GET',
6677             success : function(req){
6678                //console.log(data);
6679                 var rdata = false;
6680                 var edata;
6681                 try  {
6682                    rdata = Roo.decode(req.responseText)
6683                 } catch (e) {
6684                     Roo.log("Invalid data from server..");
6685                     Roo.log(edata);
6686                     return;
6687                 }
6688                 if (!rdata || !rdata.success) {
6689                     Roo.log(rdata);
6690                     Roo.MessageBox.alert(Roo.encode(rdata));
6691                     return;
6692                 }
6693                 var data = rdata.data;
6694                 
6695                 if (this.uploadComplete) {
6696                    Roo.MessageBox.hide();
6697                    return;
6698                 }
6699                    
6700                 if (data){
6701                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6702                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6703                     );
6704                 }
6705                 this.uploadProgress.defer(2000,this);
6706             },
6707        
6708             failure: function(data) {
6709                 Roo.log('progress url failed ');
6710                 Roo.log(data);
6711             },
6712             scope : this
6713         });
6714            
6715     },
6716     
6717     
6718     run : function()
6719     {
6720         // run get Values on the form, so it syncs any secondary forms.
6721         this.form.getValues();
6722         
6723         var o = this.options;
6724         var method = this.getMethod();
6725         var isPost = method == 'POST';
6726         if(o.clientValidation === false || this.form.isValid()){
6727             
6728             if (this.form.progressUrl) {
6729                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6730                     (new Date() * 1) + '' + Math.random());
6731                     
6732             } 
6733             
6734             
6735             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6736                 form:this.form.el.dom,
6737                 url:this.getUrl(!isPost),
6738                 method: method,
6739                 params:isPost ? this.getParams() : null,
6740                 isUpload: this.form.fileUpload
6741             }));
6742             
6743             this.uploadProgress();
6744
6745         }else if (o.clientValidation !== false){ // client validation failed
6746             this.failureType = Roo.form.Action.CLIENT_INVALID;
6747             this.form.afterAction(this, false);
6748         }
6749     },
6750
6751     success : function(response)
6752     {
6753         this.uploadComplete= true;
6754         if (this.haveProgress) {
6755             Roo.MessageBox.hide();
6756         }
6757         
6758         
6759         var result = this.processResponse(response);
6760         if(result === true || result.success){
6761             this.form.afterAction(this, true);
6762             return;
6763         }
6764         if(result.errors){
6765             this.form.markInvalid(result.errors);
6766             this.failureType = Roo.form.Action.SERVER_INVALID;
6767         }
6768         this.form.afterAction(this, false);
6769     },
6770     failure : function(response)
6771     {
6772         this.uploadComplete= true;
6773         if (this.haveProgress) {
6774             Roo.MessageBox.hide();
6775         }
6776         
6777         this.response = response;
6778         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6779         this.form.afterAction(this, false);
6780     },
6781     
6782     handleResponse : function(response){
6783         if(this.form.errorReader){
6784             var rs = this.form.errorReader.read(response);
6785             var errors = [];
6786             if(rs.records){
6787                 for(var i = 0, len = rs.records.length; i < len; i++) {
6788                     var r = rs.records[i];
6789                     errors[i] = r.data;
6790                 }
6791             }
6792             if(errors.length < 1){
6793                 errors = null;
6794             }
6795             return {
6796                 success : rs.success,
6797                 errors : errors
6798             };
6799         }
6800         var ret = false;
6801         try {
6802             ret = Roo.decode(response.responseText);
6803         } catch (e) {
6804             ret = {
6805                 success: false,
6806                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6807                 errors : []
6808             };
6809         }
6810         return ret;
6811         
6812     }
6813 });
6814
6815
6816 Roo.form.Action.Load = function(form, options){
6817     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6818     this.reader = this.form.reader;
6819 };
6820
6821 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6822     type : 'load',
6823
6824     run : function(){
6825         
6826         Roo.Ajax.request(Roo.apply(
6827                 this.createCallback(), {
6828                     method:this.getMethod(),
6829                     url:this.getUrl(false),
6830                     params:this.getParams()
6831         }));
6832     },
6833
6834     success : function(response){
6835         
6836         var result = this.processResponse(response);
6837         if(result === true || !result.success || !result.data){
6838             this.failureType = Roo.form.Action.LOAD_FAILURE;
6839             this.form.afterAction(this, false);
6840             return;
6841         }
6842         this.form.clearInvalid();
6843         this.form.setValues(result.data);
6844         this.form.afterAction(this, true);
6845     },
6846
6847     handleResponse : function(response){
6848         if(this.form.reader){
6849             var rs = this.form.reader.read(response);
6850             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6851             return {
6852                 success : rs.success,
6853                 data : data
6854             };
6855         }
6856         return Roo.decode(response.responseText);
6857     }
6858 });
6859
6860 Roo.form.Action.ACTION_TYPES = {
6861     'load' : Roo.form.Action.Load,
6862     'submit' : Roo.form.Action.Submit
6863 };/*
6864  * - LGPL
6865  *
6866  * form
6867  * 
6868  */
6869
6870 /**
6871  * @class Roo.bootstrap.Form
6872  * @extends Roo.bootstrap.Component
6873  * Bootstrap Form class
6874  * @cfg {String} method  GET | POST (default POST)
6875  * @cfg {String} labelAlign top | left (default top)
6876  * @cfg {String} align left  | right - for navbars
6877  * @cfg {Boolean} loadMask load mask when submit (default true)
6878
6879  * 
6880  * @constructor
6881  * Create a new Form
6882  * @param {Object} config The config object
6883  */
6884
6885
6886 Roo.bootstrap.Form = function(config){
6887     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6888     this.addEvents({
6889         /**
6890          * @event clientvalidation
6891          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6892          * @param {Form} this
6893          * @param {Boolean} valid true if the form has passed client-side validation
6894          */
6895         clientvalidation: true,
6896         /**
6897          * @event beforeaction
6898          * Fires before any action is performed. Return false to cancel the action.
6899          * @param {Form} this
6900          * @param {Action} action The action to be performed
6901          */
6902         beforeaction: true,
6903         /**
6904          * @event actionfailed
6905          * Fires when an action fails.
6906          * @param {Form} this
6907          * @param {Action} action The action that failed
6908          */
6909         actionfailed : true,
6910         /**
6911          * @event actioncomplete
6912          * Fires when an action is completed.
6913          * @param {Form} this
6914          * @param {Action} action The action that completed
6915          */
6916         actioncomplete : true
6917     });
6918     
6919 };
6920
6921 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
6922       
6923      /**
6924      * @cfg {String} method
6925      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6926      */
6927     method : 'POST',
6928     /**
6929      * @cfg {String} url
6930      * The URL to use for form actions if one isn't supplied in the action options.
6931      */
6932     /**
6933      * @cfg {Boolean} fileUpload
6934      * Set to true if this form is a file upload.
6935      */
6936      
6937     /**
6938      * @cfg {Object} baseParams
6939      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6940      */
6941       
6942     /**
6943      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6944      */
6945     timeout: 30,
6946     /**
6947      * @cfg {Sting} align (left|right) for navbar forms
6948      */
6949     align : 'left',
6950
6951     // private
6952     activeAction : null,
6953  
6954     /**
6955      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6956      * element by passing it or its id or mask the form itself by passing in true.
6957      * @type Mixed
6958      */
6959     waitMsgTarget : false,
6960     
6961     loadMask : true,
6962     
6963     getAutoCreate : function(){
6964         
6965         var cfg = {
6966             tag: 'form',
6967             method : this.method || 'POST',
6968             id : this.id || Roo.id(),
6969             cls : ''
6970         }
6971         if (this.parent().xtype.match(/^Nav/)) {
6972             cfg.cls = 'navbar-form navbar-' + this.align;
6973             
6974         }
6975         
6976         if (this.labelAlign == 'left' ) {
6977             cfg.cls += ' form-horizontal';
6978         }
6979         
6980         
6981         return cfg;
6982     },
6983     initEvents : function()
6984     {
6985         this.el.on('submit', this.onSubmit, this);
6986         // this was added as random key presses on the form where triggering form submit.
6987         this.el.on('keypress', function(e) {
6988             if (e.getCharCode() != 13) {
6989                 return true;
6990             }
6991             // we might need to allow it for textareas.. and some other items.
6992             // check e.getTarget().
6993             
6994             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6995                 return true;
6996             }
6997         
6998             Roo.log("keypress blocked");
6999             
7000             e.preventDefault();
7001             return false;
7002         });
7003         
7004     },
7005     // private
7006     onSubmit : function(e){
7007         e.stopEvent();
7008     },
7009     
7010      /**
7011      * Returns true if client-side validation on the form is successful.
7012      * @return Boolean
7013      */
7014     isValid : function(){
7015         var items = this.getItems();
7016         var valid = true;
7017         items.each(function(f){
7018            if(!f.validate()){
7019                valid = false;
7020                
7021            }
7022         });
7023         return valid;
7024     },
7025     /**
7026      * Returns true if any fields in this form have changed since their original load.
7027      * @return Boolean
7028      */
7029     isDirty : function(){
7030         var dirty = false;
7031         var items = this.getItems();
7032         items.each(function(f){
7033            if(f.isDirty()){
7034                dirty = true;
7035                return false;
7036            }
7037            return true;
7038         });
7039         return dirty;
7040     },
7041      /**
7042      * Performs a predefined action (submit or load) or custom actions you define on this form.
7043      * @param {String} actionName The name of the action type
7044      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7045      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7046      * accept other config options):
7047      * <pre>
7048 Property          Type             Description
7049 ----------------  ---------------  ----------------------------------------------------------------------------------
7050 url               String           The url for the action (defaults to the form's url)
7051 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7052 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7053 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7054                                    validate the form on the client (defaults to false)
7055      * </pre>
7056      * @return {BasicForm} this
7057      */
7058     doAction : function(action, options){
7059         if(typeof action == 'string'){
7060             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7061         }
7062         if(this.fireEvent('beforeaction', this, action) !== false){
7063             this.beforeAction(action);
7064             action.run.defer(100, action);
7065         }
7066         return this;
7067     },
7068     
7069     // private
7070     beforeAction : function(action){
7071         var o = action.options;
7072         
7073         if(this.loadMask){
7074             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7075         }
7076         // not really supported yet.. ??
7077         
7078         //if(this.waitMsgTarget === true){
7079         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7080         //}else if(this.waitMsgTarget){
7081         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7082         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7083         //}else {
7084         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7085        // }
7086          
7087     },
7088
7089     // private
7090     afterAction : function(action, success){
7091         this.activeAction = null;
7092         var o = action.options;
7093         
7094         //if(this.waitMsgTarget === true){
7095             this.el.unmask();
7096         //}else if(this.waitMsgTarget){
7097         //    this.waitMsgTarget.unmask();
7098         //}else{
7099         //    Roo.MessageBox.updateProgress(1);
7100         //    Roo.MessageBox.hide();
7101        // }
7102         // 
7103         if(success){
7104             if(o.reset){
7105                 this.reset();
7106             }
7107             Roo.callback(o.success, o.scope, [this, action]);
7108             this.fireEvent('actioncomplete', this, action);
7109             
7110         }else{
7111             
7112             // failure condition..
7113             // we have a scenario where updates need confirming.
7114             // eg. if a locking scenario exists..
7115             // we look for { errors : { needs_confirm : true }} in the response.
7116             if (
7117                 (typeof(action.result) != 'undefined')  &&
7118                 (typeof(action.result.errors) != 'undefined')  &&
7119                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7120            ){
7121                 var _t = this;
7122                 Roo.log("not supported yet");
7123                  /*
7124                 
7125                 Roo.MessageBox.confirm(
7126                     "Change requires confirmation",
7127                     action.result.errorMsg,
7128                     function(r) {
7129                         if (r != 'yes') {
7130                             return;
7131                         }
7132                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7133                     }
7134                     
7135                 );
7136                 */
7137                 
7138                 
7139                 return;
7140             }
7141             
7142             Roo.callback(o.failure, o.scope, [this, action]);
7143             // show an error message if no failed handler is set..
7144             if (!this.hasListener('actionfailed')) {
7145                 Roo.log("need to add dialog support");
7146                 /*
7147                 Roo.MessageBox.alert("Error",
7148                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7149                         action.result.errorMsg :
7150                         "Saving Failed, please check your entries or try again"
7151                 );
7152                 */
7153             }
7154             
7155             this.fireEvent('actionfailed', this, action);
7156         }
7157         
7158     },
7159     /**
7160      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7161      * @param {String} id The value to search for
7162      * @return Field
7163      */
7164     findField : function(id){
7165         var items = this.getItems();
7166         var field = items.get(id);
7167         if(!field){
7168              items.each(function(f){
7169                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7170                     field = f;
7171                     return false;
7172                 }
7173                 return true;
7174             });
7175         }
7176         return field || null;
7177     },
7178      /**
7179      * Mark fields in this form invalid in bulk.
7180      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7181      * @return {BasicForm} this
7182      */
7183     markInvalid : function(errors){
7184         if(errors instanceof Array){
7185             for(var i = 0, len = errors.length; i < len; i++){
7186                 var fieldError = errors[i];
7187                 var f = this.findField(fieldError.id);
7188                 if(f){
7189                     f.markInvalid(fieldError.msg);
7190                 }
7191             }
7192         }else{
7193             var field, id;
7194             for(id in errors){
7195                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7196                     field.markInvalid(errors[id]);
7197                 }
7198             }
7199         }
7200         //Roo.each(this.childForms || [], function (f) {
7201         //    f.markInvalid(errors);
7202         //});
7203         
7204         return this;
7205     },
7206
7207     /**
7208      * Set values for fields in this form in bulk.
7209      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7210      * @return {BasicForm} this
7211      */
7212     setValues : function(values){
7213         if(values instanceof Array){ // array of objects
7214             for(var i = 0, len = values.length; i < len; i++){
7215                 var v = values[i];
7216                 var f = this.findField(v.id);
7217                 if(f){
7218                     f.setValue(v.value);
7219                     if(this.trackResetOnLoad){
7220                         f.originalValue = f.getValue();
7221                     }
7222                 }
7223             }
7224         }else{ // object hash
7225             var field, id;
7226             for(id in values){
7227                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7228                     
7229                     if (field.setFromData && 
7230                         field.valueField && 
7231                         field.displayField &&
7232                         // combos' with local stores can 
7233                         // be queried via setValue()
7234                         // to set their value..
7235                         (field.store && !field.store.isLocal)
7236                         ) {
7237                         // it's a combo
7238                         var sd = { };
7239                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7240                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7241                         field.setFromData(sd);
7242                         
7243                     } else {
7244                         field.setValue(values[id]);
7245                     }
7246                     
7247                     
7248                     if(this.trackResetOnLoad){
7249                         field.originalValue = field.getValue();
7250                     }
7251                 }
7252             }
7253         }
7254          
7255         //Roo.each(this.childForms || [], function (f) {
7256         //    f.setValues(values);
7257         //});
7258                 
7259         return this;
7260     },
7261
7262     /**
7263      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7264      * they are returned as an array.
7265      * @param {Boolean} asString
7266      * @return {Object}
7267      */
7268     getValues : function(asString){
7269         //if (this.childForms) {
7270             // copy values from the child forms
7271         //    Roo.each(this.childForms, function (f) {
7272         //        this.setValues(f.getValues());
7273         //    }, this);
7274         //}
7275         
7276         
7277         
7278         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7279         if(asString === true){
7280             return fs;
7281         }
7282         return Roo.urlDecode(fs);
7283     },
7284     
7285     /**
7286      * Returns the fields in this form as an object with key/value pairs. 
7287      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7288      * @return {Object}
7289      */
7290     getFieldValues : function(with_hidden)
7291     {
7292         var items = this.getItems();
7293         var ret = {};
7294         items.each(function(f){
7295             if (!f.getName()) {
7296                 return;
7297             }
7298             var v = f.getValue();
7299             if (f.inputType =='radio') {
7300                 if (typeof(ret[f.getName()]) == 'undefined') {
7301                     ret[f.getName()] = ''; // empty..
7302                 }
7303                 
7304                 if (!f.el.dom.checked) {
7305                     return;
7306                     
7307                 }
7308                 v = f.el.dom.value;
7309                 
7310             }
7311             
7312             // not sure if this supported any more..
7313             if ((typeof(v) == 'object') && f.getRawValue) {
7314                 v = f.getRawValue() ; // dates..
7315             }
7316             // combo boxes where name != hiddenName...
7317             if (f.name != f.getName()) {
7318                 ret[f.name] = f.getRawValue();
7319             }
7320             ret[f.getName()] = v;
7321         });
7322         
7323         return ret;
7324     },
7325
7326     /**
7327      * Clears all invalid messages in this form.
7328      * @return {BasicForm} this
7329      */
7330     clearInvalid : function(){
7331         var items = this.getItems();
7332         
7333         items.each(function(f){
7334            f.clearInvalid();
7335         });
7336         
7337         
7338         
7339         return this;
7340     },
7341
7342     /**
7343      * Resets this form.
7344      * @return {BasicForm} this
7345      */
7346     reset : function(){
7347         var items = this.getItems();
7348         items.each(function(f){
7349             f.reset();
7350         });
7351         
7352         Roo.each(this.childForms || [], function (f) {
7353             f.reset();
7354         });
7355        
7356         
7357         return this;
7358     },
7359     getItems : function()
7360     {
7361         var r=new Roo.util.MixedCollection(false, function(o){
7362             return o.id || (o.id = Roo.id());
7363         });
7364         var iter = function(el) {
7365             if (el.inputEl) {
7366                 r.add(el);
7367             }
7368             if (!el.items) {
7369                 return;
7370             }
7371             Roo.each(el.items,function(e) {
7372                 iter(e);
7373             });
7374             
7375             
7376         };
7377         
7378         iter(this);
7379         return r;
7380         
7381         
7382         
7383         
7384     }
7385     
7386 });
7387
7388  
7389 /*
7390  * Based on:
7391  * Ext JS Library 1.1.1
7392  * Copyright(c) 2006-2007, Ext JS, LLC.
7393  *
7394  * Originally Released Under LGPL - original licence link has changed is not relivant.
7395  *
7396  * Fork - LGPL
7397  * <script type="text/javascript">
7398  */
7399 /**
7400  * @class Roo.form.VTypes
7401  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7402  * @singleton
7403  */
7404 Roo.form.VTypes = function(){
7405     // closure these in so they are only created once.
7406     var alpha = /^[a-zA-Z_]+$/;
7407     var alphanum = /^[a-zA-Z0-9_]+$/;
7408     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7409     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7410
7411     // All these messages and functions are configurable
7412     return {
7413         /**
7414          * The function used to validate email addresses
7415          * @param {String} value The email address
7416          */
7417         'email' : function(v){
7418             return email.test(v);
7419         },
7420         /**
7421          * The error text to display when the email validation function returns false
7422          * @type String
7423          */
7424         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7425         /**
7426          * The keystroke filter mask to be applied on email input
7427          * @type RegExp
7428          */
7429         'emailMask' : /[a-z0-9_\.\-@]/i,
7430
7431         /**
7432          * The function used to validate URLs
7433          * @param {String} value The URL
7434          */
7435         'url' : function(v){
7436             return url.test(v);
7437         },
7438         /**
7439          * The error text to display when the url validation function returns false
7440          * @type String
7441          */
7442         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7443         
7444         /**
7445          * The function used to validate alpha values
7446          * @param {String} value The value
7447          */
7448         'alpha' : function(v){
7449             return alpha.test(v);
7450         },
7451         /**
7452          * The error text to display when the alpha validation function returns false
7453          * @type String
7454          */
7455         'alphaText' : 'This field should only contain letters and _',
7456         /**
7457          * The keystroke filter mask to be applied on alpha input
7458          * @type RegExp
7459          */
7460         'alphaMask' : /[a-z_]/i,
7461
7462         /**
7463          * The function used to validate alphanumeric values
7464          * @param {String} value The value
7465          */
7466         'alphanum' : function(v){
7467             return alphanum.test(v);
7468         },
7469         /**
7470          * The error text to display when the alphanumeric validation function returns false
7471          * @type String
7472          */
7473         'alphanumText' : 'This field should only contain letters, numbers and _',
7474         /**
7475          * The keystroke filter mask to be applied on alphanumeric input
7476          * @type RegExp
7477          */
7478         'alphanumMask' : /[a-z0-9_]/i
7479     };
7480 }();/*
7481  * - LGPL
7482  *
7483  * Input
7484  * 
7485  */
7486
7487 /**
7488  * @class Roo.bootstrap.Input
7489  * @extends Roo.bootstrap.Component
7490  * Bootstrap Input class
7491  * @cfg {Boolean} disabled is it disabled
7492  * @cfg {String} fieldLabel - the label associated
7493  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7494  * @cfg {String} name name of the input
7495  * @cfg {string} fieldLabel - the label associated
7496  * @cfg {string}  inputType - input / file submit ...
7497  * @cfg {string} placeholder - placeholder to put in text.
7498  * @cfg {string}  before - input group add on before
7499  * @cfg {string} after - input group add on after
7500  * @cfg {string} size - (lg|sm) or leave empty..
7501  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7502  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7503  * @cfg {Number} md colspan out of 12 for computer-sized screens
7504  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7505  * @cfg {string} value default value of the input
7506  * @cfg {Number} labelWidth set the width of label (0-12)
7507  * @cfg {String} labelAlign (top|left)
7508  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7509  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7510
7511  * @cfg {String} align (left|center|right) Default left
7512  * @cfg {Boolean} forceFeedback (true|false) Default false
7513  * 
7514  * 
7515  * 
7516  * 
7517  * @constructor
7518  * Create a new Input
7519  * @param {Object} config The config object
7520  */
7521
7522 Roo.bootstrap.Input = function(config){
7523     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7524    
7525         this.addEvents({
7526             /**
7527              * @event focus
7528              * Fires when this field receives input focus.
7529              * @param {Roo.form.Field} this
7530              */
7531             focus : true,
7532             /**
7533              * @event blur
7534              * Fires when this field loses input focus.
7535              * @param {Roo.form.Field} this
7536              */
7537             blur : true,
7538             /**
7539              * @event specialkey
7540              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7541              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7542              * @param {Roo.form.Field} this
7543              * @param {Roo.EventObject} e The event object
7544              */
7545             specialkey : true,
7546             /**
7547              * @event change
7548              * Fires just before the field blurs if the field value has changed.
7549              * @param {Roo.form.Field} this
7550              * @param {Mixed} newValue The new value
7551              * @param {Mixed} oldValue The original value
7552              */
7553             change : true,
7554             /**
7555              * @event invalid
7556              * Fires after the field has been marked as invalid.
7557              * @param {Roo.form.Field} this
7558              * @param {String} msg The validation message
7559              */
7560             invalid : true,
7561             /**
7562              * @event valid
7563              * Fires after the field has been validated with no errors.
7564              * @param {Roo.form.Field} this
7565              */
7566             valid : true,
7567              /**
7568              * @event keyup
7569              * Fires after the key up
7570              * @param {Roo.form.Field} this
7571              * @param {Roo.EventObject}  e The event Object
7572              */
7573             keyup : true
7574         });
7575 };
7576
7577 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7578      /**
7579      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7580       automatic validation (defaults to "keyup").
7581      */
7582     validationEvent : "keyup",
7583      /**
7584      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7585      */
7586     validateOnBlur : true,
7587     /**
7588      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7589      */
7590     validationDelay : 250,
7591      /**
7592      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7593      */
7594     focusClass : "x-form-focus",  // not needed???
7595     
7596        
7597     /**
7598      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7599      */
7600     invalidClass : "has-warning",
7601     
7602     /**
7603      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7604      */
7605     validClass : "has-success",
7606     
7607     /**
7608      * @cfg {Boolean} hasFeedback (true|false) default true
7609      */
7610     hasFeedback : true,
7611     
7612     /**
7613      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7614      */
7615     invalidFeedbackClass : "glyphicon-warning-sign",
7616     
7617     /**
7618      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7619      */
7620     validFeedbackClass : "glyphicon-ok",
7621     
7622     /**
7623      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7624      */
7625     selectOnFocus : false,
7626     
7627      /**
7628      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7629      */
7630     maskRe : null,
7631        /**
7632      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7633      */
7634     vtype : null,
7635     
7636       /**
7637      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7638      */
7639     disableKeyFilter : false,
7640     
7641        /**
7642      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7643      */
7644     disabled : false,
7645      /**
7646      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7647      */
7648     allowBlank : true,
7649     /**
7650      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7651      */
7652     blankText : "This field is required",
7653     
7654      /**
7655      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7656      */
7657     minLength : 0,
7658     /**
7659      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7660      */
7661     maxLength : Number.MAX_VALUE,
7662     /**
7663      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7664      */
7665     minLengthText : "The minimum length for this field is {0}",
7666     /**
7667      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7668      */
7669     maxLengthText : "The maximum length for this field is {0}",
7670   
7671     
7672     /**
7673      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7674      * If available, this function will be called only after the basic validators all return true, and will be passed the
7675      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7676      */
7677     validator : null,
7678     /**
7679      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7680      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7681      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7682      */
7683     regex : null,
7684     /**
7685      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7686      */
7687     regexText : "",
7688     
7689     autocomplete: false,
7690     
7691     
7692     fieldLabel : '',
7693     inputType : 'text',
7694     
7695     name : false,
7696     placeholder: false,
7697     before : false,
7698     after : false,
7699     size : false,
7700     hasFocus : false,
7701     preventMark: false,
7702     isFormField : true,
7703     value : '',
7704     labelWidth : 2,
7705     labelAlign : false,
7706     readOnly : false,
7707     align : false,
7708     formatedValue : false,
7709     forceFeedback : false,
7710     
7711     parentLabelAlign : function()
7712     {
7713         var parent = this;
7714         while (parent.parent()) {
7715             parent = parent.parent();
7716             if (typeof(parent.labelAlign) !='undefined') {
7717                 return parent.labelAlign;
7718             }
7719         }
7720         return 'left';
7721         
7722     },
7723     
7724     getAutoCreate : function(){
7725         
7726         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7727         
7728         var id = Roo.id();
7729         
7730         var cfg = {};
7731         
7732         if(this.inputType != 'hidden'){
7733             cfg.cls = 'form-group' //input-group
7734         }
7735         
7736         var input =  {
7737             tag: 'input',
7738             id : id,
7739             type : this.inputType,
7740             value : this.value,
7741             cls : 'form-control',
7742             placeholder : this.placeholder || '',
7743             autocomplete : this.autocomplete || 'new-password'
7744         };
7745         
7746         
7747         if(this.align){
7748             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7749         }
7750         
7751         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7752             input.maxLength = this.maxLength;
7753         }
7754         
7755         if (this.disabled) {
7756             input.disabled=true;
7757         }
7758         
7759         if (this.readOnly) {
7760             input.readonly=true;
7761         }
7762         
7763         if (this.name) {
7764             input.name = this.name;
7765         }
7766         if (this.size) {
7767             input.cls += ' input-' + this.size;
7768         }
7769         var settings=this;
7770         ['xs','sm','md','lg'].map(function(size){
7771             if (settings[size]) {
7772                 cfg.cls += ' col-' + size + '-' + settings[size];
7773             }
7774         });
7775         
7776         var inputblock = input;
7777         
7778         var feedback = {
7779             tag: 'span',
7780             cls: 'glyphicon form-control-feedback'
7781         };
7782             
7783         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7784             
7785             inputblock = {
7786                 cls : 'has-feedback',
7787                 cn :  [
7788                     input,
7789                     feedback
7790                 ] 
7791             };  
7792         }
7793         
7794         if (this.before || this.after) {
7795             
7796             inputblock = {
7797                 cls : 'input-group',
7798                 cn :  [] 
7799             };
7800             
7801             if (this.before && typeof(this.before) == 'string') {
7802                 
7803                 inputblock.cn.push({
7804                     tag :'span',
7805                     cls : 'roo-input-before input-group-addon',
7806                     html : this.before
7807                 });
7808             }
7809             if (this.before && typeof(this.before) == 'object') {
7810                 this.before = Roo.factory(this.before);
7811                 Roo.log(this.before);
7812                 inputblock.cn.push({
7813                     tag :'span',
7814                     cls : 'roo-input-before input-group-' +
7815                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7816                 });
7817             }
7818             
7819             inputblock.cn.push(input);
7820             
7821             if (this.after && typeof(this.after) == 'string') {
7822                 inputblock.cn.push({
7823                     tag :'span',
7824                     cls : 'roo-input-after input-group-addon',
7825                     html : this.after
7826                 });
7827             }
7828             if (this.after && typeof(this.after) == 'object') {
7829                 this.after = Roo.factory(this.after);
7830                 Roo.log(this.after);
7831                 inputblock.cn.push({
7832                     tag :'span',
7833                     cls : 'roo-input-after input-group-' +
7834                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7835                 });
7836             }
7837             
7838             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7839                 inputblock.cls += ' has-feedback';
7840                 inputblock.cn.push(feedback);
7841             }
7842         };
7843         
7844         if (align ==='left' && this.fieldLabel.length) {
7845                 Roo.log("left and has label");
7846                 cfg.cn = [
7847                     
7848                     {
7849                         tag: 'label',
7850                         'for' :  id,
7851                         cls : 'control-label col-sm-' + this.labelWidth,
7852                         html : this.fieldLabel
7853                         
7854                     },
7855                     {
7856                         cls : "col-sm-" + (12 - this.labelWidth), 
7857                         cn: [
7858                             inputblock
7859                         ]
7860                     }
7861                     
7862                 ];
7863         } else if ( this.fieldLabel.length) {
7864                 Roo.log(" label");
7865                  cfg.cn = [
7866                    
7867                     {
7868                         tag: 'label',
7869                         //cls : 'input-group-addon',
7870                         html : this.fieldLabel
7871                         
7872                     },
7873                     
7874                     inputblock
7875                     
7876                 ];
7877
7878         } else {
7879             
7880                 Roo.log(" no label && no align");
7881                 cfg.cn = [
7882                     
7883                         inputblock
7884                     
7885                 ];
7886                 
7887                 
7888         };
7889         Roo.log('input-parentType: ' + this.parentType);
7890         
7891         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7892            cfg.cls += ' navbar-form';
7893            Roo.log(cfg);
7894         }
7895         
7896         return cfg;
7897         
7898     },
7899     /**
7900      * return the real input element.
7901      */
7902     inputEl: function ()
7903     {
7904         return this.el.select('input.form-control',true).first();
7905     },
7906     
7907     tooltipEl : function()
7908     {
7909         return this.inputEl();
7910     },
7911     
7912     setDisabled : function(v)
7913     {
7914         var i  = this.inputEl().dom;
7915         if (!v) {
7916             i.removeAttribute('disabled');
7917             return;
7918             
7919         }
7920         i.setAttribute('disabled','true');
7921     },
7922     initEvents : function()
7923     {
7924           
7925         this.inputEl().on("keydown" , this.fireKey,  this);
7926         this.inputEl().on("focus", this.onFocus,  this);
7927         this.inputEl().on("blur", this.onBlur,  this);
7928         
7929         this.inputEl().relayEvent('keyup', this);
7930  
7931         // reference to original value for reset
7932         this.originalValue = this.getValue();
7933         //Roo.form.TextField.superclass.initEvents.call(this);
7934         if(this.validationEvent == 'keyup'){
7935             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7936             this.inputEl().on('keyup', this.filterValidation, this);
7937         }
7938         else if(this.validationEvent !== false){
7939             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7940         }
7941         
7942         if(this.selectOnFocus){
7943             this.on("focus", this.preFocus, this);
7944             
7945         }
7946         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7947             this.inputEl().on("keypress", this.filterKeys, this);
7948         }
7949        /* if(this.grow){
7950             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
7951             this.el.on("click", this.autoSize,  this);
7952         }
7953         */
7954         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7955             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7956         }
7957         
7958         if (typeof(this.before) == 'object') {
7959             this.before.render(this.el.select('.roo-input-before',true).first());
7960         }
7961         if (typeof(this.after) == 'object') {
7962             this.after.render(this.el.select('.roo-input-after',true).first());
7963         }
7964         
7965         
7966     },
7967     filterValidation : function(e){
7968         if(!e.isNavKeyPress()){
7969             this.validationTask.delay(this.validationDelay);
7970         }
7971     },
7972      /**
7973      * Validates the field value
7974      * @return {Boolean} True if the value is valid, else false
7975      */
7976     validate : function(){
7977         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7978         if(this.disabled || this.validateValue(this.getRawValue())){
7979             this.markValid();
7980             return true;
7981         }
7982         
7983         this.markInvalid();
7984         return false;
7985     },
7986     
7987     
7988     /**
7989      * Validates a value according to the field's validation rules and marks the field as invalid
7990      * if the validation fails
7991      * @param {Mixed} value The value to validate
7992      * @return {Boolean} True if the value is valid, else false
7993      */
7994     validateValue : function(value){
7995         if(value.length < 1)  { // if it's blank
7996             if(this.allowBlank){
7997                 return true;
7998             }
7999             return false;
8000         }
8001         
8002         if(value.length < this.minLength){
8003             return false;
8004         }
8005         if(value.length > this.maxLength){
8006             return false;
8007         }
8008         if(this.vtype){
8009             var vt = Roo.form.VTypes;
8010             if(!vt[this.vtype](value, this)){
8011                 return false;
8012             }
8013         }
8014         if(typeof this.validator == "function"){
8015             var msg = this.validator(value);
8016             if(msg !== true){
8017                 return false;
8018             }
8019         }
8020         
8021         if(this.regex && !this.regex.test(value)){
8022             return false;
8023         }
8024         
8025         return true;
8026     },
8027
8028     
8029     
8030      // private
8031     fireKey : function(e){
8032         //Roo.log('field ' + e.getKey());
8033         if(e.isNavKeyPress()){
8034             this.fireEvent("specialkey", this, e);
8035         }
8036     },
8037     focus : function (selectText){
8038         if(this.rendered){
8039             this.inputEl().focus();
8040             if(selectText === true){
8041                 this.inputEl().dom.select();
8042             }
8043         }
8044         return this;
8045     } ,
8046     
8047     onFocus : function(){
8048         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8049            // this.el.addClass(this.focusClass);
8050         }
8051         if(!this.hasFocus){
8052             this.hasFocus = true;
8053             this.startValue = this.getValue();
8054             this.fireEvent("focus", this);
8055         }
8056     },
8057     
8058     beforeBlur : Roo.emptyFn,
8059
8060     
8061     // private
8062     onBlur : function(){
8063         this.beforeBlur();
8064         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8065             //this.el.removeClass(this.focusClass);
8066         }
8067         this.hasFocus = false;
8068         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8069             this.validate();
8070         }
8071         var v = this.getValue();
8072         if(String(v) !== String(this.startValue)){
8073             this.fireEvent('change', this, v, this.startValue);
8074         }
8075         this.fireEvent("blur", this);
8076     },
8077     
8078     /**
8079      * Resets the current field value to the originally loaded value and clears any validation messages
8080      */
8081     reset : function(){
8082         this.setValue(this.originalValue);
8083         this.validate();
8084     },
8085      /**
8086      * Returns the name of the field
8087      * @return {Mixed} name The name field
8088      */
8089     getName: function(){
8090         return this.name;
8091     },
8092      /**
8093      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8094      * @return {Mixed} value The field value
8095      */
8096     getValue : function(){
8097         
8098         var v = this.inputEl().getValue();
8099         
8100         return v;
8101     },
8102     /**
8103      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8104      * @return {Mixed} value The field value
8105      */
8106     getRawValue : function(){
8107         var v = this.inputEl().getValue();
8108         
8109         return v;
8110     },
8111     
8112     /**
8113      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8114      * @param {Mixed} value The value to set
8115      */
8116     setRawValue : function(v){
8117         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8118     },
8119     
8120     selectText : function(start, end){
8121         var v = this.getRawValue();
8122         if(v.length > 0){
8123             start = start === undefined ? 0 : start;
8124             end = end === undefined ? v.length : end;
8125             var d = this.inputEl().dom;
8126             if(d.setSelectionRange){
8127                 d.setSelectionRange(start, end);
8128             }else if(d.createTextRange){
8129                 var range = d.createTextRange();
8130                 range.moveStart("character", start);
8131                 range.moveEnd("character", v.length-end);
8132                 range.select();
8133             }
8134         }
8135     },
8136     
8137     /**
8138      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8139      * @param {Mixed} value The value to set
8140      */
8141     setValue : function(v){
8142         this.value = v;
8143         if(this.rendered){
8144             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8145             this.validate();
8146         }
8147     },
8148     
8149     /*
8150     processValue : function(value){
8151         if(this.stripCharsRe){
8152             var newValue = value.replace(this.stripCharsRe, '');
8153             if(newValue !== value){
8154                 this.setRawValue(newValue);
8155                 return newValue;
8156             }
8157         }
8158         return value;
8159     },
8160   */
8161     preFocus : function(){
8162         
8163         if(this.selectOnFocus){
8164             this.inputEl().dom.select();
8165         }
8166     },
8167     filterKeys : function(e){
8168         var k = e.getKey();
8169         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8170             return;
8171         }
8172         var c = e.getCharCode(), cc = String.fromCharCode(c);
8173         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8174             return;
8175         }
8176         if(!this.maskRe.test(cc)){
8177             e.stopEvent();
8178         }
8179     },
8180      /**
8181      * Clear any invalid styles/messages for this field
8182      */
8183     clearInvalid : function(){
8184         
8185         if(!this.el || this.preventMark){ // not rendered
8186             return;
8187         }
8188         this.el.removeClass(this.invalidClass);
8189         
8190         this.fireEvent('valid', this);
8191     },
8192     
8193      /**
8194      * Mark this field as valid
8195      */
8196     markValid : function(){
8197         if(!this.el  || this.preventMark){ // not rendered
8198             return;
8199         }
8200         
8201         this.el.removeClass([this.invalidClass, this.validClass]);
8202         
8203         var feedback = this.el.select('.form-control-feedback', true).first();
8204             
8205         if(feedback){
8206             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8207         }
8208
8209         if(this.disabled || this.allowBlank){
8210             return;
8211         }
8212         
8213         this.el.addClass(this.validClass);
8214         
8215         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8216             
8217             var feedback = this.el.select('.form-control-feedback', true).first();
8218             
8219             if(feedback){
8220                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8221                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8222             }
8223             
8224         }
8225         
8226         this.fireEvent('valid', this);
8227     },
8228     
8229      /**
8230      * Mark this field as invalid
8231      * @param {String} msg The validation message
8232      */
8233     markInvalid : function(msg){
8234         if(!this.el  || this.preventMark){ // not rendered
8235             return;
8236         }
8237         
8238         this.el.removeClass([this.invalidClass, this.validClass]);
8239         
8240         var feedback = this.el.select('.form-control-feedback', true).first();
8241             
8242         if(feedback){
8243             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8244         }
8245
8246         if(this.disabled || this.allowBlank){
8247             return;
8248         }
8249         
8250         this.el.addClass(this.invalidClass);
8251         
8252         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8253             
8254             var feedback = this.el.select('.form-control-feedback', true).first();
8255             
8256             if(feedback){
8257                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8258                 
8259                 if(this.getValue().length || this.forceFeedback){
8260                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8261                 }
8262                 
8263             }
8264             
8265         }
8266         
8267         this.fireEvent('invalid', this, msg);
8268     },
8269     // private
8270     SafariOnKeyDown : function(event)
8271     {
8272         // this is a workaround for a password hang bug on chrome/ webkit.
8273         
8274         var isSelectAll = false;
8275         
8276         if(this.inputEl().dom.selectionEnd > 0){
8277             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8278         }
8279         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8280             event.preventDefault();
8281             this.setValue('');
8282             return;
8283         }
8284         
8285         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8286             
8287             event.preventDefault();
8288             // this is very hacky as keydown always get's upper case.
8289             //
8290             var cc = String.fromCharCode(event.getCharCode());
8291             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8292             
8293         }
8294     },
8295     adjustWidth : function(tag, w){
8296         tag = tag.toLowerCase();
8297         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8298             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8299                 if(tag == 'input'){
8300                     return w + 2;
8301                 }
8302                 if(tag == 'textarea'){
8303                     return w-2;
8304                 }
8305             }else if(Roo.isOpera){
8306                 if(tag == 'input'){
8307                     return w + 2;
8308                 }
8309                 if(tag == 'textarea'){
8310                     return w-2;
8311                 }
8312             }
8313         }
8314         return w;
8315     }
8316     
8317 });
8318
8319  
8320 /*
8321  * - LGPL
8322  *
8323  * Input
8324  * 
8325  */
8326
8327 /**
8328  * @class Roo.bootstrap.TextArea
8329  * @extends Roo.bootstrap.Input
8330  * Bootstrap TextArea class
8331  * @cfg {Number} cols Specifies the visible width of a text area
8332  * @cfg {Number} rows Specifies the visible number of lines in a text area
8333  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8334  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8335  * @cfg {string} html text
8336  * 
8337  * @constructor
8338  * Create a new TextArea
8339  * @param {Object} config The config object
8340  */
8341
8342 Roo.bootstrap.TextArea = function(config){
8343     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8344    
8345 };
8346
8347 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8348      
8349     cols : false,
8350     rows : 5,
8351     readOnly : false,
8352     warp : 'soft',
8353     resize : false,
8354     value: false,
8355     html: false,
8356     
8357     getAutoCreate : function(){
8358         
8359         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8360         
8361         var id = Roo.id();
8362         
8363         var cfg = {};
8364         
8365         var input =  {
8366             tag: 'textarea',
8367             id : id,
8368             warp : this.warp,
8369             rows : this.rows,
8370             value : this.value || '',
8371             html: this.html || '',
8372             cls : 'form-control',
8373             placeholder : this.placeholder || '' 
8374             
8375         };
8376         
8377         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8378             input.maxLength = this.maxLength;
8379         }
8380         
8381         if(this.resize){
8382             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8383         }
8384         
8385         if(this.cols){
8386             input.cols = this.cols;
8387         }
8388         
8389         if (this.readOnly) {
8390             input.readonly = true;
8391         }
8392         
8393         if (this.name) {
8394             input.name = this.name;
8395         }
8396         
8397         if (this.size) {
8398             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8399         }
8400         
8401         var settings=this;
8402         ['xs','sm','md','lg'].map(function(size){
8403             if (settings[size]) {
8404                 cfg.cls += ' col-' + size + '-' + settings[size];
8405             }
8406         });
8407         
8408         var inputblock = input;
8409         
8410         if(this.hasFeedback && !this.allowBlank){
8411             
8412             var feedback = {
8413                 tag: 'span',
8414                 cls: 'glyphicon form-control-feedback'
8415             };
8416
8417             inputblock = {
8418                 cls : 'has-feedback',
8419                 cn :  [
8420                     input,
8421                     feedback
8422                 ] 
8423             };  
8424         }
8425         
8426         
8427         if (this.before || this.after) {
8428             
8429             inputblock = {
8430                 cls : 'input-group',
8431                 cn :  [] 
8432             };
8433             if (this.before) {
8434                 inputblock.cn.push({
8435                     tag :'span',
8436                     cls : 'input-group-addon',
8437                     html : this.before
8438                 });
8439             }
8440             
8441             inputblock.cn.push(input);
8442             
8443             if(this.hasFeedback && !this.allowBlank){
8444                 inputblock.cls += ' has-feedback';
8445                 inputblock.cn.push(feedback);
8446             }
8447             
8448             if (this.after) {
8449                 inputblock.cn.push({
8450                     tag :'span',
8451                     cls : 'input-group-addon',
8452                     html : this.after
8453                 });
8454             }
8455             
8456         }
8457         
8458         if (align ==='left' && this.fieldLabel.length) {
8459                 Roo.log("left and has label");
8460                 cfg.cn = [
8461                     
8462                     {
8463                         tag: 'label',
8464                         'for' :  id,
8465                         cls : 'control-label col-sm-' + this.labelWidth,
8466                         html : this.fieldLabel
8467                         
8468                     },
8469                     {
8470                         cls : "col-sm-" + (12 - this.labelWidth), 
8471                         cn: [
8472                             inputblock
8473                         ]
8474                     }
8475                     
8476                 ];
8477         } else if ( this.fieldLabel.length) {
8478                 Roo.log(" label");
8479                  cfg.cn = [
8480                    
8481                     {
8482                         tag: 'label',
8483                         //cls : 'input-group-addon',
8484                         html : this.fieldLabel
8485                         
8486                     },
8487                     
8488                     inputblock
8489                     
8490                 ];
8491
8492         } else {
8493             
8494                    Roo.log(" no label && no align");
8495                 cfg.cn = [
8496                     
8497                         inputblock
8498                     
8499                 ];
8500                 
8501                 
8502         }
8503         
8504         if (this.disabled) {
8505             input.disabled=true;
8506         }
8507         
8508         return cfg;
8509         
8510     },
8511     /**
8512      * return the real textarea element.
8513      */
8514     inputEl: function ()
8515     {
8516         return this.el.select('textarea.form-control',true).first();
8517     }
8518 });
8519
8520  
8521 /*
8522  * - LGPL
8523  *
8524  * trigger field - base class for combo..
8525  * 
8526  */
8527  
8528 /**
8529  * @class Roo.bootstrap.TriggerField
8530  * @extends Roo.bootstrap.Input
8531  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8532  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8533  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8534  * for which you can provide a custom implementation.  For example:
8535  * <pre><code>
8536 var trigger = new Roo.bootstrap.TriggerField();
8537 trigger.onTriggerClick = myTriggerFn;
8538 trigger.applyTo('my-field');
8539 </code></pre>
8540  *
8541  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8542  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8543  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8544  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8545  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8546
8547  * @constructor
8548  * Create a new TriggerField.
8549  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8550  * to the base TextField)
8551  */
8552 Roo.bootstrap.TriggerField = function(config){
8553     this.mimicing = false;
8554     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8555 };
8556
8557 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8558     /**
8559      * @cfg {String} triggerClass A CSS class to apply to the trigger
8560      */
8561      /**
8562      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8563      */
8564     hideTrigger:false,
8565
8566     /**
8567      * @cfg {Boolean} removable (true|false) special filter default false
8568      */
8569     removable : false,
8570     
8571     /** @cfg {Boolean} grow @hide */
8572     /** @cfg {Number} growMin @hide */
8573     /** @cfg {Number} growMax @hide */
8574
8575     /**
8576      * @hide 
8577      * @method
8578      */
8579     autoSize: Roo.emptyFn,
8580     // private
8581     monitorTab : true,
8582     // private
8583     deferHeight : true,
8584
8585     
8586     actionMode : 'wrap',
8587     
8588     caret : false,
8589     
8590     
8591     getAutoCreate : function(){
8592        
8593         var align = this.labelAlign || this.parentLabelAlign();
8594         
8595         var id = Roo.id();
8596         
8597         var cfg = {
8598             cls: 'form-group' //input-group
8599         };
8600         
8601         
8602         var input =  {
8603             tag: 'input',
8604             id : id,
8605             type : this.inputType,
8606             cls : 'form-control',
8607             autocomplete: 'new-password',
8608             placeholder : this.placeholder || '' 
8609             
8610         };
8611         if (this.name) {
8612             input.name = this.name;
8613         }
8614         if (this.size) {
8615             input.cls += ' input-' + this.size;
8616         }
8617         
8618         if (this.disabled) {
8619             input.disabled=true;
8620         }
8621         
8622         var inputblock = input;
8623         
8624         if(this.hasFeedback && !this.allowBlank){
8625             
8626             var feedback = {
8627                 tag: 'span',
8628                 cls: 'glyphicon form-control-feedback'
8629             };
8630             
8631             if(this.removable && !this.editable && !this.tickable){
8632                 inputblock = {
8633                     cls : 'has-feedback',
8634                     cn :  [
8635                         inputblock,
8636                         {
8637                             tag: 'button',
8638                             html : 'x',
8639                             cls : 'roo-combo-removable-btn close'
8640                         },
8641                         feedback
8642                     ] 
8643                 };
8644             } else {
8645                 inputblock = {
8646                     cls : 'has-feedback',
8647                     cn :  [
8648                         inputblock,
8649                         feedback
8650                     ] 
8651                 };
8652             }
8653
8654         } else {
8655             if(this.removable && !this.editable && !this.tickable){
8656                 inputblock = {
8657                     cls : 'roo-removable',
8658                     cn :  [
8659                         inputblock,
8660                         {
8661                             tag: 'button',
8662                             html : 'x',
8663                             cls : 'roo-combo-removable-btn close'
8664                         }
8665                     ] 
8666                 };
8667             }
8668         }
8669         
8670         if (this.before || this.after) {
8671             
8672             inputblock = {
8673                 cls : 'input-group',
8674                 cn :  [] 
8675             };
8676             if (this.before) {
8677                 inputblock.cn.push({
8678                     tag :'span',
8679                     cls : 'input-group-addon',
8680                     html : this.before
8681                 });
8682             }
8683             
8684             inputblock.cn.push(input);
8685             
8686             if(this.hasFeedback && !this.allowBlank){
8687                 inputblock.cls += ' has-feedback';
8688                 inputblock.cn.push(feedback);
8689             }
8690             
8691             if (this.after) {
8692                 inputblock.cn.push({
8693                     tag :'span',
8694                     cls : 'input-group-addon',
8695                     html : this.after
8696                 });
8697             }
8698             
8699         };
8700         
8701         var box = {
8702             tag: 'div',
8703             cn: [
8704                 {
8705                     tag: 'input',
8706                     type : 'hidden',
8707                     cls: 'form-hidden-field'
8708                 },
8709                 inputblock
8710             ]
8711             
8712         };
8713         
8714         if(this.multiple){
8715             Roo.log('multiple');
8716             
8717             box = {
8718                 tag: 'div',
8719                 cn: [
8720                     {
8721                         tag: 'input',
8722                         type : 'hidden',
8723                         cls: 'form-hidden-field'
8724                     },
8725                     {
8726                         tag: 'ul',
8727                         cls: 'select2-choices',
8728                         cn:[
8729                             {
8730                                 tag: 'li',
8731                                 cls: 'select2-search-field',
8732                                 cn: [
8733
8734                                     inputblock
8735                                 ]
8736                             }
8737                         ]
8738                     }
8739                 ]
8740             }
8741         };
8742         
8743         var combobox = {
8744             cls: 'select2-container input-group',
8745             cn: [
8746                 box
8747 //                {
8748 //                    tag: 'ul',
8749 //                    cls: 'typeahead typeahead-long dropdown-menu',
8750 //                    style: 'display:none'
8751 //                }
8752             ]
8753         };
8754         
8755         if(!this.multiple && this.showToggleBtn){
8756             
8757             var caret = {
8758                         tag: 'span',
8759                         cls: 'caret'
8760              };
8761             if (this.caret != false) {
8762                 caret = {
8763                      tag: 'i',
8764                      cls: 'fa fa-' + this.caret
8765                 };
8766                 
8767             }
8768             
8769             combobox.cn.push({
8770                 tag :'span',
8771                 cls : 'input-group-addon btn dropdown-toggle',
8772                 cn : [
8773                     caret,
8774                     {
8775                         tag: 'span',
8776                         cls: 'combobox-clear',
8777                         cn  : [
8778                             {
8779                                 tag : 'i',
8780                                 cls: 'icon-remove'
8781                             }
8782                         ]
8783                     }
8784                 ]
8785
8786             })
8787         }
8788         
8789         if(this.multiple){
8790             combobox.cls += ' select2-container-multi';
8791         }
8792         
8793         if (align ==='left' && this.fieldLabel.length) {
8794             
8795                 Roo.log("left and has label");
8796                 cfg.cn = [
8797                     
8798                     {
8799                         tag: 'label',
8800                         'for' :  id,
8801                         cls : 'control-label col-sm-' + this.labelWidth,
8802                         html : this.fieldLabel
8803                         
8804                     },
8805                     {
8806                         cls : "col-sm-" + (12 - this.labelWidth), 
8807                         cn: [
8808                             combobox
8809                         ]
8810                     }
8811                     
8812                 ];
8813         } else if ( this.fieldLabel.length) {
8814                 Roo.log(" label");
8815                  cfg.cn = [
8816                    
8817                     {
8818                         tag: 'label',
8819                         //cls : 'input-group-addon',
8820                         html : this.fieldLabel
8821                         
8822                     },
8823                     
8824                     combobox
8825                     
8826                 ];
8827
8828         } else {
8829             
8830                 Roo.log(" no label && no align");
8831                 cfg = combobox
8832                      
8833                 
8834         }
8835          
8836         var settings=this;
8837         ['xs','sm','md','lg'].map(function(size){
8838             if (settings[size]) {
8839                 cfg.cls += ' col-' + size + '-' + settings[size];
8840             }
8841         });
8842         Roo.log(cfg);
8843         return cfg;
8844         
8845     },
8846     
8847     
8848     
8849     // private
8850     onResize : function(w, h){
8851 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8852 //        if(typeof w == 'number'){
8853 //            var x = w - this.trigger.getWidth();
8854 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8855 //            this.trigger.setStyle('left', x+'px');
8856 //        }
8857     },
8858
8859     // private
8860     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8861
8862     // private
8863     getResizeEl : function(){
8864         return this.inputEl();
8865     },
8866
8867     // private
8868     getPositionEl : function(){
8869         return this.inputEl();
8870     },
8871
8872     // private
8873     alignErrorIcon : function(){
8874         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8875     },
8876
8877     // private
8878     initEvents : function(){
8879         
8880         this.createList();
8881         
8882         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8883         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8884         if(!this.multiple && this.showToggleBtn){
8885             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8886             if(this.hideTrigger){
8887                 this.trigger.setDisplayed(false);
8888             }
8889             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8890         }
8891         
8892         if(this.multiple){
8893             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8894         }
8895         
8896         if(this.removable && !this.editable && !this.tickable){
8897             var close = this.closeTriggerEl();
8898             
8899             if(close){
8900                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8901                 close.on('click', this.removeBtnClick, this, close);
8902             }
8903         }
8904         
8905         //this.trigger.addClassOnOver('x-form-trigger-over');
8906         //this.trigger.addClassOnClick('x-form-trigger-click');
8907         
8908         //if(!this.width){
8909         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8910         //}
8911     },
8912     
8913     closeTriggerEl : function()
8914     {
8915         var close = this.el.select('.roo-combo-removable-btn', true).first();
8916         return close ? close : false;
8917     },
8918     
8919     removeBtnClick : function(e, h, el)
8920     {
8921         e.preventDefault();
8922         
8923         if(this.fireEvent("remove", this) !== false){
8924             this.reset();
8925         }
8926     },
8927     
8928     createList : function()
8929     {
8930         this.list = Roo.get(document.body).createChild({
8931             tag: 'ul',
8932             cls: 'typeahead typeahead-long dropdown-menu',
8933             style: 'display:none'
8934         });
8935         
8936         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8937         
8938     },
8939
8940     // private
8941     initTrigger : function(){
8942        
8943     },
8944
8945     // private
8946     onDestroy : function(){
8947         if(this.trigger){
8948             this.trigger.removeAllListeners();
8949           //  this.trigger.remove();
8950         }
8951         //if(this.wrap){
8952         //    this.wrap.remove();
8953         //}
8954         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8955     },
8956
8957     // private
8958     onFocus : function(){
8959         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8960         /*
8961         if(!this.mimicing){
8962             this.wrap.addClass('x-trigger-wrap-focus');
8963             this.mimicing = true;
8964             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8965             if(this.monitorTab){
8966                 this.el.on("keydown", this.checkTab, this);
8967             }
8968         }
8969         */
8970     },
8971
8972     // private
8973     checkTab : function(e){
8974         if(e.getKey() == e.TAB){
8975             this.triggerBlur();
8976         }
8977     },
8978
8979     // private
8980     onBlur : function(){
8981         // do nothing
8982     },
8983
8984     // private
8985     mimicBlur : function(e, t){
8986         /*
8987         if(!this.wrap.contains(t) && this.validateBlur()){
8988             this.triggerBlur();
8989         }
8990         */
8991     },
8992
8993     // private
8994     triggerBlur : function(){
8995         this.mimicing = false;
8996         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8997         if(this.monitorTab){
8998             this.el.un("keydown", this.checkTab, this);
8999         }
9000         //this.wrap.removeClass('x-trigger-wrap-focus');
9001         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9002     },
9003
9004     // private
9005     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9006     validateBlur : function(e, t){
9007         return true;
9008     },
9009
9010     // private
9011     onDisable : function(){
9012         this.inputEl().dom.disabled = true;
9013         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9014         //if(this.wrap){
9015         //    this.wrap.addClass('x-item-disabled');
9016         //}
9017     },
9018
9019     // private
9020     onEnable : function(){
9021         this.inputEl().dom.disabled = false;
9022         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9023         //if(this.wrap){
9024         //    this.el.removeClass('x-item-disabled');
9025         //}
9026     },
9027
9028     // private
9029     onShow : function(){
9030         var ae = this.getActionEl();
9031         
9032         if(ae){
9033             ae.dom.style.display = '';
9034             ae.dom.style.visibility = 'visible';
9035         }
9036     },
9037
9038     // private
9039     
9040     onHide : function(){
9041         var ae = this.getActionEl();
9042         ae.dom.style.display = 'none';
9043     },
9044
9045     /**
9046      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9047      * by an implementing function.
9048      * @method
9049      * @param {EventObject} e
9050      */
9051     onTriggerClick : Roo.emptyFn
9052 });
9053  /*
9054  * Based on:
9055  * Ext JS Library 1.1.1
9056  * Copyright(c) 2006-2007, Ext JS, LLC.
9057  *
9058  * Originally Released Under LGPL - original licence link has changed is not relivant.
9059  *
9060  * Fork - LGPL
9061  * <script type="text/javascript">
9062  */
9063
9064
9065 /**
9066  * @class Roo.data.SortTypes
9067  * @singleton
9068  * Defines the default sorting (casting?) comparison functions used when sorting data.
9069  */
9070 Roo.data.SortTypes = {
9071     /**
9072      * Default sort that does nothing
9073      * @param {Mixed} s The value being converted
9074      * @return {Mixed} The comparison value
9075      */
9076     none : function(s){
9077         return s;
9078     },
9079     
9080     /**
9081      * The regular expression used to strip tags
9082      * @type {RegExp}
9083      * @property
9084      */
9085     stripTagsRE : /<\/?[^>]+>/gi,
9086     
9087     /**
9088      * Strips all HTML tags to sort on text only
9089      * @param {Mixed} s The value being converted
9090      * @return {String} The comparison value
9091      */
9092     asText : function(s){
9093         return String(s).replace(this.stripTagsRE, "");
9094     },
9095     
9096     /**
9097      * Strips all HTML tags to sort on text only - Case insensitive
9098      * @param {Mixed} s The value being converted
9099      * @return {String} The comparison value
9100      */
9101     asUCText : function(s){
9102         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9103     },
9104     
9105     /**
9106      * Case insensitive string
9107      * @param {Mixed} s The value being converted
9108      * @return {String} The comparison value
9109      */
9110     asUCString : function(s) {
9111         return String(s).toUpperCase();
9112     },
9113     
9114     /**
9115      * Date sorting
9116      * @param {Mixed} s The value being converted
9117      * @return {Number} The comparison value
9118      */
9119     asDate : function(s) {
9120         if(!s){
9121             return 0;
9122         }
9123         if(s instanceof Date){
9124             return s.getTime();
9125         }
9126         return Date.parse(String(s));
9127     },
9128     
9129     /**
9130      * Float sorting
9131      * @param {Mixed} s The value being converted
9132      * @return {Float} The comparison value
9133      */
9134     asFloat : function(s) {
9135         var val = parseFloat(String(s).replace(/,/g, ""));
9136         if(isNaN(val)) val = 0;
9137         return val;
9138     },
9139     
9140     /**
9141      * Integer sorting
9142      * @param {Mixed} s The value being converted
9143      * @return {Number} The comparison value
9144      */
9145     asInt : function(s) {
9146         var val = parseInt(String(s).replace(/,/g, ""));
9147         if(isNaN(val)) val = 0;
9148         return val;
9149     }
9150 };/*
9151  * Based on:
9152  * Ext JS Library 1.1.1
9153  * Copyright(c) 2006-2007, Ext JS, LLC.
9154  *
9155  * Originally Released Under LGPL - original licence link has changed is not relivant.
9156  *
9157  * Fork - LGPL
9158  * <script type="text/javascript">
9159  */
9160
9161 /**
9162 * @class Roo.data.Record
9163  * Instances of this class encapsulate both record <em>definition</em> information, and record
9164  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9165  * to access Records cached in an {@link Roo.data.Store} object.<br>
9166  * <p>
9167  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9168  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9169  * objects.<br>
9170  * <p>
9171  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9172  * @constructor
9173  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9174  * {@link #create}. The parameters are the same.
9175  * @param {Array} data An associative Array of data values keyed by the field name.
9176  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9177  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9178  * not specified an integer id is generated.
9179  */
9180 Roo.data.Record = function(data, id){
9181     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9182     this.data = data;
9183 };
9184
9185 /**
9186  * Generate a constructor for a specific record layout.
9187  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9188  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9189  * Each field definition object may contain the following properties: <ul>
9190  * <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,
9191  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9192  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9193  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9194  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9195  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9196  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9197  * this may be omitted.</p></li>
9198  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9199  * <ul><li>auto (Default, implies no conversion)</li>
9200  * <li>string</li>
9201  * <li>int</li>
9202  * <li>float</li>
9203  * <li>boolean</li>
9204  * <li>date</li></ul></p></li>
9205  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9206  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9207  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9208  * by the Reader into an object that will be stored in the Record. It is passed the
9209  * following parameters:<ul>
9210  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9211  * </ul></p></li>
9212  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9213  * </ul>
9214  * <br>usage:<br><pre><code>
9215 var TopicRecord = Roo.data.Record.create(
9216     {name: 'title', mapping: 'topic_title'},
9217     {name: 'author', mapping: 'username'},
9218     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9219     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9220     {name: 'lastPoster', mapping: 'user2'},
9221     {name: 'excerpt', mapping: 'post_text'}
9222 );
9223
9224 var myNewRecord = new TopicRecord({
9225     title: 'Do my job please',
9226     author: 'noobie',
9227     totalPosts: 1,
9228     lastPost: new Date(),
9229     lastPoster: 'Animal',
9230     excerpt: 'No way dude!'
9231 });
9232 myStore.add(myNewRecord);
9233 </code></pre>
9234  * @method create
9235  * @static
9236  */
9237 Roo.data.Record.create = function(o){
9238     var f = function(){
9239         f.superclass.constructor.apply(this, arguments);
9240     };
9241     Roo.extend(f, Roo.data.Record);
9242     var p = f.prototype;
9243     p.fields = new Roo.util.MixedCollection(false, function(field){
9244         return field.name;
9245     });
9246     for(var i = 0, len = o.length; i < len; i++){
9247         p.fields.add(new Roo.data.Field(o[i]));
9248     }
9249     f.getField = function(name){
9250         return p.fields.get(name);  
9251     };
9252     return f;
9253 };
9254
9255 Roo.data.Record.AUTO_ID = 1000;
9256 Roo.data.Record.EDIT = 'edit';
9257 Roo.data.Record.REJECT = 'reject';
9258 Roo.data.Record.COMMIT = 'commit';
9259
9260 Roo.data.Record.prototype = {
9261     /**
9262      * Readonly flag - true if this record has been modified.
9263      * @type Boolean
9264      */
9265     dirty : false,
9266     editing : false,
9267     error: null,
9268     modified: null,
9269
9270     // private
9271     join : function(store){
9272         this.store = store;
9273     },
9274
9275     /**
9276      * Set the named field to the specified value.
9277      * @param {String} name The name of the field to set.
9278      * @param {Object} value The value to set the field to.
9279      */
9280     set : function(name, value){
9281         if(this.data[name] == value){
9282             return;
9283         }
9284         this.dirty = true;
9285         if(!this.modified){
9286             this.modified = {};
9287         }
9288         if(typeof this.modified[name] == 'undefined'){
9289             this.modified[name] = this.data[name];
9290         }
9291         this.data[name] = value;
9292         if(!this.editing && this.store){
9293             this.store.afterEdit(this);
9294         }       
9295     },
9296
9297     /**
9298      * Get the value of the named field.
9299      * @param {String} name The name of the field to get the value of.
9300      * @return {Object} The value of the field.
9301      */
9302     get : function(name){
9303         return this.data[name]; 
9304     },
9305
9306     // private
9307     beginEdit : function(){
9308         this.editing = true;
9309         this.modified = {}; 
9310     },
9311
9312     // private
9313     cancelEdit : function(){
9314         this.editing = false;
9315         delete this.modified;
9316     },
9317
9318     // private
9319     endEdit : function(){
9320         this.editing = false;
9321         if(this.dirty && this.store){
9322             this.store.afterEdit(this);
9323         }
9324     },
9325
9326     /**
9327      * Usually called by the {@link Roo.data.Store} which owns the Record.
9328      * Rejects all changes made to the Record since either creation, or the last commit operation.
9329      * Modified fields are reverted to their original values.
9330      * <p>
9331      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9332      * of reject operations.
9333      */
9334     reject : function(){
9335         var m = this.modified;
9336         for(var n in m){
9337             if(typeof m[n] != "function"){
9338                 this.data[n] = m[n];
9339             }
9340         }
9341         this.dirty = false;
9342         delete this.modified;
9343         this.editing = false;
9344         if(this.store){
9345             this.store.afterReject(this);
9346         }
9347     },
9348
9349     /**
9350      * Usually called by the {@link Roo.data.Store} which owns the Record.
9351      * Commits all changes made to the Record since either creation, or the last commit operation.
9352      * <p>
9353      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9354      * of commit operations.
9355      */
9356     commit : function(){
9357         this.dirty = false;
9358         delete this.modified;
9359         this.editing = false;
9360         if(this.store){
9361             this.store.afterCommit(this);
9362         }
9363     },
9364
9365     // private
9366     hasError : function(){
9367         return this.error != null;
9368     },
9369
9370     // private
9371     clearError : function(){
9372         this.error = null;
9373     },
9374
9375     /**
9376      * Creates a copy of this record.
9377      * @param {String} id (optional) A new record id if you don't want to use this record's id
9378      * @return {Record}
9379      */
9380     copy : function(newId) {
9381         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9382     }
9383 };/*
9384  * Based on:
9385  * Ext JS Library 1.1.1
9386  * Copyright(c) 2006-2007, Ext JS, LLC.
9387  *
9388  * Originally Released Under LGPL - original licence link has changed is not relivant.
9389  *
9390  * Fork - LGPL
9391  * <script type="text/javascript">
9392  */
9393
9394
9395
9396 /**
9397  * @class Roo.data.Store
9398  * @extends Roo.util.Observable
9399  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9400  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9401  * <p>
9402  * 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
9403  * has no knowledge of the format of the data returned by the Proxy.<br>
9404  * <p>
9405  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9406  * instances from the data object. These records are cached and made available through accessor functions.
9407  * @constructor
9408  * Creates a new Store.
9409  * @param {Object} config A config object containing the objects needed for the Store to access data,
9410  * and read the data into Records.
9411  */
9412 Roo.data.Store = function(config){
9413     this.data = new Roo.util.MixedCollection(false);
9414     this.data.getKey = function(o){
9415         return o.id;
9416     };
9417     this.baseParams = {};
9418     // private
9419     this.paramNames = {
9420         "start" : "start",
9421         "limit" : "limit",
9422         "sort" : "sort",
9423         "dir" : "dir",
9424         "multisort" : "_multisort"
9425     };
9426
9427     if(config && config.data){
9428         this.inlineData = config.data;
9429         delete config.data;
9430     }
9431
9432     Roo.apply(this, config);
9433     
9434     if(this.reader){ // reader passed
9435         this.reader = Roo.factory(this.reader, Roo.data);
9436         this.reader.xmodule = this.xmodule || false;
9437         if(!this.recordType){
9438             this.recordType = this.reader.recordType;
9439         }
9440         if(this.reader.onMetaChange){
9441             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9442         }
9443     }
9444
9445     if(this.recordType){
9446         this.fields = this.recordType.prototype.fields;
9447     }
9448     this.modified = [];
9449
9450     this.addEvents({
9451         /**
9452          * @event datachanged
9453          * Fires when the data cache has changed, and a widget which is using this Store
9454          * as a Record cache should refresh its view.
9455          * @param {Store} this
9456          */
9457         datachanged : true,
9458         /**
9459          * @event metachange
9460          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9461          * @param {Store} this
9462          * @param {Object} meta The JSON metadata
9463          */
9464         metachange : true,
9465         /**
9466          * @event add
9467          * Fires when Records have been added to the Store
9468          * @param {Store} this
9469          * @param {Roo.data.Record[]} records The array of Records added
9470          * @param {Number} index The index at which the record(s) were added
9471          */
9472         add : true,
9473         /**
9474          * @event remove
9475          * Fires when a Record has been removed from the Store
9476          * @param {Store} this
9477          * @param {Roo.data.Record} record The Record that was removed
9478          * @param {Number} index The index at which the record was removed
9479          */
9480         remove : true,
9481         /**
9482          * @event update
9483          * Fires when a Record has been updated
9484          * @param {Store} this
9485          * @param {Roo.data.Record} record The Record that was updated
9486          * @param {String} operation The update operation being performed.  Value may be one of:
9487          * <pre><code>
9488  Roo.data.Record.EDIT
9489  Roo.data.Record.REJECT
9490  Roo.data.Record.COMMIT
9491          * </code></pre>
9492          */
9493         update : true,
9494         /**
9495          * @event clear
9496          * Fires when the data cache has been cleared.
9497          * @param {Store} this
9498          */
9499         clear : true,
9500         /**
9501          * @event beforeload
9502          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9503          * the load action will be canceled.
9504          * @param {Store} this
9505          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9506          */
9507         beforeload : true,
9508         /**
9509          * @event beforeloadadd
9510          * Fires after a new set of Records has been loaded.
9511          * @param {Store} this
9512          * @param {Roo.data.Record[]} records The Records that were loaded
9513          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9514          */
9515         beforeloadadd : true,
9516         /**
9517          * @event load
9518          * Fires after a new set of Records has been loaded, before they are added to the store.
9519          * @param {Store} this
9520          * @param {Roo.data.Record[]} records The Records that were loaded
9521          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9522          * @params {Object} return from reader
9523          */
9524         load : true,
9525         /**
9526          * @event loadexception
9527          * Fires if an exception occurs in the Proxy during loading.
9528          * Called with the signature of the Proxy's "loadexception" event.
9529          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9530          * 
9531          * @param {Proxy} 
9532          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9533          * @param {Object} load options 
9534          * @param {Object} jsonData from your request (normally this contains the Exception)
9535          */
9536         loadexception : true
9537     });
9538     
9539     if(this.proxy){
9540         this.proxy = Roo.factory(this.proxy, Roo.data);
9541         this.proxy.xmodule = this.xmodule || false;
9542         this.relayEvents(this.proxy,  ["loadexception"]);
9543     }
9544     this.sortToggle = {};
9545     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9546
9547     Roo.data.Store.superclass.constructor.call(this);
9548
9549     if(this.inlineData){
9550         this.loadData(this.inlineData);
9551         delete this.inlineData;
9552     }
9553 };
9554
9555 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9556      /**
9557     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9558     * without a remote query - used by combo/forms at present.
9559     */
9560     
9561     /**
9562     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9563     */
9564     /**
9565     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9566     */
9567     /**
9568     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9569     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9570     */
9571     /**
9572     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9573     * on any HTTP request
9574     */
9575     /**
9576     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9577     */
9578     /**
9579     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9580     */
9581     multiSort: false,
9582     /**
9583     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9584     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9585     */
9586     remoteSort : false,
9587
9588     /**
9589     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9590      * loaded or when a record is removed. (defaults to false).
9591     */
9592     pruneModifiedRecords : false,
9593
9594     // private
9595     lastOptions : null,
9596
9597     /**
9598      * Add Records to the Store and fires the add event.
9599      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9600      */
9601     add : function(records){
9602         records = [].concat(records);
9603         for(var i = 0, len = records.length; i < len; i++){
9604             records[i].join(this);
9605         }
9606         var index = this.data.length;
9607         this.data.addAll(records);
9608         this.fireEvent("add", this, records, index);
9609     },
9610
9611     /**
9612      * Remove a Record from the Store and fires the remove event.
9613      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9614      */
9615     remove : function(record){
9616         var index = this.data.indexOf(record);
9617         this.data.removeAt(index);
9618         if(this.pruneModifiedRecords){
9619             this.modified.remove(record);
9620         }
9621         this.fireEvent("remove", this, record, index);
9622     },
9623
9624     /**
9625      * Remove all Records from the Store and fires the clear event.
9626      */
9627     removeAll : function(){
9628         this.data.clear();
9629         if(this.pruneModifiedRecords){
9630             this.modified = [];
9631         }
9632         this.fireEvent("clear", this);
9633     },
9634
9635     /**
9636      * Inserts Records to the Store at the given index and fires the add event.
9637      * @param {Number} index The start index at which to insert the passed Records.
9638      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9639      */
9640     insert : function(index, records){
9641         records = [].concat(records);
9642         for(var i = 0, len = records.length; i < len; i++){
9643             this.data.insert(index, records[i]);
9644             records[i].join(this);
9645         }
9646         this.fireEvent("add", this, records, index);
9647     },
9648
9649     /**
9650      * Get the index within the cache of the passed Record.
9651      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9652      * @return {Number} The index of the passed Record. Returns -1 if not found.
9653      */
9654     indexOf : function(record){
9655         return this.data.indexOf(record);
9656     },
9657
9658     /**
9659      * Get the index within the cache of the Record with the passed id.
9660      * @param {String} id The id of the Record to find.
9661      * @return {Number} The index of the Record. Returns -1 if not found.
9662      */
9663     indexOfId : function(id){
9664         return this.data.indexOfKey(id);
9665     },
9666
9667     /**
9668      * Get the Record with the specified id.
9669      * @param {String} id The id of the Record to find.
9670      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9671      */
9672     getById : function(id){
9673         return this.data.key(id);
9674     },
9675
9676     /**
9677      * Get the Record at the specified index.
9678      * @param {Number} index The index of the Record to find.
9679      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9680      */
9681     getAt : function(index){
9682         return this.data.itemAt(index);
9683     },
9684
9685     /**
9686      * Returns a range of Records between specified indices.
9687      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9688      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9689      * @return {Roo.data.Record[]} An array of Records
9690      */
9691     getRange : function(start, end){
9692         return this.data.getRange(start, end);
9693     },
9694
9695     // private
9696     storeOptions : function(o){
9697         o = Roo.apply({}, o);
9698         delete o.callback;
9699         delete o.scope;
9700         this.lastOptions = o;
9701     },
9702
9703     /**
9704      * Loads the Record cache from the configured Proxy using the configured Reader.
9705      * <p>
9706      * If using remote paging, then the first load call must specify the <em>start</em>
9707      * and <em>limit</em> properties in the options.params property to establish the initial
9708      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9709      * <p>
9710      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9711      * and this call will return before the new data has been loaded. Perform any post-processing
9712      * in a callback function, or in a "load" event handler.</strong>
9713      * <p>
9714      * @param {Object} options An object containing properties which control loading options:<ul>
9715      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9716      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9717      * passed the following arguments:<ul>
9718      * <li>r : Roo.data.Record[]</li>
9719      * <li>options: Options object from the load call</li>
9720      * <li>success: Boolean success indicator</li></ul></li>
9721      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9722      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9723      * </ul>
9724      */
9725     load : function(options){
9726         options = options || {};
9727         if(this.fireEvent("beforeload", this, options) !== false){
9728             this.storeOptions(options);
9729             var p = Roo.apply(options.params || {}, this.baseParams);
9730             // if meta was not loaded from remote source.. try requesting it.
9731             if (!this.reader.metaFromRemote) {
9732                 p._requestMeta = 1;
9733             }
9734             if(this.sortInfo && this.remoteSort){
9735                 var pn = this.paramNames;
9736                 p[pn["sort"]] = this.sortInfo.field;
9737                 p[pn["dir"]] = this.sortInfo.direction;
9738             }
9739             if (this.multiSort) {
9740                 var pn = this.paramNames;
9741                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9742             }
9743             
9744             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9745         }
9746     },
9747
9748     /**
9749      * Reloads the Record cache from the configured Proxy using the configured Reader and
9750      * the options from the last load operation performed.
9751      * @param {Object} options (optional) An object containing properties which may override the options
9752      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9753      * the most recently used options are reused).
9754      */
9755     reload : function(options){
9756         this.load(Roo.applyIf(options||{}, this.lastOptions));
9757     },
9758
9759     // private
9760     // Called as a callback by the Reader during a load operation.
9761     loadRecords : function(o, options, success){
9762         if(!o || success === false){
9763             if(success !== false){
9764                 this.fireEvent("load", this, [], options, o);
9765             }
9766             if(options.callback){
9767                 options.callback.call(options.scope || this, [], options, false);
9768             }
9769             return;
9770         }
9771         // if data returned failure - throw an exception.
9772         if (o.success === false) {
9773             // show a message if no listener is registered.
9774             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9775                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9776             }
9777             // loadmask wil be hooked into this..
9778             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9779             return;
9780         }
9781         var r = o.records, t = o.totalRecords || r.length;
9782         
9783         this.fireEvent("beforeloadadd", this, r, options, o);
9784         
9785         if(!options || options.add !== true){
9786             if(this.pruneModifiedRecords){
9787                 this.modified = [];
9788             }
9789             for(var i = 0, len = r.length; i < len; i++){
9790                 r[i].join(this);
9791             }
9792             if(this.snapshot){
9793                 this.data = this.snapshot;
9794                 delete this.snapshot;
9795             }
9796             this.data.clear();
9797             this.data.addAll(r);
9798             this.totalLength = t;
9799             this.applySort();
9800             this.fireEvent("datachanged", this);
9801         }else{
9802             this.totalLength = Math.max(t, this.data.length+r.length);
9803             this.add(r);
9804         }
9805         this.fireEvent("load", this, r, options, o);
9806         if(options.callback){
9807             options.callback.call(options.scope || this, r, options, true);
9808         }
9809     },
9810
9811
9812     /**
9813      * Loads data from a passed data block. A Reader which understands the format of the data
9814      * must have been configured in the constructor.
9815      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9816      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9817      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9818      */
9819     loadData : function(o, append){
9820         var r = this.reader.readRecords(o);
9821         this.loadRecords(r, {add: append}, true);
9822     },
9823
9824     /**
9825      * Gets the number of cached records.
9826      * <p>
9827      * <em>If using paging, this may not be the total size of the dataset. If the data object
9828      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9829      * the data set size</em>
9830      */
9831     getCount : function(){
9832         return this.data.length || 0;
9833     },
9834
9835     /**
9836      * Gets the total number of records in the dataset as returned by the server.
9837      * <p>
9838      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9839      * the dataset size</em>
9840      */
9841     getTotalCount : function(){
9842         return this.totalLength || 0;
9843     },
9844
9845     /**
9846      * Returns the sort state of the Store as an object with two properties:
9847      * <pre><code>
9848  field {String} The name of the field by which the Records are sorted
9849  direction {String} The sort order, "ASC" or "DESC"
9850      * </code></pre>
9851      */
9852     getSortState : function(){
9853         return this.sortInfo;
9854     },
9855
9856     // private
9857     applySort : function(){
9858         if(this.sortInfo && !this.remoteSort){
9859             var s = this.sortInfo, f = s.field;
9860             var st = this.fields.get(f).sortType;
9861             var fn = function(r1, r2){
9862                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9863                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9864             };
9865             this.data.sort(s.direction, fn);
9866             if(this.snapshot && this.snapshot != this.data){
9867                 this.snapshot.sort(s.direction, fn);
9868             }
9869         }
9870     },
9871
9872     /**
9873      * Sets the default sort column and order to be used by the next load operation.
9874      * @param {String} fieldName The name of the field to sort by.
9875      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9876      */
9877     setDefaultSort : function(field, dir){
9878         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9879     },
9880
9881     /**
9882      * Sort the Records.
9883      * If remote sorting is used, the sort is performed on the server, and the cache is
9884      * reloaded. If local sorting is used, the cache is sorted internally.
9885      * @param {String} fieldName The name of the field to sort by.
9886      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9887      */
9888     sort : function(fieldName, dir){
9889         var f = this.fields.get(fieldName);
9890         if(!dir){
9891             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9892             
9893             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9894                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9895             }else{
9896                 dir = f.sortDir;
9897             }
9898         }
9899         this.sortToggle[f.name] = dir;
9900         this.sortInfo = {field: f.name, direction: dir};
9901         if(!this.remoteSort){
9902             this.applySort();
9903             this.fireEvent("datachanged", this);
9904         }else{
9905             this.load(this.lastOptions);
9906         }
9907     },
9908
9909     /**
9910      * Calls the specified function for each of the Records in the cache.
9911      * @param {Function} fn The function to call. The Record is passed as the first parameter.
9912      * Returning <em>false</em> aborts and exits the iteration.
9913      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9914      */
9915     each : function(fn, scope){
9916         this.data.each(fn, scope);
9917     },
9918
9919     /**
9920      * Gets all records modified since the last commit.  Modified records are persisted across load operations
9921      * (e.g., during paging).
9922      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9923      */
9924     getModifiedRecords : function(){
9925         return this.modified;
9926     },
9927
9928     // private
9929     createFilterFn : function(property, value, anyMatch){
9930         if(!value.exec){ // not a regex
9931             value = String(value);
9932             if(value.length == 0){
9933                 return false;
9934             }
9935             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9936         }
9937         return function(r){
9938             return value.test(r.data[property]);
9939         };
9940     },
9941
9942     /**
9943      * Sums the value of <i>property</i> for each record between start and end and returns the result.
9944      * @param {String} property A field on your records
9945      * @param {Number} start The record index to start at (defaults to 0)
9946      * @param {Number} end The last record index to include (defaults to length - 1)
9947      * @return {Number} The sum
9948      */
9949     sum : function(property, start, end){
9950         var rs = this.data.items, v = 0;
9951         start = start || 0;
9952         end = (end || end === 0) ? end : rs.length-1;
9953
9954         for(var i = start; i <= end; i++){
9955             v += (rs[i].data[property] || 0);
9956         }
9957         return v;
9958     },
9959
9960     /**
9961      * Filter the records by a specified property.
9962      * @param {String} field A field on your records
9963      * @param {String/RegExp} value Either a string that the field
9964      * should start with or a RegExp to test against the field
9965      * @param {Boolean} anyMatch True to match any part not just the beginning
9966      */
9967     filter : function(property, value, anyMatch){
9968         var fn = this.createFilterFn(property, value, anyMatch);
9969         return fn ? this.filterBy(fn) : this.clearFilter();
9970     },
9971
9972     /**
9973      * Filter by a function. The specified function will be called with each
9974      * record in this data source. If the function returns true the record is included,
9975      * otherwise it is filtered.
9976      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9977      * @param {Object} scope (optional) The scope of the function (defaults to this)
9978      */
9979     filterBy : function(fn, scope){
9980         this.snapshot = this.snapshot || this.data;
9981         this.data = this.queryBy(fn, scope||this);
9982         this.fireEvent("datachanged", this);
9983     },
9984
9985     /**
9986      * Query the records by a specified property.
9987      * @param {String} field A field on your records
9988      * @param {String/RegExp} value Either a string that the field
9989      * should start with or a RegExp to test against the field
9990      * @param {Boolean} anyMatch True to match any part not just the beginning
9991      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9992      */
9993     query : function(property, value, anyMatch){
9994         var fn = this.createFilterFn(property, value, anyMatch);
9995         return fn ? this.queryBy(fn) : this.data.clone();
9996     },
9997
9998     /**
9999      * Query by a function. The specified function will be called with each
10000      * record in this data source. If the function returns true the record is included
10001      * in the results.
10002      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10003      * @param {Object} scope (optional) The scope of the function (defaults to this)
10004       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10005      **/
10006     queryBy : function(fn, scope){
10007         var data = this.snapshot || this.data;
10008         return data.filterBy(fn, scope||this);
10009     },
10010
10011     /**
10012      * Collects unique values for a particular dataIndex from this store.
10013      * @param {String} dataIndex The property to collect
10014      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10015      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10016      * @return {Array} An array of the unique values
10017      **/
10018     collect : function(dataIndex, allowNull, bypassFilter){
10019         var d = (bypassFilter === true && this.snapshot) ?
10020                 this.snapshot.items : this.data.items;
10021         var v, sv, r = [], l = {};
10022         for(var i = 0, len = d.length; i < len; i++){
10023             v = d[i].data[dataIndex];
10024             sv = String(v);
10025             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10026                 l[sv] = true;
10027                 r[r.length] = v;
10028             }
10029         }
10030         return r;
10031     },
10032
10033     /**
10034      * Revert to a view of the Record cache with no filtering applied.
10035      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10036      */
10037     clearFilter : function(suppressEvent){
10038         if(this.snapshot && this.snapshot != this.data){
10039             this.data = this.snapshot;
10040             delete this.snapshot;
10041             if(suppressEvent !== true){
10042                 this.fireEvent("datachanged", this);
10043             }
10044         }
10045     },
10046
10047     // private
10048     afterEdit : function(record){
10049         if(this.modified.indexOf(record) == -1){
10050             this.modified.push(record);
10051         }
10052         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10053     },
10054     
10055     // private
10056     afterReject : function(record){
10057         this.modified.remove(record);
10058         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10059     },
10060
10061     // private
10062     afterCommit : function(record){
10063         this.modified.remove(record);
10064         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10065     },
10066
10067     /**
10068      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10069      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10070      */
10071     commitChanges : function(){
10072         var m = this.modified.slice(0);
10073         this.modified = [];
10074         for(var i = 0, len = m.length; i < len; i++){
10075             m[i].commit();
10076         }
10077     },
10078
10079     /**
10080      * Cancel outstanding changes on all changed records.
10081      */
10082     rejectChanges : function(){
10083         var m = this.modified.slice(0);
10084         this.modified = [];
10085         for(var i = 0, len = m.length; i < len; i++){
10086             m[i].reject();
10087         }
10088     },
10089
10090     onMetaChange : function(meta, rtype, o){
10091         this.recordType = rtype;
10092         this.fields = rtype.prototype.fields;
10093         delete this.snapshot;
10094         this.sortInfo = meta.sortInfo || this.sortInfo;
10095         this.modified = [];
10096         this.fireEvent('metachange', this, this.reader.meta);
10097     },
10098     
10099     moveIndex : function(data, type)
10100     {
10101         var index = this.indexOf(data);
10102         
10103         var newIndex = index + type;
10104         
10105         this.remove(data);
10106         
10107         this.insert(newIndex, data);
10108         
10109     }
10110 });/*
10111  * Based on:
10112  * Ext JS Library 1.1.1
10113  * Copyright(c) 2006-2007, Ext JS, LLC.
10114  *
10115  * Originally Released Under LGPL - original licence link has changed is not relivant.
10116  *
10117  * Fork - LGPL
10118  * <script type="text/javascript">
10119  */
10120
10121 /**
10122  * @class Roo.data.SimpleStore
10123  * @extends Roo.data.Store
10124  * Small helper class to make creating Stores from Array data easier.
10125  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10126  * @cfg {Array} fields An array of field definition objects, or field name strings.
10127  * @cfg {Array} data The multi-dimensional array of data
10128  * @constructor
10129  * @param {Object} config
10130  */
10131 Roo.data.SimpleStore = function(config){
10132     Roo.data.SimpleStore.superclass.constructor.call(this, {
10133         isLocal : true,
10134         reader: new Roo.data.ArrayReader({
10135                 id: config.id
10136             },
10137             Roo.data.Record.create(config.fields)
10138         ),
10139         proxy : new Roo.data.MemoryProxy(config.data)
10140     });
10141     this.load();
10142 };
10143 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10144  * Based on:
10145  * Ext JS Library 1.1.1
10146  * Copyright(c) 2006-2007, Ext JS, LLC.
10147  *
10148  * Originally Released Under LGPL - original licence link has changed is not relivant.
10149  *
10150  * Fork - LGPL
10151  * <script type="text/javascript">
10152  */
10153
10154 /**
10155 /**
10156  * @extends Roo.data.Store
10157  * @class Roo.data.JsonStore
10158  * Small helper class to make creating Stores for JSON data easier. <br/>
10159 <pre><code>
10160 var store = new Roo.data.JsonStore({
10161     url: 'get-images.php',
10162     root: 'images',
10163     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10164 });
10165 </code></pre>
10166  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10167  * JsonReader and HttpProxy (unless inline data is provided).</b>
10168  * @cfg {Array} fields An array of field definition objects, or field name strings.
10169  * @constructor
10170  * @param {Object} config
10171  */
10172 Roo.data.JsonStore = function(c){
10173     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10174         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10175         reader: new Roo.data.JsonReader(c, c.fields)
10176     }));
10177 };
10178 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10179  * Based on:
10180  * Ext JS Library 1.1.1
10181  * Copyright(c) 2006-2007, Ext JS, LLC.
10182  *
10183  * Originally Released Under LGPL - original licence link has changed is not relivant.
10184  *
10185  * Fork - LGPL
10186  * <script type="text/javascript">
10187  */
10188
10189  
10190 Roo.data.Field = function(config){
10191     if(typeof config == "string"){
10192         config = {name: config};
10193     }
10194     Roo.apply(this, config);
10195     
10196     if(!this.type){
10197         this.type = "auto";
10198     }
10199     
10200     var st = Roo.data.SortTypes;
10201     // named sortTypes are supported, here we look them up
10202     if(typeof this.sortType == "string"){
10203         this.sortType = st[this.sortType];
10204     }
10205     
10206     // set default sortType for strings and dates
10207     if(!this.sortType){
10208         switch(this.type){
10209             case "string":
10210                 this.sortType = st.asUCString;
10211                 break;
10212             case "date":
10213                 this.sortType = st.asDate;
10214                 break;
10215             default:
10216                 this.sortType = st.none;
10217         }
10218     }
10219
10220     // define once
10221     var stripRe = /[\$,%]/g;
10222
10223     // prebuilt conversion function for this field, instead of
10224     // switching every time we're reading a value
10225     if(!this.convert){
10226         var cv, dateFormat = this.dateFormat;
10227         switch(this.type){
10228             case "":
10229             case "auto":
10230             case undefined:
10231                 cv = function(v){ return v; };
10232                 break;
10233             case "string":
10234                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10235                 break;
10236             case "int":
10237                 cv = function(v){
10238                     return v !== undefined && v !== null && v !== '' ?
10239                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10240                     };
10241                 break;
10242             case "float":
10243                 cv = function(v){
10244                     return v !== undefined && v !== null && v !== '' ?
10245                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10246                     };
10247                 break;
10248             case "bool":
10249             case "boolean":
10250                 cv = function(v){ return v === true || v === "true" || v == 1; };
10251                 break;
10252             case "date":
10253                 cv = function(v){
10254                     if(!v){
10255                         return '';
10256                     }
10257                     if(v instanceof Date){
10258                         return v;
10259                     }
10260                     if(dateFormat){
10261                         if(dateFormat == "timestamp"){
10262                             return new Date(v*1000);
10263                         }
10264                         return Date.parseDate(v, dateFormat);
10265                     }
10266                     var parsed = Date.parse(v);
10267                     return parsed ? new Date(parsed) : null;
10268                 };
10269              break;
10270             
10271         }
10272         this.convert = cv;
10273     }
10274 };
10275
10276 Roo.data.Field.prototype = {
10277     dateFormat: null,
10278     defaultValue: "",
10279     mapping: null,
10280     sortType : null,
10281     sortDir : "ASC"
10282 };/*
10283  * Based on:
10284  * Ext JS Library 1.1.1
10285  * Copyright(c) 2006-2007, Ext JS, LLC.
10286  *
10287  * Originally Released Under LGPL - original licence link has changed is not relivant.
10288  *
10289  * Fork - LGPL
10290  * <script type="text/javascript">
10291  */
10292  
10293 // Base class for reading structured data from a data source.  This class is intended to be
10294 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10295
10296 /**
10297  * @class Roo.data.DataReader
10298  * Base class for reading structured data from a data source.  This class is intended to be
10299  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10300  */
10301
10302 Roo.data.DataReader = function(meta, recordType){
10303     
10304     this.meta = meta;
10305     
10306     this.recordType = recordType instanceof Array ? 
10307         Roo.data.Record.create(recordType) : recordType;
10308 };
10309
10310 Roo.data.DataReader.prototype = {
10311      /**
10312      * Create an empty record
10313      * @param {Object} data (optional) - overlay some values
10314      * @return {Roo.data.Record} record created.
10315      */
10316     newRow :  function(d) {
10317         var da =  {};
10318         this.recordType.prototype.fields.each(function(c) {
10319             switch( c.type) {
10320                 case 'int' : da[c.name] = 0; break;
10321                 case 'date' : da[c.name] = new Date(); break;
10322                 case 'float' : da[c.name] = 0.0; break;
10323                 case 'boolean' : da[c.name] = false; break;
10324                 default : da[c.name] = ""; break;
10325             }
10326             
10327         });
10328         return new this.recordType(Roo.apply(da, d));
10329     }
10330     
10331 };/*
10332  * Based on:
10333  * Ext JS Library 1.1.1
10334  * Copyright(c) 2006-2007, Ext JS, LLC.
10335  *
10336  * Originally Released Under LGPL - original licence link has changed is not relivant.
10337  *
10338  * Fork - LGPL
10339  * <script type="text/javascript">
10340  */
10341
10342 /**
10343  * @class Roo.data.DataProxy
10344  * @extends Roo.data.Observable
10345  * This class is an abstract base class for implementations which provide retrieval of
10346  * unformatted data objects.<br>
10347  * <p>
10348  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10349  * (of the appropriate type which knows how to parse the data object) to provide a block of
10350  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10351  * <p>
10352  * Custom implementations must implement the load method as described in
10353  * {@link Roo.data.HttpProxy#load}.
10354  */
10355 Roo.data.DataProxy = function(){
10356     this.addEvents({
10357         /**
10358          * @event beforeload
10359          * Fires before a network request is made to retrieve a data object.
10360          * @param {Object} This DataProxy object.
10361          * @param {Object} params The params parameter to the load function.
10362          */
10363         beforeload : true,
10364         /**
10365          * @event load
10366          * Fires before the load method's callback is called.
10367          * @param {Object} This DataProxy object.
10368          * @param {Object} o The data object.
10369          * @param {Object} arg The callback argument object passed to the load function.
10370          */
10371         load : true,
10372         /**
10373          * @event loadexception
10374          * Fires if an Exception occurs during data retrieval.
10375          * @param {Object} This DataProxy object.
10376          * @param {Object} o The data object.
10377          * @param {Object} arg The callback argument object passed to the load function.
10378          * @param {Object} e The Exception.
10379          */
10380         loadexception : true
10381     });
10382     Roo.data.DataProxy.superclass.constructor.call(this);
10383 };
10384
10385 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10386
10387     /**
10388      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10389      */
10390 /*
10391  * Based on:
10392  * Ext JS Library 1.1.1
10393  * Copyright(c) 2006-2007, Ext JS, LLC.
10394  *
10395  * Originally Released Under LGPL - original licence link has changed is not relivant.
10396  *
10397  * Fork - LGPL
10398  * <script type="text/javascript">
10399  */
10400 /**
10401  * @class Roo.data.MemoryProxy
10402  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10403  * to the Reader when its load method is called.
10404  * @constructor
10405  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10406  */
10407 Roo.data.MemoryProxy = function(data){
10408     if (data.data) {
10409         data = data.data;
10410     }
10411     Roo.data.MemoryProxy.superclass.constructor.call(this);
10412     this.data = data;
10413 };
10414
10415 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10416     /**
10417      * Load data from the requested source (in this case an in-memory
10418      * data object passed to the constructor), read the data object into
10419      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10420      * process that block using the passed callback.
10421      * @param {Object} params This parameter is not used by the MemoryProxy class.
10422      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10423      * object into a block of Roo.data.Records.
10424      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10425      * The function must be passed <ul>
10426      * <li>The Record block object</li>
10427      * <li>The "arg" argument from the load function</li>
10428      * <li>A boolean success indicator</li>
10429      * </ul>
10430      * @param {Object} scope The scope in which to call the callback
10431      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10432      */
10433     load : function(params, reader, callback, scope, arg){
10434         params = params || {};
10435         var result;
10436         try {
10437             result = reader.readRecords(this.data);
10438         }catch(e){
10439             this.fireEvent("loadexception", this, arg, null, e);
10440             callback.call(scope, null, arg, false);
10441             return;
10442         }
10443         callback.call(scope, result, arg, true);
10444     },
10445     
10446     // private
10447     update : function(params, records){
10448         
10449     }
10450 });/*
10451  * Based on:
10452  * Ext JS Library 1.1.1
10453  * Copyright(c) 2006-2007, Ext JS, LLC.
10454  *
10455  * Originally Released Under LGPL - original licence link has changed is not relivant.
10456  *
10457  * Fork - LGPL
10458  * <script type="text/javascript">
10459  */
10460 /**
10461  * @class Roo.data.HttpProxy
10462  * @extends Roo.data.DataProxy
10463  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10464  * configured to reference a certain URL.<br><br>
10465  * <p>
10466  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10467  * from which the running page was served.<br><br>
10468  * <p>
10469  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10470  * <p>
10471  * Be aware that to enable the browser to parse an XML document, the server must set
10472  * the Content-Type header in the HTTP response to "text/xml".
10473  * @constructor
10474  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10475  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10476  * will be used to make the request.
10477  */
10478 Roo.data.HttpProxy = function(conn){
10479     Roo.data.HttpProxy.superclass.constructor.call(this);
10480     // is conn a conn config or a real conn?
10481     this.conn = conn;
10482     this.useAjax = !conn || !conn.events;
10483   
10484 };
10485
10486 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10487     // thse are take from connection...
10488     
10489     /**
10490      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10491      */
10492     /**
10493      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10494      * extra parameters to each request made by this object. (defaults to undefined)
10495      */
10496     /**
10497      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10498      *  to each request made by this object. (defaults to undefined)
10499      */
10500     /**
10501      * @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)
10502      */
10503     /**
10504      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10505      */
10506      /**
10507      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10508      * @type Boolean
10509      */
10510   
10511
10512     /**
10513      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10514      * @type Boolean
10515      */
10516     /**
10517      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10518      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10519      * a finer-grained basis than the DataProxy events.
10520      */
10521     getConnection : function(){
10522         return this.useAjax ? Roo.Ajax : this.conn;
10523     },
10524
10525     /**
10526      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10527      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10528      * process that block using the passed callback.
10529      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10530      * for the request to the remote server.
10531      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10532      * object into a block of Roo.data.Records.
10533      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10534      * The function must be passed <ul>
10535      * <li>The Record block object</li>
10536      * <li>The "arg" argument from the load function</li>
10537      * <li>A boolean success indicator</li>
10538      * </ul>
10539      * @param {Object} scope The scope in which to call the callback
10540      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10541      */
10542     load : function(params, reader, callback, scope, arg){
10543         if(this.fireEvent("beforeload", this, params) !== false){
10544             var  o = {
10545                 params : params || {},
10546                 request: {
10547                     callback : callback,
10548                     scope : scope,
10549                     arg : arg
10550                 },
10551                 reader: reader,
10552                 callback : this.loadResponse,
10553                 scope: this
10554             };
10555             if(this.useAjax){
10556                 Roo.applyIf(o, this.conn);
10557                 if(this.activeRequest){
10558                     Roo.Ajax.abort(this.activeRequest);
10559                 }
10560                 this.activeRequest = Roo.Ajax.request(o);
10561             }else{
10562                 this.conn.request(o);
10563             }
10564         }else{
10565             callback.call(scope||this, null, arg, false);
10566         }
10567     },
10568
10569     // private
10570     loadResponse : function(o, success, response){
10571         delete this.activeRequest;
10572         if(!success){
10573             this.fireEvent("loadexception", this, o, response);
10574             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10575             return;
10576         }
10577         var result;
10578         try {
10579             result = o.reader.read(response);
10580         }catch(e){
10581             this.fireEvent("loadexception", this, o, response, e);
10582             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10583             return;
10584         }
10585         
10586         this.fireEvent("load", this, o, o.request.arg);
10587         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10588     },
10589
10590     // private
10591     update : function(dataSet){
10592
10593     },
10594
10595     // private
10596     updateResponse : function(dataSet){
10597
10598     }
10599 });/*
10600  * Based on:
10601  * Ext JS Library 1.1.1
10602  * Copyright(c) 2006-2007, Ext JS, LLC.
10603  *
10604  * Originally Released Under LGPL - original licence link has changed is not relivant.
10605  *
10606  * Fork - LGPL
10607  * <script type="text/javascript">
10608  */
10609
10610 /**
10611  * @class Roo.data.ScriptTagProxy
10612  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10613  * other than the originating domain of the running page.<br><br>
10614  * <p>
10615  * <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
10616  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10617  * <p>
10618  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10619  * source code that is used as the source inside a &lt;script> tag.<br><br>
10620  * <p>
10621  * In order for the browser to process the returned data, the server must wrap the data object
10622  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10623  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10624  * depending on whether the callback name was passed:
10625  * <p>
10626  * <pre><code>
10627 boolean scriptTag = false;
10628 String cb = request.getParameter("callback");
10629 if (cb != null) {
10630     scriptTag = true;
10631     response.setContentType("text/javascript");
10632 } else {
10633     response.setContentType("application/x-json");
10634 }
10635 Writer out = response.getWriter();
10636 if (scriptTag) {
10637     out.write(cb + "(");
10638 }
10639 out.print(dataBlock.toJsonString());
10640 if (scriptTag) {
10641     out.write(");");
10642 }
10643 </pre></code>
10644  *
10645  * @constructor
10646  * @param {Object} config A configuration object.
10647  */
10648 Roo.data.ScriptTagProxy = function(config){
10649     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10650     Roo.apply(this, config);
10651     this.head = document.getElementsByTagName("head")[0];
10652 };
10653
10654 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10655
10656 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10657     /**
10658      * @cfg {String} url The URL from which to request the data object.
10659      */
10660     /**
10661      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10662      */
10663     timeout : 30000,
10664     /**
10665      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10666      * the server the name of the callback function set up by the load call to process the returned data object.
10667      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10668      * javascript output which calls this named function passing the data object as its only parameter.
10669      */
10670     callbackParam : "callback",
10671     /**
10672      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10673      * name to the request.
10674      */
10675     nocache : true,
10676
10677     /**
10678      * Load data from the configured URL, read the data object into
10679      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10680      * process that block using the passed callback.
10681      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10682      * for the request to the remote server.
10683      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10684      * object into a block of Roo.data.Records.
10685      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10686      * The function must be passed <ul>
10687      * <li>The Record block object</li>
10688      * <li>The "arg" argument from the load function</li>
10689      * <li>A boolean success indicator</li>
10690      * </ul>
10691      * @param {Object} scope The scope in which to call the callback
10692      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10693      */
10694     load : function(params, reader, callback, scope, arg){
10695         if(this.fireEvent("beforeload", this, params) !== false){
10696
10697             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10698
10699             var url = this.url;
10700             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10701             if(this.nocache){
10702                 url += "&_dc=" + (new Date().getTime());
10703             }
10704             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10705             var trans = {
10706                 id : transId,
10707                 cb : "stcCallback"+transId,
10708                 scriptId : "stcScript"+transId,
10709                 params : params,
10710                 arg : arg,
10711                 url : url,
10712                 callback : callback,
10713                 scope : scope,
10714                 reader : reader
10715             };
10716             var conn = this;
10717
10718             window[trans.cb] = function(o){
10719                 conn.handleResponse(o, trans);
10720             };
10721
10722             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10723
10724             if(this.autoAbort !== false){
10725                 this.abort();
10726             }
10727
10728             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10729
10730             var script = document.createElement("script");
10731             script.setAttribute("src", url);
10732             script.setAttribute("type", "text/javascript");
10733             script.setAttribute("id", trans.scriptId);
10734             this.head.appendChild(script);
10735
10736             this.trans = trans;
10737         }else{
10738             callback.call(scope||this, null, arg, false);
10739         }
10740     },
10741
10742     // private
10743     isLoading : function(){
10744         return this.trans ? true : false;
10745     },
10746
10747     /**
10748      * Abort the current server request.
10749      */
10750     abort : function(){
10751         if(this.isLoading()){
10752             this.destroyTrans(this.trans);
10753         }
10754     },
10755
10756     // private
10757     destroyTrans : function(trans, isLoaded){
10758         this.head.removeChild(document.getElementById(trans.scriptId));
10759         clearTimeout(trans.timeoutId);
10760         if(isLoaded){
10761             window[trans.cb] = undefined;
10762             try{
10763                 delete window[trans.cb];
10764             }catch(e){}
10765         }else{
10766             // if hasn't been loaded, wait for load to remove it to prevent script error
10767             window[trans.cb] = function(){
10768                 window[trans.cb] = undefined;
10769                 try{
10770                     delete window[trans.cb];
10771                 }catch(e){}
10772             };
10773         }
10774     },
10775
10776     // private
10777     handleResponse : function(o, trans){
10778         this.trans = false;
10779         this.destroyTrans(trans, true);
10780         var result;
10781         try {
10782             result = trans.reader.readRecords(o);
10783         }catch(e){
10784             this.fireEvent("loadexception", this, o, trans.arg, e);
10785             trans.callback.call(trans.scope||window, null, trans.arg, false);
10786             return;
10787         }
10788         this.fireEvent("load", this, o, trans.arg);
10789         trans.callback.call(trans.scope||window, result, trans.arg, true);
10790     },
10791
10792     // private
10793     handleFailure : function(trans){
10794         this.trans = false;
10795         this.destroyTrans(trans, false);
10796         this.fireEvent("loadexception", this, null, trans.arg);
10797         trans.callback.call(trans.scope||window, null, trans.arg, false);
10798     }
10799 });/*
10800  * Based on:
10801  * Ext JS Library 1.1.1
10802  * Copyright(c) 2006-2007, Ext JS, LLC.
10803  *
10804  * Originally Released Under LGPL - original licence link has changed is not relivant.
10805  *
10806  * Fork - LGPL
10807  * <script type="text/javascript">
10808  */
10809
10810 /**
10811  * @class Roo.data.JsonReader
10812  * @extends Roo.data.DataReader
10813  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10814  * based on mappings in a provided Roo.data.Record constructor.
10815  * 
10816  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10817  * in the reply previously. 
10818  * 
10819  * <p>
10820  * Example code:
10821  * <pre><code>
10822 var RecordDef = Roo.data.Record.create([
10823     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10824     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10825 ]);
10826 var myReader = new Roo.data.JsonReader({
10827     totalProperty: "results",    // The property which contains the total dataset size (optional)
10828     root: "rows",                // The property which contains an Array of row objects
10829     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10830 }, RecordDef);
10831 </code></pre>
10832  * <p>
10833  * This would consume a JSON file like this:
10834  * <pre><code>
10835 { 'results': 2, 'rows': [
10836     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10837     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10838 }
10839 </code></pre>
10840  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10841  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10842  * paged from the remote server.
10843  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10844  * @cfg {String} root name of the property which contains the Array of row objects.
10845  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10846  * @cfg {Array} fields Array of field definition objects
10847  * @constructor
10848  * Create a new JsonReader
10849  * @param {Object} meta Metadata configuration options
10850  * @param {Object} recordType Either an Array of field definition objects,
10851  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10852  */
10853 Roo.data.JsonReader = function(meta, recordType){
10854     
10855     meta = meta || {};
10856     // set some defaults:
10857     Roo.applyIf(meta, {
10858         totalProperty: 'total',
10859         successProperty : 'success',
10860         root : 'data',
10861         id : 'id'
10862     });
10863     
10864     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10865 };
10866 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10867     
10868     /**
10869      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10870      * Used by Store query builder to append _requestMeta to params.
10871      * 
10872      */
10873     metaFromRemote : false,
10874     /**
10875      * This method is only used by a DataProxy which has retrieved data from a remote server.
10876      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10877      * @return {Object} data A data block which is used by an Roo.data.Store object as
10878      * a cache of Roo.data.Records.
10879      */
10880     read : function(response){
10881         var json = response.responseText;
10882        
10883         var o = /* eval:var:o */ eval("("+json+")");
10884         if(!o) {
10885             throw {message: "JsonReader.read: Json object not found"};
10886         }
10887         
10888         if(o.metaData){
10889             
10890             delete this.ef;
10891             this.metaFromRemote = true;
10892             this.meta = o.metaData;
10893             this.recordType = Roo.data.Record.create(o.metaData.fields);
10894             this.onMetaChange(this.meta, this.recordType, o);
10895         }
10896         return this.readRecords(o);
10897     },
10898
10899     // private function a store will implement
10900     onMetaChange : function(meta, recordType, o){
10901
10902     },
10903
10904     /**
10905          * @ignore
10906          */
10907     simpleAccess: function(obj, subsc) {
10908         return obj[subsc];
10909     },
10910
10911         /**
10912          * @ignore
10913          */
10914     getJsonAccessor: function(){
10915         var re = /[\[\.]/;
10916         return function(expr) {
10917             try {
10918                 return(re.test(expr))
10919                     ? new Function("obj", "return obj." + expr)
10920                     : function(obj){
10921                         return obj[expr];
10922                     };
10923             } catch(e){}
10924             return Roo.emptyFn;
10925         };
10926     }(),
10927
10928     /**
10929      * Create a data block containing Roo.data.Records from an XML document.
10930      * @param {Object} o An object which contains an Array of row objects in the property specified
10931      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10932      * which contains the total size of the dataset.
10933      * @return {Object} data A data block which is used by an Roo.data.Store object as
10934      * a cache of Roo.data.Records.
10935      */
10936     readRecords : function(o){
10937         /**
10938          * After any data loads, the raw JSON data is available for further custom processing.
10939          * @type Object
10940          */
10941         this.o = o;
10942         var s = this.meta, Record = this.recordType,
10943             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
10944
10945 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
10946         if (!this.ef) {
10947             if(s.totalProperty) {
10948                     this.getTotal = this.getJsonAccessor(s.totalProperty);
10949                 }
10950                 if(s.successProperty) {
10951                     this.getSuccess = this.getJsonAccessor(s.successProperty);
10952                 }
10953                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10954                 if (s.id) {
10955                         var g = this.getJsonAccessor(s.id);
10956                         this.getId = function(rec) {
10957                                 var r = g(rec);  
10958                                 return (r === undefined || r === "") ? null : r;
10959                         };
10960                 } else {
10961                         this.getId = function(){return null;};
10962                 }
10963             this.ef = [];
10964             for(var jj = 0; jj < fl; jj++){
10965                 f = fi[jj];
10966                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10967                 this.ef[jj] = this.getJsonAccessor(map);
10968             }
10969         }
10970
10971         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10972         if(s.totalProperty){
10973             var vt = parseInt(this.getTotal(o), 10);
10974             if(!isNaN(vt)){
10975                 totalRecords = vt;
10976             }
10977         }
10978         if(s.successProperty){
10979             var vs = this.getSuccess(o);
10980             if(vs === false || vs === 'false'){
10981                 success = false;
10982             }
10983         }
10984         var records = [];
10985         for(var i = 0; i < c; i++){
10986                 var n = root[i];
10987             var values = {};
10988             var id = this.getId(n);
10989             for(var j = 0; j < fl; j++){
10990                 f = fi[j];
10991             var v = this.ef[j](n);
10992             if (!f.convert) {
10993                 Roo.log('missing convert for ' + f.name);
10994                 Roo.log(f);
10995                 continue;
10996             }
10997             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10998             }
10999             var record = new Record(values, id);
11000             record.json = n;
11001             records[i] = record;
11002         }
11003         return {
11004             raw : o,
11005             success : success,
11006             records : records,
11007             totalRecords : totalRecords
11008         };
11009     }
11010 });/*
11011  * Based on:
11012  * Ext JS Library 1.1.1
11013  * Copyright(c) 2006-2007, Ext JS, LLC.
11014  *
11015  * Originally Released Under LGPL - original licence link has changed is not relivant.
11016  *
11017  * Fork - LGPL
11018  * <script type="text/javascript">
11019  */
11020
11021 /**
11022  * @class Roo.data.ArrayReader
11023  * @extends Roo.data.DataReader
11024  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11025  * Each element of that Array represents a row of data fields. The
11026  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11027  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11028  * <p>
11029  * Example code:.
11030  * <pre><code>
11031 var RecordDef = Roo.data.Record.create([
11032     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11033     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11034 ]);
11035 var myReader = new Roo.data.ArrayReader({
11036     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11037 }, RecordDef);
11038 </code></pre>
11039  * <p>
11040  * This would consume an Array like this:
11041  * <pre><code>
11042 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11043   </code></pre>
11044  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11045  * @constructor
11046  * Create a new JsonReader
11047  * @param {Object} meta Metadata configuration options.
11048  * @param {Object} recordType Either an Array of field definition objects
11049  * as specified to {@link Roo.data.Record#create},
11050  * or an {@link Roo.data.Record} object
11051  * created using {@link Roo.data.Record#create}.
11052  */
11053 Roo.data.ArrayReader = function(meta, recordType){
11054     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11055 };
11056
11057 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11058     /**
11059      * Create a data block containing Roo.data.Records from an XML document.
11060      * @param {Object} o An Array of row objects which represents the dataset.
11061      * @return {Object} data A data block which is used by an Roo.data.Store object as
11062      * a cache of Roo.data.Records.
11063      */
11064     readRecords : function(o){
11065         var sid = this.meta ? this.meta.id : null;
11066         var recordType = this.recordType, fields = recordType.prototype.fields;
11067         var records = [];
11068         var root = o;
11069             for(var i = 0; i < root.length; i++){
11070                     var n = root[i];
11071                 var values = {};
11072                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11073                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11074                 var f = fields.items[j];
11075                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11076                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11077                 v = f.convert(v);
11078                 values[f.name] = v;
11079             }
11080                 var record = new recordType(values, id);
11081                 record.json = n;
11082                 records[records.length] = record;
11083             }
11084             return {
11085                 records : records,
11086                 totalRecords : records.length
11087             };
11088     }
11089 });/*
11090  * - LGPL
11091  * * 
11092  */
11093
11094 /**
11095  * @class Roo.bootstrap.ComboBox
11096  * @extends Roo.bootstrap.TriggerField
11097  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11098  * @cfg {Boolean} append (true|false) default false
11099  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11100  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11101  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11102  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11103  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11104  * @cfg {Boolean} animate default true
11105  * @cfg {Boolean} emptyResultText only for touch device
11106  * @constructor
11107  * Create a new ComboBox.
11108  * @param {Object} config Configuration options
11109  */
11110 Roo.bootstrap.ComboBox = function(config){
11111     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11112     this.addEvents({
11113         /**
11114          * @event expand
11115          * Fires when the dropdown list is expanded
11116              * @param {Roo.bootstrap.ComboBox} combo This combo box
11117              */
11118         'expand' : true,
11119         /**
11120          * @event collapse
11121          * Fires when the dropdown list is collapsed
11122              * @param {Roo.bootstrap.ComboBox} combo This combo box
11123              */
11124         'collapse' : true,
11125         /**
11126          * @event beforeselect
11127          * Fires before a list item is selected. Return false to cancel the selection.
11128              * @param {Roo.bootstrap.ComboBox} combo This combo box
11129              * @param {Roo.data.Record} record The data record returned from the underlying store
11130              * @param {Number} index The index of the selected item in the dropdown list
11131              */
11132         'beforeselect' : true,
11133         /**
11134          * @event select
11135          * Fires when a list item is selected
11136              * @param {Roo.bootstrap.ComboBox} combo This combo box
11137              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11138              * @param {Number} index The index of the selected item in the dropdown list
11139              */
11140         'select' : true,
11141         /**
11142          * @event beforequery
11143          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11144          * The event object passed has these properties:
11145              * @param {Roo.bootstrap.ComboBox} combo This combo box
11146              * @param {String} query The query
11147              * @param {Boolean} forceAll true to force "all" query
11148              * @param {Boolean} cancel true to cancel the query
11149              * @param {Object} e The query event object
11150              */
11151         'beforequery': true,
11152          /**
11153          * @event add
11154          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11155              * @param {Roo.bootstrap.ComboBox} combo This combo box
11156              */
11157         'add' : true,
11158         /**
11159          * @event edit
11160          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11161              * @param {Roo.bootstrap.ComboBox} combo This combo box
11162              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11163              */
11164         'edit' : true,
11165         /**
11166          * @event remove
11167          * Fires when the remove value from the combobox array
11168              * @param {Roo.bootstrap.ComboBox} combo This combo box
11169              */
11170         'remove' : true,
11171         /**
11172          * @event specialfilter
11173          * Fires when specialfilter
11174             * @param {Roo.bootstrap.ComboBox} combo This combo box
11175             */
11176         'specialfilter' : true
11177         
11178     });
11179     
11180     this.item = [];
11181     this.tickItems = [];
11182     
11183     this.selectedIndex = -1;
11184     if(this.mode == 'local'){
11185         if(config.queryDelay === undefined){
11186             this.queryDelay = 10;
11187         }
11188         if(config.minChars === undefined){
11189             this.minChars = 0;
11190         }
11191     }
11192 };
11193
11194 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11195      
11196     /**
11197      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11198      * rendering into an Roo.Editor, defaults to false)
11199      */
11200     /**
11201      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11202      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11203      */
11204     /**
11205      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11206      */
11207     /**
11208      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11209      * the dropdown list (defaults to undefined, with no header element)
11210      */
11211
11212      /**
11213      * @cfg {String/Roo.Template} tpl The template to use to render the output
11214      */
11215      
11216      /**
11217      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11218      */
11219     listWidth: undefined,
11220     /**
11221      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11222      * mode = 'remote' or 'text' if mode = 'local')
11223      */
11224     displayField: undefined,
11225     
11226     /**
11227      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11228      * mode = 'remote' or 'value' if mode = 'local'). 
11229      * Note: use of a valueField requires the user make a selection
11230      * in order for a value to be mapped.
11231      */
11232     valueField: undefined,
11233     
11234     
11235     /**
11236      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11237      * field's data value (defaults to the underlying DOM element's name)
11238      */
11239     hiddenName: undefined,
11240     /**
11241      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11242      */
11243     listClass: '',
11244     /**
11245      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11246      */
11247     selectedClass: 'active',
11248     
11249     /**
11250      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11251      */
11252     shadow:'sides',
11253     /**
11254      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11255      * anchor positions (defaults to 'tl-bl')
11256      */
11257     listAlign: 'tl-bl?',
11258     /**
11259      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11260      */
11261     maxHeight: 300,
11262     /**
11263      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11264      * query specified by the allQuery config option (defaults to 'query')
11265      */
11266     triggerAction: 'query',
11267     /**
11268      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11269      * (defaults to 4, does not apply if editable = false)
11270      */
11271     minChars : 4,
11272     /**
11273      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11274      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11275      */
11276     typeAhead: false,
11277     /**
11278      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11279      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11280      */
11281     queryDelay: 500,
11282     /**
11283      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11284      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11285      */
11286     pageSize: 0,
11287     /**
11288      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11289      * when editable = true (defaults to false)
11290      */
11291     selectOnFocus:false,
11292     /**
11293      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11294      */
11295     queryParam: 'query',
11296     /**
11297      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11298      * when mode = 'remote' (defaults to 'Loading...')
11299      */
11300     loadingText: 'Loading...',
11301     /**
11302      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11303      */
11304     resizable: false,
11305     /**
11306      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11307      */
11308     handleHeight : 8,
11309     /**
11310      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11311      * traditional select (defaults to true)
11312      */
11313     editable: true,
11314     /**
11315      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11316      */
11317     allQuery: '',
11318     /**
11319      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11320      */
11321     mode: 'remote',
11322     /**
11323      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11324      * listWidth has a higher value)
11325      */
11326     minListWidth : 70,
11327     /**
11328      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11329      * allow the user to set arbitrary text into the field (defaults to false)
11330      */
11331     forceSelection:false,
11332     /**
11333      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11334      * if typeAhead = true (defaults to 250)
11335      */
11336     typeAheadDelay : 250,
11337     /**
11338      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11339      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11340      */
11341     valueNotFoundText : undefined,
11342     /**
11343      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11344      */
11345     blockFocus : false,
11346     
11347     /**
11348      * @cfg {Boolean} disableClear Disable showing of clear button.
11349      */
11350     disableClear : false,
11351     /**
11352      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11353      */
11354     alwaysQuery : false,
11355     
11356     /**
11357      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11358      */
11359     multiple : false,
11360     
11361     /**
11362      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11363      */
11364     invalidClass : "has-warning",
11365     
11366     /**
11367      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11368      */
11369     validClass : "has-success",
11370     
11371     /**
11372      * @cfg {Boolean} specialFilter (true|false) special filter default false
11373      */
11374     specialFilter : false,
11375     
11376     /**
11377      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11378      */
11379     mobileTouchView : true,
11380     
11381     //private
11382     addicon : false,
11383     editicon: false,
11384     
11385     page: 0,
11386     hasQuery: false,
11387     append: false,
11388     loadNext: false,
11389     autoFocus : true,
11390     tickable : false,
11391     btnPosition : 'right',
11392     triggerList : true,
11393     showToggleBtn : true,
11394     animate : true,
11395     emptyResultText: 'Empty',
11396     // element that contains real text value.. (when hidden is used..)
11397     
11398     getAutoCreate : function()
11399     {
11400         var cfg = false;
11401         
11402         /*
11403          * Touch Devices
11404          */
11405         
11406         if(Roo.isTouch && this.mobileTouchView){
11407             cfg = this.getAutoCreateTouchView();
11408             return cfg;;
11409         }
11410         
11411         /*
11412          *  Normal ComboBox
11413          */
11414         if(!this.tickable){
11415             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11416             return cfg;
11417         }
11418         
11419         /*
11420          *  ComboBox with tickable selections
11421          */
11422              
11423         var align = this.labelAlign || this.parentLabelAlign();
11424         
11425         cfg = {
11426             cls : 'form-group roo-combobox-tickable' //input-group
11427         };
11428         
11429         var buttons = {
11430             tag : 'div',
11431             cls : 'tickable-buttons',
11432             cn : [
11433                 {
11434                     tag : 'button',
11435                     type : 'button',
11436                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11437                     html : 'Edit'
11438                 },
11439                 {
11440                     tag : 'button',
11441                     type : 'button',
11442                     name : 'ok',
11443                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11444                     html : 'Done'
11445                 },
11446                 {
11447                     tag : 'button',
11448                     type : 'button',
11449                     name : 'cancel',
11450                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11451                     html : 'Cancel'
11452                 }
11453             ]
11454         };
11455         
11456         if(this.editable){
11457             buttons.cn.unshift({
11458                 tag: 'input',
11459                 cls: 'select2-search-field-input'
11460             });
11461         }
11462         
11463         var _this = this;
11464         
11465         Roo.each(buttons.cn, function(c){
11466             if (_this.size) {
11467                 c.cls += ' btn-' + _this.size;
11468             }
11469
11470             if (_this.disabled) {
11471                 c.disabled = true;
11472             }
11473         });
11474         
11475         var box = {
11476             tag: 'div',
11477             cn: [
11478                 {
11479                     tag: 'input',
11480                     type : 'hidden',
11481                     cls: 'form-hidden-field'
11482                 },
11483                 {
11484                     tag: 'ul',
11485                     cls: 'select2-choices',
11486                     cn:[
11487                         {
11488                             tag: 'li',
11489                             cls: 'select2-search-field',
11490                             cn: [
11491
11492                                 buttons
11493                             ]
11494                         }
11495                     ]
11496                 }
11497             ]
11498         }
11499         
11500         var combobox = {
11501             cls: 'select2-container input-group select2-container-multi',
11502             cn: [
11503                 box
11504 //                {
11505 //                    tag: 'ul',
11506 //                    cls: 'typeahead typeahead-long dropdown-menu',
11507 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11508 //                }
11509             ]
11510         };
11511         
11512         if(this.hasFeedback && !this.allowBlank){
11513             
11514             var feedback = {
11515                 tag: 'span',
11516                 cls: 'glyphicon form-control-feedback'
11517             };
11518
11519             combobox.cn.push(feedback);
11520         }
11521         
11522         if (align ==='left' && this.fieldLabel.length) {
11523             
11524                 Roo.log("left and has label");
11525                 cfg.cn = [
11526                     
11527                     {
11528                         tag: 'label',
11529                         'for' :  id,
11530                         cls : 'control-label col-sm-' + this.labelWidth,
11531                         html : this.fieldLabel
11532                         
11533                     },
11534                     {
11535                         cls : "col-sm-" + (12 - this.labelWidth), 
11536                         cn: [
11537                             combobox
11538                         ]
11539                     }
11540                     
11541                 ];
11542         } else if ( this.fieldLabel.length) {
11543                 Roo.log(" label");
11544                  cfg.cn = [
11545                    
11546                     {
11547                         tag: 'label',
11548                         //cls : 'input-group-addon',
11549                         html : this.fieldLabel
11550                         
11551                     },
11552                     
11553                     combobox
11554                     
11555                 ];
11556
11557         } else {
11558             
11559                 Roo.log(" no label && no align");
11560                 cfg = combobox
11561                      
11562                 
11563         }
11564          
11565         var settings=this;
11566         ['xs','sm','md','lg'].map(function(size){
11567             if (settings[size]) {
11568                 cfg.cls += ' col-' + size + '-' + settings[size];
11569             }
11570         });
11571         
11572         return cfg;
11573         
11574     },
11575     
11576     _initEventsCalled : false,
11577     
11578     // private
11579     initEvents: function()
11580     {
11581         
11582         if (this._initEventsCalled) { // as we call render... prevent looping...
11583             return;
11584         }
11585         this._initEventsCalled = true;
11586         
11587         if (!this.store) {
11588             throw "can not find store for combo";
11589         }
11590         
11591         this.store = Roo.factory(this.store, Roo.data);
11592         
11593         // if we are building from html. then this element is so complex, that we can not really
11594         // use the rendered HTML.
11595         // so we have to trash and replace the previous code.
11596         if (Roo.XComponent.build_from_html) {
11597             
11598             // remove this element....
11599             var e = this.el.dom, k=0;
11600             while (e ) { e = e.previousSibling;  ++k;}
11601
11602             this.el.remove();
11603             
11604             this.el=false;
11605             this.rendered = false;
11606             
11607             this.render(this.parent().getChildContainer(true), k);
11608             
11609             
11610             
11611         }
11612         
11613         
11614         /*
11615          * Touch Devices
11616          */
11617         
11618         if(Roo.isTouch && this.mobileTouchView){
11619             this.initTouchView();
11620             return;
11621         }
11622         
11623         if(this.tickable){
11624             this.initTickableEvents();
11625             return;
11626         }
11627         
11628         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11629         
11630         if(this.hiddenName){
11631             
11632             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11633             
11634             this.hiddenField.dom.value =
11635                 this.hiddenValue !== undefined ? this.hiddenValue :
11636                 this.value !== undefined ? this.value : '';
11637
11638             // prevent input submission
11639             this.el.dom.removeAttribute('name');
11640             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11641              
11642              
11643         }
11644         //if(Roo.isGecko){
11645         //    this.el.dom.setAttribute('autocomplete', 'off');
11646         //}
11647         
11648         var cls = 'x-combo-list';
11649         
11650         //this.list = new Roo.Layer({
11651         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11652         //});
11653         
11654         var _this = this;
11655         
11656         (function(){
11657             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11658             _this.list.setWidth(lw);
11659         }).defer(100);
11660         
11661         this.list.on('mouseover', this.onViewOver, this);
11662         this.list.on('mousemove', this.onViewMove, this);
11663         
11664         this.list.on('scroll', this.onViewScroll, this);
11665         
11666         /*
11667         this.list.swallowEvent('mousewheel');
11668         this.assetHeight = 0;
11669
11670         if(this.title){
11671             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11672             this.assetHeight += this.header.getHeight();
11673         }
11674
11675         this.innerList = this.list.createChild({cls:cls+'-inner'});
11676         this.innerList.on('mouseover', this.onViewOver, this);
11677         this.innerList.on('mousemove', this.onViewMove, this);
11678         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11679         
11680         if(this.allowBlank && !this.pageSize && !this.disableClear){
11681             this.footer = this.list.createChild({cls:cls+'-ft'});
11682             this.pageTb = new Roo.Toolbar(this.footer);
11683            
11684         }
11685         if(this.pageSize){
11686             this.footer = this.list.createChild({cls:cls+'-ft'});
11687             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11688                     {pageSize: this.pageSize});
11689             
11690         }
11691         
11692         if (this.pageTb && this.allowBlank && !this.disableClear) {
11693             var _this = this;
11694             this.pageTb.add(new Roo.Toolbar.Fill(), {
11695                 cls: 'x-btn-icon x-btn-clear',
11696                 text: '&#160;',
11697                 handler: function()
11698                 {
11699                     _this.collapse();
11700                     _this.clearValue();
11701                     _this.onSelect(false, -1);
11702                 }
11703             });
11704         }
11705         if (this.footer) {
11706             this.assetHeight += this.footer.getHeight();
11707         }
11708         */
11709             
11710         if(!this.tpl){
11711             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11712         }
11713
11714         this.view = new Roo.View(this.list, this.tpl, {
11715             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11716         });
11717         //this.view.wrapEl.setDisplayed(false);
11718         this.view.on('click', this.onViewClick, this);
11719         
11720         
11721         
11722         this.store.on('beforeload', this.onBeforeLoad, this);
11723         this.store.on('load', this.onLoad, this);
11724         this.store.on('loadexception', this.onLoadException, this);
11725         /*
11726         if(this.resizable){
11727             this.resizer = new Roo.Resizable(this.list,  {
11728                pinned:true, handles:'se'
11729             });
11730             this.resizer.on('resize', function(r, w, h){
11731                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11732                 this.listWidth = w;
11733                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11734                 this.restrictHeight();
11735             }, this);
11736             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11737         }
11738         */
11739         if(!this.editable){
11740             this.editable = true;
11741             this.setEditable(false);
11742         }
11743         
11744         /*
11745         
11746         if (typeof(this.events.add.listeners) != 'undefined') {
11747             
11748             this.addicon = this.wrap.createChild(
11749                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11750        
11751             this.addicon.on('click', function(e) {
11752                 this.fireEvent('add', this);
11753             }, this);
11754         }
11755         if (typeof(this.events.edit.listeners) != 'undefined') {
11756             
11757             this.editicon = this.wrap.createChild(
11758                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11759             if (this.addicon) {
11760                 this.editicon.setStyle('margin-left', '40px');
11761             }
11762             this.editicon.on('click', function(e) {
11763                 
11764                 // we fire even  if inothing is selected..
11765                 this.fireEvent('edit', this, this.lastData );
11766                 
11767             }, this);
11768         }
11769         */
11770         
11771         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11772             "up" : function(e){
11773                 this.inKeyMode = true;
11774                 this.selectPrev();
11775             },
11776
11777             "down" : function(e){
11778                 if(!this.isExpanded()){
11779                     this.onTriggerClick();
11780                 }else{
11781                     this.inKeyMode = true;
11782                     this.selectNext();
11783                 }
11784             },
11785
11786             "enter" : function(e){
11787 //                this.onViewClick();
11788                 //return true;
11789                 this.collapse();
11790                 
11791                 if(this.fireEvent("specialkey", this, e)){
11792                     this.onViewClick(false);
11793                 }
11794                 
11795                 return true;
11796             },
11797
11798             "esc" : function(e){
11799                 this.collapse();
11800             },
11801
11802             "tab" : function(e){
11803                 this.collapse();
11804                 
11805                 if(this.fireEvent("specialkey", this, e)){
11806                     this.onViewClick(false);
11807                 }
11808                 
11809                 return true;
11810             },
11811
11812             scope : this,
11813
11814             doRelay : function(foo, bar, hname){
11815                 if(hname == 'down' || this.scope.isExpanded()){
11816                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11817                 }
11818                 return true;
11819             },
11820
11821             forceKeyDown: true
11822         });
11823         
11824         
11825         this.queryDelay = Math.max(this.queryDelay || 10,
11826                 this.mode == 'local' ? 10 : 250);
11827         
11828         
11829         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11830         
11831         if(this.typeAhead){
11832             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11833         }
11834         if(this.editable !== false){
11835             this.inputEl().on("keyup", this.onKeyUp, this);
11836         }
11837         if(this.forceSelection){
11838             this.inputEl().on('blur', this.doForce, this);
11839         }
11840         
11841         if(this.multiple){
11842             this.choices = this.el.select('ul.select2-choices', true).first();
11843             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11844         }
11845     },
11846     
11847     initTickableEvents: function()
11848     {   
11849         this.createList();
11850         
11851         if(this.hiddenName){
11852             
11853             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11854             
11855             this.hiddenField.dom.value =
11856                 this.hiddenValue !== undefined ? this.hiddenValue :
11857                 this.value !== undefined ? this.value : '';
11858
11859             // prevent input submission
11860             this.el.dom.removeAttribute('name');
11861             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11862              
11863              
11864         }
11865         
11866 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11867         
11868         this.choices = this.el.select('ul.select2-choices', true).first();
11869         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11870         if(this.triggerList){
11871             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11872         }
11873          
11874         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11875         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11876         
11877         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11878         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11879         
11880         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11881         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11882         
11883         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11884         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11885         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11886         
11887         this.okBtn.hide();
11888         this.cancelBtn.hide();
11889         
11890         var _this = this;
11891         
11892         (function(){
11893             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11894             _this.list.setWidth(lw);
11895         }).defer(100);
11896         
11897         this.list.on('mouseover', this.onViewOver, this);
11898         this.list.on('mousemove', this.onViewMove, this);
11899         
11900         this.list.on('scroll', this.onViewScroll, this);
11901         
11902         if(!this.tpl){
11903             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>';
11904         }
11905
11906         this.view = new Roo.View(this.list, this.tpl, {
11907             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11908         });
11909         
11910         //this.view.wrapEl.setDisplayed(false);
11911         this.view.on('click', this.onViewClick, this);
11912         
11913         
11914         
11915         this.store.on('beforeload', this.onBeforeLoad, this);
11916         this.store.on('load', this.onLoad, this);
11917         this.store.on('loadexception', this.onLoadException, this);
11918         
11919         if(this.editable){
11920             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
11921                 "up" : function(e){
11922                     this.inKeyMode = true;
11923                     this.selectPrev();
11924                 },
11925
11926                 "down" : function(e){
11927                     this.inKeyMode = true;
11928                     this.selectNext();
11929                 },
11930
11931                 "enter" : function(e){
11932                     if(this.fireEvent("specialkey", this, e)){
11933                         this.onViewClick(false);
11934                     }
11935                     
11936                     return true;
11937                 },
11938
11939                 "esc" : function(e){
11940                     this.onTickableFooterButtonClick(e, false, false);
11941                 },
11942
11943                 "tab" : function(e){
11944                     this.fireEvent("specialkey", this, e);
11945                     
11946                     this.onTickableFooterButtonClick(e, false, false);
11947                     
11948                     return true;
11949                 },
11950
11951                 scope : this,
11952
11953                 doRelay : function(e, fn, key){
11954                     if(this.scope.isExpanded()){
11955                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11956                     }
11957                     return true;
11958                 },
11959
11960                 forceKeyDown: true
11961             });
11962         }
11963         
11964         this.queryDelay = Math.max(this.queryDelay || 10,
11965                 this.mode == 'local' ? 10 : 250);
11966         
11967         
11968         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11969         
11970         if(this.typeAhead){
11971             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11972         }
11973         
11974         if(this.editable !== false){
11975             this.tickableInputEl().on("keyup", this.onKeyUp, this);
11976         }
11977         
11978     },
11979
11980     onDestroy : function(){
11981         if(this.view){
11982             this.view.setStore(null);
11983             this.view.el.removeAllListeners();
11984             this.view.el.remove();
11985             this.view.purgeListeners();
11986         }
11987         if(this.list){
11988             this.list.dom.innerHTML  = '';
11989         }
11990         
11991         if(this.store){
11992             this.store.un('beforeload', this.onBeforeLoad, this);
11993             this.store.un('load', this.onLoad, this);
11994             this.store.un('loadexception', this.onLoadException, this);
11995         }
11996         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11997     },
11998
11999     // private
12000     fireKey : function(e){
12001         if(e.isNavKeyPress() && !this.list.isVisible()){
12002             this.fireEvent("specialkey", this, e);
12003         }
12004     },
12005
12006     // private
12007     onResize: function(w, h){
12008 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12009 //        
12010 //        if(typeof w != 'number'){
12011 //            // we do not handle it!?!?
12012 //            return;
12013 //        }
12014 //        var tw = this.trigger.getWidth();
12015 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12016 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12017 //        var x = w - tw;
12018 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12019 //            
12020 //        //this.trigger.setStyle('left', x+'px');
12021 //        
12022 //        if(this.list && this.listWidth === undefined){
12023 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12024 //            this.list.setWidth(lw);
12025 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12026 //        }
12027         
12028     
12029         
12030     },
12031
12032     /**
12033      * Allow or prevent the user from directly editing the field text.  If false is passed,
12034      * the user will only be able to select from the items defined in the dropdown list.  This method
12035      * is the runtime equivalent of setting the 'editable' config option at config time.
12036      * @param {Boolean} value True to allow the user to directly edit the field text
12037      */
12038     setEditable : function(value){
12039         if(value == this.editable){
12040             return;
12041         }
12042         this.editable = value;
12043         if(!value){
12044             this.inputEl().dom.setAttribute('readOnly', true);
12045             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12046             this.inputEl().addClass('x-combo-noedit');
12047         }else{
12048             this.inputEl().dom.setAttribute('readOnly', false);
12049             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12050             this.inputEl().removeClass('x-combo-noedit');
12051         }
12052     },
12053
12054     // private
12055     
12056     onBeforeLoad : function(combo,opts){
12057         if(!this.hasFocus){
12058             return;
12059         }
12060          if (!opts.add) {
12061             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12062          }
12063         this.restrictHeight();
12064         this.selectedIndex = -1;
12065     },
12066
12067     // private
12068     onLoad : function(){
12069         
12070         this.hasQuery = false;
12071         
12072         if(!this.hasFocus){
12073             return;
12074         }
12075         
12076         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12077             this.loading.hide();
12078         }
12079              
12080         if(this.store.getCount() > 0){
12081             this.expand();
12082             this.restrictHeight();
12083             if(this.lastQuery == this.allQuery){
12084                 if(this.editable && !this.tickable){
12085                     this.inputEl().dom.select();
12086                 }
12087                 
12088                 if(
12089                     !this.selectByValue(this.value, true) &&
12090                     this.autoFocus && 
12091                     (
12092                         !this.store.lastOptions ||
12093                         typeof(this.store.lastOptions.add) == 'undefined' || 
12094                         this.store.lastOptions.add != true
12095                     )
12096                 ){
12097                     this.select(0, true);
12098                 }
12099             }else{
12100                 if(this.autoFocus){
12101                     this.selectNext();
12102                 }
12103                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12104                     this.taTask.delay(this.typeAheadDelay);
12105                 }
12106             }
12107         }else{
12108             this.onEmptyResults();
12109         }
12110         
12111         //this.el.focus();
12112     },
12113     // private
12114     onLoadException : function()
12115     {
12116         this.hasQuery = false;
12117         
12118         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12119             this.loading.hide();
12120         }
12121         
12122         if(this.tickable && this.editable){
12123             return;
12124         }
12125         
12126         this.collapse();
12127         
12128         Roo.log(this.store.reader.jsonData);
12129         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12130             // fixme
12131             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12132         }
12133         
12134         
12135     },
12136     // private
12137     onTypeAhead : function(){
12138         if(this.store.getCount() > 0){
12139             var r = this.store.getAt(0);
12140             var newValue = r.data[this.displayField];
12141             var len = newValue.length;
12142             var selStart = this.getRawValue().length;
12143             
12144             if(selStart != len){
12145                 this.setRawValue(newValue);
12146                 this.selectText(selStart, newValue.length);
12147             }
12148         }
12149     },
12150
12151     // private
12152     onSelect : function(record, index){
12153         
12154         if(this.fireEvent('beforeselect', this, record, index) !== false){
12155         
12156             this.setFromData(index > -1 ? record.data : false);
12157             
12158             this.collapse();
12159             this.fireEvent('select', this, record, index);
12160         }
12161     },
12162
12163     /**
12164      * Returns the currently selected field value or empty string if no value is set.
12165      * @return {String} value The selected value
12166      */
12167     getValue : function(){
12168         
12169         if(this.multiple){
12170             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12171         }
12172         
12173         if(this.valueField){
12174             return typeof this.value != 'undefined' ? this.value : '';
12175         }else{
12176             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12177         }
12178     },
12179
12180     /**
12181      * Clears any text/value currently set in the field
12182      */
12183     clearValue : function(){
12184         if(this.hiddenField){
12185             this.hiddenField.dom.value = '';
12186         }
12187         this.value = '';
12188         this.setRawValue('');
12189         this.lastSelectionText = '';
12190         this.lastData = false;
12191         
12192         var close = this.closeTriggerEl();
12193         
12194         if(close){
12195             close.hide();
12196         }
12197         
12198     },
12199
12200     /**
12201      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12202      * will be displayed in the field.  If the value does not match the data value of an existing item,
12203      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12204      * Otherwise the field will be blank (although the value will still be set).
12205      * @param {String} value The value to match
12206      */
12207     setValue : function(v){
12208         if(this.multiple){
12209             this.syncValue();
12210             return;
12211         }
12212         
12213         var text = v;
12214         if(this.valueField){
12215             var r = this.findRecord(this.valueField, v);
12216             if(r){
12217                 text = r.data[this.displayField];
12218             }else if(this.valueNotFoundText !== undefined){
12219                 text = this.valueNotFoundText;
12220             }
12221         }
12222         this.lastSelectionText = text;
12223         if(this.hiddenField){
12224             this.hiddenField.dom.value = v;
12225         }
12226         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12227         this.value = v;
12228         
12229         var close = this.closeTriggerEl();
12230         
12231         if(close){
12232             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12233         }
12234     },
12235     /**
12236      * @property {Object} the last set data for the element
12237      */
12238     
12239     lastData : false,
12240     /**
12241      * Sets the value of the field based on a object which is related to the record format for the store.
12242      * @param {Object} value the value to set as. or false on reset?
12243      */
12244     setFromData : function(o){
12245         
12246         if(this.multiple){
12247             this.addItem(o);
12248             return;
12249         }
12250             
12251         var dv = ''; // display value
12252         var vv = ''; // value value..
12253         this.lastData = o;
12254         if (this.displayField) {
12255             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12256         } else {
12257             // this is an error condition!!!
12258             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12259         }
12260         
12261         if(this.valueField){
12262             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12263         }
12264         
12265         var close = this.closeTriggerEl();
12266         
12267         if(close){
12268             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12269         }
12270         
12271         if(this.hiddenField){
12272             this.hiddenField.dom.value = vv;
12273             
12274             this.lastSelectionText = dv;
12275             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12276             this.value = vv;
12277             return;
12278         }
12279         // no hidden field.. - we store the value in 'value', but still display
12280         // display field!!!!
12281         this.lastSelectionText = dv;
12282         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12283         this.value = vv;
12284         
12285         
12286         
12287     },
12288     // private
12289     reset : function(){
12290         // overridden so that last data is reset..
12291         
12292         if(this.multiple){
12293             this.clearItem();
12294             return;
12295         }
12296         
12297         this.setValue(this.originalValue);
12298         this.clearInvalid();
12299         this.lastData = false;
12300         if (this.view) {
12301             this.view.clearSelections();
12302         }
12303     },
12304     // private
12305     findRecord : function(prop, value){
12306         var record;
12307         if(this.store.getCount() > 0){
12308             this.store.each(function(r){
12309                 if(r.data[prop] == value){
12310                     record = r;
12311                     return false;
12312                 }
12313                 return true;
12314             });
12315         }
12316         return record;
12317     },
12318     
12319     getName: function()
12320     {
12321         // returns hidden if it's set..
12322         if (!this.rendered) {return ''};
12323         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12324         
12325     },
12326     // private
12327     onViewMove : function(e, t){
12328         this.inKeyMode = false;
12329     },
12330
12331     // private
12332     onViewOver : function(e, t){
12333         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12334             return;
12335         }
12336         var item = this.view.findItemFromChild(t);
12337         
12338         if(item){
12339             var index = this.view.indexOf(item);
12340             this.select(index, false);
12341         }
12342     },
12343
12344     // private
12345     onViewClick : function(view, doFocus, el, e)
12346     {
12347         var index = this.view.getSelectedIndexes()[0];
12348         
12349         var r = this.store.getAt(index);
12350         
12351         if(this.tickable){
12352             
12353             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12354                 return;
12355             }
12356             
12357             var rm = false;
12358             var _this = this;
12359             
12360             Roo.each(this.tickItems, function(v,k){
12361                 
12362                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12363                     _this.tickItems.splice(k, 1);
12364                     
12365                     if(typeof(e) == 'undefined' && view == false){
12366                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12367                     }
12368                     
12369                     rm = true;
12370                     return;
12371                 }
12372             });
12373             
12374             if(rm){
12375                 return;
12376             }
12377             
12378             this.tickItems.push(r.data);
12379             
12380             if(typeof(e) == 'undefined' && view == false){
12381                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12382             }
12383                     
12384             return;
12385         }
12386         
12387         if(r){
12388             this.onSelect(r, index);
12389         }
12390         if(doFocus !== false && !this.blockFocus){
12391             this.inputEl().focus();
12392         }
12393     },
12394
12395     // private
12396     restrictHeight : function(){
12397         //this.innerList.dom.style.height = '';
12398         //var inner = this.innerList.dom;
12399         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12400         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12401         //this.list.beginUpdate();
12402         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12403         this.list.alignTo(this.inputEl(), this.listAlign);
12404         this.list.alignTo(this.inputEl(), this.listAlign);
12405         //this.list.endUpdate();
12406     },
12407
12408     // private
12409     onEmptyResults : function(){
12410         
12411         if(this.tickable && this.editable){
12412             this.restrictHeight();
12413             return;
12414         }
12415         
12416         this.collapse();
12417     },
12418
12419     /**
12420      * Returns true if the dropdown list is expanded, else false.
12421      */
12422     isExpanded : function(){
12423         return this.list.isVisible();
12424     },
12425
12426     /**
12427      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12428      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12429      * @param {String} value The data value of the item to select
12430      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12431      * selected item if it is not currently in view (defaults to true)
12432      * @return {Boolean} True if the value matched an item in the list, else false
12433      */
12434     selectByValue : function(v, scrollIntoView){
12435         if(v !== undefined && v !== null){
12436             var r = this.findRecord(this.valueField || this.displayField, v);
12437             if(r){
12438                 this.select(this.store.indexOf(r), scrollIntoView);
12439                 return true;
12440             }
12441         }
12442         return false;
12443     },
12444
12445     /**
12446      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12447      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12448      * @param {Number} index The zero-based index of the list item to select
12449      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12450      * selected item if it is not currently in view (defaults to true)
12451      */
12452     select : function(index, scrollIntoView){
12453         this.selectedIndex = index;
12454         this.view.select(index);
12455         if(scrollIntoView !== false){
12456             var el = this.view.getNode(index);
12457             /*
12458              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12459              */
12460             if(el){
12461                 this.list.scrollChildIntoView(el, false);
12462             }
12463         }
12464     },
12465
12466     // private
12467     selectNext : function(){
12468         var ct = this.store.getCount();
12469         if(ct > 0){
12470             if(this.selectedIndex == -1){
12471                 this.select(0);
12472             }else if(this.selectedIndex < ct-1){
12473                 this.select(this.selectedIndex+1);
12474             }
12475         }
12476     },
12477
12478     // private
12479     selectPrev : function(){
12480         var ct = this.store.getCount();
12481         if(ct > 0){
12482             if(this.selectedIndex == -1){
12483                 this.select(0);
12484             }else if(this.selectedIndex != 0){
12485                 this.select(this.selectedIndex-1);
12486             }
12487         }
12488     },
12489
12490     // private
12491     onKeyUp : function(e){
12492         if(this.editable !== false && !e.isSpecialKey()){
12493             this.lastKey = e.getKey();
12494             this.dqTask.delay(this.queryDelay);
12495         }
12496     },
12497
12498     // private
12499     validateBlur : function(){
12500         return !this.list || !this.list.isVisible();   
12501     },
12502
12503     // private
12504     initQuery : function(){
12505         
12506         var v = this.getRawValue();
12507         
12508         if(this.tickable && this.editable){
12509             v = this.tickableInputEl().getValue();
12510         }
12511         
12512         this.doQuery(v);
12513     },
12514
12515     // private
12516     doForce : function(){
12517         if(this.inputEl().dom.value.length > 0){
12518             this.inputEl().dom.value =
12519                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12520              
12521         }
12522     },
12523
12524     /**
12525      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12526      * query allowing the query action to be canceled if needed.
12527      * @param {String} query The SQL query to execute
12528      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12529      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12530      * saved in the current store (defaults to false)
12531      */
12532     doQuery : function(q, forceAll){
12533         
12534         if(q === undefined || q === null){
12535             q = '';
12536         }
12537         var qe = {
12538             query: q,
12539             forceAll: forceAll,
12540             combo: this,
12541             cancel:false
12542         };
12543         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12544             return false;
12545         }
12546         q = qe.query;
12547         
12548         forceAll = qe.forceAll;
12549         if(forceAll === true || (q.length >= this.minChars)){
12550             
12551             this.hasQuery = true;
12552             
12553             if(this.lastQuery != q || this.alwaysQuery){
12554                 this.lastQuery = q;
12555                 if(this.mode == 'local'){
12556                     this.selectedIndex = -1;
12557                     if(forceAll){
12558                         this.store.clearFilter();
12559                     }else{
12560                         
12561                         if(this.specialFilter){
12562                             this.fireEvent('specialfilter', this);
12563                             this.onLoad();
12564                             return;
12565                         }
12566                         
12567                         this.store.filter(this.displayField, q);
12568                     }
12569                     
12570                     this.store.fireEvent("datachanged", this.store);
12571                     
12572                     this.onLoad();
12573                     
12574                     
12575                 }else{
12576                     
12577                     this.store.baseParams[this.queryParam] = q;
12578                     
12579                     var options = {params : this.getParams(q)};
12580                     
12581                     if(this.loadNext){
12582                         options.add = true;
12583                         options.params.start = this.page * this.pageSize;
12584                     }
12585                     
12586                     this.store.load(options);
12587                     
12588                     /*
12589                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12590                      *  we should expand the list on onLoad
12591                      *  so command out it
12592                      */
12593 //                    this.expand();
12594                 }
12595             }else{
12596                 this.selectedIndex = -1;
12597                 this.onLoad();   
12598             }
12599         }
12600         
12601         this.loadNext = false;
12602     },
12603     
12604     // private
12605     getParams : function(q){
12606         var p = {};
12607         //p[this.queryParam] = q;
12608         
12609         if(this.pageSize){
12610             p.start = 0;
12611             p.limit = this.pageSize;
12612         }
12613         return p;
12614     },
12615
12616     /**
12617      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12618      */
12619     collapse : function(){
12620         if(!this.isExpanded()){
12621             return;
12622         }
12623         
12624         this.list.hide();
12625         
12626         if(this.tickable){
12627             this.hasFocus = false;
12628             this.okBtn.hide();
12629             this.cancelBtn.hide();
12630             this.trigger.show();
12631             
12632             if(this.editable){
12633                 this.tickableInputEl().dom.value = '';
12634                 this.tickableInputEl().blur();
12635             }
12636             
12637         }
12638         
12639         Roo.get(document).un('mousedown', this.collapseIf, this);
12640         Roo.get(document).un('mousewheel', this.collapseIf, this);
12641         if (!this.editable) {
12642             Roo.get(document).un('keydown', this.listKeyPress, this);
12643         }
12644         this.fireEvent('collapse', this);
12645     },
12646
12647     // private
12648     collapseIf : function(e){
12649         var in_combo  = e.within(this.el);
12650         var in_list =  e.within(this.list);
12651         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12652         
12653         if (in_combo || in_list || is_list) {
12654             //e.stopPropagation();
12655             return;
12656         }
12657         
12658         if(this.tickable){
12659             this.onTickableFooterButtonClick(e, false, false);
12660         }
12661
12662         this.collapse();
12663         
12664     },
12665
12666     /**
12667      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12668      */
12669     expand : function(){
12670        
12671         if(this.isExpanded() || !this.hasFocus){
12672             return;
12673         }
12674         
12675         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12676         this.list.setWidth(lw);
12677         
12678         
12679          Roo.log('expand');
12680         
12681         this.list.show();
12682         
12683         this.restrictHeight();
12684         
12685         if(this.tickable){
12686             
12687             this.tickItems = Roo.apply([], this.item);
12688             
12689             this.okBtn.show();
12690             this.cancelBtn.show();
12691             this.trigger.hide();
12692             
12693             if(this.editable){
12694                 this.tickableInputEl().focus();
12695             }
12696             
12697         }
12698         
12699         Roo.get(document).on('mousedown', this.collapseIf, this);
12700         Roo.get(document).on('mousewheel', this.collapseIf, this);
12701         if (!this.editable) {
12702             Roo.get(document).on('keydown', this.listKeyPress, this);
12703         }
12704         
12705         this.fireEvent('expand', this);
12706     },
12707
12708     // private
12709     // Implements the default empty TriggerField.onTriggerClick function
12710     onTriggerClick : function(e)
12711     {
12712         Roo.log('trigger click');
12713         
12714         if(this.disabled || !this.triggerList){
12715             return;
12716         }
12717         
12718         this.page = 0;
12719         this.loadNext = false;
12720         
12721         if(this.isExpanded()){
12722             this.collapse();
12723             if (!this.blockFocus) {
12724                 this.inputEl().focus();
12725             }
12726             
12727         }else {
12728             this.hasFocus = true;
12729             if(this.triggerAction == 'all') {
12730                 this.doQuery(this.allQuery, true);
12731             } else {
12732                 this.doQuery(this.getRawValue());
12733             }
12734             if (!this.blockFocus) {
12735                 this.inputEl().focus();
12736             }
12737         }
12738     },
12739     
12740     onTickableTriggerClick : function(e)
12741     {
12742         if(this.disabled){
12743             return;
12744         }
12745         
12746         this.page = 0;
12747         this.loadNext = false;
12748         this.hasFocus = true;
12749         
12750         if(this.triggerAction == 'all') {
12751             this.doQuery(this.allQuery, true);
12752         } else {
12753             this.doQuery(this.getRawValue());
12754         }
12755     },
12756     
12757     onSearchFieldClick : function(e)
12758     {
12759         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12760             this.onTickableFooterButtonClick(e, false, false);
12761             return;
12762         }
12763         
12764         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12765             return;
12766         }
12767         
12768         this.page = 0;
12769         this.loadNext = false;
12770         this.hasFocus = true;
12771         
12772         if(this.triggerAction == 'all') {
12773             this.doQuery(this.allQuery, true);
12774         } else {
12775             this.doQuery(this.getRawValue());
12776         }
12777     },
12778     
12779     listKeyPress : function(e)
12780     {
12781         //Roo.log('listkeypress');
12782         // scroll to first matching element based on key pres..
12783         if (e.isSpecialKey()) {
12784             return false;
12785         }
12786         var k = String.fromCharCode(e.getKey()).toUpperCase();
12787         //Roo.log(k);
12788         var match  = false;
12789         var csel = this.view.getSelectedNodes();
12790         var cselitem = false;
12791         if (csel.length) {
12792             var ix = this.view.indexOf(csel[0]);
12793             cselitem  = this.store.getAt(ix);
12794             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12795                 cselitem = false;
12796             }
12797             
12798         }
12799         
12800         this.store.each(function(v) { 
12801             if (cselitem) {
12802                 // start at existing selection.
12803                 if (cselitem.id == v.id) {
12804                     cselitem = false;
12805                 }
12806                 return true;
12807             }
12808                 
12809             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12810                 match = this.store.indexOf(v);
12811                 return false;
12812             }
12813             return true;
12814         }, this);
12815         
12816         if (match === false) {
12817             return true; // no more action?
12818         }
12819         // scroll to?
12820         this.view.select(match);
12821         var sn = Roo.get(this.view.getSelectedNodes()[0])
12822         sn.scrollIntoView(sn.dom.parentNode, false);
12823     },
12824     
12825     onViewScroll : function(e, t){
12826         
12827         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){
12828             return;
12829         }
12830         
12831         this.hasQuery = true;
12832         
12833         this.loading = this.list.select('.loading', true).first();
12834         
12835         if(this.loading === null){
12836             this.list.createChild({
12837                 tag: 'div',
12838                 cls: 'loading select2-more-results select2-active',
12839                 html: 'Loading more results...'
12840             })
12841             
12842             this.loading = this.list.select('.loading', true).first();
12843             
12844             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12845             
12846             this.loading.hide();
12847         }
12848         
12849         this.loading.show();
12850         
12851         var _combo = this;
12852         
12853         this.page++;
12854         this.loadNext = true;
12855         
12856         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12857         
12858         return;
12859     },
12860     
12861     addItem : function(o)
12862     {   
12863         var dv = ''; // display value
12864         
12865         if (this.displayField) {
12866             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12867         } else {
12868             // this is an error condition!!!
12869             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12870         }
12871         
12872         if(!dv.length){
12873             return;
12874         }
12875         
12876         var choice = this.choices.createChild({
12877             tag: 'li',
12878             cls: 'select2-search-choice',
12879             cn: [
12880                 {
12881                     tag: 'div',
12882                     html: dv
12883                 },
12884                 {
12885                     tag: 'a',
12886                     href: '#',
12887                     cls: 'select2-search-choice-close',
12888                     tabindex: '-1'
12889                 }
12890             ]
12891             
12892         }, this.searchField);
12893         
12894         var close = choice.select('a.select2-search-choice-close', true).first()
12895         
12896         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12897         
12898         this.item.push(o);
12899         
12900         this.lastData = o;
12901         
12902         this.syncValue();
12903         
12904         this.inputEl().dom.value = '';
12905         
12906         this.validate();
12907     },
12908     
12909     onRemoveItem : function(e, _self, o)
12910     {
12911         e.preventDefault();
12912         
12913         this.lastItem = Roo.apply([], this.item);
12914         
12915         var index = this.item.indexOf(o.data) * 1;
12916         
12917         if( index < 0){
12918             Roo.log('not this item?!');
12919             return;
12920         }
12921         
12922         this.item.splice(index, 1);
12923         o.item.remove();
12924         
12925         this.syncValue();
12926         
12927         this.fireEvent('remove', this, e);
12928         
12929         this.validate();
12930         
12931     },
12932     
12933     syncValue : function()
12934     {
12935         if(!this.item.length){
12936             this.clearValue();
12937             return;
12938         }
12939             
12940         var value = [];
12941         var _this = this;
12942         Roo.each(this.item, function(i){
12943             if(_this.valueField){
12944                 value.push(i[_this.valueField]);
12945                 return;
12946             }
12947
12948             value.push(i);
12949         });
12950
12951         this.value = value.join(',');
12952
12953         if(this.hiddenField){
12954             this.hiddenField.dom.value = this.value;
12955         }
12956         
12957         this.store.fireEvent("datachanged", this.store);
12958     },
12959     
12960     clearItem : function()
12961     {
12962         if(!this.multiple){
12963             return;
12964         }
12965         
12966         this.item = [];
12967         
12968         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
12969            c.remove();
12970         });
12971         
12972         this.syncValue();
12973         
12974         this.validate();
12975         
12976         if(this.tickable && !Roo.isTouch){
12977             this.view.refresh();
12978         }
12979     },
12980     
12981     inputEl: function ()
12982     {
12983         if(Roo.isTouch && this.mobileTouchView){
12984             return this.el.select('input.form-control',true).first();
12985         }
12986         
12987         if(this.tickable){
12988             return this.searchField;
12989         }
12990         
12991         return this.el.select('input.form-control',true).first();
12992     },
12993     
12994     
12995     onTickableFooterButtonClick : function(e, btn, el)
12996     {
12997         e.preventDefault();
12998         
12999         this.lastItem = Roo.apply([], this.item);
13000         
13001         if(btn && btn.name == 'cancel'){
13002             this.tickItems = Roo.apply([], this.item);
13003             this.collapse();
13004             return;
13005         }
13006         
13007         this.clearItem();
13008         
13009         var _this = this;
13010         
13011         Roo.each(this.tickItems, function(o){
13012             _this.addItem(o);
13013         });
13014         
13015         this.collapse();
13016         
13017     },
13018     
13019     validate : function()
13020     {
13021         var v = this.getRawValue();
13022         
13023         if(this.multiple){
13024             v = this.getValue();
13025         }
13026         
13027         if(this.disabled || this.allowBlank || v.length){
13028             this.markValid();
13029             return true;
13030         }
13031         
13032         this.markInvalid();
13033         return false;
13034     },
13035     
13036     tickableInputEl : function()
13037     {
13038         if(!this.tickable || !this.editable){
13039             return this.inputEl();
13040         }
13041         
13042         return this.inputEl().select('.select2-search-field-input', true).first();
13043     },
13044     
13045     
13046     getAutoCreateTouchView : function()
13047     {
13048         var id = Roo.id();
13049         
13050         var cfg = {
13051             cls: 'form-group' //input-group
13052         };
13053         
13054         var input =  {
13055             tag: 'input',
13056             id : id,
13057             type : this.inputType,
13058             cls : 'form-control x-combo-noedit',
13059             autocomplete: 'new-password',
13060             placeholder : this.placeholder || '',
13061             readonly : true
13062         };
13063         
13064         if (this.name) {
13065             input.name = this.name;
13066         }
13067         
13068         if (this.size) {
13069             input.cls += ' input-' + this.size;
13070         }
13071         
13072         if (this.disabled) {
13073             input.disabled = true;
13074         }
13075         
13076         var inputblock = {
13077             cls : '',
13078             cn : [
13079                 input
13080             ]
13081         };
13082         
13083         if(this.before){
13084             inputblock.cls += ' input-group';
13085             
13086             inputblock.cn.unshift({
13087                 tag :'span',
13088                 cls : 'input-group-addon',
13089                 html : this.before
13090             });
13091         }
13092         
13093         if(this.removable && !this.multiple){
13094             inputblock.cls += ' roo-removable';
13095             
13096             inputblock.cn.push({
13097                 tag: 'button',
13098                 html : 'x',
13099                 cls : 'roo-combo-removable-btn close'
13100             });
13101         }
13102
13103         if(this.hasFeedback && !this.allowBlank){
13104             
13105             inputblock.cls += ' has-feedback';
13106             
13107             inputblock.cn.push({
13108                 tag: 'span',
13109                 cls: 'glyphicon form-control-feedback'
13110             });
13111             
13112         }
13113         
13114         if (this.after) {
13115             
13116             inputblock.cls += (this.before) ? '' : ' input-group';
13117             
13118             inputblock.cn.push({
13119                 tag :'span',
13120                 cls : 'input-group-addon',
13121                 html : this.after
13122             });
13123         }
13124
13125         var box = {
13126             tag: 'div',
13127             cn: [
13128                 {
13129                     tag: 'input',
13130                     type : 'hidden',
13131                     cls: 'form-hidden-field'
13132                 },
13133                 inputblock
13134             ]
13135             
13136         };
13137         
13138         if(this.multiple){
13139             box = {
13140                 tag: 'div',
13141                 cn: [
13142                     {
13143                         tag: 'input',
13144                         type : 'hidden',
13145                         cls: 'form-hidden-field'
13146                     },
13147                     {
13148                         tag: 'ul',
13149                         cls: 'select2-choices',
13150                         cn:[
13151                             {
13152                                 tag: 'li',
13153                                 cls: 'select2-search-field',
13154                                 cn: [
13155
13156                                     inputblock
13157                                 ]
13158                             }
13159                         ]
13160                     }
13161                 ]
13162             }
13163         };
13164         
13165         var combobox = {
13166             cls: 'select2-container input-group',
13167             cn: [
13168                 box
13169             ]
13170         };
13171         
13172         if(this.multiple){
13173             combobox.cls += ' select2-container-multi';
13174         }
13175         
13176         var align = this.labelAlign || this.parentLabelAlign();
13177         
13178         cfg.cn = combobox;
13179         
13180         if(this.fieldLabel.length){
13181             
13182             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13183             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13184             
13185             cfg.cn = [
13186                 {
13187                     tag: 'label',
13188                     cls : 'control-label ' + lw,
13189                     html : this.fieldLabel
13190
13191                 },
13192                 {
13193                     cls : cw, 
13194                     cn: [
13195                         combobox
13196                     ]
13197                 }
13198             ];
13199         }
13200         
13201         var settings = this;
13202         
13203         ['xs','sm','md','lg'].map(function(size){
13204             if (settings[size]) {
13205                 cfg.cls += ' col-' + size + '-' + settings[size];
13206             }
13207         });
13208         
13209         return cfg;
13210     },
13211     
13212     initTouchView : function()
13213     {
13214         this.renderTouchView();
13215         
13216         this.touchViewEl.on('scroll', function(){
13217             this.el.dom.scrollTop = 0;
13218         }, this);
13219         
13220         this.inputEl().on("click", this.showTouchView, this);
13221         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13222         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13223         
13224         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13225         
13226         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13227         this.store.on('load', this.onTouchViewLoad, this);
13228         this.store.on('loadexception', this.onTouchViewLoadException, this);
13229         
13230         if(this.hiddenName){
13231             
13232             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13233             
13234             this.hiddenField.dom.value =
13235                 this.hiddenValue !== undefined ? this.hiddenValue :
13236                 this.value !== undefined ? this.value : '';
13237         
13238             this.el.dom.removeAttribute('name');
13239             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13240         }
13241         
13242         if(this.multiple){
13243             this.choices = this.el.select('ul.select2-choices', true).first();
13244             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13245         }
13246         
13247         if(this.removable && !this.multiple){
13248             var close = this.closeTriggerEl();
13249             if(close){
13250                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13251                 close.on('click', this.removeBtnClick, this, close);
13252             }
13253         }
13254         
13255         return;
13256         
13257         
13258     },
13259     
13260     renderTouchView : function()
13261     {
13262         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13263         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13264         
13265         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13266         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13267         
13268         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13269         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13270         this.touchViewBodyEl.setStyle('overflow', 'auto');
13271         
13272         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13273         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13274         
13275         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13276         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13277         
13278     },
13279     
13280     showTouchView : function()
13281     {
13282         this.touchViewHeaderEl.hide();
13283
13284         if(this.fieldLabel.length){
13285             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13286             this.touchViewHeaderEl.show();
13287         }
13288
13289         this.touchViewEl.show();
13290
13291         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13292         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13293
13294         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13295
13296         if(this.fieldLabel.length){
13297             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13298         }
13299         
13300         this.touchViewBodyEl.setHeight(bodyHeight);
13301
13302         if(this.animate){
13303             var _this = this;
13304             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13305         }else{
13306             this.touchViewEl.addClass('in');
13307         }
13308
13309         this.doTouchViewQuery();
13310         
13311     },
13312     
13313     hideTouchView : function()
13314     {
13315         this.touchViewEl.removeClass('in');
13316
13317         if(this.animate){
13318             var _this = this;
13319             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13320         }else{
13321             this.touchViewEl.setStyle('display', 'none');
13322         }
13323         
13324     },
13325     
13326     setTouchViewValue : function()
13327     {
13328         if(this.multiple){
13329             this.clearItem();
13330         
13331             var _this = this;
13332
13333             Roo.each(this.tickItems, function(o){
13334                 this.addItem(o);
13335             }, this);
13336         }
13337         
13338         this.hideTouchView();
13339     },
13340     
13341     doTouchViewQuery : function()
13342     {
13343         var qe = {
13344             query: '',
13345             forceAll: true,
13346             combo: this,
13347             cancel:false
13348         };
13349         
13350         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13351             return false;
13352         }
13353         
13354         if(!this.alwaysQuery || this.mode == 'local'){
13355             this.onTouchViewLoad();
13356             return;
13357         }
13358         
13359         this.store.load();
13360     },
13361     
13362     onTouchViewBeforeLoad : function(combo,opts)
13363     {
13364         return;
13365     },
13366
13367     // private
13368     onTouchViewLoad : function()
13369     {
13370         if(this.store.getCount() < 1){
13371             this.onTouchViewEmptyResults();
13372             return;
13373         }
13374         
13375         this.clearTouchView();
13376         
13377         var rawValue = this.getRawValue();
13378         
13379         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13380         
13381         this.tickItems = [];
13382         
13383         this.store.data.each(function(d, rowIndex){
13384             var row = this.touchViewListGroup.createChild(template);
13385             
13386             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13387                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13388             }
13389             
13390             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13391                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13392             }
13393             
13394             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13395                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13396                 this.tickItems.push(d.data);
13397             }
13398             
13399             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13400             
13401         }, this);
13402         
13403         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13404         
13405         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13406
13407         if(this.fieldLabel.length){
13408             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13409         }
13410
13411         var listHeight = this.touchViewListGroup.getHeight();
13412         
13413         var _this = this;
13414         
13415         if(firstChecked && listHeight > bodyHeight){
13416             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13417         }
13418         
13419     },
13420     
13421     onTouchViewLoadException : function()
13422     {
13423         this.hideTouchView();
13424     },
13425     
13426     onTouchViewEmptyResults : function()
13427     {
13428         this.clearTouchView();
13429         
13430         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13431         
13432         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13433         
13434     },
13435     
13436     clearTouchView : function()
13437     {
13438         this.touchViewListGroup.dom.innerHTML = '';
13439     },
13440     
13441     onTouchViewClick : function(e, el, o)
13442     {
13443         e.preventDefault();
13444         
13445         var row = o.row;
13446         var rowIndex = o.rowIndex;
13447         
13448         var r = this.store.getAt(rowIndex);
13449         
13450         if(!this.multiple){
13451             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13452                 c.dom.removeAttribute('checked');
13453             }, this);
13454             
13455             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13456         
13457             this.setFromData(r.data);
13458             
13459             var close = this.closeTriggerEl();
13460         
13461             if(close){
13462                 close.show();
13463             }
13464
13465             this.hideTouchView();
13466             
13467             this.fireEvent('select', this, r, rowIndex);
13468             
13469             return;
13470         }
13471         
13472         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13473             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13474             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13475             return;
13476         }
13477         
13478         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13479         this.addItem(r.data);
13480         this.tickItems.push(r.data);
13481         
13482     }
13483     
13484
13485     /** 
13486     * @cfg {Boolean} grow 
13487     * @hide 
13488     */
13489     /** 
13490     * @cfg {Number} growMin 
13491     * @hide 
13492     */
13493     /** 
13494     * @cfg {Number} growMax 
13495     * @hide 
13496     */
13497     /**
13498      * @hide
13499      * @method autoSize
13500      */
13501 });
13502
13503 Roo.apply(Roo.bootstrap.ComboBox,  {
13504     
13505     header : {
13506         tag: 'div',
13507         cls: 'modal-header',
13508         cn: [
13509             {
13510                 tag: 'h4',
13511                 cls: 'modal-title'
13512             }
13513         ]
13514     },
13515     
13516     body : {
13517         tag: 'div',
13518         cls: 'modal-body',
13519         cn: [
13520             {
13521                 tag: 'ul',
13522                 cls: 'list-group'
13523             }
13524         ]
13525     },
13526     
13527     listItemRadio : {
13528         tag: 'li',
13529         cls: 'list-group-item',
13530         cn: [
13531             {
13532                 tag: 'span',
13533                 cls: 'roo-combobox-list-group-item-value'
13534             },
13535             {
13536                 tag: 'div',
13537                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13538                 cn: [
13539                     {
13540                         tag: 'input',
13541                         type: 'radio'
13542                     },
13543                     {
13544                         tag: 'label'
13545                     }
13546                 ]
13547             }
13548         ]
13549     },
13550     
13551     listItemCheckbox : {
13552         tag: 'li',
13553         cls: 'list-group-item',
13554         cn: [
13555             {
13556                 tag: 'span',
13557                 cls: 'roo-combobox-list-group-item-value'
13558             },
13559             {
13560                 tag: 'div',
13561                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13562                 cn: [
13563                     {
13564                         tag: 'input',
13565                         type: 'checkbox'
13566                     },
13567                     {
13568                         tag: 'label'
13569                     }
13570                 ]
13571             }
13572         ]
13573     },
13574     
13575     emptyResult : {
13576         tag: 'div',
13577         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13578     },
13579     
13580     footer : {
13581         tag: 'div',
13582         cls: 'modal-footer',
13583         cn: [
13584             {
13585                 tag: 'div',
13586                 cls: 'row',
13587                 cn: [
13588                     {
13589                         tag: 'div',
13590                         cls: 'col-xs-6 text-left',
13591                         cn: {
13592                             tag: 'button',
13593                             cls: 'btn btn-danger roo-touch-view-cancel',
13594                             html: 'Cancel'
13595                         }
13596                     },
13597                     {
13598                         tag: 'div',
13599                         cls: 'col-xs-6 text-right',
13600                         cn: {
13601                             tag: 'button',
13602                             cls: 'btn btn-success roo-touch-view-ok',
13603                             html: 'OK'
13604                         }
13605                     }
13606                 ]
13607             }
13608         ]
13609         
13610     }
13611 });
13612
13613 Roo.apply(Roo.bootstrap.ComboBox,  {
13614     
13615     touchViewTemplate : {
13616         tag: 'div',
13617         cls: 'modal fade roo-combobox-touch-view',
13618         cn: [
13619             {
13620                 tag: 'div',
13621                 cls: 'modal-dialog',
13622                 cn: [
13623                     {
13624                         tag: 'div',
13625                         cls: 'modal-content',
13626                         cn: [
13627                             Roo.bootstrap.ComboBox.header,
13628                             Roo.bootstrap.ComboBox.body,
13629                             Roo.bootstrap.ComboBox.footer
13630                         ]
13631                     }
13632                 ]
13633             }
13634         ]
13635     }
13636 });/*
13637  * Based on:
13638  * Ext JS Library 1.1.1
13639  * Copyright(c) 2006-2007, Ext JS, LLC.
13640  *
13641  * Originally Released Under LGPL - original licence link has changed is not relivant.
13642  *
13643  * Fork - LGPL
13644  * <script type="text/javascript">
13645  */
13646
13647 /**
13648  * @class Roo.View
13649  * @extends Roo.util.Observable
13650  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13651  * This class also supports single and multi selection modes. <br>
13652  * Create a data model bound view:
13653  <pre><code>
13654  var store = new Roo.data.Store(...);
13655
13656  var view = new Roo.View({
13657     el : "my-element",
13658     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13659  
13660     singleSelect: true,
13661     selectedClass: "ydataview-selected",
13662     store: store
13663  });
13664
13665  // listen for node click?
13666  view.on("click", function(vw, index, node, e){
13667  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13668  });
13669
13670  // load XML data
13671  dataModel.load("foobar.xml");
13672  </code></pre>
13673  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13674  * <br><br>
13675  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13676  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13677  * 
13678  * Note: old style constructor is still suported (container, template, config)
13679  * 
13680  * @constructor
13681  * Create a new View
13682  * @param {Object} config The config object
13683  * 
13684  */
13685 Roo.View = function(config, depreciated_tpl, depreciated_config){
13686     
13687     this.parent = false;
13688     
13689     if (typeof(depreciated_tpl) == 'undefined') {
13690         // new way.. - universal constructor.
13691         Roo.apply(this, config);
13692         this.el  = Roo.get(this.el);
13693     } else {
13694         // old format..
13695         this.el  = Roo.get(config);
13696         this.tpl = depreciated_tpl;
13697         Roo.apply(this, depreciated_config);
13698     }
13699     this.wrapEl  = this.el.wrap().wrap();
13700     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13701     
13702     
13703     if(typeof(this.tpl) == "string"){
13704         this.tpl = new Roo.Template(this.tpl);
13705     } else {
13706         // support xtype ctors..
13707         this.tpl = new Roo.factory(this.tpl, Roo);
13708     }
13709     
13710     
13711     this.tpl.compile();
13712     
13713     /** @private */
13714     this.addEvents({
13715         /**
13716          * @event beforeclick
13717          * Fires before a click is processed. Returns false to cancel the default action.
13718          * @param {Roo.View} this
13719          * @param {Number} index The index of the target node
13720          * @param {HTMLElement} node The target node
13721          * @param {Roo.EventObject} e The raw event object
13722          */
13723             "beforeclick" : true,
13724         /**
13725          * @event click
13726          * Fires when a template node is clicked.
13727          * @param {Roo.View} this
13728          * @param {Number} index The index of the target node
13729          * @param {HTMLElement} node The target node
13730          * @param {Roo.EventObject} e The raw event object
13731          */
13732             "click" : true,
13733         /**
13734          * @event dblclick
13735          * Fires when a template node is double clicked.
13736          * @param {Roo.View} this
13737          * @param {Number} index The index of the target node
13738          * @param {HTMLElement} node The target node
13739          * @param {Roo.EventObject} e The raw event object
13740          */
13741             "dblclick" : true,
13742         /**
13743          * @event contextmenu
13744          * Fires when a template node is right clicked.
13745          * @param {Roo.View} this
13746          * @param {Number} index The index of the target node
13747          * @param {HTMLElement} node The target node
13748          * @param {Roo.EventObject} e The raw event object
13749          */
13750             "contextmenu" : true,
13751         /**
13752          * @event selectionchange
13753          * Fires when the selected nodes change.
13754          * @param {Roo.View} this
13755          * @param {Array} selections Array of the selected nodes
13756          */
13757             "selectionchange" : true,
13758     
13759         /**
13760          * @event beforeselect
13761          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13762          * @param {Roo.View} this
13763          * @param {HTMLElement} node The node to be selected
13764          * @param {Array} selections Array of currently selected nodes
13765          */
13766             "beforeselect" : true,
13767         /**
13768          * @event preparedata
13769          * Fires on every row to render, to allow you to change the data.
13770          * @param {Roo.View} this
13771          * @param {Object} data to be rendered (change this)
13772          */
13773           "preparedata" : true
13774           
13775           
13776         });
13777
13778
13779
13780     this.el.on({
13781         "click": this.onClick,
13782         "dblclick": this.onDblClick,
13783         "contextmenu": this.onContextMenu,
13784         scope:this
13785     });
13786
13787     this.selections = [];
13788     this.nodes = [];
13789     this.cmp = new Roo.CompositeElementLite([]);
13790     if(this.store){
13791         this.store = Roo.factory(this.store, Roo.data);
13792         this.setStore(this.store, true);
13793     }
13794     
13795     if ( this.footer && this.footer.xtype) {
13796            
13797          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13798         
13799         this.footer.dataSource = this.store
13800         this.footer.container = fctr;
13801         this.footer = Roo.factory(this.footer, Roo);
13802         fctr.insertFirst(this.el);
13803         
13804         // this is a bit insane - as the paging toolbar seems to detach the el..
13805 //        dom.parentNode.parentNode.parentNode
13806          // they get detached?
13807     }
13808     
13809     
13810     Roo.View.superclass.constructor.call(this);
13811     
13812     
13813 };
13814
13815 Roo.extend(Roo.View, Roo.util.Observable, {
13816     
13817      /**
13818      * @cfg {Roo.data.Store} store Data store to load data from.
13819      */
13820     store : false,
13821     
13822     /**
13823      * @cfg {String|Roo.Element} el The container element.
13824      */
13825     el : '',
13826     
13827     /**
13828      * @cfg {String|Roo.Template} tpl The template used by this View 
13829      */
13830     tpl : false,
13831     /**
13832      * @cfg {String} dataName the named area of the template to use as the data area
13833      *                          Works with domtemplates roo-name="name"
13834      */
13835     dataName: false,
13836     /**
13837      * @cfg {String} selectedClass The css class to add to selected nodes
13838      */
13839     selectedClass : "x-view-selected",
13840      /**
13841      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13842      */
13843     emptyText : "",
13844     
13845     /**
13846      * @cfg {String} text to display on mask (default Loading)
13847      */
13848     mask : false,
13849     /**
13850      * @cfg {Boolean} multiSelect Allow multiple selection
13851      */
13852     multiSelect : false,
13853     /**
13854      * @cfg {Boolean} singleSelect Allow single selection
13855      */
13856     singleSelect:  false,
13857     
13858     /**
13859      * @cfg {Boolean} toggleSelect - selecting 
13860      */
13861     toggleSelect : false,
13862     
13863     /**
13864      * @cfg {Boolean} tickable - selecting 
13865      */
13866     tickable : false,
13867     
13868     /**
13869      * Returns the element this view is bound to.
13870      * @return {Roo.Element}
13871      */
13872     getEl : function(){
13873         return this.wrapEl;
13874     },
13875     
13876     
13877
13878     /**
13879      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13880      */
13881     refresh : function(){
13882         //Roo.log('refresh');
13883         var t = this.tpl;
13884         
13885         // if we are using something like 'domtemplate', then
13886         // the what gets used is:
13887         // t.applySubtemplate(NAME, data, wrapping data..)
13888         // the outer template then get' applied with
13889         //     the store 'extra data'
13890         // and the body get's added to the
13891         //      roo-name="data" node?
13892         //      <span class='roo-tpl-{name}'></span> ?????
13893         
13894         
13895         
13896         this.clearSelections();
13897         this.el.update("");
13898         var html = [];
13899         var records = this.store.getRange();
13900         if(records.length < 1) {
13901             
13902             // is this valid??  = should it render a template??
13903             
13904             this.el.update(this.emptyText);
13905             return;
13906         }
13907         var el = this.el;
13908         if (this.dataName) {
13909             this.el.update(t.apply(this.store.meta)); //????
13910             el = this.el.child('.roo-tpl-' + this.dataName);
13911         }
13912         
13913         for(var i = 0, len = records.length; i < len; i++){
13914             var data = this.prepareData(records[i].data, i, records[i]);
13915             this.fireEvent("preparedata", this, data, i, records[i]);
13916             
13917             var d = Roo.apply({}, data);
13918             
13919             if(this.tickable){
13920                 Roo.apply(d, {'roo-id' : Roo.id()});
13921                 
13922                 var _this = this;
13923             
13924                 Roo.each(this.parent.item, function(item){
13925                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
13926                         return;
13927                     }
13928                     Roo.apply(d, {'roo-data-checked' : 'checked'});
13929                 });
13930             }
13931             
13932             html[html.length] = Roo.util.Format.trim(
13933                 this.dataName ?
13934                     t.applySubtemplate(this.dataName, d, this.store.meta) :
13935                     t.apply(d)
13936             );
13937         }
13938         
13939         
13940         
13941         el.update(html.join(""));
13942         this.nodes = el.dom.childNodes;
13943         this.updateIndexes(0);
13944     },
13945     
13946
13947     /**
13948      * Function to override to reformat the data that is sent to
13949      * the template for each node.
13950      * DEPRICATED - use the preparedata event handler.
13951      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
13952      * a JSON object for an UpdateManager bound view).
13953      */
13954     prepareData : function(data, index, record)
13955     {
13956         this.fireEvent("preparedata", this, data, index, record);
13957         return data;
13958     },
13959
13960     onUpdate : function(ds, record){
13961         // Roo.log('on update');   
13962         this.clearSelections();
13963         var index = this.store.indexOf(record);
13964         var n = this.nodes[index];
13965         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
13966         n.parentNode.removeChild(n);
13967         this.updateIndexes(index, index);
13968     },
13969
13970     
13971     
13972 // --------- FIXME     
13973     onAdd : function(ds, records, index)
13974     {
13975         //Roo.log(['on Add', ds, records, index] );        
13976         this.clearSelections();
13977         if(this.nodes.length == 0){
13978             this.refresh();
13979             return;
13980         }
13981         var n = this.nodes[index];
13982         for(var i = 0, len = records.length; i < len; i++){
13983             var d = this.prepareData(records[i].data, i, records[i]);
13984             if(n){
13985                 this.tpl.insertBefore(n, d);
13986             }else{
13987                 
13988                 this.tpl.append(this.el, d);
13989             }
13990         }
13991         this.updateIndexes(index);
13992     },
13993
13994     onRemove : function(ds, record, index){
13995        // Roo.log('onRemove');
13996         this.clearSelections();
13997         var el = this.dataName  ?
13998             this.el.child('.roo-tpl-' + this.dataName) :
13999             this.el; 
14000         
14001         el.dom.removeChild(this.nodes[index]);
14002         this.updateIndexes(index);
14003     },
14004
14005     /**
14006      * Refresh an individual node.
14007      * @param {Number} index
14008      */
14009     refreshNode : function(index){
14010         this.onUpdate(this.store, this.store.getAt(index));
14011     },
14012
14013     updateIndexes : function(startIndex, endIndex){
14014         var ns = this.nodes;
14015         startIndex = startIndex || 0;
14016         endIndex = endIndex || ns.length - 1;
14017         for(var i = startIndex; i <= endIndex; i++){
14018             ns[i].nodeIndex = i;
14019         }
14020     },
14021
14022     /**
14023      * Changes the data store this view uses and refresh the view.
14024      * @param {Store} store
14025      */
14026     setStore : function(store, initial){
14027         if(!initial && this.store){
14028             this.store.un("datachanged", this.refresh);
14029             this.store.un("add", this.onAdd);
14030             this.store.un("remove", this.onRemove);
14031             this.store.un("update", this.onUpdate);
14032             this.store.un("clear", this.refresh);
14033             this.store.un("beforeload", this.onBeforeLoad);
14034             this.store.un("load", this.onLoad);
14035             this.store.un("loadexception", this.onLoad);
14036         }
14037         if(store){
14038           
14039             store.on("datachanged", this.refresh, this);
14040             store.on("add", this.onAdd, this);
14041             store.on("remove", this.onRemove, this);
14042             store.on("update", this.onUpdate, this);
14043             store.on("clear", this.refresh, this);
14044             store.on("beforeload", this.onBeforeLoad, this);
14045             store.on("load", this.onLoad, this);
14046             store.on("loadexception", this.onLoad, this);
14047         }
14048         
14049         if(store){
14050             this.refresh();
14051         }
14052     },
14053     /**
14054      * onbeforeLoad - masks the loading area.
14055      *
14056      */
14057     onBeforeLoad : function(store,opts)
14058     {
14059          //Roo.log('onBeforeLoad');   
14060         if (!opts.add) {
14061             this.el.update("");
14062         }
14063         this.el.mask(this.mask ? this.mask : "Loading" ); 
14064     },
14065     onLoad : function ()
14066     {
14067         this.el.unmask();
14068     },
14069     
14070
14071     /**
14072      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14073      * @param {HTMLElement} node
14074      * @return {HTMLElement} The template node
14075      */
14076     findItemFromChild : function(node){
14077         var el = this.dataName  ?
14078             this.el.child('.roo-tpl-' + this.dataName,true) :
14079             this.el.dom; 
14080         
14081         if(!node || node.parentNode == el){
14082                     return node;
14083             }
14084             var p = node.parentNode;
14085             while(p && p != el){
14086             if(p.parentNode == el){
14087                 return p;
14088             }
14089             p = p.parentNode;
14090         }
14091             return null;
14092     },
14093
14094     /** @ignore */
14095     onClick : function(e){
14096         var item = this.findItemFromChild(e.getTarget());
14097         if(item){
14098             var index = this.indexOf(item);
14099             if(this.onItemClick(item, index, e) !== false){
14100                 this.fireEvent("click", this, index, item, e);
14101             }
14102         }else{
14103             this.clearSelections();
14104         }
14105     },
14106
14107     /** @ignore */
14108     onContextMenu : function(e){
14109         var item = this.findItemFromChild(e.getTarget());
14110         if(item){
14111             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14112         }
14113     },
14114
14115     /** @ignore */
14116     onDblClick : function(e){
14117         var item = this.findItemFromChild(e.getTarget());
14118         if(item){
14119             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14120         }
14121     },
14122
14123     onItemClick : function(item, index, e)
14124     {
14125         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14126             return false;
14127         }
14128         if (this.toggleSelect) {
14129             var m = this.isSelected(item) ? 'unselect' : 'select';
14130             //Roo.log(m);
14131             var _t = this;
14132             _t[m](item, true, false);
14133             return true;
14134         }
14135         if(this.multiSelect || this.singleSelect){
14136             if(this.multiSelect && e.shiftKey && this.lastSelection){
14137                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14138             }else{
14139                 this.select(item, this.multiSelect && e.ctrlKey);
14140                 this.lastSelection = item;
14141             }
14142             
14143             if(!this.tickable){
14144                 e.preventDefault();
14145             }
14146             
14147         }
14148         return true;
14149     },
14150
14151     /**
14152      * Get the number of selected nodes.
14153      * @return {Number}
14154      */
14155     getSelectionCount : function(){
14156         return this.selections.length;
14157     },
14158
14159     /**
14160      * Get the currently selected nodes.
14161      * @return {Array} An array of HTMLElements
14162      */
14163     getSelectedNodes : function(){
14164         return this.selections;
14165     },
14166
14167     /**
14168      * Get the indexes of the selected nodes.
14169      * @return {Array}
14170      */
14171     getSelectedIndexes : function(){
14172         var indexes = [], s = this.selections;
14173         for(var i = 0, len = s.length; i < len; i++){
14174             indexes.push(s[i].nodeIndex);
14175         }
14176         return indexes;
14177     },
14178
14179     /**
14180      * Clear all selections
14181      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14182      */
14183     clearSelections : function(suppressEvent){
14184         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14185             this.cmp.elements = this.selections;
14186             this.cmp.removeClass(this.selectedClass);
14187             this.selections = [];
14188             if(!suppressEvent){
14189                 this.fireEvent("selectionchange", this, this.selections);
14190             }
14191         }
14192     },
14193
14194     /**
14195      * Returns true if the passed node is selected
14196      * @param {HTMLElement/Number} node The node or node index
14197      * @return {Boolean}
14198      */
14199     isSelected : function(node){
14200         var s = this.selections;
14201         if(s.length < 1){
14202             return false;
14203         }
14204         node = this.getNode(node);
14205         return s.indexOf(node) !== -1;
14206     },
14207
14208     /**
14209      * Selects nodes.
14210      * @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
14211      * @param {Boolean} keepExisting (optional) true to keep existing selections
14212      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14213      */
14214     select : function(nodeInfo, keepExisting, suppressEvent){
14215         if(nodeInfo instanceof Array){
14216             if(!keepExisting){
14217                 this.clearSelections(true);
14218             }
14219             for(var i = 0, len = nodeInfo.length; i < len; i++){
14220                 this.select(nodeInfo[i], true, true);
14221             }
14222             return;
14223         } 
14224         var node = this.getNode(nodeInfo);
14225         if(!node || this.isSelected(node)){
14226             return; // already selected.
14227         }
14228         if(!keepExisting){
14229             this.clearSelections(true);
14230         }
14231         
14232         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14233             Roo.fly(node).addClass(this.selectedClass);
14234             this.selections.push(node);
14235             if(!suppressEvent){
14236                 this.fireEvent("selectionchange", this, this.selections);
14237             }
14238         }
14239         
14240         
14241     },
14242       /**
14243      * Unselects nodes.
14244      * @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
14245      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14246      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14247      */
14248     unselect : function(nodeInfo, keepExisting, suppressEvent)
14249     {
14250         if(nodeInfo instanceof Array){
14251             Roo.each(this.selections, function(s) {
14252                 this.unselect(s, nodeInfo);
14253             }, this);
14254             return;
14255         }
14256         var node = this.getNode(nodeInfo);
14257         if(!node || !this.isSelected(node)){
14258             //Roo.log("not selected");
14259             return; // not selected.
14260         }
14261         // fireevent???
14262         var ns = [];
14263         Roo.each(this.selections, function(s) {
14264             if (s == node ) {
14265                 Roo.fly(node).removeClass(this.selectedClass);
14266
14267                 return;
14268             }
14269             ns.push(s);
14270         },this);
14271         
14272         this.selections= ns;
14273         this.fireEvent("selectionchange", this, this.selections);
14274     },
14275
14276     /**
14277      * Gets a template node.
14278      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14279      * @return {HTMLElement} The node or null if it wasn't found
14280      */
14281     getNode : function(nodeInfo){
14282         if(typeof nodeInfo == "string"){
14283             return document.getElementById(nodeInfo);
14284         }else if(typeof nodeInfo == "number"){
14285             return this.nodes[nodeInfo];
14286         }
14287         return nodeInfo;
14288     },
14289
14290     /**
14291      * Gets a range template nodes.
14292      * @param {Number} startIndex
14293      * @param {Number} endIndex
14294      * @return {Array} An array of nodes
14295      */
14296     getNodes : function(start, end){
14297         var ns = this.nodes;
14298         start = start || 0;
14299         end = typeof end == "undefined" ? ns.length - 1 : end;
14300         var nodes = [];
14301         if(start <= end){
14302             for(var i = start; i <= end; i++){
14303                 nodes.push(ns[i]);
14304             }
14305         } else{
14306             for(var i = start; i >= end; i--){
14307                 nodes.push(ns[i]);
14308             }
14309         }
14310         return nodes;
14311     },
14312
14313     /**
14314      * Finds the index of the passed node
14315      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14316      * @return {Number} The index of the node or -1
14317      */
14318     indexOf : function(node){
14319         node = this.getNode(node);
14320         if(typeof node.nodeIndex == "number"){
14321             return node.nodeIndex;
14322         }
14323         var ns = this.nodes;
14324         for(var i = 0, len = ns.length; i < len; i++){
14325             if(ns[i] == node){
14326                 return i;
14327             }
14328         }
14329         return -1;
14330     }
14331 });
14332 /*
14333  * - LGPL
14334  *
14335  * based on jquery fullcalendar
14336  * 
14337  */
14338
14339 Roo.bootstrap = Roo.bootstrap || {};
14340 /**
14341  * @class Roo.bootstrap.Calendar
14342  * @extends Roo.bootstrap.Component
14343  * Bootstrap Calendar class
14344  * @cfg {Boolean} loadMask (true|false) default false
14345  * @cfg {Object} header generate the user specific header of the calendar, default false
14346
14347  * @constructor
14348  * Create a new Container
14349  * @param {Object} config The config object
14350  */
14351
14352
14353
14354 Roo.bootstrap.Calendar = function(config){
14355     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14356      this.addEvents({
14357         /**
14358              * @event select
14359              * Fires when a date is selected
14360              * @param {DatePicker} this
14361              * @param {Date} date The selected date
14362              */
14363         'select': true,
14364         /**
14365              * @event monthchange
14366              * Fires when the displayed month changes 
14367              * @param {DatePicker} this
14368              * @param {Date} date The selected month
14369              */
14370         'monthchange': true,
14371         /**
14372              * @event evententer
14373              * Fires when mouse over an event
14374              * @param {Calendar} this
14375              * @param {event} Event
14376              */
14377         'evententer': true,
14378         /**
14379              * @event eventleave
14380              * Fires when the mouse leaves an
14381              * @param {Calendar} this
14382              * @param {event}
14383              */
14384         'eventleave': true,
14385         /**
14386              * @event eventclick
14387              * Fires when the mouse click an
14388              * @param {Calendar} this
14389              * @param {event}
14390              */
14391         'eventclick': true
14392         
14393     });
14394
14395 };
14396
14397 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14398     
14399      /**
14400      * @cfg {Number} startDay
14401      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14402      */
14403     startDay : 0,
14404     
14405     loadMask : false,
14406     
14407     header : false,
14408       
14409     getAutoCreate : function(){
14410         
14411         
14412         var fc_button = function(name, corner, style, content ) {
14413             return Roo.apply({},{
14414                 tag : 'span',
14415                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14416                          (corner.length ?
14417                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14418                             ''
14419                         ),
14420                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14421                 unselectable: 'on'
14422             });
14423         };
14424         
14425         var header = {};
14426         
14427         if(!this.header){
14428             header = {
14429                 tag : 'table',
14430                 cls : 'fc-header',
14431                 style : 'width:100%',
14432                 cn : [
14433                     {
14434                         tag: 'tr',
14435                         cn : [
14436                             {
14437                                 tag : 'td',
14438                                 cls : 'fc-header-left',
14439                                 cn : [
14440                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14441                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14442                                     { tag: 'span', cls: 'fc-header-space' },
14443                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14444
14445
14446                                 ]
14447                             },
14448
14449                             {
14450                                 tag : 'td',
14451                                 cls : 'fc-header-center',
14452                                 cn : [
14453                                     {
14454                                         tag: 'span',
14455                                         cls: 'fc-header-title',
14456                                         cn : {
14457                                             tag: 'H2',
14458                                             html : 'month / year'
14459                                         }
14460                                     }
14461
14462                                 ]
14463                             },
14464                             {
14465                                 tag : 'td',
14466                                 cls : 'fc-header-right',
14467                                 cn : [
14468                               /*      fc_button('month', 'left', '', 'month' ),
14469                                     fc_button('week', '', '', 'week' ),
14470                                     fc_button('day', 'right', '', 'day' )
14471                                 */    
14472
14473                                 ]
14474                             }
14475
14476                         ]
14477                     }
14478                 ]
14479             };
14480         }
14481         
14482         header = this.header;
14483         
14484        
14485         var cal_heads = function() {
14486             var ret = [];
14487             // fixme - handle this.
14488             
14489             for (var i =0; i < Date.dayNames.length; i++) {
14490                 var d = Date.dayNames[i];
14491                 ret.push({
14492                     tag: 'th',
14493                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14494                     html : d.substring(0,3)
14495                 });
14496                 
14497             }
14498             ret[0].cls += ' fc-first';
14499             ret[6].cls += ' fc-last';
14500             return ret;
14501         };
14502         var cal_cell = function(n) {
14503             return  {
14504                 tag: 'td',
14505                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14506                 cn : [
14507                     {
14508                         cn : [
14509                             {
14510                                 cls: 'fc-day-number',
14511                                 html: 'D'
14512                             },
14513                             {
14514                                 cls: 'fc-day-content',
14515                              
14516                                 cn : [
14517                                      {
14518                                         style: 'position: relative;' // height: 17px;
14519                                     }
14520                                 ]
14521                             }
14522                             
14523                             
14524                         ]
14525                     }
14526                 ]
14527                 
14528             }
14529         };
14530         var cal_rows = function() {
14531             
14532             var ret = [];
14533             for (var r = 0; r < 6; r++) {
14534                 var row= {
14535                     tag : 'tr',
14536                     cls : 'fc-week',
14537                     cn : []
14538                 };
14539                 
14540                 for (var i =0; i < Date.dayNames.length; i++) {
14541                     var d = Date.dayNames[i];
14542                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14543
14544                 }
14545                 row.cn[0].cls+=' fc-first';
14546                 row.cn[0].cn[0].style = 'min-height:90px';
14547                 row.cn[6].cls+=' fc-last';
14548                 ret.push(row);
14549                 
14550             }
14551             ret[0].cls += ' fc-first';
14552             ret[4].cls += ' fc-prev-last';
14553             ret[5].cls += ' fc-last';
14554             return ret;
14555             
14556         };
14557         
14558         var cal_table = {
14559             tag: 'table',
14560             cls: 'fc-border-separate',
14561             style : 'width:100%',
14562             cellspacing  : 0,
14563             cn : [
14564                 { 
14565                     tag: 'thead',
14566                     cn : [
14567                         { 
14568                             tag: 'tr',
14569                             cls : 'fc-first fc-last',
14570                             cn : cal_heads()
14571                         }
14572                     ]
14573                 },
14574                 { 
14575                     tag: 'tbody',
14576                     cn : cal_rows()
14577                 }
14578                   
14579             ]
14580         };
14581          
14582          var cfg = {
14583             cls : 'fc fc-ltr',
14584             cn : [
14585                 header,
14586                 {
14587                     cls : 'fc-content',
14588                     style : "position: relative;",
14589                     cn : [
14590                         {
14591                             cls : 'fc-view fc-view-month fc-grid',
14592                             style : 'position: relative',
14593                             unselectable : 'on',
14594                             cn : [
14595                                 {
14596                                     cls : 'fc-event-container',
14597                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14598                                 },
14599                                 cal_table
14600                             ]
14601                         }
14602                     ]
14603     
14604                 }
14605            ] 
14606             
14607         };
14608         
14609          
14610         
14611         return cfg;
14612     },
14613     
14614     
14615     initEvents : function()
14616     {
14617         if(!this.store){
14618             throw "can not find store for calendar";
14619         }
14620         
14621         var mark = {
14622             tag: "div",
14623             cls:"x-dlg-mask",
14624             style: "text-align:center",
14625             cn: [
14626                 {
14627                     tag: "div",
14628                     style: "background-color:white;width:50%;margin:250 auto",
14629                     cn: [
14630                         {
14631                             tag: "img",
14632                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14633                         },
14634                         {
14635                             tag: "span",
14636                             html: "Loading"
14637                         }
14638                         
14639                     ]
14640                 }
14641             ]
14642         }
14643         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14644         
14645         var size = this.el.select('.fc-content', true).first().getSize();
14646         this.maskEl.setSize(size.width, size.height);
14647         this.maskEl.enableDisplayMode("block");
14648         if(!this.loadMask){
14649             this.maskEl.hide();
14650         }
14651         
14652         this.store = Roo.factory(this.store, Roo.data);
14653         this.store.on('load', this.onLoad, this);
14654         this.store.on('beforeload', this.onBeforeLoad, this);
14655         
14656         this.resize();
14657         
14658         this.cells = this.el.select('.fc-day',true);
14659         //Roo.log(this.cells);
14660         this.textNodes = this.el.query('.fc-day-number');
14661         this.cells.addClassOnOver('fc-state-hover');
14662         
14663         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14664         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14665         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14666         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14667         
14668         this.on('monthchange', this.onMonthChange, this);
14669         
14670         this.update(new Date().clearTime());
14671     },
14672     
14673     resize : function() {
14674         var sz  = this.el.getSize();
14675         
14676         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14677         this.el.select('.fc-day-content div',true).setHeight(34);
14678     },
14679     
14680     
14681     // private
14682     showPrevMonth : function(e){
14683         this.update(this.activeDate.add("mo", -1));
14684     },
14685     showToday : function(e){
14686         this.update(new Date().clearTime());
14687     },
14688     // private
14689     showNextMonth : function(e){
14690         this.update(this.activeDate.add("mo", 1));
14691     },
14692
14693     // private
14694     showPrevYear : function(){
14695         this.update(this.activeDate.add("y", -1));
14696     },
14697
14698     // private
14699     showNextYear : function(){
14700         this.update(this.activeDate.add("y", 1));
14701     },
14702
14703     
14704    // private
14705     update : function(date)
14706     {
14707         var vd = this.activeDate;
14708         this.activeDate = date;
14709 //        if(vd && this.el){
14710 //            var t = date.getTime();
14711 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14712 //                Roo.log('using add remove');
14713 //                
14714 //                this.fireEvent('monthchange', this, date);
14715 //                
14716 //                this.cells.removeClass("fc-state-highlight");
14717 //                this.cells.each(function(c){
14718 //                   if(c.dateValue == t){
14719 //                       c.addClass("fc-state-highlight");
14720 //                       setTimeout(function(){
14721 //                            try{c.dom.firstChild.focus();}catch(e){}
14722 //                       }, 50);
14723 //                       return false;
14724 //                   }
14725 //                   return true;
14726 //                });
14727 //                return;
14728 //            }
14729 //        }
14730         
14731         var days = date.getDaysInMonth();
14732         
14733         var firstOfMonth = date.getFirstDateOfMonth();
14734         var startingPos = firstOfMonth.getDay()-this.startDay;
14735         
14736         if(startingPos < this.startDay){
14737             startingPos += 7;
14738         }
14739         
14740         var pm = date.add(Date.MONTH, -1);
14741         var prevStart = pm.getDaysInMonth()-startingPos;
14742 //        
14743         this.cells = this.el.select('.fc-day',true);
14744         this.textNodes = this.el.query('.fc-day-number');
14745         this.cells.addClassOnOver('fc-state-hover');
14746         
14747         var cells = this.cells.elements;
14748         var textEls = this.textNodes;
14749         
14750         Roo.each(cells, function(cell){
14751             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14752         });
14753         
14754         days += startingPos;
14755
14756         // convert everything to numbers so it's fast
14757         var day = 86400000;
14758         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14759         //Roo.log(d);
14760         //Roo.log(pm);
14761         //Roo.log(prevStart);
14762         
14763         var today = new Date().clearTime().getTime();
14764         var sel = date.clearTime().getTime();
14765         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14766         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14767         var ddMatch = this.disabledDatesRE;
14768         var ddText = this.disabledDatesText;
14769         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14770         var ddaysText = this.disabledDaysText;
14771         var format = this.format;
14772         
14773         var setCellClass = function(cal, cell){
14774             cell.row = 0;
14775             cell.events = [];
14776             cell.more = [];
14777             //Roo.log('set Cell Class');
14778             cell.title = "";
14779             var t = d.getTime();
14780             
14781             //Roo.log(d);
14782             
14783             cell.dateValue = t;
14784             if(t == today){
14785                 cell.className += " fc-today";
14786                 cell.className += " fc-state-highlight";
14787                 cell.title = cal.todayText;
14788             }
14789             if(t == sel){
14790                 // disable highlight in other month..
14791                 //cell.className += " fc-state-highlight";
14792                 
14793             }
14794             // disabling
14795             if(t < min) {
14796                 cell.className = " fc-state-disabled";
14797                 cell.title = cal.minText;
14798                 return;
14799             }
14800             if(t > max) {
14801                 cell.className = " fc-state-disabled";
14802                 cell.title = cal.maxText;
14803                 return;
14804             }
14805             if(ddays){
14806                 if(ddays.indexOf(d.getDay()) != -1){
14807                     cell.title = ddaysText;
14808                     cell.className = " fc-state-disabled";
14809                 }
14810             }
14811             if(ddMatch && format){
14812                 var fvalue = d.dateFormat(format);
14813                 if(ddMatch.test(fvalue)){
14814                     cell.title = ddText.replace("%0", fvalue);
14815                     cell.className = " fc-state-disabled";
14816                 }
14817             }
14818             
14819             if (!cell.initialClassName) {
14820                 cell.initialClassName = cell.dom.className;
14821             }
14822             
14823             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14824         };
14825
14826         var i = 0;
14827         
14828         for(; i < startingPos; i++) {
14829             textEls[i].innerHTML = (++prevStart);
14830             d.setDate(d.getDate()+1);
14831             
14832             cells[i].className = "fc-past fc-other-month";
14833             setCellClass(this, cells[i]);
14834         }
14835         
14836         var intDay = 0;
14837         
14838         for(; i < days; i++){
14839             intDay = i - startingPos + 1;
14840             textEls[i].innerHTML = (intDay);
14841             d.setDate(d.getDate()+1);
14842             
14843             cells[i].className = ''; // "x-date-active";
14844             setCellClass(this, cells[i]);
14845         }
14846         var extraDays = 0;
14847         
14848         for(; i < 42; i++) {
14849             textEls[i].innerHTML = (++extraDays);
14850             d.setDate(d.getDate()+1);
14851             
14852             cells[i].className = "fc-future fc-other-month";
14853             setCellClass(this, cells[i]);
14854         }
14855         
14856         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14857         
14858         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14859         
14860         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14861         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14862         
14863         if(totalRows != 6){
14864             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14865             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14866         }
14867         
14868         this.fireEvent('monthchange', this, date);
14869         
14870         
14871         /*
14872         if(!this.internalRender){
14873             var main = this.el.dom.firstChild;
14874             var w = main.offsetWidth;
14875             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14876             Roo.fly(main).setWidth(w);
14877             this.internalRender = true;
14878             // opera does not respect the auto grow header center column
14879             // then, after it gets a width opera refuses to recalculate
14880             // without a second pass
14881             if(Roo.isOpera && !this.secondPass){
14882                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14883                 this.secondPass = true;
14884                 this.update.defer(10, this, [date]);
14885             }
14886         }
14887         */
14888         
14889     },
14890     
14891     findCell : function(dt) {
14892         dt = dt.clearTime().getTime();
14893         var ret = false;
14894         this.cells.each(function(c){
14895             //Roo.log("check " +c.dateValue + '?=' + dt);
14896             if(c.dateValue == dt){
14897                 ret = c;
14898                 return false;
14899             }
14900             return true;
14901         });
14902         
14903         return ret;
14904     },
14905     
14906     findCells : function(ev) {
14907         var s = ev.start.clone().clearTime().getTime();
14908        // Roo.log(s);
14909         var e= ev.end.clone().clearTime().getTime();
14910        // Roo.log(e);
14911         var ret = [];
14912         this.cells.each(function(c){
14913              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
14914             
14915             if(c.dateValue > e){
14916                 return ;
14917             }
14918             if(c.dateValue < s){
14919                 return ;
14920             }
14921             ret.push(c);
14922         });
14923         
14924         return ret;    
14925     },
14926     
14927 //    findBestRow: function(cells)
14928 //    {
14929 //        var ret = 0;
14930 //        
14931 //        for (var i =0 ; i < cells.length;i++) {
14932 //            ret  = Math.max(cells[i].rows || 0,ret);
14933 //        }
14934 //        return ret;
14935 //        
14936 //    },
14937     
14938     
14939     addItem : function(ev)
14940     {
14941         // look for vertical location slot in
14942         var cells = this.findCells(ev);
14943         
14944 //        ev.row = this.findBestRow(cells);
14945         
14946         // work out the location.
14947         
14948         var crow = false;
14949         var rows = [];
14950         for(var i =0; i < cells.length; i++) {
14951             
14952             cells[i].row = cells[0].row;
14953             
14954             if(i == 0){
14955                 cells[i].row = cells[i].row + 1;
14956             }
14957             
14958             if (!crow) {
14959                 crow = {
14960                     start : cells[i],
14961                     end :  cells[i]
14962                 };
14963                 continue;
14964             }
14965             if (crow.start.getY() == cells[i].getY()) {
14966                 // on same row.
14967                 crow.end = cells[i];
14968                 continue;
14969             }
14970             // different row.
14971             rows.push(crow);
14972             crow = {
14973                 start: cells[i],
14974                 end : cells[i]
14975             };
14976             
14977         }
14978         
14979         rows.push(crow);
14980         ev.els = [];
14981         ev.rows = rows;
14982         ev.cells = cells;
14983         
14984         cells[0].events.push(ev);
14985         
14986         this.calevents.push(ev);
14987     },
14988     
14989     clearEvents: function() {
14990         
14991         if(!this.calevents){
14992             return;
14993         }
14994         
14995         Roo.each(this.cells.elements, function(c){
14996             c.row = 0;
14997             c.events = [];
14998             c.more = [];
14999         });
15000         
15001         Roo.each(this.calevents, function(e) {
15002             Roo.each(e.els, function(el) {
15003                 el.un('mouseenter' ,this.onEventEnter, this);
15004                 el.un('mouseleave' ,this.onEventLeave, this);
15005                 el.remove();
15006             },this);
15007         },this);
15008         
15009         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15010             e.remove();
15011         });
15012         
15013     },
15014     
15015     renderEvents: function()
15016     {   
15017         var _this = this;
15018         
15019         this.cells.each(function(c) {
15020             
15021             if(c.row < 5){
15022                 return;
15023             }
15024             
15025             var ev = c.events;
15026             
15027             var r = 4;
15028             if(c.row != c.events.length){
15029                 r = 4 - (4 - (c.row - c.events.length));
15030             }
15031             
15032             c.events = ev.slice(0, r);
15033             c.more = ev.slice(r);
15034             
15035             if(c.more.length && c.more.length == 1){
15036                 c.events.push(c.more.pop());
15037             }
15038             
15039             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15040             
15041         });
15042             
15043         this.cells.each(function(c) {
15044             
15045             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15046             
15047             
15048             for (var e = 0; e < c.events.length; e++){
15049                 var ev = c.events[e];
15050                 var rows = ev.rows;
15051                 
15052                 for(var i = 0; i < rows.length; i++) {
15053                 
15054                     // how many rows should it span..
15055
15056                     var  cfg = {
15057                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15058                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15059
15060                         unselectable : "on",
15061                         cn : [
15062                             {
15063                                 cls: 'fc-event-inner',
15064                                 cn : [
15065     //                                {
15066     //                                  tag:'span',
15067     //                                  cls: 'fc-event-time',
15068     //                                  html : cells.length > 1 ? '' : ev.time
15069     //                                },
15070                                     {
15071                                       tag:'span',
15072                                       cls: 'fc-event-title',
15073                                       html : String.format('{0}', ev.title)
15074                                     }
15075
15076
15077                                 ]
15078                             },
15079                             {
15080                                 cls: 'ui-resizable-handle ui-resizable-e',
15081                                 html : '&nbsp;&nbsp;&nbsp'
15082                             }
15083
15084                         ]
15085                     };
15086
15087                     if (i == 0) {
15088                         cfg.cls += ' fc-event-start';
15089                     }
15090                     if ((i+1) == rows.length) {
15091                         cfg.cls += ' fc-event-end';
15092                     }
15093
15094                     var ctr = _this.el.select('.fc-event-container',true).first();
15095                     var cg = ctr.createChild(cfg);
15096
15097                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15098                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15099
15100                     var r = (c.more.length) ? 1 : 0;
15101                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15102                     cg.setWidth(ebox.right - sbox.x -2);
15103
15104                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15105                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15106                     cg.on('click', _this.onEventClick, _this, ev);
15107
15108                     ev.els.push(cg);
15109                     
15110                 }
15111                 
15112             }
15113             
15114             
15115             if(c.more.length){
15116                 var  cfg = {
15117                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15118                     style : 'position: absolute',
15119                     unselectable : "on",
15120                     cn : [
15121                         {
15122                             cls: 'fc-event-inner',
15123                             cn : [
15124                                 {
15125                                   tag:'span',
15126                                   cls: 'fc-event-title',
15127                                   html : 'More'
15128                                 }
15129
15130
15131                             ]
15132                         },
15133                         {
15134                             cls: 'ui-resizable-handle ui-resizable-e',
15135                             html : '&nbsp;&nbsp;&nbsp'
15136                         }
15137
15138                     ]
15139                 };
15140
15141                 var ctr = _this.el.select('.fc-event-container',true).first();
15142                 var cg = ctr.createChild(cfg);
15143
15144                 var sbox = c.select('.fc-day-content',true).first().getBox();
15145                 var ebox = c.select('.fc-day-content',true).first().getBox();
15146                 //Roo.log(cg);
15147                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15148                 cg.setWidth(ebox.right - sbox.x -2);
15149
15150                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15151                 
15152             }
15153             
15154         });
15155         
15156         
15157         
15158     },
15159     
15160     onEventEnter: function (e, el,event,d) {
15161         this.fireEvent('evententer', this, el, event);
15162     },
15163     
15164     onEventLeave: function (e, el,event,d) {
15165         this.fireEvent('eventleave', this, el, event);
15166     },
15167     
15168     onEventClick: function (e, el,event,d) {
15169         this.fireEvent('eventclick', this, el, event);
15170     },
15171     
15172     onMonthChange: function () {
15173         this.store.load();
15174     },
15175     
15176     onMoreEventClick: function(e, el, more)
15177     {
15178         var _this = this;
15179         
15180         this.calpopover.placement = 'right';
15181         this.calpopover.setTitle('More');
15182         
15183         this.calpopover.setContent('');
15184         
15185         var ctr = this.calpopover.el.select('.popover-content', true).first();
15186         
15187         Roo.each(more, function(m){
15188             var cfg = {
15189                 cls : 'fc-event-hori fc-event-draggable',
15190                 html : m.title
15191             }
15192             var cg = ctr.createChild(cfg);
15193             
15194             cg.on('click', _this.onEventClick, _this, m);
15195         });
15196         
15197         this.calpopover.show(el);
15198         
15199         
15200     },
15201     
15202     onLoad: function () 
15203     {   
15204         this.calevents = [];
15205         var cal = this;
15206         
15207         if(this.store.getCount() > 0){
15208             this.store.data.each(function(d){
15209                cal.addItem({
15210                     id : d.data.id,
15211                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15212                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15213                     time : d.data.start_time,
15214                     title : d.data.title,
15215                     description : d.data.description,
15216                     venue : d.data.venue
15217                 });
15218             });
15219         }
15220         
15221         this.renderEvents();
15222         
15223         if(this.calevents.length && this.loadMask){
15224             this.maskEl.hide();
15225         }
15226     },
15227     
15228     onBeforeLoad: function()
15229     {
15230         this.clearEvents();
15231         if(this.loadMask){
15232             this.maskEl.show();
15233         }
15234     }
15235 });
15236
15237  
15238  /*
15239  * - LGPL
15240  *
15241  * element
15242  * 
15243  */
15244
15245 /**
15246  * @class Roo.bootstrap.Popover
15247  * @extends Roo.bootstrap.Component
15248  * Bootstrap Popover class
15249  * @cfg {String} html contents of the popover   (or false to use children..)
15250  * @cfg {String} title of popover (or false to hide)
15251  * @cfg {String} placement how it is placed
15252  * @cfg {String} trigger click || hover (or false to trigger manually)
15253  * @cfg {String} over what (parent or false to trigger manually.)
15254  * @cfg {Number} delay - delay before showing
15255  
15256  * @constructor
15257  * Create a new Popover
15258  * @param {Object} config The config object
15259  */
15260
15261 Roo.bootstrap.Popover = function(config){
15262     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15263 };
15264
15265 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15266     
15267     title: 'Fill in a title',
15268     html: false,
15269     
15270     placement : 'right',
15271     trigger : 'hover', // hover
15272     
15273     delay : 0,
15274     
15275     over: 'parent',
15276     
15277     can_build_overlaid : false,
15278     
15279     getChildContainer : function()
15280     {
15281         return this.el.select('.popover-content',true).first();
15282     },
15283     
15284     getAutoCreate : function(){
15285          Roo.log('make popover?');
15286         var cfg = {
15287            cls : 'popover roo-dynamic',
15288            style: 'display:block',
15289            cn : [
15290                 {
15291                     cls : 'arrow'
15292                 },
15293                 {
15294                     cls : 'popover-inner',
15295                     cn : [
15296                         {
15297                             tag: 'h3',
15298                             cls: 'popover-title',
15299                             html : this.title
15300                         },
15301                         {
15302                             cls : 'popover-content',
15303                             html : this.html
15304                         }
15305                     ]
15306                     
15307                 }
15308            ]
15309         };
15310         
15311         return cfg;
15312     },
15313     setTitle: function(str)
15314     {
15315         this.title = str;
15316         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15317     },
15318     setContent: function(str)
15319     {
15320         this.html = str;
15321         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15322     },
15323     // as it get's added to the bottom of the page.
15324     onRender : function(ct, position)
15325     {
15326         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15327         if(!this.el){
15328             var cfg = Roo.apply({},  this.getAutoCreate());
15329             cfg.id = Roo.id();
15330             
15331             if (this.cls) {
15332                 cfg.cls += ' ' + this.cls;
15333             }
15334             if (this.style) {
15335                 cfg.style = this.style;
15336             }
15337             Roo.log("adding to ")
15338             this.el = Roo.get(document.body).createChild(cfg, position);
15339             Roo.log(this.el);
15340         }
15341         this.initEvents();
15342     },
15343     
15344     initEvents : function()
15345     {
15346         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15347         this.el.enableDisplayMode('block');
15348         this.el.hide();
15349         if (this.over === false) {
15350             return; 
15351         }
15352         if (this.triggers === false) {
15353             return;
15354         }
15355         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15356         var triggers = this.trigger ? this.trigger.split(' ') : [];
15357         Roo.each(triggers, function(trigger) {
15358         
15359             if (trigger == 'click') {
15360                 on_el.on('click', this.toggle, this);
15361             } else if (trigger != 'manual') {
15362                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15363                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15364       
15365                 on_el.on(eventIn  ,this.enter, this);
15366                 on_el.on(eventOut, this.leave, this);
15367             }
15368         }, this);
15369         
15370     },
15371     
15372     
15373     // private
15374     timeout : null,
15375     hoverState : null,
15376     
15377     toggle : function () {
15378         this.hoverState == 'in' ? this.leave() : this.enter();
15379     },
15380     
15381     enter : function () {
15382        
15383     
15384         clearTimeout(this.timeout);
15385     
15386         this.hoverState = 'in';
15387     
15388         if (!this.delay || !this.delay.show) {
15389             this.show();
15390             return;
15391         }
15392         var _t = this;
15393         this.timeout = setTimeout(function () {
15394             if (_t.hoverState == 'in') {
15395                 _t.show();
15396             }
15397         }, this.delay.show)
15398     },
15399     leave : function() {
15400         clearTimeout(this.timeout);
15401     
15402         this.hoverState = 'out';
15403     
15404         if (!this.delay || !this.delay.hide) {
15405             this.hide();
15406             return;
15407         }
15408         var _t = this;
15409         this.timeout = setTimeout(function () {
15410             if (_t.hoverState == 'out') {
15411                 _t.hide();
15412             }
15413         }, this.delay.hide)
15414     },
15415     
15416     show : function (on_el)
15417     {
15418         if (!on_el) {
15419             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15420         }
15421         // set content.
15422         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15423         if (this.html !== false) {
15424             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15425         }
15426         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15427         if (!this.title.length) {
15428             this.el.select('.popover-title',true).hide();
15429         }
15430         
15431         var placement = typeof this.placement == 'function' ?
15432             this.placement.call(this, this.el, on_el) :
15433             this.placement;
15434             
15435         var autoToken = /\s?auto?\s?/i;
15436         var autoPlace = autoToken.test(placement);
15437         if (autoPlace) {
15438             placement = placement.replace(autoToken, '') || 'top';
15439         }
15440         
15441         //this.el.detach()
15442         //this.el.setXY([0,0]);
15443         this.el.show();
15444         this.el.dom.style.display='block';
15445         this.el.addClass(placement);
15446         
15447         //this.el.appendTo(on_el);
15448         
15449         var p = this.getPosition();
15450         var box = this.el.getBox();
15451         
15452         if (autoPlace) {
15453             // fixme..
15454         }
15455         var align = Roo.bootstrap.Popover.alignment[placement];
15456         this.el.alignTo(on_el, align[0],align[1]);
15457         //var arrow = this.el.select('.arrow',true).first();
15458         //arrow.set(align[2], 
15459         
15460         this.el.addClass('in');
15461         
15462         
15463         if (this.el.hasClass('fade')) {
15464             // fade it?
15465         }
15466         
15467     },
15468     hide : function()
15469     {
15470         this.el.setXY([0,0]);
15471         this.el.removeClass('in');
15472         this.el.hide();
15473         this.hoverState = null;
15474         
15475     }
15476     
15477 });
15478
15479 Roo.bootstrap.Popover.alignment = {
15480     'left' : ['r-l', [-10,0], 'right'],
15481     'right' : ['l-r', [10,0], 'left'],
15482     'bottom' : ['t-b', [0,10], 'top'],
15483     'top' : [ 'b-t', [0,-10], 'bottom']
15484 };
15485
15486  /*
15487  * - LGPL
15488  *
15489  * Progress
15490  * 
15491  */
15492
15493 /**
15494  * @class Roo.bootstrap.Progress
15495  * @extends Roo.bootstrap.Component
15496  * Bootstrap Progress class
15497  * @cfg {Boolean} striped striped of the progress bar
15498  * @cfg {Boolean} active animated of the progress bar
15499  * 
15500  * 
15501  * @constructor
15502  * Create a new Progress
15503  * @param {Object} config The config object
15504  */
15505
15506 Roo.bootstrap.Progress = function(config){
15507     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15508 };
15509
15510 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15511     
15512     striped : false,
15513     active: false,
15514     
15515     getAutoCreate : function(){
15516         var cfg = {
15517             tag: 'div',
15518             cls: 'progress'
15519         };
15520         
15521         
15522         if(this.striped){
15523             cfg.cls += ' progress-striped';
15524         }
15525       
15526         if(this.active){
15527             cfg.cls += ' active';
15528         }
15529         
15530         
15531         return cfg;
15532     }
15533    
15534 });
15535
15536  
15537
15538  /*
15539  * - LGPL
15540  *
15541  * ProgressBar
15542  * 
15543  */
15544
15545 /**
15546  * @class Roo.bootstrap.ProgressBar
15547  * @extends Roo.bootstrap.Component
15548  * Bootstrap ProgressBar class
15549  * @cfg {Number} aria_valuenow aria-value now
15550  * @cfg {Number} aria_valuemin aria-value min
15551  * @cfg {Number} aria_valuemax aria-value max
15552  * @cfg {String} label label for the progress bar
15553  * @cfg {String} panel (success | info | warning | danger )
15554  * @cfg {String} role role of the progress bar
15555  * @cfg {String} sr_only text
15556  * 
15557  * 
15558  * @constructor
15559  * Create a new ProgressBar
15560  * @param {Object} config The config object
15561  */
15562
15563 Roo.bootstrap.ProgressBar = function(config){
15564     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15565 };
15566
15567 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15568     
15569     aria_valuenow : 0,
15570     aria_valuemin : 0,
15571     aria_valuemax : 100,
15572     label : false,
15573     panel : false,
15574     role : false,
15575     sr_only: false,
15576     
15577     getAutoCreate : function()
15578     {
15579         
15580         var cfg = {
15581             tag: 'div',
15582             cls: 'progress-bar',
15583             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15584         };
15585         
15586         if(this.sr_only){
15587             cfg.cn = {
15588                 tag: 'span',
15589                 cls: 'sr-only',
15590                 html: this.sr_only
15591             }
15592         }
15593         
15594         if(this.role){
15595             cfg.role = this.role;
15596         }
15597         
15598         if(this.aria_valuenow){
15599             cfg['aria-valuenow'] = this.aria_valuenow;
15600         }
15601         
15602         if(this.aria_valuemin){
15603             cfg['aria-valuemin'] = this.aria_valuemin;
15604         }
15605         
15606         if(this.aria_valuemax){
15607             cfg['aria-valuemax'] = this.aria_valuemax;
15608         }
15609         
15610         if(this.label && !this.sr_only){
15611             cfg.html = this.label;
15612         }
15613         
15614         if(this.panel){
15615             cfg.cls += ' progress-bar-' + this.panel;
15616         }
15617         
15618         return cfg;
15619     },
15620     
15621     update : function(aria_valuenow)
15622     {
15623         this.aria_valuenow = aria_valuenow;
15624         
15625         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15626     }
15627    
15628 });
15629
15630  
15631
15632  /*
15633  * - LGPL
15634  *
15635  * column
15636  * 
15637  */
15638
15639 /**
15640  * @class Roo.bootstrap.TabGroup
15641  * @extends Roo.bootstrap.Column
15642  * Bootstrap Column class
15643  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15644  * @cfg {Boolean} carousel true to make the group behave like a carousel
15645  * @cfg {Number} bullets show the panel pointer.. default 0
15646  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15647  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15648  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15649  * 
15650  * @constructor
15651  * Create a new TabGroup
15652  * @param {Object} config The config object
15653  */
15654
15655 Roo.bootstrap.TabGroup = function(config){
15656     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15657     if (!this.navId) {
15658         this.navId = Roo.id();
15659     }
15660     this.tabs = [];
15661     Roo.bootstrap.TabGroup.register(this);
15662     
15663 };
15664
15665 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15666     
15667     carousel : false,
15668     transition : false,
15669     bullets : 0,
15670     timer : 0,
15671     autoslide : false,
15672     slideFn : false,
15673     slideOnTouch : false,
15674     
15675     getAutoCreate : function()
15676     {
15677         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15678         
15679         cfg.cls += ' tab-content';
15680         
15681         Roo.log('get auto create...............');
15682         
15683         if (this.carousel) {
15684             cfg.cls += ' carousel slide';
15685             
15686             cfg.cn = [{
15687                cls : 'carousel-inner'
15688             }];
15689         
15690             if(this.bullets > 0 && !Roo.isTouch){
15691                 
15692                 var bullets = {
15693                     cls : 'carousel-bullets',
15694                     cn : []
15695                 };
15696                 
15697                 if(this.bullets_cls){
15698                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15699                 }
15700                 
15701                 for (var i = 0; i < this.bullets; i++){
15702                     bullets.cn.push({
15703                         cls : 'bullet bullet-' + i
15704                     });
15705                 }
15706                 
15707                 bullets.cn.push({
15708                     cls : 'clear'
15709                 });
15710                 
15711                 cfg.cn[0].cn = bullets;
15712             }
15713         }
15714         
15715         return cfg;
15716     },
15717     
15718     initEvents:  function()
15719     {
15720         Roo.log('-------- init events on tab group ---------');
15721         
15722         if(this.bullets > 0 && !Roo.isTouch){
15723             this.initBullet();
15724         }
15725         
15726         Roo.log(this);
15727         
15728         if(Roo.isTouch && this.slideOnTouch){
15729             this.el.on("touchstart", this.onTouchStart, this);
15730         }
15731         
15732         if(this.autoslide){
15733             var _this = this;
15734             
15735             this.slideFn = window.setInterval(function() {
15736                 _this.showPanelNext();
15737             }, this.timer);
15738         }
15739         
15740     },
15741     
15742     onTouchStart : function(e, el, o)
15743     {
15744         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15745             return;
15746         }
15747         
15748         this.showPanelNext();
15749     },
15750     
15751     getChildContainer : function()
15752     {
15753         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15754     },
15755     
15756     /**
15757     * register a Navigation item
15758     * @param {Roo.bootstrap.NavItem} the navitem to add
15759     */
15760     register : function(item)
15761     {
15762         this.tabs.push( item);
15763         item.navId = this.navId; // not really needed..
15764     
15765     },
15766     
15767     getActivePanel : function()
15768     {
15769         var r = false;
15770         Roo.each(this.tabs, function(t) {
15771             if (t.active) {
15772                 r = t;
15773                 return false;
15774             }
15775             return null;
15776         });
15777         return r;
15778         
15779     },
15780     getPanelByName : function(n)
15781     {
15782         var r = false;
15783         Roo.each(this.tabs, function(t) {
15784             if (t.tabId == n) {
15785                 r = t;
15786                 return false;
15787             }
15788             return null;
15789         });
15790         return r;
15791     },
15792     indexOfPanel : function(p)
15793     {
15794         var r = false;
15795         Roo.each(this.tabs, function(t,i) {
15796             if (t.tabId == p.tabId) {
15797                 r = i;
15798                 return false;
15799             }
15800             return null;
15801         });
15802         return r;
15803     },
15804     /**
15805      * show a specific panel
15806      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15807      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15808      */
15809     showPanel : function (pan)
15810     {
15811         if(this.transition){
15812             Roo.log("waiting for the transitionend");
15813             return;
15814         }
15815         
15816         if (typeof(pan) == 'number') {
15817             pan = this.tabs[pan];
15818         }
15819         if (typeof(pan) == 'string') {
15820             pan = this.getPanelByName(pan);
15821         }
15822         if (pan.tabId == this.getActivePanel().tabId) {
15823             return true;
15824         }
15825         var cur = this.getActivePanel();
15826         
15827         if (false === cur.fireEvent('beforedeactivate')) {
15828             return false;
15829         }
15830         
15831         if(this.bullets > 0 && !Roo.isTouch){
15832             this.setActiveBullet(this.indexOfPanel(pan));
15833         }
15834         
15835         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15836             
15837             this.transition = true;
15838             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15839             var lr = dir == 'next' ? 'left' : 'right';
15840             pan.el.addClass(dir); // or prev
15841             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15842             cur.el.addClass(lr); // or right
15843             pan.el.addClass(lr);
15844             
15845             var _this = this;
15846             cur.el.on('transitionend', function() {
15847                 Roo.log("trans end?");
15848                 
15849                 pan.el.removeClass([lr,dir]);
15850                 pan.setActive(true);
15851                 
15852                 cur.el.removeClass([lr]);
15853                 cur.setActive(false);
15854                 
15855                 _this.transition = false;
15856                 
15857             }, this, { single:  true } );
15858             
15859             return true;
15860         }
15861         
15862         cur.setActive(false);
15863         pan.setActive(true);
15864         
15865         return true;
15866         
15867     },
15868     showPanelNext : function()
15869     {
15870         var i = this.indexOfPanel(this.getActivePanel());
15871         
15872         if (i >= this.tabs.length - 1 && !this.autoslide) {
15873             return;
15874         }
15875         
15876         if (i >= this.tabs.length - 1 && this.autoslide) {
15877             i = -1;
15878         }
15879         
15880         this.showPanel(this.tabs[i+1]);
15881     },
15882     
15883     showPanelPrev : function()
15884     {
15885         var i = this.indexOfPanel(this.getActivePanel());
15886         
15887         if (i  < 1 && !this.autoslide) {
15888             return;
15889         }
15890         
15891         if (i < 1 && this.autoslide) {
15892             i = this.tabs.length;
15893         }
15894         
15895         this.showPanel(this.tabs[i-1]);
15896     },
15897     
15898     initBullet : function()
15899     {
15900         if(Roo.isTouch){
15901             return;
15902         }
15903         
15904         var _this = this;
15905         
15906         for (var i = 0; i < this.bullets; i++){
15907             var bullet = this.el.select('.bullet-' + i, true).first();
15908
15909             if(!bullet){
15910                 continue;
15911             }
15912
15913             bullet.on('click', (function(e, el, o, ii, t){
15914
15915                 e.preventDefault();
15916
15917                 _this.showPanel(ii);
15918
15919                 if(_this.autoslide && _this.slideFn){
15920                     clearInterval(_this.slideFn);
15921                     _this.slideFn = window.setInterval(function() {
15922                         _this.showPanelNext();
15923                     }, _this.timer);
15924                 }
15925
15926             }).createDelegate(this, [i, bullet], true));
15927         }
15928     },
15929     
15930     setActiveBullet : function(i)
15931     {
15932         if(Roo.isTouch){
15933             return;
15934         }
15935         
15936         Roo.each(this.el.select('.bullet', true).elements, function(el){
15937             el.removeClass('selected');
15938         });
15939
15940         var bullet = this.el.select('.bullet-' + i, true).first();
15941         
15942         if(!bullet){
15943             return;
15944         }
15945         
15946         bullet.addClass('selected');
15947     }
15948     
15949     
15950   
15951 });
15952
15953  
15954
15955  
15956  
15957 Roo.apply(Roo.bootstrap.TabGroup, {
15958     
15959     groups: {},
15960      /**
15961     * register a Navigation Group
15962     * @param {Roo.bootstrap.NavGroup} the navgroup to add
15963     */
15964     register : function(navgrp)
15965     {
15966         this.groups[navgrp.navId] = navgrp;
15967         
15968     },
15969     /**
15970     * fetch a Navigation Group based on the navigation ID
15971     * if one does not exist , it will get created.
15972     * @param {string} the navgroup to add
15973     * @returns {Roo.bootstrap.NavGroup} the navgroup 
15974     */
15975     get: function(navId) {
15976         if (typeof(this.groups[navId]) == 'undefined') {
15977             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
15978         }
15979         return this.groups[navId] ;
15980     }
15981     
15982     
15983     
15984 });
15985
15986  /*
15987  * - LGPL
15988  *
15989  * TabPanel
15990  * 
15991  */
15992
15993 /**
15994  * @class Roo.bootstrap.TabPanel
15995  * @extends Roo.bootstrap.Component
15996  * Bootstrap TabPanel class
15997  * @cfg {Boolean} active panel active
15998  * @cfg {String} html panel content
15999  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16000  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16001  * 
16002  * 
16003  * @constructor
16004  * Create a new TabPanel
16005  * @param {Object} config The config object
16006  */
16007
16008 Roo.bootstrap.TabPanel = function(config){
16009     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16010     this.addEvents({
16011         /**
16012              * @event changed
16013              * Fires when the active status changes
16014              * @param {Roo.bootstrap.TabPanel} this
16015              * @param {Boolean} state the new state
16016             
16017          */
16018         'changed': true,
16019         /**
16020              * @event beforedeactivate
16021              * Fires before a tab is de-activated - can be used to do validation on a form.
16022              * @param {Roo.bootstrap.TabPanel} this
16023              * @return {Boolean} false if there is an error
16024             
16025          */
16026         'beforedeactivate': true
16027      });
16028     
16029     this.tabId = this.tabId || Roo.id();
16030   
16031 };
16032
16033 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16034     
16035     active: false,
16036     html: false,
16037     tabId: false,
16038     navId : false,
16039     
16040     getAutoCreate : function(){
16041         var cfg = {
16042             tag: 'div',
16043             // item is needed for carousel - not sure if it has any effect otherwise
16044             cls: 'tab-pane item',
16045             html: this.html || ''
16046         };
16047         
16048         if(this.active){
16049             cfg.cls += ' active';
16050         }
16051         
16052         if(this.tabId){
16053             cfg.tabId = this.tabId;
16054         }
16055         
16056         
16057         return cfg;
16058     },
16059     
16060     initEvents:  function()
16061     {
16062         Roo.log('-------- init events on tab panel ---------');
16063         
16064         var p = this.parent();
16065         this.navId = this.navId || p.navId;
16066         
16067         if (typeof(this.navId) != 'undefined') {
16068             // not really needed.. but just in case.. parent should be a NavGroup.
16069             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16070             Roo.log(['register', tg, this]);
16071             tg.register(this);
16072             
16073             var i = tg.tabs.length - 1;
16074             
16075             if(this.active && tg.bullets > 0 && i < tg.bullets){
16076                 tg.setActiveBullet(i);
16077             }
16078         }
16079         
16080     },
16081     
16082     
16083     onRender : function(ct, position)
16084     {
16085        // Roo.log("Call onRender: " + this.xtype);
16086         
16087         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16088         
16089         
16090         
16091         
16092         
16093     },
16094     
16095     setActive: function(state)
16096     {
16097         Roo.log("panel - set active " + this.tabId + "=" + state);
16098         
16099         this.active = state;
16100         if (!state) {
16101             this.el.removeClass('active');
16102             
16103         } else  if (!this.el.hasClass('active')) {
16104             this.el.addClass('active');
16105         }
16106         
16107         this.fireEvent('changed', this, state);
16108     }
16109     
16110     
16111 });
16112  
16113
16114  
16115
16116  /*
16117  * - LGPL
16118  *
16119  * DateField
16120  * 
16121  */
16122
16123 /**
16124  * @class Roo.bootstrap.DateField
16125  * @extends Roo.bootstrap.Input
16126  * Bootstrap DateField class
16127  * @cfg {Number} weekStart default 0
16128  * @cfg {String} viewMode default empty, (months|years)
16129  * @cfg {String} minViewMode default empty, (months|years)
16130  * @cfg {Number} startDate default -Infinity
16131  * @cfg {Number} endDate default Infinity
16132  * @cfg {Boolean} todayHighlight default false
16133  * @cfg {Boolean} todayBtn default false
16134  * @cfg {Boolean} calendarWeeks default false
16135  * @cfg {Object} daysOfWeekDisabled default empty
16136  * @cfg {Boolean} singleMode default false (true | false)
16137  * 
16138  * @cfg {Boolean} keyboardNavigation default true
16139  * @cfg {String} language default en
16140  * 
16141  * @constructor
16142  * Create a new DateField
16143  * @param {Object} config The config object
16144  */
16145
16146 Roo.bootstrap.DateField = function(config){
16147     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16148      this.addEvents({
16149             /**
16150              * @event show
16151              * Fires when this field show.
16152              * @param {Roo.bootstrap.DateField} this
16153              * @param {Mixed} date The date value
16154              */
16155             show : true,
16156             /**
16157              * @event show
16158              * Fires when this field hide.
16159              * @param {Roo.bootstrap.DateField} this
16160              * @param {Mixed} date The date value
16161              */
16162             hide : true,
16163             /**
16164              * @event select
16165              * Fires when select a date.
16166              * @param {Roo.bootstrap.DateField} this
16167              * @param {Mixed} date The date value
16168              */
16169             select : true
16170         });
16171 };
16172
16173 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16174     
16175     /**
16176      * @cfg {String} format
16177      * The default date format string which can be overriden for localization support.  The format must be
16178      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16179      */
16180     format : "m/d/y",
16181     /**
16182      * @cfg {String} altFormats
16183      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16184      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16185      */
16186     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16187     
16188     weekStart : 0,
16189     
16190     viewMode : '',
16191     
16192     minViewMode : '',
16193     
16194     todayHighlight : false,
16195     
16196     todayBtn: false,
16197     
16198     language: 'en',
16199     
16200     keyboardNavigation: true,
16201     
16202     calendarWeeks: false,
16203     
16204     startDate: -Infinity,
16205     
16206     endDate: Infinity,
16207     
16208     daysOfWeekDisabled: [],
16209     
16210     _events: [],
16211     
16212     singleMode : false,
16213     
16214     UTCDate: function()
16215     {
16216         return new Date(Date.UTC.apply(Date, arguments));
16217     },
16218     
16219     UTCToday: function()
16220     {
16221         var today = new Date();
16222         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16223     },
16224     
16225     getDate: function() {
16226             var d = this.getUTCDate();
16227             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16228     },
16229     
16230     getUTCDate: function() {
16231             return this.date;
16232     },
16233     
16234     setDate: function(d) {
16235             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16236     },
16237     
16238     setUTCDate: function(d) {
16239             this.date = d;
16240             this.setValue(this.formatDate(this.date));
16241     },
16242         
16243     onRender: function(ct, position)
16244     {
16245         
16246         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16247         
16248         this.language = this.language || 'en';
16249         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16250         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16251         
16252         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16253         this.format = this.format || 'm/d/y';
16254         this.isInline = false;
16255         this.isInput = true;
16256         this.component = this.el.select('.add-on', true).first() || false;
16257         this.component = (this.component && this.component.length === 0) ? false : this.component;
16258         this.hasInput = this.component && this.inputEL().length;
16259         
16260         if (typeof(this.minViewMode === 'string')) {
16261             switch (this.minViewMode) {
16262                 case 'months':
16263                     this.minViewMode = 1;
16264                     break;
16265                 case 'years':
16266                     this.minViewMode = 2;
16267                     break;
16268                 default:
16269                     this.minViewMode = 0;
16270                     break;
16271             }
16272         }
16273         
16274         if (typeof(this.viewMode === 'string')) {
16275             switch (this.viewMode) {
16276                 case 'months':
16277                     this.viewMode = 1;
16278                     break;
16279                 case 'years':
16280                     this.viewMode = 2;
16281                     break;
16282                 default:
16283                     this.viewMode = 0;
16284                     break;
16285             }
16286         }
16287                 
16288         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16289         
16290 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16291         
16292         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16293         
16294         this.picker().on('mousedown', this.onMousedown, this);
16295         this.picker().on('click', this.onClick, this);
16296         
16297         this.picker().addClass('datepicker-dropdown');
16298         
16299         this.startViewMode = this.viewMode;
16300         
16301         if(this.singleMode){
16302             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16303                 v.setVisibilityMode(Roo.Element.DISPLAY)
16304                 v.hide();
16305             });
16306             
16307             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16308                 v.setStyle('width', '189px');
16309             });
16310         }
16311         
16312         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16313             if(!this.calendarWeeks){
16314                 v.remove();
16315                 return;
16316             }
16317             
16318             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16319             v.attr('colspan', function(i, val){
16320                 return parseInt(val) + 1;
16321             });
16322         })
16323                         
16324         
16325         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16326         
16327         this.setStartDate(this.startDate);
16328         this.setEndDate(this.endDate);
16329         
16330         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16331         
16332         this.fillDow();
16333         this.fillMonths();
16334         this.update();
16335         this.showMode();
16336         
16337         if(this.isInline) {
16338             this.show();
16339         }
16340     },
16341     
16342     picker : function()
16343     {
16344         return this.pickerEl;
16345 //        return this.el.select('.datepicker', true).first();
16346     },
16347     
16348     fillDow: function()
16349     {
16350         var dowCnt = this.weekStart;
16351         
16352         var dow = {
16353             tag: 'tr',
16354             cn: [
16355                 
16356             ]
16357         };
16358         
16359         if(this.calendarWeeks){
16360             dow.cn.push({
16361                 tag: 'th',
16362                 cls: 'cw',
16363                 html: '&nbsp;'
16364             })
16365         }
16366         
16367         while (dowCnt < this.weekStart + 7) {
16368             dow.cn.push({
16369                 tag: 'th',
16370                 cls: 'dow',
16371                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16372             });
16373         }
16374         
16375         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16376     },
16377     
16378     fillMonths: function()
16379     {    
16380         var i = 0;
16381         var months = this.picker().select('>.datepicker-months td', true).first();
16382         
16383         months.dom.innerHTML = '';
16384         
16385         while (i < 12) {
16386             var month = {
16387                 tag: 'span',
16388                 cls: 'month',
16389                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16390             }
16391             
16392             months.createChild(month);
16393         }
16394         
16395     },
16396     
16397     update: function()
16398     {
16399         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;
16400         
16401         if (this.date < this.startDate) {
16402             this.viewDate = new Date(this.startDate);
16403         } else if (this.date > this.endDate) {
16404             this.viewDate = new Date(this.endDate);
16405         } else {
16406             this.viewDate = new Date(this.date);
16407         }
16408         
16409         this.fill();
16410     },
16411     
16412     fill: function() 
16413     {
16414         var d = new Date(this.viewDate),
16415                 year = d.getUTCFullYear(),
16416                 month = d.getUTCMonth(),
16417                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16418                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16419                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16420                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16421                 currentDate = this.date && this.date.valueOf(),
16422                 today = this.UTCToday();
16423         
16424         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16425         
16426 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16427         
16428 //        this.picker.select('>tfoot th.today').
16429 //                                              .text(dates[this.language].today)
16430 //                                              .toggle(this.todayBtn !== false);
16431     
16432         this.updateNavArrows();
16433         this.fillMonths();
16434                                                 
16435         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16436         
16437         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16438          
16439         prevMonth.setUTCDate(day);
16440         
16441         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16442         
16443         var nextMonth = new Date(prevMonth);
16444         
16445         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16446         
16447         nextMonth = nextMonth.valueOf();
16448         
16449         var fillMonths = false;
16450         
16451         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16452         
16453         while(prevMonth.valueOf() < nextMonth) {
16454             var clsName = '';
16455             
16456             if (prevMonth.getUTCDay() === this.weekStart) {
16457                 if(fillMonths){
16458                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16459                 }
16460                     
16461                 fillMonths = {
16462                     tag: 'tr',
16463                     cn: []
16464                 };
16465                 
16466                 if(this.calendarWeeks){
16467                     // ISO 8601: First week contains first thursday.
16468                     // ISO also states week starts on Monday, but we can be more abstract here.
16469                     var
16470                     // Start of current week: based on weekstart/current date
16471                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16472                     // Thursday of this week
16473                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16474                     // First Thursday of year, year from thursday
16475                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16476                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16477                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16478                     
16479                     fillMonths.cn.push({
16480                         tag: 'td',
16481                         cls: 'cw',
16482                         html: calWeek
16483                     });
16484                 }
16485             }
16486             
16487             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16488                 clsName += ' old';
16489             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16490                 clsName += ' new';
16491             }
16492             if (this.todayHighlight &&
16493                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16494                 prevMonth.getUTCMonth() == today.getMonth() &&
16495                 prevMonth.getUTCDate() == today.getDate()) {
16496                 clsName += ' today';
16497             }
16498             
16499             if (currentDate && prevMonth.valueOf() === currentDate) {
16500                 clsName += ' active';
16501             }
16502             
16503             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16504                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16505                     clsName += ' disabled';
16506             }
16507             
16508             fillMonths.cn.push({
16509                 tag: 'td',
16510                 cls: 'day ' + clsName,
16511                 html: prevMonth.getDate()
16512             })
16513             
16514             prevMonth.setDate(prevMonth.getDate()+1);
16515         }
16516           
16517         var currentYear = this.date && this.date.getUTCFullYear();
16518         var currentMonth = this.date && this.date.getUTCMonth();
16519         
16520         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16521         
16522         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16523             v.removeClass('active');
16524             
16525             if(currentYear === year && k === currentMonth){
16526                 v.addClass('active');
16527             }
16528             
16529             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16530                 v.addClass('disabled');
16531             }
16532             
16533         });
16534         
16535         
16536         year = parseInt(year/10, 10) * 10;
16537         
16538         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16539         
16540         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16541         
16542         year -= 1;
16543         for (var i = -1; i < 11; i++) {
16544             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16545                 tag: 'span',
16546                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16547                 html: year
16548             })
16549             
16550             year += 1;
16551         }
16552     },
16553     
16554     showMode: function(dir) 
16555     {
16556         if (dir) {
16557             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16558         }
16559         
16560         Roo.each(this.picker().select('>div',true).elements, function(v){
16561             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16562             v.hide();
16563         });
16564         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16565     },
16566     
16567     place: function()
16568     {
16569         if(this.isInline) return;
16570         
16571         this.picker().removeClass(['bottom', 'top']);
16572         
16573         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16574             /*
16575              * place to the top of element!
16576              *
16577              */
16578             
16579             this.picker().addClass('top');
16580             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16581             
16582             return;
16583         }
16584         
16585         this.picker().addClass('bottom');
16586         
16587         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16588     },
16589     
16590     parseDate : function(value)
16591     {
16592         if(!value || value instanceof Date){
16593             return value;
16594         }
16595         var v = Date.parseDate(value, this.format);
16596         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16597             v = Date.parseDate(value, 'Y-m-d');
16598         }
16599         if(!v && this.altFormats){
16600             if(!this.altFormatsArray){
16601                 this.altFormatsArray = this.altFormats.split("|");
16602             }
16603             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16604                 v = Date.parseDate(value, this.altFormatsArray[i]);
16605             }
16606         }
16607         return v;
16608     },
16609     
16610     formatDate : function(date, fmt)
16611     {   
16612         return (!date || !(date instanceof Date)) ?
16613         date : date.dateFormat(fmt || this.format);
16614     },
16615     
16616     onFocus : function()
16617     {
16618         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16619         this.show();
16620     },
16621     
16622     onBlur : function()
16623     {
16624         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16625         
16626         var d = this.inputEl().getValue();
16627         
16628         this.setValue(d);
16629                 
16630         this.hide();
16631     },
16632     
16633     show : function()
16634     {
16635         this.picker().show();
16636         this.update();
16637         this.place();
16638         
16639         this.fireEvent('show', this, this.date);
16640     },
16641     
16642     hide : function()
16643     {
16644         if(this.isInline) return;
16645         this.picker().hide();
16646         this.viewMode = this.startViewMode;
16647         this.showMode();
16648         
16649         this.fireEvent('hide', this, this.date);
16650         
16651     },
16652     
16653     onMousedown: function(e)
16654     {
16655         e.stopPropagation();
16656         e.preventDefault();
16657     },
16658     
16659     keyup: function(e)
16660     {
16661         Roo.bootstrap.DateField.superclass.keyup.call(this);
16662         this.update();
16663     },
16664
16665     setValue: function(v)
16666     {
16667         
16668         // v can be a string or a date..
16669         
16670         
16671         var d = new Date(this.parseDate(v) ).clearTime();
16672         
16673         if(isNaN(d.getTime())){
16674             this.date = this.viewDate = '';
16675             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16676             return;
16677         }
16678         
16679         v = this.formatDate(d);
16680         
16681         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16682         
16683         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16684      
16685         this.update();
16686
16687         this.fireEvent('select', this, this.date);
16688         
16689     },
16690     
16691     getValue: function()
16692     {
16693         return this.formatDate(this.date);
16694     },
16695     
16696     fireKey: function(e)
16697     {
16698         if (!this.picker().isVisible()){
16699             if (e.keyCode == 27) // allow escape to hide and re-show picker
16700                 this.show();
16701             return;
16702         }
16703         
16704         var dateChanged = false,
16705         dir, day, month,
16706         newDate, newViewDate;
16707         
16708         switch(e.keyCode){
16709             case 27: // escape
16710                 this.hide();
16711                 e.preventDefault();
16712                 break;
16713             case 37: // left
16714             case 39: // right
16715                 if (!this.keyboardNavigation) break;
16716                 dir = e.keyCode == 37 ? -1 : 1;
16717                 
16718                 if (e.ctrlKey){
16719                     newDate = this.moveYear(this.date, dir);
16720                     newViewDate = this.moveYear(this.viewDate, dir);
16721                 } else if (e.shiftKey){
16722                     newDate = this.moveMonth(this.date, dir);
16723                     newViewDate = this.moveMonth(this.viewDate, dir);
16724                 } else {
16725                     newDate = new Date(this.date);
16726                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16727                     newViewDate = new Date(this.viewDate);
16728                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16729                 }
16730                 if (this.dateWithinRange(newDate)){
16731                     this.date = newDate;
16732                     this.viewDate = newViewDate;
16733                     this.setValue(this.formatDate(this.date));
16734 //                    this.update();
16735                     e.preventDefault();
16736                     dateChanged = true;
16737                 }
16738                 break;
16739             case 38: // up
16740             case 40: // down
16741                 if (!this.keyboardNavigation) break;
16742                 dir = e.keyCode == 38 ? -1 : 1;
16743                 if (e.ctrlKey){
16744                     newDate = this.moveYear(this.date, dir);
16745                     newViewDate = this.moveYear(this.viewDate, dir);
16746                 } else if (e.shiftKey){
16747                     newDate = this.moveMonth(this.date, dir);
16748                     newViewDate = this.moveMonth(this.viewDate, dir);
16749                 } else {
16750                     newDate = new Date(this.date);
16751                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16752                     newViewDate = new Date(this.viewDate);
16753                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16754                 }
16755                 if (this.dateWithinRange(newDate)){
16756                     this.date = newDate;
16757                     this.viewDate = newViewDate;
16758                     this.setValue(this.formatDate(this.date));
16759 //                    this.update();
16760                     e.preventDefault();
16761                     dateChanged = true;
16762                 }
16763                 break;
16764             case 13: // enter
16765                 this.setValue(this.formatDate(this.date));
16766                 this.hide();
16767                 e.preventDefault();
16768                 break;
16769             case 9: // tab
16770                 this.setValue(this.formatDate(this.date));
16771                 this.hide();
16772                 break;
16773             case 16: // shift
16774             case 17: // ctrl
16775             case 18: // alt
16776                 break;
16777             default :
16778                 this.hide();
16779                 
16780         }
16781     },
16782     
16783     
16784     onClick: function(e) 
16785     {
16786         e.stopPropagation();
16787         e.preventDefault();
16788         
16789         var target = e.getTarget();
16790         
16791         if(target.nodeName.toLowerCase() === 'i'){
16792             target = Roo.get(target).dom.parentNode;
16793         }
16794         
16795         var nodeName = target.nodeName;
16796         var className = target.className;
16797         var html = target.innerHTML;
16798         //Roo.log(nodeName);
16799         
16800         switch(nodeName.toLowerCase()) {
16801             case 'th':
16802                 switch(className) {
16803                     case 'switch':
16804                         this.showMode(1);
16805                         break;
16806                     case 'prev':
16807                     case 'next':
16808                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16809                         switch(this.viewMode){
16810                                 case 0:
16811                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16812                                         break;
16813                                 case 1:
16814                                 case 2:
16815                                         this.viewDate = this.moveYear(this.viewDate, dir);
16816                                         break;
16817                         }
16818                         this.fill();
16819                         break;
16820                     case 'today':
16821                         var date = new Date();
16822                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16823 //                        this.fill()
16824                         this.setValue(this.formatDate(this.date));
16825                         
16826                         this.hide();
16827                         break;
16828                 }
16829                 break;
16830             case 'span':
16831                 if (className.indexOf('disabled') < 0) {
16832                     this.viewDate.setUTCDate(1);
16833                     if (className.indexOf('month') > -1) {
16834                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16835                     } else {
16836                         var year = parseInt(html, 10) || 0;
16837                         this.viewDate.setUTCFullYear(year);
16838                         
16839                     }
16840                     
16841                     if(this.singleMode){
16842                         this.setValue(this.formatDate(this.viewDate));
16843                         this.hide();
16844                         return;
16845                     }
16846                     
16847                     this.showMode(-1);
16848                     this.fill();
16849                 }
16850                 break;
16851                 
16852             case 'td':
16853                 //Roo.log(className);
16854                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16855                     var day = parseInt(html, 10) || 1;
16856                     var year = this.viewDate.getUTCFullYear(),
16857                         month = this.viewDate.getUTCMonth();
16858
16859                     if (className.indexOf('old') > -1) {
16860                         if(month === 0 ){
16861                             month = 11;
16862                             year -= 1;
16863                         }else{
16864                             month -= 1;
16865                         }
16866                     } else if (className.indexOf('new') > -1) {
16867                         if (month == 11) {
16868                             month = 0;
16869                             year += 1;
16870                         } else {
16871                             month += 1;
16872                         }
16873                     }
16874                     //Roo.log([year,month,day]);
16875                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16876                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16877 //                    this.fill();
16878                     //Roo.log(this.formatDate(this.date));
16879                     this.setValue(this.formatDate(this.date));
16880                     this.hide();
16881                 }
16882                 break;
16883         }
16884     },
16885     
16886     setStartDate: function(startDate)
16887     {
16888         this.startDate = startDate || -Infinity;
16889         if (this.startDate !== -Infinity) {
16890             this.startDate = this.parseDate(this.startDate);
16891         }
16892         this.update();
16893         this.updateNavArrows();
16894     },
16895
16896     setEndDate: function(endDate)
16897     {
16898         this.endDate = endDate || Infinity;
16899         if (this.endDate !== Infinity) {
16900             this.endDate = this.parseDate(this.endDate);
16901         }
16902         this.update();
16903         this.updateNavArrows();
16904     },
16905     
16906     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16907     {
16908         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
16909         if (typeof(this.daysOfWeekDisabled) !== 'object') {
16910             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
16911         }
16912         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
16913             return parseInt(d, 10);
16914         });
16915         this.update();
16916         this.updateNavArrows();
16917     },
16918     
16919     updateNavArrows: function() 
16920     {
16921         if(this.singleMode){
16922             return;
16923         }
16924         
16925         var d = new Date(this.viewDate),
16926         year = d.getUTCFullYear(),
16927         month = d.getUTCMonth();
16928         
16929         Roo.each(this.picker().select('.prev', true).elements, function(v){
16930             v.show();
16931             switch (this.viewMode) {
16932                 case 0:
16933
16934                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
16935                         v.hide();
16936                     }
16937                     break;
16938                 case 1:
16939                 case 2:
16940                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
16941                         v.hide();
16942                     }
16943                     break;
16944             }
16945         });
16946         
16947         Roo.each(this.picker().select('.next', true).elements, function(v){
16948             v.show();
16949             switch (this.viewMode) {
16950                 case 0:
16951
16952                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
16953                         v.hide();
16954                     }
16955                     break;
16956                 case 1:
16957                 case 2:
16958                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
16959                         v.hide();
16960                     }
16961                     break;
16962             }
16963         })
16964     },
16965     
16966     moveMonth: function(date, dir)
16967     {
16968         if (!dir) return date;
16969         var new_date = new Date(date.valueOf()),
16970         day = new_date.getUTCDate(),
16971         month = new_date.getUTCMonth(),
16972         mag = Math.abs(dir),
16973         new_month, test;
16974         dir = dir > 0 ? 1 : -1;
16975         if (mag == 1){
16976             test = dir == -1
16977             // If going back one month, make sure month is not current month
16978             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
16979             ? function(){
16980                 return new_date.getUTCMonth() == month;
16981             }
16982             // If going forward one month, make sure month is as expected
16983             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
16984             : function(){
16985                 return new_date.getUTCMonth() != new_month;
16986             };
16987             new_month = month + dir;
16988             new_date.setUTCMonth(new_month);
16989             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
16990             if (new_month < 0 || new_month > 11)
16991                 new_month = (new_month + 12) % 12;
16992         } else {
16993             // For magnitudes >1, move one month at a time...
16994             for (var i=0; i<mag; i++)
16995                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
16996                 new_date = this.moveMonth(new_date, dir);
16997             // ...then reset the day, keeping it in the new month
16998             new_month = new_date.getUTCMonth();
16999             new_date.setUTCDate(day);
17000             test = function(){
17001                 return new_month != new_date.getUTCMonth();
17002             };
17003         }
17004         // Common date-resetting loop -- if date is beyond end of month, make it
17005         // end of month
17006         while (test()){
17007             new_date.setUTCDate(--day);
17008             new_date.setUTCMonth(new_month);
17009         }
17010         return new_date;
17011     },
17012
17013     moveYear: function(date, dir)
17014     {
17015         return this.moveMonth(date, dir*12);
17016     },
17017
17018     dateWithinRange: function(date)
17019     {
17020         return date >= this.startDate && date <= this.endDate;
17021     },
17022
17023     
17024     remove: function() 
17025     {
17026         this.picker().remove();
17027     }
17028    
17029 });
17030
17031 Roo.apply(Roo.bootstrap.DateField,  {
17032     
17033     head : {
17034         tag: 'thead',
17035         cn: [
17036         {
17037             tag: 'tr',
17038             cn: [
17039             {
17040                 tag: 'th',
17041                 cls: 'prev',
17042                 html: '<i class="fa fa-arrow-left"/>'
17043             },
17044             {
17045                 tag: 'th',
17046                 cls: 'switch',
17047                 colspan: '5'
17048             },
17049             {
17050                 tag: 'th',
17051                 cls: 'next',
17052                 html: '<i class="fa fa-arrow-right"/>'
17053             }
17054
17055             ]
17056         }
17057         ]
17058     },
17059     
17060     content : {
17061         tag: 'tbody',
17062         cn: [
17063         {
17064             tag: 'tr',
17065             cn: [
17066             {
17067                 tag: 'td',
17068                 colspan: '7'
17069             }
17070             ]
17071         }
17072         ]
17073     },
17074     
17075     footer : {
17076         tag: 'tfoot',
17077         cn: [
17078         {
17079             tag: 'tr',
17080             cn: [
17081             {
17082                 tag: 'th',
17083                 colspan: '7',
17084                 cls: 'today'
17085             }
17086                     
17087             ]
17088         }
17089         ]
17090     },
17091     
17092     dates:{
17093         en: {
17094             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17095             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17096             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17097             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17098             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17099             today: "Today"
17100         }
17101     },
17102     
17103     modes: [
17104     {
17105         clsName: 'days',
17106         navFnc: 'Month',
17107         navStep: 1
17108     },
17109     {
17110         clsName: 'months',
17111         navFnc: 'FullYear',
17112         navStep: 1
17113     },
17114     {
17115         clsName: 'years',
17116         navFnc: 'FullYear',
17117         navStep: 10
17118     }]
17119 });
17120
17121 Roo.apply(Roo.bootstrap.DateField,  {
17122   
17123     template : {
17124         tag: 'div',
17125         cls: 'datepicker dropdown-menu roo-dynamic',
17126         cn: [
17127         {
17128             tag: 'div',
17129             cls: 'datepicker-days',
17130             cn: [
17131             {
17132                 tag: 'table',
17133                 cls: 'table-condensed',
17134                 cn:[
17135                 Roo.bootstrap.DateField.head,
17136                 {
17137                     tag: 'tbody'
17138                 },
17139                 Roo.bootstrap.DateField.footer
17140                 ]
17141             }
17142             ]
17143         },
17144         {
17145             tag: 'div',
17146             cls: 'datepicker-months',
17147             cn: [
17148             {
17149                 tag: 'table',
17150                 cls: 'table-condensed',
17151                 cn:[
17152                 Roo.bootstrap.DateField.head,
17153                 Roo.bootstrap.DateField.content,
17154                 Roo.bootstrap.DateField.footer
17155                 ]
17156             }
17157             ]
17158         },
17159         {
17160             tag: 'div',
17161             cls: 'datepicker-years',
17162             cn: [
17163             {
17164                 tag: 'table',
17165                 cls: 'table-condensed',
17166                 cn:[
17167                 Roo.bootstrap.DateField.head,
17168                 Roo.bootstrap.DateField.content,
17169                 Roo.bootstrap.DateField.footer
17170                 ]
17171             }
17172             ]
17173         }
17174         ]
17175     }
17176 });
17177
17178  
17179
17180  /*
17181  * - LGPL
17182  *
17183  * TimeField
17184  * 
17185  */
17186
17187 /**
17188  * @class Roo.bootstrap.TimeField
17189  * @extends Roo.bootstrap.Input
17190  * Bootstrap DateField class
17191  * 
17192  * 
17193  * @constructor
17194  * Create a new TimeField
17195  * @param {Object} config The config object
17196  */
17197
17198 Roo.bootstrap.TimeField = function(config){
17199     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17200     this.addEvents({
17201             /**
17202              * @event show
17203              * Fires when this field show.
17204              * @param {Roo.bootstrap.DateField} thisthis
17205              * @param {Mixed} date The date value
17206              */
17207             show : true,
17208             /**
17209              * @event show
17210              * Fires when this field hide.
17211              * @param {Roo.bootstrap.DateField} this
17212              * @param {Mixed} date The date value
17213              */
17214             hide : true,
17215             /**
17216              * @event select
17217              * Fires when select a date.
17218              * @param {Roo.bootstrap.DateField} this
17219              * @param {Mixed} date The date value
17220              */
17221             select : true
17222         });
17223 };
17224
17225 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17226     
17227     /**
17228      * @cfg {String} format
17229      * The default time format string which can be overriden for localization support.  The format must be
17230      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17231      */
17232     format : "H:i",
17233        
17234     onRender: function(ct, position)
17235     {
17236         
17237         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17238                 
17239         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17240         
17241         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17242         
17243         this.pop = this.picker().select('>.datepicker-time',true).first();
17244         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17245         
17246         this.picker().on('mousedown', this.onMousedown, this);
17247         this.picker().on('click', this.onClick, this);
17248         
17249         this.picker().addClass('datepicker-dropdown');
17250     
17251         this.fillTime();
17252         this.update();
17253             
17254         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17255         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17256         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17257         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17258         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17259         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17260
17261     },
17262     
17263     fireKey: function(e){
17264         if (!this.picker().isVisible()){
17265             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17266                 this.show();
17267             }
17268             return;
17269         }
17270
17271         e.preventDefault();
17272         
17273         switch(e.keyCode){
17274             case 27: // escape
17275                 this.hide();
17276                 break;
17277             case 37: // left
17278             case 39: // right
17279                 this.onTogglePeriod();
17280                 break;
17281             case 38: // up
17282                 this.onIncrementMinutes();
17283                 break;
17284             case 40: // down
17285                 this.onDecrementMinutes();
17286                 break;
17287             case 13: // enter
17288             case 9: // tab
17289                 this.setTime();
17290                 break;
17291         }
17292     },
17293     
17294     onClick: function(e) {
17295         e.stopPropagation();
17296         e.preventDefault();
17297     },
17298     
17299     picker : function()
17300     {
17301         return this.el.select('.datepicker', true).first();
17302     },
17303     
17304     fillTime: function()
17305     {    
17306         var time = this.pop.select('tbody', true).first();
17307         
17308         time.dom.innerHTML = '';
17309         
17310         time.createChild({
17311             tag: 'tr',
17312             cn: [
17313                 {
17314                     tag: 'td',
17315                     cn: [
17316                         {
17317                             tag: 'a',
17318                             href: '#',
17319                             cls: 'btn',
17320                             cn: [
17321                                 {
17322                                     tag: 'span',
17323                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17324                                 }
17325                             ]
17326                         } 
17327                     ]
17328                 },
17329                 {
17330                     tag: 'td',
17331                     cls: 'separator'
17332                 },
17333                 {
17334                     tag: 'td',
17335                     cn: [
17336                         {
17337                             tag: 'a',
17338                             href: '#',
17339                             cls: 'btn',
17340                             cn: [
17341                                 {
17342                                     tag: 'span',
17343                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17344                                 }
17345                             ]
17346                         }
17347                     ]
17348                 },
17349                 {
17350                     tag: 'td',
17351                     cls: 'separator'
17352                 }
17353             ]
17354         });
17355         
17356         time.createChild({
17357             tag: 'tr',
17358             cn: [
17359                 {
17360                     tag: 'td',
17361                     cn: [
17362                         {
17363                             tag: 'span',
17364                             cls: 'timepicker-hour',
17365                             html: '00'
17366                         }  
17367                     ]
17368                 },
17369                 {
17370                     tag: 'td',
17371                     cls: 'separator',
17372                     html: ':'
17373                 },
17374                 {
17375                     tag: 'td',
17376                     cn: [
17377                         {
17378                             tag: 'span',
17379                             cls: 'timepicker-minute',
17380                             html: '00'
17381                         }  
17382                     ]
17383                 },
17384                 {
17385                     tag: 'td',
17386                     cls: 'separator'
17387                 },
17388                 {
17389                     tag: 'td',
17390                     cn: [
17391                         {
17392                             tag: 'button',
17393                             type: 'button',
17394                             cls: 'btn btn-primary period',
17395                             html: 'AM'
17396                             
17397                         }
17398                     ]
17399                 }
17400             ]
17401         });
17402         
17403         time.createChild({
17404             tag: 'tr',
17405             cn: [
17406                 {
17407                     tag: 'td',
17408                     cn: [
17409                         {
17410                             tag: 'a',
17411                             href: '#',
17412                             cls: 'btn',
17413                             cn: [
17414                                 {
17415                                     tag: 'span',
17416                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17417                                 }
17418                             ]
17419                         }
17420                     ]
17421                 },
17422                 {
17423                     tag: 'td',
17424                     cls: 'separator'
17425                 },
17426                 {
17427                     tag: 'td',
17428                     cn: [
17429                         {
17430                             tag: 'a',
17431                             href: '#',
17432                             cls: 'btn',
17433                             cn: [
17434                                 {
17435                                     tag: 'span',
17436                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17437                                 }
17438                             ]
17439                         }
17440                     ]
17441                 },
17442                 {
17443                     tag: 'td',
17444                     cls: 'separator'
17445                 }
17446             ]
17447         });
17448         
17449     },
17450     
17451     update: function()
17452     {
17453         
17454         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17455         
17456         this.fill();
17457     },
17458     
17459     fill: function() 
17460     {
17461         var hours = this.time.getHours();
17462         var minutes = this.time.getMinutes();
17463         var period = 'AM';
17464         
17465         if(hours > 11){
17466             period = 'PM';
17467         }
17468         
17469         if(hours == 0){
17470             hours = 12;
17471         }
17472         
17473         
17474         if(hours > 12){
17475             hours = hours - 12;
17476         }
17477         
17478         if(hours < 10){
17479             hours = '0' + hours;
17480         }
17481         
17482         if(minutes < 10){
17483             minutes = '0' + minutes;
17484         }
17485         
17486         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17487         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17488         this.pop.select('button', true).first().dom.innerHTML = period;
17489         
17490     },
17491     
17492     place: function()
17493     {   
17494         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17495         
17496         var cls = ['bottom'];
17497         
17498         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17499             cls.pop();
17500             cls.push('top');
17501         }
17502         
17503         cls.push('right');
17504         
17505         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17506             cls.pop();
17507             cls.push('left');
17508         }
17509         
17510         this.picker().addClass(cls.join('-'));
17511         
17512         var _this = this;
17513         
17514         Roo.each(cls, function(c){
17515             if(c == 'bottom'){
17516                 _this.picker().setTop(_this.inputEl().getHeight());
17517                 return;
17518             }
17519             if(c == 'top'){
17520                 _this.picker().setTop(0 - _this.picker().getHeight());
17521                 return;
17522             }
17523             
17524             if(c == 'left'){
17525                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17526                 return;
17527             }
17528             if(c == 'right'){
17529                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17530                 return;
17531             }
17532         });
17533         
17534     },
17535   
17536     onFocus : function()
17537     {
17538         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17539         this.show();
17540     },
17541     
17542     onBlur : function()
17543     {
17544         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17545         this.hide();
17546     },
17547     
17548     show : function()
17549     {
17550         this.picker().show();
17551         this.pop.show();
17552         this.update();
17553         this.place();
17554         
17555         this.fireEvent('show', this, this.date);
17556     },
17557     
17558     hide : function()
17559     {
17560         this.picker().hide();
17561         this.pop.hide();
17562         
17563         this.fireEvent('hide', this, this.date);
17564     },
17565     
17566     setTime : function()
17567     {
17568         this.hide();
17569         this.setValue(this.time.format(this.format));
17570         
17571         this.fireEvent('select', this, this.date);
17572         
17573         
17574     },
17575     
17576     onMousedown: function(e){
17577         e.stopPropagation();
17578         e.preventDefault();
17579     },
17580     
17581     onIncrementHours: function()
17582     {
17583         Roo.log('onIncrementHours');
17584         this.time = this.time.add(Date.HOUR, 1);
17585         this.update();
17586         
17587     },
17588     
17589     onDecrementHours: function()
17590     {
17591         Roo.log('onDecrementHours');
17592         this.time = this.time.add(Date.HOUR, -1);
17593         this.update();
17594     },
17595     
17596     onIncrementMinutes: function()
17597     {
17598         Roo.log('onIncrementMinutes');
17599         this.time = this.time.add(Date.MINUTE, 1);
17600         this.update();
17601     },
17602     
17603     onDecrementMinutes: function()
17604     {
17605         Roo.log('onDecrementMinutes');
17606         this.time = this.time.add(Date.MINUTE, -1);
17607         this.update();
17608     },
17609     
17610     onTogglePeriod: function()
17611     {
17612         Roo.log('onTogglePeriod');
17613         this.time = this.time.add(Date.HOUR, 12);
17614         this.update();
17615     }
17616     
17617    
17618 });
17619
17620 Roo.apply(Roo.bootstrap.TimeField,  {
17621     
17622     content : {
17623         tag: 'tbody',
17624         cn: [
17625             {
17626                 tag: 'tr',
17627                 cn: [
17628                 {
17629                     tag: 'td',
17630                     colspan: '7'
17631                 }
17632                 ]
17633             }
17634         ]
17635     },
17636     
17637     footer : {
17638         tag: 'tfoot',
17639         cn: [
17640             {
17641                 tag: 'tr',
17642                 cn: [
17643                 {
17644                     tag: 'th',
17645                     colspan: '7',
17646                     cls: '',
17647                     cn: [
17648                         {
17649                             tag: 'button',
17650                             cls: 'btn btn-info ok',
17651                             html: 'OK'
17652                         }
17653                     ]
17654                 }
17655
17656                 ]
17657             }
17658         ]
17659     }
17660 });
17661
17662 Roo.apply(Roo.bootstrap.TimeField,  {
17663   
17664     template : {
17665         tag: 'div',
17666         cls: 'datepicker dropdown-menu',
17667         cn: [
17668             {
17669                 tag: 'div',
17670                 cls: 'datepicker-time',
17671                 cn: [
17672                 {
17673                     tag: 'table',
17674                     cls: 'table-condensed',
17675                     cn:[
17676                     Roo.bootstrap.TimeField.content,
17677                     Roo.bootstrap.TimeField.footer
17678                     ]
17679                 }
17680                 ]
17681             }
17682         ]
17683     }
17684 });
17685
17686  
17687
17688  /*
17689  * - LGPL
17690  *
17691  * MonthField
17692  * 
17693  */
17694
17695 /**
17696  * @class Roo.bootstrap.MonthField
17697  * @extends Roo.bootstrap.Input
17698  * Bootstrap MonthField class
17699  * 
17700  * @cfg {String} language default en
17701  * 
17702  * @constructor
17703  * Create a new MonthField
17704  * @param {Object} config The config object
17705  */
17706
17707 Roo.bootstrap.MonthField = function(config){
17708     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17709     
17710     this.addEvents({
17711         /**
17712          * @event show
17713          * Fires when this field show.
17714          * @param {Roo.bootstrap.MonthField} this
17715          * @param {Mixed} date The date value
17716          */
17717         show : true,
17718         /**
17719          * @event show
17720          * Fires when this field hide.
17721          * @param {Roo.bootstrap.MonthField} this
17722          * @param {Mixed} date The date value
17723          */
17724         hide : true,
17725         /**
17726          * @event select
17727          * Fires when select a date.
17728          * @param {Roo.bootstrap.MonthField} this
17729          * @param {String} oldvalue The old value
17730          * @param {String} newvalue The new value
17731          */
17732         select : true
17733     });
17734 };
17735
17736 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17737     
17738     onRender: function(ct, position)
17739     {
17740         
17741         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17742         
17743         this.language = this.language || 'en';
17744         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17745         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17746         
17747         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17748         this.isInline = false;
17749         this.isInput = true;
17750         this.component = this.el.select('.add-on', true).first() || false;
17751         this.component = (this.component && this.component.length === 0) ? false : this.component;
17752         this.hasInput = this.component && this.inputEL().length;
17753         
17754         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17755         
17756         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17757         
17758         this.picker().on('mousedown', this.onMousedown, this);
17759         this.picker().on('click', this.onClick, this);
17760         
17761         this.picker().addClass('datepicker-dropdown');
17762         
17763         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17764             v.setStyle('width', '189px');
17765         });
17766         
17767         this.fillMonths();
17768         
17769         this.update();
17770         
17771         if(this.isInline) {
17772             this.show();
17773         }
17774         
17775     },
17776     
17777     setValue: function(v, suppressEvent)
17778     {   
17779         var o = this.getValue();
17780         
17781         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17782         
17783         this.update();
17784
17785         if(suppressEvent !== true){
17786             this.fireEvent('select', this, o, v);
17787         }
17788         
17789     },
17790     
17791     getValue: function()
17792     {
17793         return this.value;
17794     },
17795     
17796     onClick: function(e) 
17797     {
17798         e.stopPropagation();
17799         e.preventDefault();
17800         
17801         var target = e.getTarget();
17802         
17803         if(target.nodeName.toLowerCase() === 'i'){
17804             target = Roo.get(target).dom.parentNode;
17805         }
17806         
17807         var nodeName = target.nodeName;
17808         var className = target.className;
17809         var html = target.innerHTML;
17810         
17811         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17812             return;
17813         }
17814         
17815         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17816         
17817         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17818         
17819         this.hide();
17820                         
17821     },
17822     
17823     picker : function()
17824     {
17825         return this.pickerEl;
17826     },
17827     
17828     fillMonths: function()
17829     {    
17830         var i = 0;
17831         var months = this.picker().select('>.datepicker-months td', true).first();
17832         
17833         months.dom.innerHTML = '';
17834         
17835         while (i < 12) {
17836             var month = {
17837                 tag: 'span',
17838                 cls: 'month',
17839                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17840             }
17841             
17842             months.createChild(month);
17843         }
17844         
17845     },
17846     
17847     update: function()
17848     {
17849         var _this = this;
17850         
17851         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17852             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17853         }
17854         
17855         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17856             e.removeClass('active');
17857             
17858             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17859                 e.addClass('active');
17860             }
17861         })
17862     },
17863     
17864     place: function()
17865     {
17866         if(this.isInline) return;
17867         
17868         this.picker().removeClass(['bottom', 'top']);
17869         
17870         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17871             /*
17872              * place to the top of element!
17873              *
17874              */
17875             
17876             this.picker().addClass('top');
17877             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17878             
17879             return;
17880         }
17881         
17882         this.picker().addClass('bottom');
17883         
17884         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17885     },
17886     
17887     onFocus : function()
17888     {
17889         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17890         this.show();
17891     },
17892     
17893     onBlur : function()
17894     {
17895         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17896         
17897         var d = this.inputEl().getValue();
17898         
17899         this.setValue(d);
17900                 
17901         this.hide();
17902     },
17903     
17904     show : function()
17905     {
17906         this.picker().show();
17907         this.picker().select('>.datepicker-months', true).first().show();
17908         this.update();
17909         this.place();
17910         
17911         this.fireEvent('show', this, this.date);
17912     },
17913     
17914     hide : function()
17915     {
17916         if(this.isInline) return;
17917         this.picker().hide();
17918         this.fireEvent('hide', this, this.date);
17919         
17920     },
17921     
17922     onMousedown: function(e)
17923     {
17924         e.stopPropagation();
17925         e.preventDefault();
17926     },
17927     
17928     keyup: function(e)
17929     {
17930         Roo.bootstrap.MonthField.superclass.keyup.call(this);
17931         this.update();
17932     },
17933
17934     fireKey: function(e)
17935     {
17936         if (!this.picker().isVisible()){
17937             if (e.keyCode == 27) // allow escape to hide and re-show picker
17938                 this.show();
17939             return;
17940         }
17941         
17942         var dir;
17943         
17944         switch(e.keyCode){
17945             case 27: // escape
17946                 this.hide();
17947                 e.preventDefault();
17948                 break;
17949             case 37: // left
17950             case 39: // right
17951                 dir = e.keyCode == 37 ? -1 : 1;
17952                 
17953                 this.vIndex = this.vIndex + dir;
17954                 
17955                 if(this.vIndex < 0){
17956                     this.vIndex = 0;
17957                 }
17958                 
17959                 if(this.vIndex > 11){
17960                     this.vIndex = 11;
17961                 }
17962                 
17963                 if(isNaN(this.vIndex)){
17964                     this.vIndex = 0;
17965                 }
17966                 
17967                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17968                 
17969                 break;
17970             case 38: // up
17971             case 40: // down
17972                 
17973                 dir = e.keyCode == 38 ? -1 : 1;
17974                 
17975                 this.vIndex = this.vIndex + dir * 4;
17976                 
17977                 if(this.vIndex < 0){
17978                     this.vIndex = 0;
17979                 }
17980                 
17981                 if(this.vIndex > 11){
17982                     this.vIndex = 11;
17983                 }
17984                 
17985                 if(isNaN(this.vIndex)){
17986                     this.vIndex = 0;
17987                 }
17988                 
17989                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17990                 break;
17991                 
17992             case 13: // enter
17993                 
17994                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
17995                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17996                 }
17997                 
17998                 this.hide();
17999                 e.preventDefault();
18000                 break;
18001             case 9: // tab
18002                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18003                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18004                 }
18005                 this.hide();
18006                 break;
18007             case 16: // shift
18008             case 17: // ctrl
18009             case 18: // alt
18010                 break;
18011             default :
18012                 this.hide();
18013                 
18014         }
18015     },
18016     
18017     remove: function() 
18018     {
18019         this.picker().remove();
18020     }
18021    
18022 });
18023
18024 Roo.apply(Roo.bootstrap.MonthField,  {
18025     
18026     content : {
18027         tag: 'tbody',
18028         cn: [
18029         {
18030             tag: 'tr',
18031             cn: [
18032             {
18033                 tag: 'td',
18034                 colspan: '7'
18035             }
18036             ]
18037         }
18038         ]
18039     },
18040     
18041     dates:{
18042         en: {
18043             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18044             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18045         }
18046     }
18047 });
18048
18049 Roo.apply(Roo.bootstrap.MonthField,  {
18050   
18051     template : {
18052         tag: 'div',
18053         cls: 'datepicker dropdown-menu roo-dynamic',
18054         cn: [
18055             {
18056                 tag: 'div',
18057                 cls: 'datepicker-months',
18058                 cn: [
18059                 {
18060                     tag: 'table',
18061                     cls: 'table-condensed',
18062                     cn:[
18063                         Roo.bootstrap.DateField.content
18064                     ]
18065                 }
18066                 ]
18067             }
18068         ]
18069     }
18070 });
18071
18072  
18073
18074  
18075  /*
18076  * - LGPL
18077  *
18078  * CheckBox
18079  * 
18080  */
18081
18082 /**
18083  * @class Roo.bootstrap.CheckBox
18084  * @extends Roo.bootstrap.Input
18085  * Bootstrap CheckBox class
18086  * 
18087  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18088  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18089  * @cfg {String} boxLabel The text that appears beside the checkbox
18090  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18091  * @cfg {Boolean} checked initnal the element
18092  * @cfg {Boolean} inline inline the element (default false)
18093  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18094  * 
18095  * @constructor
18096  * Create a new CheckBox
18097  * @param {Object} config The config object
18098  */
18099
18100 Roo.bootstrap.CheckBox = function(config){
18101     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18102    
18103     this.addEvents({
18104         /**
18105         * @event check
18106         * Fires when the element is checked or unchecked.
18107         * @param {Roo.bootstrap.CheckBox} this This input
18108         * @param {Boolean} checked The new checked value
18109         */
18110        check : true
18111     });
18112     
18113 };
18114
18115 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18116   
18117     inputType: 'checkbox',
18118     inputValue: 1,
18119     valueOff: 0,
18120     boxLabel: false,
18121     checked: false,
18122     weight : false,
18123     inline: false,
18124     
18125     getAutoCreate : function()
18126     {
18127         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18128         
18129         var id = Roo.id();
18130         
18131         var cfg = {};
18132         
18133         cfg.cls = 'form-group ' + this.inputType; //input-group
18134         
18135         if(this.inline){
18136             cfg.cls += ' ' + this.inputType + '-inline';
18137         }
18138         
18139         var input =  {
18140             tag: 'input',
18141             id : id,
18142             type : this.inputType,
18143             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18144             cls : 'roo-' + this.inputType, //'form-box',
18145             placeholder : this.placeholder || ''
18146             
18147         };
18148         
18149         if (this.weight) { // Validity check?
18150             cfg.cls += " " + this.inputType + "-" + this.weight;
18151         }
18152         
18153         if (this.disabled) {
18154             input.disabled=true;
18155         }
18156         
18157         if(this.checked){
18158             input.checked = this.checked;
18159         }
18160         
18161         if (this.name) {
18162             input.name = this.name;
18163         }
18164         
18165         if (this.size) {
18166             input.cls += ' input-' + this.size;
18167         }
18168         
18169         var settings=this;
18170         
18171         ['xs','sm','md','lg'].map(function(size){
18172             if (settings[size]) {
18173                 cfg.cls += ' col-' + size + '-' + settings[size];
18174             }
18175         });
18176         
18177         var inputblock = input;
18178          
18179         if (this.before || this.after) {
18180             
18181             inputblock = {
18182                 cls : 'input-group',
18183                 cn :  [] 
18184             };
18185             
18186             if (this.before) {
18187                 inputblock.cn.push({
18188                     tag :'span',
18189                     cls : 'input-group-addon',
18190                     html : this.before
18191                 });
18192             }
18193             
18194             inputblock.cn.push(input);
18195             
18196             if (this.after) {
18197                 inputblock.cn.push({
18198                     tag :'span',
18199                     cls : 'input-group-addon',
18200                     html : this.after
18201                 });
18202             }
18203             
18204         }
18205         
18206         if (align ==='left' && this.fieldLabel.length) {
18207                 Roo.log("left and has label");
18208                 cfg.cn = [
18209                     
18210                     {
18211                         tag: 'label',
18212                         'for' :  id,
18213                         cls : 'control-label col-md-' + this.labelWidth,
18214                         html : this.fieldLabel
18215                         
18216                     },
18217                     {
18218                         cls : "col-md-" + (12 - this.labelWidth), 
18219                         cn: [
18220                             inputblock
18221                         ]
18222                     }
18223                     
18224                 ];
18225         } else if ( this.fieldLabel.length) {
18226                 Roo.log(" label");
18227                 cfg.cn = [
18228                    
18229                     {
18230                         tag: this.boxLabel ? 'span' : 'label',
18231                         'for': id,
18232                         cls: 'control-label box-input-label',
18233                         //cls : 'input-group-addon',
18234                         html : this.fieldLabel
18235                         
18236                     },
18237                     
18238                     inputblock
18239                     
18240                 ];
18241
18242         } else {
18243             
18244                 Roo.log(" no label && no align");
18245                 cfg.cn = [  inputblock ] ;
18246                 
18247                 
18248         }
18249         if(this.boxLabel){
18250              var boxLabelCfg = {
18251                 tag: 'label',
18252                 //'for': id, // box label is handled by onclick - so no for...
18253                 cls: 'box-label',
18254                 html: this.boxLabel
18255             }
18256             
18257             if(this.tooltip){
18258                 boxLabelCfg.tooltip = this.tooltip;
18259             }
18260              
18261             cfg.cn.push(boxLabelCfg);
18262         }
18263         
18264         
18265        
18266         return cfg;
18267         
18268     },
18269     
18270     /**
18271      * return the real input element.
18272      */
18273     inputEl: function ()
18274     {
18275         return this.el.select('input.roo-' + this.inputType,true).first();
18276     },
18277     
18278     labelEl: function()
18279     {
18280         return this.el.select('label.control-label',true).first();
18281     },
18282     /* depricated... */
18283     
18284     label: function()
18285     {
18286         return this.labelEl();
18287     },
18288     
18289     initEvents : function()
18290     {
18291 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18292         
18293         this.inputEl().on('click', this.onClick,  this);
18294         
18295         if (this.boxLabel) { 
18296             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18297         }
18298         
18299         this.startValue = this.getValue();
18300         
18301         if(this.groupId){
18302             Roo.bootstrap.CheckBox.register(this);
18303         }
18304     },
18305     
18306     onClick : function()
18307     {   
18308         this.setChecked(!this.checked);
18309     },
18310     
18311     setChecked : function(state,suppressEvent)
18312     {
18313         this.startValue = this.getValue();
18314         
18315         if(this.inputType == 'radio'){
18316             
18317             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18318                 e.dom.checked = false;
18319             });
18320             
18321             this.inputEl().dom.checked = true;
18322             
18323             this.inputEl().dom.value = this.inputValue;
18324             
18325             if(suppressEvent !== true){
18326                 this.fireEvent('check', this, true);
18327             }
18328             
18329             this.validate();
18330             
18331             return;
18332         }
18333         
18334         this.checked = state;
18335         
18336         this.inputEl().dom.checked = state;
18337         
18338         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18339         
18340         if(suppressEvent !== true){
18341             this.fireEvent('check', this, state);
18342         }
18343         
18344         this.validate();
18345     },
18346     
18347     getValue : function()
18348     {
18349         if(this.inputType == 'radio'){
18350             return this.getGroupValue();
18351         }
18352         
18353         return this.inputEl().getValue();
18354         
18355     },
18356     
18357     getGroupValue : function()
18358     {
18359         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18360             return '';
18361         }
18362         
18363         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18364     },
18365     
18366     setValue : function(v,suppressEvent)
18367     {
18368         if(this.inputType == 'radio'){
18369             this.setGroupValue(v, suppressEvent);
18370             return;
18371         }
18372         
18373         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18374         
18375         this.validate();
18376     },
18377     
18378     setGroupValue : function(v, suppressEvent)
18379     {
18380         this.startValue = this.getValue();
18381         
18382         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18383             e.dom.checked = false;
18384             
18385             if(e.dom.value == v){
18386                 e.dom.checked = true;
18387             }
18388         });
18389         
18390         if(suppressEvent !== true){
18391             this.fireEvent('check', this, true);
18392         }
18393
18394         this.validate();
18395         
18396         return;
18397     },
18398     
18399     validate : function()
18400     {
18401         if(
18402                 this.disabled || 
18403                 (this.inputType == 'radio' && this.validateRadio()) ||
18404                 (this.inputType == 'checkbox' && this.validateCheckbox())
18405         ){
18406             this.markValid();
18407             return true;
18408         }
18409         
18410         this.markInvalid();
18411         return false;
18412     },
18413     
18414     validateRadio : function()
18415     {
18416         var valid = false;
18417         
18418         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18419             if(!e.dom.checked){
18420                 return;
18421             }
18422             
18423             valid = true;
18424             
18425             return false;
18426         });
18427         
18428         return valid;
18429     },
18430     
18431     validateCheckbox : function()
18432     {
18433         if(!this.groupId){
18434             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18435         }
18436         
18437         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18438         
18439         if(!group){
18440             return false;
18441         }
18442         
18443         var r = false;
18444         
18445         for(var i in group){
18446             if(r){
18447                 break;
18448             }
18449             
18450             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18451         }
18452         
18453         return r;
18454     },
18455     
18456     /**
18457      * Mark this field as valid
18458      */
18459     markValid : function()
18460     {
18461         if(this.allowBlank){
18462             return;
18463         }
18464         
18465         var _this = this;
18466         
18467         this.fireEvent('valid', this);
18468         
18469         if(this.inputType == 'radio'){
18470             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18471                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18472                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18473             });
18474             
18475             return;
18476         }
18477         
18478         if(!this.groupId){
18479             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18480             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18481             return;
18482         }
18483         
18484         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18485             
18486         if(!group){
18487             return;
18488         }
18489         
18490         for(var i in group){
18491             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18492             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18493         }
18494     },
18495     
18496      /**
18497      * Mark this field as invalid
18498      * @param {String} msg The validation message
18499      */
18500     markInvalid : function(msg)
18501     {
18502         if(this.allowBlank){
18503             return;
18504         }
18505         
18506         var _this = this;
18507         
18508         this.fireEvent('invalid', this, msg);
18509         
18510         if(this.inputType == 'radio'){
18511             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18512                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18513                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18514             });
18515             
18516             return;
18517         }
18518         
18519         if(!this.groupId){
18520             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18521             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18522             return;
18523         }
18524         
18525         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18526             
18527         if(!group){
18528             return;
18529         }
18530         
18531         for(var i in group){
18532             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18533             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18534         }
18535         
18536     }
18537     
18538 });
18539
18540 Roo.apply(Roo.bootstrap.CheckBox, {
18541     
18542     groups: {},
18543     
18544      /**
18545     * register a CheckBox Group
18546     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18547     */
18548     register : function(checkbox)
18549     {
18550         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18551             this.groups[checkbox.groupId] = {};
18552         }
18553         
18554         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18555             return;
18556         }
18557         
18558         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18559         
18560     },
18561     /**
18562     * fetch a CheckBox Group based on the group ID
18563     * @param {string} the group ID
18564     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18565     */
18566     get: function(groupId) {
18567         if (typeof(this.groups[groupId]) == 'undefined') {
18568             return false;
18569         }
18570         
18571         return this.groups[groupId] ;
18572     }
18573     
18574     
18575 });
18576 /*
18577  * - LGPL
18578  *
18579  * Radio
18580  *
18581  *
18582  * not inline
18583  *<div class="radio">
18584   <label>
18585     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18586     Option one is this and that&mdash;be sure to include why it's great
18587   </label>
18588 </div>
18589  *
18590  *
18591  *inline
18592  *<span>
18593  *<label class="radio-inline">fieldLabel</label>
18594  *<label class="radio-inline">
18595   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18596 </label>
18597 <span>
18598  * 
18599  * 
18600  */
18601
18602 /**
18603  * @class Roo.bootstrap.Radio
18604  * @extends Roo.bootstrap.CheckBox
18605  * Bootstrap Radio class
18606
18607  * @constructor
18608  * Create a new Radio
18609  * @param {Object} config The config object
18610  */
18611
18612 Roo.bootstrap.Radio = function(config){
18613     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18614    
18615 };
18616
18617 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18618     
18619     inputType: 'radio',
18620     inputValue: '',
18621     valueOff: '',
18622     
18623     getAutoCreate : function()
18624     {
18625         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18626         align = align || 'left'; // default...
18627         
18628         
18629         
18630         var id = Roo.id();
18631         
18632         var cfg = {
18633                 tag : this.inline ? 'span' : 'div',
18634                 cls : '',
18635                 cn : []
18636         };
18637         
18638         var inline = this.inline ? ' radio-inline' : '';
18639         
18640         var lbl = {
18641                 tag: 'label' ,
18642                 // does not need for, as we wrap the input with it..
18643                 'for' : id,
18644                 cls : 'control-label box-label' + inline,
18645                 cn : []
18646         };
18647         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18648         
18649         var fieldLabel = {
18650             tag: 'label' ,
18651             //cls : 'control-label' + inline,
18652             html : this.fieldLabel,
18653             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18654         };
18655         
18656  
18657         
18658         
18659         var input =  {
18660             tag: 'input',
18661             id : id,
18662             type : this.inputType,
18663             //value : (!this.checked) ? this.valueOff : this.inputValue,
18664             value : this.inputValue,
18665             cls : 'roo-radio',
18666             placeholder : this.placeholder || '' // ?? needed????
18667             
18668         };
18669         if (this.weight) { // Validity check?
18670             input.cls += " radio-" + this.weight;
18671         }
18672         if (this.disabled) {
18673             input.disabled=true;
18674         }
18675         
18676         if(this.checked){
18677             input.checked = this.checked;
18678         }
18679         
18680         if (this.name) {
18681             input.name = this.name;
18682         }
18683         
18684         if (this.size) {
18685             input.cls += ' input-' + this.size;
18686         }
18687         
18688         //?? can span's inline have a width??
18689         
18690         var settings=this;
18691         ['xs','sm','md','lg'].map(function(size){
18692             if (settings[size]) {
18693                 cfg.cls += ' col-' + size + '-' + settings[size];
18694             }
18695         });
18696         
18697         var inputblock = input;
18698         
18699         if (this.before || this.after) {
18700             
18701             inputblock = {
18702                 cls : 'input-group',
18703                 tag : 'span',
18704                 cn :  [] 
18705             };
18706             if (this.before) {
18707                 inputblock.cn.push({
18708                     tag :'span',
18709                     cls : 'input-group-addon',
18710                     html : this.before
18711                 });
18712             }
18713             inputblock.cn.push(input);
18714             if (this.after) {
18715                 inputblock.cn.push({
18716                     tag :'span',
18717                     cls : 'input-group-addon',
18718                     html : this.after
18719                 });
18720             }
18721             
18722         };
18723         
18724         
18725         if (this.fieldLabel && this.fieldLabel.length) {
18726             cfg.cn.push(fieldLabel);
18727         }
18728        
18729         // normal bootstrap puts the input inside the label.
18730         // however with our styled version - it has to go after the input.
18731        
18732         //lbl.cn.push(inputblock);
18733         
18734         var lblwrap =  {
18735             tag: 'span',
18736             cls: 'radio' + inline,
18737             cn: [
18738                 inputblock,
18739                 lbl
18740             ]
18741         };
18742         
18743         cfg.cn.push( lblwrap);
18744         
18745         if(this.boxLabel){
18746             lbl.cn.push({
18747                 tag: 'span',
18748                 html: this.boxLabel
18749             })
18750         }
18751          
18752         
18753         return cfg;
18754         
18755     },
18756     
18757     initEvents : function()
18758     {
18759 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18760         
18761         this.inputEl().on('click', this.onClick,  this);
18762         if (this.boxLabel) {
18763             Roo.log('find label')
18764             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18765         }
18766         
18767     },
18768     
18769     inputEl: function ()
18770     {
18771         return this.el.select('input.roo-radio',true).first();
18772     },
18773     onClick : function()
18774     {   
18775         Roo.log("click");
18776         this.setChecked(true);
18777     },
18778     
18779     setChecked : function(state,suppressEvent)
18780     {
18781         if(state){
18782             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18783                 v.dom.checked = false;
18784             });
18785         }
18786         Roo.log(this.inputEl().dom);
18787         this.checked = state;
18788         this.inputEl().dom.checked = state;
18789         
18790         if(suppressEvent !== true){
18791             this.fireEvent('check', this, state);
18792         }
18793         
18794         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18795         
18796     },
18797     
18798     getGroupValue : function()
18799     {
18800         var value = '';
18801         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18802             if(v.dom.checked == true){
18803                 value = v.dom.value;
18804             }
18805         });
18806         
18807         return value;
18808     },
18809     
18810     /**
18811      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18812      * @return {Mixed} value The field value
18813      */
18814     getValue : function(){
18815         return this.getGroupValue();
18816     }
18817     
18818 });
18819
18820  
18821 //<script type="text/javascript">
18822
18823 /*
18824  * Based  Ext JS Library 1.1.1
18825  * Copyright(c) 2006-2007, Ext JS, LLC.
18826  * LGPL
18827  *
18828  */
18829  
18830 /**
18831  * @class Roo.HtmlEditorCore
18832  * @extends Roo.Component
18833  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18834  *
18835  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18836  */
18837
18838 Roo.HtmlEditorCore = function(config){
18839     
18840     
18841     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18842     
18843     
18844     this.addEvents({
18845         /**
18846          * @event initialize
18847          * Fires when the editor is fully initialized (including the iframe)
18848          * @param {Roo.HtmlEditorCore} this
18849          */
18850         initialize: true,
18851         /**
18852          * @event activate
18853          * Fires when the editor is first receives the focus. Any insertion must wait
18854          * until after this event.
18855          * @param {Roo.HtmlEditorCore} this
18856          */
18857         activate: true,
18858          /**
18859          * @event beforesync
18860          * Fires before the textarea is updated with content from the editor iframe. Return false
18861          * to cancel the sync.
18862          * @param {Roo.HtmlEditorCore} this
18863          * @param {String} html
18864          */
18865         beforesync: true,
18866          /**
18867          * @event beforepush
18868          * Fires before the iframe editor is updated with content from the textarea. Return false
18869          * to cancel the push.
18870          * @param {Roo.HtmlEditorCore} this
18871          * @param {String} html
18872          */
18873         beforepush: true,
18874          /**
18875          * @event sync
18876          * Fires when the textarea is updated with content from the editor iframe.
18877          * @param {Roo.HtmlEditorCore} this
18878          * @param {String} html
18879          */
18880         sync: true,
18881          /**
18882          * @event push
18883          * Fires when the iframe editor is updated with content from the textarea.
18884          * @param {Roo.HtmlEditorCore} this
18885          * @param {String} html
18886          */
18887         push: true,
18888         
18889         /**
18890          * @event editorevent
18891          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18892          * @param {Roo.HtmlEditorCore} this
18893          */
18894         editorevent: true
18895         
18896     });
18897     
18898     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18899     
18900     // defaults : white / black...
18901     this.applyBlacklists();
18902     
18903     
18904     
18905 };
18906
18907
18908 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
18909
18910
18911      /**
18912      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
18913      */
18914     
18915     owner : false,
18916     
18917      /**
18918      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
18919      *                        Roo.resizable.
18920      */
18921     resizable : false,
18922      /**
18923      * @cfg {Number} height (in pixels)
18924      */   
18925     height: 300,
18926    /**
18927      * @cfg {Number} width (in pixels)
18928      */   
18929     width: 500,
18930     
18931     /**
18932      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18933      * 
18934      */
18935     stylesheets: false,
18936     
18937     // id of frame..
18938     frameId: false,
18939     
18940     // private properties
18941     validationEvent : false,
18942     deferHeight: true,
18943     initialized : false,
18944     activated : false,
18945     sourceEditMode : false,
18946     onFocus : Roo.emptyFn,
18947     iframePad:3,
18948     hideMode:'offsets',
18949     
18950     clearUp: true,
18951     
18952     // blacklist + whitelisted elements..
18953     black: false,
18954     white: false,
18955      
18956     
18957
18958     /**
18959      * Protected method that will not generally be called directly. It
18960      * is called when the editor initializes the iframe with HTML contents. Override this method if you
18961      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
18962      */
18963     getDocMarkup : function(){
18964         // body styles..
18965         var st = '';
18966         
18967         // inherit styels from page...?? 
18968         if (this.stylesheets === false) {
18969             
18970             Roo.get(document.head).select('style').each(function(node) {
18971                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18972             });
18973             
18974             Roo.get(document.head).select('link').each(function(node) { 
18975                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
18976             });
18977             
18978         } else if (!this.stylesheets.length) {
18979                 // simple..
18980                 st = '<style type="text/css">' +
18981                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18982                    '</style>';
18983         } else { 
18984             
18985         }
18986         
18987         st +=  '<style type="text/css">' +
18988             'IMG { cursor: pointer } ' +
18989         '</style>';
18990
18991         
18992         return '<html><head>' + st  +
18993             //<style type="text/css">' +
18994             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
18995             //'</style>' +
18996             ' </head><body class="roo-htmleditor-body"></body></html>';
18997     },
18998
18999     // private
19000     onRender : function(ct, position)
19001     {
19002         var _t = this;
19003         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19004         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19005         
19006         
19007         this.el.dom.style.border = '0 none';
19008         this.el.dom.setAttribute('tabIndex', -1);
19009         this.el.addClass('x-hidden hide');
19010         
19011         
19012         
19013         if(Roo.isIE){ // fix IE 1px bogus margin
19014             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19015         }
19016        
19017         
19018         this.frameId = Roo.id();
19019         
19020          
19021         
19022         var iframe = this.owner.wrap.createChild({
19023             tag: 'iframe',
19024             cls: 'form-control', // bootstrap..
19025             id: this.frameId,
19026             name: this.frameId,
19027             frameBorder : 'no',
19028             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19029         }, this.el
19030         );
19031         
19032         
19033         this.iframe = iframe.dom;
19034
19035          this.assignDocWin();
19036         
19037         this.doc.designMode = 'on';
19038        
19039         this.doc.open();
19040         this.doc.write(this.getDocMarkup());
19041         this.doc.close();
19042
19043         
19044         var task = { // must defer to wait for browser to be ready
19045             run : function(){
19046                 //console.log("run task?" + this.doc.readyState);
19047                 this.assignDocWin();
19048                 if(this.doc.body || this.doc.readyState == 'complete'){
19049                     try {
19050                         this.doc.designMode="on";
19051                     } catch (e) {
19052                         return;
19053                     }
19054                     Roo.TaskMgr.stop(task);
19055                     this.initEditor.defer(10, this);
19056                 }
19057             },
19058             interval : 10,
19059             duration: 10000,
19060             scope: this
19061         };
19062         Roo.TaskMgr.start(task);
19063
19064     },
19065
19066     // private
19067     onResize : function(w, h)
19068     {
19069          Roo.log('resize: ' +w + ',' + h );
19070         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19071         if(!this.iframe){
19072             return;
19073         }
19074         if(typeof w == 'number'){
19075             
19076             this.iframe.style.width = w + 'px';
19077         }
19078         if(typeof h == 'number'){
19079             
19080             this.iframe.style.height = h + 'px';
19081             if(this.doc){
19082                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19083             }
19084         }
19085         
19086     },
19087
19088     /**
19089      * Toggles the editor between standard and source edit mode.
19090      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19091      */
19092     toggleSourceEdit : function(sourceEditMode){
19093         
19094         this.sourceEditMode = sourceEditMode === true;
19095         
19096         if(this.sourceEditMode){
19097  
19098             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19099             
19100         }else{
19101             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19102             //this.iframe.className = '';
19103             this.deferFocus();
19104         }
19105         //this.setSize(this.owner.wrap.getSize());
19106         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19107     },
19108
19109     
19110   
19111
19112     /**
19113      * Protected method that will not generally be called directly. If you need/want
19114      * custom HTML cleanup, this is the method you should override.
19115      * @param {String} html The HTML to be cleaned
19116      * return {String} The cleaned HTML
19117      */
19118     cleanHtml : function(html){
19119         html = String(html);
19120         if(html.length > 5){
19121             if(Roo.isSafari){ // strip safari nonsense
19122                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19123             }
19124         }
19125         if(html == '&nbsp;'){
19126             html = '';
19127         }
19128         return html;
19129     },
19130
19131     /**
19132      * HTML Editor -> Textarea
19133      * Protected method that will not generally be called directly. Syncs the contents
19134      * of the editor iframe with the textarea.
19135      */
19136     syncValue : function(){
19137         if(this.initialized){
19138             var bd = (this.doc.body || this.doc.documentElement);
19139             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19140             var html = bd.innerHTML;
19141             if(Roo.isSafari){
19142                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19143                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19144                 if(m && m[1]){
19145                     html = '<div style="'+m[0]+'">' + html + '</div>';
19146                 }
19147             }
19148             html = this.cleanHtml(html);
19149             // fix up the special chars.. normaly like back quotes in word...
19150             // however we do not want to do this with chinese..
19151             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19152                 var cc = b.charCodeAt();
19153                 if (
19154                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19155                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19156                     (cc >= 0xf900 && cc < 0xfb00 )
19157                 ) {
19158                         return b;
19159                 }
19160                 return "&#"+cc+";" 
19161             });
19162             if(this.owner.fireEvent('beforesync', this, html) !== false){
19163                 this.el.dom.value = html;
19164                 this.owner.fireEvent('sync', this, html);
19165             }
19166         }
19167     },
19168
19169     /**
19170      * Protected method that will not generally be called directly. Pushes the value of the textarea
19171      * into the iframe editor.
19172      */
19173     pushValue : function(){
19174         if(this.initialized){
19175             var v = this.el.dom.value.trim();
19176             
19177 //            if(v.length < 1){
19178 //                v = '&#160;';
19179 //            }
19180             
19181             if(this.owner.fireEvent('beforepush', this, v) !== false){
19182                 var d = (this.doc.body || this.doc.documentElement);
19183                 d.innerHTML = v;
19184                 this.cleanUpPaste();
19185                 this.el.dom.value = d.innerHTML;
19186                 this.owner.fireEvent('push', this, v);
19187             }
19188         }
19189     },
19190
19191     // private
19192     deferFocus : function(){
19193         this.focus.defer(10, this);
19194     },
19195
19196     // doc'ed in Field
19197     focus : function(){
19198         if(this.win && !this.sourceEditMode){
19199             this.win.focus();
19200         }else{
19201             this.el.focus();
19202         }
19203     },
19204     
19205     assignDocWin: function()
19206     {
19207         var iframe = this.iframe;
19208         
19209          if(Roo.isIE){
19210             this.doc = iframe.contentWindow.document;
19211             this.win = iframe.contentWindow;
19212         } else {
19213 //            if (!Roo.get(this.frameId)) {
19214 //                return;
19215 //            }
19216 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19217 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19218             
19219             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19220                 return;
19221             }
19222             
19223             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19224             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19225         }
19226     },
19227     
19228     // private
19229     initEditor : function(){
19230         //console.log("INIT EDITOR");
19231         this.assignDocWin();
19232         
19233         
19234         
19235         this.doc.designMode="on";
19236         this.doc.open();
19237         this.doc.write(this.getDocMarkup());
19238         this.doc.close();
19239         
19240         var dbody = (this.doc.body || this.doc.documentElement);
19241         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19242         // this copies styles from the containing element into thsi one..
19243         // not sure why we need all of this..
19244         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19245         
19246         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19247         //ss['background-attachment'] = 'fixed'; // w3c
19248         dbody.bgProperties = 'fixed'; // ie
19249         //Roo.DomHelper.applyStyles(dbody, ss);
19250         Roo.EventManager.on(this.doc, {
19251             //'mousedown': this.onEditorEvent,
19252             'mouseup': this.onEditorEvent,
19253             'dblclick': this.onEditorEvent,
19254             'click': this.onEditorEvent,
19255             'keyup': this.onEditorEvent,
19256             buffer:100,
19257             scope: this
19258         });
19259         if(Roo.isGecko){
19260             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19261         }
19262         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19263             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19264         }
19265         this.initialized = true;
19266
19267         this.owner.fireEvent('initialize', this);
19268         this.pushValue();
19269     },
19270
19271     // private
19272     onDestroy : function(){
19273         
19274         
19275         
19276         if(this.rendered){
19277             
19278             //for (var i =0; i < this.toolbars.length;i++) {
19279             //    // fixme - ask toolbars for heights?
19280             //    this.toolbars[i].onDestroy();
19281            // }
19282             
19283             //this.wrap.dom.innerHTML = '';
19284             //this.wrap.remove();
19285         }
19286     },
19287
19288     // private
19289     onFirstFocus : function(){
19290         
19291         this.assignDocWin();
19292         
19293         
19294         this.activated = true;
19295          
19296     
19297         if(Roo.isGecko){ // prevent silly gecko errors
19298             this.win.focus();
19299             var s = this.win.getSelection();
19300             if(!s.focusNode || s.focusNode.nodeType != 3){
19301                 var r = s.getRangeAt(0);
19302                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19303                 r.collapse(true);
19304                 this.deferFocus();
19305             }
19306             try{
19307                 this.execCmd('useCSS', true);
19308                 this.execCmd('styleWithCSS', false);
19309             }catch(e){}
19310         }
19311         this.owner.fireEvent('activate', this);
19312     },
19313
19314     // private
19315     adjustFont: function(btn){
19316         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19317         //if(Roo.isSafari){ // safari
19318         //    adjust *= 2;
19319        // }
19320         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19321         if(Roo.isSafari){ // safari
19322             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19323             v =  (v < 10) ? 10 : v;
19324             v =  (v > 48) ? 48 : v;
19325             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19326             
19327         }
19328         
19329         
19330         v = Math.max(1, v+adjust);
19331         
19332         this.execCmd('FontSize', v  );
19333     },
19334
19335     onEditorEvent : function(e)
19336     {
19337         this.owner.fireEvent('editorevent', this, e);
19338       //  this.updateToolbar();
19339         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19340     },
19341
19342     insertTag : function(tg)
19343     {
19344         // could be a bit smarter... -> wrap the current selected tRoo..
19345         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19346             
19347             range = this.createRange(this.getSelection());
19348             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19349             wrappingNode.appendChild(range.extractContents());
19350             range.insertNode(wrappingNode);
19351
19352             return;
19353             
19354             
19355             
19356         }
19357         this.execCmd("formatblock",   tg);
19358         
19359     },
19360     
19361     insertText : function(txt)
19362     {
19363         
19364         
19365         var range = this.createRange();
19366         range.deleteContents();
19367                //alert(Sender.getAttribute('label'));
19368                
19369         range.insertNode(this.doc.createTextNode(txt));
19370     } ,
19371     
19372      
19373
19374     /**
19375      * Executes a Midas editor command on the editor document and performs necessary focus and
19376      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19377      * @param {String} cmd The Midas command
19378      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19379      */
19380     relayCmd : function(cmd, value){
19381         this.win.focus();
19382         this.execCmd(cmd, value);
19383         this.owner.fireEvent('editorevent', this);
19384         //this.updateToolbar();
19385         this.owner.deferFocus();
19386     },
19387
19388     /**
19389      * Executes a Midas editor command directly on the editor document.
19390      * For visual commands, you should use {@link #relayCmd} instead.
19391      * <b>This should only be called after the editor is initialized.</b>
19392      * @param {String} cmd The Midas command
19393      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19394      */
19395     execCmd : function(cmd, value){
19396         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19397         this.syncValue();
19398     },
19399  
19400  
19401    
19402     /**
19403      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19404      * to insert tRoo.
19405      * @param {String} text | dom node.. 
19406      */
19407     insertAtCursor : function(text)
19408     {
19409         
19410         
19411         
19412         if(!this.activated){
19413             return;
19414         }
19415         /*
19416         if(Roo.isIE){
19417             this.win.focus();
19418             var r = this.doc.selection.createRange();
19419             if(r){
19420                 r.collapse(true);
19421                 r.pasteHTML(text);
19422                 this.syncValue();
19423                 this.deferFocus();
19424             
19425             }
19426             return;
19427         }
19428         */
19429         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19430             this.win.focus();
19431             
19432             
19433             // from jquery ui (MIT licenced)
19434             var range, node;
19435             var win = this.win;
19436             
19437             if (win.getSelection && win.getSelection().getRangeAt) {
19438                 range = win.getSelection().getRangeAt(0);
19439                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19440                 range.insertNode(node);
19441             } else if (win.document.selection && win.document.selection.createRange) {
19442                 // no firefox support
19443                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19444                 win.document.selection.createRange().pasteHTML(txt);
19445             } else {
19446                 // no firefox support
19447                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19448                 this.execCmd('InsertHTML', txt);
19449             } 
19450             
19451             this.syncValue();
19452             
19453             this.deferFocus();
19454         }
19455     },
19456  // private
19457     mozKeyPress : function(e){
19458         if(e.ctrlKey){
19459             var c = e.getCharCode(), cmd;
19460           
19461             if(c > 0){
19462                 c = String.fromCharCode(c).toLowerCase();
19463                 switch(c){
19464                     case 'b':
19465                         cmd = 'bold';
19466                         break;
19467                     case 'i':
19468                         cmd = 'italic';
19469                         break;
19470                     
19471                     case 'u':
19472                         cmd = 'underline';
19473                         break;
19474                     
19475                     case 'v':
19476                         this.cleanUpPaste.defer(100, this);
19477                         return;
19478                         
19479                 }
19480                 if(cmd){
19481                     this.win.focus();
19482                     this.execCmd(cmd);
19483                     this.deferFocus();
19484                     e.preventDefault();
19485                 }
19486                 
19487             }
19488         }
19489     },
19490
19491     // private
19492     fixKeys : function(){ // load time branching for fastest keydown performance
19493         if(Roo.isIE){
19494             return function(e){
19495                 var k = e.getKey(), r;
19496                 if(k == e.TAB){
19497                     e.stopEvent();
19498                     r = this.doc.selection.createRange();
19499                     if(r){
19500                         r.collapse(true);
19501                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19502                         this.deferFocus();
19503                     }
19504                     return;
19505                 }
19506                 
19507                 if(k == e.ENTER){
19508                     r = this.doc.selection.createRange();
19509                     if(r){
19510                         var target = r.parentElement();
19511                         if(!target || target.tagName.toLowerCase() != 'li'){
19512                             e.stopEvent();
19513                             r.pasteHTML('<br />');
19514                             r.collapse(false);
19515                             r.select();
19516                         }
19517                     }
19518                 }
19519                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19520                     this.cleanUpPaste.defer(100, this);
19521                     return;
19522                 }
19523                 
19524                 
19525             };
19526         }else if(Roo.isOpera){
19527             return function(e){
19528                 var k = e.getKey();
19529                 if(k == e.TAB){
19530                     e.stopEvent();
19531                     this.win.focus();
19532                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19533                     this.deferFocus();
19534                 }
19535                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19536                     this.cleanUpPaste.defer(100, this);
19537                     return;
19538                 }
19539                 
19540             };
19541         }else if(Roo.isSafari){
19542             return function(e){
19543                 var k = e.getKey();
19544                 
19545                 if(k == e.TAB){
19546                     e.stopEvent();
19547                     this.execCmd('InsertText','\t');
19548                     this.deferFocus();
19549                     return;
19550                 }
19551                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19552                     this.cleanUpPaste.defer(100, this);
19553                     return;
19554                 }
19555                 
19556              };
19557         }
19558     }(),
19559     
19560     getAllAncestors: function()
19561     {
19562         var p = this.getSelectedNode();
19563         var a = [];
19564         if (!p) {
19565             a.push(p); // push blank onto stack..
19566             p = this.getParentElement();
19567         }
19568         
19569         
19570         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19571             a.push(p);
19572             p = p.parentNode;
19573         }
19574         a.push(this.doc.body);
19575         return a;
19576     },
19577     lastSel : false,
19578     lastSelNode : false,
19579     
19580     
19581     getSelection : function() 
19582     {
19583         this.assignDocWin();
19584         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19585     },
19586     
19587     getSelectedNode: function() 
19588     {
19589         // this may only work on Gecko!!!
19590         
19591         // should we cache this!!!!
19592         
19593         
19594         
19595          
19596         var range = this.createRange(this.getSelection()).cloneRange();
19597         
19598         if (Roo.isIE) {
19599             var parent = range.parentElement();
19600             while (true) {
19601                 var testRange = range.duplicate();
19602                 testRange.moveToElementText(parent);
19603                 if (testRange.inRange(range)) {
19604                     break;
19605                 }
19606                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19607                     break;
19608                 }
19609                 parent = parent.parentElement;
19610             }
19611             return parent;
19612         }
19613         
19614         // is ancestor a text element.
19615         var ac =  range.commonAncestorContainer;
19616         if (ac.nodeType == 3) {
19617             ac = ac.parentNode;
19618         }
19619         
19620         var ar = ac.childNodes;
19621          
19622         var nodes = [];
19623         var other_nodes = [];
19624         var has_other_nodes = false;
19625         for (var i=0;i<ar.length;i++) {
19626             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19627                 continue;
19628             }
19629             // fullly contained node.
19630             
19631             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19632                 nodes.push(ar[i]);
19633                 continue;
19634             }
19635             
19636             // probably selected..
19637             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19638                 other_nodes.push(ar[i]);
19639                 continue;
19640             }
19641             // outer..
19642             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19643                 continue;
19644             }
19645             
19646             
19647             has_other_nodes = true;
19648         }
19649         if (!nodes.length && other_nodes.length) {
19650             nodes= other_nodes;
19651         }
19652         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19653             return false;
19654         }
19655         
19656         return nodes[0];
19657     },
19658     createRange: function(sel)
19659     {
19660         // this has strange effects when using with 
19661         // top toolbar - not sure if it's a great idea.
19662         //this.editor.contentWindow.focus();
19663         if (typeof sel != "undefined") {
19664             try {
19665                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19666             } catch(e) {
19667                 return this.doc.createRange();
19668             }
19669         } else {
19670             return this.doc.createRange();
19671         }
19672     },
19673     getParentElement: function()
19674     {
19675         
19676         this.assignDocWin();
19677         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19678         
19679         var range = this.createRange(sel);
19680          
19681         try {
19682             var p = range.commonAncestorContainer;
19683             while (p.nodeType == 3) { // text node
19684                 p = p.parentNode;
19685             }
19686             return p;
19687         } catch (e) {
19688             return null;
19689         }
19690     
19691     },
19692     /***
19693      *
19694      * Range intersection.. the hard stuff...
19695      *  '-1' = before
19696      *  '0' = hits..
19697      *  '1' = after.
19698      *         [ -- selected range --- ]
19699      *   [fail]                        [fail]
19700      *
19701      *    basically..
19702      *      if end is before start or  hits it. fail.
19703      *      if start is after end or hits it fail.
19704      *
19705      *   if either hits (but other is outside. - then it's not 
19706      *   
19707      *    
19708      **/
19709     
19710     
19711     // @see http://www.thismuchiknow.co.uk/?p=64.
19712     rangeIntersectsNode : function(range, node)
19713     {
19714         var nodeRange = node.ownerDocument.createRange();
19715         try {
19716             nodeRange.selectNode(node);
19717         } catch (e) {
19718             nodeRange.selectNodeContents(node);
19719         }
19720     
19721         var rangeStartRange = range.cloneRange();
19722         rangeStartRange.collapse(true);
19723     
19724         var rangeEndRange = range.cloneRange();
19725         rangeEndRange.collapse(false);
19726     
19727         var nodeStartRange = nodeRange.cloneRange();
19728         nodeStartRange.collapse(true);
19729     
19730         var nodeEndRange = nodeRange.cloneRange();
19731         nodeEndRange.collapse(false);
19732     
19733         return rangeStartRange.compareBoundaryPoints(
19734                  Range.START_TO_START, nodeEndRange) == -1 &&
19735                rangeEndRange.compareBoundaryPoints(
19736                  Range.START_TO_START, nodeStartRange) == 1;
19737         
19738          
19739     },
19740     rangeCompareNode : function(range, node)
19741     {
19742         var nodeRange = node.ownerDocument.createRange();
19743         try {
19744             nodeRange.selectNode(node);
19745         } catch (e) {
19746             nodeRange.selectNodeContents(node);
19747         }
19748         
19749         
19750         range.collapse(true);
19751     
19752         nodeRange.collapse(true);
19753      
19754         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19755         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19756          
19757         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19758         
19759         var nodeIsBefore   =  ss == 1;
19760         var nodeIsAfter    = ee == -1;
19761         
19762         if (nodeIsBefore && nodeIsAfter)
19763             return 0; // outer
19764         if (!nodeIsBefore && nodeIsAfter)
19765             return 1; //right trailed.
19766         
19767         if (nodeIsBefore && !nodeIsAfter)
19768             return 2;  // left trailed.
19769         // fully contined.
19770         return 3;
19771     },
19772
19773     // private? - in a new class?
19774     cleanUpPaste :  function()
19775     {
19776         // cleans up the whole document..
19777         Roo.log('cleanuppaste');
19778         
19779         this.cleanUpChildren(this.doc.body);
19780         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19781         if (clean != this.doc.body.innerHTML) {
19782             this.doc.body.innerHTML = clean;
19783         }
19784         
19785     },
19786     
19787     cleanWordChars : function(input) {// change the chars to hex code
19788         var he = Roo.HtmlEditorCore;
19789         
19790         var output = input;
19791         Roo.each(he.swapCodes, function(sw) { 
19792             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19793             
19794             output = output.replace(swapper, sw[1]);
19795         });
19796         
19797         return output;
19798     },
19799     
19800     
19801     cleanUpChildren : function (n)
19802     {
19803         if (!n.childNodes.length) {
19804             return;
19805         }
19806         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19807            this.cleanUpChild(n.childNodes[i]);
19808         }
19809     },
19810     
19811     
19812         
19813     
19814     cleanUpChild : function (node)
19815     {
19816         var ed = this;
19817         //console.log(node);
19818         if (node.nodeName == "#text") {
19819             // clean up silly Windows -- stuff?
19820             return; 
19821         }
19822         if (node.nodeName == "#comment") {
19823             node.parentNode.removeChild(node);
19824             // clean up silly Windows -- stuff?
19825             return; 
19826         }
19827         var lcname = node.tagName.toLowerCase();
19828         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19829         // whitelist of tags..
19830         
19831         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19832             // remove node.
19833             node.parentNode.removeChild(node);
19834             return;
19835             
19836         }
19837         
19838         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19839         
19840         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19841         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19842         
19843         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19844         //    remove_keep_children = true;
19845         //}
19846         
19847         if (remove_keep_children) {
19848             this.cleanUpChildren(node);
19849             // inserts everything just before this node...
19850             while (node.childNodes.length) {
19851                 var cn = node.childNodes[0];
19852                 node.removeChild(cn);
19853                 node.parentNode.insertBefore(cn, node);
19854             }
19855             node.parentNode.removeChild(node);
19856             return;
19857         }
19858         
19859         if (!node.attributes || !node.attributes.length) {
19860             this.cleanUpChildren(node);
19861             return;
19862         }
19863         
19864         function cleanAttr(n,v)
19865         {
19866             
19867             if (v.match(/^\./) || v.match(/^\//)) {
19868                 return;
19869             }
19870             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19871                 return;
19872             }
19873             if (v.match(/^#/)) {
19874                 return;
19875             }
19876 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19877             node.removeAttribute(n);
19878             
19879         }
19880         
19881         var cwhite = this.cwhite;
19882         var cblack = this.cblack;
19883             
19884         function cleanStyle(n,v)
19885         {
19886             if (v.match(/expression/)) { //XSS?? should we even bother..
19887                 node.removeAttribute(n);
19888                 return;
19889             }
19890             
19891             var parts = v.split(/;/);
19892             var clean = [];
19893             
19894             Roo.each(parts, function(p) {
19895                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19896                 if (!p.length) {
19897                     return true;
19898                 }
19899                 var l = p.split(':').shift().replace(/\s+/g,'');
19900                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19901                 
19902                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19903 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19904                     //node.removeAttribute(n);
19905                     return true;
19906                 }
19907                 //Roo.log()
19908                 // only allow 'c whitelisted system attributes'
19909                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
19910 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19911                     //node.removeAttribute(n);
19912                     return true;
19913                 }
19914                 
19915                 
19916                  
19917                 
19918                 clean.push(p);
19919                 return true;
19920             });
19921             if (clean.length) { 
19922                 node.setAttribute(n, clean.join(';'));
19923             } else {
19924                 node.removeAttribute(n);
19925             }
19926             
19927         }
19928         
19929         
19930         for (var i = node.attributes.length-1; i > -1 ; i--) {
19931             var a = node.attributes[i];
19932             //console.log(a);
19933             
19934             if (a.name.toLowerCase().substr(0,2)=='on')  {
19935                 node.removeAttribute(a.name);
19936                 continue;
19937             }
19938             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
19939                 node.removeAttribute(a.name);
19940                 continue;
19941             }
19942             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
19943                 cleanAttr(a.name,a.value); // fixme..
19944                 continue;
19945             }
19946             if (a.name == 'style') {
19947                 cleanStyle(a.name,a.value);
19948                 continue;
19949             }
19950             /// clean up MS crap..
19951             // tecnically this should be a list of valid class'es..
19952             
19953             
19954             if (a.name == 'class') {
19955                 if (a.value.match(/^Mso/)) {
19956                     node.className = '';
19957                 }
19958                 
19959                 if (a.value.match(/body/)) {
19960                     node.className = '';
19961                 }
19962                 continue;
19963             }
19964             
19965             // style cleanup!?
19966             // class cleanup?
19967             
19968         }
19969         
19970         
19971         this.cleanUpChildren(node);
19972         
19973         
19974     },
19975     
19976     /**
19977      * Clean up MS wordisms...
19978      */
19979     cleanWord : function(node)
19980     {
19981         
19982         
19983         if (!node) {
19984             this.cleanWord(this.doc.body);
19985             return;
19986         }
19987         if (node.nodeName == "#text") {
19988             // clean up silly Windows -- stuff?
19989             return; 
19990         }
19991         if (node.nodeName == "#comment") {
19992             node.parentNode.removeChild(node);
19993             // clean up silly Windows -- stuff?
19994             return; 
19995         }
19996         
19997         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
19998             node.parentNode.removeChild(node);
19999             return;
20000         }
20001         
20002         // remove - but keep children..
20003         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20004             while (node.childNodes.length) {
20005                 var cn = node.childNodes[0];
20006                 node.removeChild(cn);
20007                 node.parentNode.insertBefore(cn, node);
20008             }
20009             node.parentNode.removeChild(node);
20010             this.iterateChildren(node, this.cleanWord);
20011             return;
20012         }
20013         // clean styles
20014         if (node.className.length) {
20015             
20016             var cn = node.className.split(/\W+/);
20017             var cna = [];
20018             Roo.each(cn, function(cls) {
20019                 if (cls.match(/Mso[a-zA-Z]+/)) {
20020                     return;
20021                 }
20022                 cna.push(cls);
20023             });
20024             node.className = cna.length ? cna.join(' ') : '';
20025             if (!cna.length) {
20026                 node.removeAttribute("class");
20027             }
20028         }
20029         
20030         if (node.hasAttribute("lang")) {
20031             node.removeAttribute("lang");
20032         }
20033         
20034         if (node.hasAttribute("style")) {
20035             
20036             var styles = node.getAttribute("style").split(";");
20037             var nstyle = [];
20038             Roo.each(styles, function(s) {
20039                 if (!s.match(/:/)) {
20040                     return;
20041                 }
20042                 var kv = s.split(":");
20043                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20044                     return;
20045                 }
20046                 // what ever is left... we allow.
20047                 nstyle.push(s);
20048             });
20049             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20050             if (!nstyle.length) {
20051                 node.removeAttribute('style');
20052             }
20053         }
20054         this.iterateChildren(node, this.cleanWord);
20055         
20056         
20057         
20058     },
20059     /**
20060      * iterateChildren of a Node, calling fn each time, using this as the scole..
20061      * @param {DomNode} node node to iterate children of.
20062      * @param {Function} fn method of this class to call on each item.
20063      */
20064     iterateChildren : function(node, fn)
20065     {
20066         if (!node.childNodes.length) {
20067                 return;
20068         }
20069         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20070            fn.call(this, node.childNodes[i])
20071         }
20072     },
20073     
20074     
20075     /**
20076      * cleanTableWidths.
20077      *
20078      * Quite often pasting from word etc.. results in tables with column and widths.
20079      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20080      *
20081      */
20082     cleanTableWidths : function(node)
20083     {
20084          
20085          
20086         if (!node) {
20087             this.cleanTableWidths(this.doc.body);
20088             return;
20089         }
20090         
20091         // ignore list...
20092         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20093             return; 
20094         }
20095         Roo.log(node.tagName);
20096         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20097             this.iterateChildren(node, this.cleanTableWidths);
20098             return;
20099         }
20100         if (node.hasAttribute('width')) {
20101             node.removeAttribute('width');
20102         }
20103         
20104          
20105         if (node.hasAttribute("style")) {
20106             // pretty basic...
20107             
20108             var styles = node.getAttribute("style").split(";");
20109             var nstyle = [];
20110             Roo.each(styles, function(s) {
20111                 if (!s.match(/:/)) {
20112                     return;
20113                 }
20114                 var kv = s.split(":");
20115                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20116                     return;
20117                 }
20118                 // what ever is left... we allow.
20119                 nstyle.push(s);
20120             });
20121             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20122             if (!nstyle.length) {
20123                 node.removeAttribute('style');
20124             }
20125         }
20126         
20127         this.iterateChildren(node, this.cleanTableWidths);
20128         
20129         
20130     },
20131     
20132     
20133     
20134     
20135     domToHTML : function(currentElement, depth, nopadtext) {
20136         
20137         depth = depth || 0;
20138         nopadtext = nopadtext || false;
20139     
20140         if (!currentElement) {
20141             return this.domToHTML(this.doc.body);
20142         }
20143         
20144         //Roo.log(currentElement);
20145         var j;
20146         var allText = false;
20147         var nodeName = currentElement.nodeName;
20148         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20149         
20150         if  (nodeName == '#text') {
20151             
20152             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20153         }
20154         
20155         
20156         var ret = '';
20157         if (nodeName != 'BODY') {
20158              
20159             var i = 0;
20160             // Prints the node tagName, such as <A>, <IMG>, etc
20161             if (tagName) {
20162                 var attr = [];
20163                 for(i = 0; i < currentElement.attributes.length;i++) {
20164                     // quoting?
20165                     var aname = currentElement.attributes.item(i).name;
20166                     if (!currentElement.attributes.item(i).value.length) {
20167                         continue;
20168                     }
20169                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20170                 }
20171                 
20172                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20173             } 
20174             else {
20175                 
20176                 // eack
20177             }
20178         } else {
20179             tagName = false;
20180         }
20181         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20182             return ret;
20183         }
20184         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20185             nopadtext = true;
20186         }
20187         
20188         
20189         // Traverse the tree
20190         i = 0;
20191         var currentElementChild = currentElement.childNodes.item(i);
20192         var allText = true;
20193         var innerHTML  = '';
20194         lastnode = '';
20195         while (currentElementChild) {
20196             // Formatting code (indent the tree so it looks nice on the screen)
20197             var nopad = nopadtext;
20198             if (lastnode == 'SPAN') {
20199                 nopad  = true;
20200             }
20201             // text
20202             if  (currentElementChild.nodeName == '#text') {
20203                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20204                 toadd = nopadtext ? toadd : toadd.trim();
20205                 if (!nopad && toadd.length > 80) {
20206                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20207                 }
20208                 innerHTML  += toadd;
20209                 
20210                 i++;
20211                 currentElementChild = currentElement.childNodes.item(i);
20212                 lastNode = '';
20213                 continue;
20214             }
20215             allText = false;
20216             
20217             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20218                 
20219             // Recursively traverse the tree structure of the child node
20220             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20221             lastnode = currentElementChild.nodeName;
20222             i++;
20223             currentElementChild=currentElement.childNodes.item(i);
20224         }
20225         
20226         ret += innerHTML;
20227         
20228         if (!allText) {
20229                 // The remaining code is mostly for formatting the tree
20230             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20231         }
20232         
20233         
20234         if (tagName) {
20235             ret+= "</"+tagName+">";
20236         }
20237         return ret;
20238         
20239     },
20240         
20241     applyBlacklists : function()
20242     {
20243         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20244         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20245         
20246         this.white = [];
20247         this.black = [];
20248         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20249             if (b.indexOf(tag) > -1) {
20250                 return;
20251             }
20252             this.white.push(tag);
20253             
20254         }, this);
20255         
20256         Roo.each(w, function(tag) {
20257             if (b.indexOf(tag) > -1) {
20258                 return;
20259             }
20260             if (this.white.indexOf(tag) > -1) {
20261                 return;
20262             }
20263             this.white.push(tag);
20264             
20265         }, this);
20266         
20267         
20268         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20269             if (w.indexOf(tag) > -1) {
20270                 return;
20271             }
20272             this.black.push(tag);
20273             
20274         }, this);
20275         
20276         Roo.each(b, function(tag) {
20277             if (w.indexOf(tag) > -1) {
20278                 return;
20279             }
20280             if (this.black.indexOf(tag) > -1) {
20281                 return;
20282             }
20283             this.black.push(tag);
20284             
20285         }, this);
20286         
20287         
20288         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20289         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20290         
20291         this.cwhite = [];
20292         this.cblack = [];
20293         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20294             if (b.indexOf(tag) > -1) {
20295                 return;
20296             }
20297             this.cwhite.push(tag);
20298             
20299         }, this);
20300         
20301         Roo.each(w, function(tag) {
20302             if (b.indexOf(tag) > -1) {
20303                 return;
20304             }
20305             if (this.cwhite.indexOf(tag) > -1) {
20306                 return;
20307             }
20308             this.cwhite.push(tag);
20309             
20310         }, this);
20311         
20312         
20313         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20314             if (w.indexOf(tag) > -1) {
20315                 return;
20316             }
20317             this.cblack.push(tag);
20318             
20319         }, this);
20320         
20321         Roo.each(b, function(tag) {
20322             if (w.indexOf(tag) > -1) {
20323                 return;
20324             }
20325             if (this.cblack.indexOf(tag) > -1) {
20326                 return;
20327             }
20328             this.cblack.push(tag);
20329             
20330         }, this);
20331     },
20332     
20333     setStylesheets : function(stylesheets)
20334     {
20335         if(typeof(stylesheets) == 'string'){
20336             Roo.get(this.iframe.contentDocument.head).createChild({
20337                 tag : 'link',
20338                 rel : 'stylesheet',
20339                 type : 'text/css',
20340                 href : stylesheets
20341             });
20342             
20343             return;
20344         }
20345         var _this = this;
20346      
20347         Roo.each(stylesheets, function(s) {
20348             if(!s.length){
20349                 return;
20350             }
20351             
20352             Roo.get(_this.iframe.contentDocument.head).createChild({
20353                 tag : 'link',
20354                 rel : 'stylesheet',
20355                 type : 'text/css',
20356                 href : s
20357             });
20358         });
20359
20360         
20361     },
20362     
20363     removeStylesheets : function()
20364     {
20365         var _this = this;
20366         
20367         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20368             s.remove();
20369         });
20370     }
20371     
20372     // hide stuff that is not compatible
20373     /**
20374      * @event blur
20375      * @hide
20376      */
20377     /**
20378      * @event change
20379      * @hide
20380      */
20381     /**
20382      * @event focus
20383      * @hide
20384      */
20385     /**
20386      * @event specialkey
20387      * @hide
20388      */
20389     /**
20390      * @cfg {String} fieldClass @hide
20391      */
20392     /**
20393      * @cfg {String} focusClass @hide
20394      */
20395     /**
20396      * @cfg {String} autoCreate @hide
20397      */
20398     /**
20399      * @cfg {String} inputType @hide
20400      */
20401     /**
20402      * @cfg {String} invalidClass @hide
20403      */
20404     /**
20405      * @cfg {String} invalidText @hide
20406      */
20407     /**
20408      * @cfg {String} msgFx @hide
20409      */
20410     /**
20411      * @cfg {String} validateOnBlur @hide
20412      */
20413 });
20414
20415 Roo.HtmlEditorCore.white = [
20416         'area', 'br', 'img', 'input', 'hr', 'wbr',
20417         
20418        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20419        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20420        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20421        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20422        'table',   'ul',         'xmp', 
20423        
20424        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20425       'thead',   'tr', 
20426      
20427       'dir', 'menu', 'ol', 'ul', 'dl',
20428        
20429       'embed',  'object'
20430 ];
20431
20432
20433 Roo.HtmlEditorCore.black = [
20434     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20435         'applet', // 
20436         'base',   'basefont', 'bgsound', 'blink',  'body', 
20437         'frame',  'frameset', 'head',    'html',   'ilayer', 
20438         'iframe', 'layer',  'link',     'meta',    'object',   
20439         'script', 'style' ,'title',  'xml' // clean later..
20440 ];
20441 Roo.HtmlEditorCore.clean = [
20442     'script', 'style', 'title', 'xml'
20443 ];
20444 Roo.HtmlEditorCore.remove = [
20445     'font'
20446 ];
20447 // attributes..
20448
20449 Roo.HtmlEditorCore.ablack = [
20450     'on'
20451 ];
20452     
20453 Roo.HtmlEditorCore.aclean = [ 
20454     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20455 ];
20456
20457 // protocols..
20458 Roo.HtmlEditorCore.pwhite= [
20459         'http',  'https',  'mailto'
20460 ];
20461
20462 // white listed style attributes.
20463 Roo.HtmlEditorCore.cwhite= [
20464       //  'text-align', /// default is to allow most things..
20465       
20466          
20467 //        'font-size'//??
20468 ];
20469
20470 // black listed style attributes.
20471 Roo.HtmlEditorCore.cblack= [
20472       //  'font-size' -- this can be set by the project 
20473 ];
20474
20475
20476 Roo.HtmlEditorCore.swapCodes   =[ 
20477     [    8211, "--" ], 
20478     [    8212, "--" ], 
20479     [    8216,  "'" ],  
20480     [    8217, "'" ],  
20481     [    8220, '"' ],  
20482     [    8221, '"' ],  
20483     [    8226, "*" ],  
20484     [    8230, "..." ]
20485 ]; 
20486
20487     /*
20488  * - LGPL
20489  *
20490  * HtmlEditor
20491  * 
20492  */
20493
20494 /**
20495  * @class Roo.bootstrap.HtmlEditor
20496  * @extends Roo.bootstrap.TextArea
20497  * Bootstrap HtmlEditor class
20498
20499  * @constructor
20500  * Create a new HtmlEditor
20501  * @param {Object} config The config object
20502  */
20503
20504 Roo.bootstrap.HtmlEditor = function(config){
20505     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20506     if (!this.toolbars) {
20507         this.toolbars = [];
20508     }
20509     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20510     this.addEvents({
20511             /**
20512              * @event initialize
20513              * Fires when the editor is fully initialized (including the iframe)
20514              * @param {HtmlEditor} this
20515              */
20516             initialize: true,
20517             /**
20518              * @event activate
20519              * Fires when the editor is first receives the focus. Any insertion must wait
20520              * until after this event.
20521              * @param {HtmlEditor} this
20522              */
20523             activate: true,
20524              /**
20525              * @event beforesync
20526              * Fires before the textarea is updated with content from the editor iframe. Return false
20527              * to cancel the sync.
20528              * @param {HtmlEditor} this
20529              * @param {String} html
20530              */
20531             beforesync: true,
20532              /**
20533              * @event beforepush
20534              * Fires before the iframe editor is updated with content from the textarea. Return false
20535              * to cancel the push.
20536              * @param {HtmlEditor} this
20537              * @param {String} html
20538              */
20539             beforepush: true,
20540              /**
20541              * @event sync
20542              * Fires when the textarea is updated with content from the editor iframe.
20543              * @param {HtmlEditor} this
20544              * @param {String} html
20545              */
20546             sync: true,
20547              /**
20548              * @event push
20549              * Fires when the iframe editor is updated with content from the textarea.
20550              * @param {HtmlEditor} this
20551              * @param {String} html
20552              */
20553             push: true,
20554              /**
20555              * @event editmodechange
20556              * Fires when the editor switches edit modes
20557              * @param {HtmlEditor} this
20558              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20559              */
20560             editmodechange: true,
20561             /**
20562              * @event editorevent
20563              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20564              * @param {HtmlEditor} this
20565              */
20566             editorevent: true,
20567             /**
20568              * @event firstfocus
20569              * Fires when on first focus - needed by toolbars..
20570              * @param {HtmlEditor} this
20571              */
20572             firstfocus: true,
20573             /**
20574              * @event autosave
20575              * Auto save the htmlEditor value as a file into Events
20576              * @param {HtmlEditor} this
20577              */
20578             autosave: true,
20579             /**
20580              * @event savedpreview
20581              * preview the saved version of htmlEditor
20582              * @param {HtmlEditor} this
20583              */
20584             savedpreview: true
20585         });
20586 };
20587
20588
20589 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20590     
20591     
20592       /**
20593      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20594      */
20595     toolbars : false,
20596    
20597      /**
20598      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20599      *                        Roo.resizable.
20600      */
20601     resizable : false,
20602      /**
20603      * @cfg {Number} height (in pixels)
20604      */   
20605     height: 300,
20606    /**
20607      * @cfg {Number} width (in pixels)
20608      */   
20609     width: false,
20610     
20611     /**
20612      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20613      * 
20614      */
20615     stylesheets: false,
20616     
20617     // id of frame..
20618     frameId: false,
20619     
20620     // private properties
20621     validationEvent : false,
20622     deferHeight: true,
20623     initialized : false,
20624     activated : false,
20625     
20626     onFocus : Roo.emptyFn,
20627     iframePad:3,
20628     hideMode:'offsets',
20629     
20630     
20631     tbContainer : false,
20632     
20633     toolbarContainer :function() {
20634         return this.wrap.select('.x-html-editor-tb',true).first();
20635     },
20636
20637     /**
20638      * Protected method that will not generally be called directly. It
20639      * is called when the editor creates its toolbar. Override this method if you need to
20640      * add custom toolbar buttons.
20641      * @param {HtmlEditor} editor
20642      */
20643     createToolbar : function(){
20644         
20645         Roo.log("create toolbars");
20646         
20647         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20648         this.toolbars[0].render(this.toolbarContainer());
20649         
20650         return;
20651         
20652 //        if (!editor.toolbars || !editor.toolbars.length) {
20653 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20654 //        }
20655 //        
20656 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20657 //            editor.toolbars[i] = Roo.factory(
20658 //                    typeof(editor.toolbars[i]) == 'string' ?
20659 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20660 //                Roo.bootstrap.HtmlEditor);
20661 //            editor.toolbars[i].init(editor);
20662 //        }
20663     },
20664
20665      
20666     // private
20667     onRender : function(ct, position)
20668     {
20669        // Roo.log("Call onRender: " + this.xtype);
20670         var _t = this;
20671         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20672       
20673         this.wrap = this.inputEl().wrap({
20674             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20675         });
20676         
20677         this.editorcore.onRender(ct, position);
20678          
20679         if (this.resizable) {
20680             this.resizeEl = new Roo.Resizable(this.wrap, {
20681                 pinned : true,
20682                 wrap: true,
20683                 dynamic : true,
20684                 minHeight : this.height,
20685                 height: this.height,
20686                 handles : this.resizable,
20687                 width: this.width,
20688                 listeners : {
20689                     resize : function(r, w, h) {
20690                         _t.onResize(w,h); // -something
20691                     }
20692                 }
20693             });
20694             
20695         }
20696         this.createToolbar(this);
20697        
20698         
20699         if(!this.width && this.resizable){
20700             this.setSize(this.wrap.getSize());
20701         }
20702         if (this.resizeEl) {
20703             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20704             // should trigger onReize..
20705         }
20706         
20707     },
20708
20709     // private
20710     onResize : function(w, h)
20711     {
20712         Roo.log('resize: ' +w + ',' + h );
20713         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20714         var ew = false;
20715         var eh = false;
20716         
20717         if(this.inputEl() ){
20718             if(typeof w == 'number'){
20719                 var aw = w - this.wrap.getFrameWidth('lr');
20720                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20721                 ew = aw;
20722             }
20723             if(typeof h == 'number'){
20724                  var tbh = -11;  // fixme it needs to tool bar size!
20725                 for (var i =0; i < this.toolbars.length;i++) {
20726                     // fixme - ask toolbars for heights?
20727                     tbh += this.toolbars[i].el.getHeight();
20728                     //if (this.toolbars[i].footer) {
20729                     //    tbh += this.toolbars[i].footer.el.getHeight();
20730                     //}
20731                 }
20732               
20733                 
20734                 
20735                 
20736                 
20737                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20738                 ah -= 5; // knock a few pixes off for look..
20739                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20740                 var eh = ah;
20741             }
20742         }
20743         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20744         this.editorcore.onResize(ew,eh);
20745         
20746     },
20747
20748     /**
20749      * Toggles the editor between standard and source edit mode.
20750      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20751      */
20752     toggleSourceEdit : function(sourceEditMode)
20753     {
20754         this.editorcore.toggleSourceEdit(sourceEditMode);
20755         
20756         if(this.editorcore.sourceEditMode){
20757             Roo.log('editor - showing textarea');
20758             
20759 //            Roo.log('in');
20760 //            Roo.log(this.syncValue());
20761             this.syncValue();
20762             this.inputEl().removeClass(['hide', 'x-hidden']);
20763             this.inputEl().dom.removeAttribute('tabIndex');
20764             this.inputEl().focus();
20765         }else{
20766             Roo.log('editor - hiding textarea');
20767 //            Roo.log('out')
20768 //            Roo.log(this.pushValue()); 
20769             this.pushValue();
20770             
20771             this.inputEl().addClass(['hide', 'x-hidden']);
20772             this.inputEl().dom.setAttribute('tabIndex', -1);
20773             //this.deferFocus();
20774         }
20775          
20776         if(this.resizable){
20777             this.setSize(this.wrap.getSize());
20778         }
20779         
20780         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20781     },
20782  
20783     // private (for BoxComponent)
20784     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20785
20786     // private (for BoxComponent)
20787     getResizeEl : function(){
20788         return this.wrap;
20789     },
20790
20791     // private (for BoxComponent)
20792     getPositionEl : function(){
20793         return this.wrap;
20794     },
20795
20796     // private
20797     initEvents : function(){
20798         this.originalValue = this.getValue();
20799     },
20800
20801 //    /**
20802 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20803 //     * @method
20804 //     */
20805 //    markInvalid : Roo.emptyFn,
20806 //    /**
20807 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20808 //     * @method
20809 //     */
20810 //    clearInvalid : Roo.emptyFn,
20811
20812     setValue : function(v){
20813         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20814         this.editorcore.pushValue();
20815     },
20816
20817      
20818     // private
20819     deferFocus : function(){
20820         this.focus.defer(10, this);
20821     },
20822
20823     // doc'ed in Field
20824     focus : function(){
20825         this.editorcore.focus();
20826         
20827     },
20828       
20829
20830     // private
20831     onDestroy : function(){
20832         
20833         
20834         
20835         if(this.rendered){
20836             
20837             for (var i =0; i < this.toolbars.length;i++) {
20838                 // fixme - ask toolbars for heights?
20839                 this.toolbars[i].onDestroy();
20840             }
20841             
20842             this.wrap.dom.innerHTML = '';
20843             this.wrap.remove();
20844         }
20845     },
20846
20847     // private
20848     onFirstFocus : function(){
20849         //Roo.log("onFirstFocus");
20850         this.editorcore.onFirstFocus();
20851          for (var i =0; i < this.toolbars.length;i++) {
20852             this.toolbars[i].onFirstFocus();
20853         }
20854         
20855     },
20856     
20857     // private
20858     syncValue : function()
20859     {   
20860         this.editorcore.syncValue();
20861     },
20862     
20863     pushValue : function()
20864     {   
20865         this.editorcore.pushValue();
20866     }
20867      
20868     
20869     // hide stuff that is not compatible
20870     /**
20871      * @event blur
20872      * @hide
20873      */
20874     /**
20875      * @event change
20876      * @hide
20877      */
20878     /**
20879      * @event focus
20880      * @hide
20881      */
20882     /**
20883      * @event specialkey
20884      * @hide
20885      */
20886     /**
20887      * @cfg {String} fieldClass @hide
20888      */
20889     /**
20890      * @cfg {String} focusClass @hide
20891      */
20892     /**
20893      * @cfg {String} autoCreate @hide
20894      */
20895     /**
20896      * @cfg {String} inputType @hide
20897      */
20898     /**
20899      * @cfg {String} invalidClass @hide
20900      */
20901     /**
20902      * @cfg {String} invalidText @hide
20903      */
20904     /**
20905      * @cfg {String} msgFx @hide
20906      */
20907     /**
20908      * @cfg {String} validateOnBlur @hide
20909      */
20910 });
20911  
20912     
20913    
20914    
20915    
20916       
20917 Roo.namespace('Roo.bootstrap.htmleditor');
20918 /**
20919  * @class Roo.bootstrap.HtmlEditorToolbar1
20920  * Basic Toolbar
20921  * 
20922  * Usage:
20923  *
20924  new Roo.bootstrap.HtmlEditor({
20925     ....
20926     toolbars : [
20927         new Roo.bootstrap.HtmlEditorToolbar1({
20928             disable : { fonts: 1 , format: 1, ..., ... , ...],
20929             btns : [ .... ]
20930         })
20931     }
20932      
20933  * 
20934  * @cfg {Object} disable List of elements to disable..
20935  * @cfg {Array} btns List of additional buttons.
20936  * 
20937  * 
20938  * NEEDS Extra CSS? 
20939  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
20940  */
20941  
20942 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
20943 {
20944     
20945     Roo.apply(this, config);
20946     
20947     // default disabled, based on 'good practice'..
20948     this.disable = this.disable || {};
20949     Roo.applyIf(this.disable, {
20950         fontSize : true,
20951         colors : true,
20952         specialElements : true
20953     });
20954     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
20955     
20956     this.editor = config.editor;
20957     this.editorcore = config.editor.editorcore;
20958     
20959     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
20960     
20961     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
20962     // dont call parent... till later.
20963 }
20964 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
20965      
20966     bar : true,
20967     
20968     editor : false,
20969     editorcore : false,
20970     
20971     
20972     formats : [
20973         "p" ,  
20974         "h1","h2","h3","h4","h5","h6", 
20975         "pre", "code", 
20976         "abbr", "acronym", "address", "cite", "samp", "var",
20977         'div','span'
20978     ],
20979     
20980     onRender : function(ct, position)
20981     {
20982        // Roo.log("Call onRender: " + this.xtype);
20983         
20984        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
20985        Roo.log(this.el);
20986        this.el.dom.style.marginBottom = '0';
20987        var _this = this;
20988        var editorcore = this.editorcore;
20989        var editor= this.editor;
20990        
20991        var children = [];
20992        var btn = function(id,cmd , toggle, handler){
20993        
20994             var  event = toggle ? 'toggle' : 'click';
20995        
20996             var a = {
20997                 size : 'sm',
20998                 xtype: 'Button',
20999                 xns: Roo.bootstrap,
21000                 glyphicon : id,
21001                 cmd : id || cmd,
21002                 enableToggle:toggle !== false,
21003                 //html : 'submit'
21004                 pressed : toggle ? false : null,
21005                 listeners : {}
21006             };
21007             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21008                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21009             };
21010             children.push(a);
21011             return a;
21012        }
21013         
21014         var style = {
21015                 xtype: 'Button',
21016                 size : 'sm',
21017                 xns: Roo.bootstrap,
21018                 glyphicon : 'font',
21019                 //html : 'submit'
21020                 menu : {
21021                     xtype: 'Menu',
21022                     xns: Roo.bootstrap,
21023                     items:  []
21024                 }
21025         };
21026         Roo.each(this.formats, function(f) {
21027             style.menu.items.push({
21028                 xtype :'MenuItem',
21029                 xns: Roo.bootstrap,
21030                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21031                 tagname : f,
21032                 listeners : {
21033                     click : function()
21034                     {
21035                         editorcore.insertTag(this.tagname);
21036                         editor.focus();
21037                     }
21038                 }
21039                 
21040             });
21041         });
21042          children.push(style);   
21043             
21044             
21045         btn('bold',false,true);
21046         btn('italic',false,true);
21047         btn('align-left', 'justifyleft',true);
21048         btn('align-center', 'justifycenter',true);
21049         btn('align-right' , 'justifyright',true);
21050         btn('link', false, false, function(btn) {
21051             //Roo.log("create link?");
21052             var url = prompt(this.createLinkText, this.defaultLinkValue);
21053             if(url && url != 'http:/'+'/'){
21054                 this.editorcore.relayCmd('createlink', url);
21055             }
21056         }),
21057         btn('list','insertunorderedlist',true);
21058         btn('pencil', false,true, function(btn){
21059                 Roo.log(this);
21060                 
21061                 this.toggleSourceEdit(btn.pressed);
21062         });
21063         /*
21064         var cog = {
21065                 xtype: 'Button',
21066                 size : 'sm',
21067                 xns: Roo.bootstrap,
21068                 glyphicon : 'cog',
21069                 //html : 'submit'
21070                 menu : {
21071                     xtype: 'Menu',
21072                     xns: Roo.bootstrap,
21073                     items:  []
21074                 }
21075         };
21076         
21077         cog.menu.items.push({
21078             xtype :'MenuItem',
21079             xns: Roo.bootstrap,
21080             html : Clean styles,
21081             tagname : f,
21082             listeners : {
21083                 click : function()
21084                 {
21085                     editorcore.insertTag(this.tagname);
21086                     editor.focus();
21087                 }
21088             }
21089             
21090         });
21091        */
21092         
21093          
21094        this.xtype = 'NavSimplebar';
21095         
21096         for(var i=0;i< children.length;i++) {
21097             
21098             this.buttons.add(this.addxtypeChild(children[i]));
21099             
21100         }
21101         
21102         editor.on('editorevent', this.updateToolbar, this);
21103     },
21104     onBtnClick : function(id)
21105     {
21106        this.editorcore.relayCmd(id);
21107        this.editorcore.focus();
21108     },
21109     
21110     /**
21111      * Protected method that will not generally be called directly. It triggers
21112      * a toolbar update by reading the markup state of the current selection in the editor.
21113      */
21114     updateToolbar: function(){
21115
21116         if(!this.editorcore.activated){
21117             this.editor.onFirstFocus(); // is this neeed?
21118             return;
21119         }
21120
21121         var btns = this.buttons; 
21122         var doc = this.editorcore.doc;
21123         btns.get('bold').setActive(doc.queryCommandState('bold'));
21124         btns.get('italic').setActive(doc.queryCommandState('italic'));
21125         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21126         
21127         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21128         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21129         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21130         
21131         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21132         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21133          /*
21134         
21135         var ans = this.editorcore.getAllAncestors();
21136         if (this.formatCombo) {
21137             
21138             
21139             var store = this.formatCombo.store;
21140             this.formatCombo.setValue("");
21141             for (var i =0; i < ans.length;i++) {
21142                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21143                     // select it..
21144                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21145                     break;
21146                 }
21147             }
21148         }
21149         
21150         
21151         
21152         // hides menus... - so this cant be on a menu...
21153         Roo.bootstrap.MenuMgr.hideAll();
21154         */
21155         Roo.bootstrap.MenuMgr.hideAll();
21156         //this.editorsyncValue();
21157     },
21158     onFirstFocus: function() {
21159         this.buttons.each(function(item){
21160            item.enable();
21161         });
21162     },
21163     toggleSourceEdit : function(sourceEditMode){
21164         
21165           
21166         if(sourceEditMode){
21167             Roo.log("disabling buttons");
21168            this.buttons.each( function(item){
21169                 if(item.cmd != 'pencil'){
21170                     item.disable();
21171                 }
21172             });
21173           
21174         }else{
21175             Roo.log("enabling buttons");
21176             if(this.editorcore.initialized){
21177                 this.buttons.each( function(item){
21178                     item.enable();
21179                 });
21180             }
21181             
21182         }
21183         Roo.log("calling toggole on editor");
21184         // tell the editor that it's been pressed..
21185         this.editor.toggleSourceEdit(sourceEditMode);
21186        
21187     }
21188 });
21189
21190
21191
21192
21193
21194 /**
21195  * @class Roo.bootstrap.Table.AbstractSelectionModel
21196  * @extends Roo.util.Observable
21197  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21198  * implemented by descendant classes.  This class should not be directly instantiated.
21199  * @constructor
21200  */
21201 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21202     this.locked = false;
21203     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21204 };
21205
21206
21207 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21208     /** @ignore Called by the grid automatically. Do not call directly. */
21209     init : function(grid){
21210         this.grid = grid;
21211         this.initEvents();
21212     },
21213
21214     /**
21215      * Locks the selections.
21216      */
21217     lock : function(){
21218         this.locked = true;
21219     },
21220
21221     /**
21222      * Unlocks the selections.
21223      */
21224     unlock : function(){
21225         this.locked = false;
21226     },
21227
21228     /**
21229      * Returns true if the selections are locked.
21230      * @return {Boolean}
21231      */
21232     isLocked : function(){
21233         return this.locked;
21234     }
21235 });
21236 /**
21237  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21238  * @class Roo.bootstrap.Table.RowSelectionModel
21239  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21240  * It supports multiple selections and keyboard selection/navigation. 
21241  * @constructor
21242  * @param {Object} config
21243  */
21244
21245 Roo.bootstrap.Table.RowSelectionModel = function(config){
21246     Roo.apply(this, config);
21247     this.selections = new Roo.util.MixedCollection(false, function(o){
21248         return o.id;
21249     });
21250
21251     this.last = false;
21252     this.lastActive = false;
21253
21254     this.addEvents({
21255         /**
21256              * @event selectionchange
21257              * Fires when the selection changes
21258              * @param {SelectionModel} this
21259              */
21260             "selectionchange" : true,
21261         /**
21262              * @event afterselectionchange
21263              * Fires after the selection changes (eg. by key press or clicking)
21264              * @param {SelectionModel} this
21265              */
21266             "afterselectionchange" : true,
21267         /**
21268              * @event beforerowselect
21269              * Fires when a row is selected being selected, return false to cancel.
21270              * @param {SelectionModel} this
21271              * @param {Number} rowIndex The selected index
21272              * @param {Boolean} keepExisting False if other selections will be cleared
21273              */
21274             "beforerowselect" : true,
21275         /**
21276              * @event rowselect
21277              * Fires when a row is selected.
21278              * @param {SelectionModel} this
21279              * @param {Number} rowIndex The selected index
21280              * @param {Roo.data.Record} r The record
21281              */
21282             "rowselect" : true,
21283         /**
21284              * @event rowdeselect
21285              * Fires when a row is deselected.
21286              * @param {SelectionModel} this
21287              * @param {Number} rowIndex The selected index
21288              */
21289         "rowdeselect" : true
21290     });
21291     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21292     this.locked = false;
21293 };
21294
21295 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21296     /**
21297      * @cfg {Boolean} singleSelect
21298      * True to allow selection of only one row at a time (defaults to false)
21299      */
21300     singleSelect : false,
21301
21302     // private
21303     initEvents : function(){
21304
21305         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21306             this.grid.on("mousedown", this.handleMouseDown, this);
21307         }else{ // allow click to work like normal
21308             this.grid.on("rowclick", this.handleDragableRowClick, this);
21309         }
21310
21311         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21312             "up" : function(e){
21313                 if(!e.shiftKey){
21314                     this.selectPrevious(e.shiftKey);
21315                 }else if(this.last !== false && this.lastActive !== false){
21316                     var last = this.last;
21317                     this.selectRange(this.last,  this.lastActive-1);
21318                     this.grid.getView().focusRow(this.lastActive);
21319                     if(last !== false){
21320                         this.last = last;
21321                     }
21322                 }else{
21323                     this.selectFirstRow();
21324                 }
21325                 this.fireEvent("afterselectionchange", this);
21326             },
21327             "down" : function(e){
21328                 if(!e.shiftKey){
21329                     this.selectNext(e.shiftKey);
21330                 }else if(this.last !== false && this.lastActive !== false){
21331                     var last = this.last;
21332                     this.selectRange(this.last,  this.lastActive+1);
21333                     this.grid.getView().focusRow(this.lastActive);
21334                     if(last !== false){
21335                         this.last = last;
21336                     }
21337                 }else{
21338                     this.selectFirstRow();
21339                 }
21340                 this.fireEvent("afterselectionchange", this);
21341             },
21342             scope: this
21343         });
21344
21345         var view = this.grid.view;
21346         view.on("refresh", this.onRefresh, this);
21347         view.on("rowupdated", this.onRowUpdated, this);
21348         view.on("rowremoved", this.onRemove, this);
21349     },
21350
21351     // private
21352     onRefresh : function(){
21353         var ds = this.grid.dataSource, i, v = this.grid.view;
21354         var s = this.selections;
21355         s.each(function(r){
21356             if((i = ds.indexOfId(r.id)) != -1){
21357                 v.onRowSelect(i);
21358             }else{
21359                 s.remove(r);
21360             }
21361         });
21362     },
21363
21364     // private
21365     onRemove : function(v, index, r){
21366         this.selections.remove(r);
21367     },
21368
21369     // private
21370     onRowUpdated : function(v, index, r){
21371         if(this.isSelected(r)){
21372             v.onRowSelect(index);
21373         }
21374     },
21375
21376     /**
21377      * Select records.
21378      * @param {Array} records The records to select
21379      * @param {Boolean} keepExisting (optional) True to keep existing selections
21380      */
21381     selectRecords : function(records, keepExisting){
21382         if(!keepExisting){
21383             this.clearSelections();
21384         }
21385         var ds = this.grid.dataSource;
21386         for(var i = 0, len = records.length; i < len; i++){
21387             this.selectRow(ds.indexOf(records[i]), true);
21388         }
21389     },
21390
21391     /**
21392      * Gets the number of selected rows.
21393      * @return {Number}
21394      */
21395     getCount : function(){
21396         return this.selections.length;
21397     },
21398
21399     /**
21400      * Selects the first row in the grid.
21401      */
21402     selectFirstRow : function(){
21403         this.selectRow(0);
21404     },
21405
21406     /**
21407      * Select the last row.
21408      * @param {Boolean} keepExisting (optional) True to keep existing selections
21409      */
21410     selectLastRow : function(keepExisting){
21411         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21412     },
21413
21414     /**
21415      * Selects the row immediately following the last selected row.
21416      * @param {Boolean} keepExisting (optional) True to keep existing selections
21417      */
21418     selectNext : function(keepExisting){
21419         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21420             this.selectRow(this.last+1, keepExisting);
21421             this.grid.getView().focusRow(this.last);
21422         }
21423     },
21424
21425     /**
21426      * Selects the row that precedes the last selected row.
21427      * @param {Boolean} keepExisting (optional) True to keep existing selections
21428      */
21429     selectPrevious : function(keepExisting){
21430         if(this.last){
21431             this.selectRow(this.last-1, keepExisting);
21432             this.grid.getView().focusRow(this.last);
21433         }
21434     },
21435
21436     /**
21437      * Returns the selected records
21438      * @return {Array} Array of selected records
21439      */
21440     getSelections : function(){
21441         return [].concat(this.selections.items);
21442     },
21443
21444     /**
21445      * Returns the first selected record.
21446      * @return {Record}
21447      */
21448     getSelected : function(){
21449         return this.selections.itemAt(0);
21450     },
21451
21452
21453     /**
21454      * Clears all selections.
21455      */
21456     clearSelections : function(fast){
21457         if(this.locked) return;
21458         if(fast !== true){
21459             var ds = this.grid.dataSource;
21460             var s = this.selections;
21461             s.each(function(r){
21462                 this.deselectRow(ds.indexOfId(r.id));
21463             }, this);
21464             s.clear();
21465         }else{
21466             this.selections.clear();
21467         }
21468         this.last = false;
21469     },
21470
21471
21472     /**
21473      * Selects all rows.
21474      */
21475     selectAll : function(){
21476         if(this.locked) return;
21477         this.selections.clear();
21478         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21479             this.selectRow(i, true);
21480         }
21481     },
21482
21483     /**
21484      * Returns True if there is a selection.
21485      * @return {Boolean}
21486      */
21487     hasSelection : function(){
21488         return this.selections.length > 0;
21489     },
21490
21491     /**
21492      * Returns True if the specified row is selected.
21493      * @param {Number/Record} record The record or index of the record to check
21494      * @return {Boolean}
21495      */
21496     isSelected : function(index){
21497         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21498         return (r && this.selections.key(r.id) ? true : false);
21499     },
21500
21501     /**
21502      * Returns True if the specified record id is selected.
21503      * @param {String} id The id of record to check
21504      * @return {Boolean}
21505      */
21506     isIdSelected : function(id){
21507         return (this.selections.key(id) ? true : false);
21508     },
21509
21510     // private
21511     handleMouseDown : function(e, t){
21512         var view = this.grid.getView(), rowIndex;
21513         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21514             return;
21515         };
21516         if(e.shiftKey && this.last !== false){
21517             var last = this.last;
21518             this.selectRange(last, rowIndex, e.ctrlKey);
21519             this.last = last; // reset the last
21520             view.focusRow(rowIndex);
21521         }else{
21522             var isSelected = this.isSelected(rowIndex);
21523             if(e.button !== 0 && isSelected){
21524                 view.focusRow(rowIndex);
21525             }else if(e.ctrlKey && isSelected){
21526                 this.deselectRow(rowIndex);
21527             }else if(!isSelected){
21528                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21529                 view.focusRow(rowIndex);
21530             }
21531         }
21532         this.fireEvent("afterselectionchange", this);
21533     },
21534     // private
21535     handleDragableRowClick :  function(grid, rowIndex, e) 
21536     {
21537         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21538             this.selectRow(rowIndex, false);
21539             grid.view.focusRow(rowIndex);
21540              this.fireEvent("afterselectionchange", this);
21541         }
21542     },
21543     
21544     /**
21545      * Selects multiple rows.
21546      * @param {Array} rows Array of the indexes of the row to select
21547      * @param {Boolean} keepExisting (optional) True to keep existing selections
21548      */
21549     selectRows : function(rows, keepExisting){
21550         if(!keepExisting){
21551             this.clearSelections();
21552         }
21553         for(var i = 0, len = rows.length; i < len; i++){
21554             this.selectRow(rows[i], true);
21555         }
21556     },
21557
21558     /**
21559      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21560      * @param {Number} startRow The index of the first row in the range
21561      * @param {Number} endRow The index of the last row in the range
21562      * @param {Boolean} keepExisting (optional) True to retain existing selections
21563      */
21564     selectRange : function(startRow, endRow, keepExisting){
21565         if(this.locked) return;
21566         if(!keepExisting){
21567             this.clearSelections();
21568         }
21569         if(startRow <= endRow){
21570             for(var i = startRow; i <= endRow; i++){
21571                 this.selectRow(i, true);
21572             }
21573         }else{
21574             for(var i = startRow; i >= endRow; i--){
21575                 this.selectRow(i, true);
21576             }
21577         }
21578     },
21579
21580     /**
21581      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21582      * @param {Number} startRow The index of the first row in the range
21583      * @param {Number} endRow The index of the last row in the range
21584      */
21585     deselectRange : function(startRow, endRow, preventViewNotify){
21586         if(this.locked) return;
21587         for(var i = startRow; i <= endRow; i++){
21588             this.deselectRow(i, preventViewNotify);
21589         }
21590     },
21591
21592     /**
21593      * Selects a row.
21594      * @param {Number} row The index of the row to select
21595      * @param {Boolean} keepExisting (optional) True to keep existing selections
21596      */
21597     selectRow : function(index, keepExisting, preventViewNotify){
21598         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21599         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21600             if(!keepExisting || this.singleSelect){
21601                 this.clearSelections();
21602             }
21603             var r = this.grid.dataSource.getAt(index);
21604             this.selections.add(r);
21605             this.last = this.lastActive = index;
21606             if(!preventViewNotify){
21607                 this.grid.getView().onRowSelect(index);
21608             }
21609             this.fireEvent("rowselect", this, index, r);
21610             this.fireEvent("selectionchange", this);
21611         }
21612     },
21613
21614     /**
21615      * Deselects a row.
21616      * @param {Number} row The index of the row to deselect
21617      */
21618     deselectRow : function(index, preventViewNotify){
21619         if(this.locked) return;
21620         if(this.last == index){
21621             this.last = false;
21622         }
21623         if(this.lastActive == index){
21624             this.lastActive = false;
21625         }
21626         var r = this.grid.dataSource.getAt(index);
21627         this.selections.remove(r);
21628         if(!preventViewNotify){
21629             this.grid.getView().onRowDeselect(index);
21630         }
21631         this.fireEvent("rowdeselect", this, index);
21632         this.fireEvent("selectionchange", this);
21633     },
21634
21635     // private
21636     restoreLast : function(){
21637         if(this._last){
21638             this.last = this._last;
21639         }
21640     },
21641
21642     // private
21643     acceptsNav : function(row, col, cm){
21644         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21645     },
21646
21647     // private
21648     onEditorKey : function(field, e){
21649         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21650         if(k == e.TAB){
21651             e.stopEvent();
21652             ed.completeEdit();
21653             if(e.shiftKey){
21654                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21655             }else{
21656                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21657             }
21658         }else if(k == e.ENTER && !e.ctrlKey){
21659             e.stopEvent();
21660             ed.completeEdit();
21661             if(e.shiftKey){
21662                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21663             }else{
21664                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21665             }
21666         }else if(k == e.ESC){
21667             ed.cancelEdit();
21668         }
21669         if(newCell){
21670             g.startEditing(newCell[0], newCell[1]);
21671         }
21672     }
21673 });/*
21674  * Based on:
21675  * Ext JS Library 1.1.1
21676  * Copyright(c) 2006-2007, Ext JS, LLC.
21677  *
21678  * Originally Released Under LGPL - original licence link has changed is not relivant.
21679  *
21680  * Fork - LGPL
21681  * <script type="text/javascript">
21682  */
21683  
21684 /**
21685  * @class Roo.bootstrap.PagingToolbar
21686  * @extends Roo.bootstrap.NavSimplebar
21687  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21688  * @constructor
21689  * Create a new PagingToolbar
21690  * @param {Object} config The config object
21691  * @param {Roo.data.Store} store
21692  */
21693 Roo.bootstrap.PagingToolbar = function(config)
21694 {
21695     // old args format still supported... - xtype is prefered..
21696         // created from xtype...
21697     
21698     this.ds = config.dataSource;
21699     
21700     if (config.store && !this.ds) {
21701         this.store= Roo.factory(config.store, Roo.data);
21702         this.ds = this.store;
21703         this.ds.xmodule = this.xmodule || false;
21704     }
21705     
21706     this.toolbarItems = [];
21707     if (config.items) {
21708         this.toolbarItems = config.items;
21709     }
21710     
21711     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21712     
21713     this.cursor = 0;
21714     
21715     if (this.ds) { 
21716         this.bind(this.ds);
21717     }
21718     
21719     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21720     
21721 };
21722
21723 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21724     /**
21725      * @cfg {Roo.data.Store} dataSource
21726      * The underlying data store providing the paged data
21727      */
21728     /**
21729      * @cfg {String/HTMLElement/Element} container
21730      * container The id or element that will contain the toolbar
21731      */
21732     /**
21733      * @cfg {Boolean} displayInfo
21734      * True to display the displayMsg (defaults to false)
21735      */
21736     /**
21737      * @cfg {Number} pageSize
21738      * The number of records to display per page (defaults to 20)
21739      */
21740     pageSize: 20,
21741     /**
21742      * @cfg {String} displayMsg
21743      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21744      */
21745     displayMsg : 'Displaying {0} - {1} of {2}',
21746     /**
21747      * @cfg {String} emptyMsg
21748      * The message to display when no records are found (defaults to "No data to display")
21749      */
21750     emptyMsg : 'No data to display',
21751     /**
21752      * Customizable piece of the default paging text (defaults to "Page")
21753      * @type String
21754      */
21755     beforePageText : "Page",
21756     /**
21757      * Customizable piece of the default paging text (defaults to "of %0")
21758      * @type String
21759      */
21760     afterPageText : "of {0}",
21761     /**
21762      * Customizable piece of the default paging text (defaults to "First Page")
21763      * @type String
21764      */
21765     firstText : "First Page",
21766     /**
21767      * Customizable piece of the default paging text (defaults to "Previous Page")
21768      * @type String
21769      */
21770     prevText : "Previous Page",
21771     /**
21772      * Customizable piece of the default paging text (defaults to "Next Page")
21773      * @type String
21774      */
21775     nextText : "Next Page",
21776     /**
21777      * Customizable piece of the default paging text (defaults to "Last Page")
21778      * @type String
21779      */
21780     lastText : "Last Page",
21781     /**
21782      * Customizable piece of the default paging text (defaults to "Refresh")
21783      * @type String
21784      */
21785     refreshText : "Refresh",
21786
21787     buttons : false,
21788     // private
21789     onRender : function(ct, position) 
21790     {
21791         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21792         this.navgroup.parentId = this.id;
21793         this.navgroup.onRender(this.el, null);
21794         // add the buttons to the navgroup
21795         
21796         if(this.displayInfo){
21797             Roo.log(this.el.select('ul.navbar-nav',true).first());
21798             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21799             this.displayEl = this.el.select('.x-paging-info', true).first();
21800 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21801 //            this.displayEl = navel.el.select('span',true).first();
21802         }
21803         
21804         var _this = this;
21805         
21806         if(this.buttons){
21807             Roo.each(_this.buttons, function(e){ // this might need to use render????
21808                Roo.factory(e).onRender(_this.el, null);
21809             });
21810         }
21811             
21812         Roo.each(_this.toolbarItems, function(e) {
21813             _this.navgroup.addItem(e);
21814         });
21815         
21816         
21817         this.first = this.navgroup.addItem({
21818             tooltip: this.firstText,
21819             cls: "prev",
21820             icon : 'fa fa-backward',
21821             disabled: true,
21822             preventDefault: true,
21823             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21824         });
21825         
21826         this.prev =  this.navgroup.addItem({
21827             tooltip: this.prevText,
21828             cls: "prev",
21829             icon : 'fa fa-step-backward',
21830             disabled: true,
21831             preventDefault: true,
21832             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21833         });
21834     //this.addSeparator();
21835         
21836         
21837         var field = this.navgroup.addItem( {
21838             tagtype : 'span',
21839             cls : 'x-paging-position',
21840             
21841             html : this.beforePageText  +
21842                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21843                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21844          } ); //?? escaped?
21845         
21846         this.field = field.el.select('input', true).first();
21847         this.field.on("keydown", this.onPagingKeydown, this);
21848         this.field.on("focus", function(){this.dom.select();});
21849     
21850     
21851         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21852         //this.field.setHeight(18);
21853         //this.addSeparator();
21854         this.next = this.navgroup.addItem({
21855             tooltip: this.nextText,
21856             cls: "next",
21857             html : ' <i class="fa fa-step-forward">',
21858             disabled: true,
21859             preventDefault: true,
21860             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21861         });
21862         this.last = this.navgroup.addItem({
21863             tooltip: this.lastText,
21864             icon : 'fa fa-forward',
21865             cls: "next",
21866             disabled: true,
21867             preventDefault: true,
21868             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21869         });
21870     //this.addSeparator();
21871         this.loading = this.navgroup.addItem({
21872             tooltip: this.refreshText,
21873             icon: 'fa fa-refresh',
21874             preventDefault: true,
21875             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21876         });
21877         
21878     },
21879
21880     // private
21881     updateInfo : function(){
21882         if(this.displayEl){
21883             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21884             var msg = count == 0 ?
21885                 this.emptyMsg :
21886                 String.format(
21887                     this.displayMsg,
21888                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21889                 );
21890             this.displayEl.update(msg);
21891         }
21892     },
21893
21894     // private
21895     onLoad : function(ds, r, o){
21896        this.cursor = o.params ? o.params.start : 0;
21897        var d = this.getPageData(),
21898             ap = d.activePage,
21899             ps = d.pages;
21900         
21901        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21902        this.field.dom.value = ap;
21903        this.first.setDisabled(ap == 1);
21904        this.prev.setDisabled(ap == 1);
21905        this.next.setDisabled(ap == ps);
21906        this.last.setDisabled(ap == ps);
21907        this.loading.enable();
21908        this.updateInfo();
21909     },
21910
21911     // private
21912     getPageData : function(){
21913         var total = this.ds.getTotalCount();
21914         return {
21915             total : total,
21916             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
21917             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
21918         };
21919     },
21920
21921     // private
21922     onLoadError : function(){
21923         this.loading.enable();
21924     },
21925
21926     // private
21927     onPagingKeydown : function(e){
21928         var k = e.getKey();
21929         var d = this.getPageData();
21930         if(k == e.RETURN){
21931             var v = this.field.dom.value, pageNum;
21932             if(!v || isNaN(pageNum = parseInt(v, 10))){
21933                 this.field.dom.value = d.activePage;
21934                 return;
21935             }
21936             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
21937             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21938             e.stopEvent();
21939         }
21940         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))
21941         {
21942           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
21943           this.field.dom.value = pageNum;
21944           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
21945           e.stopEvent();
21946         }
21947         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21948         {
21949           var v = this.field.dom.value, pageNum; 
21950           var increment = (e.shiftKey) ? 10 : 1;
21951           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
21952             increment *= -1;
21953           if(!v || isNaN(pageNum = parseInt(v, 10))) {
21954             this.field.dom.value = d.activePage;
21955             return;
21956           }
21957           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
21958           {
21959             this.field.dom.value = parseInt(v, 10) + increment;
21960             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
21961             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
21962           }
21963           e.stopEvent();
21964         }
21965     },
21966
21967     // private
21968     beforeLoad : function(){
21969         if(this.loading){
21970             this.loading.disable();
21971         }
21972     },
21973
21974     // private
21975     onClick : function(which){
21976         
21977         var ds = this.ds;
21978         if (!ds) {
21979             return;
21980         }
21981         
21982         switch(which){
21983             case "first":
21984                 ds.load({params:{start: 0, limit: this.pageSize}});
21985             break;
21986             case "prev":
21987                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
21988             break;
21989             case "next":
21990                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
21991             break;
21992             case "last":
21993                 var total = ds.getTotalCount();
21994                 var extra = total % this.pageSize;
21995                 var lastStart = extra ? (total - extra) : total-this.pageSize;
21996                 ds.load({params:{start: lastStart, limit: this.pageSize}});
21997             break;
21998             case "refresh":
21999                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22000             break;
22001         }
22002     },
22003
22004     /**
22005      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22006      * @param {Roo.data.Store} store The data store to unbind
22007      */
22008     unbind : function(ds){
22009         ds.un("beforeload", this.beforeLoad, this);
22010         ds.un("load", this.onLoad, this);
22011         ds.un("loadexception", this.onLoadError, this);
22012         ds.un("remove", this.updateInfo, this);
22013         ds.un("add", this.updateInfo, this);
22014         this.ds = undefined;
22015     },
22016
22017     /**
22018      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22019      * @param {Roo.data.Store} store The data store to bind
22020      */
22021     bind : function(ds){
22022         ds.on("beforeload", this.beforeLoad, this);
22023         ds.on("load", this.onLoad, this);
22024         ds.on("loadexception", this.onLoadError, this);
22025         ds.on("remove", this.updateInfo, this);
22026         ds.on("add", this.updateInfo, this);
22027         this.ds = ds;
22028     }
22029 });/*
22030  * - LGPL
22031  *
22032  * element
22033  * 
22034  */
22035
22036 /**
22037  * @class Roo.bootstrap.MessageBar
22038  * @extends Roo.bootstrap.Component
22039  * Bootstrap MessageBar class
22040  * @cfg {String} html contents of the MessageBar
22041  * @cfg {String} weight (info | success | warning | danger) default info
22042  * @cfg {String} beforeClass insert the bar before the given class
22043  * @cfg {Boolean} closable (true | false) default false
22044  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22045  * 
22046  * @constructor
22047  * Create a new Element
22048  * @param {Object} config The config object
22049  */
22050
22051 Roo.bootstrap.MessageBar = function(config){
22052     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22053 };
22054
22055 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22056     
22057     html: '',
22058     weight: 'info',
22059     closable: false,
22060     fixed: false,
22061     beforeClass: 'bootstrap-sticky-wrap',
22062     
22063     getAutoCreate : function(){
22064         
22065         var cfg = {
22066             tag: 'div',
22067             cls: 'alert alert-dismissable alert-' + this.weight,
22068             cn: [
22069                 {
22070                     tag: 'span',
22071                     cls: 'message',
22072                     html: this.html || ''
22073                 }
22074             ]
22075         }
22076         
22077         if(this.fixed){
22078             cfg.cls += ' alert-messages-fixed';
22079         }
22080         
22081         if(this.closable){
22082             cfg.cn.push({
22083                 tag: 'button',
22084                 cls: 'close',
22085                 html: 'x'
22086             });
22087         }
22088         
22089         return cfg;
22090     },
22091     
22092     onRender : function(ct, position)
22093     {
22094         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22095         
22096         if(!this.el){
22097             var cfg = Roo.apply({},  this.getAutoCreate());
22098             cfg.id = Roo.id();
22099             
22100             if (this.cls) {
22101                 cfg.cls += ' ' + this.cls;
22102             }
22103             if (this.style) {
22104                 cfg.style = this.style;
22105             }
22106             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22107             
22108             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22109         }
22110         
22111         this.el.select('>button.close').on('click', this.hide, this);
22112         
22113     },
22114     
22115     show : function()
22116     {
22117         if (!this.rendered) {
22118             this.render();
22119         }
22120         
22121         this.el.show();
22122         
22123         this.fireEvent('show', this);
22124         
22125     },
22126     
22127     hide : function()
22128     {
22129         if (!this.rendered) {
22130             this.render();
22131         }
22132         
22133         this.el.hide();
22134         
22135         this.fireEvent('hide', this);
22136     },
22137     
22138     update : function()
22139     {
22140 //        var e = this.el.dom.firstChild;
22141 //        
22142 //        if(this.closable){
22143 //            e = e.nextSibling;
22144 //        }
22145 //        
22146 //        e.data = this.html || '';
22147
22148         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22149     }
22150    
22151 });
22152
22153  
22154
22155      /*
22156  * - LGPL
22157  *
22158  * Graph
22159  * 
22160  */
22161
22162
22163 /**
22164  * @class Roo.bootstrap.Graph
22165  * @extends Roo.bootstrap.Component
22166  * Bootstrap Graph class
22167 > Prameters
22168  -sm {number} sm 4
22169  -md {number} md 5
22170  @cfg {String} graphtype  bar | vbar | pie
22171  @cfg {number} g_x coodinator | centre x (pie)
22172  @cfg {number} g_y coodinator | centre y (pie)
22173  @cfg {number} g_r radius (pie)
22174  @cfg {number} g_height height of the chart (respected by all elements in the set)
22175  @cfg {number} g_width width of the chart (respected by all elements in the set)
22176  @cfg {Object} title The title of the chart
22177     
22178  -{Array}  values
22179  -opts (object) options for the chart 
22180      o {
22181      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22182      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22183      o vgutter (number)
22184      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.
22185      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22186      o to
22187      o stretch (boolean)
22188      o }
22189  -opts (object) options for the pie
22190      o{
22191      o cut
22192      o startAngle (number)
22193      o endAngle (number)
22194      } 
22195  *
22196  * @constructor
22197  * Create a new Input
22198  * @param {Object} config The config object
22199  */
22200
22201 Roo.bootstrap.Graph = function(config){
22202     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22203     
22204     this.addEvents({
22205         // img events
22206         /**
22207          * @event click
22208          * The img click event for the img.
22209          * @param {Roo.EventObject} e
22210          */
22211         "click" : true
22212     });
22213 };
22214
22215 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22216     
22217     sm: 4,
22218     md: 5,
22219     graphtype: 'bar',
22220     g_height: 250,
22221     g_width: 400,
22222     g_x: 50,
22223     g_y: 50,
22224     g_r: 30,
22225     opts:{
22226         //g_colors: this.colors,
22227         g_type: 'soft',
22228         g_gutter: '20%'
22229
22230     },
22231     title : false,
22232
22233     getAutoCreate : function(){
22234         
22235         var cfg = {
22236             tag: 'div',
22237             html : null
22238         }
22239         
22240         
22241         return  cfg;
22242     },
22243
22244     onRender : function(ct,position){
22245         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22246         this.raphael = Raphael(this.el.dom);
22247         
22248                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22249                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22250                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22251                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22252                 /*
22253                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22254                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22255                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22256                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22257                 
22258                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22259                 r.barchart(330, 10, 300, 220, data1);
22260                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22261                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22262                 */
22263                 
22264                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22265                 // r.barchart(30, 30, 560, 250,  xdata, {
22266                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22267                 //     axis : "0 0 1 1",
22268                 //     axisxlabels :  xdata
22269                 //     //yvalues : cols,
22270                    
22271                 // });
22272 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22273 //        
22274 //        this.load(null,xdata,{
22275 //                axis : "0 0 1 1",
22276 //                axisxlabels :  xdata
22277 //                });
22278
22279     },
22280
22281     load : function(graphtype,xdata,opts){
22282         this.raphael.clear();
22283         if(!graphtype) {
22284             graphtype = this.graphtype;
22285         }
22286         if(!opts){
22287             opts = this.opts;
22288         }
22289         var r = this.raphael,
22290             fin = function () {
22291                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22292             },
22293             fout = function () {
22294                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22295             },
22296             pfin = function() {
22297                 this.sector.stop();
22298                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22299
22300                 if (this.label) {
22301                     this.label[0].stop();
22302                     this.label[0].attr({ r: 7.5 });
22303                     this.label[1].attr({ "font-weight": 800 });
22304                 }
22305             },
22306             pfout = function() {
22307                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22308
22309                 if (this.label) {
22310                     this.label[0].animate({ r: 5 }, 500, "bounce");
22311                     this.label[1].attr({ "font-weight": 400 });
22312                 }
22313             };
22314
22315         switch(graphtype){
22316             case 'bar':
22317                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22318                 break;
22319             case 'hbar':
22320                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22321                 break;
22322             case 'pie':
22323 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22324 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22325 //            
22326                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22327                 
22328                 break;
22329
22330         }
22331         
22332         if(this.title){
22333             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22334         }
22335         
22336     },
22337     
22338     setTitle: function(o)
22339     {
22340         this.title = o;
22341     },
22342     
22343     initEvents: function() {
22344         
22345         if(!this.href){
22346             this.el.on('click', this.onClick, this);
22347         }
22348     },
22349     
22350     onClick : function(e)
22351     {
22352         Roo.log('img onclick');
22353         this.fireEvent('click', this, e);
22354     }
22355    
22356 });
22357
22358  
22359 /*
22360  * - LGPL
22361  *
22362  * numberBox
22363  * 
22364  */
22365 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22366
22367 /**
22368  * @class Roo.bootstrap.dash.NumberBox
22369  * @extends Roo.bootstrap.Component
22370  * Bootstrap NumberBox class
22371  * @cfg {String} headline Box headline
22372  * @cfg {String} content Box content
22373  * @cfg {String} icon Box icon
22374  * @cfg {String} footer Footer text
22375  * @cfg {String} fhref Footer href
22376  * 
22377  * @constructor
22378  * Create a new NumberBox
22379  * @param {Object} config The config object
22380  */
22381
22382
22383 Roo.bootstrap.dash.NumberBox = function(config){
22384     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22385     
22386 };
22387
22388 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22389     
22390     headline : '',
22391     content : '',
22392     icon : '',
22393     footer : '',
22394     fhref : '',
22395     ficon : '',
22396     
22397     getAutoCreate : function(){
22398         
22399         var cfg = {
22400             tag : 'div',
22401             cls : 'small-box ',
22402             cn : [
22403                 {
22404                     tag : 'div',
22405                     cls : 'inner',
22406                     cn :[
22407                         {
22408                             tag : 'h3',
22409                             cls : 'roo-headline',
22410                             html : this.headline
22411                         },
22412                         {
22413                             tag : 'p',
22414                             cls : 'roo-content',
22415                             html : this.content
22416                         }
22417                     ]
22418                 }
22419             ]
22420         }
22421         
22422         if(this.icon){
22423             cfg.cn.push({
22424                 tag : 'div',
22425                 cls : 'icon',
22426                 cn :[
22427                     {
22428                         tag : 'i',
22429                         cls : 'ion ' + this.icon
22430                     }
22431                 ]
22432             });
22433         }
22434         
22435         if(this.footer){
22436             var footer = {
22437                 tag : 'a',
22438                 cls : 'small-box-footer',
22439                 href : this.fhref || '#',
22440                 html : this.footer
22441             };
22442             
22443             cfg.cn.push(footer);
22444             
22445         }
22446         
22447         return  cfg;
22448     },
22449
22450     onRender : function(ct,position){
22451         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22452
22453
22454        
22455                 
22456     },
22457
22458     setHeadline: function (value)
22459     {
22460         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22461     },
22462     
22463     setFooter: function (value, href)
22464     {
22465         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22466         
22467         if(href){
22468             this.el.select('a.small-box-footer',true).first().attr('href', href);
22469         }
22470         
22471     },
22472
22473     setContent: function (value)
22474     {
22475         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22476     },
22477
22478     initEvents: function() 
22479     {   
22480         
22481     }
22482     
22483 });
22484
22485  
22486 /*
22487  * - LGPL
22488  *
22489  * TabBox
22490  * 
22491  */
22492 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22493
22494 /**
22495  * @class Roo.bootstrap.dash.TabBox
22496  * @extends Roo.bootstrap.Component
22497  * Bootstrap TabBox class
22498  * @cfg {String} title Title of the TabBox
22499  * @cfg {String} icon Icon of the TabBox
22500  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22501  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22502  * 
22503  * @constructor
22504  * Create a new TabBox
22505  * @param {Object} config The config object
22506  */
22507
22508
22509 Roo.bootstrap.dash.TabBox = function(config){
22510     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22511     this.addEvents({
22512         // raw events
22513         /**
22514          * @event addpane
22515          * When a pane is added
22516          * @param {Roo.bootstrap.dash.TabPane} pane
22517          */
22518         "addpane" : true,
22519         /**
22520          * @event activatepane
22521          * When a pane is activated
22522          * @param {Roo.bootstrap.dash.TabPane} pane
22523          */
22524         "activatepane" : true
22525         
22526          
22527     });
22528     
22529     this.panes = [];
22530 };
22531
22532 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22533
22534     title : '',
22535     icon : false,
22536     showtabs : true,
22537     tabScrollable : false,
22538     
22539     getChildContainer : function()
22540     {
22541         return this.el.select('.tab-content', true).first();
22542     },
22543     
22544     getAutoCreate : function(){
22545         
22546         var header = {
22547             tag: 'li',
22548             cls: 'pull-left header',
22549             html: this.title,
22550             cn : []
22551         };
22552         
22553         if(this.icon){
22554             header.cn.push({
22555                 tag: 'i',
22556                 cls: 'fa ' + this.icon
22557             });
22558         }
22559         
22560         var h = {
22561             tag: 'ul',
22562             cls: 'nav nav-tabs pull-right',
22563             cn: [
22564                 header
22565             ]
22566         };
22567         
22568         if(this.tabScrollable){
22569             h = {
22570                 tag: 'div',
22571                 cls: 'tab-header',
22572                 cn: [
22573                     {
22574                         tag: 'ul',
22575                         cls: 'nav nav-tabs pull-right',
22576                         cn: [
22577                             header
22578                         ]
22579                     }
22580                 ]
22581             }
22582         }
22583         
22584         var cfg = {
22585             tag: 'div',
22586             cls: 'nav-tabs-custom',
22587             cn: [
22588                 h,
22589                 {
22590                     tag: 'div',
22591                     cls: 'tab-content no-padding',
22592                     cn: []
22593                 }
22594             ]
22595         }
22596
22597         return  cfg;
22598     },
22599     initEvents : function()
22600     {
22601         //Roo.log('add add pane handler');
22602         this.on('addpane', this.onAddPane, this);
22603     },
22604      /**
22605      * Updates the box title
22606      * @param {String} html to set the title to.
22607      */
22608     setTitle : function(value)
22609     {
22610         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22611     },
22612     onAddPane : function(pane)
22613     {
22614         this.panes.push(pane);
22615         //Roo.log('addpane');
22616         //Roo.log(pane);
22617         // tabs are rendere left to right..
22618         if(!this.showtabs){
22619             return;
22620         }
22621         
22622         var ctr = this.el.select('.nav-tabs', true).first();
22623          
22624          
22625         var existing = ctr.select('.nav-tab',true);
22626         var qty = existing.getCount();;
22627         
22628         
22629         var tab = ctr.createChild({
22630             tag : 'li',
22631             cls : 'nav-tab' + (qty ? '' : ' active'),
22632             cn : [
22633                 {
22634                     tag : 'a',
22635                     href:'#',
22636                     html : pane.title
22637                 }
22638             ]
22639         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22640         pane.tab = tab;
22641         
22642         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22643         if (!qty) {
22644             pane.el.addClass('active');
22645         }
22646         
22647                 
22648     },
22649     onTabClick : function(ev,un,ob,pane)
22650     {
22651         //Roo.log('tab - prev default');
22652         ev.preventDefault();
22653         
22654         
22655         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22656         pane.tab.addClass('active');
22657         //Roo.log(pane.title);
22658         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22659         // technically we should have a deactivate event.. but maybe add later.
22660         // and it should not de-activate the selected tab...
22661         this.fireEvent('activatepane', pane);
22662         pane.el.addClass('active');
22663         pane.fireEvent('activate');
22664         
22665         
22666     },
22667     
22668     getActivePane : function()
22669     {
22670         var r = false;
22671         Roo.each(this.panes, function(p) {
22672             if(p.el.hasClass('active')){
22673                 r = p;
22674                 return false;
22675             }
22676             
22677             return;
22678         });
22679         
22680         return r;
22681     }
22682     
22683     
22684 });
22685
22686  
22687 /*
22688  * - LGPL
22689  *
22690  * Tab pane
22691  * 
22692  */
22693 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22694 /**
22695  * @class Roo.bootstrap.TabPane
22696  * @extends Roo.bootstrap.Component
22697  * Bootstrap TabPane class
22698  * @cfg {Boolean} active (false | true) Default false
22699  * @cfg {String} title title of panel
22700
22701  * 
22702  * @constructor
22703  * Create a new TabPane
22704  * @param {Object} config The config object
22705  */
22706
22707 Roo.bootstrap.dash.TabPane = function(config){
22708     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22709     
22710     this.addEvents({
22711         // raw events
22712         /**
22713          * @event activate
22714          * When a pane is activated
22715          * @param {Roo.bootstrap.dash.TabPane} pane
22716          */
22717         "activate" : true
22718          
22719     });
22720 };
22721
22722 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22723     
22724     active : false,
22725     title : '',
22726     
22727     // the tabBox that this is attached to.
22728     tab : false,
22729      
22730     getAutoCreate : function() 
22731     {
22732         var cfg = {
22733             tag: 'div',
22734             cls: 'tab-pane'
22735         }
22736         
22737         if(this.active){
22738             cfg.cls += ' active';
22739         }
22740         
22741         return cfg;
22742     },
22743     initEvents  : function()
22744     {
22745         //Roo.log('trigger add pane handler');
22746         this.parent().fireEvent('addpane', this)
22747     },
22748     
22749      /**
22750      * Updates the tab title 
22751      * @param {String} html to set the title to.
22752      */
22753     setTitle: function(str)
22754     {
22755         if (!this.tab) {
22756             return;
22757         }
22758         this.title = str;
22759         this.tab.select('a', true).first().dom.innerHTML = str;
22760         
22761     }
22762     
22763     
22764     
22765 });
22766
22767  
22768
22769
22770  /*
22771  * - LGPL
22772  *
22773  * menu
22774  * 
22775  */
22776 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22777
22778 /**
22779  * @class Roo.bootstrap.menu.Menu
22780  * @extends Roo.bootstrap.Component
22781  * Bootstrap Menu class - container for Menu
22782  * @cfg {String} html Text of the menu
22783  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22784  * @cfg {String} icon Font awesome icon
22785  * @cfg {String} pos Menu align to (top | bottom) default bottom
22786  * 
22787  * 
22788  * @constructor
22789  * Create a new Menu
22790  * @param {Object} config The config object
22791  */
22792
22793
22794 Roo.bootstrap.menu.Menu = function(config){
22795     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22796     
22797     this.addEvents({
22798         /**
22799          * @event beforeshow
22800          * Fires before this menu is displayed
22801          * @param {Roo.bootstrap.menu.Menu} this
22802          */
22803         beforeshow : true,
22804         /**
22805          * @event beforehide
22806          * Fires before this menu is hidden
22807          * @param {Roo.bootstrap.menu.Menu} this
22808          */
22809         beforehide : true,
22810         /**
22811          * @event show
22812          * Fires after this menu is displayed
22813          * @param {Roo.bootstrap.menu.Menu} this
22814          */
22815         show : true,
22816         /**
22817          * @event hide
22818          * Fires after this menu is hidden
22819          * @param {Roo.bootstrap.menu.Menu} this
22820          */
22821         hide : true,
22822         /**
22823          * @event click
22824          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22825          * @param {Roo.bootstrap.menu.Menu} this
22826          * @param {Roo.EventObject} e
22827          */
22828         click : true
22829     });
22830     
22831 };
22832
22833 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22834     
22835     submenu : false,
22836     html : '',
22837     weight : 'default',
22838     icon : false,
22839     pos : 'bottom',
22840     
22841     
22842     getChildContainer : function() {
22843         if(this.isSubMenu){
22844             return this.el;
22845         }
22846         
22847         return this.el.select('ul.dropdown-menu', true).first();  
22848     },
22849     
22850     getAutoCreate : function()
22851     {
22852         var text = [
22853             {
22854                 tag : 'span',
22855                 cls : 'roo-menu-text',
22856                 html : this.html
22857             }
22858         ];
22859         
22860         if(this.icon){
22861             text.unshift({
22862                 tag : 'i',
22863                 cls : 'fa ' + this.icon
22864             })
22865         }
22866         
22867         
22868         var cfg = {
22869             tag : 'div',
22870             cls : 'btn-group',
22871             cn : [
22872                 {
22873                     tag : 'button',
22874                     cls : 'dropdown-button btn btn-' + this.weight,
22875                     cn : text
22876                 },
22877                 {
22878                     tag : 'button',
22879                     cls : 'dropdown-toggle btn btn-' + this.weight,
22880                     cn : [
22881                         {
22882                             tag : 'span',
22883                             cls : 'caret'
22884                         }
22885                     ]
22886                 },
22887                 {
22888                     tag : 'ul',
22889                     cls : 'dropdown-menu'
22890                 }
22891             ]
22892             
22893         };
22894         
22895         if(this.pos == 'top'){
22896             cfg.cls += ' dropup';
22897         }
22898         
22899         if(this.isSubMenu){
22900             cfg = {
22901                 tag : 'ul',
22902                 cls : 'dropdown-menu'
22903             }
22904         }
22905         
22906         return cfg;
22907     },
22908     
22909     onRender : function(ct, position)
22910     {
22911         this.isSubMenu = ct.hasClass('dropdown-submenu');
22912         
22913         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
22914     },
22915     
22916     initEvents : function() 
22917     {
22918         if(this.isSubMenu){
22919             return;
22920         }
22921         
22922         this.hidden = true;
22923         
22924         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
22925         this.triggerEl.on('click', this.onTriggerPress, this);
22926         
22927         this.buttonEl = this.el.select('button.dropdown-button', true).first();
22928         this.buttonEl.on('click', this.onClick, this);
22929         
22930     },
22931     
22932     list : function()
22933     {
22934         if(this.isSubMenu){
22935             return this.el;
22936         }
22937         
22938         return this.el.select('ul.dropdown-menu', true).first();
22939     },
22940     
22941     onClick : function(e)
22942     {
22943         this.fireEvent("click", this, e);
22944     },
22945     
22946     onTriggerPress  : function(e)
22947     {   
22948         if (this.isVisible()) {
22949             this.hide();
22950         } else {
22951             this.show();
22952         }
22953     },
22954     
22955     isVisible : function(){
22956         return !this.hidden;
22957     },
22958     
22959     show : function()
22960     {
22961         this.fireEvent("beforeshow", this);
22962         
22963         this.hidden = false;
22964         this.el.addClass('open');
22965         
22966         Roo.get(document).on("mouseup", this.onMouseUp, this);
22967         
22968         this.fireEvent("show", this);
22969         
22970         
22971     },
22972     
22973     hide : function()
22974     {
22975         this.fireEvent("beforehide", this);
22976         
22977         this.hidden = true;
22978         this.el.removeClass('open');
22979         
22980         Roo.get(document).un("mouseup", this.onMouseUp);
22981         
22982         this.fireEvent("hide", this);
22983     },
22984     
22985     onMouseUp : function()
22986     {
22987         this.hide();
22988     }
22989     
22990 });
22991
22992  
22993  /*
22994  * - LGPL
22995  *
22996  * menu item
22997  * 
22998  */
22999 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23000
23001 /**
23002  * @class Roo.bootstrap.menu.Item
23003  * @extends Roo.bootstrap.Component
23004  * Bootstrap MenuItem class
23005  * @cfg {Boolean} submenu (true | false) default false
23006  * @cfg {String} html text of the item
23007  * @cfg {String} href the link
23008  * @cfg {Boolean} disable (true | false) default false
23009  * @cfg {Boolean} preventDefault (true | false) default true
23010  * @cfg {String} icon Font awesome icon
23011  * @cfg {String} pos Submenu align to (left | right) default right 
23012  * 
23013  * 
23014  * @constructor
23015  * Create a new Item
23016  * @param {Object} config The config object
23017  */
23018
23019
23020 Roo.bootstrap.menu.Item = function(config){
23021     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23022     this.addEvents({
23023         /**
23024          * @event mouseover
23025          * Fires when the mouse is hovering over this menu
23026          * @param {Roo.bootstrap.menu.Item} this
23027          * @param {Roo.EventObject} e
23028          */
23029         mouseover : true,
23030         /**
23031          * @event mouseout
23032          * Fires when the mouse exits this menu
23033          * @param {Roo.bootstrap.menu.Item} this
23034          * @param {Roo.EventObject} e
23035          */
23036         mouseout : true,
23037         // raw events
23038         /**
23039          * @event click
23040          * The raw click event for the entire grid.
23041          * @param {Roo.EventObject} e
23042          */
23043         click : true
23044     });
23045 };
23046
23047 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23048     
23049     submenu : false,
23050     href : '',
23051     html : '',
23052     preventDefault: true,
23053     disable : false,
23054     icon : false,
23055     pos : 'right',
23056     
23057     getAutoCreate : function()
23058     {
23059         var text = [
23060             {
23061                 tag : 'span',
23062                 cls : 'roo-menu-item-text',
23063                 html : this.html
23064             }
23065         ];
23066         
23067         if(this.icon){
23068             text.unshift({
23069                 tag : 'i',
23070                 cls : 'fa ' + this.icon
23071             })
23072         }
23073         
23074         var cfg = {
23075             tag : 'li',
23076             cn : [
23077                 {
23078                     tag : 'a',
23079                     href : this.href || '#',
23080                     cn : text
23081                 }
23082             ]
23083         };
23084         
23085         if(this.disable){
23086             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23087         }
23088         
23089         if(this.submenu){
23090             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23091             
23092             if(this.pos == 'left'){
23093                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23094             }
23095         }
23096         
23097         return cfg;
23098     },
23099     
23100     initEvents : function() 
23101     {
23102         this.el.on('mouseover', this.onMouseOver, this);
23103         this.el.on('mouseout', this.onMouseOut, this);
23104         
23105         this.el.select('a', true).first().on('click', this.onClick, this);
23106         
23107     },
23108     
23109     onClick : function(e)
23110     {
23111         if(this.preventDefault){
23112             e.preventDefault();
23113         }
23114         
23115         this.fireEvent("click", this, e);
23116     },
23117     
23118     onMouseOver : function(e)
23119     {
23120         if(this.submenu && this.pos == 'left'){
23121             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23122         }
23123         
23124         this.fireEvent("mouseover", this, e);
23125     },
23126     
23127     onMouseOut : function(e)
23128     {
23129         this.fireEvent("mouseout", this, e);
23130     }
23131 });
23132
23133  
23134
23135  /*
23136  * - LGPL
23137  *
23138  * menu separator
23139  * 
23140  */
23141 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23142
23143 /**
23144  * @class Roo.bootstrap.menu.Separator
23145  * @extends Roo.bootstrap.Component
23146  * Bootstrap Separator class
23147  * 
23148  * @constructor
23149  * Create a new Separator
23150  * @param {Object} config The config object
23151  */
23152
23153
23154 Roo.bootstrap.menu.Separator = function(config){
23155     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23156 };
23157
23158 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23159     
23160     getAutoCreate : function(){
23161         var cfg = {
23162             tag : 'li',
23163             cls: 'divider'
23164         };
23165         
23166         return cfg;
23167     }
23168    
23169 });
23170
23171  
23172
23173  /*
23174  * - LGPL
23175  *
23176  * Tooltip
23177  * 
23178  */
23179
23180 /**
23181  * @class Roo.bootstrap.Tooltip
23182  * Bootstrap Tooltip class
23183  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23184  * to determine which dom element triggers the tooltip.
23185  * 
23186  * It needs to add support for additional attributes like tooltip-position
23187  * 
23188  * @constructor
23189  * Create a new Toolti
23190  * @param {Object} config The config object
23191  */
23192
23193 Roo.bootstrap.Tooltip = function(config){
23194     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23195 };
23196
23197 Roo.apply(Roo.bootstrap.Tooltip, {
23198     /**
23199      * @function init initialize tooltip monitoring.
23200      * @static
23201      */
23202     currentEl : false,
23203     currentTip : false,
23204     currentRegion : false,
23205     
23206     //  init : delay?
23207     
23208     init : function()
23209     {
23210         Roo.get(document).on('mouseover', this.enter ,this);
23211         Roo.get(document).on('mouseout', this.leave, this);
23212          
23213         
23214         this.currentTip = new Roo.bootstrap.Tooltip();
23215     },
23216     
23217     enter : function(ev)
23218     {
23219         var dom = ev.getTarget();
23220         
23221         //Roo.log(['enter',dom]);
23222         var el = Roo.fly(dom);
23223         if (this.currentEl) {
23224             //Roo.log(dom);
23225             //Roo.log(this.currentEl);
23226             //Roo.log(this.currentEl.contains(dom));
23227             if (this.currentEl == el) {
23228                 return;
23229             }
23230             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23231                 return;
23232             }
23233
23234         }
23235         
23236         if (this.currentTip.el) {
23237             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23238         }    
23239         //Roo.log(ev);
23240         var bindEl = el;
23241         
23242         // you can not look for children, as if el is the body.. then everythign is the child..
23243         if (!el.attr('tooltip')) { //
23244             if (!el.select("[tooltip]").elements.length) {
23245                 return;
23246             }
23247             // is the mouse over this child...?
23248             bindEl = el.select("[tooltip]").first();
23249             var xy = ev.getXY();
23250             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23251                 //Roo.log("not in region.");
23252                 return;
23253             }
23254             //Roo.log("child element over..");
23255             
23256         }
23257         this.currentEl = bindEl;
23258         this.currentTip.bind(bindEl);
23259         this.currentRegion = Roo.lib.Region.getRegion(dom);
23260         this.currentTip.enter();
23261         
23262     },
23263     leave : function(ev)
23264     {
23265         var dom = ev.getTarget();
23266         //Roo.log(['leave',dom]);
23267         if (!this.currentEl) {
23268             return;
23269         }
23270         
23271         
23272         if (dom != this.currentEl.dom) {
23273             return;
23274         }
23275         var xy = ev.getXY();
23276         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23277             return;
23278         }
23279         // only activate leave if mouse cursor is outside... bounding box..
23280         
23281         
23282         
23283         
23284         if (this.currentTip) {
23285             this.currentTip.leave();
23286         }
23287         //Roo.log('clear currentEl');
23288         this.currentEl = false;
23289         
23290         
23291     },
23292     alignment : {
23293         'left' : ['r-l', [-2,0], 'right'],
23294         'right' : ['l-r', [2,0], 'left'],
23295         'bottom' : ['t-b', [0,2], 'top'],
23296         'top' : [ 'b-t', [0,-2], 'bottom']
23297     }
23298     
23299 });
23300
23301
23302 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23303     
23304     
23305     bindEl : false,
23306     
23307     delay : null, // can be { show : 300 , hide: 500}
23308     
23309     timeout : null,
23310     
23311     hoverState : null, //???
23312     
23313     placement : 'bottom', 
23314     
23315     getAutoCreate : function(){
23316     
23317         var cfg = {
23318            cls : 'tooltip',
23319            role : 'tooltip',
23320            cn : [
23321                 {
23322                     cls : 'tooltip-arrow'
23323                 },
23324                 {
23325                     cls : 'tooltip-inner'
23326                 }
23327            ]
23328         };
23329         
23330         return cfg;
23331     },
23332     bind : function(el)
23333     {
23334         this.bindEl = el;
23335     },
23336       
23337     
23338     enter : function () {
23339        
23340         if (this.timeout != null) {
23341             clearTimeout(this.timeout);
23342         }
23343         
23344         this.hoverState = 'in';
23345          //Roo.log("enter - show");
23346         if (!this.delay || !this.delay.show) {
23347             this.show();
23348             return;
23349         }
23350         var _t = this;
23351         this.timeout = setTimeout(function () {
23352             if (_t.hoverState == 'in') {
23353                 _t.show();
23354             }
23355         }, this.delay.show);
23356     },
23357     leave : function()
23358     {
23359         clearTimeout(this.timeout);
23360     
23361         this.hoverState = 'out';
23362          if (!this.delay || !this.delay.hide) {
23363             this.hide();
23364             return;
23365         }
23366        
23367         var _t = this;
23368         this.timeout = setTimeout(function () {
23369             //Roo.log("leave - timeout");
23370             
23371             if (_t.hoverState == 'out') {
23372                 _t.hide();
23373                 Roo.bootstrap.Tooltip.currentEl = false;
23374             }
23375         }, delay);
23376     },
23377     
23378     show : function ()
23379     {
23380         if (!this.el) {
23381             this.render(document.body);
23382         }
23383         // set content.
23384         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23385         
23386         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23387         
23388         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23389         
23390         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23391         
23392         var placement = typeof this.placement == 'function' ?
23393             this.placement.call(this, this.el, on_el) :
23394             this.placement;
23395             
23396         var autoToken = /\s?auto?\s?/i;
23397         var autoPlace = autoToken.test(placement);
23398         if (autoPlace) {
23399             placement = placement.replace(autoToken, '') || 'top';
23400         }
23401         
23402         //this.el.detach()
23403         //this.el.setXY([0,0]);
23404         this.el.show();
23405         //this.el.dom.style.display='block';
23406         
23407         //this.el.appendTo(on_el);
23408         
23409         var p = this.getPosition();
23410         var box = this.el.getBox();
23411         
23412         if (autoPlace) {
23413             // fixme..
23414         }
23415         
23416         var align = Roo.bootstrap.Tooltip.alignment[placement];
23417         
23418         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23419         
23420         if(placement == 'top' || placement == 'bottom'){
23421             if(xy[0] < 0){
23422                 placement = 'right';
23423             }
23424             
23425             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23426                 placement = 'left';
23427             }
23428         }
23429         
23430         align = Roo.bootstrap.Tooltip.alignment[placement];
23431         
23432         this.el.alignTo(this.bindEl, align[0],align[1]);
23433         //var arrow = this.el.select('.arrow',true).first();
23434         //arrow.set(align[2], 
23435         
23436         this.el.addClass(placement);
23437         
23438         this.el.addClass('in fade');
23439         
23440         this.hoverState = null;
23441         
23442         if (this.el.hasClass('fade')) {
23443             // fade it?
23444         }
23445         
23446     },
23447     hide : function()
23448     {
23449          
23450         if (!this.el) {
23451             return;
23452         }
23453         //this.el.setXY([0,0]);
23454         this.el.removeClass('in');
23455         //this.el.hide();
23456         
23457     }
23458     
23459 });
23460  
23461
23462  /*
23463  * - LGPL
23464  *
23465  * Location Picker
23466  * 
23467  */
23468
23469 /**
23470  * @class Roo.bootstrap.LocationPicker
23471  * @extends Roo.bootstrap.Component
23472  * Bootstrap LocationPicker class
23473  * @cfg {Number} latitude Position when init default 0
23474  * @cfg {Number} longitude Position when init default 0
23475  * @cfg {Number} zoom default 15
23476  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23477  * @cfg {Boolean} mapTypeControl default false
23478  * @cfg {Boolean} disableDoubleClickZoom default false
23479  * @cfg {Boolean} scrollwheel default true
23480  * @cfg {Boolean} streetViewControl default false
23481  * @cfg {Number} radius default 0
23482  * @cfg {String} locationName
23483  * @cfg {Boolean} draggable default true
23484  * @cfg {Boolean} enableAutocomplete default false
23485  * @cfg {Boolean} enableReverseGeocode default true
23486  * @cfg {String} markerTitle
23487  * 
23488  * @constructor
23489  * Create a new LocationPicker
23490  * @param {Object} config The config object
23491  */
23492
23493
23494 Roo.bootstrap.LocationPicker = function(config){
23495     
23496     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23497     
23498     this.addEvents({
23499         /**
23500          * @event initial
23501          * Fires when the picker initialized.
23502          * @param {Roo.bootstrap.LocationPicker} this
23503          * @param {Google Location} location
23504          */
23505         initial : true,
23506         /**
23507          * @event positionchanged
23508          * Fires when the picker position changed.
23509          * @param {Roo.bootstrap.LocationPicker} this
23510          * @param {Google Location} location
23511          */
23512         positionchanged : true,
23513         /**
23514          * @event resize
23515          * Fires when the map resize.
23516          * @param {Roo.bootstrap.LocationPicker} this
23517          */
23518         resize : true,
23519         /**
23520          * @event show
23521          * Fires when the map show.
23522          * @param {Roo.bootstrap.LocationPicker} this
23523          */
23524         show : true,
23525         /**
23526          * @event hide
23527          * Fires when the map hide.
23528          * @param {Roo.bootstrap.LocationPicker} this
23529          */
23530         hide : true,
23531         /**
23532          * @event mapClick
23533          * Fires when click the map.
23534          * @param {Roo.bootstrap.LocationPicker} this
23535          * @param {Map event} e
23536          */
23537         mapClick : true,
23538         /**
23539          * @event mapRightClick
23540          * Fires when right click the map.
23541          * @param {Roo.bootstrap.LocationPicker} this
23542          * @param {Map event} e
23543          */
23544         mapRightClick : true,
23545         /**
23546          * @event markerClick
23547          * Fires when click the marker.
23548          * @param {Roo.bootstrap.LocationPicker} this
23549          * @param {Map event} e
23550          */
23551         markerClick : true,
23552         /**
23553          * @event markerRightClick
23554          * Fires when right click the marker.
23555          * @param {Roo.bootstrap.LocationPicker} this
23556          * @param {Map event} e
23557          */
23558         markerRightClick : true,
23559         /**
23560          * @event OverlayViewDraw
23561          * Fires when OverlayView Draw
23562          * @param {Roo.bootstrap.LocationPicker} this
23563          */
23564         OverlayViewDraw : true,
23565         /**
23566          * @event OverlayViewOnAdd
23567          * Fires when OverlayView Draw
23568          * @param {Roo.bootstrap.LocationPicker} this
23569          */
23570         OverlayViewOnAdd : true,
23571         /**
23572          * @event OverlayViewOnRemove
23573          * Fires when OverlayView Draw
23574          * @param {Roo.bootstrap.LocationPicker} this
23575          */
23576         OverlayViewOnRemove : true,
23577         /**
23578          * @event OverlayViewShow
23579          * Fires when OverlayView Draw
23580          * @param {Roo.bootstrap.LocationPicker} this
23581          * @param {Pixel} cpx
23582          */
23583         OverlayViewShow : true,
23584         /**
23585          * @event OverlayViewHide
23586          * Fires when OverlayView Draw
23587          * @param {Roo.bootstrap.LocationPicker} this
23588          */
23589         OverlayViewHide : true
23590     });
23591         
23592 };
23593
23594 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23595     
23596     gMapContext: false,
23597     
23598     latitude: 0,
23599     longitude: 0,
23600     zoom: 15,
23601     mapTypeId: false,
23602     mapTypeControl: false,
23603     disableDoubleClickZoom: false,
23604     scrollwheel: true,
23605     streetViewControl: false,
23606     radius: 0,
23607     locationName: '',
23608     draggable: true,
23609     enableAutocomplete: false,
23610     enableReverseGeocode: true,
23611     markerTitle: '',
23612     
23613     getAutoCreate: function()
23614     {
23615
23616         var cfg = {
23617             tag: 'div',
23618             cls: 'roo-location-picker'
23619         };
23620         
23621         return cfg
23622     },
23623     
23624     initEvents: function(ct, position)
23625     {       
23626         if(!this.el.getWidth() || this.isApplied()){
23627             return;
23628         }
23629         
23630         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23631         
23632         this.initial();
23633     },
23634     
23635     initial: function()
23636     {
23637         if(!this.mapTypeId){
23638             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23639         }
23640         
23641         this.gMapContext = this.GMapContext();
23642         
23643         this.initOverlayView();
23644         
23645         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23646         
23647         var _this = this;
23648                 
23649         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23650             _this.setPosition(_this.gMapContext.marker.position);
23651         });
23652         
23653         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23654             _this.fireEvent('mapClick', this, event);
23655             
23656         });
23657
23658         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23659             _this.fireEvent('mapRightClick', this, event);
23660             
23661         });
23662         
23663         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23664             _this.fireEvent('markerClick', this, event);
23665             
23666         });
23667
23668         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23669             _this.fireEvent('markerRightClick', this, event);
23670             
23671         });
23672         
23673         this.setPosition(this.gMapContext.location);
23674         
23675         this.fireEvent('initial', this, this.gMapContext.location);
23676     },
23677     
23678     initOverlayView: function()
23679     {
23680         var _this = this;
23681         
23682         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23683             
23684             draw: function()
23685             {
23686                 _this.fireEvent('OverlayViewDraw', _this);
23687             },
23688             
23689             onAdd: function()
23690             {
23691                 _this.fireEvent('OverlayViewOnAdd', _this);
23692             },
23693             
23694             onRemove: function()
23695             {
23696                 _this.fireEvent('OverlayViewOnRemove', _this);
23697             },
23698             
23699             show: function(cpx)
23700             {
23701                 _this.fireEvent('OverlayViewShow', _this, cpx);
23702             },
23703             
23704             hide: function()
23705             {
23706                 _this.fireEvent('OverlayViewHide', _this);
23707             }
23708             
23709         });
23710     },
23711     
23712     fromLatLngToContainerPixel: function(event)
23713     {
23714         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23715     },
23716     
23717     isApplied: function() 
23718     {
23719         return this.getGmapContext() == false ? false : true;
23720     },
23721     
23722     getGmapContext: function() 
23723     {
23724         return this.gMapContext
23725     },
23726     
23727     GMapContext: function() 
23728     {
23729         var position = new google.maps.LatLng(this.latitude, this.longitude);
23730         
23731         var _map = new google.maps.Map(this.el.dom, {
23732             center: position,
23733             zoom: this.zoom,
23734             mapTypeId: this.mapTypeId,
23735             mapTypeControl: this.mapTypeControl,
23736             disableDoubleClickZoom: this.disableDoubleClickZoom,
23737             scrollwheel: this.scrollwheel,
23738             streetViewControl: this.streetViewControl,
23739             locationName: this.locationName,
23740             draggable: this.draggable,
23741             enableAutocomplete: this.enableAutocomplete,
23742             enableReverseGeocode: this.enableReverseGeocode
23743         });
23744         
23745         var _marker = new google.maps.Marker({
23746             position: position,
23747             map: _map,
23748             title: this.markerTitle,
23749             draggable: this.draggable
23750         });
23751         
23752         return {
23753             map: _map,
23754             marker: _marker,
23755             circle: null,
23756             location: position,
23757             radius: this.radius,
23758             locationName: this.locationName,
23759             addressComponents: {
23760                 formatted_address: null,
23761                 addressLine1: null,
23762                 addressLine2: null,
23763                 streetName: null,
23764                 streetNumber: null,
23765                 city: null,
23766                 district: null,
23767                 state: null,
23768                 stateOrProvince: null
23769             },
23770             settings: this,
23771             domContainer: this.el.dom,
23772             geodecoder: new google.maps.Geocoder()
23773         };
23774     },
23775     
23776     drawCircle: function(center, radius, options) 
23777     {
23778         if (this.gMapContext.circle != null) {
23779             this.gMapContext.circle.setMap(null);
23780         }
23781         if (radius > 0) {
23782             radius *= 1;
23783             options = Roo.apply({}, options, {
23784                 strokeColor: "#0000FF",
23785                 strokeOpacity: .35,
23786                 strokeWeight: 2,
23787                 fillColor: "#0000FF",
23788                 fillOpacity: .2
23789             });
23790             
23791             options.map = this.gMapContext.map;
23792             options.radius = radius;
23793             options.center = center;
23794             this.gMapContext.circle = new google.maps.Circle(options);
23795             return this.gMapContext.circle;
23796         }
23797         
23798         return null;
23799     },
23800     
23801     setPosition: function(location) 
23802     {
23803         this.gMapContext.location = location;
23804         this.gMapContext.marker.setPosition(location);
23805         this.gMapContext.map.panTo(location);
23806         this.drawCircle(location, this.gMapContext.radius, {});
23807         
23808         var _this = this;
23809         
23810         if (this.gMapContext.settings.enableReverseGeocode) {
23811             this.gMapContext.geodecoder.geocode({
23812                 latLng: this.gMapContext.location
23813             }, function(results, status) {
23814                 
23815                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23816                     _this.gMapContext.locationName = results[0].formatted_address;
23817                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23818                     
23819                     _this.fireEvent('positionchanged', this, location);
23820                 }
23821             });
23822             
23823             return;
23824         }
23825         
23826         this.fireEvent('positionchanged', this, location);
23827     },
23828     
23829     resize: function()
23830     {
23831         google.maps.event.trigger(this.gMapContext.map, "resize");
23832         
23833         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23834         
23835         this.fireEvent('resize', this);
23836     },
23837     
23838     setPositionByLatLng: function(latitude, longitude)
23839     {
23840         this.setPosition(new google.maps.LatLng(latitude, longitude));
23841     },
23842     
23843     getCurrentPosition: function() 
23844     {
23845         return {
23846             latitude: this.gMapContext.location.lat(),
23847             longitude: this.gMapContext.location.lng()
23848         };
23849     },
23850     
23851     getAddressName: function() 
23852     {
23853         return this.gMapContext.locationName;
23854     },
23855     
23856     getAddressComponents: function() 
23857     {
23858         return this.gMapContext.addressComponents;
23859     },
23860     
23861     address_component_from_google_geocode: function(address_components) 
23862     {
23863         var result = {};
23864         
23865         for (var i = 0; i < address_components.length; i++) {
23866             var component = address_components[i];
23867             if (component.types.indexOf("postal_code") >= 0) {
23868                 result.postalCode = component.short_name;
23869             } else if (component.types.indexOf("street_number") >= 0) {
23870                 result.streetNumber = component.short_name;
23871             } else if (component.types.indexOf("route") >= 0) {
23872                 result.streetName = component.short_name;
23873             } else if (component.types.indexOf("neighborhood") >= 0) {
23874                 result.city = component.short_name;
23875             } else if (component.types.indexOf("locality") >= 0) {
23876                 result.city = component.short_name;
23877             } else if (component.types.indexOf("sublocality") >= 0) {
23878                 result.district = component.short_name;
23879             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23880                 result.stateOrProvince = component.short_name;
23881             } else if (component.types.indexOf("country") >= 0) {
23882                 result.country = component.short_name;
23883             }
23884         }
23885         
23886         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23887         result.addressLine2 = "";
23888         return result;
23889     },
23890     
23891     setZoomLevel: function(zoom)
23892     {
23893         this.gMapContext.map.setZoom(zoom);
23894     },
23895     
23896     show: function()
23897     {
23898         if(!this.el){
23899             return;
23900         }
23901         
23902         this.el.show();
23903         
23904         this.resize();
23905         
23906         this.fireEvent('show', this);
23907     },
23908     
23909     hide: function()
23910     {
23911         if(!this.el){
23912             return;
23913         }
23914         
23915         this.el.hide();
23916         
23917         this.fireEvent('hide', this);
23918     }
23919     
23920 });
23921
23922 Roo.apply(Roo.bootstrap.LocationPicker, {
23923     
23924     OverlayView : function(map, options)
23925     {
23926         options = options || {};
23927         
23928         this.setMap(map);
23929     }
23930     
23931     
23932 });/*
23933  * - LGPL
23934  *
23935  * Alert
23936  * 
23937  */
23938
23939 /**
23940  * @class Roo.bootstrap.Alert
23941  * @extends Roo.bootstrap.Component
23942  * Bootstrap Alert class
23943  * @cfg {String} title The title of alert
23944  * @cfg {String} html The content of alert
23945  * @cfg {String} weight (  success | info | warning | danger )
23946  * @cfg {String} faicon font-awesomeicon
23947  * 
23948  * @constructor
23949  * Create a new alert
23950  * @param {Object} config The config object
23951  */
23952
23953
23954 Roo.bootstrap.Alert = function(config){
23955     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
23956     
23957 };
23958
23959 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
23960     
23961     title: '',
23962     html: '',
23963     weight: false,
23964     faicon: false,
23965     
23966     getAutoCreate : function()
23967     {
23968         
23969         var cfg = {
23970             tag : 'div',
23971             cls : 'alert',
23972             cn : [
23973                 {
23974                     tag : 'i',
23975                     cls : 'roo-alert-icon'
23976                     
23977                 },
23978                 {
23979                     tag : 'b',
23980                     cls : 'roo-alert-title',
23981                     html : this.title
23982                 },
23983                 {
23984                     tag : 'span',
23985                     cls : 'roo-alert-text',
23986                     html : this.html
23987                 }
23988             ]
23989         };
23990         
23991         if(this.faicon){
23992             cfg.cn[0].cls += ' fa ' + this.faicon;
23993         }
23994         
23995         if(this.weight){
23996             cfg.cls += ' alert-' + this.weight;
23997         }
23998         
23999         return cfg;
24000     },
24001     
24002     initEvents: function() 
24003     {
24004         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24005     },
24006     
24007     setTitle : function(str)
24008     {
24009         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24010     },
24011     
24012     setText : function(str)
24013     {
24014         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24015     },
24016     
24017     setWeight : function(weight)
24018     {
24019         if(this.weight){
24020             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24021         }
24022         
24023         this.weight = weight;
24024         
24025         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24026     },
24027     
24028     setIcon : function(icon)
24029     {
24030         if(this.faicon){
24031             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24032         }
24033         
24034         this.faicon = icon
24035         
24036         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24037     },
24038     
24039     hide: function() 
24040     {
24041         this.el.hide();   
24042     },
24043     
24044     show: function() 
24045     {  
24046         this.el.show();   
24047     }
24048     
24049 });
24050
24051  
24052 /*
24053 * Licence: LGPL
24054 */
24055
24056 /**
24057  * @class Roo.bootstrap.UploadCropbox
24058  * @extends Roo.bootstrap.Component
24059  * Bootstrap UploadCropbox class
24060  * @cfg {String} emptyText show when image has been loaded
24061  * @cfg {String} rotateNotify show when image too small to rotate
24062  * @cfg {Number} errorTimeout default 3000
24063  * @cfg {Number} minWidth default 300
24064  * @cfg {Number} minHeight default 300
24065  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24066  * @cfg {Boolean} isDocument (true|false) default false
24067  * @cfg {String} url action url
24068  * @cfg {String} paramName default 'imageUpload'
24069  * @cfg {String} method default POST
24070  * 
24071  * @constructor
24072  * Create a new UploadCropbox
24073  * @param {Object} config The config object
24074  */
24075
24076 Roo.bootstrap.UploadCropbox = function(config){
24077     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24078     
24079     this.addEvents({
24080         /**
24081          * @event beforeselectfile
24082          * Fire before select file
24083          * @param {Roo.bootstrap.UploadCropbox} this
24084          */
24085         "beforeselectfile" : true,
24086         /**
24087          * @event initial
24088          * Fire after initEvent
24089          * @param {Roo.bootstrap.UploadCropbox} this
24090          */
24091         "initial" : true,
24092         /**
24093          * @event crop
24094          * Fire after initEvent
24095          * @param {Roo.bootstrap.UploadCropbox} this
24096          * @param {String} data
24097          */
24098         "crop" : true,
24099         /**
24100          * @event prepare
24101          * Fire when preparing the file data
24102          * @param {Roo.bootstrap.UploadCropbox} this
24103          * @param {Object} file
24104          */
24105         "prepare" : true,
24106         /**
24107          * @event exception
24108          * Fire when get exception
24109          * @param {Roo.bootstrap.UploadCropbox} this
24110          * @param {XMLHttpRequest} xhr
24111          */
24112         "exception" : true,
24113         /**
24114          * @event beforeloadcanvas
24115          * Fire before load the canvas
24116          * @param {Roo.bootstrap.UploadCropbox} this
24117          * @param {String} src
24118          */
24119         "beforeloadcanvas" : true,
24120         /**
24121          * @event trash
24122          * Fire when trash image
24123          * @param {Roo.bootstrap.UploadCropbox} this
24124          */
24125         "trash" : true,
24126         /**
24127          * @event download
24128          * Fire when download the image
24129          * @param {Roo.bootstrap.UploadCropbox} this
24130          */
24131         "download" : true,
24132         /**
24133          * @event footerbuttonclick
24134          * Fire when footerbuttonclick
24135          * @param {Roo.bootstrap.UploadCropbox} this
24136          * @param {String} type
24137          */
24138         "footerbuttonclick" : true,
24139         /**
24140          * @event resize
24141          * Fire when resize
24142          * @param {Roo.bootstrap.UploadCropbox} this
24143          */
24144         "resize" : true,
24145         /**
24146          * @event rotate
24147          * Fire when rotate the image
24148          * @param {Roo.bootstrap.UploadCropbox} this
24149          * @param {String} pos
24150          */
24151         "rotate" : true,
24152         /**
24153          * @event inspect
24154          * Fire when inspect the file
24155          * @param {Roo.bootstrap.UploadCropbox} this
24156          * @param {Object} file
24157          */
24158         "inspect" : true,
24159         /**
24160          * @event upload
24161          * Fire when xhr upload the file
24162          * @param {Roo.bootstrap.UploadCropbox} this
24163          * @param {Object} data
24164          */
24165         "upload" : true,
24166         /**
24167          * @event arrange
24168          * Fire when arrange the file data
24169          * @param {Roo.bootstrap.UploadCropbox} this
24170          * @param {Object} formData
24171          */
24172         "arrange" : true
24173     });
24174     
24175     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24176 };
24177
24178 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24179     
24180     emptyText : 'Click to upload image',
24181     rotateNotify : 'Image is too small to rotate',
24182     errorTimeout : 3000,
24183     scale : 0,
24184     baseScale : 1,
24185     rotate : 0,
24186     dragable : false,
24187     pinching : false,
24188     mouseX : 0,
24189     mouseY : 0,
24190     cropData : false,
24191     minWidth : 300,
24192     minHeight : 300,
24193     file : false,
24194     exif : {},
24195     baseRotate : 1,
24196     cropType : 'image/jpeg',
24197     buttons : false,
24198     canvasLoaded : false,
24199     isDocument : false,
24200     method : 'POST',
24201     paramName : 'imageUpload',
24202     
24203     getAutoCreate : function()
24204     {
24205         var cfg = {
24206             tag : 'div',
24207             cls : 'roo-upload-cropbox',
24208             cn : [
24209                 {
24210                     tag : 'input',
24211                     cls : 'roo-upload-cropbox-selector',
24212                     type : 'file'
24213                 },
24214                 {
24215                     tag : 'div',
24216                     cls : 'roo-upload-cropbox-body',
24217                     style : 'cursor:pointer',
24218                     cn : [
24219                         {
24220                             tag : 'div',
24221                             cls : 'roo-upload-cropbox-preview'
24222                         },
24223                         {
24224                             tag : 'div',
24225                             cls : 'roo-upload-cropbox-thumb'
24226                         },
24227                         {
24228                             tag : 'div',
24229                             cls : 'roo-upload-cropbox-empty-notify',
24230                             html : this.emptyText
24231                         },
24232                         {
24233                             tag : 'div',
24234                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24235                             html : this.rotateNotify
24236                         }
24237                     ]
24238                 },
24239                 {
24240                     tag : 'div',
24241                     cls : 'roo-upload-cropbox-footer',
24242                     cn : {
24243                         tag : 'div',
24244                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24245                         cn : []
24246                     }
24247                 }
24248             ]
24249         };
24250         
24251         return cfg;
24252     },
24253     
24254     onRender : function(ct, position)
24255     {
24256         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24257         
24258         if (this.buttons.length) {
24259             
24260             Roo.each(this.buttons, function(bb) {
24261                 
24262                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24263                 
24264                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24265                 
24266             }, this);
24267         }
24268     },
24269     
24270     initEvents : function()
24271     {
24272         this.urlAPI = (window.createObjectURL && window) || 
24273                                 (window.URL && URL.revokeObjectURL && URL) || 
24274                                 (window.webkitURL && webkitURL);
24275                         
24276         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24277         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24278         
24279         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24280         this.selectorEl.hide();
24281         
24282         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24283         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24284         
24285         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24286         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24287         this.thumbEl.hide();
24288         
24289         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24290         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24291         
24292         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24293         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24294         this.errorEl.hide();
24295         
24296         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24297         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24298         this.footerEl.hide();
24299         
24300         this.setThumbBoxSize();
24301         
24302         this.bind();
24303         
24304         this.resize();
24305         
24306         this.fireEvent('initial', this);
24307     },
24308
24309     bind : function()
24310     {
24311         var _this = this;
24312         
24313         window.addEventListener("resize", function() { _this.resize(); } );
24314         
24315         this.bodyEl.on('click', this.beforeSelectFile, this);
24316         
24317         if(Roo.isTouch){
24318             this.bodyEl.on('touchstart', this.onTouchStart, this);
24319             this.bodyEl.on('touchmove', this.onTouchMove, this);
24320             this.bodyEl.on('touchend', this.onTouchEnd, this);
24321         }
24322         
24323         if(!Roo.isTouch){
24324             this.bodyEl.on('mousedown', this.onMouseDown, this);
24325             this.bodyEl.on('mousemove', this.onMouseMove, this);
24326             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24327             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24328             Roo.get(document).on('mouseup', this.onMouseUp, this);
24329         }
24330         
24331         this.selectorEl.on('change', this.onFileSelected, this);
24332     },
24333     
24334     reset : function()
24335     {    
24336         this.scale = 0;
24337         this.baseScale = 1;
24338         this.rotate = 0;
24339         this.baseRotate = 1;
24340         this.dragable = false;
24341         this.pinching = false;
24342         this.mouseX = 0;
24343         this.mouseY = 0;
24344         this.cropData = false;
24345         this.notifyEl.dom.innerHTML = this.emptyText;
24346         
24347         this.selectorEl.dom.value = '';
24348         
24349     },
24350     
24351     resize : function()
24352     {
24353         if(this.fireEvent('resize', this) != false){
24354             this.setThumbBoxPosition();
24355             this.setCanvasPosition();
24356         }
24357     },
24358     
24359     onFooterButtonClick : function(e, el, o, type)
24360     {
24361         switch (type) {
24362             case 'rotate-left' :
24363                 this.onRotateLeft(e);
24364                 break;
24365             case 'rotate-right' :
24366                 this.onRotateRight(e);
24367                 break;
24368             case 'picture' :
24369                 this.beforeSelectFile(e);
24370                 break;
24371             case 'trash' :
24372                 this.trash(e);
24373                 break;
24374             case 'crop' :
24375                 this.crop(e);
24376                 break;
24377             case 'download' :
24378                 this.download(e);
24379                 break;
24380             default :
24381                 break;
24382         }
24383         
24384         this.fireEvent('footerbuttonclick', this, type);
24385     },
24386     
24387     beforeSelectFile : function(e)
24388     {
24389         e.preventDefault();
24390         
24391         if(this.fireEvent('beforeselectfile', this) != false){
24392             this.selectorEl.dom.click();
24393         }
24394     },
24395     
24396     onFileSelected : function(e)
24397     {
24398         e.preventDefault();
24399         
24400         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24401             return;
24402         }
24403         
24404         var file = this.selectorEl.dom.files[0];
24405         
24406         if(this.fireEvent('inspect', this, file) != false){
24407             this.prepare(file);
24408         }
24409         
24410     },
24411     
24412     trash : function(e)
24413     {
24414         this.fireEvent('trash', this);
24415     },
24416     
24417     download : function(e)
24418     {
24419         this.fireEvent('download', this);
24420     },
24421     
24422     loadCanvas : function(src)
24423     {   
24424         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24425             
24426             this.reset();
24427             
24428             this.imageEl = document.createElement('img');
24429             
24430             var _this = this;
24431             
24432             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24433             
24434             this.imageEl.src = src;
24435         }
24436     },
24437     
24438     onLoadCanvas : function()
24439     {   
24440         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24441         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24442         
24443         this.bodyEl.un('click', this.beforeSelectFile, this);
24444         
24445         this.notifyEl.hide();
24446         this.thumbEl.show();
24447         this.footerEl.show();
24448         
24449         this.baseRotateLevel();
24450         
24451         if(this.isDocument){
24452             this.setThumbBoxSize();
24453         }
24454         
24455         this.setThumbBoxPosition();
24456         
24457         this.baseScaleLevel();
24458         
24459         this.draw();
24460         
24461         this.resize();
24462         
24463         this.canvasLoaded = true;
24464         
24465     },
24466     
24467     setCanvasPosition : function()
24468     {   
24469         if(!this.canvasEl){
24470             return;
24471         }
24472         
24473         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24474         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24475         
24476         this.previewEl.setLeft(pw);
24477         this.previewEl.setTop(ph);
24478         
24479     },
24480     
24481     onMouseDown : function(e)
24482     {   
24483         e.stopEvent();
24484         
24485         this.dragable = true;
24486         this.pinching = false;
24487         
24488         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24489             this.dragable = false;
24490             return;
24491         }
24492         
24493         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24494         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24495         
24496     },
24497     
24498     onMouseMove : function(e)
24499     {   
24500         e.stopEvent();
24501         
24502         if(!this.canvasLoaded){
24503             return;
24504         }
24505         
24506         if (!this.dragable){
24507             return;
24508         }
24509         
24510         var minX = Math.ceil(this.thumbEl.getLeft(true));
24511         var minY = Math.ceil(this.thumbEl.getTop(true));
24512         
24513         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24514         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24515         
24516         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24517         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24518         
24519         x = x - this.mouseX;
24520         y = y - this.mouseY;
24521         
24522         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24523         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24524         
24525         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24526         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24527         
24528         this.previewEl.setLeft(bgX);
24529         this.previewEl.setTop(bgY);
24530         
24531         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24532         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24533     },
24534     
24535     onMouseUp : function(e)
24536     {   
24537         e.stopEvent();
24538         
24539         this.dragable = false;
24540     },
24541     
24542     onMouseWheel : function(e)
24543     {   
24544         e.stopEvent();
24545         
24546         this.startScale = this.scale;
24547         
24548         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24549         
24550         if(!this.zoomable()){
24551             this.scale = this.startScale;
24552             return;
24553         }
24554         
24555         this.draw();
24556         
24557         return;
24558     },
24559     
24560     zoomable : function()
24561     {
24562         var minScale = this.thumbEl.getWidth() / this.minWidth;
24563         
24564         if(this.minWidth < this.minHeight){
24565             minScale = this.thumbEl.getHeight() / this.minHeight;
24566         }
24567         
24568         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24569         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24570         
24571         if(
24572                 this.isDocument &&
24573                 (this.rotate == 0 || this.rotate == 180) && 
24574                 (
24575                     width > this.imageEl.OriginWidth || 
24576                     height > this.imageEl.OriginHeight ||
24577                     (width < this.minWidth && height < this.minHeight)
24578                 )
24579         ){
24580             return false;
24581         }
24582         
24583         if(
24584                 this.isDocument &&
24585                 (this.rotate == 90 || this.rotate == 270) && 
24586                 (
24587                     width > this.imageEl.OriginWidth || 
24588                     height > this.imageEl.OriginHeight ||
24589                     (width < this.minHeight && height < this.minWidth)
24590                 )
24591         ){
24592             return false;
24593         }
24594         
24595         if(
24596                 !this.isDocument &&
24597                 (this.rotate == 0 || this.rotate == 180) && 
24598                 (
24599                     width < this.minWidth || 
24600                     width > this.imageEl.OriginWidth || 
24601                     height < this.minHeight || 
24602                     height > this.imageEl.OriginHeight
24603                 )
24604         ){
24605             return false;
24606         }
24607         
24608         if(
24609                 !this.isDocument &&
24610                 (this.rotate == 90 || this.rotate == 270) && 
24611                 (
24612                     width < this.minHeight || 
24613                     width > this.imageEl.OriginWidth || 
24614                     height < this.minWidth || 
24615                     height > this.imageEl.OriginHeight
24616                 )
24617         ){
24618             return false;
24619         }
24620         
24621         return true;
24622         
24623     },
24624     
24625     onRotateLeft : function(e)
24626     {   
24627         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24628             
24629             var minScale = this.thumbEl.getWidth() / this.minWidth;
24630             
24631             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24632             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24633             
24634             this.startScale = this.scale;
24635             
24636             while (this.getScaleLevel() < minScale){
24637             
24638                 this.scale = this.scale + 1;
24639                 
24640                 if(!this.zoomable()){
24641                     break;
24642                 }
24643                 
24644                 if(
24645                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24646                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24647                 ){
24648                     continue;
24649                 }
24650                 
24651                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24652
24653                 this.draw();
24654                 
24655                 return;
24656             }
24657             
24658             this.scale = this.startScale;
24659             
24660             this.onRotateFail();
24661             
24662             return false;
24663         }
24664         
24665         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24666
24667         if(this.isDocument){
24668             this.setThumbBoxSize();
24669             this.setThumbBoxPosition();
24670             this.setCanvasPosition();
24671         }
24672         
24673         this.draw();
24674         
24675         this.fireEvent('rotate', this, 'left');
24676         
24677     },
24678     
24679     onRotateRight : function(e)
24680     {
24681         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24682             
24683             var minScale = this.thumbEl.getWidth() / this.minWidth;
24684         
24685             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24686             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24687             
24688             this.startScale = this.scale;
24689             
24690             while (this.getScaleLevel() < minScale){
24691             
24692                 this.scale = this.scale + 1;
24693                 
24694                 if(!this.zoomable()){
24695                     break;
24696                 }
24697                 
24698                 if(
24699                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24700                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24701                 ){
24702                     continue;
24703                 }
24704                 
24705                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24706
24707                 this.draw();
24708                 
24709                 return;
24710             }
24711             
24712             this.scale = this.startScale;
24713             
24714             this.onRotateFail();
24715             
24716             return false;
24717         }
24718         
24719         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24720
24721         if(this.isDocument){
24722             this.setThumbBoxSize();
24723             this.setThumbBoxPosition();
24724             this.setCanvasPosition();
24725         }
24726         
24727         this.draw();
24728         
24729         this.fireEvent('rotate', this, 'right');
24730     },
24731     
24732     onRotateFail : function()
24733     {
24734         this.errorEl.show(true);
24735         
24736         var _this = this;
24737         
24738         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24739     },
24740     
24741     draw : function()
24742     {
24743         this.previewEl.dom.innerHTML = '';
24744         
24745         var canvasEl = document.createElement("canvas");
24746         
24747         var contextEl = canvasEl.getContext("2d");
24748         
24749         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24750         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24751         var center = this.imageEl.OriginWidth / 2;
24752         
24753         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24754             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24755             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24756             center = this.imageEl.OriginHeight / 2;
24757         }
24758         
24759         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24760         
24761         contextEl.translate(center, center);
24762         contextEl.rotate(this.rotate * Math.PI / 180);
24763
24764         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24765         
24766         this.canvasEl = document.createElement("canvas");
24767         
24768         this.contextEl = this.canvasEl.getContext("2d");
24769         
24770         switch (this.rotate) {
24771             case 0 :
24772                 
24773                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24774                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24775                 
24776                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24777                 
24778                 break;
24779             case 90 : 
24780                 
24781                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24782                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24783                 
24784                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24785                     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);
24786                     break;
24787                 }
24788                 
24789                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24790                 
24791                 break;
24792             case 180 :
24793                 
24794                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24795                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24796                 
24797                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24798                     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);
24799                     break;
24800                 }
24801                 
24802                 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);
24803                 
24804                 break;
24805             case 270 :
24806                 
24807                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24808                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24809         
24810                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24811                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24812                     break;
24813                 }
24814                 
24815                 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);
24816                 
24817                 break;
24818             default : 
24819                 break;
24820         }
24821         
24822         this.previewEl.appendChild(this.canvasEl);
24823         
24824         this.setCanvasPosition();
24825     },
24826     
24827     crop : function()
24828     {
24829         if(!this.canvasLoaded){
24830             return;
24831         }
24832         
24833         var imageCanvas = document.createElement("canvas");
24834         
24835         var imageContext = imageCanvas.getContext("2d");
24836         
24837         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24838         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24839         
24840         var center = imageCanvas.width / 2;
24841         
24842         imageContext.translate(center, center);
24843         
24844         imageContext.rotate(this.rotate * Math.PI / 180);
24845         
24846         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24847         
24848         var canvas = document.createElement("canvas");
24849         
24850         var context = canvas.getContext("2d");
24851                 
24852         canvas.width = this.minWidth;
24853         canvas.height = this.minHeight;
24854
24855         switch (this.rotate) {
24856             case 0 :
24857                 
24858                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
24859                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
24860                 
24861                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24862                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24863                 
24864                 var targetWidth = this.minWidth - 2 * x;
24865                 var targetHeight = this.minHeight - 2 * y;
24866                 
24867                 var scale = 1;
24868                 
24869                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24870                     scale = targetWidth / width;
24871                 }
24872                 
24873                 if(x > 0 && y == 0){
24874                     scale = targetHeight / height;
24875                 }
24876                 
24877                 if(x > 0 && y > 0){
24878                     scale = targetWidth / width;
24879                     
24880                     if(width < height){
24881                         scale = targetHeight / height;
24882                     }
24883                 }
24884                 
24885                 context.scale(scale, scale);
24886                 
24887                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24888                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24889
24890                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24891                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24892
24893                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24894                 
24895                 break;
24896             case 90 : 
24897                 
24898                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
24899                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
24900                 
24901                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24902                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24903                 
24904                 var targetWidth = this.minWidth - 2 * x;
24905                 var targetHeight = this.minHeight - 2 * y;
24906                 
24907                 var scale = 1;
24908                 
24909                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24910                     scale = targetWidth / width;
24911                 }
24912                 
24913                 if(x > 0 && y == 0){
24914                     scale = targetHeight / height;
24915                 }
24916                 
24917                 if(x > 0 && y > 0){
24918                     scale = targetWidth / width;
24919                     
24920                     if(width < height){
24921                         scale = targetHeight / height;
24922                     }
24923                 }
24924                 
24925                 context.scale(scale, scale);
24926                 
24927                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24928                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24929
24930                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24931                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24932                 
24933                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
24934                 
24935                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24936                 
24937                 break;
24938             case 180 :
24939                 
24940                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
24941                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
24942                 
24943                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24944                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24945                 
24946                 var targetWidth = this.minWidth - 2 * x;
24947                 var targetHeight = this.minHeight - 2 * y;
24948                 
24949                 var scale = 1;
24950                 
24951                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24952                     scale = targetWidth / width;
24953                 }
24954                 
24955                 if(x > 0 && y == 0){
24956                     scale = targetHeight / height;
24957                 }
24958                 
24959                 if(x > 0 && y > 0){
24960                     scale = targetWidth / width;
24961                     
24962                     if(width < height){
24963                         scale = targetHeight / height;
24964                     }
24965                 }
24966                 
24967                 context.scale(scale, scale);
24968                 
24969                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24970                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24971
24972                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24973                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24974
24975                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
24976                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
24977                 
24978                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24979                 
24980                 break;
24981             case 270 :
24982                 
24983                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
24984                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
24985                 
24986                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24987                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24988                 
24989                 var targetWidth = this.minWidth - 2 * x;
24990                 var targetHeight = this.minHeight - 2 * y;
24991                 
24992                 var scale = 1;
24993                 
24994                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24995                     scale = targetWidth / width;
24996                 }
24997                 
24998                 if(x > 0 && y == 0){
24999                     scale = targetHeight / height;
25000                 }
25001                 
25002                 if(x > 0 && y > 0){
25003                     scale = targetWidth / width;
25004                     
25005                     if(width < height){
25006                         scale = targetHeight / height;
25007                     }
25008                 }
25009                 
25010                 context.scale(scale, scale);
25011                 
25012                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25013                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25014
25015                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25016                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25017                 
25018                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25019                 
25020                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25021                 
25022                 break;
25023             default : 
25024                 break;
25025         }
25026         
25027         this.cropData = canvas.toDataURL(this.cropType);
25028         
25029         if(this.fireEvent('crop', this, this.cropData) !== false){
25030             this.process(this.file, this.cropData);
25031         }
25032         
25033         return;
25034         
25035     },
25036     
25037     setThumbBoxSize : function()
25038     {
25039         var width, height;
25040         
25041         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25042             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25043             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25044             
25045             this.minWidth = width;
25046             this.minHeight = height;
25047             
25048             if(this.rotate == 90 || this.rotate == 270){
25049                 this.minWidth = height;
25050                 this.minHeight = width;
25051             }
25052         }
25053         
25054         height = 300;
25055         width = Math.ceil(this.minWidth * height / this.minHeight);
25056         
25057         if(this.minWidth > this.minHeight){
25058             width = 300;
25059             height = Math.ceil(this.minHeight * width / this.minWidth);
25060         }
25061         
25062         this.thumbEl.setStyle({
25063             width : width + 'px',
25064             height : height + 'px'
25065         });
25066
25067         return;
25068             
25069     },
25070     
25071     setThumbBoxPosition : function()
25072     {
25073         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25074         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25075         
25076         this.thumbEl.setLeft(x);
25077         this.thumbEl.setTop(y);
25078         
25079     },
25080     
25081     baseRotateLevel : function()
25082     {
25083         this.baseRotate = 1;
25084         
25085         if(
25086                 typeof(this.exif) != 'undefined' &&
25087                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25088                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25089         ){
25090             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25091         }
25092         
25093         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25094         
25095     },
25096     
25097     baseScaleLevel : function()
25098     {
25099         var width, height;
25100         
25101         if(this.isDocument){
25102             
25103             if(this.baseRotate == 6 || this.baseRotate == 8){
25104             
25105                 height = this.thumbEl.getHeight();
25106                 this.baseScale = height / this.imageEl.OriginWidth;
25107
25108                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25109                     width = this.thumbEl.getWidth();
25110                     this.baseScale = width / this.imageEl.OriginHeight;
25111                 }
25112
25113                 return;
25114             }
25115
25116             height = this.thumbEl.getHeight();
25117             this.baseScale = height / this.imageEl.OriginHeight;
25118
25119             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25120                 width = this.thumbEl.getWidth();
25121                 this.baseScale = width / this.imageEl.OriginWidth;
25122             }
25123
25124             return;
25125         }
25126         
25127         if(this.baseRotate == 6 || this.baseRotate == 8){
25128             
25129             width = this.thumbEl.getHeight();
25130             this.baseScale = width / this.imageEl.OriginHeight;
25131             
25132             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25133                 height = this.thumbEl.getWidth();
25134                 this.baseScale = height / this.imageEl.OriginHeight;
25135             }
25136             
25137             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25138                 height = this.thumbEl.getWidth();
25139                 this.baseScale = height / this.imageEl.OriginHeight;
25140                 
25141                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25142                     width = this.thumbEl.getHeight();
25143                     this.baseScale = width / this.imageEl.OriginWidth;
25144                 }
25145             }
25146             
25147             return;
25148         }
25149         
25150         width = this.thumbEl.getWidth();
25151         this.baseScale = width / this.imageEl.OriginWidth;
25152         
25153         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25154             height = this.thumbEl.getHeight();
25155             this.baseScale = height / this.imageEl.OriginHeight;
25156         }
25157         
25158         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25159             
25160             height = this.thumbEl.getHeight();
25161             this.baseScale = height / this.imageEl.OriginHeight;
25162             
25163             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25164                 width = this.thumbEl.getWidth();
25165                 this.baseScale = width / this.imageEl.OriginWidth;
25166             }
25167             
25168         }
25169         
25170         return;
25171     },
25172     
25173     getScaleLevel : function()
25174     {
25175         return this.baseScale * Math.pow(1.1, this.scale);
25176     },
25177     
25178     onTouchStart : function(e)
25179     {
25180         if(!this.canvasLoaded){
25181             this.beforeSelectFile(e);
25182             return;
25183         }
25184         
25185         var touches = e.browserEvent.touches;
25186         
25187         if(!touches){
25188             return;
25189         }
25190         
25191         if(touches.length == 1){
25192             this.onMouseDown(e);
25193             return;
25194         }
25195         
25196         if(touches.length != 2){
25197             return;
25198         }
25199         
25200         var coords = [];
25201         
25202         for(var i = 0, finger; finger = touches[i]; i++){
25203             coords.push(finger.pageX, finger.pageY);
25204         }
25205         
25206         var x = Math.pow(coords[0] - coords[2], 2);
25207         var y = Math.pow(coords[1] - coords[3], 2);
25208         
25209         this.startDistance = Math.sqrt(x + y);
25210         
25211         this.startScale = this.scale;
25212         
25213         this.pinching = true;
25214         this.dragable = false;
25215         
25216     },
25217     
25218     onTouchMove : function(e)
25219     {
25220         if(!this.pinching && !this.dragable){
25221             return;
25222         }
25223         
25224         var touches = e.browserEvent.touches;
25225         
25226         if(!touches){
25227             return;
25228         }
25229         
25230         if(this.dragable){
25231             this.onMouseMove(e);
25232             return;
25233         }
25234         
25235         var coords = [];
25236         
25237         for(var i = 0, finger; finger = touches[i]; i++){
25238             coords.push(finger.pageX, finger.pageY);
25239         }
25240         
25241         var x = Math.pow(coords[0] - coords[2], 2);
25242         var y = Math.pow(coords[1] - coords[3], 2);
25243         
25244         this.endDistance = Math.sqrt(x + y);
25245         
25246         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25247         
25248         if(!this.zoomable()){
25249             this.scale = this.startScale;
25250             return;
25251         }
25252         
25253         this.draw();
25254         
25255     },
25256     
25257     onTouchEnd : function(e)
25258     {
25259         this.pinching = false;
25260         this.dragable = false;
25261         
25262     },
25263     
25264     process : function(file, crop)
25265     {
25266         this.xhr = new XMLHttpRequest();
25267         
25268         file.xhr = this.xhr;
25269
25270         this.xhr.open(this.method, this.url, true);
25271         
25272         var headers = {
25273             "Accept": "application/json",
25274             "Cache-Control": "no-cache",
25275             "X-Requested-With": "XMLHttpRequest"
25276         };
25277         
25278         for (var headerName in headers) {
25279             var headerValue = headers[headerName];
25280             if (headerValue) {
25281                 this.xhr.setRequestHeader(headerName, headerValue);
25282             }
25283         }
25284         
25285         var _this = this;
25286         
25287         this.xhr.onload = function()
25288         {
25289             _this.xhrOnLoad(_this.xhr);
25290         }
25291         
25292         this.xhr.onerror = function()
25293         {
25294             _this.xhrOnError(_this.xhr);
25295         }
25296         
25297         var formData = new FormData();
25298
25299         formData.append('returnHTML', 'NO');
25300         
25301         if(crop){
25302             formData.append('crop', crop);
25303         }
25304         
25305         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25306             formData.append(this.paramName, file, file.name);
25307         }
25308         
25309         if(typeof(file.filename) != 'undefined'){
25310             formData.append('filename', file.filename);
25311         }
25312         
25313         if(typeof(file.mimetype) != 'undefined'){
25314             formData.append('mimetype', file.mimetype);
25315         }
25316         
25317         if(this.fireEvent('arrange', this, formData) != false){
25318             this.xhr.send(formData);
25319         };
25320     },
25321     
25322     xhrOnLoad : function(xhr)
25323     {
25324         if (xhr.readyState !== 4) {
25325             this.fireEvent('exception', this, xhr);
25326             return;
25327         }
25328
25329         var response = Roo.decode(xhr.responseText);
25330         
25331         if(!response.success){
25332             this.fireEvent('exception', this, xhr);
25333             return;
25334         }
25335         
25336         var response = Roo.decode(xhr.responseText);
25337         
25338         this.fireEvent('upload', this, response);
25339         
25340     },
25341     
25342     xhrOnError : function()
25343     {
25344         Roo.log('xhr on error');
25345         
25346         var response = Roo.decode(xhr.responseText);
25347           
25348         Roo.log(response);
25349         
25350     },
25351     
25352     prepare : function(file)
25353     {   
25354         this.file = false;
25355         this.exif = {};
25356         
25357         if(typeof(file) === 'string'){
25358             this.loadCanvas(file);
25359             return;
25360         }
25361         
25362         if(!file || !this.urlAPI){
25363             return;
25364         }
25365         
25366         this.file = file;
25367         this.cropType = file.type;
25368         
25369         var _this = this;
25370         
25371         if(this.fireEvent('prepare', this, this.file) != false){
25372             
25373             var reader = new FileReader();
25374             
25375             reader.onload = function (e) {
25376                 if (e.target.error) {
25377                     Roo.log(e.target.error);
25378                     return;
25379                 }
25380                 
25381                 var buffer = e.target.result,
25382                     dataView = new DataView(buffer),
25383                     offset = 2,
25384                     maxOffset = dataView.byteLength - 4,
25385                     markerBytes,
25386                     markerLength;
25387                 
25388                 if (dataView.getUint16(0) === 0xffd8) {
25389                     while (offset < maxOffset) {
25390                         markerBytes = dataView.getUint16(offset);
25391                         
25392                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25393                             markerLength = dataView.getUint16(offset + 2) + 2;
25394                             if (offset + markerLength > dataView.byteLength) {
25395                                 Roo.log('Invalid meta data: Invalid segment size.');
25396                                 break;
25397                             }
25398                             
25399                             if(markerBytes == 0xffe1){
25400                                 _this.parseExifData(
25401                                     dataView,
25402                                     offset,
25403                                     markerLength
25404                                 );
25405                             }
25406                             
25407                             offset += markerLength;
25408                             
25409                             continue;
25410                         }
25411                         
25412                         break;
25413                     }
25414                     
25415                 }
25416                 
25417                 var url = _this.urlAPI.createObjectURL(_this.file);
25418                 
25419                 _this.loadCanvas(url);
25420                 
25421                 return;
25422             }
25423             
25424             reader.readAsArrayBuffer(this.file);
25425             
25426         }
25427         
25428     },
25429     
25430     parseExifData : function(dataView, offset, length)
25431     {
25432         var tiffOffset = offset + 10,
25433             littleEndian,
25434             dirOffset;
25435     
25436         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25437             // No Exif data, might be XMP data instead
25438             return;
25439         }
25440         
25441         // Check for the ASCII code for "Exif" (0x45786966):
25442         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25443             // No Exif data, might be XMP data instead
25444             return;
25445         }
25446         if (tiffOffset + 8 > dataView.byteLength) {
25447             Roo.log('Invalid Exif data: Invalid segment size.');
25448             return;
25449         }
25450         // Check for the two null bytes:
25451         if (dataView.getUint16(offset + 8) !== 0x0000) {
25452             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25453             return;
25454         }
25455         // Check the byte alignment:
25456         switch (dataView.getUint16(tiffOffset)) {
25457         case 0x4949:
25458             littleEndian = true;
25459             break;
25460         case 0x4D4D:
25461             littleEndian = false;
25462             break;
25463         default:
25464             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25465             return;
25466         }
25467         // Check for the TIFF tag marker (0x002A):
25468         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25469             Roo.log('Invalid Exif data: Missing TIFF marker.');
25470             return;
25471         }
25472         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25473         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25474         
25475         this.parseExifTags(
25476             dataView,
25477             tiffOffset,
25478             tiffOffset + dirOffset,
25479             littleEndian
25480         );
25481     },
25482     
25483     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25484     {
25485         var tagsNumber,
25486             dirEndOffset,
25487             i;
25488         if (dirOffset + 6 > dataView.byteLength) {
25489             Roo.log('Invalid Exif data: Invalid directory offset.');
25490             return;
25491         }
25492         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25493         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25494         if (dirEndOffset + 4 > dataView.byteLength) {
25495             Roo.log('Invalid Exif data: Invalid directory size.');
25496             return;
25497         }
25498         for (i = 0; i < tagsNumber; i += 1) {
25499             this.parseExifTag(
25500                 dataView,
25501                 tiffOffset,
25502                 dirOffset + 2 + 12 * i, // tag offset
25503                 littleEndian
25504             );
25505         }
25506         // Return the offset to the next directory:
25507         return dataView.getUint32(dirEndOffset, littleEndian);
25508     },
25509     
25510     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25511     {
25512         var tag = dataView.getUint16(offset, littleEndian);
25513         
25514         this.exif[tag] = this.getExifValue(
25515             dataView,
25516             tiffOffset,
25517             offset,
25518             dataView.getUint16(offset + 2, littleEndian), // tag type
25519             dataView.getUint32(offset + 4, littleEndian), // tag length
25520             littleEndian
25521         );
25522     },
25523     
25524     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25525     {
25526         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25527             tagSize,
25528             dataOffset,
25529             values,
25530             i,
25531             str,
25532             c;
25533     
25534         if (!tagType) {
25535             Roo.log('Invalid Exif data: Invalid tag type.');
25536             return;
25537         }
25538         
25539         tagSize = tagType.size * length;
25540         // Determine if the value is contained in the dataOffset bytes,
25541         // or if the value at the dataOffset is a pointer to the actual data:
25542         dataOffset = tagSize > 4 ?
25543                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25544         if (dataOffset + tagSize > dataView.byteLength) {
25545             Roo.log('Invalid Exif data: Invalid data offset.');
25546             return;
25547         }
25548         if (length === 1) {
25549             return tagType.getValue(dataView, dataOffset, littleEndian);
25550         }
25551         values = [];
25552         for (i = 0; i < length; i += 1) {
25553             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25554         }
25555         
25556         if (tagType.ascii) {
25557             str = '';
25558             // Concatenate the chars:
25559             for (i = 0; i < values.length; i += 1) {
25560                 c = values[i];
25561                 // Ignore the terminating NULL byte(s):
25562                 if (c === '\u0000') {
25563                     break;
25564                 }
25565                 str += c;
25566             }
25567             return str;
25568         }
25569         return values;
25570     }
25571     
25572 });
25573
25574 Roo.apply(Roo.bootstrap.UploadCropbox, {
25575     tags : {
25576         'Orientation': 0x0112
25577     },
25578     
25579     Orientation: {
25580             1: 0, //'top-left',
25581 //            2: 'top-right',
25582             3: 180, //'bottom-right',
25583 //            4: 'bottom-left',
25584 //            5: 'left-top',
25585             6: 90, //'right-top',
25586 //            7: 'right-bottom',
25587             8: 270 //'left-bottom'
25588     },
25589     
25590     exifTagTypes : {
25591         // byte, 8-bit unsigned int:
25592         1: {
25593             getValue: function (dataView, dataOffset) {
25594                 return dataView.getUint8(dataOffset);
25595             },
25596             size: 1
25597         },
25598         // ascii, 8-bit byte:
25599         2: {
25600             getValue: function (dataView, dataOffset) {
25601                 return String.fromCharCode(dataView.getUint8(dataOffset));
25602             },
25603             size: 1,
25604             ascii: true
25605         },
25606         // short, 16 bit int:
25607         3: {
25608             getValue: function (dataView, dataOffset, littleEndian) {
25609                 return dataView.getUint16(dataOffset, littleEndian);
25610             },
25611             size: 2
25612         },
25613         // long, 32 bit int:
25614         4: {
25615             getValue: function (dataView, dataOffset, littleEndian) {
25616                 return dataView.getUint32(dataOffset, littleEndian);
25617             },
25618             size: 4
25619         },
25620         // rational = two long values, first is numerator, second is denominator:
25621         5: {
25622             getValue: function (dataView, dataOffset, littleEndian) {
25623                 return dataView.getUint32(dataOffset, littleEndian) /
25624                     dataView.getUint32(dataOffset + 4, littleEndian);
25625             },
25626             size: 8
25627         },
25628         // slong, 32 bit signed int:
25629         9: {
25630             getValue: function (dataView, dataOffset, littleEndian) {
25631                 return dataView.getInt32(dataOffset, littleEndian);
25632             },
25633             size: 4
25634         },
25635         // srational, two slongs, first is numerator, second is denominator:
25636         10: {
25637             getValue: function (dataView, dataOffset, littleEndian) {
25638                 return dataView.getInt32(dataOffset, littleEndian) /
25639                     dataView.getInt32(dataOffset + 4, littleEndian);
25640             },
25641             size: 8
25642         }
25643     },
25644     
25645     footer : {
25646         STANDARD : [
25647             {
25648                 tag : 'div',
25649                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25650                 action : 'rotate-left',
25651                 cn : [
25652                     {
25653                         tag : 'button',
25654                         cls : 'btn btn-default',
25655                         html : '<i class="fa fa-undo"></i>'
25656                     }
25657                 ]
25658             },
25659             {
25660                 tag : 'div',
25661                 cls : 'btn-group roo-upload-cropbox-picture',
25662                 action : 'picture',
25663                 cn : [
25664                     {
25665                         tag : 'button',
25666                         cls : 'btn btn-default',
25667                         html : '<i class="fa fa-picture-o"></i>'
25668                     }
25669                 ]
25670             },
25671             {
25672                 tag : 'div',
25673                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25674                 action : 'rotate-right',
25675                 cn : [
25676                     {
25677                         tag : 'button',
25678                         cls : 'btn btn-default',
25679                         html : '<i class="fa fa-repeat"></i>'
25680                     }
25681                 ]
25682             }
25683         ],
25684         DOCUMENT : [
25685             {
25686                 tag : 'div',
25687                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25688                 action : 'rotate-left',
25689                 cn : [
25690                     {
25691                         tag : 'button',
25692                         cls : 'btn btn-default',
25693                         html : '<i class="fa fa-undo"></i>'
25694                     }
25695                 ]
25696             },
25697             {
25698                 tag : 'div',
25699                 cls : 'btn-group roo-upload-cropbox-download',
25700                 action : 'download',
25701                 cn : [
25702                     {
25703                         tag : 'button',
25704                         cls : 'btn btn-default',
25705                         html : '<i class="fa fa-download"></i>'
25706                     }
25707                 ]
25708             },
25709             {
25710                 tag : 'div',
25711                 cls : 'btn-group roo-upload-cropbox-crop',
25712                 action : 'crop',
25713                 cn : [
25714                     {
25715                         tag : 'button',
25716                         cls : 'btn btn-default',
25717                         html : '<i class="fa fa-crop"></i>'
25718                     }
25719                 ]
25720             },
25721             {
25722                 tag : 'div',
25723                 cls : 'btn-group roo-upload-cropbox-trash',
25724                 action : 'trash',
25725                 cn : [
25726                     {
25727                         tag : 'button',
25728                         cls : 'btn btn-default',
25729                         html : '<i class="fa fa-trash"></i>'
25730                     }
25731                 ]
25732             },
25733             {
25734                 tag : 'div',
25735                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25736                 action : 'rotate-right',
25737                 cn : [
25738                     {
25739                         tag : 'button',
25740                         cls : 'btn btn-default',
25741                         html : '<i class="fa fa-repeat"></i>'
25742                     }
25743                 ]
25744             }
25745         ],
25746         ROTATOR : [
25747             {
25748                 tag : 'div',
25749                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25750                 action : 'rotate-left',
25751                 cn : [
25752                     {
25753                         tag : 'button',
25754                         cls : 'btn btn-default',
25755                         html : '<i class="fa fa-undo"></i>'
25756                     }
25757                 ]
25758             },
25759             {
25760                 tag : 'div',
25761                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25762                 action : 'rotate-right',
25763                 cn : [
25764                     {
25765                         tag : 'button',
25766                         cls : 'btn btn-default',
25767                         html : '<i class="fa fa-repeat"></i>'
25768                     }
25769                 ]
25770             }
25771         ]
25772     }
25773 });
25774
25775 /*
25776 * Licence: LGPL
25777 */
25778
25779 /**
25780  * @class Roo.bootstrap.DocumentManager
25781  * @extends Roo.bootstrap.Component
25782  * Bootstrap DocumentManager class
25783  * @cfg {String} paramName default 'imageUpload'
25784  * @cfg {String} method default POST
25785  * @cfg {String} url action url
25786  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
25787  * @cfg {Boolean} multiple multiple upload default true
25788  * @cfg {Number} thumbSize default 300
25789  * @cfg {String} fieldLabel
25790  * @cfg {Number} labelWidth default 4
25791  * @cfg {String} labelAlign (left|top) default left
25792  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
25793  * 
25794  * @constructor
25795  * Create a new DocumentManager
25796  * @param {Object} config The config object
25797  */
25798
25799 Roo.bootstrap.DocumentManager = function(config){
25800     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25801     
25802     this.addEvents({
25803         /**
25804          * @event initial
25805          * Fire when initial the DocumentManager
25806          * @param {Roo.bootstrap.DocumentManager} this
25807          */
25808         "initial" : true,
25809         /**
25810          * @event inspect
25811          * inspect selected file
25812          * @param {Roo.bootstrap.DocumentManager} this
25813          * @param {File} file
25814          */
25815         "inspect" : true,
25816         /**
25817          * @event exception
25818          * Fire when xhr load exception
25819          * @param {Roo.bootstrap.DocumentManager} this
25820          * @param {XMLHttpRequest} xhr
25821          */
25822         "exception" : true,
25823         /**
25824          * @event prepare
25825          * prepare the form data
25826          * @param {Roo.bootstrap.DocumentManager} this
25827          * @param {Object} formData
25828          */
25829         "prepare" : true,
25830         /**
25831          * @event remove
25832          * Fire when remove the file
25833          * @param {Roo.bootstrap.DocumentManager} this
25834          * @param {Object} file
25835          */
25836         "remove" : true,
25837         /**
25838          * @event refresh
25839          * Fire after refresh the file
25840          * @param {Roo.bootstrap.DocumentManager} this
25841          */
25842         "refresh" : true,
25843         /**
25844          * @event click
25845          * Fire after click the image
25846          * @param {Roo.bootstrap.DocumentManager} this
25847          * @param {Object} file
25848          */
25849         "click" : true,
25850         /**
25851          * @event edit
25852          * Fire when upload a image and editable set to true
25853          * @param {Roo.bootstrap.DocumentManager} this
25854          * @param {Object} file
25855          */
25856         "edit" : true,
25857         /**
25858          * @event beforeselectfile
25859          * Fire before select file
25860          * @param {Roo.bootstrap.DocumentManager} this
25861          */
25862         "beforeselectfile" : true,
25863         /**
25864          * @event process
25865          * Fire before process file
25866          * @param {Roo.bootstrap.DocumentManager} this
25867          * @param {Object} file
25868          */
25869         "process" : true
25870         
25871     });
25872 };
25873
25874 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
25875     
25876     boxes : 0,
25877     inputName : '',
25878     thumbSize : 300,
25879     multiple : true,
25880     files : [],
25881     method : 'POST',
25882     url : '',
25883     paramName : 'imageUpload',
25884     fieldLabel : '',
25885     labelWidth : 4,
25886     labelAlign : 'left',
25887     editable : true,
25888     delegates : [],
25889     
25890     getAutoCreate : function()
25891     {   
25892         var managerWidget = {
25893             tag : 'div',
25894             cls : 'roo-document-manager',
25895             cn : [
25896                 {
25897                     tag : 'input',
25898                     cls : 'roo-document-manager-selector',
25899                     type : 'file'
25900                 },
25901                 {
25902                     tag : 'div',
25903                     cls : 'roo-document-manager-uploader',
25904                     cn : [
25905                         {
25906                             tag : 'div',
25907                             cls : 'roo-document-manager-upload-btn',
25908                             html : '<i class="fa fa-plus"></i>'
25909                         }
25910                     ]
25911                     
25912                 }
25913             ]
25914         };
25915         
25916         var content = [
25917             {
25918                 tag : 'div',
25919                 cls : 'column col-md-12',
25920                 cn : managerWidget
25921             }
25922         ];
25923         
25924         if(this.fieldLabel.length){
25925             
25926             content = [
25927                 {
25928                     tag : 'div',
25929                     cls : 'column col-md-12',
25930                     html : this.fieldLabel
25931                 },
25932                 {
25933                     tag : 'div',
25934                     cls : 'column col-md-12',
25935                     cn : managerWidget
25936                 }
25937             ];
25938
25939             if(this.labelAlign == 'left'){
25940                 content = [
25941                     {
25942                         tag : 'div',
25943                         cls : 'column col-md-' + this.labelWidth,
25944                         html : this.fieldLabel
25945                     },
25946                     {
25947                         tag : 'div',
25948                         cls : 'column col-md-' + (12 - this.labelWidth),
25949                         cn : managerWidget
25950                     }
25951                 ];
25952                 
25953             }
25954         }
25955         
25956         var cfg = {
25957             tag : 'div',
25958             cls : 'row clearfix',
25959             cn : content
25960         };
25961         
25962         return cfg;
25963         
25964     },
25965     
25966     initEvents : function()
25967     {
25968         this.managerEl = this.el.select('.roo-document-manager', true).first();
25969         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25970         
25971         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
25972         this.selectorEl.hide();
25973         
25974         if(this.multiple){
25975             this.selectorEl.attr('multiple', 'multiple');
25976         }
25977         
25978         this.selectorEl.on('change', this.onFileSelected, this);
25979         
25980         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
25981         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25982         
25983         this.uploader.on('click', this.onUploaderClick, this);
25984         
25985         this.renderProgressDialog();
25986         
25987         var _this = this;
25988         
25989         window.addEventListener("resize", function() { _this.refresh(); } );
25990         
25991         this.fireEvent('initial', this);
25992     },
25993     
25994     renderProgressDialog : function()
25995     {
25996         var _this = this;
25997         
25998         this.progressDialog = new Roo.bootstrap.Modal({
25999             cls : 'roo-document-manager-progress-dialog',
26000             allow_close : false,
26001             title : '',
26002             buttons : [
26003                 {
26004                     name  :'cancel',
26005                     weight : 'danger',
26006                     html : 'Cancel'
26007                 }
26008             ], 
26009             listeners : { 
26010                 btnclick : function() {
26011                     _this.uploadCancel();
26012                     this.hide();
26013                 }
26014             }
26015         });
26016          
26017         this.progressDialog.render(Roo.get(document.body));
26018          
26019         this.progress = new Roo.bootstrap.Progress({
26020             cls : 'roo-document-manager-progress',
26021             active : true,
26022             striped : true
26023         });
26024         
26025         this.progress.render(this.progressDialog.getChildContainer());
26026         
26027         this.progressBar = new Roo.bootstrap.ProgressBar({
26028             cls : 'roo-document-manager-progress-bar',
26029             aria_valuenow : 0,
26030             aria_valuemin : 0,
26031             aria_valuemax : 12,
26032             panel : 'success'
26033         });
26034         
26035         this.progressBar.render(this.progress.getChildContainer());
26036     },
26037     
26038     onUploaderClick : function(e)
26039     {
26040         e.preventDefault();
26041      
26042         if(this.fireEvent('beforeselectfile', this) != false){
26043             this.selectorEl.dom.click();
26044         }
26045         
26046     },
26047     
26048     onFileSelected : function(e)
26049     {
26050         e.preventDefault();
26051         
26052         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26053             return;
26054         }
26055         
26056         Roo.each(this.selectorEl.dom.files, function(file){
26057             if(this.fireEvent('inspect', this, file) != false){
26058                 this.files.push(file);
26059             }
26060         }, this);
26061         
26062         this.queue();
26063         
26064     },
26065     
26066     queue : function()
26067     {
26068         this.selectorEl.dom.value = '';
26069         
26070         if(!this.files.length){
26071             return;
26072         }
26073         
26074         if(this.boxes > 0 && this.files.length > this.boxes){
26075             this.files = this.files.slice(0, this.boxes);
26076         }
26077         
26078         this.uploader.show();
26079         
26080         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26081             this.uploader.hide();
26082         }
26083         
26084         var _this = this;
26085         
26086         var files = [];
26087         
26088         var docs = [];
26089         
26090         Roo.each(this.files, function(file){
26091             
26092             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26093                 var f = this.renderPreview(file);
26094                 files.push(f);
26095                 return;
26096             }
26097             
26098             if(file.type.indexOf('image') != -1){
26099                 this.delegates.push(
26100                     (function(){
26101                         _this.process(file);
26102                     }).createDelegate(this)
26103                 );
26104         
26105                 return;
26106             }
26107             
26108             docs.push(
26109                 (function(){
26110                     _this.process(file);
26111                 }).createDelegate(this)
26112             );
26113             
26114         }, this);
26115         
26116         this.files = files;
26117         
26118         this.delegates = this.delegates.concat(docs);
26119         
26120         if(!this.delegates.length){
26121             this.refresh();
26122             return;
26123         }
26124         
26125         this.progressBar.aria_valuemax = this.delegates.length;
26126         
26127         this.arrange();
26128         
26129         return;
26130     },
26131     
26132     arrange : function()
26133     {
26134         if(!this.delegates.length){
26135             this.progressDialog.hide();
26136             this.refresh();
26137             return;
26138         }
26139         
26140         var delegate = this.delegates.shift();
26141         
26142         this.progressDialog.show();
26143         
26144         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26145         
26146         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26147         
26148         delegate();
26149     },
26150     
26151     refresh : function()
26152     {
26153         this.uploader.show();
26154         
26155         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26156             this.uploader.hide();
26157         }
26158         
26159         Roo.isTouch ? this.closable(false) : this.closable(true);
26160         
26161         this.fireEvent('refresh', this);
26162     },
26163     
26164     onRemove : function(e, el, o)
26165     {
26166         e.preventDefault();
26167         
26168         this.fireEvent('remove', this, o);
26169         
26170     },
26171     
26172     remove : function(o)
26173     {
26174         var files = [];
26175         
26176         Roo.each(this.files, function(file){
26177             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26178                 files.push(file);
26179                 return;
26180             }
26181
26182             o.target.remove();
26183
26184         }, this);
26185         
26186         this.files = files;
26187         
26188         this.refresh();
26189     },
26190     
26191     clear : function()
26192     {
26193         Roo.each(this.files, function(file){
26194             if(!file.target){
26195                 return;
26196             }
26197             
26198             file.target.remove();
26199
26200         }, this);
26201         
26202         this.files = [];
26203         
26204         this.refresh();
26205     },
26206     
26207     onClick : function(e, el, o)
26208     {
26209         e.preventDefault();
26210         
26211         this.fireEvent('click', this, o);
26212         
26213     },
26214     
26215     closable : function(closable)
26216     {
26217         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26218             
26219             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26220             
26221             if(closable){
26222                 el.show();
26223                 return;
26224             }
26225             
26226             el.hide();
26227             
26228         }, this);
26229     },
26230     
26231     xhrOnLoad : function(xhr)
26232     {
26233         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26234             el.remove();
26235         }, this);
26236         
26237         if (xhr.readyState !== 4) {
26238             this.arrange();
26239             this.fireEvent('exception', this, xhr);
26240             return;
26241         }
26242
26243         var response = Roo.decode(xhr.responseText);
26244         
26245         if(!response.success){
26246             this.arrange();
26247             this.fireEvent('exception', this, xhr);
26248             return;
26249         }
26250         
26251         var file = this.renderPreview(response.data);
26252         
26253         this.files.push(file);
26254         
26255         this.arrange();
26256         
26257     },
26258     
26259     xhrOnError : function()
26260     {
26261         Roo.log('xhr on error');
26262         
26263         var response = Roo.decode(xhr.responseText);
26264           
26265         Roo.log(response);
26266         
26267         this.arrange();
26268     },
26269     
26270     process : function(file)
26271     {
26272         if(this.fireEvent('process', this, file) !== false){
26273             if(this.editable && file.type.indexOf('image') != -1){
26274                 this.fireEvent('edit', this, file);
26275                 return;
26276             }
26277
26278             this.uploadStart(file, false);
26279
26280             return;
26281         }
26282         
26283     },
26284     
26285     uploadStart : function(file, crop)
26286     {
26287         this.xhr = new XMLHttpRequest();
26288         
26289         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26290             this.arrange();
26291             return;
26292         }
26293         
26294         file.xhr = this.xhr;
26295             
26296         this.managerEl.createChild({
26297             tag : 'div',
26298             cls : 'roo-document-manager-loading',
26299             cn : [
26300                 {
26301                     tag : 'div',
26302                     tooltip : file.name,
26303                     cls : 'roo-document-manager-thumb',
26304                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26305                 }
26306             ]
26307
26308         });
26309
26310         this.xhr.open(this.method, this.url, true);
26311         
26312         var headers = {
26313             "Accept": "application/json",
26314             "Cache-Control": "no-cache",
26315             "X-Requested-With": "XMLHttpRequest"
26316         };
26317         
26318         for (var headerName in headers) {
26319             var headerValue = headers[headerName];
26320             if (headerValue) {
26321                 this.xhr.setRequestHeader(headerName, headerValue);
26322             }
26323         }
26324         
26325         var _this = this;
26326         
26327         this.xhr.onload = function()
26328         {
26329             _this.xhrOnLoad(_this.xhr);
26330         }
26331         
26332         this.xhr.onerror = function()
26333         {
26334             _this.xhrOnError(_this.xhr);
26335         }
26336         
26337         var formData = new FormData();
26338
26339         formData.append('returnHTML', 'NO');
26340         
26341         if(crop){
26342             formData.append('crop', crop);
26343         }
26344         
26345         formData.append(this.paramName, file, file.name);
26346         
26347         if(this.fireEvent('prepare', this, formData) != false){
26348             this.xhr.send(formData);
26349         };
26350     },
26351     
26352     uploadCancel : function()
26353     {
26354         this.xhr.abort();
26355         
26356         this.delegates = [];
26357         
26358         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26359             el.remove();
26360         }, this);
26361         
26362         this.arrange();
26363     },
26364     
26365     renderPreview : function(file)
26366     {
26367         if(typeof(file.target) != 'undefined' && file.target){
26368             return file;
26369         }
26370         
26371         var previewEl = this.managerEl.createChild({
26372             tag : 'div',
26373             cls : 'roo-document-manager-preview',
26374             cn : [
26375                 {
26376                     tag : 'div',
26377                     tooltip : file.filename,
26378                     cls : 'roo-document-manager-thumb',
26379                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26380                 },
26381                 {
26382                     tag : 'button',
26383                     cls : 'close',
26384                     html : '<i class="fa fa-times-circle"></i>'
26385                 }
26386             ]
26387         });
26388
26389         var close = previewEl.select('button.close', true).first();
26390
26391         close.on('click', this.onRemove, this, file);
26392
26393         file.target = previewEl;
26394
26395         var image = previewEl.select('img', true).first();
26396         
26397         var _this = this;
26398         
26399         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26400         
26401         image.on('click', this.onClick, this, file);
26402         
26403         return file;
26404         
26405     },
26406     
26407     onPreviewLoad : function(file, image)
26408     {
26409         if(typeof(file.target) == 'undefined' || !file.target){
26410             return;
26411         }
26412         
26413         var width = image.dom.naturalWidth || image.dom.width;
26414         var height = image.dom.naturalHeight || image.dom.height;
26415         
26416         if(width > height){
26417             file.target.addClass('wide');
26418             return;
26419         }
26420         
26421         file.target.addClass('tall');
26422         return;
26423         
26424     },
26425     
26426     uploadFromSource : function(file, crop)
26427     {
26428         this.xhr = new XMLHttpRequest();
26429         
26430         this.managerEl.createChild({
26431             tag : 'div',
26432             cls : 'roo-document-manager-loading',
26433             cn : [
26434                 {
26435                     tag : 'div',
26436                     tooltip : file.name,
26437                     cls : 'roo-document-manager-thumb',
26438                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26439                 }
26440             ]
26441
26442         });
26443
26444         this.xhr.open(this.method, this.url, true);
26445         
26446         var headers = {
26447             "Accept": "application/json",
26448             "Cache-Control": "no-cache",
26449             "X-Requested-With": "XMLHttpRequest"
26450         };
26451         
26452         for (var headerName in headers) {
26453             var headerValue = headers[headerName];
26454             if (headerValue) {
26455                 this.xhr.setRequestHeader(headerName, headerValue);
26456             }
26457         }
26458         
26459         var _this = this;
26460         
26461         this.xhr.onload = function()
26462         {
26463             _this.xhrOnLoad(_this.xhr);
26464         }
26465         
26466         this.xhr.onerror = function()
26467         {
26468             _this.xhrOnError(_this.xhr);
26469         }
26470         
26471         var formData = new FormData();
26472
26473         formData.append('returnHTML', 'NO');
26474         
26475         formData.append('crop', crop);
26476         
26477         if(typeof(file.filename) != 'undefined'){
26478             formData.append('filename', file.filename);
26479         }
26480         
26481         if(typeof(file.mimetype) != 'undefined'){
26482             formData.append('mimetype', file.mimetype);
26483         }
26484         
26485         if(this.fireEvent('prepare', this, formData) != false){
26486             this.xhr.send(formData);
26487         };
26488     }
26489 });
26490
26491 /*
26492 * Licence: LGPL
26493 */
26494
26495 /**
26496  * @class Roo.bootstrap.DocumentViewer
26497  * @extends Roo.bootstrap.Component
26498  * Bootstrap DocumentViewer class
26499  * 
26500  * @constructor
26501  * Create a new DocumentViewer
26502  * @param {Object} config The config object
26503  */
26504
26505 Roo.bootstrap.DocumentViewer = function(config){
26506     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26507     
26508     this.addEvents({
26509         /**
26510          * @event initial
26511          * Fire after initEvent
26512          * @param {Roo.bootstrap.DocumentViewer} this
26513          */
26514         "initial" : true,
26515         /**
26516          * @event click
26517          * Fire after click
26518          * @param {Roo.bootstrap.DocumentViewer} this
26519          */
26520         "click" : true,
26521         /**
26522          * @event trash
26523          * Fire after trash button
26524          * @param {Roo.bootstrap.DocumentViewer} this
26525          */
26526         "trash" : true
26527         
26528     });
26529 };
26530
26531 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
26532     
26533     getAutoCreate : function()
26534     {
26535         var cfg = {
26536             tag : 'div',
26537             cls : 'roo-document-viewer',
26538             cn : [
26539                 {
26540                     tag : 'div',
26541                     cls : 'roo-document-viewer-body',
26542                     cn : [
26543                         {
26544                             tag : 'div',
26545                             cls : 'roo-document-viewer-thumb',
26546                             cn : [
26547                                 {
26548                                     tag : 'img',
26549                                     cls : 'roo-document-viewer-image'
26550                                 }
26551                             ]
26552                         }
26553                     ]
26554                 },
26555                 {
26556                     tag : 'div',
26557                     cls : 'roo-document-viewer-footer',
26558                     cn : {
26559                         tag : 'div',
26560                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26561                         cn : [
26562                             {
26563                                 tag : 'div',
26564                                 cls : 'btn-group',
26565                                 cn : [
26566                                     {
26567                                         tag : 'button',
26568                                         cls : 'btn btn-default roo-document-viewer-trash',
26569                                         html : '<i class="fa fa-trash"></i>'
26570                                     }
26571                                 ]
26572                             }
26573                         ]
26574                     }
26575                 }
26576             ]
26577         };
26578         
26579         return cfg;
26580     },
26581     
26582     initEvents : function()
26583     {
26584         
26585         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26586         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26587         
26588         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26589         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26590         
26591         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26592         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26593         
26594         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26595         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26596         
26597         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26598         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26599         
26600         this.bodyEl.on('click', this.onClick, this);
26601         
26602         this.trashBtn.on('click', this.onTrash, this);
26603         
26604     },
26605     
26606     initial : function()
26607     {
26608 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26609         
26610         
26611         this.fireEvent('initial', this);
26612         
26613     },
26614     
26615     onClick : function(e)
26616     {
26617         e.preventDefault();
26618         
26619         this.fireEvent('click', this);
26620     },
26621     
26622     onTrash : function(e)
26623     {
26624         e.preventDefault();
26625         
26626         this.fireEvent('trash', this);
26627     }
26628     
26629 });
26630 /*
26631  * - LGPL
26632  *
26633  * nav progress bar
26634  * 
26635  */
26636
26637 /**
26638  * @class Roo.bootstrap.NavProgressBar
26639  * @extends Roo.bootstrap.Component
26640  * Bootstrap NavProgressBar class
26641  * 
26642  * @constructor
26643  * Create a new nav progress bar
26644  * @param {Object} config The config object
26645  */
26646
26647 Roo.bootstrap.NavProgressBar = function(config){
26648     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26649
26650     this.bullets = this.bullets || [];
26651    
26652 //    Roo.bootstrap.NavProgressBar.register(this);
26653      this.addEvents({
26654         /**
26655              * @event changed
26656              * Fires when the active item changes
26657              * @param {Roo.bootstrap.NavProgressBar} this
26658              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26659              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
26660          */
26661         'changed': true
26662      });
26663     
26664 };
26665
26666 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
26667     
26668     bullets : [],
26669     barItems : [],
26670     
26671     getAutoCreate : function()
26672     {
26673         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26674         
26675         cfg = {
26676             tag : 'div',
26677             cls : 'roo-navigation-bar-group',
26678             cn : [
26679                 {
26680                     tag : 'div',
26681                     cls : 'roo-navigation-top-bar'
26682                 },
26683                 {
26684                     tag : 'div',
26685                     cls : 'roo-navigation-bullets-bar',
26686                     cn : [
26687                         {
26688                             tag : 'ul',
26689                             cls : 'roo-navigation-bar'
26690                         }
26691                     ]
26692                 },
26693                 
26694                 {
26695                     tag : 'div',
26696                     cls : 'roo-navigation-bottom-bar'
26697                 }
26698             ]
26699             
26700         };
26701         
26702         return cfg;
26703         
26704     },
26705     
26706     initEvents: function() 
26707     {
26708         
26709     },
26710     
26711     onRender : function(ct, position) 
26712     {
26713         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26714         
26715         if(this.bullets.length){
26716             Roo.each(this.bullets, function(b){
26717                this.addItem(b);
26718             }, this);
26719         }
26720         
26721         this.format();
26722         
26723     },
26724     
26725     addItem : function(cfg)
26726     {
26727         var item = new Roo.bootstrap.NavProgressItem(cfg);
26728         
26729         item.parentId = this.id;
26730         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
26731         
26732         if(cfg.html){
26733             var top = new Roo.bootstrap.Element({
26734                 tag : 'div',
26735                 cls : 'roo-navigation-bar-text'
26736             });
26737             
26738             var bottom = new Roo.bootstrap.Element({
26739                 tag : 'div',
26740                 cls : 'roo-navigation-bar-text'
26741             });
26742             
26743             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
26744             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
26745             
26746             var topText = new Roo.bootstrap.Element({
26747                 tag : 'span',
26748                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
26749             });
26750             
26751             var bottomText = new Roo.bootstrap.Element({
26752                 tag : 'span',
26753                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
26754             });
26755             
26756             topText.onRender(top.el, null);
26757             bottomText.onRender(bottom.el, null);
26758             
26759             item.topEl = top;
26760             item.bottomEl = bottom;
26761         }
26762         
26763         this.barItems.push(item);
26764         
26765         return item;
26766     },
26767     
26768     getActive : function()
26769     {
26770         var active = false;
26771         
26772         Roo.each(this.barItems, function(v){
26773             
26774             if (!v.isActive()) {
26775                 return;
26776             }
26777             
26778             active = v;
26779             return false;
26780             
26781         });
26782         
26783         return active;
26784     },
26785     
26786     setActiveItem : function(item)
26787     {
26788         var prev = false;
26789         
26790         Roo.each(this.barItems, function(v){
26791             if (v.rid == item.rid) {
26792                 return ;
26793             }
26794             
26795             if (v.isActive()) {
26796                 v.setActive(false);
26797                 prev = v;
26798             }
26799         });
26800
26801         item.setActive(true);
26802         
26803         this.fireEvent('changed', this, item, prev);
26804     },
26805     
26806     getBarItem: function(rid)
26807     {
26808         var ret = false;
26809         
26810         Roo.each(this.barItems, function(e) {
26811             if (e.rid != rid) {
26812                 return;
26813             }
26814             
26815             ret =  e;
26816             return false;
26817         });
26818         
26819         return ret;
26820     },
26821     
26822     indexOfItem : function(item)
26823     {
26824         var index = false;
26825         
26826         Roo.each(this.barItems, function(v, i){
26827             
26828             if (v.rid != item.rid) {
26829                 return;
26830             }
26831             
26832             index = i;
26833             return false
26834         });
26835         
26836         return index;
26837     },
26838     
26839     setActiveNext : function()
26840     {
26841         var i = this.indexOfItem(this.getActive());
26842         
26843         if (i > this.barItems.length) {
26844             return;
26845         }
26846         
26847         this.setActiveItem(this.barItems[i+1]);
26848     },
26849     
26850     setActivePrev : function()
26851     {
26852         var i = this.indexOfItem(this.getActive());
26853         
26854         if (i  < 1) {
26855             return;
26856         }
26857         
26858         this.setActiveItem(this.barItems[i-1]);
26859     },
26860     
26861     format : function()
26862     {
26863         if(!this.barItems.length){
26864             return;
26865         }
26866      
26867         var width = 100 / this.barItems.length;
26868         
26869         Roo.each(this.barItems, function(i){
26870             i.el.setStyle('width', width + '%');
26871             i.topEl.el.setStyle('width', width + '%');
26872             i.bottomEl.el.setStyle('width', width + '%');
26873         }, this);
26874         
26875     }
26876     
26877 });
26878 /*
26879  * - LGPL
26880  *
26881  * Nav Progress Item
26882  * 
26883  */
26884
26885 /**
26886  * @class Roo.bootstrap.NavProgressItem
26887  * @extends Roo.bootstrap.Component
26888  * Bootstrap NavProgressItem class
26889  * @cfg {String} rid the reference id
26890  * @cfg {Boolean} active (true|false) Is item active default false
26891  * @cfg {Boolean} disabled (true|false) Is item active default false
26892  * @cfg {String} html
26893  * @cfg {String} position (top|bottom) text position default bottom
26894  * @cfg {String} icon show icon instead of number
26895  * 
26896  * @constructor
26897  * Create a new NavProgressItem
26898  * @param {Object} config The config object
26899  */
26900 Roo.bootstrap.NavProgressItem = function(config){
26901     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
26902     this.addEvents({
26903         // raw events
26904         /**
26905          * @event click
26906          * The raw click event for the entire grid.
26907          * @param {Roo.bootstrap.NavProgressItem} this
26908          * @param {Roo.EventObject} e
26909          */
26910         "click" : true
26911     });
26912    
26913 };
26914
26915 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
26916     
26917     rid : '',
26918     active : false,
26919     disabled : false,
26920     html : '',
26921     position : 'bottom',
26922     icon : false,
26923     
26924     getAutoCreate : function()
26925     {
26926         var iconCls = 'roo-navigation-bar-item-icon';
26927         
26928         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
26929         
26930         var cfg = {
26931             tag: 'li',
26932             cls: 'roo-navigation-bar-item',
26933             cn : [
26934                 {
26935                     tag : 'i',
26936                     cls : iconCls
26937                 }
26938             ]
26939         }
26940         
26941         if(this.active){
26942             cfg.cls += ' active';
26943         }
26944         if(this.disabled){
26945             cfg.cls += ' disabled';
26946         }
26947         
26948         return cfg;
26949     },
26950     
26951     disable : function()
26952     {
26953         this.setDisabled(true);
26954     },
26955     
26956     enable : function()
26957     {
26958         this.setDisabled(false);
26959     },
26960     
26961     initEvents: function() 
26962     {
26963         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
26964         
26965         this.iconEl.on('click', this.onClick, this);
26966     },
26967     
26968     onClick : function(e)
26969     {
26970         e.preventDefault();
26971         
26972         if(this.disabled){
26973             return;
26974         }
26975         
26976         if(this.fireEvent('click', this, e) === false){
26977             return;
26978         };
26979         
26980         this.parent().setActiveItem(this);
26981     },
26982     
26983     isActive: function () 
26984     {
26985         return this.active;
26986     },
26987     
26988     setActive : function(state)
26989     {
26990         if(this.active == state){
26991             return;
26992         }
26993         
26994         this.active = state;
26995         
26996         if (state) {
26997             this.el.addClass('active');
26998             return;
26999         }
27000         
27001         this.el.removeClass('active');
27002         
27003         return;
27004     },
27005     
27006     setDisabled : function(state)
27007     {
27008         if(this.disabled == state){
27009             return;
27010         }
27011         
27012         this.disabled = state;
27013         
27014         if (state) {
27015             this.el.addClass('disabled');
27016             return;
27017         }
27018         
27019         this.el.removeClass('disabled');
27020     },
27021     
27022     tooltipEl : function()
27023     {
27024         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27025     }
27026 });
27027  
27028
27029