docs/default.css
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         cfg.id = this.id || Roo.id();
105         
106         // fill in the extra attributes 
107         if (this.xattr && typeof(this.xattr) =='object') {
108             for (var i in this.xattr) {
109                 cfg[i] = this.xattr[i];
110             }
111         }
112         
113         if(this.dataId){
114             cfg.dataId = this.dataId;
115         }
116         
117         if (this.cls) {
118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
119         }
120         
121         if (this.style) { // fixme needs to support more complex style data.
122             cfg.style = this.style;
123         }
124         
125         if(this.name){
126             cfg.name = this.name;
127         }
128         
129         this.el = ct.createChild(cfg, position);
130         
131         if (this.tooltip) {
132             this.tooltipEl().attr('tooltip', this.tooltip);
133         }
134         
135         if(this.tabIndex !== undefined){
136             this.el.dom.setAttribute('tabIndex', this.tabIndex);
137         }
138         this.initEvents();
139         
140         
141     },
142     /**
143      * Fetch the element to add children to
144      * @return {Roo.Element} defaults to this.el
145      */
146     getChildContainer : function()
147     {
148         return this.el;
149     },
150     /**
151      * Fetch the element to display the tooltip on.
152      * @return {Roo.Element} defaults to this.el
153      */
154     tooltipEl : function()
155     {
156         return this.el;
157     },
158         
159     addxtype  : function(tree,cntr)
160     {
161         var cn = this;
162         
163         cn = Roo.factory(tree);
164            
165         cn.parentType = this.xtype; //??
166         cn.parentId = this.id;
167         
168         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
169         if (typeof(cn.container_method) == 'string') {
170             cntr = cn.container_method;
171         }
172         
173         
174         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
175         
176         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
177         
178         var build_from_html =  Roo.XComponent.build_from_html;
179           
180         var is_body  = (tree.xtype == 'Body') ;
181           
182         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
183           
184         var self_cntr_el = Roo.get(this[cntr](false));
185         
186         // do not try and build conditional elements 
187         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
188             return false;
189         }
190         
191         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
192             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
193                 return this.addxtypeChild(tree,cntr);
194             }
195             
196             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
197                 
198             if(echild){
199                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
200             }
201             
202             Roo.log('skipping render');
203             return cn;
204             
205         }
206         
207         var ret = false;
208         if (!build_from_html) {
209             return false;
210         }
211         
212         // this i think handles overlaying multiple children of the same type
213         // with the sam eelement.. - which might be buggy..
214         while (true) {
215             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
216             
217             if (!echild) {
218                 break;
219             }
220             
221             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
222                 break;
223             }
224             
225             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
226         }
227         return ret;
228     },
229     
230     addxtypeChild : function (tree, cntr)
231     {
232         Roo.debug && Roo.log('addxtypeChild:' + cntr);
233         var cn = this;
234         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
235         
236         
237         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
238                     (typeof(tree['flexy:foreach']) != 'undefined');
239           
240         
241         
242          skip_children = false;
243         // render the element if it's not BODY.
244         if (tree.xtype != 'Body') {
245            
246             cn = Roo.factory(tree);
247            
248             cn.parentType = this.xtype; //??
249             cn.parentId = this.id;
250             
251             var build_from_html =  Roo.XComponent.build_from_html;
252             
253             
254             // does the container contain child eleemnts with 'xtype' attributes.
255             // that match this xtype..
256             // note - when we render we create these as well..
257             // so we should check to see if body has xtype set.
258             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
259                
260                 var self_cntr_el = Roo.get(this[cntr](false));
261                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
262                 if (echild) { 
263                     //Roo.log(Roo.XComponent.build_from_html);
264                     //Roo.log("got echild:");
265                     //Roo.log(echild);
266                 }
267                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
268                 // and are not displayed -this causes this to use up the wrong element when matching.
269                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
270                 
271                 
272                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
273                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
274                   
275                   
276                   
277                     cn.el = echild;
278                   //  Roo.log("GOT");
279                     //echild.dom.removeAttribute('xtype');
280                 } else {
281                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
282                     Roo.debug && Roo.log(self_cntr_el);
283                     Roo.debug && Roo.log(echild);
284                     Roo.debug && Roo.log(cn);
285                 }
286             }
287            
288             
289            
290             // if object has flexy:if - then it may or may not be rendered.
291             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
292                 // skip a flexy if element.
293                 Roo.debug && Roo.log('skipping render');
294                 Roo.debug && Roo.log(tree);
295                 if (!cn.el) {
296                     Roo.debug && Roo.log('skipping all children');
297                     skip_children = true;
298                 }
299                 
300              } else {
301                  
302                 // actually if flexy:foreach is found, we really want to create 
303                 // multiple copies here...
304                 //Roo.log('render');
305                 //Roo.log(this[cntr]());
306                 cn.render(this[cntr](true));
307              }
308             // then add the element..
309         }
310         
311         
312         // handle the kids..
313         
314         var nitems = [];
315         /*
316         if (typeof (tree.menu) != 'undefined') {
317             tree.menu.parentType = cn.xtype;
318             tree.menu.triggerEl = cn.el;
319             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
320             
321         }
322         */
323         if (!tree.items || !tree.items.length) {
324             cn.items = nitems;
325             return cn;
326         }
327         var items = tree.items;
328         delete tree.items;
329         
330         //Roo.log(items.length);
331             // add the items..
332         if (!skip_children) {    
333             for(var i =0;i < items.length;i++) {
334                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
335             }
336         }
337         
338         cn.items = nitems;
339         
340         this.fireEvent('childrenrendered', this);
341         
342         return cn;
343     },
344     /**
345      * Show a component - removes 'hidden' class
346      */
347     show : function()
348     {
349         if (this.el) {
350             this.el.removeClass('hidden');
351         }
352     },
353     /**
354      * Hide a component - adds 'hidden' class
355      */
356     hide: function()
357     {
358         if (this.el && !this.el.hasClass('hidden')) {
359             this.el.addClass('hidden');
360         }
361         
362     }
363 });
364
365  /*
366  * - LGPL
367  *
368  * Body
369  * 
370  */
371
372 /**
373  * @class Roo.bootstrap.Body
374  * @extends Roo.bootstrap.Component
375  * Bootstrap Body class
376  * 
377  * @constructor
378  * Create a new body
379  * @param {Object} config The config object
380  */
381
382 Roo.bootstrap.Body = function(config){
383     Roo.bootstrap.Body.superclass.constructor.call(this, config);
384     this.el = Roo.get(document.body);
385     if (this.cls && this.cls.length) {
386         Roo.get(document.body).addClass(this.cls);
387     }
388 };
389
390 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
391       
392         autoCreate : {
393         cls: 'container'
394     },
395     onRender : function(ct, position)
396     {
397        /* Roo.log("Roo.bootstrap.Body - onRender");
398         if (this.cls && this.cls.length) {
399             Roo.get(document.body).addClass(this.cls);
400         }
401         // style??? xttr???
402         */
403     }
404     
405     
406  
407    
408 });
409
410  /*
411  * - LGPL
412  *
413  * button group
414  * 
415  */
416
417
418 /**
419  * @class Roo.bootstrap.ButtonGroup
420  * @extends Roo.bootstrap.Component
421  * Bootstrap ButtonGroup class
422  * @cfg {String} size lg | sm | xs (default empty normal)
423  * @cfg {String} align vertical | justified  (default none)
424  * @cfg {String} direction up | down (default down)
425  * @cfg {Boolean} toolbar false | true
426  * @cfg {Boolean} btn true | false
427  * 
428  * 
429  * @constructor
430  * Create a new Input
431  * @param {Object} config The config object
432  */
433
434 Roo.bootstrap.ButtonGroup = function(config){
435     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
436 };
437
438 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
439     
440     size: '',
441     align: '',
442     direction: '',
443     toolbar: false,
444     btn: true,
445
446     getAutoCreate : function(){
447         var cfg = {
448             cls: 'btn-group',
449             html : null
450         }
451         
452         cfg.html = this.html || cfg.html;
453         
454         if (this.toolbar) {
455             cfg = {
456                 cls: 'btn-toolbar',
457                 html: null
458             }
459             
460             return cfg;
461         }
462         
463         if (['vertical','justified'].indexOf(this.align)!==-1) {
464             cfg.cls = 'btn-group-' + this.align;
465             
466             if (this.align == 'justified') {
467                 console.log(this.items);
468             }
469         }
470         
471         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
472             cfg.cls += ' btn-group-' + this.size;
473         }
474         
475         if (this.direction == 'up') {
476             cfg.cls += ' dropup' ;
477         }
478         
479         return cfg;
480     }
481    
482 });
483
484  /*
485  * - LGPL
486  *
487  * button
488  * 
489  */
490
491 /**
492  * @class Roo.bootstrap.Button
493  * @extends Roo.bootstrap.Component
494  * Bootstrap Button class
495  * @cfg {String} html The button content
496  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
497  * @cfg {String} size ( lg | sm | xs)
498  * @cfg {String} tag ( a | input | submit)
499  * @cfg {String} href empty or href
500  * @cfg {Boolean} disabled default false;
501  * @cfg {Boolean} isClose default false;
502  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
503  * @cfg {String} badge text for badge
504  * @cfg {String} theme default 
505  * @cfg {Boolean} inverse 
506  * @cfg {Boolean} toggle 
507  * @cfg {String} ontext text for on toggle state
508  * @cfg {String} offtext text for off toggle state
509  * @cfg {Boolean} defaulton 
510  * @cfg {Boolean} preventDefault  default true
511  * @cfg {Boolean} removeClass remove the standard class..
512  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
513  * 
514  * @constructor
515  * Create a new button
516  * @param {Object} config The config object
517  */
518
519
520 Roo.bootstrap.Button = function(config){
521     Roo.bootstrap.Button.superclass.constructor.call(this, config);
522     this.addEvents({
523         // raw events
524         /**
525          * @event click
526          * When a butotn is pressed
527          * @param {Roo.bootstrap.Button} this
528          * @param {Roo.EventObject} e
529          */
530         "click" : true,
531          /**
532          * @event toggle
533          * After the button has been toggles
534          * @param {Roo.EventObject} e
535          * @param {boolean} pressed (also available as button.pressed)
536          */
537         "toggle" : true
538     });
539 };
540
541 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
542     html: false,
543     active: false,
544     weight: '',
545     size: '',
546     tag: 'button',
547     href: '',
548     disabled: false,
549     isClose: false,
550     glyphicon: '',
551     badge: '',
552     theme: 'default',
553     inverse: false,
554     
555     toggle: false,
556     ontext: 'ON',
557     offtext: 'OFF',
558     defaulton: true,
559     preventDefault: true,
560     removeClass: false,
561     name: false,
562     target: false,
563     
564     
565     pressed : null,
566      
567     
568     getAutoCreate : function(){
569         
570         var cfg = {
571             tag : 'button',
572             cls : 'roo-button',
573             html: ''
574         };
575         
576         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
577             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
578             this.tag = 'button';
579         } else {
580             cfg.tag = this.tag;
581         }
582         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
583         
584         if (this.toggle == true) {
585             cfg={
586                 tag: 'div',
587                 cls: 'slider-frame roo-button',
588                 cn: [
589                     {
590                         tag: 'span',
591                         'data-on-text':'ON',
592                         'data-off-text':'OFF',
593                         cls: 'slider-button',
594                         html: this.offtext
595                     }
596                 ]
597             };
598             
599             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
600                 cfg.cls += ' '+this.weight;
601             }
602             
603             return cfg;
604         }
605         
606         if (this.isClose) {
607             cfg.cls += ' close';
608             
609             cfg["aria-hidden"] = true;
610             
611             cfg.html = "&times;";
612             
613             return cfg;
614         }
615         
616          
617         if (this.theme==='default') {
618             cfg.cls = 'btn roo-button';
619             
620             //if (this.parentType != 'Navbar') {
621             this.weight = this.weight.length ?  this.weight : 'default';
622             //}
623             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
624                 
625                 cfg.cls += ' btn-' + this.weight;
626             }
627         } else if (this.theme==='glow') {
628             
629             cfg.tag = 'a';
630             cfg.cls = 'btn-glow roo-button';
631             
632             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
633                 
634                 cfg.cls += ' ' + this.weight;
635             }
636         }
637    
638         
639         if (this.inverse) {
640             this.cls += ' inverse';
641         }
642         
643         
644         if (this.active) {
645             cfg.cls += ' active';
646         }
647         
648         if (this.disabled) {
649             cfg.disabled = 'disabled';
650         }
651         
652         if (this.items) {
653             Roo.log('changing to ul' );
654             cfg.tag = 'ul';
655             this.glyphicon = 'caret';
656         }
657         
658         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
659          
660         //gsRoo.log(this.parentType);
661         if (this.parentType === 'Navbar' && !this.parent().bar) {
662             Roo.log('changing to li?');
663             
664             cfg.tag = 'li';
665             
666             cfg.cls = '';
667             cfg.cn =  [{
668                 tag : 'a',
669                 cls : 'roo-button',
670                 html : this.html,
671                 href : this.href || '#'
672             }];
673             if (this.menu) {
674                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
675                 cfg.cls += ' dropdown';
676             }   
677             
678             delete cfg.html;
679             
680         }
681         
682        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
683         
684         if (this.glyphicon) {
685             cfg.html = ' ' + cfg.html;
686             
687             cfg.cn = [
688                 {
689                     tag: 'span',
690                     cls: 'glyphicon glyphicon-' + this.glyphicon
691                 }
692             ];
693         }
694         
695         if (this.badge) {
696             cfg.html += ' ';
697             
698             cfg.tag = 'a';
699             
700 //            cfg.cls='btn roo-button';
701             
702             cfg.href=this.href;
703             
704             var value = cfg.html;
705             
706             if(this.glyphicon){
707                 value = {
708                             tag: 'span',
709                             cls: 'glyphicon glyphicon-' + this.glyphicon,
710                             html: this.html
711                         };
712                 
713             }
714             
715             cfg.cn = [
716                 value,
717                 {
718                     tag: 'span',
719                     cls: 'badge',
720                     html: this.badge
721                 }
722             ];
723             
724             cfg.html='';
725         }
726         
727         if (this.menu) {
728             cfg.cls += ' dropdown';
729             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
730         }
731         
732         if (cfg.tag !== 'a' && this.href !== '') {
733             throw "Tag must be a to set href.";
734         } else if (this.href.length > 0) {
735             cfg.href = this.href;
736         }
737         
738         if(this.removeClass){
739             cfg.cls = '';
740         }
741         
742         if(this.target){
743             cfg.target = this.target;
744         }
745         
746         return cfg;
747     },
748     initEvents: function() {
749        // Roo.log('init events?');
750 //        Roo.log(this.el.dom);
751         // add the menu...
752         
753         if (typeof (this.menu) != 'undefined') {
754             this.menu.parentType = this.xtype;
755             this.menu.triggerEl = this.el;
756             this.addxtype(Roo.apply({}, this.menu));
757         }
758
759
760        if (this.el.hasClass('roo-button')) {
761             this.el.on('click', this.onClick, this);
762        } else {
763             this.el.select('.roo-button').on('click', this.onClick, this);
764        }
765        
766        if(this.removeClass){
767            this.el.on('click', this.onClick, this);
768        }
769        
770        this.el.enableDisplayMode();
771         
772     },
773     onClick : function(e)
774     {
775         if (this.disabled) {
776             return;
777         }
778         
779         
780         Roo.log('button on click ');
781         if(this.preventDefault){
782             e.preventDefault();
783         }
784         if (this.pressed === true || this.pressed === false) {
785             this.pressed = !this.pressed;
786             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
787             this.fireEvent('toggle', this, e, this.pressed);
788         }
789         
790         
791         this.fireEvent('click', this, e);
792     },
793     
794     /**
795      * Enables this button
796      */
797     enable : function()
798     {
799         this.disabled = false;
800         this.el.removeClass('disabled');
801     },
802     
803     /**
804      * Disable this button
805      */
806     disable : function()
807     {
808         this.disabled = true;
809         this.el.addClass('disabled');
810     },
811      /**
812      * sets the active state on/off, 
813      * @param {Boolean} state (optional) Force a particular state
814      */
815     setActive : function(v) {
816         
817         this.el[v ? 'addClass' : 'removeClass']('active');
818     },
819      /**
820      * toggles the current active state 
821      */
822     toggleActive : function()
823     {
824        var active = this.el.hasClass('active');
825        this.setActive(!active);
826        
827         
828     },
829     setText : function(str)
830     {
831         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
832     },
833     getText : function()
834     {
835         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
836     },
837     hide: function() {
838        
839      
840         this.el.hide();   
841     },
842     show: function() {
843        
844         this.el.show();   
845     }
846     
847     
848 });
849
850  /*
851  * - LGPL
852  *
853  * column
854  * 
855  */
856
857 /**
858  * @class Roo.bootstrap.Column
859  * @extends Roo.bootstrap.Component
860  * Bootstrap Column class
861  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
862  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
863  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
864  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
865  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
866  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
867  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
868  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
869  *
870  * 
871  * @cfg {Boolean} hidden (true|false) hide the element
872  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
873  * @cfg {String} fa (ban|check|...) font awesome icon
874  * @cfg {Number} fasize (1|2|....) font awsome size
875
876  * @cfg {String} icon (info-sign|check|...) glyphicon name
877
878  * @cfg {String} html content of column.
879  * 
880  * @constructor
881  * Create a new Column
882  * @param {Object} config The config object
883  */
884
885 Roo.bootstrap.Column = function(config){
886     Roo.bootstrap.Column.superclass.constructor.call(this, config);
887 };
888
889 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
890     
891     xs: false,
892     sm: false,
893     md: false,
894     lg: false,
895     xsoff: false,
896     smoff: false,
897     mdoff: false,
898     lgoff: false,
899     html: '',
900     offset: 0,
901     alert: false,
902     fa: false,
903     icon : false,
904     hidden : false,
905     fasize : 1,
906     
907     getAutoCreate : function(){
908         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
909         
910         cfg = {
911             tag: 'div',
912             cls: 'column'
913         };
914         
915         var settings=this;
916         ['xs','sm','md','lg'].map(function(size){
917             //Roo.log( size + ':' + settings[size]);
918             
919             if (settings[size+'off'] !== false) {
920                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
921             }
922             
923             if (settings[size] === false) {
924                 return;
925             }
926             Roo.log(settings[size]);
927             if (!settings[size]) { // 0 = hidden
928                 cfg.cls += ' hidden-' + size;
929                 return;
930             }
931             cfg.cls += ' col-' + size + '-' + settings[size];
932             
933         });
934         
935         if (this.hidden) {
936             cfg.cls += ' hidden';
937         }
938         
939         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
940             cfg.cls +=' alert alert-' + this.alert;
941         }
942         
943         
944         if (this.html.length) {
945             cfg.html = this.html;
946         }
947         if (this.fa) {
948             var fasize = '';
949             if (this.fasize > 1) {
950                 fasize = ' fa-' + this.fasize + 'x';
951             }
952             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
953             
954             
955         }
956         if (this.icon) {
957             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
958         }
959         
960         return cfg;
961     }
962    
963 });
964
965  
966
967  /*
968  * - LGPL
969  *
970  * page container.
971  * 
972  */
973
974
975 /**
976  * @class Roo.bootstrap.Container
977  * @extends Roo.bootstrap.Component
978  * Bootstrap Container class
979  * @cfg {Boolean} jumbotron is it a jumbotron element
980  * @cfg {String} html content of element
981  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
982  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
983  * @cfg {String} header content of header (for panel)
984  * @cfg {String} footer content of footer (for panel)
985  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
986  * @cfg {String} tag (header|aside|section) type of HTML tag.
987  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
988  * @cfg {String} fa (ban|check|...) font awesome icon
989  * @cfg {String} icon (info-sign|check|...) glyphicon name
990  * @cfg {Boolean} hidden (true|false) hide the element
991  * @cfg {Boolean} expandable (true|false) default false
992  * @cfg {Boolean} expanded (true|false) default true
993  * @cfg {String} rheader contet on the right of header
994
995  *     
996  * @constructor
997  * Create a new Container
998  * @param {Object} config The config object
999  */
1000
1001 Roo.bootstrap.Container = function(config){
1002     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1003     
1004     this.addEvents({
1005         // raw events
1006          /**
1007          * @event expand
1008          * After the panel has been expand
1009          * 
1010          * @param {Roo.bootstrap.Container} this
1011          */
1012         "expand" : true,
1013         /**
1014          * @event collapse
1015          * After the panel has been collapsed
1016          * 
1017          * @param {Roo.bootstrap.Container} this
1018          */
1019         "collapse" : true
1020     });
1021 };
1022
1023 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1024     
1025     jumbotron : false,
1026     well: '',
1027     panel : '',
1028     header: '',
1029     footer : '',
1030     sticky: '',
1031     tag : false,
1032     alert : false,
1033     fa: false,
1034     icon : false,
1035     expandable : false,
1036     rheader : '',
1037     expanded : true,
1038   
1039      
1040     getChildContainer : function() {
1041         
1042         if(!this.el){
1043             return false;
1044         }
1045         
1046         if (this.panel.length) {
1047             return this.el.select('.panel-body',true).first();
1048         }
1049         
1050         return this.el;
1051     },
1052     
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : this.tag || 'div',
1058             html : '',
1059             cls : ''
1060         };
1061         if (this.jumbotron) {
1062             cfg.cls = 'jumbotron';
1063         }
1064         
1065         
1066         
1067         // - this is applied by the parent..
1068         //if (this.cls) {
1069         //    cfg.cls = this.cls + '';
1070         //}
1071         
1072         if (this.sticky.length) {
1073             
1074             var bd = Roo.get(document.body);
1075             if (!bd.hasClass('bootstrap-sticky')) {
1076                 bd.addClass('bootstrap-sticky');
1077                 Roo.select('html',true).setStyle('height', '100%');
1078             }
1079              
1080             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1081         }
1082         
1083         
1084         if (this.well.length) {
1085             switch (this.well) {
1086                 case 'lg':
1087                 case 'sm':
1088                     cfg.cls +=' well well-' +this.well;
1089                     break;
1090                 default:
1091                     cfg.cls +=' well';
1092                     break;
1093             }
1094         }
1095         
1096         if (this.hidden) {
1097             cfg.cls += ' hidden';
1098         }
1099         
1100         
1101         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1102             cfg.cls +=' alert alert-' + this.alert;
1103         }
1104         
1105         var body = cfg;
1106         
1107         if (this.panel.length) {
1108             cfg.cls += ' panel panel-' + this.panel;
1109             cfg.cn = [];
1110             if (this.header.length) {
1111                 
1112                 var h = [];
1113                 
1114                 if(this.expandable){
1115                     
1116                     cfg.cls = cfg.cls + ' expandable';
1117                     
1118                     h.push({
1119                         tag: 'i',
1120                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1121                     });
1122                     
1123                 }
1124                 
1125                 h.push(
1126                     {
1127                         tag: 'span',
1128                         cls : 'panel-title',
1129                         html : (this.expandable ? '&nbsp;' : '') + this.header
1130                     },
1131                     {
1132                         tag: 'span',
1133                         cls: 'panel-header-right',
1134                         html: this.rheader
1135                     }
1136                 );
1137                 
1138                 cfg.cn.push({
1139                     cls : 'panel-heading',
1140                     style : this.expandable ? 'cursor: pointer' : '',
1141                     cn : h
1142                 });
1143                 
1144             }
1145             
1146             body = false;
1147             cfg.cn.push({
1148                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1149                 html : this.html
1150             });
1151             
1152             
1153             if (this.footer.length) {
1154                 cfg.cn.push({
1155                     cls : 'panel-footer',
1156                     html : this.footer
1157                     
1158                 });
1159             }
1160             
1161         }
1162         
1163         if (body) {
1164             body.html = this.html || cfg.html;
1165             // prefix with the icons..
1166             if (this.fa) {
1167                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1168             }
1169             if (this.icon) {
1170                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1171             }
1172             
1173             
1174         }
1175         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1176             cfg.cls =  'container';
1177         }
1178         
1179         return cfg;
1180     },
1181     
1182     initEvents: function() 
1183     {
1184         if(!this.expandable){
1185             return;
1186         }
1187         
1188         var headerEl = this.headerEl();
1189         
1190         if(!headerEl){
1191             return;
1192         }
1193         
1194         headerEl.on('click', this.onToggleClick, this);
1195         
1196     },
1197     
1198     onToggleClick : function()
1199     {
1200         var headerEl = this.headerEl();
1201         
1202         if(!headerEl){
1203             return;
1204         }
1205         
1206         if(this.expanded){
1207             this.collapse();
1208             return;
1209         }
1210         
1211         this.expand();
1212     },
1213     
1214     expand : function()
1215     {
1216         if(this.fireEvent('expand', this)) {
1217             
1218             this.expanded = true;
1219             
1220             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1221             
1222             this.el.select('.panel-body',true).first().removeClass('hide');
1223             
1224             var toggleEl = this.toggleEl();
1225
1226             if(!toggleEl){
1227                 return;
1228             }
1229
1230             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1231         }
1232         
1233     },
1234     
1235     collapse : function()
1236     {
1237         if(this.fireEvent('collapse', this)) {
1238             
1239             this.expanded = false;
1240             
1241             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1242             this.el.select('.panel-body',true).first().addClass('hide');
1243         
1244             var toggleEl = this.toggleEl();
1245
1246             if(!toggleEl){
1247                 return;
1248             }
1249
1250             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1251         }
1252     },
1253     
1254     toggleEl : function()
1255     {
1256         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1257             return;
1258         }
1259         
1260         return this.el.select('.panel-heading .fa',true).first();
1261     },
1262     
1263     headerEl : function()
1264     {
1265         if(!this.el || !this.panel.length || !this.header.length){
1266             return;
1267         }
1268         
1269         return this.el.select('.panel-heading',true).first()
1270     },
1271     
1272     titleEl : function()
1273     {
1274         if(!this.el || !this.panel.length || !this.header.length){
1275             return;
1276         }
1277         
1278         return this.el.select('.panel-title',true).first();
1279     },
1280     
1281     setTitle : function(v)
1282     {
1283         var titleEl = this.titleEl();
1284         
1285         if(!titleEl){
1286             return;
1287         }
1288         
1289         titleEl.dom.innerHTML = v;
1290     },
1291     
1292     getTitle : function()
1293     {
1294         
1295         var titleEl = this.titleEl();
1296         
1297         if(!titleEl){
1298             return '';
1299         }
1300         
1301         return titleEl.dom.innerHTML;
1302     },
1303     
1304     setRightTitle : function(v)
1305     {
1306         var t = this.el.select('.panel-header-right',true).first();
1307         
1308         if(!t){
1309             return;
1310         }
1311         
1312         t.dom.innerHTML = v;
1313     }
1314    
1315 });
1316
1317  /*
1318  * - LGPL
1319  *
1320  * image
1321  * 
1322  */
1323
1324
1325 /**
1326  * @class Roo.bootstrap.Img
1327  * @extends Roo.bootstrap.Component
1328  * Bootstrap Img class
1329  * @cfg {Boolean} imgResponsive false | true
1330  * @cfg {String} border rounded | circle | thumbnail
1331  * @cfg {String} src image source
1332  * @cfg {String} alt image alternative text
1333  * @cfg {String} href a tag href
1334  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1335  * @cfg {String} xsUrl xs image source
1336  * @cfg {String} smUrl sm image source
1337  * @cfg {String} mdUrl md image source
1338  * @cfg {String} lgUrl lg image source
1339  * 
1340  * @constructor
1341  * Create a new Input
1342  * @param {Object} config The config object
1343  */
1344
1345 Roo.bootstrap.Img = function(config){
1346     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1347     
1348     this.addEvents({
1349         // img events
1350         /**
1351          * @event click
1352          * The img click event for the img.
1353          * @param {Roo.EventObject} e
1354          */
1355         "click" : true
1356     });
1357 };
1358
1359 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1360     
1361     imgResponsive: true,
1362     border: '',
1363     src: '',
1364     href: false,
1365     target: false,
1366     xsUrl: '',
1367     smUrl: '',
1368     mdUrl: '',
1369     lgUrl: '',
1370
1371     getAutoCreate : function()
1372     {   
1373         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1374             return this.createSingleImg();
1375         }
1376         
1377         var cfg = {
1378             tag: 'div',
1379             cls: 'roo-image-responsive-group',
1380             cn: []
1381         }
1382         var _this = this;
1383         
1384         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1385             
1386             if(!_this[size + 'Url']){
1387                 return;
1388             }
1389             
1390             var img = {
1391                 tag: 'img',
1392                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1393                 html: _this.html || cfg.html,
1394                 src: _this[size + 'Url']
1395             }
1396             
1397             img.cls += ' roo-image-responsive-' + size;
1398             
1399             var s = ['xs', 'sm', 'md', 'lg'];
1400             
1401             s.splice(s.indexOf(size), 1);
1402             
1403             Roo.each(s, function(ss){
1404                 img.cls += ' hidden-' + ss;
1405             });
1406             
1407             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1408                 cfg.cls += ' img-' + _this.border;
1409             }
1410             
1411             if(_this.alt){
1412                 cfg.alt = _this.alt;
1413             }
1414             
1415             if(_this.href){
1416                 var a = {
1417                     tag: 'a',
1418                     href: _this.href,
1419                     cn: [
1420                         img
1421                     ]
1422                 }
1423
1424                 if(this.target){
1425                     a.target = _this.target;
1426                 }
1427             }
1428             
1429             cfg.cn.push((_this.href) ? a : img);
1430             
1431         });
1432         
1433         return cfg;
1434     },
1435     
1436     createSingleImg : function()
1437     {
1438         var cfg = {
1439             tag: 'img',
1440             cls: (this.imgResponsive) ? 'img-responsive' : '',
1441             html : null
1442         }
1443         
1444         cfg.html = this.html || cfg.html;
1445         
1446         cfg.src = this.src || cfg.src;
1447         
1448         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1449             cfg.cls += ' img-' + this.border;
1450         }
1451         
1452         if(this.alt){
1453             cfg.alt = this.alt;
1454         }
1455         
1456         if(this.href){
1457             var a = {
1458                 tag: 'a',
1459                 href: this.href,
1460                 cn: [
1461                     cfg
1462                 ]
1463             }
1464             
1465             if(this.target){
1466                 a.target = this.target;
1467             }
1468             
1469         }
1470         
1471         return (this.href) ? a : cfg;
1472     },
1473     
1474     initEvents: function() 
1475     {
1476         if(!this.href){
1477             this.el.on('click', this.onClick, this);
1478         }
1479         
1480     },
1481     
1482     onClick : function(e)
1483     {
1484         Roo.log('img onclick');
1485         this.fireEvent('click', this, e);
1486     }
1487    
1488 });
1489
1490  /*
1491  * - LGPL
1492  *
1493  * image
1494  * 
1495  */
1496
1497
1498 /**
1499  * @class Roo.bootstrap.Link
1500  * @extends Roo.bootstrap.Component
1501  * Bootstrap Link Class
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1505  * @cfg {String} html the content of the link.
1506  * @cfg {String} anchor name for the anchor link
1507
1508  * @cfg {Boolean} preventDefault (true | false) default false
1509
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Link = function(config){
1517     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1531     
1532     href: false,
1533     target: false,
1534     preventDefault: false,
1535     anchor : false,
1536     alt : false,
1537
1538     getAutoCreate : function()
1539     {
1540         
1541         var cfg = {
1542             tag: 'a'
1543         };
1544         // anchor's do not require html/href...
1545         if (this.anchor === false) {
1546             cfg.html = this.html || '';
1547             cfg.href = this.href || '#';
1548         } else {
1549             cfg.name = this.anchor;
1550             if (this.html !== false) {
1551                 cfg.html = this.html;
1552             }
1553             if (this.href !== false) {
1554                 cfg.href = this.href;
1555             }
1556         }
1557         
1558         if(this.alt !== false){
1559             cfg.alt = this.alt;
1560         }
1561         
1562         
1563         if(this.target !== false) {
1564             cfg.target = this.target;
1565         }
1566         
1567         return cfg;
1568     },
1569     
1570     initEvents: function() {
1571         
1572         if(!this.href || this.preventDefault){
1573             this.el.on('click', this.onClick, this);
1574         }
1575     },
1576     
1577     onClick : function(e)
1578     {
1579         if(this.preventDefault){
1580             e.preventDefault();
1581         }
1582         //Roo.log('img onclick');
1583         this.fireEvent('click', this, e);
1584     }
1585    
1586 });
1587
1588  /*
1589  * - LGPL
1590  *
1591  * header
1592  * 
1593  */
1594
1595 /**
1596  * @class Roo.bootstrap.Header
1597  * @extends Roo.bootstrap.Component
1598  * Bootstrap Header class
1599  * @cfg {String} html content of header
1600  * @cfg {Number} level (1|2|3|4|5|6) default 1
1601  * 
1602  * @constructor
1603  * Create a new Header
1604  * @param {Object} config The config object
1605  */
1606
1607
1608 Roo.bootstrap.Header  = function(config){
1609     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1610 };
1611
1612 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1613     
1614     //href : false,
1615     html : false,
1616     level : 1,
1617     
1618     
1619     
1620     getAutoCreate : function(){
1621         
1622         
1623         
1624         var cfg = {
1625             tag: 'h' + (1 *this.level),
1626             html: this.html || ''
1627         } ;
1628         
1629         return cfg;
1630     }
1631    
1632 });
1633
1634  
1635
1636  /*
1637  * Based on:
1638  * Ext JS Library 1.1.1
1639  * Copyright(c) 2006-2007, Ext JS, LLC.
1640  *
1641  * Originally Released Under LGPL - original licence link has changed is not relivant.
1642  *
1643  * Fork - LGPL
1644  * <script type="text/javascript">
1645  */
1646  
1647 /**
1648  * @class Roo.bootstrap.MenuMgr
1649  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1650  * @singleton
1651  */
1652 Roo.bootstrap.MenuMgr = function(){
1653    var menus, active, groups = {}, attached = false, lastShow = new Date();
1654
1655    // private - called when first menu is created
1656    function init(){
1657        menus = {};
1658        active = new Roo.util.MixedCollection();
1659        Roo.get(document).addKeyListener(27, function(){
1660            if(active.length > 0){
1661                hideAll();
1662            }
1663        });
1664    }
1665
1666    // private
1667    function hideAll(){
1668        if(active && active.length > 0){
1669            var c = active.clone();
1670            c.each(function(m){
1671                m.hide();
1672            });
1673        }
1674    }
1675
1676    // private
1677    function onHide(m){
1678        active.remove(m);
1679        if(active.length < 1){
1680            Roo.get(document).un("mouseup", onMouseDown);
1681             
1682            attached = false;
1683        }
1684    }
1685
1686    // private
1687    function onShow(m){
1688        var last = active.last();
1689        lastShow = new Date();
1690        active.add(m);
1691        if(!attached){
1692           Roo.get(document).on("mouseup", onMouseDown);
1693            
1694            attached = true;
1695        }
1696        if(m.parentMenu){
1697           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1698           m.parentMenu.activeChild = m;
1699        }else if(last && last.isVisible()){
1700           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1701        }
1702    }
1703
1704    // private
1705    function onBeforeHide(m){
1706        if(m.activeChild){
1707            m.activeChild.hide();
1708        }
1709        if(m.autoHideTimer){
1710            clearTimeout(m.autoHideTimer);
1711            delete m.autoHideTimer;
1712        }
1713    }
1714
1715    // private
1716    function onBeforeShow(m){
1717        var pm = m.parentMenu;
1718        if(!pm && !m.allowOtherMenus){
1719            hideAll();
1720        }else if(pm && pm.activeChild && active != m){
1721            pm.activeChild.hide();
1722        }
1723    }
1724
1725    // private this should really trigger on mouseup..
1726    function onMouseDown(e){
1727         Roo.log("on Mouse Up");
1728         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1729             Roo.log("hideAll");
1730             hideAll();
1731             e.stopEvent();
1732         }
1733         
1734         
1735    }
1736
1737    // private
1738    function onBeforeCheck(mi, state){
1739        if(state){
1740            var g = groups[mi.group];
1741            for(var i = 0, l = g.length; i < l; i++){
1742                if(g[i] != mi){
1743                    g[i].setChecked(false);
1744                }
1745            }
1746        }
1747    }
1748
1749    return {
1750
1751        /**
1752         * Hides all menus that are currently visible
1753         */
1754        hideAll : function(){
1755             hideAll();  
1756        },
1757
1758        // private
1759        register : function(menu){
1760            if(!menus){
1761                init();
1762            }
1763            menus[menu.id] = menu;
1764            menu.on("beforehide", onBeforeHide);
1765            menu.on("hide", onHide);
1766            menu.on("beforeshow", onBeforeShow);
1767            menu.on("show", onShow);
1768            var g = menu.group;
1769            if(g && menu.events["checkchange"]){
1770                if(!groups[g]){
1771                    groups[g] = [];
1772                }
1773                groups[g].push(menu);
1774                menu.on("checkchange", onCheck);
1775            }
1776        },
1777
1778         /**
1779          * Returns a {@link Roo.menu.Menu} object
1780          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1781          * be used to generate and return a new Menu instance.
1782          */
1783        get : function(menu){
1784            if(typeof menu == "string"){ // menu id
1785                return menus[menu];
1786            }else if(menu.events){  // menu instance
1787                return menu;
1788            }
1789            /*else if(typeof menu.length == 'number'){ // array of menu items?
1790                return new Roo.bootstrap.Menu({items:menu});
1791            }else{ // otherwise, must be a config
1792                return new Roo.bootstrap.Menu(menu);
1793            }
1794            */
1795            return false;
1796        },
1797
1798        // private
1799        unregister : function(menu){
1800            delete menus[menu.id];
1801            menu.un("beforehide", onBeforeHide);
1802            menu.un("hide", onHide);
1803            menu.un("beforeshow", onBeforeShow);
1804            menu.un("show", onShow);
1805            var g = menu.group;
1806            if(g && menu.events["checkchange"]){
1807                groups[g].remove(menu);
1808                menu.un("checkchange", onCheck);
1809            }
1810        },
1811
1812        // private
1813        registerCheckable : function(menuItem){
1814            var g = menuItem.group;
1815            if(g){
1816                if(!groups[g]){
1817                    groups[g] = [];
1818                }
1819                groups[g].push(menuItem);
1820                menuItem.on("beforecheckchange", onBeforeCheck);
1821            }
1822        },
1823
1824        // private
1825        unregisterCheckable : function(menuItem){
1826            var g = menuItem.group;
1827            if(g){
1828                groups[g].remove(menuItem);
1829                menuItem.un("beforecheckchange", onBeforeCheck);
1830            }
1831        }
1832    };
1833 }();/*
1834  * - LGPL
1835  *
1836  * menu
1837  * 
1838  */
1839
1840 /**
1841  * @class Roo.bootstrap.Menu
1842  * @extends Roo.bootstrap.Component
1843  * Bootstrap Menu class - container for MenuItems
1844  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1845  * 
1846  * @constructor
1847  * Create a new Menu
1848  * @param {Object} config The config object
1849  */
1850
1851
1852 Roo.bootstrap.Menu = function(config){
1853     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1854     if (this.registerMenu) {
1855         Roo.bootstrap.MenuMgr.register(this);
1856     }
1857     this.addEvents({
1858         /**
1859          * @event beforeshow
1860          * Fires before this menu is displayed
1861          * @param {Roo.menu.Menu} this
1862          */
1863         beforeshow : true,
1864         /**
1865          * @event beforehide
1866          * Fires before this menu is hidden
1867          * @param {Roo.menu.Menu} this
1868          */
1869         beforehide : true,
1870         /**
1871          * @event show
1872          * Fires after this menu is displayed
1873          * @param {Roo.menu.Menu} this
1874          */
1875         show : true,
1876         /**
1877          * @event hide
1878          * Fires after this menu is hidden
1879          * @param {Roo.menu.Menu} this
1880          */
1881         hide : true,
1882         /**
1883          * @event click
1884          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1885          * @param {Roo.menu.Menu} this
1886          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1887          * @param {Roo.EventObject} e
1888          */
1889         click : true,
1890         /**
1891          * @event mouseover
1892          * Fires when the mouse is hovering over this menu
1893          * @param {Roo.menu.Menu} this
1894          * @param {Roo.EventObject} e
1895          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1896          */
1897         mouseover : true,
1898         /**
1899          * @event mouseout
1900          * Fires when the mouse exits this menu
1901          * @param {Roo.menu.Menu} this
1902          * @param {Roo.EventObject} e
1903          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1904          */
1905         mouseout : true,
1906         /**
1907          * @event itemclick
1908          * Fires when a menu item contained in this menu is clicked
1909          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1910          * @param {Roo.EventObject} e
1911          */
1912         itemclick: true
1913     });
1914     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1915 };
1916
1917 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1918     
1919    /// html : false,
1920     //align : '',
1921     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1922     type: false,
1923     /**
1924      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1925      */
1926     registerMenu : true,
1927     
1928     menuItems :false, // stores the menu items..
1929     
1930     hidden:true,
1931     
1932     parentMenu : false,
1933     
1934     getChildContainer : function() {
1935         return this.el;  
1936     },
1937     
1938     getAutoCreate : function(){
1939          
1940         //if (['right'].indexOf(this.align)!==-1) {
1941         //    cfg.cn[1].cls += ' pull-right'
1942         //}
1943         
1944         
1945         var cfg = {
1946             tag : 'ul',
1947             cls : 'dropdown-menu' ,
1948             style : 'z-index:1000'
1949             
1950         }
1951         
1952         if (this.type === 'submenu') {
1953             cfg.cls = 'submenu active';
1954         }
1955         if (this.type === 'treeview') {
1956             cfg.cls = 'treeview-menu';
1957         }
1958         
1959         return cfg;
1960     },
1961     initEvents : function() {
1962         
1963        // Roo.log("ADD event");
1964        // Roo.log(this.triggerEl.dom);
1965         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1966         
1967         this.triggerEl.addClass('dropdown-toggle');
1968         this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1969
1970         this.el.on("mouseover", this.onMouseOver, this);
1971         this.el.on("mouseout", this.onMouseOut, this);
1972         
1973         
1974     },
1975     findTargetItem : function(e){
1976         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1977         if(!t){
1978             return false;
1979         }
1980         //Roo.log(t);         Roo.log(t.id);
1981         if(t && t.id){
1982             //Roo.log(this.menuitems);
1983             return this.menuitems.get(t.id);
1984             
1985             //return this.items.get(t.menuItemId);
1986         }
1987         
1988         return false;
1989     },
1990     onClick : function(e){
1991         Roo.log("menu.onClick");
1992         var t = this.findTargetItem(e);
1993         if(!t || t.isContainer){
1994             return;
1995         }
1996         Roo.log(e);
1997         /*
1998         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1999             if(t == this.activeItem && t.shouldDeactivate(e)){
2000                 this.activeItem.deactivate();
2001                 delete this.activeItem;
2002                 return;
2003             }
2004             if(t.canActivate){
2005                 this.setActiveItem(t, true);
2006             }
2007             return;
2008             
2009             
2010         }
2011         */
2012        
2013         Roo.log('pass click event');
2014         
2015         t.onClick(e);
2016         
2017         this.fireEvent("click", this, t, e);
2018         
2019         this.hide();
2020     },
2021      onMouseOver : function(e){
2022         var t  = this.findTargetItem(e);
2023         //Roo.log(t);
2024         //if(t){
2025         //    if(t.canActivate && !t.disabled){
2026         //        this.setActiveItem(t, true);
2027         //    }
2028         //}
2029         
2030         this.fireEvent("mouseover", this, e, t);
2031     },
2032     isVisible : function(){
2033         return !this.hidden;
2034     },
2035      onMouseOut : function(e){
2036         var t  = this.findTargetItem(e);
2037         
2038         //if(t ){
2039         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2040         //        this.activeItem.deactivate();
2041         //        delete this.activeItem;
2042         //    }
2043         //}
2044         this.fireEvent("mouseout", this, e, t);
2045     },
2046     
2047     
2048     /**
2049      * Displays this menu relative to another element
2050      * @param {String/HTMLElement/Roo.Element} element The element to align to
2051      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2052      * the element (defaults to this.defaultAlign)
2053      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2054      */
2055     show : function(el, pos, parentMenu){
2056         this.parentMenu = parentMenu;
2057         if(!this.el){
2058             this.render();
2059         }
2060         this.fireEvent("beforeshow", this);
2061         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2062     },
2063      /**
2064      * Displays this menu at a specific xy position
2065      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2066      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2067      */
2068     showAt : function(xy, parentMenu, /* private: */_e){
2069         this.parentMenu = parentMenu;
2070         if(!this.el){
2071             this.render();
2072         }
2073         if(_e !== false){
2074             this.fireEvent("beforeshow", this);
2075             //xy = this.el.adjustForConstraints(xy);
2076         }
2077         
2078         //this.el.show();
2079         this.hideMenuItems();
2080         this.hidden = false;
2081         this.triggerEl.addClass('open');
2082         
2083         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2084             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2085         }
2086         
2087         this.el.setXY(xy);
2088         this.focus();
2089         this.fireEvent("show", this);
2090     },
2091     
2092     focus : function(){
2093         return;
2094         if(!this.hidden){
2095             this.doFocus.defer(50, this);
2096         }
2097     },
2098
2099     doFocus : function(){
2100         if(!this.hidden){
2101             this.focusEl.focus();
2102         }
2103     },
2104
2105     /**
2106      * Hides this menu and optionally all parent menus
2107      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2108      */
2109     hide : function(deep){
2110         
2111         this.hideMenuItems();
2112         if(this.el && this.isVisible()){
2113             this.fireEvent("beforehide", this);
2114             if(this.activeItem){
2115                 this.activeItem.deactivate();
2116                 this.activeItem = null;
2117             }
2118             this.triggerEl.removeClass('open');;
2119             this.hidden = true;
2120             this.fireEvent("hide", this);
2121         }
2122         if(deep === true && this.parentMenu){
2123             this.parentMenu.hide(true);
2124         }
2125     },
2126     
2127     onTriggerPress  : function(e)
2128     {
2129         
2130         Roo.log('trigger press');
2131         //Roo.log(e.getTarget());
2132        // Roo.log(this.triggerEl.dom);
2133         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2134             return;
2135         }
2136         
2137         if (this.isVisible()) {
2138             Roo.log('hide');
2139             this.hide();
2140         } else {
2141             Roo.log('show');
2142             this.show(this.triggerEl, false, false);
2143         }
2144         
2145         e.stopEvent();
2146     },
2147     
2148          
2149        
2150     
2151     hideMenuItems : function()
2152     {
2153         //$(backdrop).remove()
2154         Roo.select('.open',true).each(function(aa) {
2155             
2156             aa.removeClass('open');
2157           //var parent = getParent($(this))
2158           //var relatedTarget = { relatedTarget: this }
2159           
2160            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2161           //if (e.isDefaultPrevented()) return
2162            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2163         })
2164     },
2165     addxtypeChild : function (tree, cntr) {
2166         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2167           
2168         this.menuitems.add(comp);
2169         return comp;
2170
2171     },
2172     getEl : function()
2173     {
2174         Roo.log(this.el);
2175         return this.el;
2176     }
2177 });
2178
2179  
2180  /*
2181  * - LGPL
2182  *
2183  * menu item
2184  * 
2185  */
2186
2187
2188 /**
2189  * @class Roo.bootstrap.MenuItem
2190  * @extends Roo.bootstrap.Component
2191  * Bootstrap MenuItem class
2192  * @cfg {String} html the menu label
2193  * @cfg {String} href the link
2194  * @cfg {Boolean} preventDefault (true | false) default true
2195  * @cfg {Boolean} isContainer (true | false) default false
2196  * 
2197  * 
2198  * @constructor
2199  * Create a new MenuItem
2200  * @param {Object} config The config object
2201  */
2202
2203
2204 Roo.bootstrap.MenuItem = function(config){
2205     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2206     this.addEvents({
2207         // raw events
2208         /**
2209          * @event click
2210          * The raw click event for the entire grid.
2211          * @param {Roo.bootstrap.MenuItem} this
2212          * @param {Roo.EventObject} e
2213          */
2214         "click" : true
2215     });
2216 };
2217
2218 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2219     
2220     href : false,
2221     html : false,
2222     preventDefault: true,
2223     isContainer : false,
2224     
2225     getAutoCreate : function(){
2226         
2227         if(this.isContainer){
2228             return {
2229                 tag: 'li',
2230                 cls: 'dropdown-menu-item'
2231             };
2232         }
2233         
2234         var cfg= {
2235             tag: 'li',
2236             cls: 'dropdown-menu-item',
2237             cn: [
2238                     {
2239                         tag : 'a',
2240                         href : '#',
2241                         html : 'Link'
2242                     }
2243                 ]
2244         };
2245         if (this.parent().type == 'treeview') {
2246             cfg.cls = 'treeview-menu';
2247         }
2248         
2249         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2250         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2251         return cfg;
2252     },
2253     
2254     initEvents: function() {
2255         
2256         //this.el.select('a').on('click', this.onClick, this);
2257         
2258     },
2259     onClick : function(e)
2260     {
2261         Roo.log('item on click ');
2262         //if(this.preventDefault){
2263         //    e.preventDefault();
2264         //}
2265         //this.parent().hideMenuItems();
2266         
2267         this.fireEvent('click', this, e);
2268     },
2269     getEl : function()
2270     {
2271         return this.el;
2272     }
2273 });
2274
2275  
2276
2277  /*
2278  * - LGPL
2279  *
2280  * menu separator
2281  * 
2282  */
2283
2284
2285 /**
2286  * @class Roo.bootstrap.MenuSeparator
2287  * @extends Roo.bootstrap.Component
2288  * Bootstrap MenuSeparator class
2289  * 
2290  * @constructor
2291  * Create a new MenuItem
2292  * @param {Object} config The config object
2293  */
2294
2295
2296 Roo.bootstrap.MenuSeparator = function(config){
2297     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2298 };
2299
2300 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2301     
2302     getAutoCreate : function(){
2303         var cfg = {
2304             cls: 'divider',
2305             tag : 'li'
2306         };
2307         
2308         return cfg;
2309     }
2310    
2311 });
2312
2313  
2314
2315  
2316 /*
2317 * Licence: LGPL
2318 */
2319
2320 /**
2321  * @class Roo.bootstrap.Modal
2322  * @extends Roo.bootstrap.Component
2323  * Bootstrap Modal class
2324  * @cfg {String} title Title of dialog
2325  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2326  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2327  * @cfg {Boolean} specificTitle default false
2328  * @cfg {Array} buttons Array of buttons or standard button set..
2329  * @cfg {String} buttonPosition (left|right|center) default right
2330  * @cfg {Boolean} animate default true
2331  * @cfg {Boolean} allow_close default true
2332  * 
2333  * @constructor
2334  * Create a new Modal Dialog
2335  * @param {Object} config The config object
2336  */
2337
2338 Roo.bootstrap.Modal = function(config){
2339     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2340     this.addEvents({
2341         // raw events
2342         /**
2343          * @event btnclick
2344          * The raw btnclick event for the button
2345          * @param {Roo.EventObject} e
2346          */
2347         "btnclick" : true
2348     });
2349     this.buttons = this.buttons || [];
2350      
2351     if (this.tmpl) {
2352         this.tmpl = Roo.factory(this.tmpl);
2353     }
2354     
2355 };
2356
2357 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2358     
2359     title : 'test dialog',
2360    
2361     buttons : false,
2362     
2363     // set on load...
2364      
2365     html: false,
2366     
2367     tmp: false,
2368     
2369     specificTitle: false,
2370     
2371     buttonPosition: 'right',
2372     
2373     allow_close : true,
2374     
2375     animate : true,
2376     
2377     
2378      // private
2379     bodyEl:  false,
2380     footerEl:  false,
2381     titleEl:  false,
2382     closeEl:  false,
2383     
2384     
2385     onRender : function(ct, position)
2386     {
2387         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2388      
2389         if(!this.el){
2390             var cfg = Roo.apply({},  this.getAutoCreate());
2391             cfg.id = Roo.id();
2392             //if(!cfg.name){
2393             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2394             //}
2395             //if (!cfg.name.length) {
2396             //    delete cfg.name;
2397            // }
2398             if (this.cls) {
2399                 cfg.cls += ' ' + this.cls;
2400             }
2401             if (this.style) {
2402                 cfg.style = this.style;
2403             }
2404             this.el = Roo.get(document.body).createChild(cfg, position);
2405         }
2406         //var type = this.el.dom.type;
2407         
2408         
2409         
2410         
2411         if(this.tabIndex !== undefined){
2412             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2413         }
2414         
2415         
2416         this.bodyEl = this.el.select('.modal-body',true).first();
2417         this.closeEl = this.el.select('.modal-header .close', true).first();
2418         this.footerEl = this.el.select('.modal-footer',true).first();
2419         this.titleEl = this.el.select('.modal-title',true).first();
2420         
2421         
2422          
2423         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2424         this.maskEl.enableDisplayMode("block");
2425         this.maskEl.hide();
2426         //this.el.addClass("x-dlg-modal");
2427     
2428         if (this.buttons.length) {
2429             Roo.each(this.buttons, function(bb) {
2430                 var b = Roo.apply({}, bb);
2431                 b.xns = b.xns || Roo.bootstrap;
2432                 b.xtype = b.xtype || 'Button';
2433                 if (typeof(b.listeners) == 'undefined') {
2434                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2435                 }
2436                 
2437                 var btn = Roo.factory(b);
2438                 
2439                 btn.onRender(this.el.select('.modal-footer div').first());
2440                 
2441             },this);
2442         }
2443         // render the children.
2444         var nitems = [];
2445         
2446         if(typeof(this.items) != 'undefined'){
2447             var items = this.items;
2448             delete this.items;
2449
2450             for(var i =0;i < items.length;i++) {
2451                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2452             }
2453         }
2454         
2455         this.items = nitems;
2456         
2457         // where are these used - they used to be body/close/footer
2458         
2459        
2460         this.initEvents();
2461         //this.el.addClass([this.fieldClass, this.cls]);
2462         
2463     },
2464     
2465     getAutoCreate : function(){
2466         
2467         
2468         var bdy = {
2469                 cls : 'modal-body',
2470                 html : this.html || ''
2471         };
2472         
2473         var title = {
2474             tag: 'h4',
2475             cls : 'modal-title',
2476             html : this.title
2477         };
2478         
2479         if(this.specificTitle){
2480             title = this.title;
2481             
2482         };
2483         
2484         var header = [];
2485         if (this.allow_close) {
2486             header.push({
2487                 tag: 'button',
2488                 cls : 'close',
2489                 html : '&times'
2490             });
2491         }
2492         header.push(title);
2493         
2494         var modal = {
2495             cls: "modal",
2496             style : 'display: none',
2497             cn : [
2498                 {
2499                     cls: "modal-dialog",
2500                     cn : [
2501                         {
2502                             cls : "modal-content",
2503                             cn : [
2504                                 {
2505                                     cls : 'modal-header',
2506                                     cn : header
2507                                 },
2508                                 bdy,
2509                                 {
2510                                     cls : 'modal-footer',
2511                                     cn : [
2512                                         {
2513                                             tag: 'div',
2514                                             cls: 'btn-' + this.buttonPosition
2515                                         }
2516                                     ]
2517                                     
2518                                 }
2519                                 
2520                                 
2521                             ]
2522                             
2523                         }
2524                     ]
2525                         
2526                 }
2527             ]
2528         };
2529         
2530         if(this.animate){
2531             modal.cls += ' fade';
2532         }
2533         
2534         return modal;
2535           
2536     },
2537     getChildContainer : function() {
2538          
2539          return this.bodyEl;
2540         
2541     },
2542     getButtonContainer : function() {
2543          return this.el.select('.modal-footer div',true).first();
2544         
2545     },
2546     initEvents : function()
2547     {
2548         if (this.allow_close) {
2549             this.closeEl.on('click', this.hide, this);
2550         }
2551         
2552         var _this = this;
2553         
2554         window.addEventListener("resize", function() { _this.resize(); } );
2555
2556     },
2557     
2558     resize : function()
2559     {
2560         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2561     },
2562     
2563     show : function() {
2564         
2565         if (!this.rendered) {
2566             this.render();
2567         }
2568         
2569         this.el.setStyle('display', 'block');
2570         
2571         if(this.animate){
2572             var _this = this;
2573             (function(){ _this.el.addClass('in'); }).defer(50);
2574         }else{
2575             this.el.addClass('in');
2576         }
2577         
2578         // not sure how we can show data in here.. 
2579         //if (this.tmpl) {
2580         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2581         //}
2582         
2583         Roo.get(document.body).addClass("x-body-masked");
2584         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2585         this.maskEl.show();
2586         this.el.setStyle('zIndex', '10001');
2587        
2588         this.fireEvent('show', this);
2589         
2590         
2591     },
2592     hide : function()
2593     {
2594         this.maskEl.hide();
2595         Roo.get(document.body).removeClass("x-body-masked");
2596         this.el.removeClass('in');
2597         
2598         if(this.animate){
2599             var _this = this;
2600             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2601         }else{
2602             this.el.setStyle('display', 'none');
2603         }
2604         
2605         this.fireEvent('hide', this);
2606     },
2607     
2608     addButton : function(str, cb)
2609     {
2610          
2611         
2612         var b = Roo.apply({}, { html : str } );
2613         b.xns = b.xns || Roo.bootstrap;
2614         b.xtype = b.xtype || 'Button';
2615         if (typeof(b.listeners) == 'undefined') {
2616             b.listeners = { click : cb.createDelegate(this)  };
2617         }
2618         
2619         var btn = Roo.factory(b);
2620            
2621         btn.onRender(this.el.select('.modal-footer div').first());
2622         
2623         return btn;   
2624        
2625     },
2626     
2627     setDefaultButton : function(btn)
2628     {
2629         //this.el.select('.modal-footer').()
2630     },
2631     resizeTo: function(w,h)
2632     {
2633         // skip..
2634     },
2635     setContentSize  : function(w, h)
2636     {
2637         
2638     },
2639     onButtonClick: function(btn,e)
2640     {
2641         //Roo.log([a,b,c]);
2642         this.fireEvent('btnclick', btn.name, e);
2643     },
2644      /**
2645      * Set the title of the Dialog
2646      * @param {String} str new Title
2647      */
2648     setTitle: function(str) {
2649         this.titleEl.dom.innerHTML = str;    
2650     },
2651     /**
2652      * Set the body of the Dialog
2653      * @param {String} str new Title
2654      */
2655     setBody: function(str) {
2656         this.bodyEl.dom.innerHTML = str;    
2657     },
2658     /**
2659      * Set the body of the Dialog using the template
2660      * @param {Obj} data - apply this data to the template and replace the body contents.
2661      */
2662     applyBody: function(obj)
2663     {
2664         if (!this.tmpl) {
2665             Roo.log("Error - using apply Body without a template");
2666             //code
2667         }
2668         this.tmpl.overwrite(this.bodyEl, obj);
2669     }
2670     
2671 });
2672
2673
2674 Roo.apply(Roo.bootstrap.Modal,  {
2675     /**
2676          * Button config that displays a single OK button
2677          * @type Object
2678          */
2679         OK :  [{
2680             name : 'ok',
2681             weight : 'primary',
2682             html : 'OK'
2683         }], 
2684         /**
2685          * Button config that displays Yes and No buttons
2686          * @type Object
2687          */
2688         YESNO : [
2689             {
2690                 name  : 'no',
2691                 html : 'No'
2692             },
2693             {
2694                 name  :'yes',
2695                 weight : 'primary',
2696                 html : 'Yes'
2697             }
2698         ],
2699         
2700         /**
2701          * Button config that displays OK and Cancel buttons
2702          * @type Object
2703          */
2704         OKCANCEL : [
2705             {
2706                name : 'cancel',
2707                 html : 'Cancel'
2708             },
2709             {
2710                 name : 'ok',
2711                 weight : 'primary',
2712                 html : 'OK'
2713             }
2714         ],
2715         /**
2716          * Button config that displays Yes, No and Cancel buttons
2717          * @type Object
2718          */
2719         YESNOCANCEL : [
2720             {
2721                 name : 'yes',
2722                 weight : 'primary',
2723                 html : 'Yes'
2724             },
2725             {
2726                 name : 'no',
2727                 html : 'No'
2728             },
2729             {
2730                 name : 'cancel',
2731                 html : 'Cancel'
2732             }
2733         ]
2734 });
2735  
2736  /*
2737  * - LGPL
2738  *
2739  * messagebox - can be used as a replace
2740  * 
2741  */
2742 /**
2743  * @class Roo.MessageBox
2744  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2745  * Example usage:
2746  *<pre><code>
2747 // Basic alert:
2748 Roo.Msg.alert('Status', 'Changes saved successfully.');
2749
2750 // Prompt for user data:
2751 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2752     if (btn == 'ok'){
2753         // process text value...
2754     }
2755 });
2756
2757 // Show a dialog using config options:
2758 Roo.Msg.show({
2759    title:'Save Changes?',
2760    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2761    buttons: Roo.Msg.YESNOCANCEL,
2762    fn: processResult,
2763    animEl: 'elId'
2764 });
2765 </code></pre>
2766  * @singleton
2767  */
2768 Roo.bootstrap.MessageBox = function(){
2769     var dlg, opt, mask, waitTimer;
2770     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2771     var buttons, activeTextEl, bwidth;
2772
2773     
2774     // private
2775     var handleButton = function(button){
2776         dlg.hide();
2777         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2778     };
2779
2780     // private
2781     var handleHide = function(){
2782         if(opt && opt.cls){
2783             dlg.el.removeClass(opt.cls);
2784         }
2785         //if(waitTimer){
2786         //    Roo.TaskMgr.stop(waitTimer);
2787         //    waitTimer = null;
2788         //}
2789     };
2790
2791     // private
2792     var updateButtons = function(b){
2793         var width = 0;
2794         if(!b){
2795             buttons["ok"].hide();
2796             buttons["cancel"].hide();
2797             buttons["yes"].hide();
2798             buttons["no"].hide();
2799             //dlg.footer.dom.style.display = 'none';
2800             return width;
2801         }
2802         dlg.footerEl.dom.style.display = '';
2803         for(var k in buttons){
2804             if(typeof buttons[k] != "function"){
2805                 if(b[k]){
2806                     buttons[k].show();
2807                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2808                     width += buttons[k].el.getWidth()+15;
2809                 }else{
2810                     buttons[k].hide();
2811                 }
2812             }
2813         }
2814         return width;
2815     };
2816
2817     // private
2818     var handleEsc = function(d, k, e){
2819         if(opt && opt.closable !== false){
2820             dlg.hide();
2821         }
2822         if(e){
2823             e.stopEvent();
2824         }
2825     };
2826
2827     return {
2828         /**
2829          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2830          * @return {Roo.BasicDialog} The BasicDialog element
2831          */
2832         getDialog : function(){
2833            if(!dlg){
2834                 dlg = new Roo.bootstrap.Modal( {
2835                     //draggable: true,
2836                     //resizable:false,
2837                     //constraintoviewport:false,
2838                     //fixedcenter:true,
2839                     //collapsible : false,
2840                     //shim:true,
2841                     //modal: true,
2842                   //  width:400,
2843                   //  height:100,
2844                     //buttonAlign:"center",
2845                     closeClick : function(){
2846                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2847                             handleButton("no");
2848                         }else{
2849                             handleButton("cancel");
2850                         }
2851                     }
2852                 });
2853                 dlg.render();
2854                 dlg.on("hide", handleHide);
2855                 mask = dlg.mask;
2856                 //dlg.addKeyListener(27, handleEsc);
2857                 buttons = {};
2858                 this.buttons = buttons;
2859                 var bt = this.buttonText;
2860                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2861                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2862                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2863                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2864                 Roo.log(buttons)
2865                 bodyEl = dlg.bodyEl.createChild({
2866
2867                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2868                         '<textarea class="roo-mb-textarea"></textarea>' +
2869                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2870                 });
2871                 msgEl = bodyEl.dom.firstChild;
2872                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2873                 textboxEl.enableDisplayMode();
2874                 textboxEl.addKeyListener([10,13], function(){
2875                     if(dlg.isVisible() && opt && opt.buttons){
2876                         if(opt.buttons.ok){
2877                             handleButton("ok");
2878                         }else if(opt.buttons.yes){
2879                             handleButton("yes");
2880                         }
2881                     }
2882                 });
2883                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2884                 textareaEl.enableDisplayMode();
2885                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2886                 progressEl.enableDisplayMode();
2887                 var pf = progressEl.dom.firstChild;
2888                 if (pf) {
2889                     pp = Roo.get(pf.firstChild);
2890                     pp.setHeight(pf.offsetHeight);
2891                 }
2892                 
2893             }
2894             return dlg;
2895         },
2896
2897         /**
2898          * Updates the message box body text
2899          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2900          * the XHTML-compliant non-breaking space character '&amp;#160;')
2901          * @return {Roo.MessageBox} This message box
2902          */
2903         updateText : function(text){
2904             if(!dlg.isVisible() && !opt.width){
2905                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2906             }
2907             msgEl.innerHTML = text || '&#160;';
2908       
2909             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2910             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2911             var w = Math.max(
2912                     Math.min(opt.width || cw , this.maxWidth), 
2913                     Math.max(opt.minWidth || this.minWidth, bwidth)
2914             );
2915             if(opt.prompt){
2916                 activeTextEl.setWidth(w);
2917             }
2918             if(dlg.isVisible()){
2919                 dlg.fixedcenter = false;
2920             }
2921             // to big, make it scroll. = But as usual stupid IE does not support
2922             // !important..
2923             
2924             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2925                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2926                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2927             } else {
2928                 bodyEl.dom.style.height = '';
2929                 bodyEl.dom.style.overflowY = '';
2930             }
2931             if (cw > w) {
2932                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2933             } else {
2934                 bodyEl.dom.style.overflowX = '';
2935             }
2936             
2937             dlg.setContentSize(w, bodyEl.getHeight());
2938             if(dlg.isVisible()){
2939                 dlg.fixedcenter = true;
2940             }
2941             return this;
2942         },
2943
2944         /**
2945          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2946          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2947          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2948          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2949          * @return {Roo.MessageBox} This message box
2950          */
2951         updateProgress : function(value, text){
2952             if(text){
2953                 this.updateText(text);
2954             }
2955             if (pp) { // weird bug on my firefox - for some reason this is not defined
2956                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2957             }
2958             return this;
2959         },        
2960
2961         /**
2962          * Returns true if the message box is currently displayed
2963          * @return {Boolean} True if the message box is visible, else false
2964          */
2965         isVisible : function(){
2966             return dlg && dlg.isVisible();  
2967         },
2968
2969         /**
2970          * Hides the message box if it is displayed
2971          */
2972         hide : function(){
2973             if(this.isVisible()){
2974                 dlg.hide();
2975             }  
2976         },
2977
2978         /**
2979          * Displays a new message box, or reinitializes an existing message box, based on the config options
2980          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2981          * The following config object properties are supported:
2982          * <pre>
2983 Property    Type             Description
2984 ----------  ---------------  ------------------------------------------------------------------------------------
2985 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2986                                    closes (defaults to undefined)
2987 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2988                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2989 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2990                                    progress and wait dialogs will ignore this property and always hide the
2991                                    close button as they can only be closed programmatically.
2992 cls               String           A custom CSS class to apply to the message box element
2993 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2994                                    displayed (defaults to 75)
2995 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2996                                    function will be btn (the name of the button that was clicked, if applicable,
2997                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2998                                    Progress and wait dialogs will ignore this option since they do not respond to
2999                                    user actions and can only be closed programmatically, so any required function
3000                                    should be called by the same code after it closes the dialog.
3001 icon              String           A CSS class that provides a background image to be used as an icon for
3002                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3003 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3004 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3005 modal             Boolean          False to allow user interaction with the page while the message box is
3006                                    displayed (defaults to true)
3007 msg               String           A string that will replace the existing message box body text (defaults
3008                                    to the XHTML-compliant non-breaking space character '&#160;')
3009 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3010 progress          Boolean          True to display a progress bar (defaults to false)
3011 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3012 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3013 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3014 title             String           The title text
3015 value             String           The string value to set into the active textbox element if displayed
3016 wait              Boolean          True to display a progress bar (defaults to false)
3017 width             Number           The width of the dialog in pixels
3018 </pre>
3019          *
3020          * Example usage:
3021          * <pre><code>
3022 Roo.Msg.show({
3023    title: 'Address',
3024    msg: 'Please enter your address:',
3025    width: 300,
3026    buttons: Roo.MessageBox.OKCANCEL,
3027    multiline: true,
3028    fn: saveAddress,
3029    animEl: 'addAddressBtn'
3030 });
3031 </code></pre>
3032          * @param {Object} config Configuration options
3033          * @return {Roo.MessageBox} This message box
3034          */
3035         show : function(options)
3036         {
3037             
3038             // this causes nightmares if you show one dialog after another
3039             // especially on callbacks..
3040              
3041             if(this.isVisible()){
3042                 
3043                 this.hide();
3044                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3045                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3046                 Roo.log("New Dialog Message:" +  options.msg )
3047                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3048                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3049                 
3050             }
3051             var d = this.getDialog();
3052             opt = options;
3053             d.setTitle(opt.title || "&#160;");
3054             d.closeEl.setDisplayed(opt.closable !== false);
3055             activeTextEl = textboxEl;
3056             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3057             if(opt.prompt){
3058                 if(opt.multiline){
3059                     textboxEl.hide();
3060                     textareaEl.show();
3061                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3062                         opt.multiline : this.defaultTextHeight);
3063                     activeTextEl = textareaEl;
3064                 }else{
3065                     textboxEl.show();
3066                     textareaEl.hide();
3067                 }
3068             }else{
3069                 textboxEl.hide();
3070                 textareaEl.hide();
3071             }
3072             progressEl.setDisplayed(opt.progress === true);
3073             this.updateProgress(0);
3074             activeTextEl.dom.value = opt.value || "";
3075             if(opt.prompt){
3076                 dlg.setDefaultButton(activeTextEl);
3077             }else{
3078                 var bs = opt.buttons;
3079                 var db = null;
3080                 if(bs && bs.ok){
3081                     db = buttons["ok"];
3082                 }else if(bs && bs.yes){
3083                     db = buttons["yes"];
3084                 }
3085                 dlg.setDefaultButton(db);
3086             }
3087             bwidth = updateButtons(opt.buttons);
3088             this.updateText(opt.msg);
3089             if(opt.cls){
3090                 d.el.addClass(opt.cls);
3091             }
3092             d.proxyDrag = opt.proxyDrag === true;
3093             d.modal = opt.modal !== false;
3094             d.mask = opt.modal !== false ? mask : false;
3095             if(!d.isVisible()){
3096                 // force it to the end of the z-index stack so it gets a cursor in FF
3097                 document.body.appendChild(dlg.el.dom);
3098                 d.animateTarget = null;
3099                 d.show(options.animEl);
3100             }
3101             return this;
3102         },
3103
3104         /**
3105          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3106          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3107          * and closing the message box when the process is complete.
3108          * @param {String} title The title bar text
3109          * @param {String} msg The message box body text
3110          * @return {Roo.MessageBox} This message box
3111          */
3112         progress : function(title, msg){
3113             this.show({
3114                 title : title,
3115                 msg : msg,
3116                 buttons: false,
3117                 progress:true,
3118                 closable:false,
3119                 minWidth: this.minProgressWidth,
3120                 modal : true
3121             });
3122             return this;
3123         },
3124
3125         /**
3126          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3127          * If a callback function is passed it will be called after the user clicks the button, and the
3128          * id of the button that was clicked will be passed as the only parameter to the callback
3129          * (could also be the top-right close button).
3130          * @param {String} title The title bar text
3131          * @param {String} msg The message box body text
3132          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3133          * @param {Object} scope (optional) The scope of the callback function
3134          * @return {Roo.MessageBox} This message box
3135          */
3136         alert : function(title, msg, fn, scope){
3137             this.show({
3138                 title : title,
3139                 msg : msg,
3140                 buttons: this.OK,
3141                 fn: fn,
3142                 scope : scope,
3143                 modal : true
3144             });
3145             return this;
3146         },
3147
3148         /**
3149          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3150          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3151          * You are responsible for closing the message box when the process is complete.
3152          * @param {String} msg The message box body text
3153          * @param {String} title (optional) The title bar text
3154          * @return {Roo.MessageBox} This message box
3155          */
3156         wait : function(msg, title){
3157             this.show({
3158                 title : title,
3159                 msg : msg,
3160                 buttons: false,
3161                 closable:false,
3162                 progress:true,
3163                 modal:true,
3164                 width:300,
3165                 wait:true
3166             });
3167             waitTimer = Roo.TaskMgr.start({
3168                 run: function(i){
3169                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3170                 },
3171                 interval: 1000
3172             });
3173             return this;
3174         },
3175
3176         /**
3177          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3178          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3179          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3180          * @param {String} title The title bar text
3181          * @param {String} msg The message box body text
3182          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3183          * @param {Object} scope (optional) The scope of the callback function
3184          * @return {Roo.MessageBox} This message box
3185          */
3186         confirm : function(title, msg, fn, scope){
3187             this.show({
3188                 title : title,
3189                 msg : msg,
3190                 buttons: this.YESNO,
3191                 fn: fn,
3192                 scope : scope,
3193                 modal : true
3194             });
3195             return this;
3196         },
3197
3198         /**
3199          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3200          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3201          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3202          * (could also be the top-right close button) and the text that was entered will be passed as the two
3203          * parameters to the callback.
3204          * @param {String} title The title bar text
3205          * @param {String} msg The message box body text
3206          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3207          * @param {Object} scope (optional) The scope of the callback function
3208          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3209          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3210          * @return {Roo.MessageBox} This message box
3211          */
3212         prompt : function(title, msg, fn, scope, multiline){
3213             this.show({
3214                 title : title,
3215                 msg : msg,
3216                 buttons: this.OKCANCEL,
3217                 fn: fn,
3218                 minWidth:250,
3219                 scope : scope,
3220                 prompt:true,
3221                 multiline: multiline,
3222                 modal : true
3223             });
3224             return this;
3225         },
3226
3227         /**
3228          * Button config that displays a single OK button
3229          * @type Object
3230          */
3231         OK : {ok:true},
3232         /**
3233          * Button config that displays Yes and No buttons
3234          * @type Object
3235          */
3236         YESNO : {yes:true, no:true},
3237         /**
3238          * Button config that displays OK and Cancel buttons
3239          * @type Object
3240          */
3241         OKCANCEL : {ok:true, cancel:true},
3242         /**
3243          * Button config that displays Yes, No and Cancel buttons
3244          * @type Object
3245          */
3246         YESNOCANCEL : {yes:true, no:true, cancel:true},
3247
3248         /**
3249          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3250          * @type Number
3251          */
3252         defaultTextHeight : 75,
3253         /**
3254          * The maximum width in pixels of the message box (defaults to 600)
3255          * @type Number
3256          */
3257         maxWidth : 600,
3258         /**
3259          * The minimum width in pixels of the message box (defaults to 100)
3260          * @type Number
3261          */
3262         minWidth : 100,
3263         /**
3264          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3265          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3266          * @type Number
3267          */
3268         minProgressWidth : 250,
3269         /**
3270          * An object containing the default button text strings that can be overriden for localized language support.
3271          * Supported properties are: ok, cancel, yes and no.
3272          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3273          * @type Object
3274          */
3275         buttonText : {
3276             ok : "OK",
3277             cancel : "Cancel",
3278             yes : "Yes",
3279             no : "No"
3280         }
3281     };
3282 }();
3283
3284 /**
3285  * Shorthand for {@link Roo.MessageBox}
3286  */
3287 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3288 Roo.Msg = Roo.Msg || Roo.MessageBox;
3289 /*
3290  * - LGPL
3291  *
3292  * navbar
3293  * 
3294  */
3295
3296 /**
3297  * @class Roo.bootstrap.Navbar
3298  * @extends Roo.bootstrap.Component
3299  * Bootstrap Navbar class
3300
3301  * @constructor
3302  * Create a new Navbar
3303  * @param {Object} config The config object
3304  */
3305
3306
3307 Roo.bootstrap.Navbar = function(config){
3308     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3309     
3310 };
3311
3312 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3313     
3314     
3315    
3316     // private
3317     navItems : false,
3318     loadMask : false,
3319     
3320     
3321     getAutoCreate : function(){
3322         
3323         
3324         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3325         
3326     },
3327     
3328     initEvents :function ()
3329     {
3330         //Roo.log(this.el.select('.navbar-toggle',true));
3331         this.el.select('.navbar-toggle',true).on('click', function() {
3332            // Roo.log('click');
3333             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3334         }, this);
3335         
3336         var mark = {
3337             tag: "div",
3338             cls:"x-dlg-mask"
3339         }
3340         
3341         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3342         
3343         var size = this.el.getSize();
3344         this.maskEl.setSize(size.width, size.height);
3345         this.maskEl.enableDisplayMode("block");
3346         this.maskEl.hide();
3347         
3348         if(this.loadMask){
3349             this.maskEl.show();
3350         }
3351     },
3352     
3353     
3354     getChildContainer : function()
3355     {
3356         if (this.el.select('.collapse').getCount()) {
3357             return this.el.select('.collapse',true).first();
3358         }
3359         
3360         return this.el;
3361     },
3362     
3363     mask : function()
3364     {
3365         this.maskEl.show();
3366     },
3367     
3368     unmask : function()
3369     {
3370         this.maskEl.hide();
3371     } 
3372     
3373     
3374     
3375     
3376 });
3377
3378
3379
3380  
3381
3382  /*
3383  * - LGPL
3384  *
3385  * navbar
3386  * 
3387  */
3388
3389 /**
3390  * @class Roo.bootstrap.NavSimplebar
3391  * @extends Roo.bootstrap.Navbar
3392  * Bootstrap Sidebar class
3393  *
3394  * @cfg {Boolean} inverse is inverted color
3395  * 
3396  * @cfg {String} type (nav | pills | tabs)
3397  * @cfg {Boolean} arrangement stacked | justified
3398  * @cfg {String} align (left | right) alignment
3399  * 
3400  * @cfg {Boolean} main (true|false) main nav bar? default false
3401  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3402  * 
3403  * @cfg {String} tag (header|footer|nav|div) default is nav 
3404
3405  * 
3406  * 
3407  * 
3408  * @constructor
3409  * Create a new Sidebar
3410  * @param {Object} config The config object
3411  */
3412
3413
3414 Roo.bootstrap.NavSimplebar = function(config){
3415     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3416 };
3417
3418 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3419     
3420     inverse: false,
3421     
3422     type: false,
3423     arrangement: '',
3424     align : false,
3425     
3426     
3427     
3428     main : false,
3429     
3430     
3431     tag : false,
3432     
3433     
3434     getAutoCreate : function(){
3435         
3436         
3437         var cfg = {
3438             tag : this.tag || 'div',
3439             cls : 'navbar'
3440         };
3441           
3442         
3443         cfg.cn = [
3444             {
3445                 cls: 'nav',
3446                 tag : 'ul'
3447             }
3448         ];
3449         
3450          
3451         this.type = this.type || 'nav';
3452         if (['tabs','pills'].indexOf(this.type)!==-1) {
3453             cfg.cn[0].cls += ' nav-' + this.type
3454         
3455         
3456         } else {
3457             if (this.type!=='nav') {
3458                 Roo.log('nav type must be nav/tabs/pills')
3459             }
3460             cfg.cn[0].cls += ' navbar-nav'
3461         }
3462         
3463         
3464         
3465         
3466         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3467             cfg.cn[0].cls += ' nav-' + this.arrangement;
3468         }
3469         
3470         
3471         if (this.align === 'right') {
3472             cfg.cn[0].cls += ' navbar-right';
3473         }
3474         
3475         if (this.inverse) {
3476             cfg.cls += ' navbar-inverse';
3477             
3478         }
3479         
3480         
3481         return cfg;
3482     
3483         
3484     }
3485     
3486     
3487     
3488 });
3489
3490
3491
3492  
3493
3494  
3495        /*
3496  * - LGPL
3497  *
3498  * navbar
3499  * 
3500  */
3501
3502 /**
3503  * @class Roo.bootstrap.NavHeaderbar
3504  * @extends Roo.bootstrap.NavSimplebar
3505  * Bootstrap Sidebar class
3506  *
3507  * @cfg {String} brand what is brand
3508  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3509  * @cfg {String} brand_href href of the brand
3510  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3511  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3512  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3513  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3514  * 
3515  * @constructor
3516  * Create a new Sidebar
3517  * @param {Object} config The config object
3518  */
3519
3520
3521 Roo.bootstrap.NavHeaderbar = function(config){
3522     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3523       
3524 };
3525
3526 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3527     
3528     position: '',
3529     brand: '',
3530     brand_href: false,
3531     srButton : true,
3532     autohide : false,
3533     desktopCenter : false,
3534    
3535     
3536     getAutoCreate : function(){
3537         
3538         var   cfg = {
3539             tag: this.nav || 'nav',
3540             cls: 'navbar',
3541             role: 'navigation',
3542             cn: []
3543         };
3544         
3545         var cn = cfg.cn;
3546         if (this.desktopCenter) {
3547             cn.push({cls : 'container', cn : []});
3548             cn = cn[0].cn;
3549         }
3550         
3551         if(this.srButton){
3552             cn.push({
3553                 tag: 'div',
3554                 cls: 'navbar-header',
3555                 cn: [
3556                     {
3557                         tag: 'button',
3558                         type: 'button',
3559                         cls: 'navbar-toggle',
3560                         'data-toggle': 'collapse',
3561                         cn: [
3562                             {
3563                                 tag: 'span',
3564                                 cls: 'sr-only',
3565                                 html: 'Toggle navigation'
3566                             },
3567                             {
3568                                 tag: 'span',
3569                                 cls: 'icon-bar'
3570                             },
3571                             {
3572                                 tag: 'span',
3573                                 cls: 'icon-bar'
3574                             },
3575                             {
3576                                 tag: 'span',
3577                                 cls: 'icon-bar'
3578                             }
3579                         ]
3580                     }
3581                 ]
3582             });
3583         }
3584         
3585         cn.push({
3586             tag: 'div',
3587             cls: 'collapse navbar-collapse',
3588             cn : []
3589         });
3590         
3591         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3592         
3593         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3594             cfg.cls += ' navbar-' + this.position;
3595             
3596             // tag can override this..
3597             
3598             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3599         }
3600         
3601         if (this.brand !== '') {
3602             cn[0].cn.push({
3603                 tag: 'a',
3604                 href: this.brand_href ? this.brand_href : '#',
3605                 cls: 'navbar-brand',
3606                 cn: [
3607                 this.brand
3608                 ]
3609             });
3610         }
3611         
3612         if(this.main){
3613             cfg.cls += ' main-nav';
3614         }
3615         
3616         
3617         return cfg;
3618
3619         
3620     },
3621     getHeaderChildContainer : function()
3622     {
3623         if (this.el.select('.navbar-header').getCount()) {
3624             return this.el.select('.navbar-header',true).first();
3625         }
3626         
3627         return this.getChildContainer();
3628     },
3629     
3630     
3631     initEvents : function()
3632     {
3633         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3634         
3635         if (this.autohide) {
3636             
3637             var prevScroll = 0;
3638             var ft = this.el;
3639             
3640             Roo.get(document).on('scroll',function(e) {
3641                 var ns = Roo.get(document).getScroll().top;
3642                 var os = prevScroll;
3643                 prevScroll = ns;
3644                 
3645                 if(ns > os){
3646                     ft.removeClass('slideDown');
3647                     ft.addClass('slideUp');
3648                     return;
3649                 }
3650                 ft.removeClass('slideUp');
3651                 ft.addClass('slideDown');
3652                  
3653               
3654           },this);
3655         }
3656     }    
3657     
3658 });
3659
3660
3661
3662  
3663
3664  /*
3665  * - LGPL
3666  *
3667  * navbar
3668  * 
3669  */
3670
3671 /**
3672  * @class Roo.bootstrap.NavSidebar
3673  * @extends Roo.bootstrap.Navbar
3674  * Bootstrap Sidebar class
3675  * 
3676  * @constructor
3677  * Create a new Sidebar
3678  * @param {Object} config The config object
3679  */
3680
3681
3682 Roo.bootstrap.NavSidebar = function(config){
3683     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3684 };
3685
3686 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3687     
3688     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3689     
3690     getAutoCreate : function(){
3691         
3692         
3693         return  {
3694             tag: 'div',
3695             cls: 'sidebar sidebar-nav'
3696         };
3697     
3698         
3699     }
3700     
3701     
3702     
3703 });
3704
3705
3706
3707  
3708
3709  /*
3710  * - LGPL
3711  *
3712  * nav group
3713  * 
3714  */
3715
3716 /**
3717  * @class Roo.bootstrap.NavGroup
3718  * @extends Roo.bootstrap.Component
3719  * Bootstrap NavGroup class
3720  * @cfg {String} align (left|right)
3721  * @cfg {Boolean} inverse
3722  * @cfg {String} type (nav|pills|tab) default nav
3723  * @cfg {String} navId - reference Id for navbar.
3724
3725  * 
3726  * @constructor
3727  * Create a new nav group
3728  * @param {Object} config The config object
3729  */
3730
3731 Roo.bootstrap.NavGroup = function(config){
3732     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3733     this.navItems = [];
3734    
3735     Roo.bootstrap.NavGroup.register(this);
3736      this.addEvents({
3737         /**
3738              * @event changed
3739              * Fires when the active item changes
3740              * @param {Roo.bootstrap.NavGroup} this
3741              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3742              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3743          */
3744         'changed': true
3745      });
3746     
3747 };
3748
3749 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3750     
3751     align: '',
3752     inverse: false,
3753     form: false,
3754     type: 'nav',
3755     navId : '',
3756     // private
3757     
3758     navItems : false, 
3759     
3760     getAutoCreate : function()
3761     {
3762         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3763         
3764         cfg = {
3765             tag : 'ul',
3766             cls: 'nav' 
3767         }
3768         
3769         if (['tabs','pills'].indexOf(this.type)!==-1) {
3770             cfg.cls += ' nav-' + this.type
3771         } else {
3772             if (this.type!=='nav') {
3773                 Roo.log('nav type must be nav/tabs/pills')
3774             }
3775             cfg.cls += ' navbar-nav'
3776         }
3777         
3778         if (this.parent().sidebar) {
3779             cfg = {
3780                 tag: 'ul',
3781                 cls: 'dashboard-menu sidebar-menu'
3782             }
3783             
3784             return cfg;
3785         }
3786         
3787         if (this.form === true) {
3788             cfg = {
3789                 tag: 'form',
3790                 cls: 'navbar-form'
3791             }
3792             
3793             if (this.align === 'right') {
3794                 cfg.cls += ' navbar-right';
3795             } else {
3796                 cfg.cls += ' navbar-left';
3797             }
3798         }
3799         
3800         if (this.align === 'right') {
3801             cfg.cls += ' navbar-right';
3802         }
3803         
3804         if (this.inverse) {
3805             cfg.cls += ' navbar-inverse';
3806             
3807         }
3808         
3809         
3810         return cfg;
3811     },
3812     /**
3813     * sets the active Navigation item
3814     * @param {Roo.bootstrap.NavItem} the new current navitem
3815     */
3816     setActiveItem : function(item)
3817     {
3818         var prev = false;
3819         Roo.each(this.navItems, function(v){
3820             if (v == item) {
3821                 return ;
3822             }
3823             if (v.isActive()) {
3824                 v.setActive(false, true);
3825                 prev = v;
3826                 
3827             }
3828             
3829         });
3830
3831         item.setActive(true, true);
3832         this.fireEvent('changed', this, item, prev);
3833         
3834         
3835     },
3836     /**
3837     * gets the active Navigation item
3838     * @return {Roo.bootstrap.NavItem} the current navitem
3839     */
3840     getActive : function()
3841     {
3842         
3843         var prev = false;
3844         Roo.each(this.navItems, function(v){
3845             
3846             if (v.isActive()) {
3847                 prev = v;
3848                 
3849             }
3850             
3851         });
3852         return prev;
3853     },
3854     
3855     indexOfNav : function()
3856     {
3857         
3858         var prev = false;
3859         Roo.each(this.navItems, function(v,i){
3860             
3861             if (v.isActive()) {
3862                 prev = i;
3863                 
3864             }
3865             
3866         });
3867         return prev;
3868     },
3869     /**
3870     * adds a Navigation item
3871     * @param {Roo.bootstrap.NavItem} the navitem to add
3872     */
3873     addItem : function(cfg)
3874     {
3875         var cn = new Roo.bootstrap.NavItem(cfg);
3876         this.register(cn);
3877         cn.parentId = this.id;
3878         cn.onRender(this.el, null);
3879         return cn;
3880     },
3881     /**
3882     * register a Navigation item
3883     * @param {Roo.bootstrap.NavItem} the navitem to add
3884     */
3885     register : function(item)
3886     {
3887         this.navItems.push( item);
3888         item.navId = this.navId;
3889     
3890     },
3891     
3892     /**
3893     * clear all the Navigation item
3894     */
3895    
3896     clearAll : function()
3897     {
3898         this.navItems = [];
3899         this.el.dom.innerHTML = '';
3900     },
3901     
3902     getNavItem: function(tabId)
3903     {
3904         var ret = false;
3905         Roo.each(this.navItems, function(e) {
3906             if (e.tabId == tabId) {
3907                ret =  e;
3908                return false;
3909             }
3910             return true;
3911             
3912         });
3913         return ret;
3914     },
3915     
3916     setActiveNext : function()
3917     {
3918         var i = this.indexOfNav(this.getActive());
3919         if (i > this.navItems.length) {
3920             return;
3921         }
3922         this.setActiveItem(this.navItems[i+1]);
3923     },
3924     setActivePrev : function()
3925     {
3926         var i = this.indexOfNav(this.getActive());
3927         if (i  < 1) {
3928             return;
3929         }
3930         this.setActiveItem(this.navItems[i-1]);
3931     },
3932     clearWasActive : function(except) {
3933         Roo.each(this.navItems, function(e) {
3934             if (e.tabId != except.tabId && e.was_active) {
3935                e.was_active = false;
3936                return false;
3937             }
3938             return true;
3939             
3940         });
3941     },
3942     getWasActive : function ()
3943     {
3944         var r = false;
3945         Roo.each(this.navItems, function(e) {
3946             if (e.was_active) {
3947                r = e;
3948                return false;
3949             }
3950             return true;
3951             
3952         });
3953         return r;
3954     }
3955     
3956     
3957 });
3958
3959  
3960 Roo.apply(Roo.bootstrap.NavGroup, {
3961     
3962     groups: {},
3963      /**
3964     * register a Navigation Group
3965     * @param {Roo.bootstrap.NavGroup} the navgroup to add
3966     */
3967     register : function(navgrp)
3968     {
3969         this.groups[navgrp.navId] = navgrp;
3970         
3971     },
3972     /**
3973     * fetch a Navigation Group based on the navigation ID
3974     * @param {string} the navgroup to add
3975     * @returns {Roo.bootstrap.NavGroup} the navgroup 
3976     */
3977     get: function(navId) {
3978         if (typeof(this.groups[navId]) == 'undefined') {
3979             return false;
3980             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3981         }
3982         return this.groups[navId] ;
3983     }
3984     
3985     
3986     
3987 });
3988
3989  /*
3990  * - LGPL
3991  *
3992  * row
3993  * 
3994  */
3995
3996 /**
3997  * @class Roo.bootstrap.NavItem
3998  * @extends Roo.bootstrap.Component
3999  * Bootstrap Navbar.NavItem class
4000  * @cfg {String} href  link to
4001  * @cfg {String} html content of button
4002  * @cfg {String} badge text inside badge
4003  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4004  * @cfg {String} glyphicon name of glyphicon
4005  * @cfg {String} icon name of font awesome icon
4006  * @cfg {Boolean} active Is item active
4007  * @cfg {Boolean} disabled Is item disabled
4008  
4009  * @cfg {Boolean} preventDefault (true | false) default false
4010  * @cfg {String} tabId the tab that this item activates.
4011  * @cfg {String} tagtype (a|span) render as a href or span?
4012  * @cfg {Boolean} animateRef (true|false) link to element default false  
4013   
4014  * @constructor
4015  * Create a new Navbar Item
4016  * @param {Object} config The config object
4017  */
4018 Roo.bootstrap.NavItem = function(config){
4019     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4020     this.addEvents({
4021         // raw events
4022         /**
4023          * @event click
4024          * The raw click event for the entire grid.
4025          * @param {Roo.EventObject} e
4026          */
4027         "click" : true,
4028          /**
4029             * @event changed
4030             * Fires when the active item active state changes
4031             * @param {Roo.bootstrap.NavItem} this
4032             * @param {boolean} state the new state
4033              
4034          */
4035         'changed': true,
4036         /**
4037             * @event scrollto
4038             * Fires when scroll to element
4039             * @param {Roo.bootstrap.NavItem} this
4040             * @param {Object} options
4041             * @param {Roo.EventObject} e
4042              
4043          */
4044         'scrollto': true
4045     });
4046    
4047 };
4048
4049 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4050     
4051     href: false,
4052     html: '',
4053     badge: '',
4054     icon: false,
4055     glyphicon: false,
4056     active: false,
4057     preventDefault : false,
4058     tabId : false,
4059     tagtype : 'a',
4060     disabled : false,
4061     animateRef : false,
4062     was_active : false,
4063     
4064     getAutoCreate : function(){
4065          
4066         var cfg = {
4067             tag: 'li',
4068             cls: 'nav-item'
4069             
4070         }
4071         if (this.active) {
4072             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4073         }
4074         if (this.disabled) {
4075             cfg.cls += ' disabled';
4076         }
4077         
4078         if (this.href || this.html || this.glyphicon || this.icon) {
4079             cfg.cn = [
4080                 {
4081                     tag: this.tagtype,
4082                     href : this.href || "#",
4083                     html: this.html || ''
4084                 }
4085             ];
4086             
4087             if (this.icon) {
4088                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4089             }
4090
4091             if(this.glyphicon) {
4092                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4093             }
4094             
4095             if (this.menu) {
4096                 
4097                 cfg.cn[0].html += " <span class='caret'></span>";
4098              
4099             }
4100             
4101             if (this.badge !== '') {
4102                  
4103                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4104             }
4105         }
4106         
4107         
4108         
4109         return cfg;
4110     },
4111     initEvents: function() 
4112     {
4113         if (typeof (this.menu) != 'undefined') {
4114             this.menu.parentType = this.xtype;
4115             this.menu.triggerEl = this.el;
4116             this.menu = this.addxtype(Roo.apply({}, this.menu));
4117         }
4118         
4119         this.el.select('a',true).on('click', this.onClick, this);
4120         
4121         if(this.tagtype == 'span'){
4122             this.el.select('span',true).on('click', this.onClick, this);
4123         }
4124        
4125         // at this point parent should be available..
4126         this.parent().register(this);
4127     },
4128     
4129     onClick : function(e)
4130     {
4131         if(
4132                 this.preventDefault || 
4133                 this.href == '#' 
4134         ){
4135             
4136             e.preventDefault();
4137         }
4138         
4139         if (this.disabled) {
4140             return;
4141         }
4142         
4143         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4144         if (tg && tg.transition) {
4145             Roo.log("waiting for the transitionend");
4146             return;
4147         }
4148         
4149         
4150         
4151         //Roo.log("fire event clicked");
4152         if(this.fireEvent('click', this, e) === false){
4153             return;
4154         };
4155         
4156         if(this.tagtype == 'span'){
4157             return;
4158         }
4159         
4160         //Roo.log(this.href);
4161         var ael = this.el.select('a',true).first();
4162         //Roo.log(ael);
4163         
4164         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4165             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4166             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4167                 return; // ignore... - it's a 'hash' to another page.
4168             }
4169             
4170             e.preventDefault();
4171             this.scrollToElement(e);
4172         }
4173         
4174         
4175         var p =  this.parent();
4176    
4177         if (['tabs','pills'].indexOf(p.type)!==-1) {
4178             if (typeof(p.setActiveItem) !== 'undefined') {
4179                 p.setActiveItem(this);
4180             }
4181         }
4182         
4183         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4184         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4185             // remove the collapsed menu expand...
4186             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4187         }
4188     },
4189     
4190     isActive: function () {
4191         return this.active
4192     },
4193     setActive : function(state, fire, is_was_active)
4194     {
4195         if (this.active && !state && this.navId) {
4196             this.was_active = true;
4197             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4198             if (nv) {
4199                 nv.clearWasActive(this);
4200             }
4201             
4202         }
4203         this.active = state;
4204         
4205         if (!state ) {
4206             this.el.removeClass('active');
4207         } else if (!this.el.hasClass('active')) {
4208             this.el.addClass('active');
4209         }
4210         if (fire) {
4211             this.fireEvent('changed', this, state);
4212         }
4213         
4214         // show a panel if it's registered and related..
4215         
4216         if (!this.navId || !this.tabId || !state || is_was_active) {
4217             return;
4218         }
4219         
4220         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4221         if (!tg) {
4222             return;
4223         }
4224         var pan = tg.getPanelByName(this.tabId);
4225         if (!pan) {
4226             return;
4227         }
4228         // if we can not flip to new panel - go back to old nav highlight..
4229         if (false == tg.showPanel(pan)) {
4230             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4231             if (nv) {
4232                 var onav = nv.getWasActive();
4233                 if (onav) {
4234                     onav.setActive(true, false, true);
4235                 }
4236             }
4237             
4238         }
4239         
4240         
4241         
4242     },
4243      // this should not be here...
4244     setDisabled : function(state)
4245     {
4246         this.disabled = state;
4247         if (!state ) {
4248             this.el.removeClass('disabled');
4249         } else if (!this.el.hasClass('disabled')) {
4250             this.el.addClass('disabled');
4251         }
4252         
4253     },
4254     
4255     /**
4256      * Fetch the element to display the tooltip on.
4257      * @return {Roo.Element} defaults to this.el
4258      */
4259     tooltipEl : function()
4260     {
4261         return this.el.select('' + this.tagtype + '', true).first();
4262     },
4263     
4264     scrollToElement : function(e)
4265     {
4266         var c = document.body;
4267         
4268         /*
4269          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4270          */
4271         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4272             c = document.documentElement;
4273         }
4274         
4275         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4276         
4277         if(!target){
4278             return;
4279         }
4280
4281         var o = target.calcOffsetsTo(c);
4282         
4283         var options = {
4284             target : target,
4285             value : o[1]
4286         }
4287         
4288         this.fireEvent('scrollto', this, options, e);
4289         
4290         Roo.get(c).scrollTo('top', options.value, true);
4291         
4292         return;
4293     }
4294 });
4295  
4296
4297  /*
4298  * - LGPL
4299  *
4300  * sidebar item
4301  *
4302  *  li
4303  *    <span> icon </span>
4304  *    <span> text </span>
4305  *    <span>badge </span>
4306  */
4307
4308 /**
4309  * @class Roo.bootstrap.NavSidebarItem
4310  * @extends Roo.bootstrap.NavItem
4311  * Bootstrap Navbar.NavSidebarItem class
4312  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4313  * @constructor
4314  * Create a new Navbar Button
4315  * @param {Object} config The config object
4316  */
4317 Roo.bootstrap.NavSidebarItem = function(config){
4318     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4319     this.addEvents({
4320         // raw events
4321         /**
4322          * @event click
4323          * The raw click event for the entire grid.
4324          * @param {Roo.EventObject} e
4325          */
4326         "click" : true,
4327          /**
4328             * @event changed
4329             * Fires when the active item active state changes
4330             * @param {Roo.bootstrap.NavSidebarItem} this
4331             * @param {boolean} state the new state
4332              
4333          */
4334         'changed': true
4335     });
4336    
4337 };
4338
4339 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4340     
4341     badgeWeight : 'default',
4342     
4343     getAutoCreate : function(){
4344         
4345         
4346         var a = {
4347                 tag: 'a',
4348                 href : this.href || '#',
4349                 cls: '',
4350                 html : '',
4351                 cn : []
4352         };
4353         var cfg = {
4354             tag: 'li',
4355             cls: '',
4356             cn: [ a ]
4357         }
4358         var span = {
4359             tag: 'span',
4360             html : this.html || ''
4361         }
4362         
4363         
4364         if (this.active) {
4365             cfg.cls += ' active';
4366         }
4367         
4368         if (this.disabled) {
4369             cfg.cls += ' disabled';
4370         }
4371         
4372         // left icon..
4373         if (this.glyphicon || this.icon) {
4374             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4375             a.cn.push({ tag : 'i', cls : c }) ;
4376         }
4377         // html..
4378         a.cn.push(span);
4379         // then badge..
4380         if (this.badge !== '') {
4381             
4382             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4383         }
4384         // fi
4385         if (this.menu) {
4386             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4387             a.cls += 'dropdown-toggle treeview' ;
4388             
4389         }
4390         
4391         
4392         
4393         return cfg;
4394          
4395            
4396     },
4397     
4398     initEvents : function()
4399     { 
4400         this.el.on('click', this.onClick, this);
4401         
4402         if(this.badge !== ''){
4403             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4404         }
4405         
4406     },
4407     
4408     onClick : function(e)
4409     {
4410         if(this.disabled){
4411             e.preventDefault();
4412             return;
4413         }
4414         
4415         if(this.preventDefault){
4416             e.preventDefault();
4417         }
4418         
4419         this.fireEvent('click', this);
4420     },
4421     
4422     disable : function()
4423     {
4424         this.setDisabled(true);
4425     },
4426     
4427     enable : function()
4428     {
4429         this.setDisabled(false);
4430     },
4431     
4432     setDisabled : function(state)
4433     {
4434         if(this.disabled == state){
4435             return;
4436         }
4437         
4438         this.disabled = state;
4439         
4440         if (state) {
4441             this.el.addClass('disabled');
4442             return;
4443         }
4444         
4445         this.el.removeClass('disabled');
4446         
4447         return;
4448     },
4449     
4450     setActive : function(state)
4451     {
4452         if(this.active == state){
4453             return;
4454         }
4455         
4456         this.active = state;
4457         
4458         if (state) {
4459             this.el.addClass('active');
4460             return;
4461         }
4462         
4463         this.el.removeClass('active');
4464         
4465         return;
4466     },
4467     
4468     isActive: function () 
4469     {
4470         return this.active;
4471     },
4472     
4473     setBadge : function(str)
4474     {
4475         if(!this.badgeEl){
4476             return;
4477         }
4478         
4479         this.badgeEl.dom.innerHTML = str;
4480     }
4481     
4482    
4483      
4484  
4485 });
4486  
4487
4488  /*
4489  * - LGPL
4490  *
4491  * row
4492  * 
4493  */
4494
4495 /**
4496  * @class Roo.bootstrap.Row
4497  * @extends Roo.bootstrap.Component
4498  * Bootstrap Row class (contains columns...)
4499  * 
4500  * @constructor
4501  * Create a new Row
4502  * @param {Object} config The config object
4503  */
4504
4505 Roo.bootstrap.Row = function(config){
4506     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4507 };
4508
4509 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4510     
4511     getAutoCreate : function(){
4512        return {
4513             cls: 'row clearfix'
4514        };
4515     }
4516     
4517     
4518 });
4519
4520  
4521
4522  /*
4523  * - LGPL
4524  *
4525  * element
4526  * 
4527  */
4528
4529 /**
4530  * @class Roo.bootstrap.Element
4531  * @extends Roo.bootstrap.Component
4532  * Bootstrap Element class
4533  * @cfg {String} html contents of the element
4534  * @cfg {String} tag tag of the element
4535  * @cfg {String} cls class of the element
4536  * @cfg {Boolean} preventDefault (true|false) default false
4537  * @cfg {Boolean} clickable (true|false) default false
4538  * 
4539  * @constructor
4540  * Create a new Element
4541  * @param {Object} config The config object
4542  */
4543
4544 Roo.bootstrap.Element = function(config){
4545     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4546     
4547     this.addEvents({
4548         // raw events
4549         /**
4550          * @event click
4551          * When a element is chick
4552          * @param {Roo.bootstrap.Element} this
4553          * @param {Roo.EventObject} e
4554          */
4555         "click" : true
4556     });
4557 };
4558
4559 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4560     
4561     tag: 'div',
4562     cls: '',
4563     html: '',
4564     preventDefault: false, 
4565     clickable: false,
4566     
4567     getAutoCreate : function(){
4568         
4569         var cfg = {
4570             tag: this.tag,
4571             cls: this.cls,
4572             html: this.html
4573         }
4574         
4575         return cfg;
4576     },
4577     
4578     initEvents: function() 
4579     {
4580         Roo.bootstrap.Element.superclass.initEvents.call(this);
4581         
4582         if(this.clickable){
4583             this.el.on('click', this.onClick, this);
4584         }
4585         
4586     },
4587     
4588     onClick : function(e)
4589     {
4590         if(this.preventDefault){
4591             e.preventDefault();
4592         }
4593         
4594         this.fireEvent('click', this, e);
4595     },
4596     
4597     getValue : function()
4598     {
4599         return this.el.dom.innerHTML;
4600     },
4601     
4602     setValue : function(value)
4603     {
4604         this.el.dom.innerHTML = value;
4605     }
4606    
4607 });
4608
4609  
4610
4611  /*
4612  * - LGPL
4613  *
4614  * pagination
4615  * 
4616  */
4617
4618 /**
4619  * @class Roo.bootstrap.Pagination
4620  * @extends Roo.bootstrap.Component
4621  * Bootstrap Pagination class
4622  * @cfg {String} size xs | sm | md | lg
4623  * @cfg {Boolean} inverse false | true
4624  * 
4625  * @constructor
4626  * Create a new Pagination
4627  * @param {Object} config The config object
4628  */
4629
4630 Roo.bootstrap.Pagination = function(config){
4631     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4632 };
4633
4634 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4635     
4636     cls: false,
4637     size: false,
4638     inverse: false,
4639     
4640     getAutoCreate : function(){
4641         var cfg = {
4642             tag: 'ul',
4643                 cls: 'pagination'
4644         };
4645         if (this.inverse) {
4646             cfg.cls += ' inverse';
4647         }
4648         if (this.html) {
4649             cfg.html=this.html;
4650         }
4651         if (this.cls) {
4652             cfg.cls += " " + this.cls;
4653         }
4654         return cfg;
4655     }
4656    
4657 });
4658
4659  
4660
4661  /*
4662  * - LGPL
4663  *
4664  * Pagination item
4665  * 
4666  */
4667
4668
4669 /**
4670  * @class Roo.bootstrap.PaginationItem
4671  * @extends Roo.bootstrap.Component
4672  * Bootstrap PaginationItem class
4673  * @cfg {String} html text
4674  * @cfg {String} href the link
4675  * @cfg {Boolean} preventDefault (true | false) default true
4676  * @cfg {Boolean} active (true | false) default false
4677  * @cfg {Boolean} disabled default false
4678  * 
4679  * 
4680  * @constructor
4681  * Create a new PaginationItem
4682  * @param {Object} config The config object
4683  */
4684
4685
4686 Roo.bootstrap.PaginationItem = function(config){
4687     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4688     this.addEvents({
4689         // raw events
4690         /**
4691          * @event click
4692          * The raw click event for the entire grid.
4693          * @param {Roo.EventObject} e
4694          */
4695         "click" : true
4696     });
4697 };
4698
4699 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4700     
4701     href : false,
4702     html : false,
4703     preventDefault: true,
4704     active : false,
4705     cls : false,
4706     disabled: false,
4707     
4708     getAutoCreate : function(){
4709         var cfg= {
4710             tag: 'li',
4711             cn: [
4712                 {
4713                     tag : 'a',
4714                     href : this.href ? this.href : '#',
4715                     html : this.html ? this.html : ''
4716                 }
4717             ]
4718         };
4719         
4720         if(this.cls){
4721             cfg.cls = this.cls;
4722         }
4723         
4724         if(this.disabled){
4725             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4726         }
4727         
4728         if(this.active){
4729             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4730         }
4731         
4732         return cfg;
4733     },
4734     
4735     initEvents: function() {
4736         
4737         this.el.on('click', this.onClick, this);
4738         
4739     },
4740     onClick : function(e)
4741     {
4742         Roo.log('PaginationItem on click ');
4743         if(this.preventDefault){
4744             e.preventDefault();
4745         }
4746         
4747         if(this.disabled){
4748             return;
4749         }
4750         
4751         this.fireEvent('click', this, e);
4752     }
4753    
4754 });
4755
4756  
4757
4758  /*
4759  * - LGPL
4760  *
4761  * slider
4762  * 
4763  */
4764
4765
4766 /**
4767  * @class Roo.bootstrap.Slider
4768  * @extends Roo.bootstrap.Component
4769  * Bootstrap Slider class
4770  *    
4771  * @constructor
4772  * Create a new Slider
4773  * @param {Object} config The config object
4774  */
4775
4776 Roo.bootstrap.Slider = function(config){
4777     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4778 };
4779
4780 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4781     
4782     getAutoCreate : function(){
4783         
4784         var cfg = {
4785             tag: 'div',
4786             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4787             cn: [
4788                 {
4789                     tag: 'a',
4790                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4791                 }
4792             ]
4793         }
4794         
4795         return cfg;
4796     }
4797    
4798 });
4799
4800  /*
4801  * Based on:
4802  * Ext JS Library 1.1.1
4803  * Copyright(c) 2006-2007, Ext JS, LLC.
4804  *
4805  * Originally Released Under LGPL - original licence link has changed is not relivant.
4806  *
4807  * Fork - LGPL
4808  * <script type="text/javascript">
4809  */
4810  
4811
4812 /**
4813  * @class Roo.grid.ColumnModel
4814  * @extends Roo.util.Observable
4815  * This is the default implementation of a ColumnModel used by the Grid. It defines
4816  * the columns in the grid.
4817  * <br>Usage:<br>
4818  <pre><code>
4819  var colModel = new Roo.grid.ColumnModel([
4820         {header: "Ticker", width: 60, sortable: true, locked: true},
4821         {header: "Company Name", width: 150, sortable: true},
4822         {header: "Market Cap.", width: 100, sortable: true},
4823         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4824         {header: "Employees", width: 100, sortable: true, resizable: false}
4825  ]);
4826  </code></pre>
4827  * <p>
4828  
4829  * The config options listed for this class are options which may appear in each
4830  * individual column definition.
4831  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4832  * @constructor
4833  * @param {Object} config An Array of column config objects. See this class's
4834  * config objects for details.
4835 */
4836 Roo.grid.ColumnModel = function(config){
4837         /**
4838      * The config passed into the constructor
4839      */
4840     this.config = config;
4841     this.lookup = {};
4842
4843     // if no id, create one
4844     // if the column does not have a dataIndex mapping,
4845     // map it to the order it is in the config
4846     for(var i = 0, len = config.length; i < len; i++){
4847         var c = config[i];
4848         if(typeof c.dataIndex == "undefined"){
4849             c.dataIndex = i;
4850         }
4851         if(typeof c.renderer == "string"){
4852             c.renderer = Roo.util.Format[c.renderer];
4853         }
4854         if(typeof c.id == "undefined"){
4855             c.id = Roo.id();
4856         }
4857         if(c.editor && c.editor.xtype){
4858             c.editor  = Roo.factory(c.editor, Roo.grid);
4859         }
4860         if(c.editor && c.editor.isFormField){
4861             c.editor = new Roo.grid.GridEditor(c.editor);
4862         }
4863         this.lookup[c.id] = c;
4864     }
4865
4866     /**
4867      * The width of columns which have no width specified (defaults to 100)
4868      * @type Number
4869      */
4870     this.defaultWidth = 100;
4871
4872     /**
4873      * Default sortable of columns which have no sortable specified (defaults to false)
4874      * @type Boolean
4875      */
4876     this.defaultSortable = false;
4877
4878     this.addEvents({
4879         /**
4880              * @event widthchange
4881              * Fires when the width of a column changes.
4882              * @param {ColumnModel} this
4883              * @param {Number} columnIndex The column index
4884              * @param {Number} newWidth The new width
4885              */
4886             "widthchange": true,
4887         /**
4888              * @event headerchange
4889              * Fires when the text of a header changes.
4890              * @param {ColumnModel} this
4891              * @param {Number} columnIndex The column index
4892              * @param {Number} newText The new header text
4893              */
4894             "headerchange": true,
4895         /**
4896              * @event hiddenchange
4897              * Fires when a column is hidden or "unhidden".
4898              * @param {ColumnModel} this
4899              * @param {Number} columnIndex The column index
4900              * @param {Boolean} hidden true if hidden, false otherwise
4901              */
4902             "hiddenchange": true,
4903             /**
4904          * @event columnmoved
4905          * Fires when a column is moved.
4906          * @param {ColumnModel} this
4907          * @param {Number} oldIndex
4908          * @param {Number} newIndex
4909          */
4910         "columnmoved" : true,
4911         /**
4912          * @event columlockchange
4913          * Fires when a column's locked state is changed
4914          * @param {ColumnModel} this
4915          * @param {Number} colIndex
4916          * @param {Boolean} locked true if locked
4917          */
4918         "columnlockchange" : true
4919     });
4920     Roo.grid.ColumnModel.superclass.constructor.call(this);
4921 };
4922 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4923     /**
4924      * @cfg {String} header The header text to display in the Grid view.
4925      */
4926     /**
4927      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4928      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4929      * specified, the column's index is used as an index into the Record's data Array.
4930      */
4931     /**
4932      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4933      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4934      */
4935     /**
4936      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4937      * Defaults to the value of the {@link #defaultSortable} property.
4938      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4939      */
4940     /**
4941      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4942      */
4943     /**
4944      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4945      */
4946     /**
4947      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4948      */
4949     /**
4950      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4951      */
4952     /**
4953      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4954      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4955      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4956      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4957      */
4958        /**
4959      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4960      */
4961     /**
4962      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4963      */
4964     /**
4965      * @cfg {String} cursor (Optional)
4966      */
4967     /**
4968      * @cfg {String} tooltip (Optional)
4969      */
4970     /**
4971      * Returns the id of the column at the specified index.
4972      * @param {Number} index The column index
4973      * @return {String} the id
4974      */
4975     getColumnId : function(index){
4976         return this.config[index].id;
4977     },
4978
4979     /**
4980      * Returns the column for a specified id.
4981      * @param {String} id The column id
4982      * @return {Object} the column
4983      */
4984     getColumnById : function(id){
4985         return this.lookup[id];
4986     },
4987
4988     
4989     /**
4990      * Returns the column for a specified dataIndex.
4991      * @param {String} dataIndex The column dataIndex
4992      * @return {Object|Boolean} the column or false if not found
4993      */
4994     getColumnByDataIndex: function(dataIndex){
4995         var index = this.findColumnIndex(dataIndex);
4996         return index > -1 ? this.config[index] : false;
4997     },
4998     
4999     /**
5000      * Returns the index for a specified column id.
5001      * @param {String} id The column id
5002      * @return {Number} the index, or -1 if not found
5003      */
5004     getIndexById : function(id){
5005         for(var i = 0, len = this.config.length; i < len; i++){
5006             if(this.config[i].id == id){
5007                 return i;
5008             }
5009         }
5010         return -1;
5011     },
5012     
5013     /**
5014      * Returns the index for a specified column dataIndex.
5015      * @param {String} dataIndex The column dataIndex
5016      * @return {Number} the index, or -1 if not found
5017      */
5018     
5019     findColumnIndex : function(dataIndex){
5020         for(var i = 0, len = this.config.length; i < len; i++){
5021             if(this.config[i].dataIndex == dataIndex){
5022                 return i;
5023             }
5024         }
5025         return -1;
5026     },
5027     
5028     
5029     moveColumn : function(oldIndex, newIndex){
5030         var c = this.config[oldIndex];
5031         this.config.splice(oldIndex, 1);
5032         this.config.splice(newIndex, 0, c);
5033         this.dataMap = null;
5034         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5035     },
5036
5037     isLocked : function(colIndex){
5038         return this.config[colIndex].locked === true;
5039     },
5040
5041     setLocked : function(colIndex, value, suppressEvent){
5042         if(this.isLocked(colIndex) == value){
5043             return;
5044         }
5045         this.config[colIndex].locked = value;
5046         if(!suppressEvent){
5047             this.fireEvent("columnlockchange", this, colIndex, value);
5048         }
5049     },
5050
5051     getTotalLockedWidth : function(){
5052         var totalWidth = 0;
5053         for(var i = 0; i < this.config.length; i++){
5054             if(this.isLocked(i) && !this.isHidden(i)){
5055                 this.totalWidth += this.getColumnWidth(i);
5056             }
5057         }
5058         return totalWidth;
5059     },
5060
5061     getLockedCount : function(){
5062         for(var i = 0, len = this.config.length; i < len; i++){
5063             if(!this.isLocked(i)){
5064                 return i;
5065             }
5066         }
5067     },
5068
5069     /**
5070      * Returns the number of columns.
5071      * @return {Number}
5072      */
5073     getColumnCount : function(visibleOnly){
5074         if(visibleOnly === true){
5075             var c = 0;
5076             for(var i = 0, len = this.config.length; i < len; i++){
5077                 if(!this.isHidden(i)){
5078                     c++;
5079                 }
5080             }
5081             return c;
5082         }
5083         return this.config.length;
5084     },
5085
5086     /**
5087      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5088      * @param {Function} fn
5089      * @param {Object} scope (optional)
5090      * @return {Array} result
5091      */
5092     getColumnsBy : function(fn, scope){
5093         var r = [];
5094         for(var i = 0, len = this.config.length; i < len; i++){
5095             var c = this.config[i];
5096             if(fn.call(scope||this, c, i) === true){
5097                 r[r.length] = c;
5098             }
5099         }
5100         return r;
5101     },
5102
5103     /**
5104      * Returns true if the specified column is sortable.
5105      * @param {Number} col The column index
5106      * @return {Boolean}
5107      */
5108     isSortable : function(col){
5109         if(typeof this.config[col].sortable == "undefined"){
5110             return this.defaultSortable;
5111         }
5112         return this.config[col].sortable;
5113     },
5114
5115     /**
5116      * Returns the rendering (formatting) function defined for the column.
5117      * @param {Number} col The column index.
5118      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5119      */
5120     getRenderer : function(col){
5121         if(!this.config[col].renderer){
5122             return Roo.grid.ColumnModel.defaultRenderer;
5123         }
5124         return this.config[col].renderer;
5125     },
5126
5127     /**
5128      * Sets the rendering (formatting) function for a column.
5129      * @param {Number} col The column index
5130      * @param {Function} fn The function to use to process the cell's raw data
5131      * to return HTML markup for the grid view. The render function is called with
5132      * the following parameters:<ul>
5133      * <li>Data value.</li>
5134      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5135      * <li>css A CSS style string to apply to the table cell.</li>
5136      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5137      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5138      * <li>Row index</li>
5139      * <li>Column index</li>
5140      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5141      */
5142     setRenderer : function(col, fn){
5143         this.config[col].renderer = fn;
5144     },
5145
5146     /**
5147      * Returns the width for the specified column.
5148      * @param {Number} col The column index
5149      * @return {Number}
5150      */
5151     getColumnWidth : function(col){
5152         return this.config[col].width * 1 || this.defaultWidth;
5153     },
5154
5155     /**
5156      * Sets the width for a column.
5157      * @param {Number} col The column index
5158      * @param {Number} width The new width
5159      */
5160     setColumnWidth : function(col, width, suppressEvent){
5161         this.config[col].width = width;
5162         this.totalWidth = null;
5163         if(!suppressEvent){
5164              this.fireEvent("widthchange", this, col, width);
5165         }
5166     },
5167
5168     /**
5169      * Returns the total width of all columns.
5170      * @param {Boolean} includeHidden True to include hidden column widths
5171      * @return {Number}
5172      */
5173     getTotalWidth : function(includeHidden){
5174         if(!this.totalWidth){
5175             this.totalWidth = 0;
5176             for(var i = 0, len = this.config.length; i < len; i++){
5177                 if(includeHidden || !this.isHidden(i)){
5178                     this.totalWidth += this.getColumnWidth(i);
5179                 }
5180             }
5181         }
5182         return this.totalWidth;
5183     },
5184
5185     /**
5186      * Returns the header for the specified column.
5187      * @param {Number} col The column index
5188      * @return {String}
5189      */
5190     getColumnHeader : function(col){
5191         return this.config[col].header;
5192     },
5193
5194     /**
5195      * Sets the header for a column.
5196      * @param {Number} col The column index
5197      * @param {String} header The new header
5198      */
5199     setColumnHeader : function(col, header){
5200         this.config[col].header = header;
5201         this.fireEvent("headerchange", this, col, header);
5202     },
5203
5204     /**
5205      * Returns the tooltip for the specified column.
5206      * @param {Number} col The column index
5207      * @return {String}
5208      */
5209     getColumnTooltip : function(col){
5210             return this.config[col].tooltip;
5211     },
5212     /**
5213      * Sets the tooltip for a column.
5214      * @param {Number} col The column index
5215      * @param {String} tooltip The new tooltip
5216      */
5217     setColumnTooltip : function(col, tooltip){
5218             this.config[col].tooltip = tooltip;
5219     },
5220
5221     /**
5222      * Returns the dataIndex for the specified column.
5223      * @param {Number} col The column index
5224      * @return {Number}
5225      */
5226     getDataIndex : function(col){
5227         return this.config[col].dataIndex;
5228     },
5229
5230     /**
5231      * Sets the dataIndex for a column.
5232      * @param {Number} col The column index
5233      * @param {Number} dataIndex The new dataIndex
5234      */
5235     setDataIndex : function(col, dataIndex){
5236         this.config[col].dataIndex = dataIndex;
5237     },
5238
5239     
5240     
5241     /**
5242      * Returns true if the cell is editable.
5243      * @param {Number} colIndex The column index
5244      * @param {Number} rowIndex The row index
5245      * @return {Boolean}
5246      */
5247     isCellEditable : function(colIndex, rowIndex){
5248         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5249     },
5250
5251     /**
5252      * Returns the editor defined for the cell/column.
5253      * return false or null to disable editing.
5254      * @param {Number} colIndex The column index
5255      * @param {Number} rowIndex The row index
5256      * @return {Object}
5257      */
5258     getCellEditor : function(colIndex, rowIndex){
5259         return this.config[colIndex].editor;
5260     },
5261
5262     /**
5263      * Sets if a column is editable.
5264      * @param {Number} col The column index
5265      * @param {Boolean} editable True if the column is editable
5266      */
5267     setEditable : function(col, editable){
5268         this.config[col].editable = editable;
5269     },
5270
5271
5272     /**
5273      * Returns true if the column is hidden.
5274      * @param {Number} colIndex The column index
5275      * @return {Boolean}
5276      */
5277     isHidden : function(colIndex){
5278         return this.config[colIndex].hidden;
5279     },
5280
5281
5282     /**
5283      * Returns true if the column width cannot be changed
5284      */
5285     isFixed : function(colIndex){
5286         return this.config[colIndex].fixed;
5287     },
5288
5289     /**
5290      * Returns true if the column can be resized
5291      * @return {Boolean}
5292      */
5293     isResizable : function(colIndex){
5294         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5295     },
5296     /**
5297      * Sets if a column is hidden.
5298      * @param {Number} colIndex The column index
5299      * @param {Boolean} hidden True if the column is hidden
5300      */
5301     setHidden : function(colIndex, hidden){
5302         this.config[colIndex].hidden = hidden;
5303         this.totalWidth = null;
5304         this.fireEvent("hiddenchange", this, colIndex, hidden);
5305     },
5306
5307     /**
5308      * Sets the editor for a column.
5309      * @param {Number} col The column index
5310      * @param {Object} editor The editor object
5311      */
5312     setEditor : function(col, editor){
5313         this.config[col].editor = editor;
5314     }
5315 });
5316
5317 Roo.grid.ColumnModel.defaultRenderer = function(value){
5318         if(typeof value == "string" && value.length < 1){
5319             return "&#160;";
5320         }
5321         return value;
5322 };
5323
5324 // Alias for backwards compatibility
5325 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5326 /*
5327  * Based on:
5328  * Ext JS Library 1.1.1
5329  * Copyright(c) 2006-2007, Ext JS, LLC.
5330  *
5331  * Originally Released Under LGPL - original licence link has changed is not relivant.
5332  *
5333  * Fork - LGPL
5334  * <script type="text/javascript">
5335  */
5336  
5337 /**
5338  * @class Roo.LoadMask
5339  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5340  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5341  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5342  * element's UpdateManager load indicator and will be destroyed after the initial load.
5343  * @constructor
5344  * Create a new LoadMask
5345  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5346  * @param {Object} config The config object
5347  */
5348 Roo.LoadMask = function(el, config){
5349     this.el = Roo.get(el);
5350     Roo.apply(this, config);
5351     if(this.store){
5352         this.store.on('beforeload', this.onBeforeLoad, this);
5353         this.store.on('load', this.onLoad, this);
5354         this.store.on('loadexception', this.onLoadException, this);
5355         this.removeMask = false;
5356     }else{
5357         var um = this.el.getUpdateManager();
5358         um.showLoadIndicator = false; // disable the default indicator
5359         um.on('beforeupdate', this.onBeforeLoad, this);
5360         um.on('update', this.onLoad, this);
5361         um.on('failure', this.onLoad, this);
5362         this.removeMask = true;
5363     }
5364 };
5365
5366 Roo.LoadMask.prototype = {
5367     /**
5368      * @cfg {Boolean} removeMask
5369      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5370      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5371      */
5372     /**
5373      * @cfg {String} msg
5374      * The text to display in a centered loading message box (defaults to 'Loading...')
5375      */
5376     msg : 'Loading...',
5377     /**
5378      * @cfg {String} msgCls
5379      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5380      */
5381     msgCls : 'x-mask-loading',
5382
5383     /**
5384      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5385      * @type Boolean
5386      */
5387     disabled: false,
5388
5389     /**
5390      * Disables the mask to prevent it from being displayed
5391      */
5392     disable : function(){
5393        this.disabled = true;
5394     },
5395
5396     /**
5397      * Enables the mask so that it can be displayed
5398      */
5399     enable : function(){
5400         this.disabled = false;
5401     },
5402     
5403     onLoadException : function()
5404     {
5405         Roo.log(arguments);
5406         
5407         if (typeof(arguments[3]) != 'undefined') {
5408             Roo.MessageBox.alert("Error loading",arguments[3]);
5409         } 
5410         /*
5411         try {
5412             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5413                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5414             }   
5415         } catch(e) {
5416             
5417         }
5418         */
5419     
5420         
5421         
5422         this.el.unmask(this.removeMask);
5423     },
5424     // private
5425     onLoad : function()
5426     {
5427         this.el.unmask(this.removeMask);
5428     },
5429
5430     // private
5431     onBeforeLoad : function(){
5432         if(!this.disabled){
5433             this.el.mask(this.msg, this.msgCls);
5434         }
5435     },
5436
5437     // private
5438     destroy : function(){
5439         if(this.store){
5440             this.store.un('beforeload', this.onBeforeLoad, this);
5441             this.store.un('load', this.onLoad, this);
5442             this.store.un('loadexception', this.onLoadException, this);
5443         }else{
5444             var um = this.el.getUpdateManager();
5445             um.un('beforeupdate', this.onBeforeLoad, this);
5446             um.un('update', this.onLoad, this);
5447             um.un('failure', this.onLoad, this);
5448         }
5449     }
5450 };/*
5451  * - LGPL
5452  *
5453  * table
5454  * 
5455  */
5456
5457 /**
5458  * @class Roo.bootstrap.Table
5459  * @extends Roo.bootstrap.Component
5460  * Bootstrap Table class
5461  * @cfg {String} cls table class
5462  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5463  * @cfg {String} bgcolor Specifies the background color for a table
5464  * @cfg {Number} border Specifies whether the table cells should have borders or not
5465  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5466  * @cfg {Number} cellspacing Specifies the space between cells
5467  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5468  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5469  * @cfg {String} sortable Specifies that the table should be sortable
5470  * @cfg {String} summary Specifies a summary of the content of a table
5471  * @cfg {Number} width Specifies the width of a table
5472  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5473  * 
5474  * @cfg {boolean} striped Should the rows be alternative striped
5475  * @cfg {boolean} bordered Add borders to the table
5476  * @cfg {boolean} hover Add hover highlighting
5477  * @cfg {boolean} condensed Format condensed
5478  * @cfg {boolean} responsive Format condensed
5479  * @cfg {Boolean} loadMask (true|false) default false
5480  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5481  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5482  * @cfg {Boolean} rowSelection (true|false) default false
5483  * @cfg {Boolean} cellSelection (true|false) default false
5484  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5485  
5486  * 
5487  * @constructor
5488  * Create a new Table
5489  * @param {Object} config The config object
5490  */
5491
5492 Roo.bootstrap.Table = function(config){
5493     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5494     
5495     // BC...
5496     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5497     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5498     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5499     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5500     
5501     
5502     if (this.sm) {
5503         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5504         this.sm = this.selModel;
5505         this.sm.xmodule = this.xmodule || false;
5506     }
5507     if (this.cm && typeof(this.cm.config) == 'undefined') {
5508         this.colModel = new Roo.grid.ColumnModel(this.cm);
5509         this.cm = this.colModel;
5510         this.cm.xmodule = this.xmodule || false;
5511     }
5512     if (this.store) {
5513         this.store= Roo.factory(this.store, Roo.data);
5514         this.ds = this.store;
5515         this.ds.xmodule = this.xmodule || false;
5516          
5517     }
5518     if (this.footer && this.store) {
5519         this.footer.dataSource = this.ds;
5520         this.footer = Roo.factory(this.footer);
5521     }
5522     
5523     /** @private */
5524     this.addEvents({
5525         /**
5526          * @event cellclick
5527          * Fires when a cell is clicked
5528          * @param {Roo.bootstrap.Table} this
5529          * @param {Roo.Element} el
5530          * @param {Number} rowIndex
5531          * @param {Number} columnIndex
5532          * @param {Roo.EventObject} e
5533          */
5534         "cellclick" : true,
5535         /**
5536          * @event celldblclick
5537          * Fires when a cell is double clicked
5538          * @param {Roo.bootstrap.Table} this
5539          * @param {Roo.Element} el
5540          * @param {Number} rowIndex
5541          * @param {Number} columnIndex
5542          * @param {Roo.EventObject} e
5543          */
5544         "celldblclick" : true,
5545         /**
5546          * @event rowclick
5547          * Fires when a row is clicked
5548          * @param {Roo.bootstrap.Table} this
5549          * @param {Roo.Element} el
5550          * @param {Number} rowIndex
5551          * @param {Roo.EventObject} e
5552          */
5553         "rowclick" : true,
5554         /**
5555          * @event rowdblclick
5556          * Fires when a row is double clicked
5557          * @param {Roo.bootstrap.Table} this
5558          * @param {Roo.Element} el
5559          * @param {Number} rowIndex
5560          * @param {Roo.EventObject} e
5561          */
5562         "rowdblclick" : true,
5563         /**
5564          * @event mouseover
5565          * Fires when a mouseover occur
5566          * @param {Roo.bootstrap.Table} this
5567          * @param {Roo.Element} el
5568          * @param {Number} rowIndex
5569          * @param {Number} columnIndex
5570          * @param {Roo.EventObject} e
5571          */
5572         "mouseover" : true,
5573         /**
5574          * @event mouseout
5575          * Fires when a mouseout occur
5576          * @param {Roo.bootstrap.Table} this
5577          * @param {Roo.Element} el
5578          * @param {Number} rowIndex
5579          * @param {Number} columnIndex
5580          * @param {Roo.EventObject} e
5581          */
5582         "mouseout" : true,
5583         /**
5584          * @event rowclass
5585          * Fires when a row is rendered, so you can change add a style to it.
5586          * @param {Roo.bootstrap.Table} this
5587          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5588          */
5589         'rowclass' : true,
5590           /**
5591          * @event rowsrendered
5592          * Fires when all the  rows have been rendered
5593          * @param {Roo.bootstrap.Table} this
5594          */
5595         'rowsrendered' : true
5596         
5597     });
5598 };
5599
5600 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5601     
5602     cls: false,
5603     align: false,
5604     bgcolor: false,
5605     border: false,
5606     cellpadding: false,
5607     cellspacing: false,
5608     frame: false,
5609     rules: false,
5610     sortable: false,
5611     summary: false,
5612     width: false,
5613     striped : false,
5614     bordered: false,
5615     hover:  false,
5616     condensed : false,
5617     responsive : false,
5618     sm : false,
5619     cm : false,
5620     store : false,
5621     loadMask : false,
5622     footerShow : true,
5623     headerShow : true,
5624   
5625     rowSelection : false,
5626     cellSelection : false,
5627     layout : false,
5628     
5629     // Roo.Element - the tbody
5630     mainBody: false, 
5631     
5632     getAutoCreate : function(){
5633         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5634         
5635         cfg = {
5636             tag: 'table',
5637             cls : 'table',
5638             cn : []
5639         }
5640             
5641         if (this.striped) {
5642             cfg.cls += ' table-striped';
5643         }
5644         
5645         if (this.hover) {
5646             cfg.cls += ' table-hover';
5647         }
5648         if (this.bordered) {
5649             cfg.cls += ' table-bordered';
5650         }
5651         if (this.condensed) {
5652             cfg.cls += ' table-condensed';
5653         }
5654         if (this.responsive) {
5655             cfg.cls += ' table-responsive';
5656         }
5657         
5658         if (this.cls) {
5659             cfg.cls+=  ' ' +this.cls;
5660         }
5661         
5662         // this lot should be simplifed...
5663         
5664         if (this.align) {
5665             cfg.align=this.align;
5666         }
5667         if (this.bgcolor) {
5668             cfg.bgcolor=this.bgcolor;
5669         }
5670         if (this.border) {
5671             cfg.border=this.border;
5672         }
5673         if (this.cellpadding) {
5674             cfg.cellpadding=this.cellpadding;
5675         }
5676         if (this.cellspacing) {
5677             cfg.cellspacing=this.cellspacing;
5678         }
5679         if (this.frame) {
5680             cfg.frame=this.frame;
5681         }
5682         if (this.rules) {
5683             cfg.rules=this.rules;
5684         }
5685         if (this.sortable) {
5686             cfg.sortable=this.sortable;
5687         }
5688         if (this.summary) {
5689             cfg.summary=this.summary;
5690         }
5691         if (this.width) {
5692             cfg.width=this.width;
5693         }
5694         if (this.layout) {
5695             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5696         }
5697         
5698         if(this.store || this.cm){
5699             if(this.headerShow){
5700                 cfg.cn.push(this.renderHeader());
5701             }
5702             
5703             cfg.cn.push(this.renderBody());
5704             
5705             if(this.footerShow){
5706                 cfg.cn.push(this.renderFooter());
5707             }
5708             
5709             cfg.cls+=  ' TableGrid';
5710         }
5711         
5712         return { cn : [ cfg ] };
5713     },
5714     
5715     initEvents : function()
5716     {   
5717         if(!this.store || !this.cm){
5718             return;
5719         }
5720         
5721         //Roo.log('initEvents with ds!!!!');
5722         
5723         this.mainBody = this.el.select('tbody', true).first();
5724         
5725         
5726         var _this = this;
5727         
5728         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5729             e.on('click', _this.sort, _this);
5730         });
5731         
5732         this.el.on("click", this.onClick, this);
5733         this.el.on("dblclick", this.onDblClick, this);
5734         
5735         // why is this done????? = it breaks dialogs??
5736         //this.parent().el.setStyle('position', 'relative');
5737         
5738         
5739         if (this.footer) {
5740             this.footer.parentId = this.id;
5741             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5742         }
5743         
5744         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5745         
5746         this.store.on('load', this.onLoad, this);
5747         this.store.on('beforeload', this.onBeforeLoad, this);
5748         this.store.on('update', this.onUpdate, this);
5749         this.store.on('add', this.onAdd, this);
5750         
5751     },
5752     
5753     onMouseover : function(e, el)
5754     {
5755         var cell = Roo.get(el);
5756         
5757         if(!cell){
5758             return;
5759         }
5760         
5761         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5762             cell = cell.findParent('td', false, true);
5763         }
5764         
5765         var row = cell.findParent('tr', false, true);
5766         var cellIndex = cell.dom.cellIndex;
5767         var rowIndex = row.dom.rowIndex - 1; // start from 0
5768         
5769         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5770         
5771     },
5772     
5773     onMouseout : function(e, el)
5774     {
5775         var cell = Roo.get(el);
5776         
5777         if(!cell){
5778             return;
5779         }
5780         
5781         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5782             cell = cell.findParent('td', false, true);
5783         }
5784         
5785         var row = cell.findParent('tr', false, true);
5786         var cellIndex = cell.dom.cellIndex;
5787         var rowIndex = row.dom.rowIndex - 1; // start from 0
5788         
5789         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5790         
5791     },
5792     
5793     onClick : function(e, el)
5794     {
5795         var cell = Roo.get(el);
5796         
5797         if(!cell || (!this.cellSelection && !this.rowSelection)){
5798             return;
5799         }
5800         
5801         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5802             cell = cell.findParent('td', false, true);
5803         }
5804         
5805         if(!cell || typeof(cell) == 'undefined'){
5806             return;
5807         }
5808         
5809         var row = cell.findParent('tr', false, true);
5810         
5811         if(!row || typeof(row) == 'undefined'){
5812             return;
5813         }
5814         
5815         var cellIndex = cell.dom.cellIndex;
5816         var rowIndex = this.getRowIndex(row);
5817         
5818         // why??? - should these not be based on SelectionModel?
5819         if(this.cellSelection){
5820             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5821         }
5822         
5823         if(this.rowSelection){
5824             this.fireEvent('rowclick', this, row, rowIndex, e);
5825         }
5826         
5827         
5828     },
5829     
5830     onDblClick : function(e,el)
5831     {
5832         var cell = Roo.get(el);
5833         
5834         if(!cell || (!this.CellSelection && !this.RowSelection)){
5835             return;
5836         }
5837         
5838         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5839             cell = cell.findParent('td', false, true);
5840         }
5841         
5842         if(!cell || typeof(cell) == 'undefined'){
5843             return;
5844         }
5845         
5846         var row = cell.findParent('tr', false, true);
5847         
5848         if(!row || typeof(row) == 'undefined'){
5849             return;
5850         }
5851         
5852         var cellIndex = cell.dom.cellIndex;
5853         var rowIndex = this.getRowIndex(row);
5854         
5855         if(this.CellSelection){
5856             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5857         }
5858         
5859         if(this.RowSelection){
5860             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5861         }
5862     },
5863     
5864     sort : function(e,el)
5865     {
5866         var col = Roo.get(el);
5867         
5868         if(!col.hasClass('sortable')){
5869             return;
5870         }
5871         
5872         var sort = col.attr('sort');
5873         var dir = 'ASC';
5874         
5875         if(col.hasClass('glyphicon-arrow-up')){
5876             dir = 'DESC';
5877         }
5878         
5879         this.store.sortInfo = {field : sort, direction : dir};
5880         
5881         if (this.footer) {
5882             Roo.log("calling footer first");
5883             this.footer.onClick('first');
5884         } else {
5885         
5886             this.store.load({ params : { start : 0 } });
5887         }
5888     },
5889     
5890     renderHeader : function()
5891     {
5892         var header = {
5893             tag: 'thead',
5894             cn : []
5895         };
5896         
5897         var cm = this.cm;
5898         
5899         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5900             
5901             var config = cm.config[i];
5902             
5903             var c = {
5904                 tag: 'th',
5905                 style : '',
5906                 html: cm.getColumnHeader(i)
5907             };
5908             
5909             var hh = '';
5910             
5911             if(typeof(config.lgHeader) != 'undefined'){
5912                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5913             }
5914             
5915             if(typeof(config.mdHeader) != 'undefined'){
5916                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5917             }
5918             
5919             if(typeof(config.smHeader) != 'undefined'){
5920                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5921             }
5922             
5923             if(typeof(config.xsHeader) != 'undefined'){
5924                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5925             }
5926             
5927             if(hh.length){
5928                 c.html = hh;
5929             }
5930             
5931             if(typeof(config.tooltip) != 'undefined'){
5932                 c.tooltip = config.tooltip;
5933             }
5934             
5935             if(typeof(config.colspan) != 'undefined'){
5936                 c.colspan = config.colspan;
5937             }
5938             
5939             if(typeof(config.hidden) != 'undefined' && config.hidden){
5940                 c.style += ' display:none;';
5941             }
5942             
5943             if(typeof(config.dataIndex) != 'undefined'){
5944                 c.sort = config.dataIndex;
5945             }
5946             
5947             if(typeof(config.sortable) != 'undefined' && config.sortable){
5948                 c.cls = 'sortable';
5949             }
5950             
5951             if(typeof(config.align) != 'undefined' && config.align.length){
5952                 c.style += ' text-align:' + config.align + ';';
5953             }
5954             
5955             if(typeof(config.width) != 'undefined'){
5956                 c.style += ' width:' + config.width + 'px;';
5957             }
5958             
5959             if(typeof(config.cls) != 'undefined'){
5960                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
5961             }
5962             
5963             header.cn.push(c)
5964         }
5965         
5966         return header;
5967     },
5968     
5969     renderBody : function()
5970     {
5971         var body = {
5972             tag: 'tbody',
5973             cn : [
5974                 {
5975                     tag: 'tr',
5976                     cn : [
5977                         {
5978                             tag : 'td',
5979                             colspan :  this.cm.getColumnCount()
5980                         }
5981                     ]
5982                 }
5983             ]
5984         };
5985         
5986         return body;
5987     },
5988     
5989     renderFooter : function()
5990     {
5991         var footer = {
5992             tag: 'tfoot',
5993             cn : [
5994                 {
5995                     tag: 'tr',
5996                     cn : [
5997                         {
5998                             tag : 'td',
5999                             colspan :  this.cm.getColumnCount()
6000                         }
6001                     ]
6002                 }
6003             ]
6004         };
6005         
6006         return footer;
6007     },
6008     
6009     
6010     
6011     onLoad : function()
6012     {
6013         Roo.log('ds onload');
6014         this.clear();
6015         
6016         var _this = this;
6017         var cm = this.cm;
6018         var ds = this.store;
6019         
6020         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6021             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6022             
6023             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6024                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6025             }
6026             
6027             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6028                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6029             }
6030         });
6031         
6032         var tbody =  this.mainBody;
6033               
6034         if(ds.getCount() > 0){
6035             ds.data.each(function(d,rowIndex){
6036                 var row =  this.renderRow(cm, ds, rowIndex);
6037                 
6038                 tbody.createChild(row);
6039                 
6040                 var _this = this;
6041                 
6042                 if(row.cellObjects.length){
6043                     Roo.each(row.cellObjects, function(r){
6044                         _this.renderCellObject(r);
6045                     })
6046                 }
6047                 
6048             }, this);
6049         }
6050         
6051         Roo.each(this.el.select('tbody td', true).elements, function(e){
6052             e.on('mouseover', _this.onMouseover, _this);
6053         });
6054         
6055         Roo.each(this.el.select('tbody td', true).elements, function(e){
6056             e.on('mouseout', _this.onMouseout, _this);
6057         });
6058         this.fireEvent('rowsrendered', this);
6059         //if(this.loadMask){
6060         //    this.maskEl.hide();
6061         //}
6062     },
6063     
6064     
6065     onUpdate : function(ds,record)
6066     {
6067         this.refreshRow(record);
6068     },
6069     
6070     onRemove : function(ds, record, index, isUpdate){
6071         if(isUpdate !== true){
6072             this.fireEvent("beforerowremoved", this, index, record);
6073         }
6074         var bt = this.mainBody.dom;
6075         
6076         var rows = this.el.select('tbody > tr', true).elements;
6077         
6078         if(typeof(rows[index]) != 'undefined'){
6079             bt.removeChild(rows[index].dom);
6080         }
6081         
6082 //        if(bt.rows[index]){
6083 //            bt.removeChild(bt.rows[index]);
6084 //        }
6085         
6086         if(isUpdate !== true){
6087             //this.stripeRows(index);
6088             //this.syncRowHeights(index, index);
6089             //this.layout();
6090             this.fireEvent("rowremoved", this, index, record);
6091         }
6092     },
6093     
6094     onAdd : function(ds, records, rowIndex)
6095     {
6096         //Roo.log('on Add called');
6097         // - note this does not handle multiple adding very well..
6098         var bt = this.mainBody.dom;
6099         for (var i =0 ; i < records.length;i++) {
6100             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6101             //Roo.log(records[i]);
6102             //Roo.log(this.store.getAt(rowIndex+i));
6103             this.insertRow(this.store, rowIndex + i, false);
6104             return;
6105         }
6106         
6107     },
6108     
6109     
6110     refreshRow : function(record){
6111         var ds = this.store, index;
6112         if(typeof record == 'number'){
6113             index = record;
6114             record = ds.getAt(index);
6115         }else{
6116             index = ds.indexOf(record);
6117         }
6118         this.insertRow(ds, index, true);
6119         this.onRemove(ds, record, index+1, true);
6120         //this.syncRowHeights(index, index);
6121         //this.layout();
6122         this.fireEvent("rowupdated", this, index, record);
6123     },
6124     
6125     insertRow : function(dm, rowIndex, isUpdate){
6126         
6127         if(!isUpdate){
6128             this.fireEvent("beforerowsinserted", this, rowIndex);
6129         }
6130             //var s = this.getScrollState();
6131         var row = this.renderRow(this.cm, this.store, rowIndex);
6132         // insert before rowIndex..
6133         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6134         
6135         var _this = this;
6136                 
6137         if(row.cellObjects.length){
6138             Roo.each(row.cellObjects, function(r){
6139                 _this.renderCellObject(r);
6140             })
6141         }
6142             
6143         if(!isUpdate){
6144             this.fireEvent("rowsinserted", this, rowIndex);
6145             //this.syncRowHeights(firstRow, lastRow);
6146             //this.stripeRows(firstRow);
6147             //this.layout();
6148         }
6149         
6150     },
6151     
6152     
6153     getRowDom : function(rowIndex)
6154     {
6155         var rows = this.el.select('tbody > tr', true).elements;
6156         
6157         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6158         
6159     },
6160     // returns the object tree for a tr..
6161   
6162     
6163     renderRow : function(cm, ds, rowIndex) 
6164     {
6165         
6166         var d = ds.getAt(rowIndex);
6167         
6168         var row = {
6169             tag : 'tr',
6170             cn : []
6171         };
6172             
6173         var cellObjects = [];
6174         
6175         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6176             var config = cm.config[i];
6177             
6178             var renderer = cm.getRenderer(i);
6179             var value = '';
6180             var id = false;
6181             
6182             if(typeof(renderer) !== 'undefined'){
6183                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6184             }
6185             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6186             // and are rendered into the cells after the row is rendered - using the id for the element.
6187             
6188             if(typeof(value) === 'object'){
6189                 id = Roo.id();
6190                 cellObjects.push({
6191                     container : id,
6192                     cfg : value 
6193                 })
6194             }
6195             
6196             var rowcfg = {
6197                 record: d,
6198                 rowIndex : rowIndex,
6199                 colIndex : i,
6200                 rowClass : ''
6201             }
6202
6203             this.fireEvent('rowclass', this, rowcfg);
6204             
6205             var td = {
6206                 tag: 'td',
6207                 cls : rowcfg.rowClass,
6208                 style: '',
6209                 html: (typeof(value) === 'object') ? '' : value
6210             };
6211             
6212             if (id) {
6213                 td.id = id;
6214             }
6215             
6216             if(typeof(config.colspan) != 'undefined'){
6217                 td.colspan = config.colspan;
6218             }
6219             
6220             if(typeof(config.hidden) != 'undefined' && config.hidden){
6221                 td.style += ' display:none;';
6222             }
6223             
6224             if(typeof(config.align) != 'undefined' && config.align.length){
6225                 td.style += ' text-align:' + config.align + ';';
6226             }
6227             
6228             if(typeof(config.width) != 'undefined'){
6229                 td.style += ' width:' +  config.width + 'px;';
6230             }
6231             
6232             if(typeof(config.cursor) != 'undefined'){
6233                 td.style += ' cursor:' +  config.cursor + ';';
6234             }
6235             
6236             if(typeof(config.cls) != 'undefined'){
6237                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6238             }
6239              
6240             row.cn.push(td);
6241            
6242         }
6243         
6244         row.cellObjects = cellObjects;
6245         
6246         return row;
6247           
6248     },
6249     
6250     
6251     
6252     onBeforeLoad : function()
6253     {
6254         //Roo.log('ds onBeforeLoad');
6255         
6256         //this.clear();
6257         
6258         //if(this.loadMask){
6259         //    this.maskEl.show();
6260         //}
6261     },
6262      /**
6263      * Remove all rows
6264      */
6265     clear : function()
6266     {
6267         this.el.select('tbody', true).first().dom.innerHTML = '';
6268     },
6269     /**
6270      * Show or hide a row.
6271      * @param {Number} rowIndex to show or hide
6272      * @param {Boolean} state hide
6273      */
6274     setRowVisibility : function(rowIndex, state)
6275     {
6276         var bt = this.mainBody.dom;
6277         
6278         var rows = this.el.select('tbody > tr', true).elements;
6279         
6280         if(typeof(rows[rowIndex]) == 'undefined'){
6281             return;
6282         }
6283         rows[rowIndex].dom.style.display = state ? '' : 'none';
6284     },
6285     
6286     
6287     getSelectionModel : function(){
6288         if(!this.selModel){
6289             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6290         }
6291         return this.selModel;
6292     },
6293     /*
6294      * Render the Roo.bootstrap object from renderder
6295      */
6296     renderCellObject : function(r)
6297     {
6298         var _this = this;
6299         
6300         var t = r.cfg.render(r.container);
6301         
6302         if(r.cfg.cn){
6303             Roo.each(r.cfg.cn, function(c){
6304                 var child = {
6305                     container: t.getChildContainer(),
6306                     cfg: c
6307                 }
6308                 _this.renderCellObject(child);
6309             })
6310         }
6311     },
6312     
6313     getRowIndex : function(row)
6314     {
6315         var rowIndex = -1;
6316         
6317         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6318             if(el != row){
6319                 return;
6320             }
6321             
6322             rowIndex = index;
6323         });
6324         
6325         return rowIndex;
6326     }
6327    
6328 });
6329
6330  
6331
6332  /*
6333  * - LGPL
6334  *
6335  * table cell
6336  * 
6337  */
6338
6339 /**
6340  * @class Roo.bootstrap.TableCell
6341  * @extends Roo.bootstrap.Component
6342  * Bootstrap TableCell class
6343  * @cfg {String} html cell contain text
6344  * @cfg {String} cls cell class
6345  * @cfg {String} tag cell tag (td|th) default td
6346  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6347  * @cfg {String} align Aligns the content in a cell
6348  * @cfg {String} axis Categorizes cells
6349  * @cfg {String} bgcolor Specifies the background color of a cell
6350  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6351  * @cfg {Number} colspan Specifies the number of columns a cell should span
6352  * @cfg {String} headers Specifies one or more header cells a cell is related to
6353  * @cfg {Number} height Sets the height of a cell
6354  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6355  * @cfg {Number} rowspan Sets the number of rows a cell should span
6356  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6357  * @cfg {String} valign Vertical aligns the content in a cell
6358  * @cfg {Number} width Specifies the width of a cell
6359  * 
6360  * @constructor
6361  * Create a new TableCell
6362  * @param {Object} config The config object
6363  */
6364
6365 Roo.bootstrap.TableCell = function(config){
6366     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6367 };
6368
6369 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6370     
6371     html: false,
6372     cls: false,
6373     tag: false,
6374     abbr: false,
6375     align: false,
6376     axis: false,
6377     bgcolor: false,
6378     charoff: false,
6379     colspan: false,
6380     headers: false,
6381     height: false,
6382     nowrap: false,
6383     rowspan: false,
6384     scope: false,
6385     valign: false,
6386     width: false,
6387     
6388     
6389     getAutoCreate : function(){
6390         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6391         
6392         cfg = {
6393             tag: 'td'
6394         }
6395         
6396         if(this.tag){
6397             cfg.tag = this.tag;
6398         }
6399         
6400         if (this.html) {
6401             cfg.html=this.html
6402         }
6403         if (this.cls) {
6404             cfg.cls=this.cls
6405         }
6406         if (this.abbr) {
6407             cfg.abbr=this.abbr
6408         }
6409         if (this.align) {
6410             cfg.align=this.align
6411         }
6412         if (this.axis) {
6413             cfg.axis=this.axis
6414         }
6415         if (this.bgcolor) {
6416             cfg.bgcolor=this.bgcolor
6417         }
6418         if (this.charoff) {
6419             cfg.charoff=this.charoff
6420         }
6421         if (this.colspan) {
6422             cfg.colspan=this.colspan
6423         }
6424         if (this.headers) {
6425             cfg.headers=this.headers
6426         }
6427         if (this.height) {
6428             cfg.height=this.height
6429         }
6430         if (this.nowrap) {
6431             cfg.nowrap=this.nowrap
6432         }
6433         if (this.rowspan) {
6434             cfg.rowspan=this.rowspan
6435         }
6436         if (this.scope) {
6437             cfg.scope=this.scope
6438         }
6439         if (this.valign) {
6440             cfg.valign=this.valign
6441         }
6442         if (this.width) {
6443             cfg.width=this.width
6444         }
6445         
6446         
6447         return cfg;
6448     }
6449    
6450 });
6451
6452  
6453
6454  /*
6455  * - LGPL
6456  *
6457  * table row
6458  * 
6459  */
6460
6461 /**
6462  * @class Roo.bootstrap.TableRow
6463  * @extends Roo.bootstrap.Component
6464  * Bootstrap TableRow class
6465  * @cfg {String} cls row class
6466  * @cfg {String} align Aligns the content in a table row
6467  * @cfg {String} bgcolor Specifies a background color for a table row
6468  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6469  * @cfg {String} valign Vertical aligns the content in a table row
6470  * 
6471  * @constructor
6472  * Create a new TableRow
6473  * @param {Object} config The config object
6474  */
6475
6476 Roo.bootstrap.TableRow = function(config){
6477     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6478 };
6479
6480 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6481     
6482     cls: false,
6483     align: false,
6484     bgcolor: false,
6485     charoff: false,
6486     valign: false,
6487     
6488     getAutoCreate : function(){
6489         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6490         
6491         cfg = {
6492             tag: 'tr'
6493         }
6494             
6495         if(this.cls){
6496             cfg.cls = this.cls;
6497         }
6498         if(this.align){
6499             cfg.align = this.align;
6500         }
6501         if(this.bgcolor){
6502             cfg.bgcolor = this.bgcolor;
6503         }
6504         if(this.charoff){
6505             cfg.charoff = this.charoff;
6506         }
6507         if(this.valign){
6508             cfg.valign = this.valign;
6509         }
6510         
6511         return cfg;
6512     }
6513    
6514 });
6515
6516  
6517
6518  /*
6519  * - LGPL
6520  *
6521  * table body
6522  * 
6523  */
6524
6525 /**
6526  * @class Roo.bootstrap.TableBody
6527  * @extends Roo.bootstrap.Component
6528  * Bootstrap TableBody class
6529  * @cfg {String} cls element class
6530  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6531  * @cfg {String} align Aligns the content inside the element
6532  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6533  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6534  * 
6535  * @constructor
6536  * Create a new TableBody
6537  * @param {Object} config The config object
6538  */
6539
6540 Roo.bootstrap.TableBody = function(config){
6541     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6542 };
6543
6544 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6545     
6546     cls: false,
6547     tag: false,
6548     align: false,
6549     charoff: false,
6550     valign: false,
6551     
6552     getAutoCreate : function(){
6553         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6554         
6555         cfg = {
6556             tag: 'tbody'
6557         }
6558             
6559         if (this.cls) {
6560             cfg.cls=this.cls
6561         }
6562         if(this.tag){
6563             cfg.tag = this.tag;
6564         }
6565         
6566         if(this.align){
6567             cfg.align = this.align;
6568         }
6569         if(this.charoff){
6570             cfg.charoff = this.charoff;
6571         }
6572         if(this.valign){
6573             cfg.valign = this.valign;
6574         }
6575         
6576         return cfg;
6577     }
6578     
6579     
6580 //    initEvents : function()
6581 //    {
6582 //        
6583 //        if(!this.store){
6584 //            return;
6585 //        }
6586 //        
6587 //        this.store = Roo.factory(this.store, Roo.data);
6588 //        this.store.on('load', this.onLoad, this);
6589 //        
6590 //        this.store.load();
6591 //        
6592 //    },
6593 //    
6594 //    onLoad: function () 
6595 //    {   
6596 //        this.fireEvent('load', this);
6597 //    }
6598 //    
6599 //   
6600 });
6601
6602  
6603
6604  /*
6605  * Based on:
6606  * Ext JS Library 1.1.1
6607  * Copyright(c) 2006-2007, Ext JS, LLC.
6608  *
6609  * Originally Released Under LGPL - original licence link has changed is not relivant.
6610  *
6611  * Fork - LGPL
6612  * <script type="text/javascript">
6613  */
6614
6615 // as we use this in bootstrap.
6616 Roo.namespace('Roo.form');
6617  /**
6618  * @class Roo.form.Action
6619  * Internal Class used to handle form actions
6620  * @constructor
6621  * @param {Roo.form.BasicForm} el The form element or its id
6622  * @param {Object} config Configuration options
6623  */
6624
6625  
6626  
6627 // define the action interface
6628 Roo.form.Action = function(form, options){
6629     this.form = form;
6630     this.options = options || {};
6631 };
6632 /**
6633  * Client Validation Failed
6634  * @const 
6635  */
6636 Roo.form.Action.CLIENT_INVALID = 'client';
6637 /**
6638  * Server Validation Failed
6639  * @const 
6640  */
6641 Roo.form.Action.SERVER_INVALID = 'server';
6642  /**
6643  * Connect to Server Failed
6644  * @const 
6645  */
6646 Roo.form.Action.CONNECT_FAILURE = 'connect';
6647 /**
6648  * Reading Data from Server Failed
6649  * @const 
6650  */
6651 Roo.form.Action.LOAD_FAILURE = 'load';
6652
6653 Roo.form.Action.prototype = {
6654     type : 'default',
6655     failureType : undefined,
6656     response : undefined,
6657     result : undefined,
6658
6659     // interface method
6660     run : function(options){
6661
6662     },
6663
6664     // interface method
6665     success : function(response){
6666
6667     },
6668
6669     // interface method
6670     handleResponse : function(response){
6671
6672     },
6673
6674     // default connection failure
6675     failure : function(response){
6676         
6677         this.response = response;
6678         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6679         this.form.afterAction(this, false);
6680     },
6681
6682     processResponse : function(response){
6683         this.response = response;
6684         if(!response.responseText){
6685             return true;
6686         }
6687         this.result = this.handleResponse(response);
6688         return this.result;
6689     },
6690
6691     // utility functions used internally
6692     getUrl : function(appendParams){
6693         var url = this.options.url || this.form.url || this.form.el.dom.action;
6694         if(appendParams){
6695             var p = this.getParams();
6696             if(p){
6697                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6698             }
6699         }
6700         return url;
6701     },
6702
6703     getMethod : function(){
6704         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6705     },
6706
6707     getParams : function(){
6708         var bp = this.form.baseParams;
6709         var p = this.options.params;
6710         if(p){
6711             if(typeof p == "object"){
6712                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6713             }else if(typeof p == 'string' && bp){
6714                 p += '&' + Roo.urlEncode(bp);
6715             }
6716         }else if(bp){
6717             p = Roo.urlEncode(bp);
6718         }
6719         return p;
6720     },
6721
6722     createCallback : function(){
6723         return {
6724             success: this.success,
6725             failure: this.failure,
6726             scope: this,
6727             timeout: (this.form.timeout*1000),
6728             upload: this.form.fileUpload ? this.success : undefined
6729         };
6730     }
6731 };
6732
6733 Roo.form.Action.Submit = function(form, options){
6734     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6735 };
6736
6737 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6738     type : 'submit',
6739
6740     haveProgress : false,
6741     uploadComplete : false,
6742     
6743     // uploadProgress indicator.
6744     uploadProgress : function()
6745     {
6746         if (!this.form.progressUrl) {
6747             return;
6748         }
6749         
6750         if (!this.haveProgress) {
6751             Roo.MessageBox.progress("Uploading", "Uploading");
6752         }
6753         if (this.uploadComplete) {
6754            Roo.MessageBox.hide();
6755            return;
6756         }
6757         
6758         this.haveProgress = true;
6759    
6760         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6761         
6762         var c = new Roo.data.Connection();
6763         c.request({
6764             url : this.form.progressUrl,
6765             params: {
6766                 id : uid
6767             },
6768             method: 'GET',
6769             success : function(req){
6770                //console.log(data);
6771                 var rdata = false;
6772                 var edata;
6773                 try  {
6774                    rdata = Roo.decode(req.responseText)
6775                 } catch (e) {
6776                     Roo.log("Invalid data from server..");
6777                     Roo.log(edata);
6778                     return;
6779                 }
6780                 if (!rdata || !rdata.success) {
6781                     Roo.log(rdata);
6782                     Roo.MessageBox.alert(Roo.encode(rdata));
6783                     return;
6784                 }
6785                 var data = rdata.data;
6786                 
6787                 if (this.uploadComplete) {
6788                    Roo.MessageBox.hide();
6789                    return;
6790                 }
6791                    
6792                 if (data){
6793                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6794                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6795                     );
6796                 }
6797                 this.uploadProgress.defer(2000,this);
6798             },
6799        
6800             failure: function(data) {
6801                 Roo.log('progress url failed ');
6802                 Roo.log(data);
6803             },
6804             scope : this
6805         });
6806            
6807     },
6808     
6809     
6810     run : function()
6811     {
6812         // run get Values on the form, so it syncs any secondary forms.
6813         this.form.getValues();
6814         
6815         var o = this.options;
6816         var method = this.getMethod();
6817         var isPost = method == 'POST';
6818         if(o.clientValidation === false || this.form.isValid()){
6819             
6820             if (this.form.progressUrl) {
6821                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6822                     (new Date() * 1) + '' + Math.random());
6823                     
6824             } 
6825             
6826             
6827             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6828                 form:this.form.el.dom,
6829                 url:this.getUrl(!isPost),
6830                 method: method,
6831                 params:isPost ? this.getParams() : null,
6832                 isUpload: this.form.fileUpload
6833             }));
6834             
6835             this.uploadProgress();
6836
6837         }else if (o.clientValidation !== false){ // client validation failed
6838             this.failureType = Roo.form.Action.CLIENT_INVALID;
6839             this.form.afterAction(this, false);
6840         }
6841     },
6842
6843     success : function(response)
6844     {
6845         this.uploadComplete= true;
6846         if (this.haveProgress) {
6847             Roo.MessageBox.hide();
6848         }
6849         
6850         
6851         var result = this.processResponse(response);
6852         if(result === true || result.success){
6853             this.form.afterAction(this, true);
6854             return;
6855         }
6856         if(result.errors){
6857             this.form.markInvalid(result.errors);
6858             this.failureType = Roo.form.Action.SERVER_INVALID;
6859         }
6860         this.form.afterAction(this, false);
6861     },
6862     failure : function(response)
6863     {
6864         this.uploadComplete= true;
6865         if (this.haveProgress) {
6866             Roo.MessageBox.hide();
6867         }
6868         
6869         this.response = response;
6870         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6871         this.form.afterAction(this, false);
6872     },
6873     
6874     handleResponse : function(response){
6875         if(this.form.errorReader){
6876             var rs = this.form.errorReader.read(response);
6877             var errors = [];
6878             if(rs.records){
6879                 for(var i = 0, len = rs.records.length; i < len; i++) {
6880                     var r = rs.records[i];
6881                     errors[i] = r.data;
6882                 }
6883             }
6884             if(errors.length < 1){
6885                 errors = null;
6886             }
6887             return {
6888                 success : rs.success,
6889                 errors : errors
6890             };
6891         }
6892         var ret = false;
6893         try {
6894             ret = Roo.decode(response.responseText);
6895         } catch (e) {
6896             ret = {
6897                 success: false,
6898                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6899                 errors : []
6900             };
6901         }
6902         return ret;
6903         
6904     }
6905 });
6906
6907
6908 Roo.form.Action.Load = function(form, options){
6909     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6910     this.reader = this.form.reader;
6911 };
6912
6913 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6914     type : 'load',
6915
6916     run : function(){
6917         
6918         Roo.Ajax.request(Roo.apply(
6919                 this.createCallback(), {
6920                     method:this.getMethod(),
6921                     url:this.getUrl(false),
6922                     params:this.getParams()
6923         }));
6924     },
6925
6926     success : function(response){
6927         
6928         var result = this.processResponse(response);
6929         if(result === true || !result.success || !result.data){
6930             this.failureType = Roo.form.Action.LOAD_FAILURE;
6931             this.form.afterAction(this, false);
6932             return;
6933         }
6934         this.form.clearInvalid();
6935         this.form.setValues(result.data);
6936         this.form.afterAction(this, true);
6937     },
6938
6939     handleResponse : function(response){
6940         if(this.form.reader){
6941             var rs = this.form.reader.read(response);
6942             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6943             return {
6944                 success : rs.success,
6945                 data : data
6946             };
6947         }
6948         return Roo.decode(response.responseText);
6949     }
6950 });
6951
6952 Roo.form.Action.ACTION_TYPES = {
6953     'load' : Roo.form.Action.Load,
6954     'submit' : Roo.form.Action.Submit
6955 };/*
6956  * - LGPL
6957  *
6958  * form
6959  * 
6960  */
6961
6962 /**
6963  * @class Roo.bootstrap.Form
6964  * @extends Roo.bootstrap.Component
6965  * Bootstrap Form class
6966  * @cfg {String} method  GET | POST (default POST)
6967  * @cfg {String} labelAlign top | left (default top)
6968  * @cfg {String} align left  | right - for navbars
6969  * @cfg {Boolean} loadMask load mask when submit (default true)
6970
6971  * 
6972  * @constructor
6973  * Create a new Form
6974  * @param {Object} config The config object
6975  */
6976
6977
6978 Roo.bootstrap.Form = function(config){
6979     Roo.bootstrap.Form.superclass.constructor.call(this, config);
6980     this.addEvents({
6981         /**
6982          * @event clientvalidation
6983          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6984          * @param {Form} this
6985          * @param {Boolean} valid true if the form has passed client-side validation
6986          */
6987         clientvalidation: true,
6988         /**
6989          * @event beforeaction
6990          * Fires before any action is performed. Return false to cancel the action.
6991          * @param {Form} this
6992          * @param {Action} action The action to be performed
6993          */
6994         beforeaction: true,
6995         /**
6996          * @event actionfailed
6997          * Fires when an action fails.
6998          * @param {Form} this
6999          * @param {Action} action The action that failed
7000          */
7001         actionfailed : true,
7002         /**
7003          * @event actioncomplete
7004          * Fires when an action is completed.
7005          * @param {Form} this
7006          * @param {Action} action The action that completed
7007          */
7008         actioncomplete : true
7009     });
7010     
7011 };
7012
7013 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7014       
7015      /**
7016      * @cfg {String} method
7017      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7018      */
7019     method : 'POST',
7020     /**
7021      * @cfg {String} url
7022      * The URL to use for form actions if one isn't supplied in the action options.
7023      */
7024     /**
7025      * @cfg {Boolean} fileUpload
7026      * Set to true if this form is a file upload.
7027      */
7028      
7029     /**
7030      * @cfg {Object} baseParams
7031      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7032      */
7033       
7034     /**
7035      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7036      */
7037     timeout: 30,
7038     /**
7039      * @cfg {Sting} align (left|right) for navbar forms
7040      */
7041     align : 'left',
7042
7043     // private
7044     activeAction : null,
7045  
7046     /**
7047      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7048      * element by passing it or its id or mask the form itself by passing in true.
7049      * @type Mixed
7050      */
7051     waitMsgTarget : false,
7052     
7053     loadMask : true,
7054     
7055     getAutoCreate : function(){
7056         
7057         var cfg = {
7058             tag: 'form',
7059             method : this.method || 'POST',
7060             id : this.id || Roo.id(),
7061             cls : ''
7062         }
7063         if (this.parent().xtype.match(/^Nav/)) {
7064             cfg.cls = 'navbar-form navbar-' + this.align;
7065             
7066         }
7067         
7068         if (this.labelAlign == 'left' ) {
7069             cfg.cls += ' form-horizontal';
7070         }
7071         
7072         
7073         return cfg;
7074     },
7075     initEvents : function()
7076     {
7077         this.el.on('submit', this.onSubmit, this);
7078         // this was added as random key presses on the form where triggering form submit.
7079         this.el.on('keypress', function(e) {
7080             if (e.getCharCode() != 13) {
7081                 return true;
7082             }
7083             // we might need to allow it for textareas.. and some other items.
7084             // check e.getTarget().
7085             
7086             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7087                 return true;
7088             }
7089         
7090             Roo.log("keypress blocked");
7091             
7092             e.preventDefault();
7093             return false;
7094         });
7095         
7096     },
7097     // private
7098     onSubmit : function(e){
7099         e.stopEvent();
7100     },
7101     
7102      /**
7103      * Returns true if client-side validation on the form is successful.
7104      * @return Boolean
7105      */
7106     isValid : function(){
7107         var items = this.getItems();
7108         var valid = true;
7109         items.each(function(f){
7110            if(!f.validate()){
7111                valid = false;
7112                
7113            }
7114         });
7115         return valid;
7116     },
7117     /**
7118      * Returns true if any fields in this form have changed since their original load.
7119      * @return Boolean
7120      */
7121     isDirty : function(){
7122         var dirty = false;
7123         var items = this.getItems();
7124         items.each(function(f){
7125            if(f.isDirty()){
7126                dirty = true;
7127                return false;
7128            }
7129            return true;
7130         });
7131         return dirty;
7132     },
7133      /**
7134      * Performs a predefined action (submit or load) or custom actions you define on this form.
7135      * @param {String} actionName The name of the action type
7136      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7137      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7138      * accept other config options):
7139      * <pre>
7140 Property          Type             Description
7141 ----------------  ---------------  ----------------------------------------------------------------------------------
7142 url               String           The url for the action (defaults to the form's url)
7143 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7144 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7145 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7146                                    validate the form on the client (defaults to false)
7147      * </pre>
7148      * @return {BasicForm} this
7149      */
7150     doAction : function(action, options){
7151         if(typeof action == 'string'){
7152             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7153         }
7154         if(this.fireEvent('beforeaction', this, action) !== false){
7155             this.beforeAction(action);
7156             action.run.defer(100, action);
7157         }
7158         return this;
7159     },
7160     
7161     // private
7162     beforeAction : function(action){
7163         var o = action.options;
7164         
7165         if(this.loadMask){
7166             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7167         }
7168         // not really supported yet.. ??
7169         
7170         //if(this.waitMsgTarget === true){
7171         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7172         //}else if(this.waitMsgTarget){
7173         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7174         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7175         //}else {
7176         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7177        // }
7178          
7179     },
7180
7181     // private
7182     afterAction : function(action, success){
7183         this.activeAction = null;
7184         var o = action.options;
7185         
7186         //if(this.waitMsgTarget === true){
7187             this.el.unmask();
7188         //}else if(this.waitMsgTarget){
7189         //    this.waitMsgTarget.unmask();
7190         //}else{
7191         //    Roo.MessageBox.updateProgress(1);
7192         //    Roo.MessageBox.hide();
7193        // }
7194         // 
7195         if(success){
7196             if(o.reset){
7197                 this.reset();
7198             }
7199             Roo.callback(o.success, o.scope, [this, action]);
7200             this.fireEvent('actioncomplete', this, action);
7201             
7202         }else{
7203             
7204             // failure condition..
7205             // we have a scenario where updates need confirming.
7206             // eg. if a locking scenario exists..
7207             // we look for { errors : { needs_confirm : true }} in the response.
7208             if (
7209                 (typeof(action.result) != 'undefined')  &&
7210                 (typeof(action.result.errors) != 'undefined')  &&
7211                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7212            ){
7213                 var _t = this;
7214                 Roo.log("not supported yet");
7215                  /*
7216                 
7217                 Roo.MessageBox.confirm(
7218                     "Change requires confirmation",
7219                     action.result.errorMsg,
7220                     function(r) {
7221                         if (r != 'yes') {
7222                             return;
7223                         }
7224                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7225                     }
7226                     
7227                 );
7228                 */
7229                 
7230                 
7231                 return;
7232             }
7233             
7234             Roo.callback(o.failure, o.scope, [this, action]);
7235             // show an error message if no failed handler is set..
7236             if (!this.hasListener('actionfailed')) {
7237                 Roo.log("need to add dialog support");
7238                 /*
7239                 Roo.MessageBox.alert("Error",
7240                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7241                         action.result.errorMsg :
7242                         "Saving Failed, please check your entries or try again"
7243                 );
7244                 */
7245             }
7246             
7247             this.fireEvent('actionfailed', this, action);
7248         }
7249         
7250     },
7251     /**
7252      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7253      * @param {String} id The value to search for
7254      * @return Field
7255      */
7256     findField : function(id){
7257         var items = this.getItems();
7258         var field = items.get(id);
7259         if(!field){
7260              items.each(function(f){
7261                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7262                     field = f;
7263                     return false;
7264                 }
7265                 return true;
7266             });
7267         }
7268         return field || null;
7269     },
7270      /**
7271      * Mark fields in this form invalid in bulk.
7272      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7273      * @return {BasicForm} this
7274      */
7275     markInvalid : function(errors){
7276         if(errors instanceof Array){
7277             for(var i = 0, len = errors.length; i < len; i++){
7278                 var fieldError = errors[i];
7279                 var f = this.findField(fieldError.id);
7280                 if(f){
7281                     f.markInvalid(fieldError.msg);
7282                 }
7283             }
7284         }else{
7285             var field, id;
7286             for(id in errors){
7287                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7288                     field.markInvalid(errors[id]);
7289                 }
7290             }
7291         }
7292         //Roo.each(this.childForms || [], function (f) {
7293         //    f.markInvalid(errors);
7294         //});
7295         
7296         return this;
7297     },
7298
7299     /**
7300      * Set values for fields in this form in bulk.
7301      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7302      * @return {BasicForm} this
7303      */
7304     setValues : function(values){
7305         if(values instanceof Array){ // array of objects
7306             for(var i = 0, len = values.length; i < len; i++){
7307                 var v = values[i];
7308                 var f = this.findField(v.id);
7309                 if(f){
7310                     f.setValue(v.value);
7311                     if(this.trackResetOnLoad){
7312                         f.originalValue = f.getValue();
7313                     }
7314                 }
7315             }
7316         }else{ // object hash
7317             var field, id;
7318             for(id in values){
7319                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7320                     
7321                     if (field.setFromData && 
7322                         field.valueField && 
7323                         field.displayField &&
7324                         // combos' with local stores can 
7325                         // be queried via setValue()
7326                         // to set their value..
7327                         (field.store && !field.store.isLocal)
7328                         ) {
7329                         // it's a combo
7330                         var sd = { };
7331                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7332                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7333                         field.setFromData(sd);
7334                         
7335                     } else {
7336                         field.setValue(values[id]);
7337                     }
7338                     
7339                     
7340                     if(this.trackResetOnLoad){
7341                         field.originalValue = field.getValue();
7342                     }
7343                 }
7344             }
7345         }
7346          
7347         //Roo.each(this.childForms || [], function (f) {
7348         //    f.setValues(values);
7349         //});
7350                 
7351         return this;
7352     },
7353
7354     /**
7355      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7356      * they are returned as an array.
7357      * @param {Boolean} asString
7358      * @return {Object}
7359      */
7360     getValues : function(asString){
7361         //if (this.childForms) {
7362             // copy values from the child forms
7363         //    Roo.each(this.childForms, function (f) {
7364         //        this.setValues(f.getValues());
7365         //    }, this);
7366         //}
7367         
7368         
7369         
7370         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7371         if(asString === true){
7372             return fs;
7373         }
7374         return Roo.urlDecode(fs);
7375     },
7376     
7377     /**
7378      * Returns the fields in this form as an object with key/value pairs. 
7379      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7380      * @return {Object}
7381      */
7382     getFieldValues : function(with_hidden)
7383     {
7384         var items = this.getItems();
7385         var ret = {};
7386         items.each(function(f){
7387             if (!f.getName()) {
7388                 return;
7389             }
7390             var v = f.getValue();
7391             if (f.inputType =='radio') {
7392                 if (typeof(ret[f.getName()]) == 'undefined') {
7393                     ret[f.getName()] = ''; // empty..
7394                 }
7395                 
7396                 if (!f.el.dom.checked) {
7397                     return;
7398                     
7399                 }
7400                 v = f.el.dom.value;
7401                 
7402             }
7403             
7404             // not sure if this supported any more..
7405             if ((typeof(v) == 'object') && f.getRawValue) {
7406                 v = f.getRawValue() ; // dates..
7407             }
7408             // combo boxes where name != hiddenName...
7409             if (f.name != f.getName()) {
7410                 ret[f.name] = f.getRawValue();
7411             }
7412             ret[f.getName()] = v;
7413         });
7414         
7415         return ret;
7416     },
7417
7418     /**
7419      * Clears all invalid messages in this form.
7420      * @return {BasicForm} this
7421      */
7422     clearInvalid : function(){
7423         var items = this.getItems();
7424         
7425         items.each(function(f){
7426            f.clearInvalid();
7427         });
7428         
7429         
7430         
7431         return this;
7432     },
7433
7434     /**
7435      * Resets this form.
7436      * @return {BasicForm} this
7437      */
7438     reset : function(){
7439         var items = this.getItems();
7440         items.each(function(f){
7441             f.reset();
7442         });
7443         
7444         Roo.each(this.childForms || [], function (f) {
7445             f.reset();
7446         });
7447        
7448         
7449         return this;
7450     },
7451     getItems : function()
7452     {
7453         var r=new Roo.util.MixedCollection(false, function(o){
7454             return o.id || (o.id = Roo.id());
7455         });
7456         var iter = function(el) {
7457             if (el.inputEl) {
7458                 r.add(el);
7459             }
7460             if (!el.items) {
7461                 return;
7462             }
7463             Roo.each(el.items,function(e) {
7464                 iter(e);
7465             });
7466             
7467             
7468         };
7469         
7470         iter(this);
7471         return r;
7472         
7473         
7474         
7475         
7476     }
7477     
7478 });
7479
7480  
7481 /*
7482  * Based on:
7483  * Ext JS Library 1.1.1
7484  * Copyright(c) 2006-2007, Ext JS, LLC.
7485  *
7486  * Originally Released Under LGPL - original licence link has changed is not relivant.
7487  *
7488  * Fork - LGPL
7489  * <script type="text/javascript">
7490  */
7491 /**
7492  * @class Roo.form.VTypes
7493  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7494  * @singleton
7495  */
7496 Roo.form.VTypes = function(){
7497     // closure these in so they are only created once.
7498     var alpha = /^[a-zA-Z_]+$/;
7499     var alphanum = /^[a-zA-Z0-9_]+$/;
7500     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
7501     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7502
7503     // All these messages and functions are configurable
7504     return {
7505         /**
7506          * The function used to validate email addresses
7507          * @param {String} value The email address
7508          */
7509         'email' : function(v){
7510             return email.test(v);
7511         },
7512         /**
7513          * The error text to display when the email validation function returns false
7514          * @type String
7515          */
7516         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7517         /**
7518          * The keystroke filter mask to be applied on email input
7519          * @type RegExp
7520          */
7521         'emailMask' : /[a-z0-9_\.\-@]/i,
7522
7523         /**
7524          * The function used to validate URLs
7525          * @param {String} value The URL
7526          */
7527         'url' : function(v){
7528             return url.test(v);
7529         },
7530         /**
7531          * The error text to display when the url validation function returns false
7532          * @type String
7533          */
7534         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7535         
7536         /**
7537          * The function used to validate alpha values
7538          * @param {String} value The value
7539          */
7540         'alpha' : function(v){
7541             return alpha.test(v);
7542         },
7543         /**
7544          * The error text to display when the alpha validation function returns false
7545          * @type String
7546          */
7547         'alphaText' : 'This field should only contain letters and _',
7548         /**
7549          * The keystroke filter mask to be applied on alpha input
7550          * @type RegExp
7551          */
7552         'alphaMask' : /[a-z_]/i,
7553
7554         /**
7555          * The function used to validate alphanumeric values
7556          * @param {String} value The value
7557          */
7558         'alphanum' : function(v){
7559             return alphanum.test(v);
7560         },
7561         /**
7562          * The error text to display when the alphanumeric validation function returns false
7563          * @type String
7564          */
7565         'alphanumText' : 'This field should only contain letters, numbers and _',
7566         /**
7567          * The keystroke filter mask to be applied on alphanumeric input
7568          * @type RegExp
7569          */
7570         'alphanumMask' : /[a-z0-9_]/i
7571     };
7572 }();/*
7573  * - LGPL
7574  *
7575  * Input
7576  * 
7577  */
7578
7579 /**
7580  * @class Roo.bootstrap.Input
7581  * @extends Roo.bootstrap.Component
7582  * Bootstrap Input class
7583  * @cfg {Boolean} disabled is it disabled
7584  * @cfg {String} fieldLabel - the label associated
7585  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7586  * @cfg {String} name name of the input
7587  * @cfg {string} fieldLabel - the label associated
7588  * @cfg {string}  inputType - input / file submit ...
7589  * @cfg {string} placeholder - placeholder to put in text.
7590  * @cfg {string}  before - input group add on before
7591  * @cfg {string} after - input group add on after
7592  * @cfg {string} size - (lg|sm) or leave empty..
7593  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7594  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7595  * @cfg {Number} md colspan out of 12 for computer-sized screens
7596  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7597  * @cfg {string} value default value of the input
7598  * @cfg {Number} labelWidth set the width of label (0-12)
7599  * @cfg {String} labelAlign (top|left)
7600  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7601  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7602
7603  * @cfg {String} align (left|center|right) Default left
7604  * @cfg {Boolean} forceFeedback (true|false) Default false
7605  * 
7606  * 
7607  * 
7608  * 
7609  * @constructor
7610  * Create a new Input
7611  * @param {Object} config The config object
7612  */
7613
7614 Roo.bootstrap.Input = function(config){
7615     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7616    
7617         this.addEvents({
7618             /**
7619              * @event focus
7620              * Fires when this field receives input focus.
7621              * @param {Roo.form.Field} this
7622              */
7623             focus : true,
7624             /**
7625              * @event blur
7626              * Fires when this field loses input focus.
7627              * @param {Roo.form.Field} this
7628              */
7629             blur : true,
7630             /**
7631              * @event specialkey
7632              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7633              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7634              * @param {Roo.form.Field} this
7635              * @param {Roo.EventObject} e The event object
7636              */
7637             specialkey : true,
7638             /**
7639              * @event change
7640              * Fires just before the field blurs if the field value has changed.
7641              * @param {Roo.form.Field} this
7642              * @param {Mixed} newValue The new value
7643              * @param {Mixed} oldValue The original value
7644              */
7645             change : true,
7646             /**
7647              * @event invalid
7648              * Fires after the field has been marked as invalid.
7649              * @param {Roo.form.Field} this
7650              * @param {String} msg The validation message
7651              */
7652             invalid : true,
7653             /**
7654              * @event valid
7655              * Fires after the field has been validated with no errors.
7656              * @param {Roo.form.Field} this
7657              */
7658             valid : true,
7659              /**
7660              * @event keyup
7661              * Fires after the key up
7662              * @param {Roo.form.Field} this
7663              * @param {Roo.EventObject}  e The event Object
7664              */
7665             keyup : true
7666         });
7667 };
7668
7669 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7670      /**
7671      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7672       automatic validation (defaults to "keyup").
7673      */
7674     validationEvent : "keyup",
7675      /**
7676      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7677      */
7678     validateOnBlur : true,
7679     /**
7680      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7681      */
7682     validationDelay : 250,
7683      /**
7684      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7685      */
7686     focusClass : "x-form-focus",  // not needed???
7687     
7688        
7689     /**
7690      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7691      */
7692     invalidClass : "has-warning",
7693     
7694     /**
7695      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7696      */
7697     validClass : "has-success",
7698     
7699     /**
7700      * @cfg {Boolean} hasFeedback (true|false) default true
7701      */
7702     hasFeedback : true,
7703     
7704     /**
7705      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7706      */
7707     invalidFeedbackClass : "glyphicon-warning-sign",
7708     
7709     /**
7710      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7711      */
7712     validFeedbackClass : "glyphicon-ok",
7713     
7714     /**
7715      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7716      */
7717     selectOnFocus : false,
7718     
7719      /**
7720      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7721      */
7722     maskRe : null,
7723        /**
7724      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7725      */
7726     vtype : null,
7727     
7728       /**
7729      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7730      */
7731     disableKeyFilter : false,
7732     
7733        /**
7734      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7735      */
7736     disabled : false,
7737      /**
7738      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7739      */
7740     allowBlank : true,
7741     /**
7742      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7743      */
7744     blankText : "This field is required",
7745     
7746      /**
7747      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7748      */
7749     minLength : 0,
7750     /**
7751      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7752      */
7753     maxLength : Number.MAX_VALUE,
7754     /**
7755      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7756      */
7757     minLengthText : "The minimum length for this field is {0}",
7758     /**
7759      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7760      */
7761     maxLengthText : "The maximum length for this field is {0}",
7762   
7763     
7764     /**
7765      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7766      * If available, this function will be called only after the basic validators all return true, and will be passed the
7767      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7768      */
7769     validator : null,
7770     /**
7771      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7772      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7773      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7774      */
7775     regex : null,
7776     /**
7777      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7778      */
7779     regexText : "",
7780     
7781     autocomplete: false,
7782     
7783     
7784     fieldLabel : '',
7785     inputType : 'text',
7786     
7787     name : false,
7788     placeholder: false,
7789     before : false,
7790     after : false,
7791     size : false,
7792     hasFocus : false,
7793     preventMark: false,
7794     isFormField : true,
7795     value : '',
7796     labelWidth : 2,
7797     labelAlign : false,
7798     readOnly : false,
7799     align : false,
7800     formatedValue : false,
7801     forceFeedback : false,
7802     
7803     parentLabelAlign : function()
7804     {
7805         var parent = this;
7806         while (parent.parent()) {
7807             parent = parent.parent();
7808             if (typeof(parent.labelAlign) !='undefined') {
7809                 return parent.labelAlign;
7810             }
7811         }
7812         return 'left';
7813         
7814     },
7815     
7816     getAutoCreate : function(){
7817         
7818         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7819         
7820         var id = Roo.id();
7821         
7822         var cfg = {};
7823         
7824         if(this.inputType != 'hidden'){
7825             cfg.cls = 'form-group' //input-group
7826         }
7827         
7828         var input =  {
7829             tag: 'input',
7830             id : id,
7831             type : this.inputType,
7832             value : this.value,
7833             cls : 'form-control',
7834             placeholder : this.placeholder || '',
7835             autocomplete : this.autocomplete || 'new-password'
7836         };
7837         
7838         
7839         if(this.align){
7840             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7841         }
7842         
7843         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7844             input.maxLength = this.maxLength;
7845         }
7846         
7847         if (this.disabled) {
7848             input.disabled=true;
7849         }
7850         
7851         if (this.readOnly) {
7852             input.readonly=true;
7853         }
7854         
7855         if (this.name) {
7856             input.name = this.name;
7857         }
7858         if (this.size) {
7859             input.cls += ' input-' + this.size;
7860         }
7861         var settings=this;
7862         ['xs','sm','md','lg'].map(function(size){
7863             if (settings[size]) {
7864                 cfg.cls += ' col-' + size + '-' + settings[size];
7865             }
7866         });
7867         
7868         var inputblock = input;
7869         
7870         var feedback = {
7871             tag: 'span',
7872             cls: 'glyphicon form-control-feedback'
7873         };
7874             
7875         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7876             
7877             inputblock = {
7878                 cls : 'has-feedback',
7879                 cn :  [
7880                     input,
7881                     feedback
7882                 ] 
7883             };  
7884         }
7885         
7886         if (this.before || this.after) {
7887             
7888             inputblock = {
7889                 cls : 'input-group',
7890                 cn :  [] 
7891             };
7892             
7893             if (this.before && typeof(this.before) == 'string') {
7894                 
7895                 inputblock.cn.push({
7896                     tag :'span',
7897                     cls : 'roo-input-before input-group-addon',
7898                     html : this.before
7899                 });
7900             }
7901             if (this.before && typeof(this.before) == 'object') {
7902                 this.before = Roo.factory(this.before);
7903                 Roo.log(this.before);
7904                 inputblock.cn.push({
7905                     tag :'span',
7906                     cls : 'roo-input-before input-group-' +
7907                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7908                 });
7909             }
7910             
7911             inputblock.cn.push(input);
7912             
7913             if (this.after && typeof(this.after) == 'string') {
7914                 inputblock.cn.push({
7915                     tag :'span',
7916                     cls : 'roo-input-after input-group-addon',
7917                     html : this.after
7918                 });
7919             }
7920             if (this.after && typeof(this.after) == 'object') {
7921                 this.after = Roo.factory(this.after);
7922                 Roo.log(this.after);
7923                 inputblock.cn.push({
7924                     tag :'span',
7925                     cls : 'roo-input-after input-group-' +
7926                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7927                 });
7928             }
7929             
7930             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7931                 inputblock.cls += ' has-feedback';
7932                 inputblock.cn.push(feedback);
7933             }
7934         };
7935         
7936         if (align ==='left' && this.fieldLabel.length) {
7937                 Roo.log("left and has label");
7938                 cfg.cn = [
7939                     
7940                     {
7941                         tag: 'label',
7942                         'for' :  id,
7943                         cls : 'control-label col-sm-' + this.labelWidth,
7944                         html : this.fieldLabel
7945                         
7946                     },
7947                     {
7948                         cls : "col-sm-" + (12 - this.labelWidth), 
7949                         cn: [
7950                             inputblock
7951                         ]
7952                     }
7953                     
7954                 ];
7955         } else if ( this.fieldLabel.length) {
7956                 Roo.log(" label");
7957                  cfg.cn = [
7958                    
7959                     {
7960                         tag: 'label',
7961                         //cls : 'input-group-addon',
7962                         html : this.fieldLabel
7963                         
7964                     },
7965                     
7966                     inputblock
7967                     
7968                 ];
7969
7970         } else {
7971             
7972                 Roo.log(" no label && no align");
7973                 cfg.cn = [
7974                     
7975                         inputblock
7976                     
7977                 ];
7978                 
7979                 
7980         };
7981         Roo.log('input-parentType: ' + this.parentType);
7982         
7983         if (this.parentType === 'Navbar' &&  this.parent().bar) {
7984            cfg.cls += ' navbar-form';
7985            Roo.log(cfg);
7986         }
7987         
7988         return cfg;
7989         
7990     },
7991     /**
7992      * return the real input element.
7993      */
7994     inputEl: function ()
7995     {
7996         return this.el.select('input.form-control',true).first();
7997     },
7998     
7999     tooltipEl : function()
8000     {
8001         return this.inputEl();
8002     },
8003     
8004     setDisabled : function(v)
8005     {
8006         var i  = this.inputEl().dom;
8007         if (!v) {
8008             i.removeAttribute('disabled');
8009             return;
8010             
8011         }
8012         i.setAttribute('disabled','true');
8013     },
8014     initEvents : function()
8015     {
8016           
8017         this.inputEl().on("keydown" , this.fireKey,  this);
8018         this.inputEl().on("focus", this.onFocus,  this);
8019         this.inputEl().on("blur", this.onBlur,  this);
8020         
8021         this.inputEl().relayEvent('keyup', this);
8022  
8023         // reference to original value for reset
8024         this.originalValue = this.getValue();
8025         //Roo.form.TextField.superclass.initEvents.call(this);
8026         if(this.validationEvent == 'keyup'){
8027             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8028             this.inputEl().on('keyup', this.filterValidation, this);
8029         }
8030         else if(this.validationEvent !== false){
8031             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8032         }
8033         
8034         if(this.selectOnFocus){
8035             this.on("focus", this.preFocus, this);
8036             
8037         }
8038         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8039             this.inputEl().on("keypress", this.filterKeys, this);
8040         }
8041        /* if(this.grow){
8042             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8043             this.el.on("click", this.autoSize,  this);
8044         }
8045         */
8046         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8047             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8048         }
8049         
8050         if (typeof(this.before) == 'object') {
8051             this.before.render(this.el.select('.roo-input-before',true).first());
8052         }
8053         if (typeof(this.after) == 'object') {
8054             this.after.render(this.el.select('.roo-input-after',true).first());
8055         }
8056         
8057         
8058     },
8059     filterValidation : function(e){
8060         if(!e.isNavKeyPress()){
8061             this.validationTask.delay(this.validationDelay);
8062         }
8063     },
8064      /**
8065      * Validates the field value
8066      * @return {Boolean} True if the value is valid, else false
8067      */
8068     validate : function(){
8069         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8070         if(this.disabled || this.validateValue(this.getRawValue())){
8071             this.markValid();
8072             return true;
8073         }
8074         
8075         this.markInvalid();
8076         return false;
8077     },
8078     
8079     
8080     /**
8081      * Validates a value according to the field's validation rules and marks the field as invalid
8082      * if the validation fails
8083      * @param {Mixed} value The value to validate
8084      * @return {Boolean} True if the value is valid, else false
8085      */
8086     validateValue : function(value){
8087         if(value.length < 1)  { // if it's blank
8088             if(this.allowBlank){
8089                 return true;
8090             }
8091             return false;
8092         }
8093         
8094         if(value.length < this.minLength){
8095             return false;
8096         }
8097         if(value.length > this.maxLength){
8098             return false;
8099         }
8100         if(this.vtype){
8101             var vt = Roo.form.VTypes;
8102             if(!vt[this.vtype](value, this)){
8103                 return false;
8104             }
8105         }
8106         if(typeof this.validator == "function"){
8107             var msg = this.validator(value);
8108             if(msg !== true){
8109                 return false;
8110             }
8111         }
8112         
8113         if(this.regex && !this.regex.test(value)){
8114             return false;
8115         }
8116         
8117         return true;
8118     },
8119
8120     
8121     
8122      // private
8123     fireKey : function(e){
8124         //Roo.log('field ' + e.getKey());
8125         if(e.isNavKeyPress()){
8126             this.fireEvent("specialkey", this, e);
8127         }
8128     },
8129     focus : function (selectText){
8130         if(this.rendered){
8131             this.inputEl().focus();
8132             if(selectText === true){
8133                 this.inputEl().dom.select();
8134             }
8135         }
8136         return this;
8137     } ,
8138     
8139     onFocus : function(){
8140         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8141            // this.el.addClass(this.focusClass);
8142         }
8143         if(!this.hasFocus){
8144             this.hasFocus = true;
8145             this.startValue = this.getValue();
8146             this.fireEvent("focus", this);
8147         }
8148     },
8149     
8150     beforeBlur : Roo.emptyFn,
8151
8152     
8153     // private
8154     onBlur : function(){
8155         this.beforeBlur();
8156         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8157             //this.el.removeClass(this.focusClass);
8158         }
8159         this.hasFocus = false;
8160         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8161             this.validate();
8162         }
8163         var v = this.getValue();
8164         if(String(v) !== String(this.startValue)){
8165             this.fireEvent('change', this, v, this.startValue);
8166         }
8167         this.fireEvent("blur", this);
8168     },
8169     
8170     /**
8171      * Resets the current field value to the originally loaded value and clears any validation messages
8172      */
8173     reset : function(){
8174         this.setValue(this.originalValue);
8175         this.validate();
8176     },
8177      /**
8178      * Returns the name of the field
8179      * @return {Mixed} name The name field
8180      */
8181     getName: function(){
8182         return this.name;
8183     },
8184      /**
8185      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8186      * @return {Mixed} value The field value
8187      */
8188     getValue : function(){
8189         
8190         var v = this.inputEl().getValue();
8191         
8192         return v;
8193     },
8194     /**
8195      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8196      * @return {Mixed} value The field value
8197      */
8198     getRawValue : function(){
8199         var v = this.inputEl().getValue();
8200         
8201         return v;
8202     },
8203     
8204     /**
8205      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8206      * @param {Mixed} value The value to set
8207      */
8208     setRawValue : function(v){
8209         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8210     },
8211     
8212     selectText : function(start, end){
8213         var v = this.getRawValue();
8214         if(v.length > 0){
8215             start = start === undefined ? 0 : start;
8216             end = end === undefined ? v.length : end;
8217             var d = this.inputEl().dom;
8218             if(d.setSelectionRange){
8219                 d.setSelectionRange(start, end);
8220             }else if(d.createTextRange){
8221                 var range = d.createTextRange();
8222                 range.moveStart("character", start);
8223                 range.moveEnd("character", v.length-end);
8224                 range.select();
8225             }
8226         }
8227     },
8228     
8229     /**
8230      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8231      * @param {Mixed} value The value to set
8232      */
8233     setValue : function(v){
8234         this.value = v;
8235         if(this.rendered){
8236             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8237             this.validate();
8238         }
8239     },
8240     
8241     /*
8242     processValue : function(value){
8243         if(this.stripCharsRe){
8244             var newValue = value.replace(this.stripCharsRe, '');
8245             if(newValue !== value){
8246                 this.setRawValue(newValue);
8247                 return newValue;
8248             }
8249         }
8250         return value;
8251     },
8252   */
8253     preFocus : function(){
8254         
8255         if(this.selectOnFocus){
8256             this.inputEl().dom.select();
8257         }
8258     },
8259     filterKeys : function(e){
8260         var k = e.getKey();
8261         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8262             return;
8263         }
8264         var c = e.getCharCode(), cc = String.fromCharCode(c);
8265         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8266             return;
8267         }
8268         if(!this.maskRe.test(cc)){
8269             e.stopEvent();
8270         }
8271     },
8272      /**
8273      * Clear any invalid styles/messages for this field
8274      */
8275     clearInvalid : function(){
8276         
8277         if(!this.el || this.preventMark){ // not rendered
8278             return;
8279         }
8280         this.el.removeClass(this.invalidClass);
8281         
8282         this.fireEvent('valid', this);
8283     },
8284     
8285      /**
8286      * Mark this field as valid
8287      */
8288     markValid : function(){
8289         if(!this.el  || this.preventMark){ // not rendered
8290             return;
8291         }
8292         
8293         this.el.removeClass([this.invalidClass, this.validClass]);
8294         
8295         var feedback = this.el.select('.form-control-feedback', true).first();
8296             
8297         if(feedback){
8298             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8299         }
8300
8301         if(this.disabled || this.allowBlank){
8302             return;
8303         }
8304         
8305         this.el.addClass(this.validClass);
8306         
8307         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8308             
8309             var feedback = this.el.select('.form-control-feedback', true).first();
8310             
8311             if(feedback){
8312                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8313                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8314             }
8315             
8316         }
8317         
8318         this.fireEvent('valid', this);
8319     },
8320     
8321      /**
8322      * Mark this field as invalid
8323      * @param {String} msg The validation message
8324      */
8325     markInvalid : function(msg){
8326         if(!this.el  || this.preventMark){ // not rendered
8327             return;
8328         }
8329         
8330         this.el.removeClass([this.invalidClass, this.validClass]);
8331         
8332         var feedback = this.el.select('.form-control-feedback', true).first();
8333             
8334         if(feedback){
8335             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8336         }
8337
8338         if(this.disabled || this.allowBlank){
8339             return;
8340         }
8341         
8342         this.el.addClass(this.invalidClass);
8343         
8344         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8345             
8346             var feedback = this.el.select('.form-control-feedback', true).first();
8347             
8348             if(feedback){
8349                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8350                 
8351                 if(this.getValue().length || this.forceFeedback){
8352                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8353                 }
8354                 
8355             }
8356             
8357         }
8358         
8359         this.fireEvent('invalid', this, msg);
8360     },
8361     // private
8362     SafariOnKeyDown : function(event)
8363     {
8364         // this is a workaround for a password hang bug on chrome/ webkit.
8365         
8366         var isSelectAll = false;
8367         
8368         if(this.inputEl().dom.selectionEnd > 0){
8369             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8370         }
8371         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8372             event.preventDefault();
8373             this.setValue('');
8374             return;
8375         }
8376         
8377         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8378             
8379             event.preventDefault();
8380             // this is very hacky as keydown always get's upper case.
8381             //
8382             var cc = String.fromCharCode(event.getCharCode());
8383             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8384             
8385         }
8386     },
8387     adjustWidth : function(tag, w){
8388         tag = tag.toLowerCase();
8389         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8390             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8391                 if(tag == 'input'){
8392                     return w + 2;
8393                 }
8394                 if(tag == 'textarea'){
8395                     return w-2;
8396                 }
8397             }else if(Roo.isOpera){
8398                 if(tag == 'input'){
8399                     return w + 2;
8400                 }
8401                 if(tag == 'textarea'){
8402                     return w-2;
8403                 }
8404             }
8405         }
8406         return w;
8407     }
8408     
8409 });
8410
8411  
8412 /*
8413  * - LGPL
8414  *
8415  * Input
8416  * 
8417  */
8418
8419 /**
8420  * @class Roo.bootstrap.TextArea
8421  * @extends Roo.bootstrap.Input
8422  * Bootstrap TextArea class
8423  * @cfg {Number} cols Specifies the visible width of a text area
8424  * @cfg {Number} rows Specifies the visible number of lines in a text area
8425  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8426  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8427  * @cfg {string} html text
8428  * 
8429  * @constructor
8430  * Create a new TextArea
8431  * @param {Object} config The config object
8432  */
8433
8434 Roo.bootstrap.TextArea = function(config){
8435     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8436    
8437 };
8438
8439 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8440      
8441     cols : false,
8442     rows : 5,
8443     readOnly : false,
8444     warp : 'soft',
8445     resize : false,
8446     value: false,
8447     html: false,
8448     
8449     getAutoCreate : function(){
8450         
8451         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8452         
8453         var id = Roo.id();
8454         
8455         var cfg = {};
8456         
8457         var input =  {
8458             tag: 'textarea',
8459             id : id,
8460             warp : this.warp,
8461             rows : this.rows,
8462             value : this.value || '',
8463             html: this.html || '',
8464             cls : 'form-control',
8465             placeholder : this.placeholder || '' 
8466             
8467         };
8468         
8469         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8470             input.maxLength = this.maxLength;
8471         }
8472         
8473         if(this.resize){
8474             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8475         }
8476         
8477         if(this.cols){
8478             input.cols = this.cols;
8479         }
8480         
8481         if (this.readOnly) {
8482             input.readonly = true;
8483         }
8484         
8485         if (this.name) {
8486             input.name = this.name;
8487         }
8488         
8489         if (this.size) {
8490             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8491         }
8492         
8493         var settings=this;
8494         ['xs','sm','md','lg'].map(function(size){
8495             if (settings[size]) {
8496                 cfg.cls += ' col-' + size + '-' + settings[size];
8497             }
8498         });
8499         
8500         var inputblock = input;
8501         
8502         if(this.hasFeedback && !this.allowBlank){
8503             
8504             var feedback = {
8505                 tag: 'span',
8506                 cls: 'glyphicon form-control-feedback'
8507             };
8508
8509             inputblock = {
8510                 cls : 'has-feedback',
8511                 cn :  [
8512                     input,
8513                     feedback
8514                 ] 
8515             };  
8516         }
8517         
8518         
8519         if (this.before || this.after) {
8520             
8521             inputblock = {
8522                 cls : 'input-group',
8523                 cn :  [] 
8524             };
8525             if (this.before) {
8526                 inputblock.cn.push({
8527                     tag :'span',
8528                     cls : 'input-group-addon',
8529                     html : this.before
8530                 });
8531             }
8532             
8533             inputblock.cn.push(input);
8534             
8535             if(this.hasFeedback && !this.allowBlank){
8536                 inputblock.cls += ' has-feedback';
8537                 inputblock.cn.push(feedback);
8538             }
8539             
8540             if (this.after) {
8541                 inputblock.cn.push({
8542                     tag :'span',
8543                     cls : 'input-group-addon',
8544                     html : this.after
8545                 });
8546             }
8547             
8548         }
8549         
8550         if (align ==='left' && this.fieldLabel.length) {
8551                 Roo.log("left and has label");
8552                 cfg.cn = [
8553                     
8554                     {
8555                         tag: 'label',
8556                         'for' :  id,
8557                         cls : 'control-label col-sm-' + this.labelWidth,
8558                         html : this.fieldLabel
8559                         
8560                     },
8561                     {
8562                         cls : "col-sm-" + (12 - this.labelWidth), 
8563                         cn: [
8564                             inputblock
8565                         ]
8566                     }
8567                     
8568                 ];
8569         } else if ( this.fieldLabel.length) {
8570                 Roo.log(" label");
8571                  cfg.cn = [
8572                    
8573                     {
8574                         tag: 'label',
8575                         //cls : 'input-group-addon',
8576                         html : this.fieldLabel
8577                         
8578                     },
8579                     
8580                     inputblock
8581                     
8582                 ];
8583
8584         } else {
8585             
8586                    Roo.log(" no label && no align");
8587                 cfg.cn = [
8588                     
8589                         inputblock
8590                     
8591                 ];
8592                 
8593                 
8594         }
8595         
8596         if (this.disabled) {
8597             input.disabled=true;
8598         }
8599         
8600         return cfg;
8601         
8602     },
8603     /**
8604      * return the real textarea element.
8605      */
8606     inputEl: function ()
8607     {
8608         return this.el.select('textarea.form-control',true).first();
8609     }
8610 });
8611
8612  
8613 /*
8614  * - LGPL
8615  *
8616  * trigger field - base class for combo..
8617  * 
8618  */
8619  
8620 /**
8621  * @class Roo.bootstrap.TriggerField
8622  * @extends Roo.bootstrap.Input
8623  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8624  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8625  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8626  * for which you can provide a custom implementation.  For example:
8627  * <pre><code>
8628 var trigger = new Roo.bootstrap.TriggerField();
8629 trigger.onTriggerClick = myTriggerFn;
8630 trigger.applyTo('my-field');
8631 </code></pre>
8632  *
8633  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8634  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8635  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8636  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8637  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8638
8639  * @constructor
8640  * Create a new TriggerField.
8641  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8642  * to the base TextField)
8643  */
8644 Roo.bootstrap.TriggerField = function(config){
8645     this.mimicing = false;
8646     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8647 };
8648
8649 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8650     /**
8651      * @cfg {String} triggerClass A CSS class to apply to the trigger
8652      */
8653      /**
8654      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8655      */
8656     hideTrigger:false,
8657
8658     /**
8659      * @cfg {Boolean} removable (true|false) special filter default false
8660      */
8661     removable : false,
8662     
8663     /** @cfg {Boolean} grow @hide */
8664     /** @cfg {Number} growMin @hide */
8665     /** @cfg {Number} growMax @hide */
8666
8667     /**
8668      * @hide 
8669      * @method
8670      */
8671     autoSize: Roo.emptyFn,
8672     // private
8673     monitorTab : true,
8674     // private
8675     deferHeight : true,
8676
8677     
8678     actionMode : 'wrap',
8679     
8680     caret : false,
8681     
8682     
8683     getAutoCreate : function(){
8684        
8685         var align = this.labelAlign || this.parentLabelAlign();
8686         
8687         var id = Roo.id();
8688         
8689         var cfg = {
8690             cls: 'form-group' //input-group
8691         };
8692         
8693         
8694         var input =  {
8695             tag: 'input',
8696             id : id,
8697             type : this.inputType,
8698             cls : 'form-control',
8699             autocomplete: 'new-password',
8700             placeholder : this.placeholder || '' 
8701             
8702         };
8703         if (this.name) {
8704             input.name = this.name;
8705         }
8706         if (this.size) {
8707             input.cls += ' input-' + this.size;
8708         }
8709         
8710         if (this.disabled) {
8711             input.disabled=true;
8712         }
8713         
8714         var inputblock = input;
8715         
8716         if(this.hasFeedback && !this.allowBlank){
8717             
8718             var feedback = {
8719                 tag: 'span',
8720                 cls: 'glyphicon form-control-feedback'
8721             };
8722             
8723             if(this.removable && !this.editable && !this.tickable){
8724                 inputblock = {
8725                     cls : 'has-feedback',
8726                     cn :  [
8727                         inputblock,
8728                         {
8729                             tag: 'button',
8730                             html : 'x',
8731                             cls : 'roo-combo-removable-btn close'
8732                         },
8733                         feedback
8734                     ] 
8735                 };
8736             } else {
8737                 inputblock = {
8738                     cls : 'has-feedback',
8739                     cn :  [
8740                         inputblock,
8741                         feedback
8742                     ] 
8743                 };
8744             }
8745
8746         } else {
8747             if(this.removable && !this.editable && !this.tickable){
8748                 inputblock = {
8749                     cls : 'roo-removable',
8750                     cn :  [
8751                         inputblock,
8752                         {
8753                             tag: 'button',
8754                             html : 'x',
8755                             cls : 'roo-combo-removable-btn close'
8756                         }
8757                     ] 
8758                 };
8759             }
8760         }
8761         
8762         if (this.before || this.after) {
8763             
8764             inputblock = {
8765                 cls : 'input-group',
8766                 cn :  [] 
8767             };
8768             if (this.before) {
8769                 inputblock.cn.push({
8770                     tag :'span',
8771                     cls : 'input-group-addon',
8772                     html : this.before
8773                 });
8774             }
8775             
8776             inputblock.cn.push(input);
8777             
8778             if(this.hasFeedback && !this.allowBlank){
8779                 inputblock.cls += ' has-feedback';
8780                 inputblock.cn.push(feedback);
8781             }
8782             
8783             if (this.after) {
8784                 inputblock.cn.push({
8785                     tag :'span',
8786                     cls : 'input-group-addon',
8787                     html : this.after
8788                 });
8789             }
8790             
8791         };
8792         
8793         var box = {
8794             tag: 'div',
8795             cn: [
8796                 {
8797                     tag: 'input',
8798                     type : 'hidden',
8799                     cls: 'form-hidden-field'
8800                 },
8801                 inputblock
8802             ]
8803             
8804         };
8805         
8806         if(this.multiple){
8807             Roo.log('multiple');
8808             
8809             box = {
8810                 tag: 'div',
8811                 cn: [
8812                     {
8813                         tag: 'input',
8814                         type : 'hidden',
8815                         cls: 'form-hidden-field'
8816                     },
8817                     {
8818                         tag: 'ul',
8819                         cls: 'select2-choices',
8820                         cn:[
8821                             {
8822                                 tag: 'li',
8823                                 cls: 'select2-search-field',
8824                                 cn: [
8825
8826                                     inputblock
8827                                 ]
8828                             }
8829                         ]
8830                     }
8831                 ]
8832             }
8833         };
8834         
8835         var combobox = {
8836             cls: 'select2-container input-group',
8837             cn: [
8838                 box
8839 //                {
8840 //                    tag: 'ul',
8841 //                    cls: 'typeahead typeahead-long dropdown-menu',
8842 //                    style: 'display:none'
8843 //                }
8844             ]
8845         };
8846         
8847         if(!this.multiple && this.showToggleBtn){
8848             
8849             var caret = {
8850                         tag: 'span',
8851                         cls: 'caret'
8852              };
8853             if (this.caret != false) {
8854                 caret = {
8855                      tag: 'i',
8856                      cls: 'fa fa-' + this.caret
8857                 };
8858                 
8859             }
8860             
8861             combobox.cn.push({
8862                 tag :'span',
8863                 cls : 'input-group-addon btn dropdown-toggle',
8864                 cn : [
8865                     caret,
8866                     {
8867                         tag: 'span',
8868                         cls: 'combobox-clear',
8869                         cn  : [
8870                             {
8871                                 tag : 'i',
8872                                 cls: 'icon-remove'
8873                             }
8874                         ]
8875                     }
8876                 ]
8877
8878             })
8879         }
8880         
8881         if(this.multiple){
8882             combobox.cls += ' select2-container-multi';
8883         }
8884         
8885         if (align ==='left' && this.fieldLabel.length) {
8886             
8887                 Roo.log("left and has label");
8888                 cfg.cn = [
8889                     
8890                     {
8891                         tag: 'label',
8892                         'for' :  id,
8893                         cls : 'control-label col-sm-' + this.labelWidth,
8894                         html : this.fieldLabel
8895                         
8896                     },
8897                     {
8898                         cls : "col-sm-" + (12 - this.labelWidth), 
8899                         cn: [
8900                             combobox
8901                         ]
8902                     }
8903                     
8904                 ];
8905         } else if ( this.fieldLabel.length) {
8906                 Roo.log(" label");
8907                  cfg.cn = [
8908                    
8909                     {
8910                         tag: 'label',
8911                         //cls : 'input-group-addon',
8912                         html : this.fieldLabel
8913                         
8914                     },
8915                     
8916                     combobox
8917                     
8918                 ];
8919
8920         } else {
8921             
8922                 Roo.log(" no label && no align");
8923                 cfg = combobox
8924                      
8925                 
8926         }
8927          
8928         var settings=this;
8929         ['xs','sm','md','lg'].map(function(size){
8930             if (settings[size]) {
8931                 cfg.cls += ' col-' + size + '-' + settings[size];
8932             }
8933         });
8934         Roo.log(cfg);
8935         return cfg;
8936         
8937     },
8938     
8939     
8940     
8941     // private
8942     onResize : function(w, h){
8943 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8944 //        if(typeof w == 'number'){
8945 //            var x = w - this.trigger.getWidth();
8946 //            this.inputEl().setWidth(this.adjustWidth('input', x));
8947 //            this.trigger.setStyle('left', x+'px');
8948 //        }
8949     },
8950
8951     // private
8952     adjustSize : Roo.BoxComponent.prototype.adjustSize,
8953
8954     // private
8955     getResizeEl : function(){
8956         return this.inputEl();
8957     },
8958
8959     // private
8960     getPositionEl : function(){
8961         return this.inputEl();
8962     },
8963
8964     // private
8965     alignErrorIcon : function(){
8966         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8967     },
8968
8969     // private
8970     initEvents : function(){
8971         
8972         this.createList();
8973         
8974         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8975         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8976         if(!this.multiple && this.showToggleBtn){
8977             this.trigger = this.el.select('span.dropdown-toggle',true).first();
8978             if(this.hideTrigger){
8979                 this.trigger.setDisplayed(false);
8980             }
8981             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8982         }
8983         
8984         if(this.multiple){
8985             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8986         }
8987         
8988         if(this.removable && !this.editable && !this.tickable){
8989             var close = this.closeTriggerEl();
8990             
8991             if(close){
8992                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
8993                 close.on('click', this.removeBtnClick, this, close);
8994             }
8995         }
8996         
8997         //this.trigger.addClassOnOver('x-form-trigger-over');
8998         //this.trigger.addClassOnClick('x-form-trigger-click');
8999         
9000         //if(!this.width){
9001         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9002         //}
9003     },
9004     
9005     closeTriggerEl : function()
9006     {
9007         var close = this.el.select('.roo-combo-removable-btn', true).first();
9008         return close ? close : false;
9009     },
9010     
9011     removeBtnClick : function(e, h, el)
9012     {
9013         e.preventDefault();
9014         
9015         if(this.fireEvent("remove", this) !== false){
9016             this.reset();
9017         }
9018     },
9019     
9020     createList : function()
9021     {
9022         this.list = Roo.get(document.body).createChild({
9023             tag: 'ul',
9024             cls: 'typeahead typeahead-long dropdown-menu',
9025             style: 'display:none'
9026         });
9027         
9028         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9029         
9030     },
9031
9032     // private
9033     initTrigger : function(){
9034        
9035     },
9036
9037     // private
9038     onDestroy : function(){
9039         if(this.trigger){
9040             this.trigger.removeAllListeners();
9041           //  this.trigger.remove();
9042         }
9043         //if(this.wrap){
9044         //    this.wrap.remove();
9045         //}
9046         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9047     },
9048
9049     // private
9050     onFocus : function(){
9051         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9052         /*
9053         if(!this.mimicing){
9054             this.wrap.addClass('x-trigger-wrap-focus');
9055             this.mimicing = true;
9056             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9057             if(this.monitorTab){
9058                 this.el.on("keydown", this.checkTab, this);
9059             }
9060         }
9061         */
9062     },
9063
9064     // private
9065     checkTab : function(e){
9066         if(e.getKey() == e.TAB){
9067             this.triggerBlur();
9068         }
9069     },
9070
9071     // private
9072     onBlur : function(){
9073         // do nothing
9074     },
9075
9076     // private
9077     mimicBlur : function(e, t){
9078         /*
9079         if(!this.wrap.contains(t) && this.validateBlur()){
9080             this.triggerBlur();
9081         }
9082         */
9083     },
9084
9085     // private
9086     triggerBlur : function(){
9087         this.mimicing = false;
9088         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9089         if(this.monitorTab){
9090             this.el.un("keydown", this.checkTab, this);
9091         }
9092         //this.wrap.removeClass('x-trigger-wrap-focus');
9093         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9094     },
9095
9096     // private
9097     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9098     validateBlur : function(e, t){
9099         return true;
9100     },
9101
9102     // private
9103     onDisable : function(){
9104         this.inputEl().dom.disabled = true;
9105         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9106         //if(this.wrap){
9107         //    this.wrap.addClass('x-item-disabled');
9108         //}
9109     },
9110
9111     // private
9112     onEnable : function(){
9113         this.inputEl().dom.disabled = false;
9114         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9115         //if(this.wrap){
9116         //    this.el.removeClass('x-item-disabled');
9117         //}
9118     },
9119
9120     // private
9121     onShow : function(){
9122         var ae = this.getActionEl();
9123         
9124         if(ae){
9125             ae.dom.style.display = '';
9126             ae.dom.style.visibility = 'visible';
9127         }
9128     },
9129
9130     // private
9131     
9132     onHide : function(){
9133         var ae = this.getActionEl();
9134         ae.dom.style.display = 'none';
9135     },
9136
9137     /**
9138      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9139      * by an implementing function.
9140      * @method
9141      * @param {EventObject} e
9142      */
9143     onTriggerClick : Roo.emptyFn
9144 });
9145  /*
9146  * Based on:
9147  * Ext JS Library 1.1.1
9148  * Copyright(c) 2006-2007, Ext JS, LLC.
9149  *
9150  * Originally Released Under LGPL - original licence link has changed is not relivant.
9151  *
9152  * Fork - LGPL
9153  * <script type="text/javascript">
9154  */
9155
9156
9157 /**
9158  * @class Roo.data.SortTypes
9159  * @singleton
9160  * Defines the default sorting (casting?) comparison functions used when sorting data.
9161  */
9162 Roo.data.SortTypes = {
9163     /**
9164      * Default sort that does nothing
9165      * @param {Mixed} s The value being converted
9166      * @return {Mixed} The comparison value
9167      */
9168     none : function(s){
9169         return s;
9170     },
9171     
9172     /**
9173      * The regular expression used to strip tags
9174      * @type {RegExp}
9175      * @property
9176      */
9177     stripTagsRE : /<\/?[^>]+>/gi,
9178     
9179     /**
9180      * Strips all HTML tags to sort on text only
9181      * @param {Mixed} s The value being converted
9182      * @return {String} The comparison value
9183      */
9184     asText : function(s){
9185         return String(s).replace(this.stripTagsRE, "");
9186     },
9187     
9188     /**
9189      * Strips all HTML tags to sort on text only - Case insensitive
9190      * @param {Mixed} s The value being converted
9191      * @return {String} The comparison value
9192      */
9193     asUCText : function(s){
9194         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9195     },
9196     
9197     /**
9198      * Case insensitive string
9199      * @param {Mixed} s The value being converted
9200      * @return {String} The comparison value
9201      */
9202     asUCString : function(s) {
9203         return String(s).toUpperCase();
9204     },
9205     
9206     /**
9207      * Date sorting
9208      * @param {Mixed} s The value being converted
9209      * @return {Number} The comparison value
9210      */
9211     asDate : function(s) {
9212         if(!s){
9213             return 0;
9214         }
9215         if(s instanceof Date){
9216             return s.getTime();
9217         }
9218         return Date.parse(String(s));
9219     },
9220     
9221     /**
9222      * Float sorting
9223      * @param {Mixed} s The value being converted
9224      * @return {Float} The comparison value
9225      */
9226     asFloat : function(s) {
9227         var val = parseFloat(String(s).replace(/,/g, ""));
9228         if(isNaN(val)) val = 0;
9229         return val;
9230     },
9231     
9232     /**
9233      * Integer sorting
9234      * @param {Mixed} s The value being converted
9235      * @return {Number} The comparison value
9236      */
9237     asInt : function(s) {
9238         var val = parseInt(String(s).replace(/,/g, ""));
9239         if(isNaN(val)) val = 0;
9240         return val;
9241     }
9242 };/*
9243  * Based on:
9244  * Ext JS Library 1.1.1
9245  * Copyright(c) 2006-2007, Ext JS, LLC.
9246  *
9247  * Originally Released Under LGPL - original licence link has changed is not relivant.
9248  *
9249  * Fork - LGPL
9250  * <script type="text/javascript">
9251  */
9252
9253 /**
9254 * @class Roo.data.Record
9255  * Instances of this class encapsulate both record <em>definition</em> information, and record
9256  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9257  * to access Records cached in an {@link Roo.data.Store} object.<br>
9258  * <p>
9259  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9260  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9261  * objects.<br>
9262  * <p>
9263  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9264  * @constructor
9265  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9266  * {@link #create}. The parameters are the same.
9267  * @param {Array} data An associative Array of data values keyed by the field name.
9268  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9269  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9270  * not specified an integer id is generated.
9271  */
9272 Roo.data.Record = function(data, id){
9273     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9274     this.data = data;
9275 };
9276
9277 /**
9278  * Generate a constructor for a specific record layout.
9279  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9280  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9281  * Each field definition object may contain the following properties: <ul>
9282  * <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,
9283  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9284  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9285  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9286  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9287  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9288  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9289  * this may be omitted.</p></li>
9290  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9291  * <ul><li>auto (Default, implies no conversion)</li>
9292  * <li>string</li>
9293  * <li>int</li>
9294  * <li>float</li>
9295  * <li>boolean</li>
9296  * <li>date</li></ul></p></li>
9297  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9298  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9299  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9300  * by the Reader into an object that will be stored in the Record. It is passed the
9301  * following parameters:<ul>
9302  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9303  * </ul></p></li>
9304  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9305  * </ul>
9306  * <br>usage:<br><pre><code>
9307 var TopicRecord = Roo.data.Record.create(
9308     {name: 'title', mapping: 'topic_title'},
9309     {name: 'author', mapping: 'username'},
9310     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9311     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9312     {name: 'lastPoster', mapping: 'user2'},
9313     {name: 'excerpt', mapping: 'post_text'}
9314 );
9315
9316 var myNewRecord = new TopicRecord({
9317     title: 'Do my job please',
9318     author: 'noobie',
9319     totalPosts: 1,
9320     lastPost: new Date(),
9321     lastPoster: 'Animal',
9322     excerpt: 'No way dude!'
9323 });
9324 myStore.add(myNewRecord);
9325 </code></pre>
9326  * @method create
9327  * @static
9328  */
9329 Roo.data.Record.create = function(o){
9330     var f = function(){
9331         f.superclass.constructor.apply(this, arguments);
9332     };
9333     Roo.extend(f, Roo.data.Record);
9334     var p = f.prototype;
9335     p.fields = new Roo.util.MixedCollection(false, function(field){
9336         return field.name;
9337     });
9338     for(var i = 0, len = o.length; i < len; i++){
9339         p.fields.add(new Roo.data.Field(o[i]));
9340     }
9341     f.getField = function(name){
9342         return p.fields.get(name);  
9343     };
9344     return f;
9345 };
9346
9347 Roo.data.Record.AUTO_ID = 1000;
9348 Roo.data.Record.EDIT = 'edit';
9349 Roo.data.Record.REJECT = 'reject';
9350 Roo.data.Record.COMMIT = 'commit';
9351
9352 Roo.data.Record.prototype = {
9353     /**
9354      * Readonly flag - true if this record has been modified.
9355      * @type Boolean
9356      */
9357     dirty : false,
9358     editing : false,
9359     error: null,
9360     modified: null,
9361
9362     // private
9363     join : function(store){
9364         this.store = store;
9365     },
9366
9367     /**
9368      * Set the named field to the specified value.
9369      * @param {String} name The name of the field to set.
9370      * @param {Object} value The value to set the field to.
9371      */
9372     set : function(name, value){
9373         if(this.data[name] == value){
9374             return;
9375         }
9376         this.dirty = true;
9377         if(!this.modified){
9378             this.modified = {};
9379         }
9380         if(typeof this.modified[name] == 'undefined'){
9381             this.modified[name] = this.data[name];
9382         }
9383         this.data[name] = value;
9384         if(!this.editing && this.store){
9385             this.store.afterEdit(this);
9386         }       
9387     },
9388
9389     /**
9390      * Get the value of the named field.
9391      * @param {String} name The name of the field to get the value of.
9392      * @return {Object} The value of the field.
9393      */
9394     get : function(name){
9395         return this.data[name]; 
9396     },
9397
9398     // private
9399     beginEdit : function(){
9400         this.editing = true;
9401         this.modified = {}; 
9402     },
9403
9404     // private
9405     cancelEdit : function(){
9406         this.editing = false;
9407         delete this.modified;
9408     },
9409
9410     // private
9411     endEdit : function(){
9412         this.editing = false;
9413         if(this.dirty && this.store){
9414             this.store.afterEdit(this);
9415         }
9416     },
9417
9418     /**
9419      * Usually called by the {@link Roo.data.Store} which owns the Record.
9420      * Rejects all changes made to the Record since either creation, or the last commit operation.
9421      * Modified fields are reverted to their original values.
9422      * <p>
9423      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9424      * of reject operations.
9425      */
9426     reject : function(){
9427         var m = this.modified;
9428         for(var n in m){
9429             if(typeof m[n] != "function"){
9430                 this.data[n] = m[n];
9431             }
9432         }
9433         this.dirty = false;
9434         delete this.modified;
9435         this.editing = false;
9436         if(this.store){
9437             this.store.afterReject(this);
9438         }
9439     },
9440
9441     /**
9442      * Usually called by the {@link Roo.data.Store} which owns the Record.
9443      * Commits all changes made to the Record since either creation, or the last commit operation.
9444      * <p>
9445      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9446      * of commit operations.
9447      */
9448     commit : function(){
9449         this.dirty = false;
9450         delete this.modified;
9451         this.editing = false;
9452         if(this.store){
9453             this.store.afterCommit(this);
9454         }
9455     },
9456
9457     // private
9458     hasError : function(){
9459         return this.error != null;
9460     },
9461
9462     // private
9463     clearError : function(){
9464         this.error = null;
9465     },
9466
9467     /**
9468      * Creates a copy of this record.
9469      * @param {String} id (optional) A new record id if you don't want to use this record's id
9470      * @return {Record}
9471      */
9472     copy : function(newId) {
9473         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9474     }
9475 };/*
9476  * Based on:
9477  * Ext JS Library 1.1.1
9478  * Copyright(c) 2006-2007, Ext JS, LLC.
9479  *
9480  * Originally Released Under LGPL - original licence link has changed is not relivant.
9481  *
9482  * Fork - LGPL
9483  * <script type="text/javascript">
9484  */
9485
9486
9487
9488 /**
9489  * @class Roo.data.Store
9490  * @extends Roo.util.Observable
9491  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9492  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9493  * <p>
9494  * 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
9495  * has no knowledge of the format of the data returned by the Proxy.<br>
9496  * <p>
9497  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9498  * instances from the data object. These records are cached and made available through accessor functions.
9499  * @constructor
9500  * Creates a new Store.
9501  * @param {Object} config A config object containing the objects needed for the Store to access data,
9502  * and read the data into Records.
9503  */
9504 Roo.data.Store = function(config){
9505     this.data = new Roo.util.MixedCollection(false);
9506     this.data.getKey = function(o){
9507         return o.id;
9508     };
9509     this.baseParams = {};
9510     // private
9511     this.paramNames = {
9512         "start" : "start",
9513         "limit" : "limit",
9514         "sort" : "sort",
9515         "dir" : "dir",
9516         "multisort" : "_multisort"
9517     };
9518
9519     if(config && config.data){
9520         this.inlineData = config.data;
9521         delete config.data;
9522     }
9523
9524     Roo.apply(this, config);
9525     
9526     if(this.reader){ // reader passed
9527         this.reader = Roo.factory(this.reader, Roo.data);
9528         this.reader.xmodule = this.xmodule || false;
9529         if(!this.recordType){
9530             this.recordType = this.reader.recordType;
9531         }
9532         if(this.reader.onMetaChange){
9533             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9534         }
9535     }
9536
9537     if(this.recordType){
9538         this.fields = this.recordType.prototype.fields;
9539     }
9540     this.modified = [];
9541
9542     this.addEvents({
9543         /**
9544          * @event datachanged
9545          * Fires when the data cache has changed, and a widget which is using this Store
9546          * as a Record cache should refresh its view.
9547          * @param {Store} this
9548          */
9549         datachanged : true,
9550         /**
9551          * @event metachange
9552          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9553          * @param {Store} this
9554          * @param {Object} meta The JSON metadata
9555          */
9556         metachange : true,
9557         /**
9558          * @event add
9559          * Fires when Records have been added to the Store
9560          * @param {Store} this
9561          * @param {Roo.data.Record[]} records The array of Records added
9562          * @param {Number} index The index at which the record(s) were added
9563          */
9564         add : true,
9565         /**
9566          * @event remove
9567          * Fires when a Record has been removed from the Store
9568          * @param {Store} this
9569          * @param {Roo.data.Record} record The Record that was removed
9570          * @param {Number} index The index at which the record was removed
9571          */
9572         remove : true,
9573         /**
9574          * @event update
9575          * Fires when a Record has been updated
9576          * @param {Store} this
9577          * @param {Roo.data.Record} record The Record that was updated
9578          * @param {String} operation The update operation being performed.  Value may be one of:
9579          * <pre><code>
9580  Roo.data.Record.EDIT
9581  Roo.data.Record.REJECT
9582  Roo.data.Record.COMMIT
9583          * </code></pre>
9584          */
9585         update : true,
9586         /**
9587          * @event clear
9588          * Fires when the data cache has been cleared.
9589          * @param {Store} this
9590          */
9591         clear : true,
9592         /**
9593          * @event beforeload
9594          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9595          * the load action will be canceled.
9596          * @param {Store} this
9597          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9598          */
9599         beforeload : true,
9600         /**
9601          * @event beforeloadadd
9602          * Fires after a new set of Records has been loaded.
9603          * @param {Store} this
9604          * @param {Roo.data.Record[]} records The Records that were loaded
9605          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9606          */
9607         beforeloadadd : true,
9608         /**
9609          * @event load
9610          * Fires after a new set of Records has been loaded, before they are added to the store.
9611          * @param {Store} this
9612          * @param {Roo.data.Record[]} records The Records that were loaded
9613          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9614          * @params {Object} return from reader
9615          */
9616         load : true,
9617         /**
9618          * @event loadexception
9619          * Fires if an exception occurs in the Proxy during loading.
9620          * Called with the signature of the Proxy's "loadexception" event.
9621          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9622          * 
9623          * @param {Proxy} 
9624          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9625          * @param {Object} load options 
9626          * @param {Object} jsonData from your request (normally this contains the Exception)
9627          */
9628         loadexception : true
9629     });
9630     
9631     if(this.proxy){
9632         this.proxy = Roo.factory(this.proxy, Roo.data);
9633         this.proxy.xmodule = this.xmodule || false;
9634         this.relayEvents(this.proxy,  ["loadexception"]);
9635     }
9636     this.sortToggle = {};
9637     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9638
9639     Roo.data.Store.superclass.constructor.call(this);
9640
9641     if(this.inlineData){
9642         this.loadData(this.inlineData);
9643         delete this.inlineData;
9644     }
9645 };
9646
9647 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9648      /**
9649     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9650     * without a remote query - used by combo/forms at present.
9651     */
9652     
9653     /**
9654     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9655     */
9656     /**
9657     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9658     */
9659     /**
9660     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9661     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9662     */
9663     /**
9664     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9665     * on any HTTP request
9666     */
9667     /**
9668     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9669     */
9670     /**
9671     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9672     */
9673     multiSort: false,
9674     /**
9675     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9676     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9677     */
9678     remoteSort : false,
9679
9680     /**
9681     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9682      * loaded or when a record is removed. (defaults to false).
9683     */
9684     pruneModifiedRecords : false,
9685
9686     // private
9687     lastOptions : null,
9688
9689     /**
9690      * Add Records to the Store and fires the add event.
9691      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9692      */
9693     add : function(records){
9694         records = [].concat(records);
9695         for(var i = 0, len = records.length; i < len; i++){
9696             records[i].join(this);
9697         }
9698         var index = this.data.length;
9699         this.data.addAll(records);
9700         this.fireEvent("add", this, records, index);
9701     },
9702
9703     /**
9704      * Remove a Record from the Store and fires the remove event.
9705      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9706      */
9707     remove : function(record){
9708         var index = this.data.indexOf(record);
9709         this.data.removeAt(index);
9710         if(this.pruneModifiedRecords){
9711             this.modified.remove(record);
9712         }
9713         this.fireEvent("remove", this, record, index);
9714     },
9715
9716     /**
9717      * Remove all Records from the Store and fires the clear event.
9718      */
9719     removeAll : function(){
9720         this.data.clear();
9721         if(this.pruneModifiedRecords){
9722             this.modified = [];
9723         }
9724         this.fireEvent("clear", this);
9725     },
9726
9727     /**
9728      * Inserts Records to the Store at the given index and fires the add event.
9729      * @param {Number} index The start index at which to insert the passed Records.
9730      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9731      */
9732     insert : function(index, records){
9733         records = [].concat(records);
9734         for(var i = 0, len = records.length; i < len; i++){
9735             this.data.insert(index, records[i]);
9736             records[i].join(this);
9737         }
9738         this.fireEvent("add", this, records, index);
9739     },
9740
9741     /**
9742      * Get the index within the cache of the passed Record.
9743      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9744      * @return {Number} The index of the passed Record. Returns -1 if not found.
9745      */
9746     indexOf : function(record){
9747         return this.data.indexOf(record);
9748     },
9749
9750     /**
9751      * Get the index within the cache of the Record with the passed id.
9752      * @param {String} id The id of the Record to find.
9753      * @return {Number} The index of the Record. Returns -1 if not found.
9754      */
9755     indexOfId : function(id){
9756         return this.data.indexOfKey(id);
9757     },
9758
9759     /**
9760      * Get the Record with the specified id.
9761      * @param {String} id The id of the Record to find.
9762      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
9763      */
9764     getById : function(id){
9765         return this.data.key(id);
9766     },
9767
9768     /**
9769      * Get the Record at the specified index.
9770      * @param {Number} index The index of the Record to find.
9771      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
9772      */
9773     getAt : function(index){
9774         return this.data.itemAt(index);
9775     },
9776
9777     /**
9778      * Returns a range of Records between specified indices.
9779      * @param {Number} startIndex (optional) The starting index (defaults to 0)
9780      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
9781      * @return {Roo.data.Record[]} An array of Records
9782      */
9783     getRange : function(start, end){
9784         return this.data.getRange(start, end);
9785     },
9786
9787     // private
9788     storeOptions : function(o){
9789         o = Roo.apply({}, o);
9790         delete o.callback;
9791         delete o.scope;
9792         this.lastOptions = o;
9793     },
9794
9795     /**
9796      * Loads the Record cache from the configured Proxy using the configured Reader.
9797      * <p>
9798      * If using remote paging, then the first load call must specify the <em>start</em>
9799      * and <em>limit</em> properties in the options.params property to establish the initial
9800      * position within the dataset, and the number of Records to cache on each read from the Proxy.
9801      * <p>
9802      * <strong>It is important to note that for remote data sources, loading is asynchronous,
9803      * and this call will return before the new data has been loaded. Perform any post-processing
9804      * in a callback function, or in a "load" event handler.</strong>
9805      * <p>
9806      * @param {Object} options An object containing properties which control loading options:<ul>
9807      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
9808      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
9809      * passed the following arguments:<ul>
9810      * <li>r : Roo.data.Record[]</li>
9811      * <li>options: Options object from the load call</li>
9812      * <li>success: Boolean success indicator</li></ul></li>
9813      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
9814      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
9815      * </ul>
9816      */
9817     load : function(options){
9818         options = options || {};
9819         if(this.fireEvent("beforeload", this, options) !== false){
9820             this.storeOptions(options);
9821             var p = Roo.apply(options.params || {}, this.baseParams);
9822             // if meta was not loaded from remote source.. try requesting it.
9823             if (!this.reader.metaFromRemote) {
9824                 p._requestMeta = 1;
9825             }
9826             if(this.sortInfo && this.remoteSort){
9827                 var pn = this.paramNames;
9828                 p[pn["sort"]] = this.sortInfo.field;
9829                 p[pn["dir"]] = this.sortInfo.direction;
9830             }
9831             if (this.multiSort) {
9832                 var pn = this.paramNames;
9833                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
9834             }
9835             
9836             this.proxy.load(p, this.reader, this.loadRecords, this, options);
9837         }
9838     },
9839
9840     /**
9841      * Reloads the Record cache from the configured Proxy using the configured Reader and
9842      * the options from the last load operation performed.
9843      * @param {Object} options (optional) An object containing properties which may override the options
9844      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
9845      * the most recently used options are reused).
9846      */
9847     reload : function(options){
9848         this.load(Roo.applyIf(options||{}, this.lastOptions));
9849     },
9850
9851     // private
9852     // Called as a callback by the Reader during a load operation.
9853     loadRecords : function(o, options, success){
9854         if(!o || success === false){
9855             if(success !== false){
9856                 this.fireEvent("load", this, [], options, o);
9857             }
9858             if(options.callback){
9859                 options.callback.call(options.scope || this, [], options, false);
9860             }
9861             return;
9862         }
9863         // if data returned failure - throw an exception.
9864         if (o.success === false) {
9865             // show a message if no listener is registered.
9866             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
9867                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
9868             }
9869             // loadmask wil be hooked into this..
9870             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
9871             return;
9872         }
9873         var r = o.records, t = o.totalRecords || r.length;
9874         
9875         this.fireEvent("beforeloadadd", this, r, options, o);
9876         
9877         if(!options || options.add !== true){
9878             if(this.pruneModifiedRecords){
9879                 this.modified = [];
9880             }
9881             for(var i = 0, len = r.length; i < len; i++){
9882                 r[i].join(this);
9883             }
9884             if(this.snapshot){
9885                 this.data = this.snapshot;
9886                 delete this.snapshot;
9887             }
9888             this.data.clear();
9889             this.data.addAll(r);
9890             this.totalLength = t;
9891             this.applySort();
9892             this.fireEvent("datachanged", this);
9893         }else{
9894             this.totalLength = Math.max(t, this.data.length+r.length);
9895             this.add(r);
9896         }
9897         this.fireEvent("load", this, r, options, o);
9898         if(options.callback){
9899             options.callback.call(options.scope || this, r, options, true);
9900         }
9901     },
9902
9903
9904     /**
9905      * Loads data from a passed data block. A Reader which understands the format of the data
9906      * must have been configured in the constructor.
9907      * @param {Object} data The data block from which to read the Records.  The format of the data expected
9908      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9909      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9910      */
9911     loadData : function(o, append){
9912         var r = this.reader.readRecords(o);
9913         this.loadRecords(r, {add: append}, true);
9914     },
9915
9916     /**
9917      * Gets the number of cached records.
9918      * <p>
9919      * <em>If using paging, this may not be the total size of the dataset. If the data object
9920      * used by the Reader contains the dataset size, then the getTotalCount() function returns
9921      * the data set size</em>
9922      */
9923     getCount : function(){
9924         return this.data.length || 0;
9925     },
9926
9927     /**
9928      * Gets the total number of records in the dataset as returned by the server.
9929      * <p>
9930      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9931      * the dataset size</em>
9932      */
9933     getTotalCount : function(){
9934         return this.totalLength || 0;
9935     },
9936
9937     /**
9938      * Returns the sort state of the Store as an object with two properties:
9939      * <pre><code>
9940  field {String} The name of the field by which the Records are sorted
9941  direction {String} The sort order, "ASC" or "DESC"
9942      * </code></pre>
9943      */
9944     getSortState : function(){
9945         return this.sortInfo;
9946     },
9947
9948     // private
9949     applySort : function(){
9950         if(this.sortInfo && !this.remoteSort){
9951             var s = this.sortInfo, f = s.field;
9952             var st = this.fields.get(f).sortType;
9953             var fn = function(r1, r2){
9954                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9955                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9956             };
9957             this.data.sort(s.direction, fn);
9958             if(this.snapshot && this.snapshot != this.data){
9959                 this.snapshot.sort(s.direction, fn);
9960             }
9961         }
9962     },
9963
9964     /**
9965      * Sets the default sort column and order to be used by the next load operation.
9966      * @param {String} fieldName The name of the field to sort by.
9967      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9968      */
9969     setDefaultSort : function(field, dir){
9970         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9971     },
9972
9973     /**
9974      * Sort the Records.
9975      * If remote sorting is used, the sort is performed on the server, and the cache is
9976      * reloaded. If local sorting is used, the cache is sorted internally.
9977      * @param {String} fieldName The name of the field to sort by.
9978      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9979      */
9980     sort : function(fieldName, dir){
9981         var f = this.fields.get(fieldName);
9982         if(!dir){
9983             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9984             
9985             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9986                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9987             }else{
9988                 dir = f.sortDir;
9989             }
9990         }
9991         this.sortToggle[f.name] = dir;
9992         this.sortInfo = {field: f.name, direction: dir};
9993         if(!this.remoteSort){
9994             this.applySort();
9995             this.fireEvent("datachanged", this);
9996         }else{
9997             this.load(this.lastOptions);
9998         }
9999     },
10000
10001     /**
10002      * Calls the specified function for each of the Records in the cache.
10003      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10004      * Returning <em>false</em> aborts and exits the iteration.
10005      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10006      */
10007     each : function(fn, scope){
10008         this.data.each(fn, scope);
10009     },
10010
10011     /**
10012      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10013      * (e.g., during paging).
10014      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10015      */
10016     getModifiedRecords : function(){
10017         return this.modified;
10018     },
10019
10020     // private
10021     createFilterFn : function(property, value, anyMatch){
10022         if(!value.exec){ // not a regex
10023             value = String(value);
10024             if(value.length == 0){
10025                 return false;
10026             }
10027             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10028         }
10029         return function(r){
10030             return value.test(r.data[property]);
10031         };
10032     },
10033
10034     /**
10035      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10036      * @param {String} property A field on your records
10037      * @param {Number} start The record index to start at (defaults to 0)
10038      * @param {Number} end The last record index to include (defaults to length - 1)
10039      * @return {Number} The sum
10040      */
10041     sum : function(property, start, end){
10042         var rs = this.data.items, v = 0;
10043         start = start || 0;
10044         end = (end || end === 0) ? end : rs.length-1;
10045
10046         for(var i = start; i <= end; i++){
10047             v += (rs[i].data[property] || 0);
10048         }
10049         return v;
10050     },
10051
10052     /**
10053      * Filter the records by a specified property.
10054      * @param {String} field A field on your records
10055      * @param {String/RegExp} value Either a string that the field
10056      * should start with or a RegExp to test against the field
10057      * @param {Boolean} anyMatch True to match any part not just the beginning
10058      */
10059     filter : function(property, value, anyMatch){
10060         var fn = this.createFilterFn(property, value, anyMatch);
10061         return fn ? this.filterBy(fn) : this.clearFilter();
10062     },
10063
10064     /**
10065      * Filter by a function. The specified function will be called with each
10066      * record in this data source. If the function returns true the record is included,
10067      * otherwise it is filtered.
10068      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10069      * @param {Object} scope (optional) The scope of the function (defaults to this)
10070      */
10071     filterBy : function(fn, scope){
10072         this.snapshot = this.snapshot || this.data;
10073         this.data = this.queryBy(fn, scope||this);
10074         this.fireEvent("datachanged", this);
10075     },
10076
10077     /**
10078      * Query the records by a specified property.
10079      * @param {String} field A field on your records
10080      * @param {String/RegExp} value Either a string that the field
10081      * should start with or a RegExp to test against the field
10082      * @param {Boolean} anyMatch True to match any part not just the beginning
10083      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10084      */
10085     query : function(property, value, anyMatch){
10086         var fn = this.createFilterFn(property, value, anyMatch);
10087         return fn ? this.queryBy(fn) : this.data.clone();
10088     },
10089
10090     /**
10091      * Query by a function. The specified function will be called with each
10092      * record in this data source. If the function returns true the record is included
10093      * in the results.
10094      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10095      * @param {Object} scope (optional) The scope of the function (defaults to this)
10096       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10097      **/
10098     queryBy : function(fn, scope){
10099         var data = this.snapshot || this.data;
10100         return data.filterBy(fn, scope||this);
10101     },
10102
10103     /**
10104      * Collects unique values for a particular dataIndex from this store.
10105      * @param {String} dataIndex The property to collect
10106      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10107      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10108      * @return {Array} An array of the unique values
10109      **/
10110     collect : function(dataIndex, allowNull, bypassFilter){
10111         var d = (bypassFilter === true && this.snapshot) ?
10112                 this.snapshot.items : this.data.items;
10113         var v, sv, r = [], l = {};
10114         for(var i = 0, len = d.length; i < len; i++){
10115             v = d[i].data[dataIndex];
10116             sv = String(v);
10117             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10118                 l[sv] = true;
10119                 r[r.length] = v;
10120             }
10121         }
10122         return r;
10123     },
10124
10125     /**
10126      * Revert to a view of the Record cache with no filtering applied.
10127      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10128      */
10129     clearFilter : function(suppressEvent){
10130         if(this.snapshot && this.snapshot != this.data){
10131             this.data = this.snapshot;
10132             delete this.snapshot;
10133             if(suppressEvent !== true){
10134                 this.fireEvent("datachanged", this);
10135             }
10136         }
10137     },
10138
10139     // private
10140     afterEdit : function(record){
10141         if(this.modified.indexOf(record) == -1){
10142             this.modified.push(record);
10143         }
10144         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10145     },
10146     
10147     // private
10148     afterReject : function(record){
10149         this.modified.remove(record);
10150         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10151     },
10152
10153     // private
10154     afterCommit : function(record){
10155         this.modified.remove(record);
10156         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10157     },
10158
10159     /**
10160      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10161      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10162      */
10163     commitChanges : function(){
10164         var m = this.modified.slice(0);
10165         this.modified = [];
10166         for(var i = 0, len = m.length; i < len; i++){
10167             m[i].commit();
10168         }
10169     },
10170
10171     /**
10172      * Cancel outstanding changes on all changed records.
10173      */
10174     rejectChanges : function(){
10175         var m = this.modified.slice(0);
10176         this.modified = [];
10177         for(var i = 0, len = m.length; i < len; i++){
10178             m[i].reject();
10179         }
10180     },
10181
10182     onMetaChange : function(meta, rtype, o){
10183         this.recordType = rtype;
10184         this.fields = rtype.prototype.fields;
10185         delete this.snapshot;
10186         this.sortInfo = meta.sortInfo || this.sortInfo;
10187         this.modified = [];
10188         this.fireEvent('metachange', this, this.reader.meta);
10189     },
10190     
10191     moveIndex : function(data, type)
10192     {
10193         var index = this.indexOf(data);
10194         
10195         var newIndex = index + type;
10196         
10197         this.remove(data);
10198         
10199         this.insert(newIndex, data);
10200         
10201     }
10202 });/*
10203  * Based on:
10204  * Ext JS Library 1.1.1
10205  * Copyright(c) 2006-2007, Ext JS, LLC.
10206  *
10207  * Originally Released Under LGPL - original licence link has changed is not relivant.
10208  *
10209  * Fork - LGPL
10210  * <script type="text/javascript">
10211  */
10212
10213 /**
10214  * @class Roo.data.SimpleStore
10215  * @extends Roo.data.Store
10216  * Small helper class to make creating Stores from Array data easier.
10217  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10218  * @cfg {Array} fields An array of field definition objects, or field name strings.
10219  * @cfg {Array} data The multi-dimensional array of data
10220  * @constructor
10221  * @param {Object} config
10222  */
10223 Roo.data.SimpleStore = function(config){
10224     Roo.data.SimpleStore.superclass.constructor.call(this, {
10225         isLocal : true,
10226         reader: new Roo.data.ArrayReader({
10227                 id: config.id
10228             },
10229             Roo.data.Record.create(config.fields)
10230         ),
10231         proxy : new Roo.data.MemoryProxy(config.data)
10232     });
10233     this.load();
10234 };
10235 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10236  * Based on:
10237  * Ext JS Library 1.1.1
10238  * Copyright(c) 2006-2007, Ext JS, LLC.
10239  *
10240  * Originally Released Under LGPL - original licence link has changed is not relivant.
10241  *
10242  * Fork - LGPL
10243  * <script type="text/javascript">
10244  */
10245
10246 /**
10247 /**
10248  * @extends Roo.data.Store
10249  * @class Roo.data.JsonStore
10250  * Small helper class to make creating Stores for JSON data easier. <br/>
10251 <pre><code>
10252 var store = new Roo.data.JsonStore({
10253     url: 'get-images.php',
10254     root: 'images',
10255     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10256 });
10257 </code></pre>
10258  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10259  * JsonReader and HttpProxy (unless inline data is provided).</b>
10260  * @cfg {Array} fields An array of field definition objects, or field name strings.
10261  * @constructor
10262  * @param {Object} config
10263  */
10264 Roo.data.JsonStore = function(c){
10265     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10266         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10267         reader: new Roo.data.JsonReader(c, c.fields)
10268     }));
10269 };
10270 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10271  * Based on:
10272  * Ext JS Library 1.1.1
10273  * Copyright(c) 2006-2007, Ext JS, LLC.
10274  *
10275  * Originally Released Under LGPL - original licence link has changed is not relivant.
10276  *
10277  * Fork - LGPL
10278  * <script type="text/javascript">
10279  */
10280
10281  
10282 Roo.data.Field = function(config){
10283     if(typeof config == "string"){
10284         config = {name: config};
10285     }
10286     Roo.apply(this, config);
10287     
10288     if(!this.type){
10289         this.type = "auto";
10290     }
10291     
10292     var st = Roo.data.SortTypes;
10293     // named sortTypes are supported, here we look them up
10294     if(typeof this.sortType == "string"){
10295         this.sortType = st[this.sortType];
10296     }
10297     
10298     // set default sortType for strings and dates
10299     if(!this.sortType){
10300         switch(this.type){
10301             case "string":
10302                 this.sortType = st.asUCString;
10303                 break;
10304             case "date":
10305                 this.sortType = st.asDate;
10306                 break;
10307             default:
10308                 this.sortType = st.none;
10309         }
10310     }
10311
10312     // define once
10313     var stripRe = /[\$,%]/g;
10314
10315     // prebuilt conversion function for this field, instead of
10316     // switching every time we're reading a value
10317     if(!this.convert){
10318         var cv, dateFormat = this.dateFormat;
10319         switch(this.type){
10320             case "":
10321             case "auto":
10322             case undefined:
10323                 cv = function(v){ return v; };
10324                 break;
10325             case "string":
10326                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10327                 break;
10328             case "int":
10329                 cv = function(v){
10330                     return v !== undefined && v !== null && v !== '' ?
10331                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10332                     };
10333                 break;
10334             case "float":
10335                 cv = function(v){
10336                     return v !== undefined && v !== null && v !== '' ?
10337                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10338                     };
10339                 break;
10340             case "bool":
10341             case "boolean":
10342                 cv = function(v){ return v === true || v === "true" || v == 1; };
10343                 break;
10344             case "date":
10345                 cv = function(v){
10346                     if(!v){
10347                         return '';
10348                     }
10349                     if(v instanceof Date){
10350                         return v;
10351                     }
10352                     if(dateFormat){
10353                         if(dateFormat == "timestamp"){
10354                             return new Date(v*1000);
10355                         }
10356                         return Date.parseDate(v, dateFormat);
10357                     }
10358                     var parsed = Date.parse(v);
10359                     return parsed ? new Date(parsed) : null;
10360                 };
10361              break;
10362             
10363         }
10364         this.convert = cv;
10365     }
10366 };
10367
10368 Roo.data.Field.prototype = {
10369     dateFormat: null,
10370     defaultValue: "",
10371     mapping: null,
10372     sortType : null,
10373     sortDir : "ASC"
10374 };/*
10375  * Based on:
10376  * Ext JS Library 1.1.1
10377  * Copyright(c) 2006-2007, Ext JS, LLC.
10378  *
10379  * Originally Released Under LGPL - original licence link has changed is not relivant.
10380  *
10381  * Fork - LGPL
10382  * <script type="text/javascript">
10383  */
10384  
10385 // Base class for reading structured data from a data source.  This class is intended to be
10386 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10387
10388 /**
10389  * @class Roo.data.DataReader
10390  * Base class for reading structured data from a data source.  This class is intended to be
10391  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10392  */
10393
10394 Roo.data.DataReader = function(meta, recordType){
10395     
10396     this.meta = meta;
10397     
10398     this.recordType = recordType instanceof Array ? 
10399         Roo.data.Record.create(recordType) : recordType;
10400 };
10401
10402 Roo.data.DataReader.prototype = {
10403      /**
10404      * Create an empty record
10405      * @param {Object} data (optional) - overlay some values
10406      * @return {Roo.data.Record} record created.
10407      */
10408     newRow :  function(d) {
10409         var da =  {};
10410         this.recordType.prototype.fields.each(function(c) {
10411             switch( c.type) {
10412                 case 'int' : da[c.name] = 0; break;
10413                 case 'date' : da[c.name] = new Date(); break;
10414                 case 'float' : da[c.name] = 0.0; break;
10415                 case 'boolean' : da[c.name] = false; break;
10416                 default : da[c.name] = ""; break;
10417             }
10418             
10419         });
10420         return new this.recordType(Roo.apply(da, d));
10421     }
10422     
10423 };/*
10424  * Based on:
10425  * Ext JS Library 1.1.1
10426  * Copyright(c) 2006-2007, Ext JS, LLC.
10427  *
10428  * Originally Released Under LGPL - original licence link has changed is not relivant.
10429  *
10430  * Fork - LGPL
10431  * <script type="text/javascript">
10432  */
10433
10434 /**
10435  * @class Roo.data.DataProxy
10436  * @extends Roo.data.Observable
10437  * This class is an abstract base class for implementations which provide retrieval of
10438  * unformatted data objects.<br>
10439  * <p>
10440  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10441  * (of the appropriate type which knows how to parse the data object) to provide a block of
10442  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10443  * <p>
10444  * Custom implementations must implement the load method as described in
10445  * {@link Roo.data.HttpProxy#load}.
10446  */
10447 Roo.data.DataProxy = function(){
10448     this.addEvents({
10449         /**
10450          * @event beforeload
10451          * Fires before a network request is made to retrieve a data object.
10452          * @param {Object} This DataProxy object.
10453          * @param {Object} params The params parameter to the load function.
10454          */
10455         beforeload : true,
10456         /**
10457          * @event load
10458          * Fires before the load method's callback is called.
10459          * @param {Object} This DataProxy object.
10460          * @param {Object} o The data object.
10461          * @param {Object} arg The callback argument object passed to the load function.
10462          */
10463         load : true,
10464         /**
10465          * @event loadexception
10466          * Fires if an Exception occurs during data retrieval.
10467          * @param {Object} This DataProxy object.
10468          * @param {Object} o The data object.
10469          * @param {Object} arg The callback argument object passed to the load function.
10470          * @param {Object} e The Exception.
10471          */
10472         loadexception : true
10473     });
10474     Roo.data.DataProxy.superclass.constructor.call(this);
10475 };
10476
10477 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10478
10479     /**
10480      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10481      */
10482 /*
10483  * Based on:
10484  * Ext JS Library 1.1.1
10485  * Copyright(c) 2006-2007, Ext JS, LLC.
10486  *
10487  * Originally Released Under LGPL - original licence link has changed is not relivant.
10488  *
10489  * Fork - LGPL
10490  * <script type="text/javascript">
10491  */
10492 /**
10493  * @class Roo.data.MemoryProxy
10494  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10495  * to the Reader when its load method is called.
10496  * @constructor
10497  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10498  */
10499 Roo.data.MemoryProxy = function(data){
10500     if (data.data) {
10501         data = data.data;
10502     }
10503     Roo.data.MemoryProxy.superclass.constructor.call(this);
10504     this.data = data;
10505 };
10506
10507 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10508     /**
10509      * Load data from the requested source (in this case an in-memory
10510      * data object passed to the constructor), read the data object into
10511      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10512      * process that block using the passed callback.
10513      * @param {Object} params This parameter is not used by the MemoryProxy class.
10514      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10515      * object into a block of Roo.data.Records.
10516      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10517      * The function must be passed <ul>
10518      * <li>The Record block object</li>
10519      * <li>The "arg" argument from the load function</li>
10520      * <li>A boolean success indicator</li>
10521      * </ul>
10522      * @param {Object} scope The scope in which to call the callback
10523      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10524      */
10525     load : function(params, reader, callback, scope, arg){
10526         params = params || {};
10527         var result;
10528         try {
10529             result = reader.readRecords(this.data);
10530         }catch(e){
10531             this.fireEvent("loadexception", this, arg, null, e);
10532             callback.call(scope, null, arg, false);
10533             return;
10534         }
10535         callback.call(scope, result, arg, true);
10536     },
10537     
10538     // private
10539     update : function(params, records){
10540         
10541     }
10542 });/*
10543  * Based on:
10544  * Ext JS Library 1.1.1
10545  * Copyright(c) 2006-2007, Ext JS, LLC.
10546  *
10547  * Originally Released Under LGPL - original licence link has changed is not relivant.
10548  *
10549  * Fork - LGPL
10550  * <script type="text/javascript">
10551  */
10552 /**
10553  * @class Roo.data.HttpProxy
10554  * @extends Roo.data.DataProxy
10555  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10556  * configured to reference a certain URL.<br><br>
10557  * <p>
10558  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10559  * from which the running page was served.<br><br>
10560  * <p>
10561  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10562  * <p>
10563  * Be aware that to enable the browser to parse an XML document, the server must set
10564  * the Content-Type header in the HTTP response to "text/xml".
10565  * @constructor
10566  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10567  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10568  * will be used to make the request.
10569  */
10570 Roo.data.HttpProxy = function(conn){
10571     Roo.data.HttpProxy.superclass.constructor.call(this);
10572     // is conn a conn config or a real conn?
10573     this.conn = conn;
10574     this.useAjax = !conn || !conn.events;
10575   
10576 };
10577
10578 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10579     // thse are take from connection...
10580     
10581     /**
10582      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10583      */
10584     /**
10585      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10586      * extra parameters to each request made by this object. (defaults to undefined)
10587      */
10588     /**
10589      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10590      *  to each request made by this object. (defaults to undefined)
10591      */
10592     /**
10593      * @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)
10594      */
10595     /**
10596      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10597      */
10598      /**
10599      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10600      * @type Boolean
10601      */
10602   
10603
10604     /**
10605      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10606      * @type Boolean
10607      */
10608     /**
10609      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10610      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10611      * a finer-grained basis than the DataProxy events.
10612      */
10613     getConnection : function(){
10614         return this.useAjax ? Roo.Ajax : this.conn;
10615     },
10616
10617     /**
10618      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10619      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10620      * process that block using the passed callback.
10621      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10622      * for the request to the remote server.
10623      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10624      * object into a block of Roo.data.Records.
10625      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10626      * The function must be passed <ul>
10627      * <li>The Record block object</li>
10628      * <li>The "arg" argument from the load function</li>
10629      * <li>A boolean success indicator</li>
10630      * </ul>
10631      * @param {Object} scope The scope in which to call the callback
10632      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10633      */
10634     load : function(params, reader, callback, scope, arg){
10635         if(this.fireEvent("beforeload", this, params) !== false){
10636             var  o = {
10637                 params : params || {},
10638                 request: {
10639                     callback : callback,
10640                     scope : scope,
10641                     arg : arg
10642                 },
10643                 reader: reader,
10644                 callback : this.loadResponse,
10645                 scope: this
10646             };
10647             if(this.useAjax){
10648                 Roo.applyIf(o, this.conn);
10649                 if(this.activeRequest){
10650                     Roo.Ajax.abort(this.activeRequest);
10651                 }
10652                 this.activeRequest = Roo.Ajax.request(o);
10653             }else{
10654                 this.conn.request(o);
10655             }
10656         }else{
10657             callback.call(scope||this, null, arg, false);
10658         }
10659     },
10660
10661     // private
10662     loadResponse : function(o, success, response){
10663         delete this.activeRequest;
10664         if(!success){
10665             this.fireEvent("loadexception", this, o, response);
10666             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10667             return;
10668         }
10669         var result;
10670         try {
10671             result = o.reader.read(response);
10672         }catch(e){
10673             this.fireEvent("loadexception", this, o, response, e);
10674             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10675             return;
10676         }
10677         
10678         this.fireEvent("load", this, o, o.request.arg);
10679         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10680     },
10681
10682     // private
10683     update : function(dataSet){
10684
10685     },
10686
10687     // private
10688     updateResponse : function(dataSet){
10689
10690     }
10691 });/*
10692  * Based on:
10693  * Ext JS Library 1.1.1
10694  * Copyright(c) 2006-2007, Ext JS, LLC.
10695  *
10696  * Originally Released Under LGPL - original licence link has changed is not relivant.
10697  *
10698  * Fork - LGPL
10699  * <script type="text/javascript">
10700  */
10701
10702 /**
10703  * @class Roo.data.ScriptTagProxy
10704  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10705  * other than the originating domain of the running page.<br><br>
10706  * <p>
10707  * <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
10708  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10709  * <p>
10710  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10711  * source code that is used as the source inside a &lt;script> tag.<br><br>
10712  * <p>
10713  * In order for the browser to process the returned data, the server must wrap the data object
10714  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10715  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10716  * depending on whether the callback name was passed:
10717  * <p>
10718  * <pre><code>
10719 boolean scriptTag = false;
10720 String cb = request.getParameter("callback");
10721 if (cb != null) {
10722     scriptTag = true;
10723     response.setContentType("text/javascript");
10724 } else {
10725     response.setContentType("application/x-json");
10726 }
10727 Writer out = response.getWriter();
10728 if (scriptTag) {
10729     out.write(cb + "(");
10730 }
10731 out.print(dataBlock.toJsonString());
10732 if (scriptTag) {
10733     out.write(");");
10734 }
10735 </pre></code>
10736  *
10737  * @constructor
10738  * @param {Object} config A configuration object.
10739  */
10740 Roo.data.ScriptTagProxy = function(config){
10741     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10742     Roo.apply(this, config);
10743     this.head = document.getElementsByTagName("head")[0];
10744 };
10745
10746 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10747
10748 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
10749     /**
10750      * @cfg {String} url The URL from which to request the data object.
10751      */
10752     /**
10753      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
10754      */
10755     timeout : 30000,
10756     /**
10757      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
10758      * the server the name of the callback function set up by the load call to process the returned data object.
10759      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
10760      * javascript output which calls this named function passing the data object as its only parameter.
10761      */
10762     callbackParam : "callback",
10763     /**
10764      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
10765      * name to the request.
10766      */
10767     nocache : true,
10768
10769     /**
10770      * Load data from the configured URL, read the data object into
10771      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10772      * process that block using the passed callback.
10773      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10774      * for the request to the remote server.
10775      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10776      * object into a block of Roo.data.Records.
10777      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10778      * The function must be passed <ul>
10779      * <li>The Record block object</li>
10780      * <li>The "arg" argument from the load function</li>
10781      * <li>A boolean success indicator</li>
10782      * </ul>
10783      * @param {Object} scope The scope in which to call the callback
10784      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10785      */
10786     load : function(params, reader, callback, scope, arg){
10787         if(this.fireEvent("beforeload", this, params) !== false){
10788
10789             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
10790
10791             var url = this.url;
10792             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
10793             if(this.nocache){
10794                 url += "&_dc=" + (new Date().getTime());
10795             }
10796             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
10797             var trans = {
10798                 id : transId,
10799                 cb : "stcCallback"+transId,
10800                 scriptId : "stcScript"+transId,
10801                 params : params,
10802                 arg : arg,
10803                 url : url,
10804                 callback : callback,
10805                 scope : scope,
10806                 reader : reader
10807             };
10808             var conn = this;
10809
10810             window[trans.cb] = function(o){
10811                 conn.handleResponse(o, trans);
10812             };
10813
10814             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
10815
10816             if(this.autoAbort !== false){
10817                 this.abort();
10818             }
10819
10820             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
10821
10822             var script = document.createElement("script");
10823             script.setAttribute("src", url);
10824             script.setAttribute("type", "text/javascript");
10825             script.setAttribute("id", trans.scriptId);
10826             this.head.appendChild(script);
10827
10828             this.trans = trans;
10829         }else{
10830             callback.call(scope||this, null, arg, false);
10831         }
10832     },
10833
10834     // private
10835     isLoading : function(){
10836         return this.trans ? true : false;
10837     },
10838
10839     /**
10840      * Abort the current server request.
10841      */
10842     abort : function(){
10843         if(this.isLoading()){
10844             this.destroyTrans(this.trans);
10845         }
10846     },
10847
10848     // private
10849     destroyTrans : function(trans, isLoaded){
10850         this.head.removeChild(document.getElementById(trans.scriptId));
10851         clearTimeout(trans.timeoutId);
10852         if(isLoaded){
10853             window[trans.cb] = undefined;
10854             try{
10855                 delete window[trans.cb];
10856             }catch(e){}
10857         }else{
10858             // if hasn't been loaded, wait for load to remove it to prevent script error
10859             window[trans.cb] = function(){
10860                 window[trans.cb] = undefined;
10861                 try{
10862                     delete window[trans.cb];
10863                 }catch(e){}
10864             };
10865         }
10866     },
10867
10868     // private
10869     handleResponse : function(o, trans){
10870         this.trans = false;
10871         this.destroyTrans(trans, true);
10872         var result;
10873         try {
10874             result = trans.reader.readRecords(o);
10875         }catch(e){
10876             this.fireEvent("loadexception", this, o, trans.arg, e);
10877             trans.callback.call(trans.scope||window, null, trans.arg, false);
10878             return;
10879         }
10880         this.fireEvent("load", this, o, trans.arg);
10881         trans.callback.call(trans.scope||window, result, trans.arg, true);
10882     },
10883
10884     // private
10885     handleFailure : function(trans){
10886         this.trans = false;
10887         this.destroyTrans(trans, false);
10888         this.fireEvent("loadexception", this, null, trans.arg);
10889         trans.callback.call(trans.scope||window, null, trans.arg, false);
10890     }
10891 });/*
10892  * Based on:
10893  * Ext JS Library 1.1.1
10894  * Copyright(c) 2006-2007, Ext JS, LLC.
10895  *
10896  * Originally Released Under LGPL - original licence link has changed is not relivant.
10897  *
10898  * Fork - LGPL
10899  * <script type="text/javascript">
10900  */
10901
10902 /**
10903  * @class Roo.data.JsonReader
10904  * @extends Roo.data.DataReader
10905  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10906  * based on mappings in a provided Roo.data.Record constructor.
10907  * 
10908  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10909  * in the reply previously. 
10910  * 
10911  * <p>
10912  * Example code:
10913  * <pre><code>
10914 var RecordDef = Roo.data.Record.create([
10915     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
10916     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
10917 ]);
10918 var myReader = new Roo.data.JsonReader({
10919     totalProperty: "results",    // The property which contains the total dataset size (optional)
10920     root: "rows",                // The property which contains an Array of row objects
10921     id: "id"                     // The property within each row object that provides an ID for the record (optional)
10922 }, RecordDef);
10923 </code></pre>
10924  * <p>
10925  * This would consume a JSON file like this:
10926  * <pre><code>
10927 { 'results': 2, 'rows': [
10928     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10929     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10930 }
10931 </code></pre>
10932  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10933  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10934  * paged from the remote server.
10935  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10936  * @cfg {String} root name of the property which contains the Array of row objects.
10937  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10938  * @cfg {Array} fields Array of field definition objects
10939  * @constructor
10940  * Create a new JsonReader
10941  * @param {Object} meta Metadata configuration options
10942  * @param {Object} recordType Either an Array of field definition objects,
10943  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10944  */
10945 Roo.data.JsonReader = function(meta, recordType){
10946     
10947     meta = meta || {};
10948     // set some defaults:
10949     Roo.applyIf(meta, {
10950         totalProperty: 'total',
10951         successProperty : 'success',
10952         root : 'data',
10953         id : 'id'
10954     });
10955     
10956     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10957 };
10958 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10959     
10960     /**
10961      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
10962      * Used by Store query builder to append _requestMeta to params.
10963      * 
10964      */
10965     metaFromRemote : false,
10966     /**
10967      * This method is only used by a DataProxy which has retrieved data from a remote server.
10968      * @param {Object} response The XHR object which contains the JSON data in its responseText.
10969      * @return {Object} data A data block which is used by an Roo.data.Store object as
10970      * a cache of Roo.data.Records.
10971      */
10972     read : function(response){
10973         var json = response.responseText;
10974        
10975         var o = /* eval:var:o */ eval("("+json+")");
10976         if(!o) {
10977             throw {message: "JsonReader.read: Json object not found"};
10978         }
10979         
10980         if(o.metaData){
10981             
10982             delete this.ef;
10983             this.metaFromRemote = true;
10984             this.meta = o.metaData;
10985             this.recordType = Roo.data.Record.create(o.metaData.fields);
10986             this.onMetaChange(this.meta, this.recordType, o);
10987         }
10988         return this.readRecords(o);
10989     },
10990
10991     // private function a store will implement
10992     onMetaChange : function(meta, recordType, o){
10993
10994     },
10995
10996     /**
10997          * @ignore
10998          */
10999     simpleAccess: function(obj, subsc) {
11000         return obj[subsc];
11001     },
11002
11003         /**
11004          * @ignore
11005          */
11006     getJsonAccessor: function(){
11007         var re = /[\[\.]/;
11008         return function(expr) {
11009             try {
11010                 return(re.test(expr))
11011                     ? new Function("obj", "return obj." + expr)
11012                     : function(obj){
11013                         return obj[expr];
11014                     };
11015             } catch(e){}
11016             return Roo.emptyFn;
11017         };
11018     }(),
11019
11020     /**
11021      * Create a data block containing Roo.data.Records from an XML document.
11022      * @param {Object} o An object which contains an Array of row objects in the property specified
11023      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11024      * which contains the total size of the dataset.
11025      * @return {Object} data A data block which is used by an Roo.data.Store object as
11026      * a cache of Roo.data.Records.
11027      */
11028     readRecords : function(o){
11029         /**
11030          * After any data loads, the raw JSON data is available for further custom processing.
11031          * @type Object
11032          */
11033         this.o = o;
11034         var s = this.meta, Record = this.recordType,
11035             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11036
11037 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11038         if (!this.ef) {
11039             if(s.totalProperty) {
11040                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11041                 }
11042                 if(s.successProperty) {
11043                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11044                 }
11045                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11046                 if (s.id) {
11047                         var g = this.getJsonAccessor(s.id);
11048                         this.getId = function(rec) {
11049                                 var r = g(rec);  
11050                                 return (r === undefined || r === "") ? null : r;
11051                         };
11052                 } else {
11053                         this.getId = function(){return null;};
11054                 }
11055             this.ef = [];
11056             for(var jj = 0; jj < fl; jj++){
11057                 f = fi[jj];
11058                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11059                 this.ef[jj] = this.getJsonAccessor(map);
11060             }
11061         }
11062
11063         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11064         if(s.totalProperty){
11065             var vt = parseInt(this.getTotal(o), 10);
11066             if(!isNaN(vt)){
11067                 totalRecords = vt;
11068             }
11069         }
11070         if(s.successProperty){
11071             var vs = this.getSuccess(o);
11072             if(vs === false || vs === 'false'){
11073                 success = false;
11074             }
11075         }
11076         var records = [];
11077         for(var i = 0; i < c; i++){
11078                 var n = root[i];
11079             var values = {};
11080             var id = this.getId(n);
11081             for(var j = 0; j < fl; j++){
11082                 f = fi[j];
11083             var v = this.ef[j](n);
11084             if (!f.convert) {
11085                 Roo.log('missing convert for ' + f.name);
11086                 Roo.log(f);
11087                 continue;
11088             }
11089             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11090             }
11091             var record = new Record(values, id);
11092             record.json = n;
11093             records[i] = record;
11094         }
11095         return {
11096             raw : o,
11097             success : success,
11098             records : records,
11099             totalRecords : totalRecords
11100         };
11101     }
11102 });/*
11103  * Based on:
11104  * Ext JS Library 1.1.1
11105  * Copyright(c) 2006-2007, Ext JS, LLC.
11106  *
11107  * Originally Released Under LGPL - original licence link has changed is not relivant.
11108  *
11109  * Fork - LGPL
11110  * <script type="text/javascript">
11111  */
11112
11113 /**
11114  * @class Roo.data.ArrayReader
11115  * @extends Roo.data.DataReader
11116  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11117  * Each element of that Array represents a row of data fields. The
11118  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11119  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11120  * <p>
11121  * Example code:.
11122  * <pre><code>
11123 var RecordDef = Roo.data.Record.create([
11124     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11125     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11126 ]);
11127 var myReader = new Roo.data.ArrayReader({
11128     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11129 }, RecordDef);
11130 </code></pre>
11131  * <p>
11132  * This would consume an Array like this:
11133  * <pre><code>
11134 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11135   </code></pre>
11136  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11137  * @constructor
11138  * Create a new JsonReader
11139  * @param {Object} meta Metadata configuration options.
11140  * @param {Object} recordType Either an Array of field definition objects
11141  * as specified to {@link Roo.data.Record#create},
11142  * or an {@link Roo.data.Record} object
11143  * created using {@link Roo.data.Record#create}.
11144  */
11145 Roo.data.ArrayReader = function(meta, recordType){
11146     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11147 };
11148
11149 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11150     /**
11151      * Create a data block containing Roo.data.Records from an XML document.
11152      * @param {Object} o An Array of row objects which represents the dataset.
11153      * @return {Object} data A data block which is used by an Roo.data.Store object as
11154      * a cache of Roo.data.Records.
11155      */
11156     readRecords : function(o){
11157         var sid = this.meta ? this.meta.id : null;
11158         var recordType = this.recordType, fields = recordType.prototype.fields;
11159         var records = [];
11160         var root = o;
11161             for(var i = 0; i < root.length; i++){
11162                     var n = root[i];
11163                 var values = {};
11164                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11165                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11166                 var f = fields.items[j];
11167                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11168                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11169                 v = f.convert(v);
11170                 values[f.name] = v;
11171             }
11172                 var record = new recordType(values, id);
11173                 record.json = n;
11174                 records[records.length] = record;
11175             }
11176             return {
11177                 records : records,
11178                 totalRecords : records.length
11179             };
11180     }
11181 });/*
11182  * - LGPL
11183  * * 
11184  */
11185
11186 /**
11187  * @class Roo.bootstrap.ComboBox
11188  * @extends Roo.bootstrap.TriggerField
11189  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11190  * @cfg {Boolean} append (true|false) default false
11191  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11192  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11193  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11194  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11195  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11196  * @cfg {Boolean} animate default true
11197  * @cfg {Boolean} emptyResultText only for touch device
11198  * @constructor
11199  * Create a new ComboBox.
11200  * @param {Object} config Configuration options
11201  */
11202 Roo.bootstrap.ComboBox = function(config){
11203     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11204     this.addEvents({
11205         /**
11206          * @event expand
11207          * Fires when the dropdown list is expanded
11208              * @param {Roo.bootstrap.ComboBox} combo This combo box
11209              */
11210         'expand' : true,
11211         /**
11212          * @event collapse
11213          * Fires when the dropdown list is collapsed
11214              * @param {Roo.bootstrap.ComboBox} combo This combo box
11215              */
11216         'collapse' : true,
11217         /**
11218          * @event beforeselect
11219          * Fires before a list item is selected. Return false to cancel the selection.
11220              * @param {Roo.bootstrap.ComboBox} combo This combo box
11221              * @param {Roo.data.Record} record The data record returned from the underlying store
11222              * @param {Number} index The index of the selected item in the dropdown list
11223              */
11224         'beforeselect' : true,
11225         /**
11226          * @event select
11227          * Fires when a list item is selected
11228              * @param {Roo.bootstrap.ComboBox} combo This combo box
11229              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11230              * @param {Number} index The index of the selected item in the dropdown list
11231              */
11232         'select' : true,
11233         /**
11234          * @event beforequery
11235          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11236          * The event object passed has these properties:
11237              * @param {Roo.bootstrap.ComboBox} combo This combo box
11238              * @param {String} query The query
11239              * @param {Boolean} forceAll true to force "all" query
11240              * @param {Boolean} cancel true to cancel the query
11241              * @param {Object} e The query event object
11242              */
11243         'beforequery': true,
11244          /**
11245          * @event add
11246          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11247              * @param {Roo.bootstrap.ComboBox} combo This combo box
11248              */
11249         'add' : true,
11250         /**
11251          * @event edit
11252          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11253              * @param {Roo.bootstrap.ComboBox} combo This combo box
11254              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11255              */
11256         'edit' : true,
11257         /**
11258          * @event remove
11259          * Fires when the remove value from the combobox array
11260              * @param {Roo.bootstrap.ComboBox} combo This combo box
11261              */
11262         'remove' : true,
11263         /**
11264          * @event specialfilter
11265          * Fires when specialfilter
11266             * @param {Roo.bootstrap.ComboBox} combo This combo box
11267             */
11268         'specialfilter' : true
11269         
11270     });
11271     
11272     this.item = [];
11273     this.tickItems = [];
11274     
11275     this.selectedIndex = -1;
11276     if(this.mode == 'local'){
11277         if(config.queryDelay === undefined){
11278             this.queryDelay = 10;
11279         }
11280         if(config.minChars === undefined){
11281             this.minChars = 0;
11282         }
11283     }
11284 };
11285
11286 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11287      
11288     /**
11289      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11290      * rendering into an Roo.Editor, defaults to false)
11291      */
11292     /**
11293      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11294      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11295      */
11296     /**
11297      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11298      */
11299     /**
11300      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11301      * the dropdown list (defaults to undefined, with no header element)
11302      */
11303
11304      /**
11305      * @cfg {String/Roo.Template} tpl The template to use to render the output
11306      */
11307      
11308      /**
11309      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11310      */
11311     listWidth: undefined,
11312     /**
11313      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11314      * mode = 'remote' or 'text' if mode = 'local')
11315      */
11316     displayField: undefined,
11317     
11318     /**
11319      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11320      * mode = 'remote' or 'value' if mode = 'local'). 
11321      * Note: use of a valueField requires the user make a selection
11322      * in order for a value to be mapped.
11323      */
11324     valueField: undefined,
11325     
11326     
11327     /**
11328      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11329      * field's data value (defaults to the underlying DOM element's name)
11330      */
11331     hiddenName: undefined,
11332     /**
11333      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11334      */
11335     listClass: '',
11336     /**
11337      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11338      */
11339     selectedClass: 'active',
11340     
11341     /**
11342      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11343      */
11344     shadow:'sides',
11345     /**
11346      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11347      * anchor positions (defaults to 'tl-bl')
11348      */
11349     listAlign: 'tl-bl?',
11350     /**
11351      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11352      */
11353     maxHeight: 300,
11354     /**
11355      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11356      * query specified by the allQuery config option (defaults to 'query')
11357      */
11358     triggerAction: 'query',
11359     /**
11360      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11361      * (defaults to 4, does not apply if editable = false)
11362      */
11363     minChars : 4,
11364     /**
11365      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11366      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11367      */
11368     typeAhead: false,
11369     /**
11370      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11371      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11372      */
11373     queryDelay: 500,
11374     /**
11375      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11376      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11377      */
11378     pageSize: 0,
11379     /**
11380      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11381      * when editable = true (defaults to false)
11382      */
11383     selectOnFocus:false,
11384     /**
11385      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11386      */
11387     queryParam: 'query',
11388     /**
11389      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11390      * when mode = 'remote' (defaults to 'Loading...')
11391      */
11392     loadingText: 'Loading...',
11393     /**
11394      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11395      */
11396     resizable: false,
11397     /**
11398      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11399      */
11400     handleHeight : 8,
11401     /**
11402      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11403      * traditional select (defaults to true)
11404      */
11405     editable: true,
11406     /**
11407      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11408      */
11409     allQuery: '',
11410     /**
11411      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11412      */
11413     mode: 'remote',
11414     /**
11415      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11416      * listWidth has a higher value)
11417      */
11418     minListWidth : 70,
11419     /**
11420      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11421      * allow the user to set arbitrary text into the field (defaults to false)
11422      */
11423     forceSelection:false,
11424     /**
11425      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11426      * if typeAhead = true (defaults to 250)
11427      */
11428     typeAheadDelay : 250,
11429     /**
11430      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11431      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11432      */
11433     valueNotFoundText : undefined,
11434     /**
11435      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11436      */
11437     blockFocus : false,
11438     
11439     /**
11440      * @cfg {Boolean} disableClear Disable showing of clear button.
11441      */
11442     disableClear : false,
11443     /**
11444      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11445      */
11446     alwaysQuery : false,
11447     
11448     /**
11449      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11450      */
11451     multiple : false,
11452     
11453     /**
11454      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11455      */
11456     invalidClass : "has-warning",
11457     
11458     /**
11459      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11460      */
11461     validClass : "has-success",
11462     
11463     /**
11464      * @cfg {Boolean} specialFilter (true|false) special filter default false
11465      */
11466     specialFilter : false,
11467     
11468     /**
11469      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11470      */
11471     mobileTouchView : true,
11472     
11473     //private
11474     addicon : false,
11475     editicon: false,
11476     
11477     page: 0,
11478     hasQuery: false,
11479     append: false,
11480     loadNext: false,
11481     autoFocus : true,
11482     tickable : false,
11483     btnPosition : 'right',
11484     triggerList : true,
11485     showToggleBtn : true,
11486     animate : true,
11487     emptyResultText: 'Empty',
11488     // element that contains real text value.. (when hidden is used..)
11489     
11490     getAutoCreate : function()
11491     {
11492         var cfg = false;
11493         
11494         /*
11495          * Touch Devices
11496          */
11497         
11498         if(Roo.isTouch && this.mobileTouchView){
11499             cfg = this.getAutoCreateTouchView();
11500             return cfg;;
11501         }
11502         
11503         /*
11504          *  Normal ComboBox
11505          */
11506         if(!this.tickable){
11507             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11508             return cfg;
11509         }
11510         
11511         /*
11512          *  ComboBox with tickable selections
11513          */
11514              
11515         var align = this.labelAlign || this.parentLabelAlign();
11516         
11517         cfg = {
11518             cls : 'form-group roo-combobox-tickable' //input-group
11519         };
11520         
11521         var buttons = {
11522             tag : 'div',
11523             cls : 'tickable-buttons',
11524             cn : [
11525                 {
11526                     tag : 'button',
11527                     type : 'button',
11528                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11529                     html : 'Edit'
11530                 },
11531                 {
11532                     tag : 'button',
11533                     type : 'button',
11534                     name : 'ok',
11535                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11536                     html : 'Done'
11537                 },
11538                 {
11539                     tag : 'button',
11540                     type : 'button',
11541                     name : 'cancel',
11542                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11543                     html : 'Cancel'
11544                 }
11545             ]
11546         };
11547         
11548         if(this.editable){
11549             buttons.cn.unshift({
11550                 tag: 'input',
11551                 cls: 'select2-search-field-input'
11552             });
11553         }
11554         
11555         var _this = this;
11556         
11557         Roo.each(buttons.cn, function(c){
11558             if (_this.size) {
11559                 c.cls += ' btn-' + _this.size;
11560             }
11561
11562             if (_this.disabled) {
11563                 c.disabled = true;
11564             }
11565         });
11566         
11567         var box = {
11568             tag: 'div',
11569             cn: [
11570                 {
11571                     tag: 'input',
11572                     type : 'hidden',
11573                     cls: 'form-hidden-field'
11574                 },
11575                 {
11576                     tag: 'ul',
11577                     cls: 'select2-choices',
11578                     cn:[
11579                         {
11580                             tag: 'li',
11581                             cls: 'select2-search-field',
11582                             cn: [
11583
11584                                 buttons
11585                             ]
11586                         }
11587                     ]
11588                 }
11589             ]
11590         }
11591         
11592         var combobox = {
11593             cls: 'select2-container input-group select2-container-multi',
11594             cn: [
11595                 box
11596 //                {
11597 //                    tag: 'ul',
11598 //                    cls: 'typeahead typeahead-long dropdown-menu',
11599 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11600 //                }
11601             ]
11602         };
11603         
11604         if(this.hasFeedback && !this.allowBlank){
11605             
11606             var feedback = {
11607                 tag: 'span',
11608                 cls: 'glyphicon form-control-feedback'
11609             };
11610
11611             combobox.cn.push(feedback);
11612         }
11613         
11614         if (align ==='left' && this.fieldLabel.length) {
11615             
11616                 Roo.log("left and has label");
11617                 cfg.cn = [
11618                     
11619                     {
11620                         tag: 'label',
11621                         'for' :  id,
11622                         cls : 'control-label col-sm-' + this.labelWidth,
11623                         html : this.fieldLabel
11624                         
11625                     },
11626                     {
11627                         cls : "col-sm-" + (12 - this.labelWidth), 
11628                         cn: [
11629                             combobox
11630                         ]
11631                     }
11632                     
11633                 ];
11634         } else if ( this.fieldLabel.length) {
11635                 Roo.log(" label");
11636                  cfg.cn = [
11637                    
11638                     {
11639                         tag: 'label',
11640                         //cls : 'input-group-addon',
11641                         html : this.fieldLabel
11642                         
11643                     },
11644                     
11645                     combobox
11646                     
11647                 ];
11648
11649         } else {
11650             
11651                 Roo.log(" no label && no align");
11652                 cfg = combobox
11653                      
11654                 
11655         }
11656          
11657         var settings=this;
11658         ['xs','sm','md','lg'].map(function(size){
11659             if (settings[size]) {
11660                 cfg.cls += ' col-' + size + '-' + settings[size];
11661             }
11662         });
11663         
11664         return cfg;
11665         
11666     },
11667     
11668     _initEventsCalled : false,
11669     
11670     // private
11671     initEvents: function()
11672     {
11673         
11674         if (this._initEventsCalled) { // as we call render... prevent looping...
11675             return;
11676         }
11677         this._initEventsCalled = true;
11678         
11679         if (!this.store) {
11680             throw "can not find store for combo";
11681         }
11682         
11683         this.store = Roo.factory(this.store, Roo.data);
11684         
11685         // if we are building from html. then this element is so complex, that we can not really
11686         // use the rendered HTML.
11687         // so we have to trash and replace the previous code.
11688         if (Roo.XComponent.build_from_html) {
11689             
11690             // remove this element....
11691             var e = this.el.dom, k=0;
11692             while (e ) { e = e.previousSibling;  ++k;}
11693
11694             this.el.remove();
11695             
11696             this.el=false;
11697             this.rendered = false;
11698             
11699             this.render(this.parent().getChildContainer(true), k);
11700             
11701             
11702             
11703         }
11704         
11705         
11706         /*
11707          * Touch Devices
11708          */
11709         
11710         if(Roo.isTouch && this.mobileTouchView){
11711             this.initTouchView();
11712             return;
11713         }
11714         
11715         if(this.tickable){
11716             this.initTickableEvents();
11717             return;
11718         }
11719         
11720         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11721         
11722         if(this.hiddenName){
11723             
11724             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11725             
11726             this.hiddenField.dom.value =
11727                 this.hiddenValue !== undefined ? this.hiddenValue :
11728                 this.value !== undefined ? this.value : '';
11729
11730             // prevent input submission
11731             this.el.dom.removeAttribute('name');
11732             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11733              
11734              
11735         }
11736         //if(Roo.isGecko){
11737         //    this.el.dom.setAttribute('autocomplete', 'off');
11738         //}
11739         
11740         var cls = 'x-combo-list';
11741         
11742         //this.list = new Roo.Layer({
11743         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
11744         //});
11745         
11746         var _this = this;
11747         
11748         (function(){
11749             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11750             _this.list.setWidth(lw);
11751         }).defer(100);
11752         
11753         this.list.on('mouseover', this.onViewOver, this);
11754         this.list.on('mousemove', this.onViewMove, this);
11755         
11756         this.list.on('scroll', this.onViewScroll, this);
11757         
11758         /*
11759         this.list.swallowEvent('mousewheel');
11760         this.assetHeight = 0;
11761
11762         if(this.title){
11763             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
11764             this.assetHeight += this.header.getHeight();
11765         }
11766
11767         this.innerList = this.list.createChild({cls:cls+'-inner'});
11768         this.innerList.on('mouseover', this.onViewOver, this);
11769         this.innerList.on('mousemove', this.onViewMove, this);
11770         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11771         
11772         if(this.allowBlank && !this.pageSize && !this.disableClear){
11773             this.footer = this.list.createChild({cls:cls+'-ft'});
11774             this.pageTb = new Roo.Toolbar(this.footer);
11775            
11776         }
11777         if(this.pageSize){
11778             this.footer = this.list.createChild({cls:cls+'-ft'});
11779             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
11780                     {pageSize: this.pageSize});
11781             
11782         }
11783         
11784         if (this.pageTb && this.allowBlank && !this.disableClear) {
11785             var _this = this;
11786             this.pageTb.add(new Roo.Toolbar.Fill(), {
11787                 cls: 'x-btn-icon x-btn-clear',
11788                 text: '&#160;',
11789                 handler: function()
11790                 {
11791                     _this.collapse();
11792                     _this.clearValue();
11793                     _this.onSelect(false, -1);
11794                 }
11795             });
11796         }
11797         if (this.footer) {
11798             this.assetHeight += this.footer.getHeight();
11799         }
11800         */
11801             
11802         if(!this.tpl){
11803             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
11804         }
11805
11806         this.view = new Roo.View(this.list, this.tpl, {
11807             singleSelect:true, store: this.store, selectedClass: this.selectedClass
11808         });
11809         //this.view.wrapEl.setDisplayed(false);
11810         this.view.on('click', this.onViewClick, this);
11811         
11812         
11813         
11814         this.store.on('beforeload', this.onBeforeLoad, this);
11815         this.store.on('load', this.onLoad, this);
11816         this.store.on('loadexception', this.onLoadException, this);
11817         /*
11818         if(this.resizable){
11819             this.resizer = new Roo.Resizable(this.list,  {
11820                pinned:true, handles:'se'
11821             });
11822             this.resizer.on('resize', function(r, w, h){
11823                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
11824                 this.listWidth = w;
11825                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
11826                 this.restrictHeight();
11827             }, this);
11828             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
11829         }
11830         */
11831         if(!this.editable){
11832             this.editable = true;
11833             this.setEditable(false);
11834         }
11835         
11836         /*
11837         
11838         if (typeof(this.events.add.listeners) != 'undefined') {
11839             
11840             this.addicon = this.wrap.createChild(
11841                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
11842        
11843             this.addicon.on('click', function(e) {
11844                 this.fireEvent('add', this);
11845             }, this);
11846         }
11847         if (typeof(this.events.edit.listeners) != 'undefined') {
11848             
11849             this.editicon = this.wrap.createChild(
11850                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
11851             if (this.addicon) {
11852                 this.editicon.setStyle('margin-left', '40px');
11853             }
11854             this.editicon.on('click', function(e) {
11855                 
11856                 // we fire even  if inothing is selected..
11857                 this.fireEvent('edit', this, this.lastData );
11858                 
11859             }, this);
11860         }
11861         */
11862         
11863         this.keyNav = new Roo.KeyNav(this.inputEl(), {
11864             "up" : function(e){
11865                 this.inKeyMode = true;
11866                 this.selectPrev();
11867             },
11868
11869             "down" : function(e){
11870                 if(!this.isExpanded()){
11871                     this.onTriggerClick();
11872                 }else{
11873                     this.inKeyMode = true;
11874                     this.selectNext();
11875                 }
11876             },
11877
11878             "enter" : function(e){
11879 //                this.onViewClick();
11880                 //return true;
11881                 this.collapse();
11882                 
11883                 if(this.fireEvent("specialkey", this, e)){
11884                     this.onViewClick(false);
11885                 }
11886                 
11887                 return true;
11888             },
11889
11890             "esc" : function(e){
11891                 this.collapse();
11892             },
11893
11894             "tab" : function(e){
11895                 this.collapse();
11896                 
11897                 if(this.fireEvent("specialkey", this, e)){
11898                     this.onViewClick(false);
11899                 }
11900                 
11901                 return true;
11902             },
11903
11904             scope : this,
11905
11906             doRelay : function(foo, bar, hname){
11907                 if(hname == 'down' || this.scope.isExpanded()){
11908                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11909                 }
11910                 return true;
11911             },
11912
11913             forceKeyDown: true
11914         });
11915         
11916         
11917         this.queryDelay = Math.max(this.queryDelay || 10,
11918                 this.mode == 'local' ? 10 : 250);
11919         
11920         
11921         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11922         
11923         if(this.typeAhead){
11924             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11925         }
11926         if(this.editable !== false){
11927             this.inputEl().on("keyup", this.onKeyUp, this);
11928         }
11929         if(this.forceSelection){
11930             this.inputEl().on('blur', this.doForce, this);
11931         }
11932         
11933         if(this.multiple){
11934             this.choices = this.el.select('ul.select2-choices', true).first();
11935             this.searchField = this.el.select('ul li.select2-search-field', true).first();
11936         }
11937     },
11938     
11939     initTickableEvents: function()
11940     {   
11941         this.createList();
11942         
11943         if(this.hiddenName){
11944             
11945             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11946             
11947             this.hiddenField.dom.value =
11948                 this.hiddenValue !== undefined ? this.hiddenValue :
11949                 this.value !== undefined ? this.value : '';
11950
11951             // prevent input submission
11952             this.el.dom.removeAttribute('name');
11953             this.hiddenField.dom.setAttribute('name', this.hiddenName);
11954              
11955              
11956         }
11957         
11958 //        this.list = this.el.select('ul.dropdown-menu',true).first();
11959         
11960         this.choices = this.el.select('ul.select2-choices', true).first();
11961         this.searchField = this.el.select('ul li.select2-search-field', true).first();
11962         if(this.triggerList){
11963             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
11964         }
11965          
11966         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
11967         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
11968         
11969         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
11970         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
11971         
11972         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
11973         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11974         
11975         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11976         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11977         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11978         
11979         this.okBtn.hide();
11980         this.cancelBtn.hide();
11981         
11982         var _this = this;
11983         
11984         (function(){
11985             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11986             _this.list.setWidth(lw);
11987         }).defer(100);
11988         
11989         this.list.on('mouseover', this.onViewOver, this);
11990         this.list.on('mousemove', this.onViewMove, this);
11991         
11992         this.list.on('scroll', this.onViewScroll, this);
11993         
11994         if(!this.tpl){
11995             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>';
11996         }
11997
11998         this.view = new Roo.View(this.list, this.tpl, {
11999             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12000         });
12001         
12002         //this.view.wrapEl.setDisplayed(false);
12003         this.view.on('click', this.onViewClick, this);
12004         
12005         
12006         
12007         this.store.on('beforeload', this.onBeforeLoad, this);
12008         this.store.on('load', this.onLoad, this);
12009         this.store.on('loadexception', this.onLoadException, this);
12010         
12011         if(this.editable){
12012             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12013                 "up" : function(e){
12014                     this.inKeyMode = true;
12015                     this.selectPrev();
12016                 },
12017
12018                 "down" : function(e){
12019                     this.inKeyMode = true;
12020                     this.selectNext();
12021                 },
12022
12023                 "enter" : function(e){
12024                     if(this.fireEvent("specialkey", this, e)){
12025                         this.onViewClick(false);
12026                     }
12027                     
12028                     return true;
12029                 },
12030
12031                 "esc" : function(e){
12032                     this.onTickableFooterButtonClick(e, false, false);
12033                 },
12034
12035                 "tab" : function(e){
12036                     this.fireEvent("specialkey", this, e);
12037                     
12038                     this.onTickableFooterButtonClick(e, false, false);
12039                     
12040                     return true;
12041                 },
12042
12043                 scope : this,
12044
12045                 doRelay : function(e, fn, key){
12046                     if(this.scope.isExpanded()){
12047                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12048                     }
12049                     return true;
12050                 },
12051
12052                 forceKeyDown: true
12053             });
12054         }
12055         
12056         this.queryDelay = Math.max(this.queryDelay || 10,
12057                 this.mode == 'local' ? 10 : 250);
12058         
12059         
12060         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12061         
12062         if(this.typeAhead){
12063             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12064         }
12065         
12066         if(this.editable !== false){
12067             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12068         }
12069         
12070     },
12071
12072     onDestroy : function(){
12073         if(this.view){
12074             this.view.setStore(null);
12075             this.view.el.removeAllListeners();
12076             this.view.el.remove();
12077             this.view.purgeListeners();
12078         }
12079         if(this.list){
12080             this.list.dom.innerHTML  = '';
12081         }
12082         
12083         if(this.store){
12084             this.store.un('beforeload', this.onBeforeLoad, this);
12085             this.store.un('load', this.onLoad, this);
12086             this.store.un('loadexception', this.onLoadException, this);
12087         }
12088         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12089     },
12090
12091     // private
12092     fireKey : function(e){
12093         if(e.isNavKeyPress() && !this.list.isVisible()){
12094             this.fireEvent("specialkey", this, e);
12095         }
12096     },
12097
12098     // private
12099     onResize: function(w, h){
12100 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12101 //        
12102 //        if(typeof w != 'number'){
12103 //            // we do not handle it!?!?
12104 //            return;
12105 //        }
12106 //        var tw = this.trigger.getWidth();
12107 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12108 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12109 //        var x = w - tw;
12110 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12111 //            
12112 //        //this.trigger.setStyle('left', x+'px');
12113 //        
12114 //        if(this.list && this.listWidth === undefined){
12115 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12116 //            this.list.setWidth(lw);
12117 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12118 //        }
12119         
12120     
12121         
12122     },
12123
12124     /**
12125      * Allow or prevent the user from directly editing the field text.  If false is passed,
12126      * the user will only be able to select from the items defined in the dropdown list.  This method
12127      * is the runtime equivalent of setting the 'editable' config option at config time.
12128      * @param {Boolean} value True to allow the user to directly edit the field text
12129      */
12130     setEditable : function(value){
12131         if(value == this.editable){
12132             return;
12133         }
12134         this.editable = value;
12135         if(!value){
12136             this.inputEl().dom.setAttribute('readOnly', true);
12137             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12138             this.inputEl().addClass('x-combo-noedit');
12139         }else{
12140             this.inputEl().dom.setAttribute('readOnly', false);
12141             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12142             this.inputEl().removeClass('x-combo-noedit');
12143         }
12144     },
12145
12146     // private
12147     
12148     onBeforeLoad : function(combo,opts){
12149         if(!this.hasFocus){
12150             return;
12151         }
12152          if (!opts.add) {
12153             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12154          }
12155         this.restrictHeight();
12156         this.selectedIndex = -1;
12157     },
12158
12159     // private
12160     onLoad : function(){
12161         
12162         this.hasQuery = false;
12163         
12164         if(!this.hasFocus){
12165             return;
12166         }
12167         
12168         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12169             this.loading.hide();
12170         }
12171              
12172         if(this.store.getCount() > 0){
12173             this.expand();
12174             this.restrictHeight();
12175             if(this.lastQuery == this.allQuery){
12176                 if(this.editable && !this.tickable){
12177                     this.inputEl().dom.select();
12178                 }
12179                 
12180                 if(
12181                     !this.selectByValue(this.value, true) &&
12182                     this.autoFocus && 
12183                     (
12184                         !this.store.lastOptions ||
12185                         typeof(this.store.lastOptions.add) == 'undefined' || 
12186                         this.store.lastOptions.add != true
12187                     )
12188                 ){
12189                     this.select(0, true);
12190                 }
12191             }else{
12192                 if(this.autoFocus){
12193                     this.selectNext();
12194                 }
12195                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12196                     this.taTask.delay(this.typeAheadDelay);
12197                 }
12198             }
12199         }else{
12200             this.onEmptyResults();
12201         }
12202         
12203         //this.el.focus();
12204     },
12205     // private
12206     onLoadException : function()
12207     {
12208         this.hasQuery = false;
12209         
12210         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12211             this.loading.hide();
12212         }
12213         
12214         if(this.tickable && this.editable){
12215             return;
12216         }
12217         
12218         this.collapse();
12219         
12220         Roo.log(this.store.reader.jsonData);
12221         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12222             // fixme
12223             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12224         }
12225         
12226         
12227     },
12228     // private
12229     onTypeAhead : function(){
12230         if(this.store.getCount() > 0){
12231             var r = this.store.getAt(0);
12232             var newValue = r.data[this.displayField];
12233             var len = newValue.length;
12234             var selStart = this.getRawValue().length;
12235             
12236             if(selStart != len){
12237                 this.setRawValue(newValue);
12238                 this.selectText(selStart, newValue.length);
12239             }
12240         }
12241     },
12242
12243     // private
12244     onSelect : function(record, index){
12245         
12246         if(this.fireEvent('beforeselect', this, record, index) !== false){
12247         
12248             this.setFromData(index > -1 ? record.data : false);
12249             
12250             this.collapse();
12251             this.fireEvent('select', this, record, index);
12252         }
12253     },
12254
12255     /**
12256      * Returns the currently selected field value or empty string if no value is set.
12257      * @return {String} value The selected value
12258      */
12259     getValue : function(){
12260         
12261         if(this.multiple){
12262             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12263         }
12264         
12265         if(this.valueField){
12266             return typeof this.value != 'undefined' ? this.value : '';
12267         }else{
12268             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12269         }
12270     },
12271
12272     /**
12273      * Clears any text/value currently set in the field
12274      */
12275     clearValue : function(){
12276         if(this.hiddenField){
12277             this.hiddenField.dom.value = '';
12278         }
12279         this.value = '';
12280         this.setRawValue('');
12281         this.lastSelectionText = '';
12282         this.lastData = false;
12283         
12284         var close = this.closeTriggerEl();
12285         
12286         if(close){
12287             close.hide();
12288         }
12289         
12290     },
12291
12292     /**
12293      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12294      * will be displayed in the field.  If the value does not match the data value of an existing item,
12295      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12296      * Otherwise the field will be blank (although the value will still be set).
12297      * @param {String} value The value to match
12298      */
12299     setValue : function(v){
12300         if(this.multiple){
12301             this.syncValue();
12302             return;
12303         }
12304         
12305         var text = v;
12306         if(this.valueField){
12307             var r = this.findRecord(this.valueField, v);
12308             if(r){
12309                 text = r.data[this.displayField];
12310             }else if(this.valueNotFoundText !== undefined){
12311                 text = this.valueNotFoundText;
12312             }
12313         }
12314         this.lastSelectionText = text;
12315         if(this.hiddenField){
12316             this.hiddenField.dom.value = v;
12317         }
12318         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12319         this.value = v;
12320         
12321         var close = this.closeTriggerEl();
12322         
12323         if(close){
12324             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12325         }
12326     },
12327     /**
12328      * @property {Object} the last set data for the element
12329      */
12330     
12331     lastData : false,
12332     /**
12333      * Sets the value of the field based on a object which is related to the record format for the store.
12334      * @param {Object} value the value to set as. or false on reset?
12335      */
12336     setFromData : function(o){
12337         
12338         if(this.multiple){
12339             this.addItem(o);
12340             return;
12341         }
12342             
12343         var dv = ''; // display value
12344         var vv = ''; // value value..
12345         this.lastData = o;
12346         if (this.displayField) {
12347             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12348         } else {
12349             // this is an error condition!!!
12350             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12351         }
12352         
12353         if(this.valueField){
12354             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12355         }
12356         
12357         var close = this.closeTriggerEl();
12358         
12359         if(close){
12360             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12361         }
12362         
12363         if(this.hiddenField){
12364             this.hiddenField.dom.value = vv;
12365             
12366             this.lastSelectionText = dv;
12367             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12368             this.value = vv;
12369             return;
12370         }
12371         // no hidden field.. - we store the value in 'value', but still display
12372         // display field!!!!
12373         this.lastSelectionText = dv;
12374         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12375         this.value = vv;
12376         
12377         
12378         
12379     },
12380     // private
12381     reset : function(){
12382         // overridden so that last data is reset..
12383         
12384         if(this.multiple){
12385             this.clearItem();
12386             return;
12387         }
12388         
12389         this.setValue(this.originalValue);
12390         this.clearInvalid();
12391         this.lastData = false;
12392         if (this.view) {
12393             this.view.clearSelections();
12394         }
12395     },
12396     // private
12397     findRecord : function(prop, value){
12398         var record;
12399         if(this.store.getCount() > 0){
12400             this.store.each(function(r){
12401                 if(r.data[prop] == value){
12402                     record = r;
12403                     return false;
12404                 }
12405                 return true;
12406             });
12407         }
12408         return record;
12409     },
12410     
12411     getName: function()
12412     {
12413         // returns hidden if it's set..
12414         if (!this.rendered) {return ''};
12415         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12416         
12417     },
12418     // private
12419     onViewMove : function(e, t){
12420         this.inKeyMode = false;
12421     },
12422
12423     // private
12424     onViewOver : function(e, t){
12425         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12426             return;
12427         }
12428         var item = this.view.findItemFromChild(t);
12429         
12430         if(item){
12431             var index = this.view.indexOf(item);
12432             this.select(index, false);
12433         }
12434     },
12435
12436     // private
12437     onViewClick : function(view, doFocus, el, e)
12438     {
12439         var index = this.view.getSelectedIndexes()[0];
12440         
12441         var r = this.store.getAt(index);
12442         
12443         if(this.tickable){
12444             
12445             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12446                 return;
12447             }
12448             
12449             var rm = false;
12450             var _this = this;
12451             
12452             Roo.each(this.tickItems, function(v,k){
12453                 
12454                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12455                     _this.tickItems.splice(k, 1);
12456                     
12457                     if(typeof(e) == 'undefined' && view == false){
12458                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12459                     }
12460                     
12461                     rm = true;
12462                     return;
12463                 }
12464             });
12465             
12466             if(rm){
12467                 return;
12468             }
12469             
12470             this.tickItems.push(r.data);
12471             
12472             if(typeof(e) == 'undefined' && view == false){
12473                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12474             }
12475                     
12476             return;
12477         }
12478         
12479         if(r){
12480             this.onSelect(r, index);
12481         }
12482         if(doFocus !== false && !this.blockFocus){
12483             this.inputEl().focus();
12484         }
12485     },
12486
12487     // private
12488     restrictHeight : function(){
12489         //this.innerList.dom.style.height = '';
12490         //var inner = this.innerList.dom;
12491         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12492         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12493         //this.list.beginUpdate();
12494         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12495         this.list.alignTo(this.inputEl(), this.listAlign);
12496         this.list.alignTo(this.inputEl(), this.listAlign);
12497         //this.list.endUpdate();
12498     },
12499
12500     // private
12501     onEmptyResults : function(){
12502         
12503         if(this.tickable && this.editable){
12504             this.restrictHeight();
12505             return;
12506         }
12507         
12508         this.collapse();
12509     },
12510
12511     /**
12512      * Returns true if the dropdown list is expanded, else false.
12513      */
12514     isExpanded : function(){
12515         return this.list.isVisible();
12516     },
12517
12518     /**
12519      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12520      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12521      * @param {String} value The data value of the item to select
12522      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12523      * selected item if it is not currently in view (defaults to true)
12524      * @return {Boolean} True if the value matched an item in the list, else false
12525      */
12526     selectByValue : function(v, scrollIntoView){
12527         if(v !== undefined && v !== null){
12528             var r = this.findRecord(this.valueField || this.displayField, v);
12529             if(r){
12530                 this.select(this.store.indexOf(r), scrollIntoView);
12531                 return true;
12532             }
12533         }
12534         return false;
12535     },
12536
12537     /**
12538      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12539      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12540      * @param {Number} index The zero-based index of the list item to select
12541      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12542      * selected item if it is not currently in view (defaults to true)
12543      */
12544     select : function(index, scrollIntoView){
12545         this.selectedIndex = index;
12546         this.view.select(index);
12547         if(scrollIntoView !== false){
12548             var el = this.view.getNode(index);
12549             /*
12550              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12551              */
12552             if(el){
12553                 this.list.scrollChildIntoView(el, false);
12554             }
12555         }
12556     },
12557
12558     // private
12559     selectNext : function(){
12560         var ct = this.store.getCount();
12561         if(ct > 0){
12562             if(this.selectedIndex == -1){
12563                 this.select(0);
12564             }else if(this.selectedIndex < ct-1){
12565                 this.select(this.selectedIndex+1);
12566             }
12567         }
12568     },
12569
12570     // private
12571     selectPrev : function(){
12572         var ct = this.store.getCount();
12573         if(ct > 0){
12574             if(this.selectedIndex == -1){
12575                 this.select(0);
12576             }else if(this.selectedIndex != 0){
12577                 this.select(this.selectedIndex-1);
12578             }
12579         }
12580     },
12581
12582     // private
12583     onKeyUp : function(e){
12584         if(this.editable !== false && !e.isSpecialKey()){
12585             this.lastKey = e.getKey();
12586             this.dqTask.delay(this.queryDelay);
12587         }
12588     },
12589
12590     // private
12591     validateBlur : function(){
12592         return !this.list || !this.list.isVisible();   
12593     },
12594
12595     // private
12596     initQuery : function(){
12597         
12598         var v = this.getRawValue();
12599         
12600         if(this.tickable && this.editable){
12601             v = this.tickableInputEl().getValue();
12602         }
12603         
12604         this.doQuery(v);
12605     },
12606
12607     // private
12608     doForce : function(){
12609         if(this.inputEl().dom.value.length > 0){
12610             this.inputEl().dom.value =
12611                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12612              
12613         }
12614     },
12615
12616     /**
12617      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12618      * query allowing the query action to be canceled if needed.
12619      * @param {String} query The SQL query to execute
12620      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12621      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12622      * saved in the current store (defaults to false)
12623      */
12624     doQuery : function(q, forceAll){
12625         
12626         if(q === undefined || q === null){
12627             q = '';
12628         }
12629         var qe = {
12630             query: q,
12631             forceAll: forceAll,
12632             combo: this,
12633             cancel:false
12634         };
12635         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12636             return false;
12637         }
12638         q = qe.query;
12639         
12640         forceAll = qe.forceAll;
12641         if(forceAll === true || (q.length >= this.minChars)){
12642             
12643             this.hasQuery = true;
12644             
12645             if(this.lastQuery != q || this.alwaysQuery){
12646                 this.lastQuery = q;
12647                 if(this.mode == 'local'){
12648                     this.selectedIndex = -1;
12649                     if(forceAll){
12650                         this.store.clearFilter();
12651                     }else{
12652                         
12653                         if(this.specialFilter){
12654                             this.fireEvent('specialfilter', this);
12655                             this.onLoad();
12656                             return;
12657                         }
12658                         
12659                         this.store.filter(this.displayField, q);
12660                     }
12661                     
12662                     this.store.fireEvent("datachanged", this.store);
12663                     
12664                     this.onLoad();
12665                     
12666                     
12667                 }else{
12668                     
12669                     this.store.baseParams[this.queryParam] = q;
12670                     
12671                     var options = {params : this.getParams(q)};
12672                     
12673                     if(this.loadNext){
12674                         options.add = true;
12675                         options.params.start = this.page * this.pageSize;
12676                     }
12677                     
12678                     this.store.load(options);
12679                     
12680                     /*
12681                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12682                      *  we should expand the list on onLoad
12683                      *  so command out it
12684                      */
12685 //                    this.expand();
12686                 }
12687             }else{
12688                 this.selectedIndex = -1;
12689                 this.onLoad();   
12690             }
12691         }
12692         
12693         this.loadNext = false;
12694     },
12695     
12696     // private
12697     getParams : function(q){
12698         var p = {};
12699         //p[this.queryParam] = q;
12700         
12701         if(this.pageSize){
12702             p.start = 0;
12703             p.limit = this.pageSize;
12704         }
12705         return p;
12706     },
12707
12708     /**
12709      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12710      */
12711     collapse : function(){
12712         if(!this.isExpanded()){
12713             return;
12714         }
12715         
12716         this.list.hide();
12717         
12718         if(this.tickable){
12719             this.hasFocus = false;
12720             this.okBtn.hide();
12721             this.cancelBtn.hide();
12722             this.trigger.show();
12723             
12724             if(this.editable){
12725                 this.tickableInputEl().dom.value = '';
12726                 this.tickableInputEl().blur();
12727             }
12728             
12729         }
12730         
12731         Roo.get(document).un('mousedown', this.collapseIf, this);
12732         Roo.get(document).un('mousewheel', this.collapseIf, this);
12733         if (!this.editable) {
12734             Roo.get(document).un('keydown', this.listKeyPress, this);
12735         }
12736         this.fireEvent('collapse', this);
12737     },
12738
12739     // private
12740     collapseIf : function(e){
12741         var in_combo  = e.within(this.el);
12742         var in_list =  e.within(this.list);
12743         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
12744         
12745         if (in_combo || in_list || is_list) {
12746             //e.stopPropagation();
12747             return;
12748         }
12749         
12750         if(this.tickable){
12751             this.onTickableFooterButtonClick(e, false, false);
12752         }
12753
12754         this.collapse();
12755         
12756     },
12757
12758     /**
12759      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
12760      */
12761     expand : function(){
12762        
12763         if(this.isExpanded() || !this.hasFocus){
12764             return;
12765         }
12766         
12767         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
12768         this.list.setWidth(lw);
12769         
12770         
12771          Roo.log('expand');
12772         
12773         this.list.show();
12774         
12775         this.restrictHeight();
12776         
12777         if(this.tickable){
12778             
12779             this.tickItems = Roo.apply([], this.item);
12780             
12781             this.okBtn.show();
12782             this.cancelBtn.show();
12783             this.trigger.hide();
12784             
12785             if(this.editable){
12786                 this.tickableInputEl().focus();
12787             }
12788             
12789         }
12790         
12791         Roo.get(document).on('mousedown', this.collapseIf, this);
12792         Roo.get(document).on('mousewheel', this.collapseIf, this);
12793         if (!this.editable) {
12794             Roo.get(document).on('keydown', this.listKeyPress, this);
12795         }
12796         
12797         this.fireEvent('expand', this);
12798     },
12799
12800     // private
12801     // Implements the default empty TriggerField.onTriggerClick function
12802     onTriggerClick : function(e)
12803     {
12804         Roo.log('trigger click');
12805         
12806         if(this.disabled || !this.triggerList){
12807             return;
12808         }
12809         
12810         this.page = 0;
12811         this.loadNext = false;
12812         
12813         if(this.isExpanded()){
12814             this.collapse();
12815             if (!this.blockFocus) {
12816                 this.inputEl().focus();
12817             }
12818             
12819         }else {
12820             this.hasFocus = true;
12821             if(this.triggerAction == 'all') {
12822                 this.doQuery(this.allQuery, true);
12823             } else {
12824                 this.doQuery(this.getRawValue());
12825             }
12826             if (!this.blockFocus) {
12827                 this.inputEl().focus();
12828             }
12829         }
12830     },
12831     
12832     onTickableTriggerClick : function(e)
12833     {
12834         if(this.disabled){
12835             return;
12836         }
12837         
12838         this.page = 0;
12839         this.loadNext = false;
12840         this.hasFocus = true;
12841         
12842         if(this.triggerAction == 'all') {
12843             this.doQuery(this.allQuery, true);
12844         } else {
12845             this.doQuery(this.getRawValue());
12846         }
12847     },
12848     
12849     onSearchFieldClick : function(e)
12850     {
12851         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
12852             this.onTickableFooterButtonClick(e, false, false);
12853             return;
12854         }
12855         
12856         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
12857             return;
12858         }
12859         
12860         this.page = 0;
12861         this.loadNext = false;
12862         this.hasFocus = true;
12863         
12864         if(this.triggerAction == 'all') {
12865             this.doQuery(this.allQuery, true);
12866         } else {
12867             this.doQuery(this.getRawValue());
12868         }
12869     },
12870     
12871     listKeyPress : function(e)
12872     {
12873         //Roo.log('listkeypress');
12874         // scroll to first matching element based on key pres..
12875         if (e.isSpecialKey()) {
12876             return false;
12877         }
12878         var k = String.fromCharCode(e.getKey()).toUpperCase();
12879         //Roo.log(k);
12880         var match  = false;
12881         var csel = this.view.getSelectedNodes();
12882         var cselitem = false;
12883         if (csel.length) {
12884             var ix = this.view.indexOf(csel[0]);
12885             cselitem  = this.store.getAt(ix);
12886             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
12887                 cselitem = false;
12888             }
12889             
12890         }
12891         
12892         this.store.each(function(v) { 
12893             if (cselitem) {
12894                 // start at existing selection.
12895                 if (cselitem.id == v.id) {
12896                     cselitem = false;
12897                 }
12898                 return true;
12899             }
12900                 
12901             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
12902                 match = this.store.indexOf(v);
12903                 return false;
12904             }
12905             return true;
12906         }, this);
12907         
12908         if (match === false) {
12909             return true; // no more action?
12910         }
12911         // scroll to?
12912         this.view.select(match);
12913         var sn = Roo.get(this.view.getSelectedNodes()[0])
12914         sn.scrollIntoView(sn.dom.parentNode, false);
12915     },
12916     
12917     onViewScroll : function(e, t){
12918         
12919         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){
12920             return;
12921         }
12922         
12923         this.hasQuery = true;
12924         
12925         this.loading = this.list.select('.loading', true).first();
12926         
12927         if(this.loading === null){
12928             this.list.createChild({
12929                 tag: 'div',
12930                 cls: 'loading select2-more-results select2-active',
12931                 html: 'Loading more results...'
12932             })
12933             
12934             this.loading = this.list.select('.loading', true).first();
12935             
12936             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
12937             
12938             this.loading.hide();
12939         }
12940         
12941         this.loading.show();
12942         
12943         var _combo = this;
12944         
12945         this.page++;
12946         this.loadNext = true;
12947         
12948         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
12949         
12950         return;
12951     },
12952     
12953     addItem : function(o)
12954     {   
12955         var dv = ''; // display value
12956         
12957         if (this.displayField) {
12958             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12959         } else {
12960             // this is an error condition!!!
12961             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12962         }
12963         
12964         if(!dv.length){
12965             return;
12966         }
12967         
12968         var choice = this.choices.createChild({
12969             tag: 'li',
12970             cls: 'select2-search-choice',
12971             cn: [
12972                 {
12973                     tag: 'div',
12974                     html: dv
12975                 },
12976                 {
12977                     tag: 'a',
12978                     href: '#',
12979                     cls: 'select2-search-choice-close',
12980                     tabindex: '-1'
12981                 }
12982             ]
12983             
12984         }, this.searchField);
12985         
12986         var close = choice.select('a.select2-search-choice-close', true).first()
12987         
12988         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
12989         
12990         this.item.push(o);
12991         
12992         this.lastData = o;
12993         
12994         this.syncValue();
12995         
12996         this.inputEl().dom.value = '';
12997         
12998         this.validate();
12999     },
13000     
13001     onRemoveItem : function(e, _self, o)
13002     {
13003         e.preventDefault();
13004         
13005         this.lastItem = Roo.apply([], this.item);
13006         
13007         var index = this.item.indexOf(o.data) * 1;
13008         
13009         if( index < 0){
13010             Roo.log('not this item?!');
13011             return;
13012         }
13013         
13014         this.item.splice(index, 1);
13015         o.item.remove();
13016         
13017         this.syncValue();
13018         
13019         this.fireEvent('remove', this, e);
13020         
13021         this.validate();
13022         
13023     },
13024     
13025     syncValue : function()
13026     {
13027         if(!this.item.length){
13028             this.clearValue();
13029             return;
13030         }
13031             
13032         var value = [];
13033         var _this = this;
13034         Roo.each(this.item, function(i){
13035             if(_this.valueField){
13036                 value.push(i[_this.valueField]);
13037                 return;
13038             }
13039
13040             value.push(i);
13041         });
13042
13043         this.value = value.join(',');
13044
13045         if(this.hiddenField){
13046             this.hiddenField.dom.value = this.value;
13047         }
13048         
13049         this.store.fireEvent("datachanged", this.store);
13050     },
13051     
13052     clearItem : function()
13053     {
13054         if(!this.multiple){
13055             return;
13056         }
13057         
13058         this.item = [];
13059         
13060         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13061            c.remove();
13062         });
13063         
13064         this.syncValue();
13065         
13066         this.validate();
13067         
13068         if(this.tickable && !Roo.isTouch){
13069             this.view.refresh();
13070         }
13071     },
13072     
13073     inputEl: function ()
13074     {
13075         if(Roo.isTouch && this.mobileTouchView){
13076             return this.el.select('input.form-control',true).first();
13077         }
13078         
13079         if(this.tickable){
13080             return this.searchField;
13081         }
13082         
13083         return this.el.select('input.form-control',true).first();
13084     },
13085     
13086     
13087     onTickableFooterButtonClick : function(e, btn, el)
13088     {
13089         e.preventDefault();
13090         
13091         this.lastItem = Roo.apply([], this.item);
13092         
13093         if(btn && btn.name == 'cancel'){
13094             this.tickItems = Roo.apply([], this.item);
13095             this.collapse();
13096             return;
13097         }
13098         
13099         this.clearItem();
13100         
13101         var _this = this;
13102         
13103         Roo.each(this.tickItems, function(o){
13104             _this.addItem(o);
13105         });
13106         
13107         this.collapse();
13108         
13109     },
13110     
13111     validate : function()
13112     {
13113         var v = this.getRawValue();
13114         
13115         if(this.multiple){
13116             v = this.getValue();
13117         }
13118         
13119         if(this.disabled || this.allowBlank || v.length){
13120             this.markValid();
13121             return true;
13122         }
13123         
13124         this.markInvalid();
13125         return false;
13126     },
13127     
13128     tickableInputEl : function()
13129     {
13130         if(!this.tickable || !this.editable){
13131             return this.inputEl();
13132         }
13133         
13134         return this.inputEl().select('.select2-search-field-input', true).first();
13135     },
13136     
13137     
13138     getAutoCreateTouchView : function()
13139     {
13140         var id = Roo.id();
13141         
13142         var cfg = {
13143             cls: 'form-group' //input-group
13144         };
13145         
13146         var input =  {
13147             tag: 'input',
13148             id : id,
13149             type : this.inputType,
13150             cls : 'form-control x-combo-noedit',
13151             autocomplete: 'new-password',
13152             placeholder : this.placeholder || '',
13153             readonly : true
13154         };
13155         
13156         if (this.name) {
13157             input.name = this.name;
13158         }
13159         
13160         if (this.size) {
13161             input.cls += ' input-' + this.size;
13162         }
13163         
13164         if (this.disabled) {
13165             input.disabled = true;
13166         }
13167         
13168         var inputblock = {
13169             cls : '',
13170             cn : [
13171                 input
13172             ]
13173         };
13174         
13175         if(this.before){
13176             inputblock.cls += ' input-group';
13177             
13178             inputblock.cn.unshift({
13179                 tag :'span',
13180                 cls : 'input-group-addon',
13181                 html : this.before
13182             });
13183         }
13184         
13185         if(this.removable && !this.multiple){
13186             inputblock.cls += ' roo-removable';
13187             
13188             inputblock.cn.push({
13189                 tag: 'button',
13190                 html : 'x',
13191                 cls : 'roo-combo-removable-btn close'
13192             });
13193         }
13194
13195         if(this.hasFeedback && !this.allowBlank){
13196             
13197             inputblock.cls += ' has-feedback';
13198             
13199             inputblock.cn.push({
13200                 tag: 'span',
13201                 cls: 'glyphicon form-control-feedback'
13202             });
13203             
13204         }
13205         
13206         if (this.after) {
13207             
13208             inputblock.cls += (this.before) ? '' : ' input-group';
13209             
13210             inputblock.cn.push({
13211                 tag :'span',
13212                 cls : 'input-group-addon',
13213                 html : this.after
13214             });
13215         }
13216
13217         var box = {
13218             tag: 'div',
13219             cn: [
13220                 {
13221                     tag: 'input',
13222                     type : 'hidden',
13223                     cls: 'form-hidden-field'
13224                 },
13225                 inputblock
13226             ]
13227             
13228         };
13229         
13230         if(this.multiple){
13231             box = {
13232                 tag: 'div',
13233                 cn: [
13234                     {
13235                         tag: 'input',
13236                         type : 'hidden',
13237                         cls: 'form-hidden-field'
13238                     },
13239                     {
13240                         tag: 'ul',
13241                         cls: 'select2-choices',
13242                         cn:[
13243                             {
13244                                 tag: 'li',
13245                                 cls: 'select2-search-field',
13246                                 cn: [
13247
13248                                     inputblock
13249                                 ]
13250                             }
13251                         ]
13252                     }
13253                 ]
13254             }
13255         };
13256         
13257         var combobox = {
13258             cls: 'select2-container input-group',
13259             cn: [
13260                 box
13261             ]
13262         };
13263         
13264         if(this.multiple){
13265             combobox.cls += ' select2-container-multi';
13266         }
13267         
13268         var align = this.labelAlign || this.parentLabelAlign();
13269         
13270         cfg.cn = combobox;
13271         
13272         if(this.fieldLabel.length){
13273             
13274             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13275             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13276             
13277             cfg.cn = [
13278                 {
13279                     tag: 'label',
13280                     cls : 'control-label ' + lw,
13281                     html : this.fieldLabel
13282
13283                 },
13284                 {
13285                     cls : cw, 
13286                     cn: [
13287                         combobox
13288                     ]
13289                 }
13290             ];
13291         }
13292         
13293         var settings = this;
13294         
13295         ['xs','sm','md','lg'].map(function(size){
13296             if (settings[size]) {
13297                 cfg.cls += ' col-' + size + '-' + settings[size];
13298             }
13299         });
13300         
13301         return cfg;
13302     },
13303     
13304     initTouchView : function()
13305     {
13306         this.renderTouchView();
13307         
13308         this.touchViewEl.on('scroll', function(){
13309             this.el.dom.scrollTop = 0;
13310         }, this);
13311         
13312         this.inputEl().on("click", this.showTouchView, this);
13313         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13314         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13315         
13316         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13317         
13318         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13319         this.store.on('load', this.onTouchViewLoad, this);
13320         this.store.on('loadexception', this.onTouchViewLoadException, this);
13321         
13322         if(this.hiddenName){
13323             
13324             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13325             
13326             this.hiddenField.dom.value =
13327                 this.hiddenValue !== undefined ? this.hiddenValue :
13328                 this.value !== undefined ? this.value : '';
13329         
13330             this.el.dom.removeAttribute('name');
13331             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13332         }
13333         
13334         if(this.multiple){
13335             this.choices = this.el.select('ul.select2-choices', true).first();
13336             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13337         }
13338         
13339         if(this.removable && !this.multiple){
13340             var close = this.closeTriggerEl();
13341             if(close){
13342                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13343                 close.on('click', this.removeBtnClick, this, close);
13344             }
13345         }
13346         
13347         return;
13348         
13349         
13350     },
13351     
13352     renderTouchView : function()
13353     {
13354         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13355         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13356         
13357         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13358         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13359         
13360         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13361         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13362         this.touchViewBodyEl.setStyle('overflow', 'auto');
13363         
13364         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13365         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13366         
13367         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13368         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13369         
13370     },
13371     
13372     showTouchView : function()
13373     {
13374         this.touchViewHeaderEl.hide();
13375
13376         if(this.fieldLabel.length){
13377             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13378             this.touchViewHeaderEl.show();
13379         }
13380
13381         this.touchViewEl.show();
13382
13383         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13384         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13385
13386         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13387
13388         if(this.fieldLabel.length){
13389             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13390         }
13391         
13392         this.touchViewBodyEl.setHeight(bodyHeight);
13393
13394         if(this.animate){
13395             var _this = this;
13396             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13397         }else{
13398             this.touchViewEl.addClass('in');
13399         }
13400
13401         this.doTouchViewQuery();
13402         
13403     },
13404     
13405     hideTouchView : function()
13406     {
13407         this.touchViewEl.removeClass('in');
13408
13409         if(this.animate){
13410             var _this = this;
13411             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13412         }else{
13413             this.touchViewEl.setStyle('display', 'none');
13414         }
13415         
13416     },
13417     
13418     setTouchViewValue : function()
13419     {
13420         if(this.multiple){
13421             this.clearItem();
13422         
13423             var _this = this;
13424
13425             Roo.each(this.tickItems, function(o){
13426                 this.addItem(o);
13427             }, this);
13428         }
13429         
13430         this.hideTouchView();
13431     },
13432     
13433     doTouchViewQuery : function()
13434     {
13435         var qe = {
13436             query: '',
13437             forceAll: true,
13438             combo: this,
13439             cancel:false
13440         };
13441         
13442         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13443             return false;
13444         }
13445         
13446         if(!this.alwaysQuery || this.mode == 'local'){
13447             this.onTouchViewLoad();
13448             return;
13449         }
13450         
13451         this.store.load();
13452     },
13453     
13454     onTouchViewBeforeLoad : function(combo,opts)
13455     {
13456         return;
13457     },
13458
13459     // private
13460     onTouchViewLoad : function()
13461     {
13462         if(this.store.getCount() < 1){
13463             this.onTouchViewEmptyResults();
13464             return;
13465         }
13466         
13467         this.clearTouchView();
13468         
13469         var rawValue = this.getRawValue();
13470         
13471         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13472         
13473         this.tickItems = [];
13474         
13475         this.store.data.each(function(d, rowIndex){
13476             var row = this.touchViewListGroup.createChild(template);
13477             
13478             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13479                 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = d.data[this.displayField];
13480             }
13481             
13482             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13483                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13484             }
13485             
13486             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13487                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13488                 this.tickItems.push(d.data);
13489             }
13490             
13491             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13492             
13493         }, this);
13494         
13495         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13496         
13497         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13498
13499         if(this.fieldLabel.length){
13500             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13501         }
13502
13503         var listHeight = this.touchViewListGroup.getHeight();
13504         
13505         var _this = this;
13506         
13507         if(firstChecked && listHeight > bodyHeight){
13508             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13509         }
13510         
13511     },
13512     
13513     onTouchViewLoadException : function()
13514     {
13515         this.hideTouchView();
13516     },
13517     
13518     onTouchViewEmptyResults : function()
13519     {
13520         this.clearTouchView();
13521         
13522         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13523         
13524         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13525         
13526     },
13527     
13528     clearTouchView : function()
13529     {
13530         this.touchViewListGroup.dom.innerHTML = '';
13531     },
13532     
13533     onTouchViewClick : function(e, el, o)
13534     {
13535         e.preventDefault();
13536         
13537         var row = o.row;
13538         var rowIndex = o.rowIndex;
13539         
13540         var r = this.store.getAt(rowIndex);
13541         
13542         if(!this.multiple){
13543             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13544                 c.dom.removeAttribute('checked');
13545             }, this);
13546             
13547             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13548         
13549             this.setFromData(r.data);
13550             
13551             var close = this.closeTriggerEl();
13552         
13553             if(close){
13554                 close.show();
13555             }
13556
13557             this.hideTouchView();
13558             
13559             this.fireEvent('select', this, r, rowIndex);
13560             
13561             return;
13562         }
13563         
13564         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13565             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13566             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13567             return;
13568         }
13569         
13570         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13571         this.addItem(r.data);
13572         this.tickItems.push(r.data);
13573         
13574     }
13575     
13576
13577     /** 
13578     * @cfg {Boolean} grow 
13579     * @hide 
13580     */
13581     /** 
13582     * @cfg {Number} growMin 
13583     * @hide 
13584     */
13585     /** 
13586     * @cfg {Number} growMax 
13587     * @hide 
13588     */
13589     /**
13590      * @hide
13591      * @method autoSize
13592      */
13593 });
13594
13595 Roo.apply(Roo.bootstrap.ComboBox,  {
13596     
13597     header : {
13598         tag: 'div',
13599         cls: 'modal-header',
13600         cn: [
13601             {
13602                 tag: 'h4',
13603                 cls: 'modal-title'
13604             }
13605         ]
13606     },
13607     
13608     body : {
13609         tag: 'div',
13610         cls: 'modal-body',
13611         cn: [
13612             {
13613                 tag: 'ul',
13614                 cls: 'list-group'
13615             }
13616         ]
13617     },
13618     
13619     listItemRadio : {
13620         tag: 'li',
13621         cls: 'list-group-item',
13622         cn: [
13623             {
13624                 tag: 'span',
13625                 cls: 'roo-combobox-list-group-item-value'
13626             },
13627             {
13628                 tag: 'div',
13629                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13630                 cn: [
13631                     {
13632                         tag: 'input',
13633                         type: 'radio'
13634                     },
13635                     {
13636                         tag: 'label'
13637                     }
13638                 ]
13639             }
13640         ]
13641     },
13642     
13643     listItemCheckbox : {
13644         tag: 'li',
13645         cls: 'list-group-item',
13646         cn: [
13647             {
13648                 tag: 'span',
13649                 cls: 'roo-combobox-list-group-item-value'
13650             },
13651             {
13652                 tag: 'div',
13653                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13654                 cn: [
13655                     {
13656                         tag: 'input',
13657                         type: 'checkbox'
13658                     },
13659                     {
13660                         tag: 'label'
13661                     }
13662                 ]
13663             }
13664         ]
13665     },
13666     
13667     emptyResult : {
13668         tag: 'div',
13669         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13670     },
13671     
13672     footer : {
13673         tag: 'div',
13674         cls: 'modal-footer',
13675         cn: [
13676             {
13677                 tag: 'div',
13678                 cls: 'row',
13679                 cn: [
13680                     {
13681                         tag: 'div',
13682                         cls: 'col-xs-6 text-left',
13683                         cn: {
13684                             tag: 'button',
13685                             cls: 'btn btn-danger roo-touch-view-cancel',
13686                             html: 'Cancel'
13687                         }
13688                     },
13689                     {
13690                         tag: 'div',
13691                         cls: 'col-xs-6 text-right',
13692                         cn: {
13693                             tag: 'button',
13694                             cls: 'btn btn-success roo-touch-view-ok',
13695                             html: 'OK'
13696                         }
13697                     }
13698                 ]
13699             }
13700         ]
13701         
13702     }
13703 });
13704
13705 Roo.apply(Roo.bootstrap.ComboBox,  {
13706     
13707     touchViewTemplate : {
13708         tag: 'div',
13709         cls: 'modal fade roo-combobox-touch-view',
13710         cn: [
13711             {
13712                 tag: 'div',
13713                 cls: 'modal-dialog',
13714                 cn: [
13715                     {
13716                         tag: 'div',
13717                         cls: 'modal-content',
13718                         cn: [
13719                             Roo.bootstrap.ComboBox.header,
13720                             Roo.bootstrap.ComboBox.body,
13721                             Roo.bootstrap.ComboBox.footer
13722                         ]
13723                     }
13724                 ]
13725             }
13726         ]
13727     }
13728 });/*
13729  * Based on:
13730  * Ext JS Library 1.1.1
13731  * Copyright(c) 2006-2007, Ext JS, LLC.
13732  *
13733  * Originally Released Under LGPL - original licence link has changed is not relivant.
13734  *
13735  * Fork - LGPL
13736  * <script type="text/javascript">
13737  */
13738
13739 /**
13740  * @class Roo.View
13741  * @extends Roo.util.Observable
13742  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
13743  * This class also supports single and multi selection modes. <br>
13744  * Create a data model bound view:
13745  <pre><code>
13746  var store = new Roo.data.Store(...);
13747
13748  var view = new Roo.View({
13749     el : "my-element",
13750     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
13751  
13752     singleSelect: true,
13753     selectedClass: "ydataview-selected",
13754     store: store
13755  });
13756
13757  // listen for node click?
13758  view.on("click", function(vw, index, node, e){
13759  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
13760  });
13761
13762  // load XML data
13763  dataModel.load("foobar.xml");
13764  </code></pre>
13765  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
13766  * <br><br>
13767  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
13768  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
13769  * 
13770  * Note: old style constructor is still suported (container, template, config)
13771  * 
13772  * @constructor
13773  * Create a new View
13774  * @param {Object} config The config object
13775  * 
13776  */
13777 Roo.View = function(config, depreciated_tpl, depreciated_config){
13778     
13779     this.parent = false;
13780     
13781     if (typeof(depreciated_tpl) == 'undefined') {
13782         // new way.. - universal constructor.
13783         Roo.apply(this, config);
13784         this.el  = Roo.get(this.el);
13785     } else {
13786         // old format..
13787         this.el  = Roo.get(config);
13788         this.tpl = depreciated_tpl;
13789         Roo.apply(this, depreciated_config);
13790     }
13791     this.wrapEl  = this.el.wrap().wrap();
13792     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
13793     
13794     
13795     if(typeof(this.tpl) == "string"){
13796         this.tpl = new Roo.Template(this.tpl);
13797     } else {
13798         // support xtype ctors..
13799         this.tpl = new Roo.factory(this.tpl, Roo);
13800     }
13801     
13802     
13803     this.tpl.compile();
13804     
13805     /** @private */
13806     this.addEvents({
13807         /**
13808          * @event beforeclick
13809          * Fires before a click is processed. Returns false to cancel the default action.
13810          * @param {Roo.View} this
13811          * @param {Number} index The index of the target node
13812          * @param {HTMLElement} node The target node
13813          * @param {Roo.EventObject} e The raw event object
13814          */
13815             "beforeclick" : true,
13816         /**
13817          * @event click
13818          * Fires when a template node is clicked.
13819          * @param {Roo.View} this
13820          * @param {Number} index The index of the target node
13821          * @param {HTMLElement} node The target node
13822          * @param {Roo.EventObject} e The raw event object
13823          */
13824             "click" : true,
13825         /**
13826          * @event dblclick
13827          * Fires when a template node is double clicked.
13828          * @param {Roo.View} this
13829          * @param {Number} index The index of the target node
13830          * @param {HTMLElement} node The target node
13831          * @param {Roo.EventObject} e The raw event object
13832          */
13833             "dblclick" : true,
13834         /**
13835          * @event contextmenu
13836          * Fires when a template node is right clicked.
13837          * @param {Roo.View} this
13838          * @param {Number} index The index of the target node
13839          * @param {HTMLElement} node The target node
13840          * @param {Roo.EventObject} e The raw event object
13841          */
13842             "contextmenu" : true,
13843         /**
13844          * @event selectionchange
13845          * Fires when the selected nodes change.
13846          * @param {Roo.View} this
13847          * @param {Array} selections Array of the selected nodes
13848          */
13849             "selectionchange" : true,
13850     
13851         /**
13852          * @event beforeselect
13853          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
13854          * @param {Roo.View} this
13855          * @param {HTMLElement} node The node to be selected
13856          * @param {Array} selections Array of currently selected nodes
13857          */
13858             "beforeselect" : true,
13859         /**
13860          * @event preparedata
13861          * Fires on every row to render, to allow you to change the data.
13862          * @param {Roo.View} this
13863          * @param {Object} data to be rendered (change this)
13864          */
13865           "preparedata" : true
13866           
13867           
13868         });
13869
13870
13871
13872     this.el.on({
13873         "click": this.onClick,
13874         "dblclick": this.onDblClick,
13875         "contextmenu": this.onContextMenu,
13876         scope:this
13877     });
13878
13879     this.selections = [];
13880     this.nodes = [];
13881     this.cmp = new Roo.CompositeElementLite([]);
13882     if(this.store){
13883         this.store = Roo.factory(this.store, Roo.data);
13884         this.setStore(this.store, true);
13885     }
13886     
13887     if ( this.footer && this.footer.xtype) {
13888            
13889          var fctr = this.wrapEl.appendChild(document.createElement("div"));
13890         
13891         this.footer.dataSource = this.store
13892         this.footer.container = fctr;
13893         this.footer = Roo.factory(this.footer, Roo);
13894         fctr.insertFirst(this.el);
13895         
13896         // this is a bit insane - as the paging toolbar seems to detach the el..
13897 //        dom.parentNode.parentNode.parentNode
13898          // they get detached?
13899     }
13900     
13901     
13902     Roo.View.superclass.constructor.call(this);
13903     
13904     
13905 };
13906
13907 Roo.extend(Roo.View, Roo.util.Observable, {
13908     
13909      /**
13910      * @cfg {Roo.data.Store} store Data store to load data from.
13911      */
13912     store : false,
13913     
13914     /**
13915      * @cfg {String|Roo.Element} el The container element.
13916      */
13917     el : '',
13918     
13919     /**
13920      * @cfg {String|Roo.Template} tpl The template used by this View 
13921      */
13922     tpl : false,
13923     /**
13924      * @cfg {String} dataName the named area of the template to use as the data area
13925      *                          Works with domtemplates roo-name="name"
13926      */
13927     dataName: false,
13928     /**
13929      * @cfg {String} selectedClass The css class to add to selected nodes
13930      */
13931     selectedClass : "x-view-selected",
13932      /**
13933      * @cfg {String} emptyText The empty text to show when nothing is loaded.
13934      */
13935     emptyText : "",
13936     
13937     /**
13938      * @cfg {String} text to display on mask (default Loading)
13939      */
13940     mask : false,
13941     /**
13942      * @cfg {Boolean} multiSelect Allow multiple selection
13943      */
13944     multiSelect : false,
13945     /**
13946      * @cfg {Boolean} singleSelect Allow single selection
13947      */
13948     singleSelect:  false,
13949     
13950     /**
13951      * @cfg {Boolean} toggleSelect - selecting 
13952      */
13953     toggleSelect : false,
13954     
13955     /**
13956      * @cfg {Boolean} tickable - selecting 
13957      */
13958     tickable : false,
13959     
13960     /**
13961      * Returns the element this view is bound to.
13962      * @return {Roo.Element}
13963      */
13964     getEl : function(){
13965         return this.wrapEl;
13966     },
13967     
13968     
13969
13970     /**
13971      * Refreshes the view. - called by datachanged on the store. - do not call directly.
13972      */
13973     refresh : function(){
13974         //Roo.log('refresh');
13975         var t = this.tpl;
13976         
13977         // if we are using something like 'domtemplate', then
13978         // the what gets used is:
13979         // t.applySubtemplate(NAME, data, wrapping data..)
13980         // the outer template then get' applied with
13981         //     the store 'extra data'
13982         // and the body get's added to the
13983         //      roo-name="data" node?
13984         //      <span class='roo-tpl-{name}'></span> ?????
13985         
13986         
13987         
13988         this.clearSelections();
13989         this.el.update("");
13990         var html = [];
13991         var records = this.store.getRange();
13992         if(records.length < 1) {
13993             
13994             // is this valid??  = should it render a template??
13995             
13996             this.el.update(this.emptyText);
13997             return;
13998         }
13999         var el = this.el;
14000         if (this.dataName) {
14001             this.el.update(t.apply(this.store.meta)); //????
14002             el = this.el.child('.roo-tpl-' + this.dataName);
14003         }
14004         
14005         for(var i = 0, len = records.length; i < len; i++){
14006             var data = this.prepareData(records[i].data, i, records[i]);
14007             this.fireEvent("preparedata", this, data, i, records[i]);
14008             
14009             var d = Roo.apply({}, data);
14010             
14011             if(this.tickable){
14012                 Roo.apply(d, {'roo-id' : Roo.id()});
14013                 
14014                 var _this = this;
14015             
14016                 Roo.each(this.parent.item, function(item){
14017                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14018                         return;
14019                     }
14020                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14021                 });
14022             }
14023             
14024             html[html.length] = Roo.util.Format.trim(
14025                 this.dataName ?
14026                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14027                     t.apply(d)
14028             );
14029         }
14030         
14031         
14032         
14033         el.update(html.join(""));
14034         this.nodes = el.dom.childNodes;
14035         this.updateIndexes(0);
14036     },
14037     
14038
14039     /**
14040      * Function to override to reformat the data that is sent to
14041      * the template for each node.
14042      * DEPRICATED - use the preparedata event handler.
14043      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14044      * a JSON object for an UpdateManager bound view).
14045      */
14046     prepareData : function(data, index, record)
14047     {
14048         this.fireEvent("preparedata", this, data, index, record);
14049         return data;
14050     },
14051
14052     onUpdate : function(ds, record){
14053         // Roo.log('on update');   
14054         this.clearSelections();
14055         var index = this.store.indexOf(record);
14056         var n = this.nodes[index];
14057         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14058         n.parentNode.removeChild(n);
14059         this.updateIndexes(index, index);
14060     },
14061
14062     
14063     
14064 // --------- FIXME     
14065     onAdd : function(ds, records, index)
14066     {
14067         //Roo.log(['on Add', ds, records, index] );        
14068         this.clearSelections();
14069         if(this.nodes.length == 0){
14070             this.refresh();
14071             return;
14072         }
14073         var n = this.nodes[index];
14074         for(var i = 0, len = records.length; i < len; i++){
14075             var d = this.prepareData(records[i].data, i, records[i]);
14076             if(n){
14077                 this.tpl.insertBefore(n, d);
14078             }else{
14079                 
14080                 this.tpl.append(this.el, d);
14081             }
14082         }
14083         this.updateIndexes(index);
14084     },
14085
14086     onRemove : function(ds, record, index){
14087        // Roo.log('onRemove');
14088         this.clearSelections();
14089         var el = this.dataName  ?
14090             this.el.child('.roo-tpl-' + this.dataName) :
14091             this.el; 
14092         
14093         el.dom.removeChild(this.nodes[index]);
14094         this.updateIndexes(index);
14095     },
14096
14097     /**
14098      * Refresh an individual node.
14099      * @param {Number} index
14100      */
14101     refreshNode : function(index){
14102         this.onUpdate(this.store, this.store.getAt(index));
14103     },
14104
14105     updateIndexes : function(startIndex, endIndex){
14106         var ns = this.nodes;
14107         startIndex = startIndex || 0;
14108         endIndex = endIndex || ns.length - 1;
14109         for(var i = startIndex; i <= endIndex; i++){
14110             ns[i].nodeIndex = i;
14111         }
14112     },
14113
14114     /**
14115      * Changes the data store this view uses and refresh the view.
14116      * @param {Store} store
14117      */
14118     setStore : function(store, initial){
14119         if(!initial && this.store){
14120             this.store.un("datachanged", this.refresh);
14121             this.store.un("add", this.onAdd);
14122             this.store.un("remove", this.onRemove);
14123             this.store.un("update", this.onUpdate);
14124             this.store.un("clear", this.refresh);
14125             this.store.un("beforeload", this.onBeforeLoad);
14126             this.store.un("load", this.onLoad);
14127             this.store.un("loadexception", this.onLoad);
14128         }
14129         if(store){
14130           
14131             store.on("datachanged", this.refresh, this);
14132             store.on("add", this.onAdd, this);
14133             store.on("remove", this.onRemove, this);
14134             store.on("update", this.onUpdate, this);
14135             store.on("clear", this.refresh, this);
14136             store.on("beforeload", this.onBeforeLoad, this);
14137             store.on("load", this.onLoad, this);
14138             store.on("loadexception", this.onLoad, this);
14139         }
14140         
14141         if(store){
14142             this.refresh();
14143         }
14144     },
14145     /**
14146      * onbeforeLoad - masks the loading area.
14147      *
14148      */
14149     onBeforeLoad : function(store,opts)
14150     {
14151          //Roo.log('onBeforeLoad');   
14152         if (!opts.add) {
14153             this.el.update("");
14154         }
14155         this.el.mask(this.mask ? this.mask : "Loading" ); 
14156     },
14157     onLoad : function ()
14158     {
14159         this.el.unmask();
14160     },
14161     
14162
14163     /**
14164      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14165      * @param {HTMLElement} node
14166      * @return {HTMLElement} The template node
14167      */
14168     findItemFromChild : function(node){
14169         var el = this.dataName  ?
14170             this.el.child('.roo-tpl-' + this.dataName,true) :
14171             this.el.dom; 
14172         
14173         if(!node || node.parentNode == el){
14174                     return node;
14175             }
14176             var p = node.parentNode;
14177             while(p && p != el){
14178             if(p.parentNode == el){
14179                 return p;
14180             }
14181             p = p.parentNode;
14182         }
14183             return null;
14184     },
14185
14186     /** @ignore */
14187     onClick : function(e){
14188         var item = this.findItemFromChild(e.getTarget());
14189         if(item){
14190             var index = this.indexOf(item);
14191             if(this.onItemClick(item, index, e) !== false){
14192                 this.fireEvent("click", this, index, item, e);
14193             }
14194         }else{
14195             this.clearSelections();
14196         }
14197     },
14198
14199     /** @ignore */
14200     onContextMenu : function(e){
14201         var item = this.findItemFromChild(e.getTarget());
14202         if(item){
14203             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14204         }
14205     },
14206
14207     /** @ignore */
14208     onDblClick : function(e){
14209         var item = this.findItemFromChild(e.getTarget());
14210         if(item){
14211             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14212         }
14213     },
14214
14215     onItemClick : function(item, index, e)
14216     {
14217         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14218             return false;
14219         }
14220         if (this.toggleSelect) {
14221             var m = this.isSelected(item) ? 'unselect' : 'select';
14222             //Roo.log(m);
14223             var _t = this;
14224             _t[m](item, true, false);
14225             return true;
14226         }
14227         if(this.multiSelect || this.singleSelect){
14228             if(this.multiSelect && e.shiftKey && this.lastSelection){
14229                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14230             }else{
14231                 this.select(item, this.multiSelect && e.ctrlKey);
14232                 this.lastSelection = item;
14233             }
14234             
14235             if(!this.tickable){
14236                 e.preventDefault();
14237             }
14238             
14239         }
14240         return true;
14241     },
14242
14243     /**
14244      * Get the number of selected nodes.
14245      * @return {Number}
14246      */
14247     getSelectionCount : function(){
14248         return this.selections.length;
14249     },
14250
14251     /**
14252      * Get the currently selected nodes.
14253      * @return {Array} An array of HTMLElements
14254      */
14255     getSelectedNodes : function(){
14256         return this.selections;
14257     },
14258
14259     /**
14260      * Get the indexes of the selected nodes.
14261      * @return {Array}
14262      */
14263     getSelectedIndexes : function(){
14264         var indexes = [], s = this.selections;
14265         for(var i = 0, len = s.length; i < len; i++){
14266             indexes.push(s[i].nodeIndex);
14267         }
14268         return indexes;
14269     },
14270
14271     /**
14272      * Clear all selections
14273      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14274      */
14275     clearSelections : function(suppressEvent){
14276         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14277             this.cmp.elements = this.selections;
14278             this.cmp.removeClass(this.selectedClass);
14279             this.selections = [];
14280             if(!suppressEvent){
14281                 this.fireEvent("selectionchange", this, this.selections);
14282             }
14283         }
14284     },
14285
14286     /**
14287      * Returns true if the passed node is selected
14288      * @param {HTMLElement/Number} node The node or node index
14289      * @return {Boolean}
14290      */
14291     isSelected : function(node){
14292         var s = this.selections;
14293         if(s.length < 1){
14294             return false;
14295         }
14296         node = this.getNode(node);
14297         return s.indexOf(node) !== -1;
14298     },
14299
14300     /**
14301      * Selects nodes.
14302      * @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
14303      * @param {Boolean} keepExisting (optional) true to keep existing selections
14304      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14305      */
14306     select : function(nodeInfo, keepExisting, suppressEvent){
14307         if(nodeInfo instanceof Array){
14308             if(!keepExisting){
14309                 this.clearSelections(true);
14310             }
14311             for(var i = 0, len = nodeInfo.length; i < len; i++){
14312                 this.select(nodeInfo[i], true, true);
14313             }
14314             return;
14315         } 
14316         var node = this.getNode(nodeInfo);
14317         if(!node || this.isSelected(node)){
14318             return; // already selected.
14319         }
14320         if(!keepExisting){
14321             this.clearSelections(true);
14322         }
14323         
14324         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14325             Roo.fly(node).addClass(this.selectedClass);
14326             this.selections.push(node);
14327             if(!suppressEvent){
14328                 this.fireEvent("selectionchange", this, this.selections);
14329             }
14330         }
14331         
14332         
14333     },
14334       /**
14335      * Unselects nodes.
14336      * @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
14337      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14338      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14339      */
14340     unselect : function(nodeInfo, keepExisting, suppressEvent)
14341     {
14342         if(nodeInfo instanceof Array){
14343             Roo.each(this.selections, function(s) {
14344                 this.unselect(s, nodeInfo);
14345             }, this);
14346             return;
14347         }
14348         var node = this.getNode(nodeInfo);
14349         if(!node || !this.isSelected(node)){
14350             //Roo.log("not selected");
14351             return; // not selected.
14352         }
14353         // fireevent???
14354         var ns = [];
14355         Roo.each(this.selections, function(s) {
14356             if (s == node ) {
14357                 Roo.fly(node).removeClass(this.selectedClass);
14358
14359                 return;
14360             }
14361             ns.push(s);
14362         },this);
14363         
14364         this.selections= ns;
14365         this.fireEvent("selectionchange", this, this.selections);
14366     },
14367
14368     /**
14369      * Gets a template node.
14370      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14371      * @return {HTMLElement} The node or null if it wasn't found
14372      */
14373     getNode : function(nodeInfo){
14374         if(typeof nodeInfo == "string"){
14375             return document.getElementById(nodeInfo);
14376         }else if(typeof nodeInfo == "number"){
14377             return this.nodes[nodeInfo];
14378         }
14379         return nodeInfo;
14380     },
14381
14382     /**
14383      * Gets a range template nodes.
14384      * @param {Number} startIndex
14385      * @param {Number} endIndex
14386      * @return {Array} An array of nodes
14387      */
14388     getNodes : function(start, end){
14389         var ns = this.nodes;
14390         start = start || 0;
14391         end = typeof end == "undefined" ? ns.length - 1 : end;
14392         var nodes = [];
14393         if(start <= end){
14394             for(var i = start; i <= end; i++){
14395                 nodes.push(ns[i]);
14396             }
14397         } else{
14398             for(var i = start; i >= end; i--){
14399                 nodes.push(ns[i]);
14400             }
14401         }
14402         return nodes;
14403     },
14404
14405     /**
14406      * Finds the index of the passed node
14407      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14408      * @return {Number} The index of the node or -1
14409      */
14410     indexOf : function(node){
14411         node = this.getNode(node);
14412         if(typeof node.nodeIndex == "number"){
14413             return node.nodeIndex;
14414         }
14415         var ns = this.nodes;
14416         for(var i = 0, len = ns.length; i < len; i++){
14417             if(ns[i] == node){
14418                 return i;
14419             }
14420         }
14421         return -1;
14422     }
14423 });
14424 /*
14425  * - LGPL
14426  *
14427  * based on jquery fullcalendar
14428  * 
14429  */
14430
14431 Roo.bootstrap = Roo.bootstrap || {};
14432 /**
14433  * @class Roo.bootstrap.Calendar
14434  * @extends Roo.bootstrap.Component
14435  * Bootstrap Calendar class
14436  * @cfg {Boolean} loadMask (true|false) default false
14437  * @cfg {Object} header generate the user specific header of the calendar, default false
14438
14439  * @constructor
14440  * Create a new Container
14441  * @param {Object} config The config object
14442  */
14443
14444
14445
14446 Roo.bootstrap.Calendar = function(config){
14447     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14448      this.addEvents({
14449         /**
14450              * @event select
14451              * Fires when a date is selected
14452              * @param {DatePicker} this
14453              * @param {Date} date The selected date
14454              */
14455         'select': true,
14456         /**
14457              * @event monthchange
14458              * Fires when the displayed month changes 
14459              * @param {DatePicker} this
14460              * @param {Date} date The selected month
14461              */
14462         'monthchange': true,
14463         /**
14464              * @event evententer
14465              * Fires when mouse over an event
14466              * @param {Calendar} this
14467              * @param {event} Event
14468              */
14469         'evententer': true,
14470         /**
14471              * @event eventleave
14472              * Fires when the mouse leaves an
14473              * @param {Calendar} this
14474              * @param {event}
14475              */
14476         'eventleave': true,
14477         /**
14478              * @event eventclick
14479              * Fires when the mouse click an
14480              * @param {Calendar} this
14481              * @param {event}
14482              */
14483         'eventclick': true
14484         
14485     });
14486
14487 };
14488
14489 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14490     
14491      /**
14492      * @cfg {Number} startDay
14493      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14494      */
14495     startDay : 0,
14496     
14497     loadMask : false,
14498     
14499     header : false,
14500       
14501     getAutoCreate : function(){
14502         
14503         
14504         var fc_button = function(name, corner, style, content ) {
14505             return Roo.apply({},{
14506                 tag : 'span',
14507                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14508                          (corner.length ?
14509                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14510                             ''
14511                         ),
14512                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14513                 unselectable: 'on'
14514             });
14515         };
14516         
14517         var header = {};
14518         
14519         if(!this.header){
14520             header = {
14521                 tag : 'table',
14522                 cls : 'fc-header',
14523                 style : 'width:100%',
14524                 cn : [
14525                     {
14526                         tag: 'tr',
14527                         cn : [
14528                             {
14529                                 tag : 'td',
14530                                 cls : 'fc-header-left',
14531                                 cn : [
14532                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14533                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14534                                     { tag: 'span', cls: 'fc-header-space' },
14535                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14536
14537
14538                                 ]
14539                             },
14540
14541                             {
14542                                 tag : 'td',
14543                                 cls : 'fc-header-center',
14544                                 cn : [
14545                                     {
14546                                         tag: 'span',
14547                                         cls: 'fc-header-title',
14548                                         cn : {
14549                                             tag: 'H2',
14550                                             html : 'month / year'
14551                                         }
14552                                     }
14553
14554                                 ]
14555                             },
14556                             {
14557                                 tag : 'td',
14558                                 cls : 'fc-header-right',
14559                                 cn : [
14560                               /*      fc_button('month', 'left', '', 'month' ),
14561                                     fc_button('week', '', '', 'week' ),
14562                                     fc_button('day', 'right', '', 'day' )
14563                                 */    
14564
14565                                 ]
14566                             }
14567
14568                         ]
14569                     }
14570                 ]
14571             };
14572         }
14573         
14574         header = this.header;
14575         
14576        
14577         var cal_heads = function() {
14578             var ret = [];
14579             // fixme - handle this.
14580             
14581             for (var i =0; i < Date.dayNames.length; i++) {
14582                 var d = Date.dayNames[i];
14583                 ret.push({
14584                     tag: 'th',
14585                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14586                     html : d.substring(0,3)
14587                 });
14588                 
14589             }
14590             ret[0].cls += ' fc-first';
14591             ret[6].cls += ' fc-last';
14592             return ret;
14593         };
14594         var cal_cell = function(n) {
14595             return  {
14596                 tag: 'td',
14597                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14598                 cn : [
14599                     {
14600                         cn : [
14601                             {
14602                                 cls: 'fc-day-number',
14603                                 html: 'D'
14604                             },
14605                             {
14606                                 cls: 'fc-day-content',
14607                              
14608                                 cn : [
14609                                      {
14610                                         style: 'position: relative;' // height: 17px;
14611                                     }
14612                                 ]
14613                             }
14614                             
14615                             
14616                         ]
14617                     }
14618                 ]
14619                 
14620             }
14621         };
14622         var cal_rows = function() {
14623             
14624             var ret = [];
14625             for (var r = 0; r < 6; r++) {
14626                 var row= {
14627                     tag : 'tr',
14628                     cls : 'fc-week',
14629                     cn : []
14630                 };
14631                 
14632                 for (var i =0; i < Date.dayNames.length; i++) {
14633                     var d = Date.dayNames[i];
14634                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14635
14636                 }
14637                 row.cn[0].cls+=' fc-first';
14638                 row.cn[0].cn[0].style = 'min-height:90px';
14639                 row.cn[6].cls+=' fc-last';
14640                 ret.push(row);
14641                 
14642             }
14643             ret[0].cls += ' fc-first';
14644             ret[4].cls += ' fc-prev-last';
14645             ret[5].cls += ' fc-last';
14646             return ret;
14647             
14648         };
14649         
14650         var cal_table = {
14651             tag: 'table',
14652             cls: 'fc-border-separate',
14653             style : 'width:100%',
14654             cellspacing  : 0,
14655             cn : [
14656                 { 
14657                     tag: 'thead',
14658                     cn : [
14659                         { 
14660                             tag: 'tr',
14661                             cls : 'fc-first fc-last',
14662                             cn : cal_heads()
14663                         }
14664                     ]
14665                 },
14666                 { 
14667                     tag: 'tbody',
14668                     cn : cal_rows()
14669                 }
14670                   
14671             ]
14672         };
14673          
14674          var cfg = {
14675             cls : 'fc fc-ltr',
14676             cn : [
14677                 header,
14678                 {
14679                     cls : 'fc-content',
14680                     style : "position: relative;",
14681                     cn : [
14682                         {
14683                             cls : 'fc-view fc-view-month fc-grid',
14684                             style : 'position: relative',
14685                             unselectable : 'on',
14686                             cn : [
14687                                 {
14688                                     cls : 'fc-event-container',
14689                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14690                                 },
14691                                 cal_table
14692                             ]
14693                         }
14694                     ]
14695     
14696                 }
14697            ] 
14698             
14699         };
14700         
14701          
14702         
14703         return cfg;
14704     },
14705     
14706     
14707     initEvents : function()
14708     {
14709         if(!this.store){
14710             throw "can not find store for calendar";
14711         }
14712         
14713         var mark = {
14714             tag: "div",
14715             cls:"x-dlg-mask",
14716             style: "text-align:center",
14717             cn: [
14718                 {
14719                     tag: "div",
14720                     style: "background-color:white;width:50%;margin:250 auto",
14721                     cn: [
14722                         {
14723                             tag: "img",
14724                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
14725                         },
14726                         {
14727                             tag: "span",
14728                             html: "Loading"
14729                         }
14730                         
14731                     ]
14732                 }
14733             ]
14734         }
14735         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
14736         
14737         var size = this.el.select('.fc-content', true).first().getSize();
14738         this.maskEl.setSize(size.width, size.height);
14739         this.maskEl.enableDisplayMode("block");
14740         if(!this.loadMask){
14741             this.maskEl.hide();
14742         }
14743         
14744         this.store = Roo.factory(this.store, Roo.data);
14745         this.store.on('load', this.onLoad, this);
14746         this.store.on('beforeload', this.onBeforeLoad, this);
14747         
14748         this.resize();
14749         
14750         this.cells = this.el.select('.fc-day',true);
14751         //Roo.log(this.cells);
14752         this.textNodes = this.el.query('.fc-day-number');
14753         this.cells.addClassOnOver('fc-state-hover');
14754         
14755         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
14756         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
14757         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
14758         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
14759         
14760         this.on('monthchange', this.onMonthChange, this);
14761         
14762         this.update(new Date().clearTime());
14763     },
14764     
14765     resize : function() {
14766         var sz  = this.el.getSize();
14767         
14768         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
14769         this.el.select('.fc-day-content div',true).setHeight(34);
14770     },
14771     
14772     
14773     // private
14774     showPrevMonth : function(e){
14775         this.update(this.activeDate.add("mo", -1));
14776     },
14777     showToday : function(e){
14778         this.update(new Date().clearTime());
14779     },
14780     // private
14781     showNextMonth : function(e){
14782         this.update(this.activeDate.add("mo", 1));
14783     },
14784
14785     // private
14786     showPrevYear : function(){
14787         this.update(this.activeDate.add("y", -1));
14788     },
14789
14790     // private
14791     showNextYear : function(){
14792         this.update(this.activeDate.add("y", 1));
14793     },
14794
14795     
14796    // private
14797     update : function(date)
14798     {
14799         var vd = this.activeDate;
14800         this.activeDate = date;
14801 //        if(vd && this.el){
14802 //            var t = date.getTime();
14803 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
14804 //                Roo.log('using add remove');
14805 //                
14806 //                this.fireEvent('monthchange', this, date);
14807 //                
14808 //                this.cells.removeClass("fc-state-highlight");
14809 //                this.cells.each(function(c){
14810 //                   if(c.dateValue == t){
14811 //                       c.addClass("fc-state-highlight");
14812 //                       setTimeout(function(){
14813 //                            try{c.dom.firstChild.focus();}catch(e){}
14814 //                       }, 50);
14815 //                       return false;
14816 //                   }
14817 //                   return true;
14818 //                });
14819 //                return;
14820 //            }
14821 //        }
14822         
14823         var days = date.getDaysInMonth();
14824         
14825         var firstOfMonth = date.getFirstDateOfMonth();
14826         var startingPos = firstOfMonth.getDay()-this.startDay;
14827         
14828         if(startingPos < this.startDay){
14829             startingPos += 7;
14830         }
14831         
14832         var pm = date.add(Date.MONTH, -1);
14833         var prevStart = pm.getDaysInMonth()-startingPos;
14834 //        
14835         this.cells = this.el.select('.fc-day',true);
14836         this.textNodes = this.el.query('.fc-day-number');
14837         this.cells.addClassOnOver('fc-state-hover');
14838         
14839         var cells = this.cells.elements;
14840         var textEls = this.textNodes;
14841         
14842         Roo.each(cells, function(cell){
14843             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
14844         });
14845         
14846         days += startingPos;
14847
14848         // convert everything to numbers so it's fast
14849         var day = 86400000;
14850         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
14851         //Roo.log(d);
14852         //Roo.log(pm);
14853         //Roo.log(prevStart);
14854         
14855         var today = new Date().clearTime().getTime();
14856         var sel = date.clearTime().getTime();
14857         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
14858         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
14859         var ddMatch = this.disabledDatesRE;
14860         var ddText = this.disabledDatesText;
14861         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
14862         var ddaysText = this.disabledDaysText;
14863         var format = this.format;
14864         
14865         var setCellClass = function(cal, cell){
14866             cell.row = 0;
14867             cell.events = [];
14868             cell.more = [];
14869             //Roo.log('set Cell Class');
14870             cell.title = "";
14871             var t = d.getTime();
14872             
14873             //Roo.log(d);
14874             
14875             cell.dateValue = t;
14876             if(t == today){
14877                 cell.className += " fc-today";
14878                 cell.className += " fc-state-highlight";
14879                 cell.title = cal.todayText;
14880             }
14881             if(t == sel){
14882                 // disable highlight in other month..
14883                 //cell.className += " fc-state-highlight";
14884                 
14885             }
14886             // disabling
14887             if(t < min) {
14888                 cell.className = " fc-state-disabled";
14889                 cell.title = cal.minText;
14890                 return;
14891             }
14892             if(t > max) {
14893                 cell.className = " fc-state-disabled";
14894                 cell.title = cal.maxText;
14895                 return;
14896             }
14897             if(ddays){
14898                 if(ddays.indexOf(d.getDay()) != -1){
14899                     cell.title = ddaysText;
14900                     cell.className = " fc-state-disabled";
14901                 }
14902             }
14903             if(ddMatch && format){
14904                 var fvalue = d.dateFormat(format);
14905                 if(ddMatch.test(fvalue)){
14906                     cell.title = ddText.replace("%0", fvalue);
14907                     cell.className = " fc-state-disabled";
14908                 }
14909             }
14910             
14911             if (!cell.initialClassName) {
14912                 cell.initialClassName = cell.dom.className;
14913             }
14914             
14915             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
14916         };
14917
14918         var i = 0;
14919         
14920         for(; i < startingPos; i++) {
14921             textEls[i].innerHTML = (++prevStart);
14922             d.setDate(d.getDate()+1);
14923             
14924             cells[i].className = "fc-past fc-other-month";
14925             setCellClass(this, cells[i]);
14926         }
14927         
14928         var intDay = 0;
14929         
14930         for(; i < days; i++){
14931             intDay = i - startingPos + 1;
14932             textEls[i].innerHTML = (intDay);
14933             d.setDate(d.getDate()+1);
14934             
14935             cells[i].className = ''; // "x-date-active";
14936             setCellClass(this, cells[i]);
14937         }
14938         var extraDays = 0;
14939         
14940         for(; i < 42; i++) {
14941             textEls[i].innerHTML = (++extraDays);
14942             d.setDate(d.getDate()+1);
14943             
14944             cells[i].className = "fc-future fc-other-month";
14945             setCellClass(this, cells[i]);
14946         }
14947         
14948         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
14949         
14950         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
14951         
14952         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
14953         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
14954         
14955         if(totalRows != 6){
14956             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
14957             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
14958         }
14959         
14960         this.fireEvent('monthchange', this, date);
14961         
14962         
14963         /*
14964         if(!this.internalRender){
14965             var main = this.el.dom.firstChild;
14966             var w = main.offsetWidth;
14967             this.el.setWidth(w + this.el.getBorderWidth("lr"));
14968             Roo.fly(main).setWidth(w);
14969             this.internalRender = true;
14970             // opera does not respect the auto grow header center column
14971             // then, after it gets a width opera refuses to recalculate
14972             // without a second pass
14973             if(Roo.isOpera && !this.secondPass){
14974                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
14975                 this.secondPass = true;
14976                 this.update.defer(10, this, [date]);
14977             }
14978         }
14979         */
14980         
14981     },
14982     
14983     findCell : function(dt) {
14984         dt = dt.clearTime().getTime();
14985         var ret = false;
14986         this.cells.each(function(c){
14987             //Roo.log("check " +c.dateValue + '?=' + dt);
14988             if(c.dateValue == dt){
14989                 ret = c;
14990                 return false;
14991             }
14992             return true;
14993         });
14994         
14995         return ret;
14996     },
14997     
14998     findCells : function(ev) {
14999         var s = ev.start.clone().clearTime().getTime();
15000        // Roo.log(s);
15001         var e= ev.end.clone().clearTime().getTime();
15002        // Roo.log(e);
15003         var ret = [];
15004         this.cells.each(function(c){
15005              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15006             
15007             if(c.dateValue > e){
15008                 return ;
15009             }
15010             if(c.dateValue < s){
15011                 return ;
15012             }
15013             ret.push(c);
15014         });
15015         
15016         return ret;    
15017     },
15018     
15019 //    findBestRow: function(cells)
15020 //    {
15021 //        var ret = 0;
15022 //        
15023 //        for (var i =0 ; i < cells.length;i++) {
15024 //            ret  = Math.max(cells[i].rows || 0,ret);
15025 //        }
15026 //        return ret;
15027 //        
15028 //    },
15029     
15030     
15031     addItem : function(ev)
15032     {
15033         // look for vertical location slot in
15034         var cells = this.findCells(ev);
15035         
15036 //        ev.row = this.findBestRow(cells);
15037         
15038         // work out the location.
15039         
15040         var crow = false;
15041         var rows = [];
15042         for(var i =0; i < cells.length; i++) {
15043             
15044             cells[i].row = cells[0].row;
15045             
15046             if(i == 0){
15047                 cells[i].row = cells[i].row + 1;
15048             }
15049             
15050             if (!crow) {
15051                 crow = {
15052                     start : cells[i],
15053                     end :  cells[i]
15054                 };
15055                 continue;
15056             }
15057             if (crow.start.getY() == cells[i].getY()) {
15058                 // on same row.
15059                 crow.end = cells[i];
15060                 continue;
15061             }
15062             // different row.
15063             rows.push(crow);
15064             crow = {
15065                 start: cells[i],
15066                 end : cells[i]
15067             };
15068             
15069         }
15070         
15071         rows.push(crow);
15072         ev.els = [];
15073         ev.rows = rows;
15074         ev.cells = cells;
15075         
15076         cells[0].events.push(ev);
15077         
15078         this.calevents.push(ev);
15079     },
15080     
15081     clearEvents: function() {
15082         
15083         if(!this.calevents){
15084             return;
15085         }
15086         
15087         Roo.each(this.cells.elements, function(c){
15088             c.row = 0;
15089             c.events = [];
15090             c.more = [];
15091         });
15092         
15093         Roo.each(this.calevents, function(e) {
15094             Roo.each(e.els, function(el) {
15095                 el.un('mouseenter' ,this.onEventEnter, this);
15096                 el.un('mouseleave' ,this.onEventLeave, this);
15097                 el.remove();
15098             },this);
15099         },this);
15100         
15101         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15102             e.remove();
15103         });
15104         
15105     },
15106     
15107     renderEvents: function()
15108     {   
15109         var _this = this;
15110         
15111         this.cells.each(function(c) {
15112             
15113             if(c.row < 5){
15114                 return;
15115             }
15116             
15117             var ev = c.events;
15118             
15119             var r = 4;
15120             if(c.row != c.events.length){
15121                 r = 4 - (4 - (c.row - c.events.length));
15122             }
15123             
15124             c.events = ev.slice(0, r);
15125             c.more = ev.slice(r);
15126             
15127             if(c.more.length && c.more.length == 1){
15128                 c.events.push(c.more.pop());
15129             }
15130             
15131             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15132             
15133         });
15134             
15135         this.cells.each(function(c) {
15136             
15137             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15138             
15139             
15140             for (var e = 0; e < c.events.length; e++){
15141                 var ev = c.events[e];
15142                 var rows = ev.rows;
15143                 
15144                 for(var i = 0; i < rows.length; i++) {
15145                 
15146                     // how many rows should it span..
15147
15148                     var  cfg = {
15149                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15150                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15151
15152                         unselectable : "on",
15153                         cn : [
15154                             {
15155                                 cls: 'fc-event-inner',
15156                                 cn : [
15157     //                                {
15158     //                                  tag:'span',
15159     //                                  cls: 'fc-event-time',
15160     //                                  html : cells.length > 1 ? '' : ev.time
15161     //                                },
15162                                     {
15163                                       tag:'span',
15164                                       cls: 'fc-event-title',
15165                                       html : String.format('{0}', ev.title)
15166                                     }
15167
15168
15169                                 ]
15170                             },
15171                             {
15172                                 cls: 'ui-resizable-handle ui-resizable-e',
15173                                 html : '&nbsp;&nbsp;&nbsp'
15174                             }
15175
15176                         ]
15177                     };
15178
15179                     if (i == 0) {
15180                         cfg.cls += ' fc-event-start';
15181                     }
15182                     if ((i+1) == rows.length) {
15183                         cfg.cls += ' fc-event-end';
15184                     }
15185
15186                     var ctr = _this.el.select('.fc-event-container',true).first();
15187                     var cg = ctr.createChild(cfg);
15188
15189                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15190                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15191
15192                     var r = (c.more.length) ? 1 : 0;
15193                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15194                     cg.setWidth(ebox.right - sbox.x -2);
15195
15196                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15197                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15198                     cg.on('click', _this.onEventClick, _this, ev);
15199
15200                     ev.els.push(cg);
15201                     
15202                 }
15203                 
15204             }
15205             
15206             
15207             if(c.more.length){
15208                 var  cfg = {
15209                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15210                     style : 'position: absolute',
15211                     unselectable : "on",
15212                     cn : [
15213                         {
15214                             cls: 'fc-event-inner',
15215                             cn : [
15216                                 {
15217                                   tag:'span',
15218                                   cls: 'fc-event-title',
15219                                   html : 'More'
15220                                 }
15221
15222
15223                             ]
15224                         },
15225                         {
15226                             cls: 'ui-resizable-handle ui-resizable-e',
15227                             html : '&nbsp;&nbsp;&nbsp'
15228                         }
15229
15230                     ]
15231                 };
15232
15233                 var ctr = _this.el.select('.fc-event-container',true).first();
15234                 var cg = ctr.createChild(cfg);
15235
15236                 var sbox = c.select('.fc-day-content',true).first().getBox();
15237                 var ebox = c.select('.fc-day-content',true).first().getBox();
15238                 //Roo.log(cg);
15239                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15240                 cg.setWidth(ebox.right - sbox.x -2);
15241
15242                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15243                 
15244             }
15245             
15246         });
15247         
15248         
15249         
15250     },
15251     
15252     onEventEnter: function (e, el,event,d) {
15253         this.fireEvent('evententer', this, el, event);
15254     },
15255     
15256     onEventLeave: function (e, el,event,d) {
15257         this.fireEvent('eventleave', this, el, event);
15258     },
15259     
15260     onEventClick: function (e, el,event,d) {
15261         this.fireEvent('eventclick', this, el, event);
15262     },
15263     
15264     onMonthChange: function () {
15265         this.store.load();
15266     },
15267     
15268     onMoreEventClick: function(e, el, more)
15269     {
15270         var _this = this;
15271         
15272         this.calpopover.placement = 'right';
15273         this.calpopover.setTitle('More');
15274         
15275         this.calpopover.setContent('');
15276         
15277         var ctr = this.calpopover.el.select('.popover-content', true).first();
15278         
15279         Roo.each(more, function(m){
15280             var cfg = {
15281                 cls : 'fc-event-hori fc-event-draggable',
15282                 html : m.title
15283             }
15284             var cg = ctr.createChild(cfg);
15285             
15286             cg.on('click', _this.onEventClick, _this, m);
15287         });
15288         
15289         this.calpopover.show(el);
15290         
15291         
15292     },
15293     
15294     onLoad: function () 
15295     {   
15296         this.calevents = [];
15297         var cal = this;
15298         
15299         if(this.store.getCount() > 0){
15300             this.store.data.each(function(d){
15301                cal.addItem({
15302                     id : d.data.id,
15303                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15304                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15305                     time : d.data.start_time,
15306                     title : d.data.title,
15307                     description : d.data.description,
15308                     venue : d.data.venue
15309                 });
15310             });
15311         }
15312         
15313         this.renderEvents();
15314         
15315         if(this.calevents.length && this.loadMask){
15316             this.maskEl.hide();
15317         }
15318     },
15319     
15320     onBeforeLoad: function()
15321     {
15322         this.clearEvents();
15323         if(this.loadMask){
15324             this.maskEl.show();
15325         }
15326     }
15327 });
15328
15329  
15330  /*
15331  * - LGPL
15332  *
15333  * element
15334  * 
15335  */
15336
15337 /**
15338  * @class Roo.bootstrap.Popover
15339  * @extends Roo.bootstrap.Component
15340  * Bootstrap Popover class
15341  * @cfg {String} html contents of the popover   (or false to use children..)
15342  * @cfg {String} title of popover (or false to hide)
15343  * @cfg {String} placement how it is placed
15344  * @cfg {String} trigger click || hover (or false to trigger manually)
15345  * @cfg {String} over what (parent or false to trigger manually.)
15346  * @cfg {Number} delay - delay before showing
15347  
15348  * @constructor
15349  * Create a new Popover
15350  * @param {Object} config The config object
15351  */
15352
15353 Roo.bootstrap.Popover = function(config){
15354     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15355 };
15356
15357 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15358     
15359     title: 'Fill in a title',
15360     html: false,
15361     
15362     placement : 'right',
15363     trigger : 'hover', // hover
15364     
15365     delay : 0,
15366     
15367     over: 'parent',
15368     
15369     can_build_overlaid : false,
15370     
15371     getChildContainer : function()
15372     {
15373         return this.el.select('.popover-content',true).first();
15374     },
15375     
15376     getAutoCreate : function(){
15377          Roo.log('make popover?');
15378         var cfg = {
15379            cls : 'popover roo-dynamic',
15380            style: 'display:block',
15381            cn : [
15382                 {
15383                     cls : 'arrow'
15384                 },
15385                 {
15386                     cls : 'popover-inner',
15387                     cn : [
15388                         {
15389                             tag: 'h3',
15390                             cls: 'popover-title',
15391                             html : this.title
15392                         },
15393                         {
15394                             cls : 'popover-content',
15395                             html : this.html
15396                         }
15397                     ]
15398                     
15399                 }
15400            ]
15401         };
15402         
15403         return cfg;
15404     },
15405     setTitle: function(str)
15406     {
15407         this.title = str;
15408         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15409     },
15410     setContent: function(str)
15411     {
15412         this.html = str;
15413         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15414     },
15415     // as it get's added to the bottom of the page.
15416     onRender : function(ct, position)
15417     {
15418         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15419         if(!this.el){
15420             var cfg = Roo.apply({},  this.getAutoCreate());
15421             cfg.id = Roo.id();
15422             
15423             if (this.cls) {
15424                 cfg.cls += ' ' + this.cls;
15425             }
15426             if (this.style) {
15427                 cfg.style = this.style;
15428             }
15429             Roo.log("adding to ")
15430             this.el = Roo.get(document.body).createChild(cfg, position);
15431             Roo.log(this.el);
15432         }
15433         this.initEvents();
15434     },
15435     
15436     initEvents : function()
15437     {
15438         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15439         this.el.enableDisplayMode('block');
15440         this.el.hide();
15441         if (this.over === false) {
15442             return; 
15443         }
15444         if (this.triggers === false) {
15445             return;
15446         }
15447         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15448         var triggers = this.trigger ? this.trigger.split(' ') : [];
15449         Roo.each(triggers, function(trigger) {
15450         
15451             if (trigger == 'click') {
15452                 on_el.on('click', this.toggle, this);
15453             } else if (trigger != 'manual') {
15454                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15455                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15456       
15457                 on_el.on(eventIn  ,this.enter, this);
15458                 on_el.on(eventOut, this.leave, this);
15459             }
15460         }, this);
15461         
15462     },
15463     
15464     
15465     // private
15466     timeout : null,
15467     hoverState : null,
15468     
15469     toggle : function () {
15470         this.hoverState == 'in' ? this.leave() : this.enter();
15471     },
15472     
15473     enter : function () {
15474        
15475     
15476         clearTimeout(this.timeout);
15477     
15478         this.hoverState = 'in';
15479     
15480         if (!this.delay || !this.delay.show) {
15481             this.show();
15482             return;
15483         }
15484         var _t = this;
15485         this.timeout = setTimeout(function () {
15486             if (_t.hoverState == 'in') {
15487                 _t.show();
15488             }
15489         }, this.delay.show)
15490     },
15491     leave : function() {
15492         clearTimeout(this.timeout);
15493     
15494         this.hoverState = 'out';
15495     
15496         if (!this.delay || !this.delay.hide) {
15497             this.hide();
15498             return;
15499         }
15500         var _t = this;
15501         this.timeout = setTimeout(function () {
15502             if (_t.hoverState == 'out') {
15503                 _t.hide();
15504             }
15505         }, this.delay.hide)
15506     },
15507     
15508     show : function (on_el)
15509     {
15510         if (!on_el) {
15511             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15512         }
15513         // set content.
15514         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15515         if (this.html !== false) {
15516             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15517         }
15518         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15519         if (!this.title.length) {
15520             this.el.select('.popover-title',true).hide();
15521         }
15522         
15523         var placement = typeof this.placement == 'function' ?
15524             this.placement.call(this, this.el, on_el) :
15525             this.placement;
15526             
15527         var autoToken = /\s?auto?\s?/i;
15528         var autoPlace = autoToken.test(placement);
15529         if (autoPlace) {
15530             placement = placement.replace(autoToken, '') || 'top';
15531         }
15532         
15533         //this.el.detach()
15534         //this.el.setXY([0,0]);
15535         this.el.show();
15536         this.el.dom.style.display='block';
15537         this.el.addClass(placement);
15538         
15539         //this.el.appendTo(on_el);
15540         
15541         var p = this.getPosition();
15542         var box = this.el.getBox();
15543         
15544         if (autoPlace) {
15545             // fixme..
15546         }
15547         var align = Roo.bootstrap.Popover.alignment[placement];
15548         this.el.alignTo(on_el, align[0],align[1]);
15549         //var arrow = this.el.select('.arrow',true).first();
15550         //arrow.set(align[2], 
15551         
15552         this.el.addClass('in');
15553         
15554         
15555         if (this.el.hasClass('fade')) {
15556             // fade it?
15557         }
15558         
15559     },
15560     hide : function()
15561     {
15562         this.el.setXY([0,0]);
15563         this.el.removeClass('in');
15564         this.el.hide();
15565         this.hoverState = null;
15566         
15567     }
15568     
15569 });
15570
15571 Roo.bootstrap.Popover.alignment = {
15572     'left' : ['r-l', [-10,0], 'right'],
15573     'right' : ['l-r', [10,0], 'left'],
15574     'bottom' : ['t-b', [0,10], 'top'],
15575     'top' : [ 'b-t', [0,-10], 'bottom']
15576 };
15577
15578  /*
15579  * - LGPL
15580  *
15581  * Progress
15582  * 
15583  */
15584
15585 /**
15586  * @class Roo.bootstrap.Progress
15587  * @extends Roo.bootstrap.Component
15588  * Bootstrap Progress class
15589  * @cfg {Boolean} striped striped of the progress bar
15590  * @cfg {Boolean} active animated of the progress bar
15591  * 
15592  * 
15593  * @constructor
15594  * Create a new Progress
15595  * @param {Object} config The config object
15596  */
15597
15598 Roo.bootstrap.Progress = function(config){
15599     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15600 };
15601
15602 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15603     
15604     striped : false,
15605     active: false,
15606     
15607     getAutoCreate : function(){
15608         var cfg = {
15609             tag: 'div',
15610             cls: 'progress'
15611         };
15612         
15613         
15614         if(this.striped){
15615             cfg.cls += ' progress-striped';
15616         }
15617       
15618         if(this.active){
15619             cfg.cls += ' active';
15620         }
15621         
15622         
15623         return cfg;
15624     }
15625    
15626 });
15627
15628  
15629
15630  /*
15631  * - LGPL
15632  *
15633  * ProgressBar
15634  * 
15635  */
15636
15637 /**
15638  * @class Roo.bootstrap.ProgressBar
15639  * @extends Roo.bootstrap.Component
15640  * Bootstrap ProgressBar class
15641  * @cfg {Number} aria_valuenow aria-value now
15642  * @cfg {Number} aria_valuemin aria-value min
15643  * @cfg {Number} aria_valuemax aria-value max
15644  * @cfg {String} label label for the progress bar
15645  * @cfg {String} panel (success | info | warning | danger )
15646  * @cfg {String} role role of the progress bar
15647  * @cfg {String} sr_only text
15648  * 
15649  * 
15650  * @constructor
15651  * Create a new ProgressBar
15652  * @param {Object} config The config object
15653  */
15654
15655 Roo.bootstrap.ProgressBar = function(config){
15656     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15657 };
15658
15659 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15660     
15661     aria_valuenow : 0,
15662     aria_valuemin : 0,
15663     aria_valuemax : 100,
15664     label : false,
15665     panel : false,
15666     role : false,
15667     sr_only: false,
15668     
15669     getAutoCreate : function()
15670     {
15671         
15672         var cfg = {
15673             tag: 'div',
15674             cls: 'progress-bar',
15675             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15676         };
15677         
15678         if(this.sr_only){
15679             cfg.cn = {
15680                 tag: 'span',
15681                 cls: 'sr-only',
15682                 html: this.sr_only
15683             }
15684         }
15685         
15686         if(this.role){
15687             cfg.role = this.role;
15688         }
15689         
15690         if(this.aria_valuenow){
15691             cfg['aria-valuenow'] = this.aria_valuenow;
15692         }
15693         
15694         if(this.aria_valuemin){
15695             cfg['aria-valuemin'] = this.aria_valuemin;
15696         }
15697         
15698         if(this.aria_valuemax){
15699             cfg['aria-valuemax'] = this.aria_valuemax;
15700         }
15701         
15702         if(this.label && !this.sr_only){
15703             cfg.html = this.label;
15704         }
15705         
15706         if(this.panel){
15707             cfg.cls += ' progress-bar-' + this.panel;
15708         }
15709         
15710         return cfg;
15711     },
15712     
15713     update : function(aria_valuenow)
15714     {
15715         this.aria_valuenow = aria_valuenow;
15716         
15717         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
15718     }
15719    
15720 });
15721
15722  
15723
15724  /*
15725  * - LGPL
15726  *
15727  * column
15728  * 
15729  */
15730
15731 /**
15732  * @class Roo.bootstrap.TabGroup
15733  * @extends Roo.bootstrap.Column
15734  * Bootstrap Column class
15735  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
15736  * @cfg {Boolean} carousel true to make the group behave like a carousel
15737  * @cfg {Number} bullets show the panel pointer.. default 0
15738  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
15739  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
15740  * @cfg {Number} timer auto slide timer .. default 0 millisecond
15741  * 
15742  * @constructor
15743  * Create a new TabGroup
15744  * @param {Object} config The config object
15745  */
15746
15747 Roo.bootstrap.TabGroup = function(config){
15748     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
15749     if (!this.navId) {
15750         this.navId = Roo.id();
15751     }
15752     this.tabs = [];
15753     Roo.bootstrap.TabGroup.register(this);
15754     
15755 };
15756
15757 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
15758     
15759     carousel : false,
15760     transition : false,
15761     bullets : 0,
15762     timer : 0,
15763     autoslide : false,
15764     slideFn : false,
15765     slideOnTouch : false,
15766     
15767     getAutoCreate : function()
15768     {
15769         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
15770         
15771         cfg.cls += ' tab-content';
15772         
15773         Roo.log('get auto create...............');
15774         
15775         if (this.carousel) {
15776             cfg.cls += ' carousel slide';
15777             
15778             cfg.cn = [{
15779                cls : 'carousel-inner'
15780             }];
15781         
15782             if(this.bullets > 0 && !Roo.isTouch){
15783                 
15784                 var bullets = {
15785                     cls : 'carousel-bullets',
15786                     cn : []
15787                 };
15788                 
15789                 if(this.bullets_cls){
15790                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
15791                 }
15792                 
15793                 for (var i = 0; i < this.bullets; i++){
15794                     bullets.cn.push({
15795                         cls : 'bullet bullet-' + i
15796                     });
15797                 }
15798                 
15799                 bullets.cn.push({
15800                     cls : 'clear'
15801                 });
15802                 
15803                 cfg.cn[0].cn = bullets;
15804             }
15805         }
15806         
15807         return cfg;
15808     },
15809     
15810     initEvents:  function()
15811     {
15812         Roo.log('-------- init events on tab group ---------');
15813         
15814         if(this.bullets > 0 && !Roo.isTouch){
15815             this.initBullet();
15816         }
15817         
15818         Roo.log(this);
15819         
15820         if(Roo.isTouch && this.slideOnTouch){
15821             this.el.on("touchstart", this.onTouchStart, this);
15822         }
15823         
15824         if(this.autoslide){
15825             var _this = this;
15826             
15827             this.slideFn = window.setInterval(function() {
15828                 _this.showPanelNext();
15829             }, this.timer);
15830         }
15831         
15832     },
15833     
15834     onTouchStart : function(e, el, o)
15835     {
15836         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
15837             return;
15838         }
15839         
15840         this.showPanelNext();
15841     },
15842     
15843     getChildContainer : function()
15844     {
15845         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
15846     },
15847     
15848     /**
15849     * register a Navigation item
15850     * @param {Roo.bootstrap.NavItem} the navitem to add
15851     */
15852     register : function(item)
15853     {
15854         this.tabs.push( item);
15855         item.navId = this.navId; // not really needed..
15856     
15857     },
15858     
15859     getActivePanel : function()
15860     {
15861         var r = false;
15862         Roo.each(this.tabs, function(t) {
15863             if (t.active) {
15864                 r = t;
15865                 return false;
15866             }
15867             return null;
15868         });
15869         return r;
15870         
15871     },
15872     getPanelByName : function(n)
15873     {
15874         var r = false;
15875         Roo.each(this.tabs, function(t) {
15876             if (t.tabId == n) {
15877                 r = t;
15878                 return false;
15879             }
15880             return null;
15881         });
15882         return r;
15883     },
15884     indexOfPanel : function(p)
15885     {
15886         var r = false;
15887         Roo.each(this.tabs, function(t,i) {
15888             if (t.tabId == p.tabId) {
15889                 r = i;
15890                 return false;
15891             }
15892             return null;
15893         });
15894         return r;
15895     },
15896     /**
15897      * show a specific panel
15898      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
15899      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
15900      */
15901     showPanel : function (pan)
15902     {
15903         if(this.transition){
15904             Roo.log("waiting for the transitionend");
15905             return;
15906         }
15907         
15908         if (typeof(pan) == 'number') {
15909             pan = this.tabs[pan];
15910         }
15911         if (typeof(pan) == 'string') {
15912             pan = this.getPanelByName(pan);
15913         }
15914         if (pan.tabId == this.getActivePanel().tabId) {
15915             return true;
15916         }
15917         var cur = this.getActivePanel();
15918         
15919         if (false === cur.fireEvent('beforedeactivate')) {
15920             return false;
15921         }
15922         
15923         if(this.bullets > 0 && !Roo.isTouch){
15924             this.setActiveBullet(this.indexOfPanel(pan));
15925         }
15926         
15927         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
15928             
15929             this.transition = true;
15930             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
15931             var lr = dir == 'next' ? 'left' : 'right';
15932             pan.el.addClass(dir); // or prev
15933             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
15934             cur.el.addClass(lr); // or right
15935             pan.el.addClass(lr);
15936             
15937             var _this = this;
15938             cur.el.on('transitionend', function() {
15939                 Roo.log("trans end?");
15940                 
15941                 pan.el.removeClass([lr,dir]);
15942                 pan.setActive(true);
15943                 
15944                 cur.el.removeClass([lr]);
15945                 cur.setActive(false);
15946                 
15947                 _this.transition = false;
15948                 
15949             }, this, { single:  true } );
15950             
15951             return true;
15952         }
15953         
15954         cur.setActive(false);
15955         pan.setActive(true);
15956         
15957         return true;
15958         
15959     },
15960     showPanelNext : function()
15961     {
15962         var i = this.indexOfPanel(this.getActivePanel());
15963         
15964         if (i >= this.tabs.length - 1 && !this.autoslide) {
15965             return;
15966         }
15967         
15968         if (i >= this.tabs.length - 1 && this.autoslide) {
15969             i = -1;
15970         }
15971         
15972         this.showPanel(this.tabs[i+1]);
15973     },
15974     
15975     showPanelPrev : function()
15976     {
15977         var i = this.indexOfPanel(this.getActivePanel());
15978         
15979         if (i  < 1 && !this.autoslide) {
15980             return;
15981         }
15982         
15983         if (i < 1 && this.autoslide) {
15984             i = this.tabs.length;
15985         }
15986         
15987         this.showPanel(this.tabs[i-1]);
15988     },
15989     
15990     initBullet : function()
15991     {
15992         if(Roo.isTouch){
15993             return;
15994         }
15995         
15996         var _this = this;
15997         
15998         for (var i = 0; i < this.bullets; i++){
15999             var bullet = this.el.select('.bullet-' + i, true).first();
16000
16001             if(!bullet){
16002                 continue;
16003             }
16004
16005             bullet.on('click', (function(e, el, o, ii, t){
16006
16007                 e.preventDefault();
16008
16009                 _this.showPanel(ii);
16010
16011                 if(_this.autoslide && _this.slideFn){
16012                     clearInterval(_this.slideFn);
16013                     _this.slideFn = window.setInterval(function() {
16014                         _this.showPanelNext();
16015                     }, _this.timer);
16016                 }
16017
16018             }).createDelegate(this, [i, bullet], true));
16019         }
16020     },
16021     
16022     setActiveBullet : function(i)
16023     {
16024         if(Roo.isTouch){
16025             return;
16026         }
16027         
16028         Roo.each(this.el.select('.bullet', true).elements, function(el){
16029             el.removeClass('selected');
16030         });
16031
16032         var bullet = this.el.select('.bullet-' + i, true).first();
16033         
16034         if(!bullet){
16035             return;
16036         }
16037         
16038         bullet.addClass('selected');
16039     }
16040     
16041     
16042   
16043 });
16044
16045  
16046
16047  
16048  
16049 Roo.apply(Roo.bootstrap.TabGroup, {
16050     
16051     groups: {},
16052      /**
16053     * register a Navigation Group
16054     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16055     */
16056     register : function(navgrp)
16057     {
16058         this.groups[navgrp.navId] = navgrp;
16059         
16060     },
16061     /**
16062     * fetch a Navigation Group based on the navigation ID
16063     * if one does not exist , it will get created.
16064     * @param {string} the navgroup to add
16065     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16066     */
16067     get: function(navId) {
16068         if (typeof(this.groups[navId]) == 'undefined') {
16069             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16070         }
16071         return this.groups[navId] ;
16072     }
16073     
16074     
16075     
16076 });
16077
16078  /*
16079  * - LGPL
16080  *
16081  * TabPanel
16082  * 
16083  */
16084
16085 /**
16086  * @class Roo.bootstrap.TabPanel
16087  * @extends Roo.bootstrap.Component
16088  * Bootstrap TabPanel class
16089  * @cfg {Boolean} active panel active
16090  * @cfg {String} html panel content
16091  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16092  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16093  * 
16094  * 
16095  * @constructor
16096  * Create a new TabPanel
16097  * @param {Object} config The config object
16098  */
16099
16100 Roo.bootstrap.TabPanel = function(config){
16101     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16102     this.addEvents({
16103         /**
16104              * @event changed
16105              * Fires when the active status changes
16106              * @param {Roo.bootstrap.TabPanel} this
16107              * @param {Boolean} state the new state
16108             
16109          */
16110         'changed': true,
16111         /**
16112              * @event beforedeactivate
16113              * Fires before a tab is de-activated - can be used to do validation on a form.
16114              * @param {Roo.bootstrap.TabPanel} this
16115              * @return {Boolean} false if there is an error
16116             
16117          */
16118         'beforedeactivate': true
16119      });
16120     
16121     this.tabId = this.tabId || Roo.id();
16122   
16123 };
16124
16125 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16126     
16127     active: false,
16128     html: false,
16129     tabId: false,
16130     navId : false,
16131     
16132     getAutoCreate : function(){
16133         var cfg = {
16134             tag: 'div',
16135             // item is needed for carousel - not sure if it has any effect otherwise
16136             cls: 'tab-pane item',
16137             html: this.html || ''
16138         };
16139         
16140         if(this.active){
16141             cfg.cls += ' active';
16142         }
16143         
16144         if(this.tabId){
16145             cfg.tabId = this.tabId;
16146         }
16147         
16148         
16149         return cfg;
16150     },
16151     
16152     initEvents:  function()
16153     {
16154         Roo.log('-------- init events on tab panel ---------');
16155         
16156         var p = this.parent();
16157         this.navId = this.navId || p.navId;
16158         
16159         if (typeof(this.navId) != 'undefined') {
16160             // not really needed.. but just in case.. parent should be a NavGroup.
16161             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16162             Roo.log(['register', tg, this]);
16163             tg.register(this);
16164             
16165             var i = tg.tabs.length - 1;
16166             
16167             if(this.active && tg.bullets > 0 && i < tg.bullets){
16168                 tg.setActiveBullet(i);
16169             }
16170         }
16171         
16172     },
16173     
16174     
16175     onRender : function(ct, position)
16176     {
16177        // Roo.log("Call onRender: " + this.xtype);
16178         
16179         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16180         
16181         
16182         
16183         
16184         
16185     },
16186     
16187     setActive: function(state)
16188     {
16189         Roo.log("panel - set active " + this.tabId + "=" + state);
16190         
16191         this.active = state;
16192         if (!state) {
16193             this.el.removeClass('active');
16194             
16195         } else  if (!this.el.hasClass('active')) {
16196             this.el.addClass('active');
16197         }
16198         
16199         this.fireEvent('changed', this, state);
16200     }
16201     
16202     
16203 });
16204  
16205
16206  
16207
16208  /*
16209  * - LGPL
16210  *
16211  * DateField
16212  * 
16213  */
16214
16215 /**
16216  * @class Roo.bootstrap.DateField
16217  * @extends Roo.bootstrap.Input
16218  * Bootstrap DateField class
16219  * @cfg {Number} weekStart default 0
16220  * @cfg {String} viewMode default empty, (months|years)
16221  * @cfg {String} minViewMode default empty, (months|years)
16222  * @cfg {Number} startDate default -Infinity
16223  * @cfg {Number} endDate default Infinity
16224  * @cfg {Boolean} todayHighlight default false
16225  * @cfg {Boolean} todayBtn default false
16226  * @cfg {Boolean} calendarWeeks default false
16227  * @cfg {Object} daysOfWeekDisabled default empty
16228  * @cfg {Boolean} singleMode default false (true | false)
16229  * 
16230  * @cfg {Boolean} keyboardNavigation default true
16231  * @cfg {String} language default en
16232  * 
16233  * @constructor
16234  * Create a new DateField
16235  * @param {Object} config The config object
16236  */
16237
16238 Roo.bootstrap.DateField = function(config){
16239     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16240      this.addEvents({
16241             /**
16242              * @event show
16243              * Fires when this field show.
16244              * @param {Roo.bootstrap.DateField} this
16245              * @param {Mixed} date The date value
16246              */
16247             show : true,
16248             /**
16249              * @event show
16250              * Fires when this field hide.
16251              * @param {Roo.bootstrap.DateField} this
16252              * @param {Mixed} date The date value
16253              */
16254             hide : true,
16255             /**
16256              * @event select
16257              * Fires when select a date.
16258              * @param {Roo.bootstrap.DateField} this
16259              * @param {Mixed} date The date value
16260              */
16261             select : true
16262         });
16263 };
16264
16265 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16266     
16267     /**
16268      * @cfg {String} format
16269      * The default date format string which can be overriden for localization support.  The format must be
16270      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16271      */
16272     format : "m/d/y",
16273     /**
16274      * @cfg {String} altFormats
16275      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16276      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16277      */
16278     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16279     
16280     weekStart : 0,
16281     
16282     viewMode : '',
16283     
16284     minViewMode : '',
16285     
16286     todayHighlight : false,
16287     
16288     todayBtn: false,
16289     
16290     language: 'en',
16291     
16292     keyboardNavigation: true,
16293     
16294     calendarWeeks: false,
16295     
16296     startDate: -Infinity,
16297     
16298     endDate: Infinity,
16299     
16300     daysOfWeekDisabled: [],
16301     
16302     _events: [],
16303     
16304     singleMode : false,
16305     
16306     UTCDate: function()
16307     {
16308         return new Date(Date.UTC.apply(Date, arguments));
16309     },
16310     
16311     UTCToday: function()
16312     {
16313         var today = new Date();
16314         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16315     },
16316     
16317     getDate: function() {
16318             var d = this.getUTCDate();
16319             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16320     },
16321     
16322     getUTCDate: function() {
16323             return this.date;
16324     },
16325     
16326     setDate: function(d) {
16327             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16328     },
16329     
16330     setUTCDate: function(d) {
16331             this.date = d;
16332             this.setValue(this.formatDate(this.date));
16333     },
16334         
16335     onRender: function(ct, position)
16336     {
16337         
16338         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16339         
16340         this.language = this.language || 'en';
16341         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16342         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16343         
16344         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16345         this.format = this.format || 'm/d/y';
16346         this.isInline = false;
16347         this.isInput = true;
16348         this.component = this.el.select('.add-on', true).first() || false;
16349         this.component = (this.component && this.component.length === 0) ? false : this.component;
16350         this.hasInput = this.component && this.inputEL().length;
16351         
16352         if (typeof(this.minViewMode === 'string')) {
16353             switch (this.minViewMode) {
16354                 case 'months':
16355                     this.minViewMode = 1;
16356                     break;
16357                 case 'years':
16358                     this.minViewMode = 2;
16359                     break;
16360                 default:
16361                     this.minViewMode = 0;
16362                     break;
16363             }
16364         }
16365         
16366         if (typeof(this.viewMode === 'string')) {
16367             switch (this.viewMode) {
16368                 case 'months':
16369                     this.viewMode = 1;
16370                     break;
16371                 case 'years':
16372                     this.viewMode = 2;
16373                     break;
16374                 default:
16375                     this.viewMode = 0;
16376                     break;
16377             }
16378         }
16379                 
16380         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16381         
16382 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16383         
16384         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16385         
16386         this.picker().on('mousedown', this.onMousedown, this);
16387         this.picker().on('click', this.onClick, this);
16388         
16389         this.picker().addClass('datepicker-dropdown');
16390         
16391         this.startViewMode = this.viewMode;
16392         
16393         if(this.singleMode){
16394             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16395                 v.setVisibilityMode(Roo.Element.DISPLAY)
16396                 v.hide();
16397             });
16398             
16399             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16400                 v.setStyle('width', '189px');
16401             });
16402         }
16403         
16404         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16405             if(!this.calendarWeeks){
16406                 v.remove();
16407                 return;
16408             }
16409             
16410             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16411             v.attr('colspan', function(i, val){
16412                 return parseInt(val) + 1;
16413             });
16414         })
16415                         
16416         
16417         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16418         
16419         this.setStartDate(this.startDate);
16420         this.setEndDate(this.endDate);
16421         
16422         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16423         
16424         this.fillDow();
16425         this.fillMonths();
16426         this.update();
16427         this.showMode();
16428         
16429         if(this.isInline) {
16430             this.show();
16431         }
16432     },
16433     
16434     picker : function()
16435     {
16436         return this.pickerEl;
16437 //        return this.el.select('.datepicker', true).first();
16438     },
16439     
16440     fillDow: function()
16441     {
16442         var dowCnt = this.weekStart;
16443         
16444         var dow = {
16445             tag: 'tr',
16446             cn: [
16447                 
16448             ]
16449         };
16450         
16451         if(this.calendarWeeks){
16452             dow.cn.push({
16453                 tag: 'th',
16454                 cls: 'cw',
16455                 html: '&nbsp;'
16456             })
16457         }
16458         
16459         while (dowCnt < this.weekStart + 7) {
16460             dow.cn.push({
16461                 tag: 'th',
16462                 cls: 'dow',
16463                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16464             });
16465         }
16466         
16467         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16468     },
16469     
16470     fillMonths: function()
16471     {    
16472         var i = 0;
16473         var months = this.picker().select('>.datepicker-months td', true).first();
16474         
16475         months.dom.innerHTML = '';
16476         
16477         while (i < 12) {
16478             var month = {
16479                 tag: 'span',
16480                 cls: 'month',
16481                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16482             }
16483             
16484             months.createChild(month);
16485         }
16486         
16487     },
16488     
16489     update: function()
16490     {
16491         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;
16492         
16493         if (this.date < this.startDate) {
16494             this.viewDate = new Date(this.startDate);
16495         } else if (this.date > this.endDate) {
16496             this.viewDate = new Date(this.endDate);
16497         } else {
16498             this.viewDate = new Date(this.date);
16499         }
16500         
16501         this.fill();
16502     },
16503     
16504     fill: function() 
16505     {
16506         var d = new Date(this.viewDate),
16507                 year = d.getUTCFullYear(),
16508                 month = d.getUTCMonth(),
16509                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16510                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16511                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16512                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16513                 currentDate = this.date && this.date.valueOf(),
16514                 today = this.UTCToday();
16515         
16516         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16517         
16518 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16519         
16520 //        this.picker.select('>tfoot th.today').
16521 //                                              .text(dates[this.language].today)
16522 //                                              .toggle(this.todayBtn !== false);
16523     
16524         this.updateNavArrows();
16525         this.fillMonths();
16526                                                 
16527         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16528         
16529         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16530          
16531         prevMonth.setUTCDate(day);
16532         
16533         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16534         
16535         var nextMonth = new Date(prevMonth);
16536         
16537         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16538         
16539         nextMonth = nextMonth.valueOf();
16540         
16541         var fillMonths = false;
16542         
16543         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16544         
16545         while(prevMonth.valueOf() < nextMonth) {
16546             var clsName = '';
16547             
16548             if (prevMonth.getUTCDay() === this.weekStart) {
16549                 if(fillMonths){
16550                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16551                 }
16552                     
16553                 fillMonths = {
16554                     tag: 'tr',
16555                     cn: []
16556                 };
16557                 
16558                 if(this.calendarWeeks){
16559                     // ISO 8601: First week contains first thursday.
16560                     // ISO also states week starts on Monday, but we can be more abstract here.
16561                     var
16562                     // Start of current week: based on weekstart/current date
16563                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16564                     // Thursday of this week
16565                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16566                     // First Thursday of year, year from thursday
16567                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16568                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16569                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16570                     
16571                     fillMonths.cn.push({
16572                         tag: 'td',
16573                         cls: 'cw',
16574                         html: calWeek
16575                     });
16576                 }
16577             }
16578             
16579             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16580                 clsName += ' old';
16581             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16582                 clsName += ' new';
16583             }
16584             if (this.todayHighlight &&
16585                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16586                 prevMonth.getUTCMonth() == today.getMonth() &&
16587                 prevMonth.getUTCDate() == today.getDate()) {
16588                 clsName += ' today';
16589             }
16590             
16591             if (currentDate && prevMonth.valueOf() === currentDate) {
16592                 clsName += ' active';
16593             }
16594             
16595             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16596                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16597                     clsName += ' disabled';
16598             }
16599             
16600             fillMonths.cn.push({
16601                 tag: 'td',
16602                 cls: 'day ' + clsName,
16603                 html: prevMonth.getDate()
16604             })
16605             
16606             prevMonth.setDate(prevMonth.getDate()+1);
16607         }
16608           
16609         var currentYear = this.date && this.date.getUTCFullYear();
16610         var currentMonth = this.date && this.date.getUTCMonth();
16611         
16612         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16613         
16614         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16615             v.removeClass('active');
16616             
16617             if(currentYear === year && k === currentMonth){
16618                 v.addClass('active');
16619             }
16620             
16621             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16622                 v.addClass('disabled');
16623             }
16624             
16625         });
16626         
16627         
16628         year = parseInt(year/10, 10) * 10;
16629         
16630         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16631         
16632         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16633         
16634         year -= 1;
16635         for (var i = -1; i < 11; i++) {
16636             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16637                 tag: 'span',
16638                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16639                 html: year
16640             })
16641             
16642             year += 1;
16643         }
16644     },
16645     
16646     showMode: function(dir) 
16647     {
16648         if (dir) {
16649             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16650         }
16651         
16652         Roo.each(this.picker().select('>div',true).elements, function(v){
16653             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16654             v.hide();
16655         });
16656         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16657     },
16658     
16659     place: function()
16660     {
16661         if(this.isInline) return;
16662         
16663         this.picker().removeClass(['bottom', 'top']);
16664         
16665         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16666             /*
16667              * place to the top of element!
16668              *
16669              */
16670             
16671             this.picker().addClass('top');
16672             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16673             
16674             return;
16675         }
16676         
16677         this.picker().addClass('bottom');
16678         
16679         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16680     },
16681     
16682     parseDate : function(value)
16683     {
16684         if(!value || value instanceof Date){
16685             return value;
16686         }
16687         var v = Date.parseDate(value, this.format);
16688         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
16689             v = Date.parseDate(value, 'Y-m-d');
16690         }
16691         if(!v && this.altFormats){
16692             if(!this.altFormatsArray){
16693                 this.altFormatsArray = this.altFormats.split("|");
16694             }
16695             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
16696                 v = Date.parseDate(value, this.altFormatsArray[i]);
16697             }
16698         }
16699         return v;
16700     },
16701     
16702     formatDate : function(date, fmt)
16703     {   
16704         return (!date || !(date instanceof Date)) ?
16705         date : date.dateFormat(fmt || this.format);
16706     },
16707     
16708     onFocus : function()
16709     {
16710         Roo.bootstrap.DateField.superclass.onFocus.call(this);
16711         this.show();
16712     },
16713     
16714     onBlur : function()
16715     {
16716         Roo.bootstrap.DateField.superclass.onBlur.call(this);
16717         
16718         var d = this.inputEl().getValue();
16719         
16720         this.setValue(d);
16721                 
16722         this.hide();
16723     },
16724     
16725     show : function()
16726     {
16727         this.picker().show();
16728         this.update();
16729         this.place();
16730         
16731         this.fireEvent('show', this, this.date);
16732     },
16733     
16734     hide : function()
16735     {
16736         if(this.isInline) return;
16737         this.picker().hide();
16738         this.viewMode = this.startViewMode;
16739         this.showMode();
16740         
16741         this.fireEvent('hide', this, this.date);
16742         
16743     },
16744     
16745     onMousedown: function(e)
16746     {
16747         e.stopPropagation();
16748         e.preventDefault();
16749     },
16750     
16751     keyup: function(e)
16752     {
16753         Roo.bootstrap.DateField.superclass.keyup.call(this);
16754         this.update();
16755     },
16756
16757     setValue: function(v)
16758     {
16759         
16760         // v can be a string or a date..
16761         
16762         
16763         var d = new Date(this.parseDate(v) ).clearTime();
16764         
16765         if(isNaN(d.getTime())){
16766             this.date = this.viewDate = '';
16767             Roo.bootstrap.DateField.superclass.setValue.call(this, '');
16768             return;
16769         }
16770         
16771         v = this.formatDate(d);
16772         
16773         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
16774         
16775         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
16776      
16777         this.update();
16778
16779         this.fireEvent('select', this, this.date);
16780         
16781     },
16782     
16783     getValue: function()
16784     {
16785         return this.formatDate(this.date);
16786     },
16787     
16788     fireKey: function(e)
16789     {
16790         if (!this.picker().isVisible()){
16791             if (e.keyCode == 27) // allow escape to hide and re-show picker
16792                 this.show();
16793             return;
16794         }
16795         
16796         var dateChanged = false,
16797         dir, day, month,
16798         newDate, newViewDate;
16799         
16800         switch(e.keyCode){
16801             case 27: // escape
16802                 this.hide();
16803                 e.preventDefault();
16804                 break;
16805             case 37: // left
16806             case 39: // right
16807                 if (!this.keyboardNavigation) break;
16808                 dir = e.keyCode == 37 ? -1 : 1;
16809                 
16810                 if (e.ctrlKey){
16811                     newDate = this.moveYear(this.date, dir);
16812                     newViewDate = this.moveYear(this.viewDate, dir);
16813                 } else if (e.shiftKey){
16814                     newDate = this.moveMonth(this.date, dir);
16815                     newViewDate = this.moveMonth(this.viewDate, dir);
16816                 } else {
16817                     newDate = new Date(this.date);
16818                     newDate.setUTCDate(this.date.getUTCDate() + dir);
16819                     newViewDate = new Date(this.viewDate);
16820                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
16821                 }
16822                 if (this.dateWithinRange(newDate)){
16823                     this.date = newDate;
16824                     this.viewDate = newViewDate;
16825                     this.setValue(this.formatDate(this.date));
16826 //                    this.update();
16827                     e.preventDefault();
16828                     dateChanged = true;
16829                 }
16830                 break;
16831             case 38: // up
16832             case 40: // down
16833                 if (!this.keyboardNavigation) break;
16834                 dir = e.keyCode == 38 ? -1 : 1;
16835                 if (e.ctrlKey){
16836                     newDate = this.moveYear(this.date, dir);
16837                     newViewDate = this.moveYear(this.viewDate, dir);
16838                 } else if (e.shiftKey){
16839                     newDate = this.moveMonth(this.date, dir);
16840                     newViewDate = this.moveMonth(this.viewDate, dir);
16841                 } else {
16842                     newDate = new Date(this.date);
16843                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
16844                     newViewDate = new Date(this.viewDate);
16845                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
16846                 }
16847                 if (this.dateWithinRange(newDate)){
16848                     this.date = newDate;
16849                     this.viewDate = newViewDate;
16850                     this.setValue(this.formatDate(this.date));
16851 //                    this.update();
16852                     e.preventDefault();
16853                     dateChanged = true;
16854                 }
16855                 break;
16856             case 13: // enter
16857                 this.setValue(this.formatDate(this.date));
16858                 this.hide();
16859                 e.preventDefault();
16860                 break;
16861             case 9: // tab
16862                 this.setValue(this.formatDate(this.date));
16863                 this.hide();
16864                 break;
16865             case 16: // shift
16866             case 17: // ctrl
16867             case 18: // alt
16868                 break;
16869             default :
16870                 this.hide();
16871                 
16872         }
16873     },
16874     
16875     
16876     onClick: function(e) 
16877     {
16878         e.stopPropagation();
16879         e.preventDefault();
16880         
16881         var target = e.getTarget();
16882         
16883         if(target.nodeName.toLowerCase() === 'i'){
16884             target = Roo.get(target).dom.parentNode;
16885         }
16886         
16887         var nodeName = target.nodeName;
16888         var className = target.className;
16889         var html = target.innerHTML;
16890         //Roo.log(nodeName);
16891         
16892         switch(nodeName.toLowerCase()) {
16893             case 'th':
16894                 switch(className) {
16895                     case 'switch':
16896                         this.showMode(1);
16897                         break;
16898                     case 'prev':
16899                     case 'next':
16900                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
16901                         switch(this.viewMode){
16902                                 case 0:
16903                                         this.viewDate = this.moveMonth(this.viewDate, dir);
16904                                         break;
16905                                 case 1:
16906                                 case 2:
16907                                         this.viewDate = this.moveYear(this.viewDate, dir);
16908                                         break;
16909                         }
16910                         this.fill();
16911                         break;
16912                     case 'today':
16913                         var date = new Date();
16914                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
16915 //                        this.fill()
16916                         this.setValue(this.formatDate(this.date));
16917                         
16918                         this.hide();
16919                         break;
16920                 }
16921                 break;
16922             case 'span':
16923                 if (className.indexOf('disabled') < 0) {
16924                     this.viewDate.setUTCDate(1);
16925                     if (className.indexOf('month') > -1) {
16926                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
16927                     } else {
16928                         var year = parseInt(html, 10) || 0;
16929                         this.viewDate.setUTCFullYear(year);
16930                         
16931                     }
16932                     
16933                     if(this.singleMode){
16934                         this.setValue(this.formatDate(this.viewDate));
16935                         this.hide();
16936                         return;
16937                     }
16938                     
16939                     this.showMode(-1);
16940                     this.fill();
16941                 }
16942                 break;
16943                 
16944             case 'td':
16945                 //Roo.log(className);
16946                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
16947                     var day = parseInt(html, 10) || 1;
16948                     var year = this.viewDate.getUTCFullYear(),
16949                         month = this.viewDate.getUTCMonth();
16950
16951                     if (className.indexOf('old') > -1) {
16952                         if(month === 0 ){
16953                             month = 11;
16954                             year -= 1;
16955                         }else{
16956                             month -= 1;
16957                         }
16958                     } else if (className.indexOf('new') > -1) {
16959                         if (month == 11) {
16960                             month = 0;
16961                             year += 1;
16962                         } else {
16963                             month += 1;
16964                         }
16965                     }
16966                     //Roo.log([year,month,day]);
16967                     this.date = this.UTCDate(year, month, day,0,0,0,0);
16968                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
16969 //                    this.fill();
16970                     //Roo.log(this.formatDate(this.date));
16971                     this.setValue(this.formatDate(this.date));
16972                     this.hide();
16973                 }
16974                 break;
16975         }
16976     },
16977     
16978     setStartDate: function(startDate)
16979     {
16980         this.startDate = startDate || -Infinity;
16981         if (this.startDate !== -Infinity) {
16982             this.startDate = this.parseDate(this.startDate);
16983         }
16984         this.update();
16985         this.updateNavArrows();
16986     },
16987
16988     setEndDate: function(endDate)
16989     {
16990         this.endDate = endDate || Infinity;
16991         if (this.endDate !== Infinity) {
16992             this.endDate = this.parseDate(this.endDate);
16993         }
16994         this.update();
16995         this.updateNavArrows();
16996     },
16997     
16998     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
16999     {
17000         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17001         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17002             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17003         }
17004         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17005             return parseInt(d, 10);
17006         });
17007         this.update();
17008         this.updateNavArrows();
17009     },
17010     
17011     updateNavArrows: function() 
17012     {
17013         if(this.singleMode){
17014             return;
17015         }
17016         
17017         var d = new Date(this.viewDate),
17018         year = d.getUTCFullYear(),
17019         month = d.getUTCMonth();
17020         
17021         Roo.each(this.picker().select('.prev', true).elements, function(v){
17022             v.show();
17023             switch (this.viewMode) {
17024                 case 0:
17025
17026                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17027                         v.hide();
17028                     }
17029                     break;
17030                 case 1:
17031                 case 2:
17032                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17033                         v.hide();
17034                     }
17035                     break;
17036             }
17037         });
17038         
17039         Roo.each(this.picker().select('.next', true).elements, function(v){
17040             v.show();
17041             switch (this.viewMode) {
17042                 case 0:
17043
17044                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17045                         v.hide();
17046                     }
17047                     break;
17048                 case 1:
17049                 case 2:
17050                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17051                         v.hide();
17052                     }
17053                     break;
17054             }
17055         })
17056     },
17057     
17058     moveMonth: function(date, dir)
17059     {
17060         if (!dir) return date;
17061         var new_date = new Date(date.valueOf()),
17062         day = new_date.getUTCDate(),
17063         month = new_date.getUTCMonth(),
17064         mag = Math.abs(dir),
17065         new_month, test;
17066         dir = dir > 0 ? 1 : -1;
17067         if (mag == 1){
17068             test = dir == -1
17069             // If going back one month, make sure month is not current month
17070             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17071             ? function(){
17072                 return new_date.getUTCMonth() == month;
17073             }
17074             // If going forward one month, make sure month is as expected
17075             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17076             : function(){
17077                 return new_date.getUTCMonth() != new_month;
17078             };
17079             new_month = month + dir;
17080             new_date.setUTCMonth(new_month);
17081             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17082             if (new_month < 0 || new_month > 11)
17083                 new_month = (new_month + 12) % 12;
17084         } else {
17085             // For magnitudes >1, move one month at a time...
17086             for (var i=0; i<mag; i++)
17087                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17088                 new_date = this.moveMonth(new_date, dir);
17089             // ...then reset the day, keeping it in the new month
17090             new_month = new_date.getUTCMonth();
17091             new_date.setUTCDate(day);
17092             test = function(){
17093                 return new_month != new_date.getUTCMonth();
17094             };
17095         }
17096         // Common date-resetting loop -- if date is beyond end of month, make it
17097         // end of month
17098         while (test()){
17099             new_date.setUTCDate(--day);
17100             new_date.setUTCMonth(new_month);
17101         }
17102         return new_date;
17103     },
17104
17105     moveYear: function(date, dir)
17106     {
17107         return this.moveMonth(date, dir*12);
17108     },
17109
17110     dateWithinRange: function(date)
17111     {
17112         return date >= this.startDate && date <= this.endDate;
17113     },
17114
17115     
17116     remove: function() 
17117     {
17118         this.picker().remove();
17119     }
17120    
17121 });
17122
17123 Roo.apply(Roo.bootstrap.DateField,  {
17124     
17125     head : {
17126         tag: 'thead',
17127         cn: [
17128         {
17129             tag: 'tr',
17130             cn: [
17131             {
17132                 tag: 'th',
17133                 cls: 'prev',
17134                 html: '<i class="fa fa-arrow-left"/>'
17135             },
17136             {
17137                 tag: 'th',
17138                 cls: 'switch',
17139                 colspan: '5'
17140             },
17141             {
17142                 tag: 'th',
17143                 cls: 'next',
17144                 html: '<i class="fa fa-arrow-right"/>'
17145             }
17146
17147             ]
17148         }
17149         ]
17150     },
17151     
17152     content : {
17153         tag: 'tbody',
17154         cn: [
17155         {
17156             tag: 'tr',
17157             cn: [
17158             {
17159                 tag: 'td',
17160                 colspan: '7'
17161             }
17162             ]
17163         }
17164         ]
17165     },
17166     
17167     footer : {
17168         tag: 'tfoot',
17169         cn: [
17170         {
17171             tag: 'tr',
17172             cn: [
17173             {
17174                 tag: 'th',
17175                 colspan: '7',
17176                 cls: 'today'
17177             }
17178                     
17179             ]
17180         }
17181         ]
17182     },
17183     
17184     dates:{
17185         en: {
17186             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17187             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17188             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17189             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17190             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17191             today: "Today"
17192         }
17193     },
17194     
17195     modes: [
17196     {
17197         clsName: 'days',
17198         navFnc: 'Month',
17199         navStep: 1
17200     },
17201     {
17202         clsName: 'months',
17203         navFnc: 'FullYear',
17204         navStep: 1
17205     },
17206     {
17207         clsName: 'years',
17208         navFnc: 'FullYear',
17209         navStep: 10
17210     }]
17211 });
17212
17213 Roo.apply(Roo.bootstrap.DateField,  {
17214   
17215     template : {
17216         tag: 'div',
17217         cls: 'datepicker dropdown-menu roo-dynamic',
17218         cn: [
17219         {
17220             tag: 'div',
17221             cls: 'datepicker-days',
17222             cn: [
17223             {
17224                 tag: 'table',
17225                 cls: 'table-condensed',
17226                 cn:[
17227                 Roo.bootstrap.DateField.head,
17228                 {
17229                     tag: 'tbody'
17230                 },
17231                 Roo.bootstrap.DateField.footer
17232                 ]
17233             }
17234             ]
17235         },
17236         {
17237             tag: 'div',
17238             cls: 'datepicker-months',
17239             cn: [
17240             {
17241                 tag: 'table',
17242                 cls: 'table-condensed',
17243                 cn:[
17244                 Roo.bootstrap.DateField.head,
17245                 Roo.bootstrap.DateField.content,
17246                 Roo.bootstrap.DateField.footer
17247                 ]
17248             }
17249             ]
17250         },
17251         {
17252             tag: 'div',
17253             cls: 'datepicker-years',
17254             cn: [
17255             {
17256                 tag: 'table',
17257                 cls: 'table-condensed',
17258                 cn:[
17259                 Roo.bootstrap.DateField.head,
17260                 Roo.bootstrap.DateField.content,
17261                 Roo.bootstrap.DateField.footer
17262                 ]
17263             }
17264             ]
17265         }
17266         ]
17267     }
17268 });
17269
17270  
17271
17272  /*
17273  * - LGPL
17274  *
17275  * TimeField
17276  * 
17277  */
17278
17279 /**
17280  * @class Roo.bootstrap.TimeField
17281  * @extends Roo.bootstrap.Input
17282  * Bootstrap DateField class
17283  * 
17284  * 
17285  * @constructor
17286  * Create a new TimeField
17287  * @param {Object} config The config object
17288  */
17289
17290 Roo.bootstrap.TimeField = function(config){
17291     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17292     this.addEvents({
17293             /**
17294              * @event show
17295              * Fires when this field show.
17296              * @param {Roo.bootstrap.DateField} thisthis
17297              * @param {Mixed} date The date value
17298              */
17299             show : true,
17300             /**
17301              * @event show
17302              * Fires when this field hide.
17303              * @param {Roo.bootstrap.DateField} this
17304              * @param {Mixed} date The date value
17305              */
17306             hide : true,
17307             /**
17308              * @event select
17309              * Fires when select a date.
17310              * @param {Roo.bootstrap.DateField} this
17311              * @param {Mixed} date The date value
17312              */
17313             select : true
17314         });
17315 };
17316
17317 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17318     
17319     /**
17320      * @cfg {String} format
17321      * The default time format string which can be overriden for localization support.  The format must be
17322      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17323      */
17324     format : "H:i",
17325        
17326     onRender: function(ct, position)
17327     {
17328         
17329         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17330                 
17331         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17332         
17333         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17334         
17335         this.pop = this.picker().select('>.datepicker-time',true).first();
17336         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17337         
17338         this.picker().on('mousedown', this.onMousedown, this);
17339         this.picker().on('click', this.onClick, this);
17340         
17341         this.picker().addClass('datepicker-dropdown');
17342     
17343         this.fillTime();
17344         this.update();
17345             
17346         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17347         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17348         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17349         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17350         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17351         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17352
17353     },
17354     
17355     fireKey: function(e){
17356         if (!this.picker().isVisible()){
17357             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17358                 this.show();
17359             }
17360             return;
17361         }
17362
17363         e.preventDefault();
17364         
17365         switch(e.keyCode){
17366             case 27: // escape
17367                 this.hide();
17368                 break;
17369             case 37: // left
17370             case 39: // right
17371                 this.onTogglePeriod();
17372                 break;
17373             case 38: // up
17374                 this.onIncrementMinutes();
17375                 break;
17376             case 40: // down
17377                 this.onDecrementMinutes();
17378                 break;
17379             case 13: // enter
17380             case 9: // tab
17381                 this.setTime();
17382                 break;
17383         }
17384     },
17385     
17386     onClick: function(e) {
17387         e.stopPropagation();
17388         e.preventDefault();
17389     },
17390     
17391     picker : function()
17392     {
17393         return this.el.select('.datepicker', true).first();
17394     },
17395     
17396     fillTime: function()
17397     {    
17398         var time = this.pop.select('tbody', true).first();
17399         
17400         time.dom.innerHTML = '';
17401         
17402         time.createChild({
17403             tag: 'tr',
17404             cn: [
17405                 {
17406                     tag: 'td',
17407                     cn: [
17408                         {
17409                             tag: 'a',
17410                             href: '#',
17411                             cls: 'btn',
17412                             cn: [
17413                                 {
17414                                     tag: 'span',
17415                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17416                                 }
17417                             ]
17418                         } 
17419                     ]
17420                 },
17421                 {
17422                     tag: 'td',
17423                     cls: 'separator'
17424                 },
17425                 {
17426                     tag: 'td',
17427                     cn: [
17428                         {
17429                             tag: 'a',
17430                             href: '#',
17431                             cls: 'btn',
17432                             cn: [
17433                                 {
17434                                     tag: 'span',
17435                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17436                                 }
17437                             ]
17438                         }
17439                     ]
17440                 },
17441                 {
17442                     tag: 'td',
17443                     cls: 'separator'
17444                 }
17445             ]
17446         });
17447         
17448         time.createChild({
17449             tag: 'tr',
17450             cn: [
17451                 {
17452                     tag: 'td',
17453                     cn: [
17454                         {
17455                             tag: 'span',
17456                             cls: 'timepicker-hour',
17457                             html: '00'
17458                         }  
17459                     ]
17460                 },
17461                 {
17462                     tag: 'td',
17463                     cls: 'separator',
17464                     html: ':'
17465                 },
17466                 {
17467                     tag: 'td',
17468                     cn: [
17469                         {
17470                             tag: 'span',
17471                             cls: 'timepicker-minute',
17472                             html: '00'
17473                         }  
17474                     ]
17475                 },
17476                 {
17477                     tag: 'td',
17478                     cls: 'separator'
17479                 },
17480                 {
17481                     tag: 'td',
17482                     cn: [
17483                         {
17484                             tag: 'button',
17485                             type: 'button',
17486                             cls: 'btn btn-primary period',
17487                             html: 'AM'
17488                             
17489                         }
17490                     ]
17491                 }
17492             ]
17493         });
17494         
17495         time.createChild({
17496             tag: 'tr',
17497             cn: [
17498                 {
17499                     tag: 'td',
17500                     cn: [
17501                         {
17502                             tag: 'a',
17503                             href: '#',
17504                             cls: 'btn',
17505                             cn: [
17506                                 {
17507                                     tag: 'span',
17508                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17509                                 }
17510                             ]
17511                         }
17512                     ]
17513                 },
17514                 {
17515                     tag: 'td',
17516                     cls: 'separator'
17517                 },
17518                 {
17519                     tag: 'td',
17520                     cn: [
17521                         {
17522                             tag: 'a',
17523                             href: '#',
17524                             cls: 'btn',
17525                             cn: [
17526                                 {
17527                                     tag: 'span',
17528                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17529                                 }
17530                             ]
17531                         }
17532                     ]
17533                 },
17534                 {
17535                     tag: 'td',
17536                     cls: 'separator'
17537                 }
17538             ]
17539         });
17540         
17541     },
17542     
17543     update: function()
17544     {
17545         
17546         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17547         
17548         this.fill();
17549     },
17550     
17551     fill: function() 
17552     {
17553         var hours = this.time.getHours();
17554         var minutes = this.time.getMinutes();
17555         var period = 'AM';
17556         
17557         if(hours > 11){
17558             period = 'PM';
17559         }
17560         
17561         if(hours == 0){
17562             hours = 12;
17563         }
17564         
17565         
17566         if(hours > 12){
17567             hours = hours - 12;
17568         }
17569         
17570         if(hours < 10){
17571             hours = '0' + hours;
17572         }
17573         
17574         if(minutes < 10){
17575             minutes = '0' + minutes;
17576         }
17577         
17578         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17579         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17580         this.pop.select('button', true).first().dom.innerHTML = period;
17581         
17582     },
17583     
17584     place: function()
17585     {   
17586         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17587         
17588         var cls = ['bottom'];
17589         
17590         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17591             cls.pop();
17592             cls.push('top');
17593         }
17594         
17595         cls.push('right');
17596         
17597         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17598             cls.pop();
17599             cls.push('left');
17600         }
17601         
17602         this.picker().addClass(cls.join('-'));
17603         
17604         var _this = this;
17605         
17606         Roo.each(cls, function(c){
17607             if(c == 'bottom'){
17608                 _this.picker().setTop(_this.inputEl().getHeight());
17609                 return;
17610             }
17611             if(c == 'top'){
17612                 _this.picker().setTop(0 - _this.picker().getHeight());
17613                 return;
17614             }
17615             
17616             if(c == 'left'){
17617                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17618                 return;
17619             }
17620             if(c == 'right'){
17621                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17622                 return;
17623             }
17624         });
17625         
17626     },
17627   
17628     onFocus : function()
17629     {
17630         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17631         this.show();
17632     },
17633     
17634     onBlur : function()
17635     {
17636         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17637         this.hide();
17638     },
17639     
17640     show : function()
17641     {
17642         this.picker().show();
17643         this.pop.show();
17644         this.update();
17645         this.place();
17646         
17647         this.fireEvent('show', this, this.date);
17648     },
17649     
17650     hide : function()
17651     {
17652         this.picker().hide();
17653         this.pop.hide();
17654         
17655         this.fireEvent('hide', this, this.date);
17656     },
17657     
17658     setTime : function()
17659     {
17660         this.hide();
17661         this.setValue(this.time.format(this.format));
17662         
17663         this.fireEvent('select', this, this.date);
17664         
17665         
17666     },
17667     
17668     onMousedown: function(e){
17669         e.stopPropagation();
17670         e.preventDefault();
17671     },
17672     
17673     onIncrementHours: function()
17674     {
17675         Roo.log('onIncrementHours');
17676         this.time = this.time.add(Date.HOUR, 1);
17677         this.update();
17678         
17679     },
17680     
17681     onDecrementHours: function()
17682     {
17683         Roo.log('onDecrementHours');
17684         this.time = this.time.add(Date.HOUR, -1);
17685         this.update();
17686     },
17687     
17688     onIncrementMinutes: function()
17689     {
17690         Roo.log('onIncrementMinutes');
17691         this.time = this.time.add(Date.MINUTE, 1);
17692         this.update();
17693     },
17694     
17695     onDecrementMinutes: function()
17696     {
17697         Roo.log('onDecrementMinutes');
17698         this.time = this.time.add(Date.MINUTE, -1);
17699         this.update();
17700     },
17701     
17702     onTogglePeriod: function()
17703     {
17704         Roo.log('onTogglePeriod');
17705         this.time = this.time.add(Date.HOUR, 12);
17706         this.update();
17707     }
17708     
17709    
17710 });
17711
17712 Roo.apply(Roo.bootstrap.TimeField,  {
17713     
17714     content : {
17715         tag: 'tbody',
17716         cn: [
17717             {
17718                 tag: 'tr',
17719                 cn: [
17720                 {
17721                     tag: 'td',
17722                     colspan: '7'
17723                 }
17724                 ]
17725             }
17726         ]
17727     },
17728     
17729     footer : {
17730         tag: 'tfoot',
17731         cn: [
17732             {
17733                 tag: 'tr',
17734                 cn: [
17735                 {
17736                     tag: 'th',
17737                     colspan: '7',
17738                     cls: '',
17739                     cn: [
17740                         {
17741                             tag: 'button',
17742                             cls: 'btn btn-info ok',
17743                             html: 'OK'
17744                         }
17745                     ]
17746                 }
17747
17748                 ]
17749             }
17750         ]
17751     }
17752 });
17753
17754 Roo.apply(Roo.bootstrap.TimeField,  {
17755   
17756     template : {
17757         tag: 'div',
17758         cls: 'datepicker dropdown-menu',
17759         cn: [
17760             {
17761                 tag: 'div',
17762                 cls: 'datepicker-time',
17763                 cn: [
17764                 {
17765                     tag: 'table',
17766                     cls: 'table-condensed',
17767                     cn:[
17768                     Roo.bootstrap.TimeField.content,
17769                     Roo.bootstrap.TimeField.footer
17770                     ]
17771                 }
17772                 ]
17773             }
17774         ]
17775     }
17776 });
17777
17778  
17779
17780  /*
17781  * - LGPL
17782  *
17783  * MonthField
17784  * 
17785  */
17786
17787 /**
17788  * @class Roo.bootstrap.MonthField
17789  * @extends Roo.bootstrap.Input
17790  * Bootstrap MonthField class
17791  * 
17792  * @cfg {String} language default en
17793  * 
17794  * @constructor
17795  * Create a new MonthField
17796  * @param {Object} config The config object
17797  */
17798
17799 Roo.bootstrap.MonthField = function(config){
17800     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
17801     
17802     this.addEvents({
17803         /**
17804          * @event show
17805          * Fires when this field show.
17806          * @param {Roo.bootstrap.MonthField} this
17807          * @param {Mixed} date The date value
17808          */
17809         show : true,
17810         /**
17811          * @event show
17812          * Fires when this field hide.
17813          * @param {Roo.bootstrap.MonthField} this
17814          * @param {Mixed} date The date value
17815          */
17816         hide : true,
17817         /**
17818          * @event select
17819          * Fires when select a date.
17820          * @param {Roo.bootstrap.MonthField} this
17821          * @param {String} oldvalue The old value
17822          * @param {String} newvalue The new value
17823          */
17824         select : true
17825     });
17826 };
17827
17828 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
17829     
17830     onRender: function(ct, position)
17831     {
17832         
17833         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
17834         
17835         this.language = this.language || 'en';
17836         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
17837         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
17838         
17839         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
17840         this.isInline = false;
17841         this.isInput = true;
17842         this.component = this.el.select('.add-on', true).first() || false;
17843         this.component = (this.component && this.component.length === 0) ? false : this.component;
17844         this.hasInput = this.component && this.inputEL().length;
17845         
17846         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
17847         
17848         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17849         
17850         this.picker().on('mousedown', this.onMousedown, this);
17851         this.picker().on('click', this.onClick, this);
17852         
17853         this.picker().addClass('datepicker-dropdown');
17854         
17855         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17856             v.setStyle('width', '189px');
17857         });
17858         
17859         this.fillMonths();
17860         
17861         this.update();
17862         
17863         if(this.isInline) {
17864             this.show();
17865         }
17866         
17867     },
17868     
17869     setValue: function(v, suppressEvent)
17870     {   
17871         var o = this.getValue();
17872         
17873         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
17874         
17875         this.update();
17876
17877         if(suppressEvent !== true){
17878             this.fireEvent('select', this, o, v);
17879         }
17880         
17881     },
17882     
17883     getValue: function()
17884     {
17885         return this.value;
17886     },
17887     
17888     onClick: function(e) 
17889     {
17890         e.stopPropagation();
17891         e.preventDefault();
17892         
17893         var target = e.getTarget();
17894         
17895         if(target.nodeName.toLowerCase() === 'i'){
17896             target = Roo.get(target).dom.parentNode;
17897         }
17898         
17899         var nodeName = target.nodeName;
17900         var className = target.className;
17901         var html = target.innerHTML;
17902         
17903         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
17904             return;
17905         }
17906         
17907         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
17908         
17909         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
17910         
17911         this.hide();
17912                         
17913     },
17914     
17915     picker : function()
17916     {
17917         return this.pickerEl;
17918     },
17919     
17920     fillMonths: function()
17921     {    
17922         var i = 0;
17923         var months = this.picker().select('>.datepicker-months td', true).first();
17924         
17925         months.dom.innerHTML = '';
17926         
17927         while (i < 12) {
17928             var month = {
17929                 tag: 'span',
17930                 cls: 'month',
17931                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
17932             }
17933             
17934             months.createChild(month);
17935         }
17936         
17937     },
17938     
17939     update: function()
17940     {
17941         var _this = this;
17942         
17943         if(typeof(this.vIndex) == 'undefined' && this.value.length){
17944             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
17945         }
17946         
17947         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
17948             e.removeClass('active');
17949             
17950             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
17951                 e.addClass('active');
17952             }
17953         })
17954     },
17955     
17956     place: function()
17957     {
17958         if(this.isInline) return;
17959         
17960         this.picker().removeClass(['bottom', 'top']);
17961         
17962         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17963             /*
17964              * place to the top of element!
17965              *
17966              */
17967             
17968             this.picker().addClass('top');
17969             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17970             
17971             return;
17972         }
17973         
17974         this.picker().addClass('bottom');
17975         
17976         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17977     },
17978     
17979     onFocus : function()
17980     {
17981         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
17982         this.show();
17983     },
17984     
17985     onBlur : function()
17986     {
17987         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
17988         
17989         var d = this.inputEl().getValue();
17990         
17991         this.setValue(d);
17992                 
17993         this.hide();
17994     },
17995     
17996     show : function()
17997     {
17998         this.picker().show();
17999         this.picker().select('>.datepicker-months', true).first().show();
18000         this.update();
18001         this.place();
18002         
18003         this.fireEvent('show', this, this.date);
18004     },
18005     
18006     hide : function()
18007     {
18008         if(this.isInline) return;
18009         this.picker().hide();
18010         this.fireEvent('hide', this, this.date);
18011         
18012     },
18013     
18014     onMousedown: function(e)
18015     {
18016         e.stopPropagation();
18017         e.preventDefault();
18018     },
18019     
18020     keyup: function(e)
18021     {
18022         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18023         this.update();
18024     },
18025
18026     fireKey: function(e)
18027     {
18028         if (!this.picker().isVisible()){
18029             if (e.keyCode == 27) // allow escape to hide and re-show picker
18030                 this.show();
18031             return;
18032         }
18033         
18034         var dir;
18035         
18036         switch(e.keyCode){
18037             case 27: // escape
18038                 this.hide();
18039                 e.preventDefault();
18040                 break;
18041             case 37: // left
18042             case 39: // right
18043                 dir = e.keyCode == 37 ? -1 : 1;
18044                 
18045                 this.vIndex = this.vIndex + dir;
18046                 
18047                 if(this.vIndex < 0){
18048                     this.vIndex = 0;
18049                 }
18050                 
18051                 if(this.vIndex > 11){
18052                     this.vIndex = 11;
18053                 }
18054                 
18055                 if(isNaN(this.vIndex)){
18056                     this.vIndex = 0;
18057                 }
18058                 
18059                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18060                 
18061                 break;
18062             case 38: // up
18063             case 40: // down
18064                 
18065                 dir = e.keyCode == 38 ? -1 : 1;
18066                 
18067                 this.vIndex = this.vIndex + dir * 4;
18068                 
18069                 if(this.vIndex < 0){
18070                     this.vIndex = 0;
18071                 }
18072                 
18073                 if(this.vIndex > 11){
18074                     this.vIndex = 11;
18075                 }
18076                 
18077                 if(isNaN(this.vIndex)){
18078                     this.vIndex = 0;
18079                 }
18080                 
18081                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18082                 break;
18083                 
18084             case 13: // enter
18085                 
18086                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18087                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18088                 }
18089                 
18090                 this.hide();
18091                 e.preventDefault();
18092                 break;
18093             case 9: // tab
18094                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18095                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18096                 }
18097                 this.hide();
18098                 break;
18099             case 16: // shift
18100             case 17: // ctrl
18101             case 18: // alt
18102                 break;
18103             default :
18104                 this.hide();
18105                 
18106         }
18107     },
18108     
18109     remove: function() 
18110     {
18111         this.picker().remove();
18112     }
18113    
18114 });
18115
18116 Roo.apply(Roo.bootstrap.MonthField,  {
18117     
18118     content : {
18119         tag: 'tbody',
18120         cn: [
18121         {
18122             tag: 'tr',
18123             cn: [
18124             {
18125                 tag: 'td',
18126                 colspan: '7'
18127             }
18128             ]
18129         }
18130         ]
18131     },
18132     
18133     dates:{
18134         en: {
18135             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18136             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18137         }
18138     }
18139 });
18140
18141 Roo.apply(Roo.bootstrap.MonthField,  {
18142   
18143     template : {
18144         tag: 'div',
18145         cls: 'datepicker dropdown-menu roo-dynamic',
18146         cn: [
18147             {
18148                 tag: 'div',
18149                 cls: 'datepicker-months',
18150                 cn: [
18151                 {
18152                     tag: 'table',
18153                     cls: 'table-condensed',
18154                     cn:[
18155                         Roo.bootstrap.DateField.content
18156                     ]
18157                 }
18158                 ]
18159             }
18160         ]
18161     }
18162 });
18163
18164  
18165
18166  
18167  /*
18168  * - LGPL
18169  *
18170  * CheckBox
18171  * 
18172  */
18173
18174 /**
18175  * @class Roo.bootstrap.CheckBox
18176  * @extends Roo.bootstrap.Input
18177  * Bootstrap CheckBox class
18178  * 
18179  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18180  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18181  * @cfg {String} boxLabel The text that appears beside the checkbox
18182  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18183  * @cfg {Boolean} checked initnal the element
18184  * @cfg {Boolean} inline inline the element (default false)
18185  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18186  * 
18187  * @constructor
18188  * Create a new CheckBox
18189  * @param {Object} config The config object
18190  */
18191
18192 Roo.bootstrap.CheckBox = function(config){
18193     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18194    
18195     this.addEvents({
18196         /**
18197         * @event check
18198         * Fires when the element is checked or unchecked.
18199         * @param {Roo.bootstrap.CheckBox} this This input
18200         * @param {Boolean} checked The new checked value
18201         */
18202        check : true
18203     });
18204     
18205 };
18206
18207 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18208   
18209     inputType: 'checkbox',
18210     inputValue: 1,
18211     valueOff: 0,
18212     boxLabel: false,
18213     checked: false,
18214     weight : false,
18215     inline: false,
18216     
18217     getAutoCreate : function()
18218     {
18219         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18220         
18221         var id = Roo.id();
18222         
18223         var cfg = {};
18224         
18225         cfg.cls = 'form-group ' + this.inputType; //input-group
18226         
18227         if(this.inline){
18228             cfg.cls += ' ' + this.inputType + '-inline';
18229         }
18230         
18231         var input =  {
18232             tag: 'input',
18233             id : id,
18234             type : this.inputType,
18235             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18236             cls : 'roo-' + this.inputType, //'form-box',
18237             placeholder : this.placeholder || ''
18238             
18239         };
18240         
18241         if (this.weight) { // Validity check?
18242             cfg.cls += " " + this.inputType + "-" + this.weight;
18243         }
18244         
18245         if (this.disabled) {
18246             input.disabled=true;
18247         }
18248         
18249         if(this.checked){
18250             input.checked = this.checked;
18251         }
18252         
18253         if (this.name) {
18254             input.name = this.name;
18255         }
18256         
18257         if (this.size) {
18258             input.cls += ' input-' + this.size;
18259         }
18260         
18261         var settings=this;
18262         
18263         ['xs','sm','md','lg'].map(function(size){
18264             if (settings[size]) {
18265                 cfg.cls += ' col-' + size + '-' + settings[size];
18266             }
18267         });
18268         
18269         var inputblock = input;
18270          
18271         if (this.before || this.after) {
18272             
18273             inputblock = {
18274                 cls : 'input-group',
18275                 cn :  [] 
18276             };
18277             
18278             if (this.before) {
18279                 inputblock.cn.push({
18280                     tag :'span',
18281                     cls : 'input-group-addon',
18282                     html : this.before
18283                 });
18284             }
18285             
18286             inputblock.cn.push(input);
18287             
18288             if (this.after) {
18289                 inputblock.cn.push({
18290                     tag :'span',
18291                     cls : 'input-group-addon',
18292                     html : this.after
18293                 });
18294             }
18295             
18296         }
18297         
18298         if (align ==='left' && this.fieldLabel.length) {
18299                 Roo.log("left and has label");
18300                 cfg.cn = [
18301                     
18302                     {
18303                         tag: 'label',
18304                         'for' :  id,
18305                         cls : 'control-label col-md-' + this.labelWidth,
18306                         html : this.fieldLabel
18307                         
18308                     },
18309                     {
18310                         cls : "col-md-" + (12 - this.labelWidth), 
18311                         cn: [
18312                             inputblock
18313                         ]
18314                     }
18315                     
18316                 ];
18317         } else if ( this.fieldLabel.length) {
18318                 Roo.log(" label");
18319                 cfg.cn = [
18320                    
18321                     {
18322                         tag: this.boxLabel ? 'span' : 'label',
18323                         'for': id,
18324                         cls: 'control-label box-input-label',
18325                         //cls : 'input-group-addon',
18326                         html : this.fieldLabel
18327                         
18328                     },
18329                     
18330                     inputblock
18331                     
18332                 ];
18333
18334         } else {
18335             
18336                 Roo.log(" no label && no align");
18337                 cfg.cn = [  inputblock ] ;
18338                 
18339                 
18340         }
18341         if(this.boxLabel){
18342              var boxLabelCfg = {
18343                 tag: 'label',
18344                 //'for': id, // box label is handled by onclick - so no for...
18345                 cls: 'box-label',
18346                 html: this.boxLabel
18347             }
18348             
18349             if(this.tooltip){
18350                 boxLabelCfg.tooltip = this.tooltip;
18351             }
18352              
18353             cfg.cn.push(boxLabelCfg);
18354         }
18355         
18356         
18357        
18358         return cfg;
18359         
18360     },
18361     
18362     /**
18363      * return the real input element.
18364      */
18365     inputEl: function ()
18366     {
18367         return this.el.select('input.roo-' + this.inputType,true).first();
18368     },
18369     
18370     labelEl: function()
18371     {
18372         return this.el.select('label.control-label',true).first();
18373     },
18374     /* depricated... */
18375     
18376     label: function()
18377     {
18378         return this.labelEl();
18379     },
18380     
18381     initEvents : function()
18382     {
18383 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18384         
18385         this.inputEl().on('click', this.onClick,  this);
18386         
18387         if (this.boxLabel) { 
18388             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18389         }
18390         
18391         this.startValue = this.getValue();
18392         
18393         if(this.groupId){
18394             Roo.bootstrap.CheckBox.register(this);
18395         }
18396     },
18397     
18398     onClick : function()
18399     {   
18400         this.setChecked(!this.checked);
18401     },
18402     
18403     setChecked : function(state,suppressEvent)
18404     {
18405         this.startValue = this.getValue();
18406         
18407         if(this.inputType == 'radio'){
18408             
18409             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18410                 e.dom.checked = false;
18411             });
18412             
18413             this.inputEl().dom.checked = true;
18414             
18415             this.inputEl().dom.value = this.inputValue;
18416             
18417             if(suppressEvent !== true){
18418                 this.fireEvent('check', this, true);
18419             }
18420             
18421             this.validate();
18422             
18423             return;
18424         }
18425         
18426         this.checked = state;
18427         
18428         this.inputEl().dom.checked = state;
18429         
18430         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18431         
18432         if(suppressEvent !== true){
18433             this.fireEvent('check', this, state);
18434         }
18435         
18436         this.validate();
18437     },
18438     
18439     getValue : function()
18440     {
18441         if(this.inputType == 'radio'){
18442             return this.getGroupValue();
18443         }
18444         
18445         return this.inputEl().getValue();
18446         
18447     },
18448     
18449     getGroupValue : function()
18450     {
18451         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18452             return '';
18453         }
18454         
18455         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18456     },
18457     
18458     setValue : function(v,suppressEvent)
18459     {
18460         if(this.inputType == 'radio'){
18461             this.setGroupValue(v, suppressEvent);
18462             return;
18463         }
18464         
18465         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18466         
18467         this.validate();
18468     },
18469     
18470     setGroupValue : function(v, suppressEvent)
18471     {
18472         this.startValue = this.getValue();
18473         
18474         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18475             e.dom.checked = false;
18476             
18477             if(e.dom.value == v){
18478                 e.dom.checked = true;
18479             }
18480         });
18481         
18482         if(suppressEvent !== true){
18483             this.fireEvent('check', this, true);
18484         }
18485
18486         this.validate();
18487         
18488         return;
18489     },
18490     
18491     validate : function()
18492     {
18493         if(
18494                 this.disabled || 
18495                 (this.inputType == 'radio' && this.validateRadio()) ||
18496                 (this.inputType == 'checkbox' && this.validateCheckbox())
18497         ){
18498             this.markValid();
18499             return true;
18500         }
18501         
18502         this.markInvalid();
18503         return false;
18504     },
18505     
18506     validateRadio : function()
18507     {
18508         var valid = false;
18509         
18510         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18511             if(!e.dom.checked){
18512                 return;
18513             }
18514             
18515             valid = true;
18516             
18517             return false;
18518         });
18519         
18520         return valid;
18521     },
18522     
18523     validateCheckbox : function()
18524     {
18525         if(!this.groupId){
18526             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18527         }
18528         
18529         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18530         
18531         if(!group){
18532             return false;
18533         }
18534         
18535         var r = false;
18536         
18537         for(var i in group){
18538             if(r){
18539                 break;
18540             }
18541             
18542             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18543         }
18544         
18545         return r;
18546     },
18547     
18548     /**
18549      * Mark this field as valid
18550      */
18551     markValid : function()
18552     {
18553         if(this.allowBlank){
18554             return;
18555         }
18556         
18557         var _this = this;
18558         
18559         this.fireEvent('valid', this);
18560         
18561         if(this.inputType == 'radio'){
18562             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18563                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18564                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18565             });
18566             
18567             return;
18568         }
18569         
18570         if(!this.groupId){
18571             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18572             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18573             return;
18574         }
18575         
18576         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18577             
18578         if(!group){
18579             return;
18580         }
18581         
18582         for(var i in group){
18583             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18584             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18585         }
18586     },
18587     
18588      /**
18589      * Mark this field as invalid
18590      * @param {String} msg The validation message
18591      */
18592     markInvalid : function(msg)
18593     {
18594         if(this.allowBlank){
18595             return;
18596         }
18597         
18598         var _this = this;
18599         
18600         this.fireEvent('invalid', this, msg);
18601         
18602         if(this.inputType == 'radio'){
18603             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18604                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18605                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18606             });
18607             
18608             return;
18609         }
18610         
18611         if(!this.groupId){
18612             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18613             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18614             return;
18615         }
18616         
18617         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18618             
18619         if(!group){
18620             return;
18621         }
18622         
18623         for(var i in group){
18624             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18625             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18626         }
18627         
18628     }
18629     
18630 });
18631
18632 Roo.apply(Roo.bootstrap.CheckBox, {
18633     
18634     groups: {},
18635     
18636      /**
18637     * register a CheckBox Group
18638     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18639     */
18640     register : function(checkbox)
18641     {
18642         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18643             this.groups[checkbox.groupId] = {};
18644         }
18645         
18646         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18647             return;
18648         }
18649         
18650         this.groups[checkbox.groupId][checkbox.name] = checkbox;
18651         
18652     },
18653     /**
18654     * fetch a CheckBox Group based on the group ID
18655     * @param {string} the group ID
18656     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
18657     */
18658     get: function(groupId) {
18659         if (typeof(this.groups[groupId]) == 'undefined') {
18660             return false;
18661         }
18662         
18663         return this.groups[groupId] ;
18664     }
18665     
18666     
18667 });
18668 /*
18669  * - LGPL
18670  *
18671  * Radio
18672  *
18673  *
18674  * not inline
18675  *<div class="radio">
18676   <label>
18677     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
18678     Option one is this and that&mdash;be sure to include why it's great
18679   </label>
18680 </div>
18681  *
18682  *
18683  *inline
18684  *<span>
18685  *<label class="radio-inline">fieldLabel</label>
18686  *<label class="radio-inline">
18687   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
18688 </label>
18689 <span>
18690  * 
18691  * 
18692  */
18693
18694 /**
18695  * @class Roo.bootstrap.Radio
18696  * @extends Roo.bootstrap.CheckBox
18697  * Bootstrap Radio class
18698
18699  * @constructor
18700  * Create a new Radio
18701  * @param {Object} config The config object
18702  */
18703
18704 Roo.bootstrap.Radio = function(config){
18705     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
18706    
18707 };
18708
18709 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
18710     
18711     inputType: 'radio',
18712     inputValue: '',
18713     valueOff: '',
18714     
18715     getAutoCreate : function()
18716     {
18717         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18718         align = align || 'left'; // default...
18719         
18720         
18721         
18722         var id = Roo.id();
18723         
18724         var cfg = {
18725                 tag : this.inline ? 'span' : 'div',
18726                 cls : '',
18727                 cn : []
18728         };
18729         
18730         var inline = this.inline ? ' radio-inline' : '';
18731         
18732         var lbl = {
18733                 tag: 'label' ,
18734                 // does not need for, as we wrap the input with it..
18735                 'for' : id,
18736                 cls : 'control-label box-label' + inline,
18737                 cn : []
18738         };
18739         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
18740         
18741         var fieldLabel = {
18742             tag: 'label' ,
18743             //cls : 'control-label' + inline,
18744             html : this.fieldLabel,
18745             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
18746         };
18747         
18748  
18749         
18750         
18751         var input =  {
18752             tag: 'input',
18753             id : id,
18754             type : this.inputType,
18755             //value : (!this.checked) ? this.valueOff : this.inputValue,
18756             value : this.inputValue,
18757             cls : 'roo-radio',
18758             placeholder : this.placeholder || '' // ?? needed????
18759             
18760         };
18761         if (this.weight) { // Validity check?
18762             input.cls += " radio-" + this.weight;
18763         }
18764         if (this.disabled) {
18765             input.disabled=true;
18766         }
18767         
18768         if(this.checked){
18769             input.checked = this.checked;
18770         }
18771         
18772         if (this.name) {
18773             input.name = this.name;
18774         }
18775         
18776         if (this.size) {
18777             input.cls += ' input-' + this.size;
18778         }
18779         
18780         //?? can span's inline have a width??
18781         
18782         var settings=this;
18783         ['xs','sm','md','lg'].map(function(size){
18784             if (settings[size]) {
18785                 cfg.cls += ' col-' + size + '-' + settings[size];
18786             }
18787         });
18788         
18789         var inputblock = input;
18790         
18791         if (this.before || this.after) {
18792             
18793             inputblock = {
18794                 cls : 'input-group',
18795                 tag : 'span',
18796                 cn :  [] 
18797             };
18798             if (this.before) {
18799                 inputblock.cn.push({
18800                     tag :'span',
18801                     cls : 'input-group-addon',
18802                     html : this.before
18803                 });
18804             }
18805             inputblock.cn.push(input);
18806             if (this.after) {
18807                 inputblock.cn.push({
18808                     tag :'span',
18809                     cls : 'input-group-addon',
18810                     html : this.after
18811                 });
18812             }
18813             
18814         };
18815         
18816         
18817         if (this.fieldLabel && this.fieldLabel.length) {
18818             cfg.cn.push(fieldLabel);
18819         }
18820        
18821         // normal bootstrap puts the input inside the label.
18822         // however with our styled version - it has to go after the input.
18823        
18824         //lbl.cn.push(inputblock);
18825         
18826         var lblwrap =  {
18827             tag: 'span',
18828             cls: 'radio' + inline,
18829             cn: [
18830                 inputblock,
18831                 lbl
18832             ]
18833         };
18834         
18835         cfg.cn.push( lblwrap);
18836         
18837         if(this.boxLabel){
18838             lbl.cn.push({
18839                 tag: 'span',
18840                 html: this.boxLabel
18841             })
18842         }
18843          
18844         
18845         return cfg;
18846         
18847     },
18848     
18849     initEvents : function()
18850     {
18851 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18852         
18853         this.inputEl().on('click', this.onClick,  this);
18854         if (this.boxLabel) {
18855             Roo.log('find label')
18856             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
18857         }
18858         
18859     },
18860     
18861     inputEl: function ()
18862     {
18863         return this.el.select('input.roo-radio',true).first();
18864     },
18865     onClick : function()
18866     {   
18867         Roo.log("click");
18868         this.setChecked(true);
18869     },
18870     
18871     setChecked : function(state,suppressEvent)
18872     {
18873         if(state){
18874             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18875                 v.dom.checked = false;
18876             });
18877         }
18878         Roo.log(this.inputEl().dom);
18879         this.checked = state;
18880         this.inputEl().dom.checked = state;
18881         
18882         if(suppressEvent !== true){
18883             this.fireEvent('check', this, state);
18884         }
18885         
18886         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18887         
18888     },
18889     
18890     getGroupValue : function()
18891     {
18892         var value = '';
18893         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
18894             if(v.dom.checked == true){
18895                 value = v.dom.value;
18896             }
18897         });
18898         
18899         return value;
18900     },
18901     
18902     /**
18903      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
18904      * @return {Mixed} value The field value
18905      */
18906     getValue : function(){
18907         return this.getGroupValue();
18908     }
18909     
18910 });
18911
18912  
18913 //<script type="text/javascript">
18914
18915 /*
18916  * Based  Ext JS Library 1.1.1
18917  * Copyright(c) 2006-2007, Ext JS, LLC.
18918  * LGPL
18919  *
18920  */
18921  
18922 /**
18923  * @class Roo.HtmlEditorCore
18924  * @extends Roo.Component
18925  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
18926  *
18927  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
18928  */
18929
18930 Roo.HtmlEditorCore = function(config){
18931     
18932     
18933     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
18934     
18935     
18936     this.addEvents({
18937         /**
18938          * @event initialize
18939          * Fires when the editor is fully initialized (including the iframe)
18940          * @param {Roo.HtmlEditorCore} this
18941          */
18942         initialize: true,
18943         /**
18944          * @event activate
18945          * Fires when the editor is first receives the focus. Any insertion must wait
18946          * until after this event.
18947          * @param {Roo.HtmlEditorCore} this
18948          */
18949         activate: true,
18950          /**
18951          * @event beforesync
18952          * Fires before the textarea is updated with content from the editor iframe. Return false
18953          * to cancel the sync.
18954          * @param {Roo.HtmlEditorCore} this
18955          * @param {String} html
18956          */
18957         beforesync: true,
18958          /**
18959          * @event beforepush
18960          * Fires before the iframe editor is updated with content from the textarea. Return false
18961          * to cancel the push.
18962          * @param {Roo.HtmlEditorCore} this
18963          * @param {String} html
18964          */
18965         beforepush: true,
18966          /**
18967          * @event sync
18968          * Fires when the textarea is updated with content from the editor iframe.
18969          * @param {Roo.HtmlEditorCore} this
18970          * @param {String} html
18971          */
18972         sync: true,
18973          /**
18974          * @event push
18975          * Fires when the iframe editor is updated with content from the textarea.
18976          * @param {Roo.HtmlEditorCore} this
18977          * @param {String} html
18978          */
18979         push: true,
18980         
18981         /**
18982          * @event editorevent
18983          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18984          * @param {Roo.HtmlEditorCore} this
18985          */
18986         editorevent: true
18987         
18988     });
18989     
18990     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
18991     
18992     // defaults : white / black...
18993     this.applyBlacklists();
18994     
18995     
18996     
18997 };
18998
18999
19000 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19001
19002
19003      /**
19004      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19005      */
19006     
19007     owner : false,
19008     
19009      /**
19010      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19011      *                        Roo.resizable.
19012      */
19013     resizable : false,
19014      /**
19015      * @cfg {Number} height (in pixels)
19016      */   
19017     height: 300,
19018    /**
19019      * @cfg {Number} width (in pixels)
19020      */   
19021     width: 500,
19022     
19023     /**
19024      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19025      * 
19026      */
19027     stylesheets: false,
19028     
19029     // id of frame..
19030     frameId: false,
19031     
19032     // private properties
19033     validationEvent : false,
19034     deferHeight: true,
19035     initialized : false,
19036     activated : false,
19037     sourceEditMode : false,
19038     onFocus : Roo.emptyFn,
19039     iframePad:3,
19040     hideMode:'offsets',
19041     
19042     clearUp: true,
19043     
19044     // blacklist + whitelisted elements..
19045     black: false,
19046     white: false,
19047      
19048     
19049
19050     /**
19051      * Protected method that will not generally be called directly. It
19052      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19053      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19054      */
19055     getDocMarkup : function(){
19056         // body styles..
19057         var st = '';
19058         
19059         // inherit styels from page...?? 
19060         if (this.stylesheets === false) {
19061             
19062             Roo.get(document.head).select('style').each(function(node) {
19063                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19064             });
19065             
19066             Roo.get(document.head).select('link').each(function(node) { 
19067                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19068             });
19069             
19070         } else if (!this.stylesheets.length) {
19071                 // simple..
19072                 st = '<style type="text/css">' +
19073                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19074                    '</style>';
19075         } else { 
19076             
19077         }
19078         
19079         st +=  '<style type="text/css">' +
19080             'IMG { cursor: pointer } ' +
19081         '</style>';
19082
19083         
19084         return '<html><head>' + st  +
19085             //<style type="text/css">' +
19086             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19087             //'</style>' +
19088             ' </head><body class="roo-htmleditor-body"></body></html>';
19089     },
19090
19091     // private
19092     onRender : function(ct, position)
19093     {
19094         var _t = this;
19095         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19096         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19097         
19098         
19099         this.el.dom.style.border = '0 none';
19100         this.el.dom.setAttribute('tabIndex', -1);
19101         this.el.addClass('x-hidden hide');
19102         
19103         
19104         
19105         if(Roo.isIE){ // fix IE 1px bogus margin
19106             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19107         }
19108        
19109         
19110         this.frameId = Roo.id();
19111         
19112          
19113         
19114         var iframe = this.owner.wrap.createChild({
19115             tag: 'iframe',
19116             cls: 'form-control', // bootstrap..
19117             id: this.frameId,
19118             name: this.frameId,
19119             frameBorder : 'no',
19120             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19121         }, this.el
19122         );
19123         
19124         
19125         this.iframe = iframe.dom;
19126
19127          this.assignDocWin();
19128         
19129         this.doc.designMode = 'on';
19130        
19131         this.doc.open();
19132         this.doc.write(this.getDocMarkup());
19133         this.doc.close();
19134
19135         
19136         var task = { // must defer to wait for browser to be ready
19137             run : function(){
19138                 //console.log("run task?" + this.doc.readyState);
19139                 this.assignDocWin();
19140                 if(this.doc.body || this.doc.readyState == 'complete'){
19141                     try {
19142                         this.doc.designMode="on";
19143                     } catch (e) {
19144                         return;
19145                     }
19146                     Roo.TaskMgr.stop(task);
19147                     this.initEditor.defer(10, this);
19148                 }
19149             },
19150             interval : 10,
19151             duration: 10000,
19152             scope: this
19153         };
19154         Roo.TaskMgr.start(task);
19155
19156     },
19157
19158     // private
19159     onResize : function(w, h)
19160     {
19161          Roo.log('resize: ' +w + ',' + h );
19162         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19163         if(!this.iframe){
19164             return;
19165         }
19166         if(typeof w == 'number'){
19167             
19168             this.iframe.style.width = w + 'px';
19169         }
19170         if(typeof h == 'number'){
19171             
19172             this.iframe.style.height = h + 'px';
19173             if(this.doc){
19174                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19175             }
19176         }
19177         
19178     },
19179
19180     /**
19181      * Toggles the editor between standard and source edit mode.
19182      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19183      */
19184     toggleSourceEdit : function(sourceEditMode){
19185         
19186         this.sourceEditMode = sourceEditMode === true;
19187         
19188         if(this.sourceEditMode){
19189  
19190             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19191             
19192         }else{
19193             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19194             //this.iframe.className = '';
19195             this.deferFocus();
19196         }
19197         //this.setSize(this.owner.wrap.getSize());
19198         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19199     },
19200
19201     
19202   
19203
19204     /**
19205      * Protected method that will not generally be called directly. If you need/want
19206      * custom HTML cleanup, this is the method you should override.
19207      * @param {String} html The HTML to be cleaned
19208      * return {String} The cleaned HTML
19209      */
19210     cleanHtml : function(html){
19211         html = String(html);
19212         if(html.length > 5){
19213             if(Roo.isSafari){ // strip safari nonsense
19214                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19215             }
19216         }
19217         if(html == '&nbsp;'){
19218             html = '';
19219         }
19220         return html;
19221     },
19222
19223     /**
19224      * HTML Editor -> Textarea
19225      * Protected method that will not generally be called directly. Syncs the contents
19226      * of the editor iframe with the textarea.
19227      */
19228     syncValue : function(){
19229         if(this.initialized){
19230             var bd = (this.doc.body || this.doc.documentElement);
19231             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19232             var html = bd.innerHTML;
19233             if(Roo.isSafari){
19234                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19235                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19236                 if(m && m[1]){
19237                     html = '<div style="'+m[0]+'">' + html + '</div>';
19238                 }
19239             }
19240             html = this.cleanHtml(html);
19241             // fix up the special chars.. normaly like back quotes in word...
19242             // however we do not want to do this with chinese..
19243             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19244                 var cc = b.charCodeAt();
19245                 if (
19246                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19247                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19248                     (cc >= 0xf900 && cc < 0xfb00 )
19249                 ) {
19250                         return b;
19251                 }
19252                 return "&#"+cc+";" 
19253             });
19254             if(this.owner.fireEvent('beforesync', this, html) !== false){
19255                 this.el.dom.value = html;
19256                 this.owner.fireEvent('sync', this, html);
19257             }
19258         }
19259     },
19260
19261     /**
19262      * Protected method that will not generally be called directly. Pushes the value of the textarea
19263      * into the iframe editor.
19264      */
19265     pushValue : function(){
19266         if(this.initialized){
19267             var v = this.el.dom.value.trim();
19268             
19269 //            if(v.length < 1){
19270 //                v = '&#160;';
19271 //            }
19272             
19273             if(this.owner.fireEvent('beforepush', this, v) !== false){
19274                 var d = (this.doc.body || this.doc.documentElement);
19275                 d.innerHTML = v;
19276                 this.cleanUpPaste();
19277                 this.el.dom.value = d.innerHTML;
19278                 this.owner.fireEvent('push', this, v);
19279             }
19280         }
19281     },
19282
19283     // private
19284     deferFocus : function(){
19285         this.focus.defer(10, this);
19286     },
19287
19288     // doc'ed in Field
19289     focus : function(){
19290         if(this.win && !this.sourceEditMode){
19291             this.win.focus();
19292         }else{
19293             this.el.focus();
19294         }
19295     },
19296     
19297     assignDocWin: function()
19298     {
19299         var iframe = this.iframe;
19300         
19301          if(Roo.isIE){
19302             this.doc = iframe.contentWindow.document;
19303             this.win = iframe.contentWindow;
19304         } else {
19305 //            if (!Roo.get(this.frameId)) {
19306 //                return;
19307 //            }
19308 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19309 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19310             
19311             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19312                 return;
19313             }
19314             
19315             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19316             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19317         }
19318     },
19319     
19320     // private
19321     initEditor : function(){
19322         //console.log("INIT EDITOR");
19323         this.assignDocWin();
19324         
19325         
19326         
19327         this.doc.designMode="on";
19328         this.doc.open();
19329         this.doc.write(this.getDocMarkup());
19330         this.doc.close();
19331         
19332         var dbody = (this.doc.body || this.doc.documentElement);
19333         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19334         // this copies styles from the containing element into thsi one..
19335         // not sure why we need all of this..
19336         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19337         
19338         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19339         //ss['background-attachment'] = 'fixed'; // w3c
19340         dbody.bgProperties = 'fixed'; // ie
19341         //Roo.DomHelper.applyStyles(dbody, ss);
19342         Roo.EventManager.on(this.doc, {
19343             //'mousedown': this.onEditorEvent,
19344             'mouseup': this.onEditorEvent,
19345             'dblclick': this.onEditorEvent,
19346             'click': this.onEditorEvent,
19347             'keyup': this.onEditorEvent,
19348             buffer:100,
19349             scope: this
19350         });
19351         if(Roo.isGecko){
19352             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19353         }
19354         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19355             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19356         }
19357         this.initialized = true;
19358
19359         this.owner.fireEvent('initialize', this);
19360         this.pushValue();
19361     },
19362
19363     // private
19364     onDestroy : function(){
19365         
19366         
19367         
19368         if(this.rendered){
19369             
19370             //for (var i =0; i < this.toolbars.length;i++) {
19371             //    // fixme - ask toolbars for heights?
19372             //    this.toolbars[i].onDestroy();
19373            // }
19374             
19375             //this.wrap.dom.innerHTML = '';
19376             //this.wrap.remove();
19377         }
19378     },
19379
19380     // private
19381     onFirstFocus : function(){
19382         
19383         this.assignDocWin();
19384         
19385         
19386         this.activated = true;
19387          
19388     
19389         if(Roo.isGecko){ // prevent silly gecko errors
19390             this.win.focus();
19391             var s = this.win.getSelection();
19392             if(!s.focusNode || s.focusNode.nodeType != 3){
19393                 var r = s.getRangeAt(0);
19394                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19395                 r.collapse(true);
19396                 this.deferFocus();
19397             }
19398             try{
19399                 this.execCmd('useCSS', true);
19400                 this.execCmd('styleWithCSS', false);
19401             }catch(e){}
19402         }
19403         this.owner.fireEvent('activate', this);
19404     },
19405
19406     // private
19407     adjustFont: function(btn){
19408         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19409         //if(Roo.isSafari){ // safari
19410         //    adjust *= 2;
19411        // }
19412         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19413         if(Roo.isSafari){ // safari
19414             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19415             v =  (v < 10) ? 10 : v;
19416             v =  (v > 48) ? 48 : v;
19417             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19418             
19419         }
19420         
19421         
19422         v = Math.max(1, v+adjust);
19423         
19424         this.execCmd('FontSize', v  );
19425     },
19426
19427     onEditorEvent : function(e)
19428     {
19429         this.owner.fireEvent('editorevent', this, e);
19430       //  this.updateToolbar();
19431         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19432     },
19433
19434     insertTag : function(tg)
19435     {
19436         // could be a bit smarter... -> wrap the current selected tRoo..
19437         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19438             
19439             range = this.createRange(this.getSelection());
19440             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19441             wrappingNode.appendChild(range.extractContents());
19442             range.insertNode(wrappingNode);
19443
19444             return;
19445             
19446             
19447             
19448         }
19449         this.execCmd("formatblock",   tg);
19450         
19451     },
19452     
19453     insertText : function(txt)
19454     {
19455         
19456         
19457         var range = this.createRange();
19458         range.deleteContents();
19459                //alert(Sender.getAttribute('label'));
19460                
19461         range.insertNode(this.doc.createTextNode(txt));
19462     } ,
19463     
19464      
19465
19466     /**
19467      * Executes a Midas editor command on the editor document and performs necessary focus and
19468      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19469      * @param {String} cmd The Midas command
19470      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19471      */
19472     relayCmd : function(cmd, value){
19473         this.win.focus();
19474         this.execCmd(cmd, value);
19475         this.owner.fireEvent('editorevent', this);
19476         //this.updateToolbar();
19477         this.owner.deferFocus();
19478     },
19479
19480     /**
19481      * Executes a Midas editor command directly on the editor document.
19482      * For visual commands, you should use {@link #relayCmd} instead.
19483      * <b>This should only be called after the editor is initialized.</b>
19484      * @param {String} cmd The Midas command
19485      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19486      */
19487     execCmd : function(cmd, value){
19488         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19489         this.syncValue();
19490     },
19491  
19492  
19493    
19494     /**
19495      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19496      * to insert tRoo.
19497      * @param {String} text | dom node.. 
19498      */
19499     insertAtCursor : function(text)
19500     {
19501         
19502         
19503         
19504         if(!this.activated){
19505             return;
19506         }
19507         /*
19508         if(Roo.isIE){
19509             this.win.focus();
19510             var r = this.doc.selection.createRange();
19511             if(r){
19512                 r.collapse(true);
19513                 r.pasteHTML(text);
19514                 this.syncValue();
19515                 this.deferFocus();
19516             
19517             }
19518             return;
19519         }
19520         */
19521         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19522             this.win.focus();
19523             
19524             
19525             // from jquery ui (MIT licenced)
19526             var range, node;
19527             var win = this.win;
19528             
19529             if (win.getSelection && win.getSelection().getRangeAt) {
19530                 range = win.getSelection().getRangeAt(0);
19531                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19532                 range.insertNode(node);
19533             } else if (win.document.selection && win.document.selection.createRange) {
19534                 // no firefox support
19535                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19536                 win.document.selection.createRange().pasteHTML(txt);
19537             } else {
19538                 // no firefox support
19539                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19540                 this.execCmd('InsertHTML', txt);
19541             } 
19542             
19543             this.syncValue();
19544             
19545             this.deferFocus();
19546         }
19547     },
19548  // private
19549     mozKeyPress : function(e){
19550         if(e.ctrlKey){
19551             var c = e.getCharCode(), cmd;
19552           
19553             if(c > 0){
19554                 c = String.fromCharCode(c).toLowerCase();
19555                 switch(c){
19556                     case 'b':
19557                         cmd = 'bold';
19558                         break;
19559                     case 'i':
19560                         cmd = 'italic';
19561                         break;
19562                     
19563                     case 'u':
19564                         cmd = 'underline';
19565                         break;
19566                     
19567                     case 'v':
19568                         this.cleanUpPaste.defer(100, this);
19569                         return;
19570                         
19571                 }
19572                 if(cmd){
19573                     this.win.focus();
19574                     this.execCmd(cmd);
19575                     this.deferFocus();
19576                     e.preventDefault();
19577                 }
19578                 
19579             }
19580         }
19581     },
19582
19583     // private
19584     fixKeys : function(){ // load time branching for fastest keydown performance
19585         if(Roo.isIE){
19586             return function(e){
19587                 var k = e.getKey(), r;
19588                 if(k == e.TAB){
19589                     e.stopEvent();
19590                     r = this.doc.selection.createRange();
19591                     if(r){
19592                         r.collapse(true);
19593                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19594                         this.deferFocus();
19595                     }
19596                     return;
19597                 }
19598                 
19599                 if(k == e.ENTER){
19600                     r = this.doc.selection.createRange();
19601                     if(r){
19602                         var target = r.parentElement();
19603                         if(!target || target.tagName.toLowerCase() != 'li'){
19604                             e.stopEvent();
19605                             r.pasteHTML('<br />');
19606                             r.collapse(false);
19607                             r.select();
19608                         }
19609                     }
19610                 }
19611                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19612                     this.cleanUpPaste.defer(100, this);
19613                     return;
19614                 }
19615                 
19616                 
19617             };
19618         }else if(Roo.isOpera){
19619             return function(e){
19620                 var k = e.getKey();
19621                 if(k == e.TAB){
19622                     e.stopEvent();
19623                     this.win.focus();
19624                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19625                     this.deferFocus();
19626                 }
19627                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19628                     this.cleanUpPaste.defer(100, this);
19629                     return;
19630                 }
19631                 
19632             };
19633         }else if(Roo.isSafari){
19634             return function(e){
19635                 var k = e.getKey();
19636                 
19637                 if(k == e.TAB){
19638                     e.stopEvent();
19639                     this.execCmd('InsertText','\t');
19640                     this.deferFocus();
19641                     return;
19642                 }
19643                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19644                     this.cleanUpPaste.defer(100, this);
19645                     return;
19646                 }
19647                 
19648              };
19649         }
19650     }(),
19651     
19652     getAllAncestors: function()
19653     {
19654         var p = this.getSelectedNode();
19655         var a = [];
19656         if (!p) {
19657             a.push(p); // push blank onto stack..
19658             p = this.getParentElement();
19659         }
19660         
19661         
19662         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
19663             a.push(p);
19664             p = p.parentNode;
19665         }
19666         a.push(this.doc.body);
19667         return a;
19668     },
19669     lastSel : false,
19670     lastSelNode : false,
19671     
19672     
19673     getSelection : function() 
19674     {
19675         this.assignDocWin();
19676         return Roo.isIE ? this.doc.selection : this.win.getSelection();
19677     },
19678     
19679     getSelectedNode: function() 
19680     {
19681         // this may only work on Gecko!!!
19682         
19683         // should we cache this!!!!
19684         
19685         
19686         
19687          
19688         var range = this.createRange(this.getSelection()).cloneRange();
19689         
19690         if (Roo.isIE) {
19691             var parent = range.parentElement();
19692             while (true) {
19693                 var testRange = range.duplicate();
19694                 testRange.moveToElementText(parent);
19695                 if (testRange.inRange(range)) {
19696                     break;
19697                 }
19698                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
19699                     break;
19700                 }
19701                 parent = parent.parentElement;
19702             }
19703             return parent;
19704         }
19705         
19706         // is ancestor a text element.
19707         var ac =  range.commonAncestorContainer;
19708         if (ac.nodeType == 3) {
19709             ac = ac.parentNode;
19710         }
19711         
19712         var ar = ac.childNodes;
19713          
19714         var nodes = [];
19715         var other_nodes = [];
19716         var has_other_nodes = false;
19717         for (var i=0;i<ar.length;i++) {
19718             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
19719                 continue;
19720             }
19721             // fullly contained node.
19722             
19723             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
19724                 nodes.push(ar[i]);
19725                 continue;
19726             }
19727             
19728             // probably selected..
19729             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
19730                 other_nodes.push(ar[i]);
19731                 continue;
19732             }
19733             // outer..
19734             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
19735                 continue;
19736             }
19737             
19738             
19739             has_other_nodes = true;
19740         }
19741         if (!nodes.length && other_nodes.length) {
19742             nodes= other_nodes;
19743         }
19744         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
19745             return false;
19746         }
19747         
19748         return nodes[0];
19749     },
19750     createRange: function(sel)
19751     {
19752         // this has strange effects when using with 
19753         // top toolbar - not sure if it's a great idea.
19754         //this.editor.contentWindow.focus();
19755         if (typeof sel != "undefined") {
19756             try {
19757                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
19758             } catch(e) {
19759                 return this.doc.createRange();
19760             }
19761         } else {
19762             return this.doc.createRange();
19763         }
19764     },
19765     getParentElement: function()
19766     {
19767         
19768         this.assignDocWin();
19769         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
19770         
19771         var range = this.createRange(sel);
19772          
19773         try {
19774             var p = range.commonAncestorContainer;
19775             while (p.nodeType == 3) { // text node
19776                 p = p.parentNode;
19777             }
19778             return p;
19779         } catch (e) {
19780             return null;
19781         }
19782     
19783     },
19784     /***
19785      *
19786      * Range intersection.. the hard stuff...
19787      *  '-1' = before
19788      *  '0' = hits..
19789      *  '1' = after.
19790      *         [ -- selected range --- ]
19791      *   [fail]                        [fail]
19792      *
19793      *    basically..
19794      *      if end is before start or  hits it. fail.
19795      *      if start is after end or hits it fail.
19796      *
19797      *   if either hits (but other is outside. - then it's not 
19798      *   
19799      *    
19800      **/
19801     
19802     
19803     // @see http://www.thismuchiknow.co.uk/?p=64.
19804     rangeIntersectsNode : function(range, node)
19805     {
19806         var nodeRange = node.ownerDocument.createRange();
19807         try {
19808             nodeRange.selectNode(node);
19809         } catch (e) {
19810             nodeRange.selectNodeContents(node);
19811         }
19812     
19813         var rangeStartRange = range.cloneRange();
19814         rangeStartRange.collapse(true);
19815     
19816         var rangeEndRange = range.cloneRange();
19817         rangeEndRange.collapse(false);
19818     
19819         var nodeStartRange = nodeRange.cloneRange();
19820         nodeStartRange.collapse(true);
19821     
19822         var nodeEndRange = nodeRange.cloneRange();
19823         nodeEndRange.collapse(false);
19824     
19825         return rangeStartRange.compareBoundaryPoints(
19826                  Range.START_TO_START, nodeEndRange) == -1 &&
19827                rangeEndRange.compareBoundaryPoints(
19828                  Range.START_TO_START, nodeStartRange) == 1;
19829         
19830          
19831     },
19832     rangeCompareNode : function(range, node)
19833     {
19834         var nodeRange = node.ownerDocument.createRange();
19835         try {
19836             nodeRange.selectNode(node);
19837         } catch (e) {
19838             nodeRange.selectNodeContents(node);
19839         }
19840         
19841         
19842         range.collapse(true);
19843     
19844         nodeRange.collapse(true);
19845      
19846         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
19847         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
19848          
19849         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
19850         
19851         var nodeIsBefore   =  ss == 1;
19852         var nodeIsAfter    = ee == -1;
19853         
19854         if (nodeIsBefore && nodeIsAfter)
19855             return 0; // outer
19856         if (!nodeIsBefore && nodeIsAfter)
19857             return 1; //right trailed.
19858         
19859         if (nodeIsBefore && !nodeIsAfter)
19860             return 2;  // left trailed.
19861         // fully contined.
19862         return 3;
19863     },
19864
19865     // private? - in a new class?
19866     cleanUpPaste :  function()
19867     {
19868         // cleans up the whole document..
19869         Roo.log('cleanuppaste');
19870         
19871         this.cleanUpChildren(this.doc.body);
19872         var clean = this.cleanWordChars(this.doc.body.innerHTML);
19873         if (clean != this.doc.body.innerHTML) {
19874             this.doc.body.innerHTML = clean;
19875         }
19876         
19877     },
19878     
19879     cleanWordChars : function(input) {// change the chars to hex code
19880         var he = Roo.HtmlEditorCore;
19881         
19882         var output = input;
19883         Roo.each(he.swapCodes, function(sw) { 
19884             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
19885             
19886             output = output.replace(swapper, sw[1]);
19887         });
19888         
19889         return output;
19890     },
19891     
19892     
19893     cleanUpChildren : function (n)
19894     {
19895         if (!n.childNodes.length) {
19896             return;
19897         }
19898         for (var i = n.childNodes.length-1; i > -1 ; i--) {
19899            this.cleanUpChild(n.childNodes[i]);
19900         }
19901     },
19902     
19903     
19904         
19905     
19906     cleanUpChild : function (node)
19907     {
19908         var ed = this;
19909         //console.log(node);
19910         if (node.nodeName == "#text") {
19911             // clean up silly Windows -- stuff?
19912             return; 
19913         }
19914         if (node.nodeName == "#comment") {
19915             node.parentNode.removeChild(node);
19916             // clean up silly Windows -- stuff?
19917             return; 
19918         }
19919         var lcname = node.tagName.toLowerCase();
19920         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
19921         // whitelist of tags..
19922         
19923         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
19924             // remove node.
19925             node.parentNode.removeChild(node);
19926             return;
19927             
19928         }
19929         
19930         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
19931         
19932         // remove <a name=....> as rendering on yahoo mailer is borked with this.
19933         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
19934         
19935         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
19936         //    remove_keep_children = true;
19937         //}
19938         
19939         if (remove_keep_children) {
19940             this.cleanUpChildren(node);
19941             // inserts everything just before this node...
19942             while (node.childNodes.length) {
19943                 var cn = node.childNodes[0];
19944                 node.removeChild(cn);
19945                 node.parentNode.insertBefore(cn, node);
19946             }
19947             node.parentNode.removeChild(node);
19948             return;
19949         }
19950         
19951         if (!node.attributes || !node.attributes.length) {
19952             this.cleanUpChildren(node);
19953             return;
19954         }
19955         
19956         function cleanAttr(n,v)
19957         {
19958             
19959             if (v.match(/^\./) || v.match(/^\//)) {
19960                 return;
19961             }
19962             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
19963                 return;
19964             }
19965             if (v.match(/^#/)) {
19966                 return;
19967             }
19968 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
19969             node.removeAttribute(n);
19970             
19971         }
19972         
19973         var cwhite = this.cwhite;
19974         var cblack = this.cblack;
19975             
19976         function cleanStyle(n,v)
19977         {
19978             if (v.match(/expression/)) { //XSS?? should we even bother..
19979                 node.removeAttribute(n);
19980                 return;
19981             }
19982             
19983             var parts = v.split(/;/);
19984             var clean = [];
19985             
19986             Roo.each(parts, function(p) {
19987                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
19988                 if (!p.length) {
19989                     return true;
19990                 }
19991                 var l = p.split(':').shift().replace(/\s+/g,'');
19992                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
19993                 
19994                 if ( cwhite.length && cblack.indexOf(l) > -1) {
19995 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
19996                     //node.removeAttribute(n);
19997                     return true;
19998                 }
19999                 //Roo.log()
20000                 // only allow 'c whitelisted system attributes'
20001                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20002 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20003                     //node.removeAttribute(n);
20004                     return true;
20005                 }
20006                 
20007                 
20008                  
20009                 
20010                 clean.push(p);
20011                 return true;
20012             });
20013             if (clean.length) { 
20014                 node.setAttribute(n, clean.join(';'));
20015             } else {
20016                 node.removeAttribute(n);
20017             }
20018             
20019         }
20020         
20021         
20022         for (var i = node.attributes.length-1; i > -1 ; i--) {
20023             var a = node.attributes[i];
20024             //console.log(a);
20025             
20026             if (a.name.toLowerCase().substr(0,2)=='on')  {
20027                 node.removeAttribute(a.name);
20028                 continue;
20029             }
20030             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20031                 node.removeAttribute(a.name);
20032                 continue;
20033             }
20034             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20035                 cleanAttr(a.name,a.value); // fixme..
20036                 continue;
20037             }
20038             if (a.name == 'style') {
20039                 cleanStyle(a.name,a.value);
20040                 continue;
20041             }
20042             /// clean up MS crap..
20043             // tecnically this should be a list of valid class'es..
20044             
20045             
20046             if (a.name == 'class') {
20047                 if (a.value.match(/^Mso/)) {
20048                     node.className = '';
20049                 }
20050                 
20051                 if (a.value.match(/body/)) {
20052                     node.className = '';
20053                 }
20054                 continue;
20055             }
20056             
20057             // style cleanup!?
20058             // class cleanup?
20059             
20060         }
20061         
20062         
20063         this.cleanUpChildren(node);
20064         
20065         
20066     },
20067     
20068     /**
20069      * Clean up MS wordisms...
20070      */
20071     cleanWord : function(node)
20072     {
20073         
20074         
20075         if (!node) {
20076             this.cleanWord(this.doc.body);
20077             return;
20078         }
20079         if (node.nodeName == "#text") {
20080             // clean up silly Windows -- stuff?
20081             return; 
20082         }
20083         if (node.nodeName == "#comment") {
20084             node.parentNode.removeChild(node);
20085             // clean up silly Windows -- stuff?
20086             return; 
20087         }
20088         
20089         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20090             node.parentNode.removeChild(node);
20091             return;
20092         }
20093         
20094         // remove - but keep children..
20095         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20096             while (node.childNodes.length) {
20097                 var cn = node.childNodes[0];
20098                 node.removeChild(cn);
20099                 node.parentNode.insertBefore(cn, node);
20100             }
20101             node.parentNode.removeChild(node);
20102             this.iterateChildren(node, this.cleanWord);
20103             return;
20104         }
20105         // clean styles
20106         if (node.className.length) {
20107             
20108             var cn = node.className.split(/\W+/);
20109             var cna = [];
20110             Roo.each(cn, function(cls) {
20111                 if (cls.match(/Mso[a-zA-Z]+/)) {
20112                     return;
20113                 }
20114                 cna.push(cls);
20115             });
20116             node.className = cna.length ? cna.join(' ') : '';
20117             if (!cna.length) {
20118                 node.removeAttribute("class");
20119             }
20120         }
20121         
20122         if (node.hasAttribute("lang")) {
20123             node.removeAttribute("lang");
20124         }
20125         
20126         if (node.hasAttribute("style")) {
20127             
20128             var styles = node.getAttribute("style").split(";");
20129             var nstyle = [];
20130             Roo.each(styles, function(s) {
20131                 if (!s.match(/:/)) {
20132                     return;
20133                 }
20134                 var kv = s.split(":");
20135                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20136                     return;
20137                 }
20138                 // what ever is left... we allow.
20139                 nstyle.push(s);
20140             });
20141             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20142             if (!nstyle.length) {
20143                 node.removeAttribute('style');
20144             }
20145         }
20146         this.iterateChildren(node, this.cleanWord);
20147         
20148         
20149         
20150     },
20151     /**
20152      * iterateChildren of a Node, calling fn each time, using this as the scole..
20153      * @param {DomNode} node node to iterate children of.
20154      * @param {Function} fn method of this class to call on each item.
20155      */
20156     iterateChildren : function(node, fn)
20157     {
20158         if (!node.childNodes.length) {
20159                 return;
20160         }
20161         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20162            fn.call(this, node.childNodes[i])
20163         }
20164     },
20165     
20166     
20167     /**
20168      * cleanTableWidths.
20169      *
20170      * Quite often pasting from word etc.. results in tables with column and widths.
20171      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20172      *
20173      */
20174     cleanTableWidths : function(node)
20175     {
20176          
20177          
20178         if (!node) {
20179             this.cleanTableWidths(this.doc.body);
20180             return;
20181         }
20182         
20183         // ignore list...
20184         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20185             return; 
20186         }
20187         Roo.log(node.tagName);
20188         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20189             this.iterateChildren(node, this.cleanTableWidths);
20190             return;
20191         }
20192         if (node.hasAttribute('width')) {
20193             node.removeAttribute('width');
20194         }
20195         
20196          
20197         if (node.hasAttribute("style")) {
20198             // pretty basic...
20199             
20200             var styles = node.getAttribute("style").split(";");
20201             var nstyle = [];
20202             Roo.each(styles, function(s) {
20203                 if (!s.match(/:/)) {
20204                     return;
20205                 }
20206                 var kv = s.split(":");
20207                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20208                     return;
20209                 }
20210                 // what ever is left... we allow.
20211                 nstyle.push(s);
20212             });
20213             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20214             if (!nstyle.length) {
20215                 node.removeAttribute('style');
20216             }
20217         }
20218         
20219         this.iterateChildren(node, this.cleanTableWidths);
20220         
20221         
20222     },
20223     
20224     
20225     
20226     
20227     domToHTML : function(currentElement, depth, nopadtext) {
20228         
20229         depth = depth || 0;
20230         nopadtext = nopadtext || false;
20231     
20232         if (!currentElement) {
20233             return this.domToHTML(this.doc.body);
20234         }
20235         
20236         //Roo.log(currentElement);
20237         var j;
20238         var allText = false;
20239         var nodeName = currentElement.nodeName;
20240         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20241         
20242         if  (nodeName == '#text') {
20243             
20244             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20245         }
20246         
20247         
20248         var ret = '';
20249         if (nodeName != 'BODY') {
20250              
20251             var i = 0;
20252             // Prints the node tagName, such as <A>, <IMG>, etc
20253             if (tagName) {
20254                 var attr = [];
20255                 for(i = 0; i < currentElement.attributes.length;i++) {
20256                     // quoting?
20257                     var aname = currentElement.attributes.item(i).name;
20258                     if (!currentElement.attributes.item(i).value.length) {
20259                         continue;
20260                     }
20261                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20262                 }
20263                 
20264                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20265             } 
20266             else {
20267                 
20268                 // eack
20269             }
20270         } else {
20271             tagName = false;
20272         }
20273         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20274             return ret;
20275         }
20276         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20277             nopadtext = true;
20278         }
20279         
20280         
20281         // Traverse the tree
20282         i = 0;
20283         var currentElementChild = currentElement.childNodes.item(i);
20284         var allText = true;
20285         var innerHTML  = '';
20286         lastnode = '';
20287         while (currentElementChild) {
20288             // Formatting code (indent the tree so it looks nice on the screen)
20289             var nopad = nopadtext;
20290             if (lastnode == 'SPAN') {
20291                 nopad  = true;
20292             }
20293             // text
20294             if  (currentElementChild.nodeName == '#text') {
20295                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20296                 toadd = nopadtext ? toadd : toadd.trim();
20297                 if (!nopad && toadd.length > 80) {
20298                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20299                 }
20300                 innerHTML  += toadd;
20301                 
20302                 i++;
20303                 currentElementChild = currentElement.childNodes.item(i);
20304                 lastNode = '';
20305                 continue;
20306             }
20307             allText = false;
20308             
20309             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20310                 
20311             // Recursively traverse the tree structure of the child node
20312             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20313             lastnode = currentElementChild.nodeName;
20314             i++;
20315             currentElementChild=currentElement.childNodes.item(i);
20316         }
20317         
20318         ret += innerHTML;
20319         
20320         if (!allText) {
20321                 // The remaining code is mostly for formatting the tree
20322             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20323         }
20324         
20325         
20326         if (tagName) {
20327             ret+= "</"+tagName+">";
20328         }
20329         return ret;
20330         
20331     },
20332         
20333     applyBlacklists : function()
20334     {
20335         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20336         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20337         
20338         this.white = [];
20339         this.black = [];
20340         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20341             if (b.indexOf(tag) > -1) {
20342                 return;
20343             }
20344             this.white.push(tag);
20345             
20346         }, this);
20347         
20348         Roo.each(w, function(tag) {
20349             if (b.indexOf(tag) > -1) {
20350                 return;
20351             }
20352             if (this.white.indexOf(tag) > -1) {
20353                 return;
20354             }
20355             this.white.push(tag);
20356             
20357         }, this);
20358         
20359         
20360         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20361             if (w.indexOf(tag) > -1) {
20362                 return;
20363             }
20364             this.black.push(tag);
20365             
20366         }, this);
20367         
20368         Roo.each(b, function(tag) {
20369             if (w.indexOf(tag) > -1) {
20370                 return;
20371             }
20372             if (this.black.indexOf(tag) > -1) {
20373                 return;
20374             }
20375             this.black.push(tag);
20376             
20377         }, this);
20378         
20379         
20380         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20381         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20382         
20383         this.cwhite = [];
20384         this.cblack = [];
20385         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20386             if (b.indexOf(tag) > -1) {
20387                 return;
20388             }
20389             this.cwhite.push(tag);
20390             
20391         }, this);
20392         
20393         Roo.each(w, function(tag) {
20394             if (b.indexOf(tag) > -1) {
20395                 return;
20396             }
20397             if (this.cwhite.indexOf(tag) > -1) {
20398                 return;
20399             }
20400             this.cwhite.push(tag);
20401             
20402         }, this);
20403         
20404         
20405         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20406             if (w.indexOf(tag) > -1) {
20407                 return;
20408             }
20409             this.cblack.push(tag);
20410             
20411         }, this);
20412         
20413         Roo.each(b, function(tag) {
20414             if (w.indexOf(tag) > -1) {
20415                 return;
20416             }
20417             if (this.cblack.indexOf(tag) > -1) {
20418                 return;
20419             }
20420             this.cblack.push(tag);
20421             
20422         }, this);
20423     },
20424     
20425     setStylesheets : function(stylesheets)
20426     {
20427         if(typeof(stylesheets) == 'string'){
20428             Roo.get(this.iframe.contentDocument.head).createChild({
20429                 tag : 'link',
20430                 rel : 'stylesheet',
20431                 type : 'text/css',
20432                 href : stylesheets
20433             });
20434             
20435             return;
20436         }
20437         var _this = this;
20438      
20439         Roo.each(stylesheets, function(s) {
20440             if(!s.length){
20441                 return;
20442             }
20443             
20444             Roo.get(_this.iframe.contentDocument.head).createChild({
20445                 tag : 'link',
20446                 rel : 'stylesheet',
20447                 type : 'text/css',
20448                 href : s
20449             });
20450         });
20451
20452         
20453     },
20454     
20455     removeStylesheets : function()
20456     {
20457         var _this = this;
20458         
20459         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20460             s.remove();
20461         });
20462     }
20463     
20464     // hide stuff that is not compatible
20465     /**
20466      * @event blur
20467      * @hide
20468      */
20469     /**
20470      * @event change
20471      * @hide
20472      */
20473     /**
20474      * @event focus
20475      * @hide
20476      */
20477     /**
20478      * @event specialkey
20479      * @hide
20480      */
20481     /**
20482      * @cfg {String} fieldClass @hide
20483      */
20484     /**
20485      * @cfg {String} focusClass @hide
20486      */
20487     /**
20488      * @cfg {String} autoCreate @hide
20489      */
20490     /**
20491      * @cfg {String} inputType @hide
20492      */
20493     /**
20494      * @cfg {String} invalidClass @hide
20495      */
20496     /**
20497      * @cfg {String} invalidText @hide
20498      */
20499     /**
20500      * @cfg {String} msgFx @hide
20501      */
20502     /**
20503      * @cfg {String} validateOnBlur @hide
20504      */
20505 });
20506
20507 Roo.HtmlEditorCore.white = [
20508         'area', 'br', 'img', 'input', 'hr', 'wbr',
20509         
20510        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20511        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20512        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20513        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20514        'table',   'ul',         'xmp', 
20515        
20516        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20517       'thead',   'tr', 
20518      
20519       'dir', 'menu', 'ol', 'ul', 'dl',
20520        
20521       'embed',  'object'
20522 ];
20523
20524
20525 Roo.HtmlEditorCore.black = [
20526     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20527         'applet', // 
20528         'base',   'basefont', 'bgsound', 'blink',  'body', 
20529         'frame',  'frameset', 'head',    'html',   'ilayer', 
20530         'iframe', 'layer',  'link',     'meta',    'object',   
20531         'script', 'style' ,'title',  'xml' // clean later..
20532 ];
20533 Roo.HtmlEditorCore.clean = [
20534     'script', 'style', 'title', 'xml'
20535 ];
20536 Roo.HtmlEditorCore.remove = [
20537     'font'
20538 ];
20539 // attributes..
20540
20541 Roo.HtmlEditorCore.ablack = [
20542     'on'
20543 ];
20544     
20545 Roo.HtmlEditorCore.aclean = [ 
20546     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20547 ];
20548
20549 // protocols..
20550 Roo.HtmlEditorCore.pwhite= [
20551         'http',  'https',  'mailto'
20552 ];
20553
20554 // white listed style attributes.
20555 Roo.HtmlEditorCore.cwhite= [
20556       //  'text-align', /// default is to allow most things..
20557       
20558          
20559 //        'font-size'//??
20560 ];
20561
20562 // black listed style attributes.
20563 Roo.HtmlEditorCore.cblack= [
20564       //  'font-size' -- this can be set by the project 
20565 ];
20566
20567
20568 Roo.HtmlEditorCore.swapCodes   =[ 
20569     [    8211, "--" ], 
20570     [    8212, "--" ], 
20571     [    8216,  "'" ],  
20572     [    8217, "'" ],  
20573     [    8220, '"' ],  
20574     [    8221, '"' ],  
20575     [    8226, "*" ],  
20576     [    8230, "..." ]
20577 ]; 
20578
20579     /*
20580  * - LGPL
20581  *
20582  * HtmlEditor
20583  * 
20584  */
20585
20586 /**
20587  * @class Roo.bootstrap.HtmlEditor
20588  * @extends Roo.bootstrap.TextArea
20589  * Bootstrap HtmlEditor class
20590
20591  * @constructor
20592  * Create a new HtmlEditor
20593  * @param {Object} config The config object
20594  */
20595
20596 Roo.bootstrap.HtmlEditor = function(config){
20597     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20598     if (!this.toolbars) {
20599         this.toolbars = [];
20600     }
20601     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20602     this.addEvents({
20603             /**
20604              * @event initialize
20605              * Fires when the editor is fully initialized (including the iframe)
20606              * @param {HtmlEditor} this
20607              */
20608             initialize: true,
20609             /**
20610              * @event activate
20611              * Fires when the editor is first receives the focus. Any insertion must wait
20612              * until after this event.
20613              * @param {HtmlEditor} this
20614              */
20615             activate: true,
20616              /**
20617              * @event beforesync
20618              * Fires before the textarea is updated with content from the editor iframe. Return false
20619              * to cancel the sync.
20620              * @param {HtmlEditor} this
20621              * @param {String} html
20622              */
20623             beforesync: true,
20624              /**
20625              * @event beforepush
20626              * Fires before the iframe editor is updated with content from the textarea. Return false
20627              * to cancel the push.
20628              * @param {HtmlEditor} this
20629              * @param {String} html
20630              */
20631             beforepush: true,
20632              /**
20633              * @event sync
20634              * Fires when the textarea is updated with content from the editor iframe.
20635              * @param {HtmlEditor} this
20636              * @param {String} html
20637              */
20638             sync: true,
20639              /**
20640              * @event push
20641              * Fires when the iframe editor is updated with content from the textarea.
20642              * @param {HtmlEditor} this
20643              * @param {String} html
20644              */
20645             push: true,
20646              /**
20647              * @event editmodechange
20648              * Fires when the editor switches edit modes
20649              * @param {HtmlEditor} this
20650              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
20651              */
20652             editmodechange: true,
20653             /**
20654              * @event editorevent
20655              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20656              * @param {HtmlEditor} this
20657              */
20658             editorevent: true,
20659             /**
20660              * @event firstfocus
20661              * Fires when on first focus - needed by toolbars..
20662              * @param {HtmlEditor} this
20663              */
20664             firstfocus: true,
20665             /**
20666              * @event autosave
20667              * Auto save the htmlEditor value as a file into Events
20668              * @param {HtmlEditor} this
20669              */
20670             autosave: true,
20671             /**
20672              * @event savedpreview
20673              * preview the saved version of htmlEditor
20674              * @param {HtmlEditor} this
20675              */
20676             savedpreview: true
20677         });
20678 };
20679
20680
20681 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
20682     
20683     
20684       /**
20685      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
20686      */
20687     toolbars : false,
20688    
20689      /**
20690      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20691      *                        Roo.resizable.
20692      */
20693     resizable : false,
20694      /**
20695      * @cfg {Number} height (in pixels)
20696      */   
20697     height: 300,
20698    /**
20699      * @cfg {Number} width (in pixels)
20700      */   
20701     width: false,
20702     
20703     /**
20704      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20705      * 
20706      */
20707     stylesheets: false,
20708     
20709     // id of frame..
20710     frameId: false,
20711     
20712     // private properties
20713     validationEvent : false,
20714     deferHeight: true,
20715     initialized : false,
20716     activated : false,
20717     
20718     onFocus : Roo.emptyFn,
20719     iframePad:3,
20720     hideMode:'offsets',
20721     
20722     
20723     tbContainer : false,
20724     
20725     toolbarContainer :function() {
20726         return this.wrap.select('.x-html-editor-tb',true).first();
20727     },
20728
20729     /**
20730      * Protected method that will not generally be called directly. It
20731      * is called when the editor creates its toolbar. Override this method if you need to
20732      * add custom toolbar buttons.
20733      * @param {HtmlEditor} editor
20734      */
20735     createToolbar : function(){
20736         
20737         Roo.log("create toolbars");
20738         
20739         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
20740         this.toolbars[0].render(this.toolbarContainer());
20741         
20742         return;
20743         
20744 //        if (!editor.toolbars || !editor.toolbars.length) {
20745 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
20746 //        }
20747 //        
20748 //        for (var i =0 ; i < editor.toolbars.length;i++) {
20749 //            editor.toolbars[i] = Roo.factory(
20750 //                    typeof(editor.toolbars[i]) == 'string' ?
20751 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
20752 //                Roo.bootstrap.HtmlEditor);
20753 //            editor.toolbars[i].init(editor);
20754 //        }
20755     },
20756
20757      
20758     // private
20759     onRender : function(ct, position)
20760     {
20761        // Roo.log("Call onRender: " + this.xtype);
20762         var _t = this;
20763         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
20764       
20765         this.wrap = this.inputEl().wrap({
20766             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
20767         });
20768         
20769         this.editorcore.onRender(ct, position);
20770          
20771         if (this.resizable) {
20772             this.resizeEl = new Roo.Resizable(this.wrap, {
20773                 pinned : true,
20774                 wrap: true,
20775                 dynamic : true,
20776                 minHeight : this.height,
20777                 height: this.height,
20778                 handles : this.resizable,
20779                 width: this.width,
20780                 listeners : {
20781                     resize : function(r, w, h) {
20782                         _t.onResize(w,h); // -something
20783                     }
20784                 }
20785             });
20786             
20787         }
20788         this.createToolbar(this);
20789        
20790         
20791         if(!this.width && this.resizable){
20792             this.setSize(this.wrap.getSize());
20793         }
20794         if (this.resizeEl) {
20795             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
20796             // should trigger onReize..
20797         }
20798         
20799     },
20800
20801     // private
20802     onResize : function(w, h)
20803     {
20804         Roo.log('resize: ' +w + ',' + h );
20805         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
20806         var ew = false;
20807         var eh = false;
20808         
20809         if(this.inputEl() ){
20810             if(typeof w == 'number'){
20811                 var aw = w - this.wrap.getFrameWidth('lr');
20812                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
20813                 ew = aw;
20814             }
20815             if(typeof h == 'number'){
20816                  var tbh = -11;  // fixme it needs to tool bar size!
20817                 for (var i =0; i < this.toolbars.length;i++) {
20818                     // fixme - ask toolbars for heights?
20819                     tbh += this.toolbars[i].el.getHeight();
20820                     //if (this.toolbars[i].footer) {
20821                     //    tbh += this.toolbars[i].footer.el.getHeight();
20822                     //}
20823                 }
20824               
20825                 
20826                 
20827                 
20828                 
20829                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
20830                 ah -= 5; // knock a few pixes off for look..
20831                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
20832                 var eh = ah;
20833             }
20834         }
20835         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
20836         this.editorcore.onResize(ew,eh);
20837         
20838     },
20839
20840     /**
20841      * Toggles the editor between standard and source edit mode.
20842      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20843      */
20844     toggleSourceEdit : function(sourceEditMode)
20845     {
20846         this.editorcore.toggleSourceEdit(sourceEditMode);
20847         
20848         if(this.editorcore.sourceEditMode){
20849             Roo.log('editor - showing textarea');
20850             
20851 //            Roo.log('in');
20852 //            Roo.log(this.syncValue());
20853             this.syncValue();
20854             this.inputEl().removeClass(['hide', 'x-hidden']);
20855             this.inputEl().dom.removeAttribute('tabIndex');
20856             this.inputEl().focus();
20857         }else{
20858             Roo.log('editor - hiding textarea');
20859 //            Roo.log('out')
20860 //            Roo.log(this.pushValue()); 
20861             this.pushValue();
20862             
20863             this.inputEl().addClass(['hide', 'x-hidden']);
20864             this.inputEl().dom.setAttribute('tabIndex', -1);
20865             //this.deferFocus();
20866         }
20867          
20868         if(this.resizable){
20869             this.setSize(this.wrap.getSize());
20870         }
20871         
20872         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
20873     },
20874  
20875     // private (for BoxComponent)
20876     adjustSize : Roo.BoxComponent.prototype.adjustSize,
20877
20878     // private (for BoxComponent)
20879     getResizeEl : function(){
20880         return this.wrap;
20881     },
20882
20883     // private (for BoxComponent)
20884     getPositionEl : function(){
20885         return this.wrap;
20886     },
20887
20888     // private
20889     initEvents : function(){
20890         this.originalValue = this.getValue();
20891     },
20892
20893 //    /**
20894 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20895 //     * @method
20896 //     */
20897 //    markInvalid : Roo.emptyFn,
20898 //    /**
20899 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
20900 //     * @method
20901 //     */
20902 //    clearInvalid : Roo.emptyFn,
20903
20904     setValue : function(v){
20905         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
20906         this.editorcore.pushValue();
20907     },
20908
20909      
20910     // private
20911     deferFocus : function(){
20912         this.focus.defer(10, this);
20913     },
20914
20915     // doc'ed in Field
20916     focus : function(){
20917         this.editorcore.focus();
20918         
20919     },
20920       
20921
20922     // private
20923     onDestroy : function(){
20924         
20925         
20926         
20927         if(this.rendered){
20928             
20929             for (var i =0; i < this.toolbars.length;i++) {
20930                 // fixme - ask toolbars for heights?
20931                 this.toolbars[i].onDestroy();
20932             }
20933             
20934             this.wrap.dom.innerHTML = '';
20935             this.wrap.remove();
20936         }
20937     },
20938
20939     // private
20940     onFirstFocus : function(){
20941         //Roo.log("onFirstFocus");
20942         this.editorcore.onFirstFocus();
20943          for (var i =0; i < this.toolbars.length;i++) {
20944             this.toolbars[i].onFirstFocus();
20945         }
20946         
20947     },
20948     
20949     // private
20950     syncValue : function()
20951     {   
20952         this.editorcore.syncValue();
20953     },
20954     
20955     pushValue : function()
20956     {   
20957         this.editorcore.pushValue();
20958     }
20959      
20960     
20961     // hide stuff that is not compatible
20962     /**
20963      * @event blur
20964      * @hide
20965      */
20966     /**
20967      * @event change
20968      * @hide
20969      */
20970     /**
20971      * @event focus
20972      * @hide
20973      */
20974     /**
20975      * @event specialkey
20976      * @hide
20977      */
20978     /**
20979      * @cfg {String} fieldClass @hide
20980      */
20981     /**
20982      * @cfg {String} focusClass @hide
20983      */
20984     /**
20985      * @cfg {String} autoCreate @hide
20986      */
20987     /**
20988      * @cfg {String} inputType @hide
20989      */
20990     /**
20991      * @cfg {String} invalidClass @hide
20992      */
20993     /**
20994      * @cfg {String} invalidText @hide
20995      */
20996     /**
20997      * @cfg {String} msgFx @hide
20998      */
20999     /**
21000      * @cfg {String} validateOnBlur @hide
21001      */
21002 });
21003  
21004     
21005    
21006    
21007    
21008       
21009 Roo.namespace('Roo.bootstrap.htmleditor');
21010 /**
21011  * @class Roo.bootstrap.HtmlEditorToolbar1
21012  * Basic Toolbar
21013  * 
21014  * Usage:
21015  *
21016  new Roo.bootstrap.HtmlEditor({
21017     ....
21018     toolbars : [
21019         new Roo.bootstrap.HtmlEditorToolbar1({
21020             disable : { fonts: 1 , format: 1, ..., ... , ...],
21021             btns : [ .... ]
21022         })
21023     }
21024      
21025  * 
21026  * @cfg {Object} disable List of elements to disable..
21027  * @cfg {Array} btns List of additional buttons.
21028  * 
21029  * 
21030  * NEEDS Extra CSS? 
21031  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21032  */
21033  
21034 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21035 {
21036     
21037     Roo.apply(this, config);
21038     
21039     // default disabled, based on 'good practice'..
21040     this.disable = this.disable || {};
21041     Roo.applyIf(this.disable, {
21042         fontSize : true,
21043         colors : true,
21044         specialElements : true
21045     });
21046     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21047     
21048     this.editor = config.editor;
21049     this.editorcore = config.editor.editorcore;
21050     
21051     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21052     
21053     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21054     // dont call parent... till later.
21055 }
21056 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21057      
21058     bar : true,
21059     
21060     editor : false,
21061     editorcore : false,
21062     
21063     
21064     formats : [
21065         "p" ,  
21066         "h1","h2","h3","h4","h5","h6", 
21067         "pre", "code", 
21068         "abbr", "acronym", "address", "cite", "samp", "var",
21069         'div','span'
21070     ],
21071     
21072     onRender : function(ct, position)
21073     {
21074        // Roo.log("Call onRender: " + this.xtype);
21075         
21076        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21077        Roo.log(this.el);
21078        this.el.dom.style.marginBottom = '0';
21079        var _this = this;
21080        var editorcore = this.editorcore;
21081        var editor= this.editor;
21082        
21083        var children = [];
21084        var btn = function(id,cmd , toggle, handler){
21085        
21086             var  event = toggle ? 'toggle' : 'click';
21087        
21088             var a = {
21089                 size : 'sm',
21090                 xtype: 'Button',
21091                 xns: Roo.bootstrap,
21092                 glyphicon : id,
21093                 cmd : id || cmd,
21094                 enableToggle:toggle !== false,
21095                 //html : 'submit'
21096                 pressed : toggle ? false : null,
21097                 listeners : {}
21098             };
21099             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21100                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21101             };
21102             children.push(a);
21103             return a;
21104        }
21105         
21106         var style = {
21107                 xtype: 'Button',
21108                 size : 'sm',
21109                 xns: Roo.bootstrap,
21110                 glyphicon : 'font',
21111                 //html : 'submit'
21112                 menu : {
21113                     xtype: 'Menu',
21114                     xns: Roo.bootstrap,
21115                     items:  []
21116                 }
21117         };
21118         Roo.each(this.formats, function(f) {
21119             style.menu.items.push({
21120                 xtype :'MenuItem',
21121                 xns: Roo.bootstrap,
21122                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21123                 tagname : f,
21124                 listeners : {
21125                     click : function()
21126                     {
21127                         editorcore.insertTag(this.tagname);
21128                         editor.focus();
21129                     }
21130                 }
21131                 
21132             });
21133         });
21134          children.push(style);   
21135             
21136             
21137         btn('bold',false,true);
21138         btn('italic',false,true);
21139         btn('align-left', 'justifyleft',true);
21140         btn('align-center', 'justifycenter',true);
21141         btn('align-right' , 'justifyright',true);
21142         btn('link', false, false, function(btn) {
21143             //Roo.log("create link?");
21144             var url = prompt(this.createLinkText, this.defaultLinkValue);
21145             if(url && url != 'http:/'+'/'){
21146                 this.editorcore.relayCmd('createlink', url);
21147             }
21148         }),
21149         btn('list','insertunorderedlist',true);
21150         btn('pencil', false,true, function(btn){
21151                 Roo.log(this);
21152                 
21153                 this.toggleSourceEdit(btn.pressed);
21154         });
21155         /*
21156         var cog = {
21157                 xtype: 'Button',
21158                 size : 'sm',
21159                 xns: Roo.bootstrap,
21160                 glyphicon : 'cog',
21161                 //html : 'submit'
21162                 menu : {
21163                     xtype: 'Menu',
21164                     xns: Roo.bootstrap,
21165                     items:  []
21166                 }
21167         };
21168         
21169         cog.menu.items.push({
21170             xtype :'MenuItem',
21171             xns: Roo.bootstrap,
21172             html : Clean styles,
21173             tagname : f,
21174             listeners : {
21175                 click : function()
21176                 {
21177                     editorcore.insertTag(this.tagname);
21178                     editor.focus();
21179                 }
21180             }
21181             
21182         });
21183        */
21184         
21185          
21186        this.xtype = 'NavSimplebar';
21187         
21188         for(var i=0;i< children.length;i++) {
21189             
21190             this.buttons.add(this.addxtypeChild(children[i]));
21191             
21192         }
21193         
21194         editor.on('editorevent', this.updateToolbar, this);
21195     },
21196     onBtnClick : function(id)
21197     {
21198        this.editorcore.relayCmd(id);
21199        this.editorcore.focus();
21200     },
21201     
21202     /**
21203      * Protected method that will not generally be called directly. It triggers
21204      * a toolbar update by reading the markup state of the current selection in the editor.
21205      */
21206     updateToolbar: function(){
21207
21208         if(!this.editorcore.activated){
21209             this.editor.onFirstFocus(); // is this neeed?
21210             return;
21211         }
21212
21213         var btns = this.buttons; 
21214         var doc = this.editorcore.doc;
21215         btns.get('bold').setActive(doc.queryCommandState('bold'));
21216         btns.get('italic').setActive(doc.queryCommandState('italic'));
21217         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21218         
21219         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21220         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21221         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21222         
21223         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21224         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21225          /*
21226         
21227         var ans = this.editorcore.getAllAncestors();
21228         if (this.formatCombo) {
21229             
21230             
21231             var store = this.formatCombo.store;
21232             this.formatCombo.setValue("");
21233             for (var i =0; i < ans.length;i++) {
21234                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21235                     // select it..
21236                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21237                     break;
21238                 }
21239             }
21240         }
21241         
21242         
21243         
21244         // hides menus... - so this cant be on a menu...
21245         Roo.bootstrap.MenuMgr.hideAll();
21246         */
21247         Roo.bootstrap.MenuMgr.hideAll();
21248         //this.editorsyncValue();
21249     },
21250     onFirstFocus: function() {
21251         this.buttons.each(function(item){
21252            item.enable();
21253         });
21254     },
21255     toggleSourceEdit : function(sourceEditMode){
21256         
21257           
21258         if(sourceEditMode){
21259             Roo.log("disabling buttons");
21260            this.buttons.each( function(item){
21261                 if(item.cmd != 'pencil'){
21262                     item.disable();
21263                 }
21264             });
21265           
21266         }else{
21267             Roo.log("enabling buttons");
21268             if(this.editorcore.initialized){
21269                 this.buttons.each( function(item){
21270                     item.enable();
21271                 });
21272             }
21273             
21274         }
21275         Roo.log("calling toggole on editor");
21276         // tell the editor that it's been pressed..
21277         this.editor.toggleSourceEdit(sourceEditMode);
21278        
21279     }
21280 });
21281
21282
21283
21284
21285
21286 /**
21287  * @class Roo.bootstrap.Table.AbstractSelectionModel
21288  * @extends Roo.util.Observable
21289  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21290  * implemented by descendant classes.  This class should not be directly instantiated.
21291  * @constructor
21292  */
21293 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21294     this.locked = false;
21295     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21296 };
21297
21298
21299 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21300     /** @ignore Called by the grid automatically. Do not call directly. */
21301     init : function(grid){
21302         this.grid = grid;
21303         this.initEvents();
21304     },
21305
21306     /**
21307      * Locks the selections.
21308      */
21309     lock : function(){
21310         this.locked = true;
21311     },
21312
21313     /**
21314      * Unlocks the selections.
21315      */
21316     unlock : function(){
21317         this.locked = false;
21318     },
21319
21320     /**
21321      * Returns true if the selections are locked.
21322      * @return {Boolean}
21323      */
21324     isLocked : function(){
21325         return this.locked;
21326     }
21327 });
21328 /**
21329  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21330  * @class Roo.bootstrap.Table.RowSelectionModel
21331  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21332  * It supports multiple selections and keyboard selection/navigation. 
21333  * @constructor
21334  * @param {Object} config
21335  */
21336
21337 Roo.bootstrap.Table.RowSelectionModel = function(config){
21338     Roo.apply(this, config);
21339     this.selections = new Roo.util.MixedCollection(false, function(o){
21340         return o.id;
21341     });
21342
21343     this.last = false;
21344     this.lastActive = false;
21345
21346     this.addEvents({
21347         /**
21348              * @event selectionchange
21349              * Fires when the selection changes
21350              * @param {SelectionModel} this
21351              */
21352             "selectionchange" : true,
21353         /**
21354              * @event afterselectionchange
21355              * Fires after the selection changes (eg. by key press or clicking)
21356              * @param {SelectionModel} this
21357              */
21358             "afterselectionchange" : true,
21359         /**
21360              * @event beforerowselect
21361              * Fires when a row is selected being selected, return false to cancel.
21362              * @param {SelectionModel} this
21363              * @param {Number} rowIndex The selected index
21364              * @param {Boolean} keepExisting False if other selections will be cleared
21365              */
21366             "beforerowselect" : true,
21367         /**
21368              * @event rowselect
21369              * Fires when a row is selected.
21370              * @param {SelectionModel} this
21371              * @param {Number} rowIndex The selected index
21372              * @param {Roo.data.Record} r The record
21373              */
21374             "rowselect" : true,
21375         /**
21376              * @event rowdeselect
21377              * Fires when a row is deselected.
21378              * @param {SelectionModel} this
21379              * @param {Number} rowIndex The selected index
21380              */
21381         "rowdeselect" : true
21382     });
21383     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21384     this.locked = false;
21385 };
21386
21387 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21388     /**
21389      * @cfg {Boolean} singleSelect
21390      * True to allow selection of only one row at a time (defaults to false)
21391      */
21392     singleSelect : false,
21393
21394     // private
21395     initEvents : function(){
21396
21397         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21398             this.grid.on("mousedown", this.handleMouseDown, this);
21399         }else{ // allow click to work like normal
21400             this.grid.on("rowclick", this.handleDragableRowClick, this);
21401         }
21402
21403         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21404             "up" : function(e){
21405                 if(!e.shiftKey){
21406                     this.selectPrevious(e.shiftKey);
21407                 }else if(this.last !== false && this.lastActive !== false){
21408                     var last = this.last;
21409                     this.selectRange(this.last,  this.lastActive-1);
21410                     this.grid.getView().focusRow(this.lastActive);
21411                     if(last !== false){
21412                         this.last = last;
21413                     }
21414                 }else{
21415                     this.selectFirstRow();
21416                 }
21417                 this.fireEvent("afterselectionchange", this);
21418             },
21419             "down" : function(e){
21420                 if(!e.shiftKey){
21421                     this.selectNext(e.shiftKey);
21422                 }else if(this.last !== false && this.lastActive !== false){
21423                     var last = this.last;
21424                     this.selectRange(this.last,  this.lastActive+1);
21425                     this.grid.getView().focusRow(this.lastActive);
21426                     if(last !== false){
21427                         this.last = last;
21428                     }
21429                 }else{
21430                     this.selectFirstRow();
21431                 }
21432                 this.fireEvent("afterselectionchange", this);
21433             },
21434             scope: this
21435         });
21436
21437         var view = this.grid.view;
21438         view.on("refresh", this.onRefresh, this);
21439         view.on("rowupdated", this.onRowUpdated, this);
21440         view.on("rowremoved", this.onRemove, this);
21441     },
21442
21443     // private
21444     onRefresh : function(){
21445         var ds = this.grid.dataSource, i, v = this.grid.view;
21446         var s = this.selections;
21447         s.each(function(r){
21448             if((i = ds.indexOfId(r.id)) != -1){
21449                 v.onRowSelect(i);
21450             }else{
21451                 s.remove(r);
21452             }
21453         });
21454     },
21455
21456     // private
21457     onRemove : function(v, index, r){
21458         this.selections.remove(r);
21459     },
21460
21461     // private
21462     onRowUpdated : function(v, index, r){
21463         if(this.isSelected(r)){
21464             v.onRowSelect(index);
21465         }
21466     },
21467
21468     /**
21469      * Select records.
21470      * @param {Array} records The records to select
21471      * @param {Boolean} keepExisting (optional) True to keep existing selections
21472      */
21473     selectRecords : function(records, keepExisting){
21474         if(!keepExisting){
21475             this.clearSelections();
21476         }
21477         var ds = this.grid.dataSource;
21478         for(var i = 0, len = records.length; i < len; i++){
21479             this.selectRow(ds.indexOf(records[i]), true);
21480         }
21481     },
21482
21483     /**
21484      * Gets the number of selected rows.
21485      * @return {Number}
21486      */
21487     getCount : function(){
21488         return this.selections.length;
21489     },
21490
21491     /**
21492      * Selects the first row in the grid.
21493      */
21494     selectFirstRow : function(){
21495         this.selectRow(0);
21496     },
21497
21498     /**
21499      * Select the last row.
21500      * @param {Boolean} keepExisting (optional) True to keep existing selections
21501      */
21502     selectLastRow : function(keepExisting){
21503         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21504     },
21505
21506     /**
21507      * Selects the row immediately following the last selected row.
21508      * @param {Boolean} keepExisting (optional) True to keep existing selections
21509      */
21510     selectNext : function(keepExisting){
21511         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21512             this.selectRow(this.last+1, keepExisting);
21513             this.grid.getView().focusRow(this.last);
21514         }
21515     },
21516
21517     /**
21518      * Selects the row that precedes the last selected row.
21519      * @param {Boolean} keepExisting (optional) True to keep existing selections
21520      */
21521     selectPrevious : function(keepExisting){
21522         if(this.last){
21523             this.selectRow(this.last-1, keepExisting);
21524             this.grid.getView().focusRow(this.last);
21525         }
21526     },
21527
21528     /**
21529      * Returns the selected records
21530      * @return {Array} Array of selected records
21531      */
21532     getSelections : function(){
21533         return [].concat(this.selections.items);
21534     },
21535
21536     /**
21537      * Returns the first selected record.
21538      * @return {Record}
21539      */
21540     getSelected : function(){
21541         return this.selections.itemAt(0);
21542     },
21543
21544
21545     /**
21546      * Clears all selections.
21547      */
21548     clearSelections : function(fast){
21549         if(this.locked) return;
21550         if(fast !== true){
21551             var ds = this.grid.dataSource;
21552             var s = this.selections;
21553             s.each(function(r){
21554                 this.deselectRow(ds.indexOfId(r.id));
21555             }, this);
21556             s.clear();
21557         }else{
21558             this.selections.clear();
21559         }
21560         this.last = false;
21561     },
21562
21563
21564     /**
21565      * Selects all rows.
21566      */
21567     selectAll : function(){
21568         if(this.locked) return;
21569         this.selections.clear();
21570         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21571             this.selectRow(i, true);
21572         }
21573     },
21574
21575     /**
21576      * Returns True if there is a selection.
21577      * @return {Boolean}
21578      */
21579     hasSelection : function(){
21580         return this.selections.length > 0;
21581     },
21582
21583     /**
21584      * Returns True if the specified row is selected.
21585      * @param {Number/Record} record The record or index of the record to check
21586      * @return {Boolean}
21587      */
21588     isSelected : function(index){
21589         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21590         return (r && this.selections.key(r.id) ? true : false);
21591     },
21592
21593     /**
21594      * Returns True if the specified record id is selected.
21595      * @param {String} id The id of record to check
21596      * @return {Boolean}
21597      */
21598     isIdSelected : function(id){
21599         return (this.selections.key(id) ? true : false);
21600     },
21601
21602     // private
21603     handleMouseDown : function(e, t){
21604         var view = this.grid.getView(), rowIndex;
21605         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21606             return;
21607         };
21608         if(e.shiftKey && this.last !== false){
21609             var last = this.last;
21610             this.selectRange(last, rowIndex, e.ctrlKey);
21611             this.last = last; // reset the last
21612             view.focusRow(rowIndex);
21613         }else{
21614             var isSelected = this.isSelected(rowIndex);
21615             if(e.button !== 0 && isSelected){
21616                 view.focusRow(rowIndex);
21617             }else if(e.ctrlKey && isSelected){
21618                 this.deselectRow(rowIndex);
21619             }else if(!isSelected){
21620                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21621                 view.focusRow(rowIndex);
21622             }
21623         }
21624         this.fireEvent("afterselectionchange", this);
21625     },
21626     // private
21627     handleDragableRowClick :  function(grid, rowIndex, e) 
21628     {
21629         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21630             this.selectRow(rowIndex, false);
21631             grid.view.focusRow(rowIndex);
21632              this.fireEvent("afterselectionchange", this);
21633         }
21634     },
21635     
21636     /**
21637      * Selects multiple rows.
21638      * @param {Array} rows Array of the indexes of the row to select
21639      * @param {Boolean} keepExisting (optional) True to keep existing selections
21640      */
21641     selectRows : function(rows, keepExisting){
21642         if(!keepExisting){
21643             this.clearSelections();
21644         }
21645         for(var i = 0, len = rows.length; i < len; i++){
21646             this.selectRow(rows[i], true);
21647         }
21648     },
21649
21650     /**
21651      * Selects a range of rows. All rows in between startRow and endRow are also selected.
21652      * @param {Number} startRow The index of the first row in the range
21653      * @param {Number} endRow The index of the last row in the range
21654      * @param {Boolean} keepExisting (optional) True to retain existing selections
21655      */
21656     selectRange : function(startRow, endRow, keepExisting){
21657         if(this.locked) return;
21658         if(!keepExisting){
21659             this.clearSelections();
21660         }
21661         if(startRow <= endRow){
21662             for(var i = startRow; i <= endRow; i++){
21663                 this.selectRow(i, true);
21664             }
21665         }else{
21666             for(var i = startRow; i >= endRow; i--){
21667                 this.selectRow(i, true);
21668             }
21669         }
21670     },
21671
21672     /**
21673      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
21674      * @param {Number} startRow The index of the first row in the range
21675      * @param {Number} endRow The index of the last row in the range
21676      */
21677     deselectRange : function(startRow, endRow, preventViewNotify){
21678         if(this.locked) return;
21679         for(var i = startRow; i <= endRow; i++){
21680             this.deselectRow(i, preventViewNotify);
21681         }
21682     },
21683
21684     /**
21685      * Selects a row.
21686      * @param {Number} row The index of the row to select
21687      * @param {Boolean} keepExisting (optional) True to keep existing selections
21688      */
21689     selectRow : function(index, keepExisting, preventViewNotify){
21690         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
21691         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
21692             if(!keepExisting || this.singleSelect){
21693                 this.clearSelections();
21694             }
21695             var r = this.grid.dataSource.getAt(index);
21696             this.selections.add(r);
21697             this.last = this.lastActive = index;
21698             if(!preventViewNotify){
21699                 this.grid.getView().onRowSelect(index);
21700             }
21701             this.fireEvent("rowselect", this, index, r);
21702             this.fireEvent("selectionchange", this);
21703         }
21704     },
21705
21706     /**
21707      * Deselects a row.
21708      * @param {Number} row The index of the row to deselect
21709      */
21710     deselectRow : function(index, preventViewNotify){
21711         if(this.locked) return;
21712         if(this.last == index){
21713             this.last = false;
21714         }
21715         if(this.lastActive == index){
21716             this.lastActive = false;
21717         }
21718         var r = this.grid.dataSource.getAt(index);
21719         this.selections.remove(r);
21720         if(!preventViewNotify){
21721             this.grid.getView().onRowDeselect(index);
21722         }
21723         this.fireEvent("rowdeselect", this, index);
21724         this.fireEvent("selectionchange", this);
21725     },
21726
21727     // private
21728     restoreLast : function(){
21729         if(this._last){
21730             this.last = this._last;
21731         }
21732     },
21733
21734     // private
21735     acceptsNav : function(row, col, cm){
21736         return !cm.isHidden(col) && cm.isCellEditable(col, row);
21737     },
21738
21739     // private
21740     onEditorKey : function(field, e){
21741         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
21742         if(k == e.TAB){
21743             e.stopEvent();
21744             ed.completeEdit();
21745             if(e.shiftKey){
21746                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
21747             }else{
21748                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
21749             }
21750         }else if(k == e.ENTER && !e.ctrlKey){
21751             e.stopEvent();
21752             ed.completeEdit();
21753             if(e.shiftKey){
21754                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
21755             }else{
21756                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
21757             }
21758         }else if(k == e.ESC){
21759             ed.cancelEdit();
21760         }
21761         if(newCell){
21762             g.startEditing(newCell[0], newCell[1]);
21763         }
21764     }
21765 });/*
21766  * Based on:
21767  * Ext JS Library 1.1.1
21768  * Copyright(c) 2006-2007, Ext JS, LLC.
21769  *
21770  * Originally Released Under LGPL - original licence link has changed is not relivant.
21771  *
21772  * Fork - LGPL
21773  * <script type="text/javascript">
21774  */
21775  
21776 /**
21777  * @class Roo.bootstrap.PagingToolbar
21778  * @extends Roo.bootstrap.NavSimplebar
21779  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
21780  * @constructor
21781  * Create a new PagingToolbar
21782  * @param {Object} config The config object
21783  * @param {Roo.data.Store} store
21784  */
21785 Roo.bootstrap.PagingToolbar = function(config)
21786 {
21787     // old args format still supported... - xtype is prefered..
21788         // created from xtype...
21789     
21790     this.ds = config.dataSource;
21791     
21792     if (config.store && !this.ds) {
21793         this.store= Roo.factory(config.store, Roo.data);
21794         this.ds = this.store;
21795         this.ds.xmodule = this.xmodule || false;
21796     }
21797     
21798     this.toolbarItems = [];
21799     if (config.items) {
21800         this.toolbarItems = config.items;
21801     }
21802     
21803     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
21804     
21805     this.cursor = 0;
21806     
21807     if (this.ds) { 
21808         this.bind(this.ds);
21809     }
21810     
21811     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
21812     
21813 };
21814
21815 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
21816     /**
21817      * @cfg {Roo.data.Store} dataSource
21818      * The underlying data store providing the paged data
21819      */
21820     /**
21821      * @cfg {String/HTMLElement/Element} container
21822      * container The id or element that will contain the toolbar
21823      */
21824     /**
21825      * @cfg {Boolean} displayInfo
21826      * True to display the displayMsg (defaults to false)
21827      */
21828     /**
21829      * @cfg {Number} pageSize
21830      * The number of records to display per page (defaults to 20)
21831      */
21832     pageSize: 20,
21833     /**
21834      * @cfg {String} displayMsg
21835      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
21836      */
21837     displayMsg : 'Displaying {0} - {1} of {2}',
21838     /**
21839      * @cfg {String} emptyMsg
21840      * The message to display when no records are found (defaults to "No data to display")
21841      */
21842     emptyMsg : 'No data to display',
21843     /**
21844      * Customizable piece of the default paging text (defaults to "Page")
21845      * @type String
21846      */
21847     beforePageText : "Page",
21848     /**
21849      * Customizable piece of the default paging text (defaults to "of %0")
21850      * @type String
21851      */
21852     afterPageText : "of {0}",
21853     /**
21854      * Customizable piece of the default paging text (defaults to "First Page")
21855      * @type String
21856      */
21857     firstText : "First Page",
21858     /**
21859      * Customizable piece of the default paging text (defaults to "Previous Page")
21860      * @type String
21861      */
21862     prevText : "Previous Page",
21863     /**
21864      * Customizable piece of the default paging text (defaults to "Next Page")
21865      * @type String
21866      */
21867     nextText : "Next Page",
21868     /**
21869      * Customizable piece of the default paging text (defaults to "Last Page")
21870      * @type String
21871      */
21872     lastText : "Last Page",
21873     /**
21874      * Customizable piece of the default paging text (defaults to "Refresh")
21875      * @type String
21876      */
21877     refreshText : "Refresh",
21878
21879     buttons : false,
21880     // private
21881     onRender : function(ct, position) 
21882     {
21883         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
21884         this.navgroup.parentId = this.id;
21885         this.navgroup.onRender(this.el, null);
21886         // add the buttons to the navgroup
21887         
21888         if(this.displayInfo){
21889             Roo.log(this.el.select('ul.navbar-nav',true).first());
21890             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
21891             this.displayEl = this.el.select('.x-paging-info', true).first();
21892 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
21893 //            this.displayEl = navel.el.select('span',true).first();
21894         }
21895         
21896         var _this = this;
21897         
21898         if(this.buttons){
21899             Roo.each(_this.buttons, function(e){ // this might need to use render????
21900                Roo.factory(e).onRender(_this.el, null);
21901             });
21902         }
21903             
21904         Roo.each(_this.toolbarItems, function(e) {
21905             _this.navgroup.addItem(e);
21906         });
21907         
21908         
21909         this.first = this.navgroup.addItem({
21910             tooltip: this.firstText,
21911             cls: "prev",
21912             icon : 'fa fa-backward',
21913             disabled: true,
21914             preventDefault: true,
21915             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
21916         });
21917         
21918         this.prev =  this.navgroup.addItem({
21919             tooltip: this.prevText,
21920             cls: "prev",
21921             icon : 'fa fa-step-backward',
21922             disabled: true,
21923             preventDefault: true,
21924             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
21925         });
21926     //this.addSeparator();
21927         
21928         
21929         var field = this.navgroup.addItem( {
21930             tagtype : 'span',
21931             cls : 'x-paging-position',
21932             
21933             html : this.beforePageText  +
21934                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
21935                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
21936          } ); //?? escaped?
21937         
21938         this.field = field.el.select('input', true).first();
21939         this.field.on("keydown", this.onPagingKeydown, this);
21940         this.field.on("focus", function(){this.dom.select();});
21941     
21942     
21943         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
21944         //this.field.setHeight(18);
21945         //this.addSeparator();
21946         this.next = this.navgroup.addItem({
21947             tooltip: this.nextText,
21948             cls: "next",
21949             html : ' <i class="fa fa-step-forward">',
21950             disabled: true,
21951             preventDefault: true,
21952             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
21953         });
21954         this.last = this.navgroup.addItem({
21955             tooltip: this.lastText,
21956             icon : 'fa fa-forward',
21957             cls: "next",
21958             disabled: true,
21959             preventDefault: true,
21960             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
21961         });
21962     //this.addSeparator();
21963         this.loading = this.navgroup.addItem({
21964             tooltip: this.refreshText,
21965             icon: 'fa fa-refresh',
21966             preventDefault: true,
21967             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
21968         });
21969         
21970     },
21971
21972     // private
21973     updateInfo : function(){
21974         if(this.displayEl){
21975             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
21976             var msg = count == 0 ?
21977                 this.emptyMsg :
21978                 String.format(
21979                     this.displayMsg,
21980                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
21981                 );
21982             this.displayEl.update(msg);
21983         }
21984     },
21985
21986     // private
21987     onLoad : function(ds, r, o){
21988        this.cursor = o.params ? o.params.start : 0;
21989        var d = this.getPageData(),
21990             ap = d.activePage,
21991             ps = d.pages;
21992         
21993        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
21994        this.field.dom.value = ap;
21995        this.first.setDisabled(ap == 1);
21996        this.prev.setDisabled(ap == 1);
21997        this.next.setDisabled(ap == ps);
21998        this.last.setDisabled(ap == ps);
21999        this.loading.enable();
22000        this.updateInfo();
22001     },
22002
22003     // private
22004     getPageData : function(){
22005         var total = this.ds.getTotalCount();
22006         return {
22007             total : total,
22008             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22009             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22010         };
22011     },
22012
22013     // private
22014     onLoadError : function(){
22015         this.loading.enable();
22016     },
22017
22018     // private
22019     onPagingKeydown : function(e){
22020         var k = e.getKey();
22021         var d = this.getPageData();
22022         if(k == e.RETURN){
22023             var v = this.field.dom.value, pageNum;
22024             if(!v || isNaN(pageNum = parseInt(v, 10))){
22025                 this.field.dom.value = d.activePage;
22026                 return;
22027             }
22028             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22029             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22030             e.stopEvent();
22031         }
22032         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))
22033         {
22034           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22035           this.field.dom.value = pageNum;
22036           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22037           e.stopEvent();
22038         }
22039         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22040         {
22041           var v = this.field.dom.value, pageNum; 
22042           var increment = (e.shiftKey) ? 10 : 1;
22043           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22044             increment *= -1;
22045           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22046             this.field.dom.value = d.activePage;
22047             return;
22048           }
22049           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22050           {
22051             this.field.dom.value = parseInt(v, 10) + increment;
22052             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22053             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22054           }
22055           e.stopEvent();
22056         }
22057     },
22058
22059     // private
22060     beforeLoad : function(){
22061         if(this.loading){
22062             this.loading.disable();
22063         }
22064     },
22065
22066     // private
22067     onClick : function(which){
22068         
22069         var ds = this.ds;
22070         if (!ds) {
22071             return;
22072         }
22073         
22074         switch(which){
22075             case "first":
22076                 ds.load({params:{start: 0, limit: this.pageSize}});
22077             break;
22078             case "prev":
22079                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22080             break;
22081             case "next":
22082                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22083             break;
22084             case "last":
22085                 var total = ds.getTotalCount();
22086                 var extra = total % this.pageSize;
22087                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22088                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22089             break;
22090             case "refresh":
22091                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22092             break;
22093         }
22094     },
22095
22096     /**
22097      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22098      * @param {Roo.data.Store} store The data store to unbind
22099      */
22100     unbind : function(ds){
22101         ds.un("beforeload", this.beforeLoad, this);
22102         ds.un("load", this.onLoad, this);
22103         ds.un("loadexception", this.onLoadError, this);
22104         ds.un("remove", this.updateInfo, this);
22105         ds.un("add", this.updateInfo, this);
22106         this.ds = undefined;
22107     },
22108
22109     /**
22110      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22111      * @param {Roo.data.Store} store The data store to bind
22112      */
22113     bind : function(ds){
22114         ds.on("beforeload", this.beforeLoad, this);
22115         ds.on("load", this.onLoad, this);
22116         ds.on("loadexception", this.onLoadError, this);
22117         ds.on("remove", this.updateInfo, this);
22118         ds.on("add", this.updateInfo, this);
22119         this.ds = ds;
22120     }
22121 });/*
22122  * - LGPL
22123  *
22124  * element
22125  * 
22126  */
22127
22128 /**
22129  * @class Roo.bootstrap.MessageBar
22130  * @extends Roo.bootstrap.Component
22131  * Bootstrap MessageBar class
22132  * @cfg {String} html contents of the MessageBar
22133  * @cfg {String} weight (info | success | warning | danger) default info
22134  * @cfg {String} beforeClass insert the bar before the given class
22135  * @cfg {Boolean} closable (true | false) default false
22136  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22137  * 
22138  * @constructor
22139  * Create a new Element
22140  * @param {Object} config The config object
22141  */
22142
22143 Roo.bootstrap.MessageBar = function(config){
22144     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22145 };
22146
22147 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22148     
22149     html: '',
22150     weight: 'info',
22151     closable: false,
22152     fixed: false,
22153     beforeClass: 'bootstrap-sticky-wrap',
22154     
22155     getAutoCreate : function(){
22156         
22157         var cfg = {
22158             tag: 'div',
22159             cls: 'alert alert-dismissable alert-' + this.weight,
22160             cn: [
22161                 {
22162                     tag: 'span',
22163                     cls: 'message',
22164                     html: this.html || ''
22165                 }
22166             ]
22167         }
22168         
22169         if(this.fixed){
22170             cfg.cls += ' alert-messages-fixed';
22171         }
22172         
22173         if(this.closable){
22174             cfg.cn.push({
22175                 tag: 'button',
22176                 cls: 'close',
22177                 html: 'x'
22178             });
22179         }
22180         
22181         return cfg;
22182     },
22183     
22184     onRender : function(ct, position)
22185     {
22186         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22187         
22188         if(!this.el){
22189             var cfg = Roo.apply({},  this.getAutoCreate());
22190             cfg.id = Roo.id();
22191             
22192             if (this.cls) {
22193                 cfg.cls += ' ' + this.cls;
22194             }
22195             if (this.style) {
22196                 cfg.style = this.style;
22197             }
22198             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22199             
22200             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22201         }
22202         
22203         this.el.select('>button.close').on('click', this.hide, this);
22204         
22205     },
22206     
22207     show : function()
22208     {
22209         if (!this.rendered) {
22210             this.render();
22211         }
22212         
22213         this.el.show();
22214         
22215         this.fireEvent('show', this);
22216         
22217     },
22218     
22219     hide : function()
22220     {
22221         if (!this.rendered) {
22222             this.render();
22223         }
22224         
22225         this.el.hide();
22226         
22227         this.fireEvent('hide', this);
22228     },
22229     
22230     update : function()
22231     {
22232 //        var e = this.el.dom.firstChild;
22233 //        
22234 //        if(this.closable){
22235 //            e = e.nextSibling;
22236 //        }
22237 //        
22238 //        e.data = this.html || '';
22239
22240         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22241     }
22242    
22243 });
22244
22245  
22246
22247      /*
22248  * - LGPL
22249  *
22250  * Graph
22251  * 
22252  */
22253
22254
22255 /**
22256  * @class Roo.bootstrap.Graph
22257  * @extends Roo.bootstrap.Component
22258  * Bootstrap Graph class
22259 > Prameters
22260  -sm {number} sm 4
22261  -md {number} md 5
22262  @cfg {String} graphtype  bar | vbar | pie
22263  @cfg {number} g_x coodinator | centre x (pie)
22264  @cfg {number} g_y coodinator | centre y (pie)
22265  @cfg {number} g_r radius (pie)
22266  @cfg {number} g_height height of the chart (respected by all elements in the set)
22267  @cfg {number} g_width width of the chart (respected by all elements in the set)
22268  @cfg {Object} title The title of the chart
22269     
22270  -{Array}  values
22271  -opts (object) options for the chart 
22272      o {
22273      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22274      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22275      o vgutter (number)
22276      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.
22277      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22278      o to
22279      o stretch (boolean)
22280      o }
22281  -opts (object) options for the pie
22282      o{
22283      o cut
22284      o startAngle (number)
22285      o endAngle (number)
22286      } 
22287  *
22288  * @constructor
22289  * Create a new Input
22290  * @param {Object} config The config object
22291  */
22292
22293 Roo.bootstrap.Graph = function(config){
22294     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22295     
22296     this.addEvents({
22297         // img events
22298         /**
22299          * @event click
22300          * The img click event for the img.
22301          * @param {Roo.EventObject} e
22302          */
22303         "click" : true
22304     });
22305 };
22306
22307 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22308     
22309     sm: 4,
22310     md: 5,
22311     graphtype: 'bar',
22312     g_height: 250,
22313     g_width: 400,
22314     g_x: 50,
22315     g_y: 50,
22316     g_r: 30,
22317     opts:{
22318         //g_colors: this.colors,
22319         g_type: 'soft',
22320         g_gutter: '20%'
22321
22322     },
22323     title : false,
22324
22325     getAutoCreate : function(){
22326         
22327         var cfg = {
22328             tag: 'div',
22329             html : null
22330         }
22331         
22332         
22333         return  cfg;
22334     },
22335
22336     onRender : function(ct,position){
22337         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22338         this.raphael = Raphael(this.el.dom);
22339         
22340                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22341                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22342                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22343                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22344                 /*
22345                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22346                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22347                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22348                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22349                 
22350                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22351                 r.barchart(330, 10, 300, 220, data1);
22352                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22353                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22354                 */
22355                 
22356                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22357                 // r.barchart(30, 30, 560, 250,  xdata, {
22358                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22359                 //     axis : "0 0 1 1",
22360                 //     axisxlabels :  xdata
22361                 //     //yvalues : cols,
22362                    
22363                 // });
22364 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22365 //        
22366 //        this.load(null,xdata,{
22367 //                axis : "0 0 1 1",
22368 //                axisxlabels :  xdata
22369 //                });
22370
22371     },
22372
22373     load : function(graphtype,xdata,opts){
22374         this.raphael.clear();
22375         if(!graphtype) {
22376             graphtype = this.graphtype;
22377         }
22378         if(!opts){
22379             opts = this.opts;
22380         }
22381         var r = this.raphael,
22382             fin = function () {
22383                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22384             },
22385             fout = function () {
22386                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22387             },
22388             pfin = function() {
22389                 this.sector.stop();
22390                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22391
22392                 if (this.label) {
22393                     this.label[0].stop();
22394                     this.label[0].attr({ r: 7.5 });
22395                     this.label[1].attr({ "font-weight": 800 });
22396                 }
22397             },
22398             pfout = function() {
22399                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22400
22401                 if (this.label) {
22402                     this.label[0].animate({ r: 5 }, 500, "bounce");
22403                     this.label[1].attr({ "font-weight": 400 });
22404                 }
22405             };
22406
22407         switch(graphtype){
22408             case 'bar':
22409                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22410                 break;
22411             case 'hbar':
22412                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22413                 break;
22414             case 'pie':
22415 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22416 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22417 //            
22418                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22419                 
22420                 break;
22421
22422         }
22423         
22424         if(this.title){
22425             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22426         }
22427         
22428     },
22429     
22430     setTitle: function(o)
22431     {
22432         this.title = o;
22433     },
22434     
22435     initEvents: function() {
22436         
22437         if(!this.href){
22438             this.el.on('click', this.onClick, this);
22439         }
22440     },
22441     
22442     onClick : function(e)
22443     {
22444         Roo.log('img onclick');
22445         this.fireEvent('click', this, e);
22446     }
22447    
22448 });
22449
22450  
22451 /*
22452  * - LGPL
22453  *
22454  * numberBox
22455  * 
22456  */
22457 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22458
22459 /**
22460  * @class Roo.bootstrap.dash.NumberBox
22461  * @extends Roo.bootstrap.Component
22462  * Bootstrap NumberBox class
22463  * @cfg {String} headline Box headline
22464  * @cfg {String} content Box content
22465  * @cfg {String} icon Box icon
22466  * @cfg {String} footer Footer text
22467  * @cfg {String} fhref Footer href
22468  * 
22469  * @constructor
22470  * Create a new NumberBox
22471  * @param {Object} config The config object
22472  */
22473
22474
22475 Roo.bootstrap.dash.NumberBox = function(config){
22476     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22477     
22478 };
22479
22480 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22481     
22482     headline : '',
22483     content : '',
22484     icon : '',
22485     footer : '',
22486     fhref : '',
22487     ficon : '',
22488     
22489     getAutoCreate : function(){
22490         
22491         var cfg = {
22492             tag : 'div',
22493             cls : 'small-box ',
22494             cn : [
22495                 {
22496                     tag : 'div',
22497                     cls : 'inner',
22498                     cn :[
22499                         {
22500                             tag : 'h3',
22501                             cls : 'roo-headline',
22502                             html : this.headline
22503                         },
22504                         {
22505                             tag : 'p',
22506                             cls : 'roo-content',
22507                             html : this.content
22508                         }
22509                     ]
22510                 }
22511             ]
22512         }
22513         
22514         if(this.icon){
22515             cfg.cn.push({
22516                 tag : 'div',
22517                 cls : 'icon',
22518                 cn :[
22519                     {
22520                         tag : 'i',
22521                         cls : 'ion ' + this.icon
22522                     }
22523                 ]
22524             });
22525         }
22526         
22527         if(this.footer){
22528             var footer = {
22529                 tag : 'a',
22530                 cls : 'small-box-footer',
22531                 href : this.fhref || '#',
22532                 html : this.footer
22533             };
22534             
22535             cfg.cn.push(footer);
22536             
22537         }
22538         
22539         return  cfg;
22540     },
22541
22542     onRender : function(ct,position){
22543         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22544
22545
22546        
22547                 
22548     },
22549
22550     setHeadline: function (value)
22551     {
22552         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22553     },
22554     
22555     setFooter: function (value, href)
22556     {
22557         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22558         
22559         if(href){
22560             this.el.select('a.small-box-footer',true).first().attr('href', href);
22561         }
22562         
22563     },
22564
22565     setContent: function (value)
22566     {
22567         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22568     },
22569
22570     initEvents: function() 
22571     {   
22572         
22573     }
22574     
22575 });
22576
22577  
22578 /*
22579  * - LGPL
22580  *
22581  * TabBox
22582  * 
22583  */
22584 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22585
22586 /**
22587  * @class Roo.bootstrap.dash.TabBox
22588  * @extends Roo.bootstrap.Component
22589  * Bootstrap TabBox class
22590  * @cfg {String} title Title of the TabBox
22591  * @cfg {String} icon Icon of the TabBox
22592  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22593  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22594  * 
22595  * @constructor
22596  * Create a new TabBox
22597  * @param {Object} config The config object
22598  */
22599
22600
22601 Roo.bootstrap.dash.TabBox = function(config){
22602     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22603     this.addEvents({
22604         // raw events
22605         /**
22606          * @event addpane
22607          * When a pane is added
22608          * @param {Roo.bootstrap.dash.TabPane} pane
22609          */
22610         "addpane" : true,
22611         /**
22612          * @event activatepane
22613          * When a pane is activated
22614          * @param {Roo.bootstrap.dash.TabPane} pane
22615          */
22616         "activatepane" : true
22617         
22618          
22619     });
22620     
22621     this.panes = [];
22622 };
22623
22624 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22625
22626     title : '',
22627     icon : false,
22628     showtabs : true,
22629     tabScrollable : false,
22630     
22631     getChildContainer : function()
22632     {
22633         return this.el.select('.tab-content', true).first();
22634     },
22635     
22636     getAutoCreate : function(){
22637         
22638         var header = {
22639             tag: 'li',
22640             cls: 'pull-left header',
22641             html: this.title,
22642             cn : []
22643         };
22644         
22645         if(this.icon){
22646             header.cn.push({
22647                 tag: 'i',
22648                 cls: 'fa ' + this.icon
22649             });
22650         }
22651         
22652         var h = {
22653             tag: 'ul',
22654             cls: 'nav nav-tabs pull-right',
22655             cn: [
22656                 header
22657             ]
22658         };
22659         
22660         if(this.tabScrollable){
22661             h = {
22662                 tag: 'div',
22663                 cls: 'tab-header',
22664                 cn: [
22665                     {
22666                         tag: 'ul',
22667                         cls: 'nav nav-tabs pull-right',
22668                         cn: [
22669                             header
22670                         ]
22671                     }
22672                 ]
22673             }
22674         }
22675         
22676         var cfg = {
22677             tag: 'div',
22678             cls: 'nav-tabs-custom',
22679             cn: [
22680                 h,
22681                 {
22682                     tag: 'div',
22683                     cls: 'tab-content no-padding',
22684                     cn: []
22685                 }
22686             ]
22687         }
22688
22689         return  cfg;
22690     },
22691     initEvents : function()
22692     {
22693         //Roo.log('add add pane handler');
22694         this.on('addpane', this.onAddPane, this);
22695     },
22696      /**
22697      * Updates the box title
22698      * @param {String} html to set the title to.
22699      */
22700     setTitle : function(value)
22701     {
22702         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
22703     },
22704     onAddPane : function(pane)
22705     {
22706         this.panes.push(pane);
22707         //Roo.log('addpane');
22708         //Roo.log(pane);
22709         // tabs are rendere left to right..
22710         if(!this.showtabs){
22711             return;
22712         }
22713         
22714         var ctr = this.el.select('.nav-tabs', true).first();
22715          
22716          
22717         var existing = ctr.select('.nav-tab',true);
22718         var qty = existing.getCount();;
22719         
22720         
22721         var tab = ctr.createChild({
22722             tag : 'li',
22723             cls : 'nav-tab' + (qty ? '' : ' active'),
22724             cn : [
22725                 {
22726                     tag : 'a',
22727                     href:'#',
22728                     html : pane.title
22729                 }
22730             ]
22731         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
22732         pane.tab = tab;
22733         
22734         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
22735         if (!qty) {
22736             pane.el.addClass('active');
22737         }
22738         
22739                 
22740     },
22741     onTabClick : function(ev,un,ob,pane)
22742     {
22743         //Roo.log('tab - prev default');
22744         ev.preventDefault();
22745         
22746         
22747         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
22748         pane.tab.addClass('active');
22749         //Roo.log(pane.title);
22750         this.getChildContainer().select('.tab-pane',true).removeClass('active');
22751         // technically we should have a deactivate event.. but maybe add later.
22752         // and it should not de-activate the selected tab...
22753         this.fireEvent('activatepane', pane);
22754         pane.el.addClass('active');
22755         pane.fireEvent('activate');
22756         
22757         
22758     },
22759     
22760     getActivePane : function()
22761     {
22762         var r = false;
22763         Roo.each(this.panes, function(p) {
22764             if(p.el.hasClass('active')){
22765                 r = p;
22766                 return false;
22767             }
22768             
22769             return;
22770         });
22771         
22772         return r;
22773     }
22774     
22775     
22776 });
22777
22778  
22779 /*
22780  * - LGPL
22781  *
22782  * Tab pane
22783  * 
22784  */
22785 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22786 /**
22787  * @class Roo.bootstrap.TabPane
22788  * @extends Roo.bootstrap.Component
22789  * Bootstrap TabPane class
22790  * @cfg {Boolean} active (false | true) Default false
22791  * @cfg {String} title title of panel
22792
22793  * 
22794  * @constructor
22795  * Create a new TabPane
22796  * @param {Object} config The config object
22797  */
22798
22799 Roo.bootstrap.dash.TabPane = function(config){
22800     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
22801     
22802     this.addEvents({
22803         // raw events
22804         /**
22805          * @event activate
22806          * When a pane is activated
22807          * @param {Roo.bootstrap.dash.TabPane} pane
22808          */
22809         "activate" : true
22810          
22811     });
22812 };
22813
22814 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
22815     
22816     active : false,
22817     title : '',
22818     
22819     // the tabBox that this is attached to.
22820     tab : false,
22821      
22822     getAutoCreate : function() 
22823     {
22824         var cfg = {
22825             tag: 'div',
22826             cls: 'tab-pane'
22827         }
22828         
22829         if(this.active){
22830             cfg.cls += ' active';
22831         }
22832         
22833         return cfg;
22834     },
22835     initEvents  : function()
22836     {
22837         //Roo.log('trigger add pane handler');
22838         this.parent().fireEvent('addpane', this)
22839     },
22840     
22841      /**
22842      * Updates the tab title 
22843      * @param {String} html to set the title to.
22844      */
22845     setTitle: function(str)
22846     {
22847         if (!this.tab) {
22848             return;
22849         }
22850         this.title = str;
22851         this.tab.select('a', true).first().dom.innerHTML = str;
22852         
22853     }
22854     
22855     
22856     
22857 });
22858
22859  
22860
22861
22862  /*
22863  * - LGPL
22864  *
22865  * menu
22866  * 
22867  */
22868 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
22869
22870 /**
22871  * @class Roo.bootstrap.menu.Menu
22872  * @extends Roo.bootstrap.Component
22873  * Bootstrap Menu class - container for Menu
22874  * @cfg {String} html Text of the menu
22875  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
22876  * @cfg {String} icon Font awesome icon
22877  * @cfg {String} pos Menu align to (top | bottom) default bottom
22878  * 
22879  * 
22880  * @constructor
22881  * Create a new Menu
22882  * @param {Object} config The config object
22883  */
22884
22885
22886 Roo.bootstrap.menu.Menu = function(config){
22887     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
22888     
22889     this.addEvents({
22890         /**
22891          * @event beforeshow
22892          * Fires before this menu is displayed
22893          * @param {Roo.bootstrap.menu.Menu} this
22894          */
22895         beforeshow : true,
22896         /**
22897          * @event beforehide
22898          * Fires before this menu is hidden
22899          * @param {Roo.bootstrap.menu.Menu} this
22900          */
22901         beforehide : true,
22902         /**
22903          * @event show
22904          * Fires after this menu is displayed
22905          * @param {Roo.bootstrap.menu.Menu} this
22906          */
22907         show : true,
22908         /**
22909          * @event hide
22910          * Fires after this menu is hidden
22911          * @param {Roo.bootstrap.menu.Menu} this
22912          */
22913         hide : true,
22914         /**
22915          * @event click
22916          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
22917          * @param {Roo.bootstrap.menu.Menu} this
22918          * @param {Roo.EventObject} e
22919          */
22920         click : true
22921     });
22922     
22923 };
22924
22925 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
22926     
22927     submenu : false,
22928     html : '',
22929     weight : 'default',
22930     icon : false,
22931     pos : 'bottom',
22932     
22933     
22934     getChildContainer : function() {
22935         if(this.isSubMenu){
22936             return this.el;
22937         }
22938         
22939         return this.el.select('ul.dropdown-menu', true).first();  
22940     },
22941     
22942     getAutoCreate : function()
22943     {
22944         var text = [
22945             {
22946                 tag : 'span',
22947                 cls : 'roo-menu-text',
22948                 html : this.html
22949             }
22950         ];
22951         
22952         if(this.icon){
22953             text.unshift({
22954                 tag : 'i',
22955                 cls : 'fa ' + this.icon
22956             })
22957         }
22958         
22959         
22960         var cfg = {
22961             tag : 'div',
22962             cls : 'btn-group',
22963             cn : [
22964                 {
22965                     tag : 'button',
22966                     cls : 'dropdown-button btn btn-' + this.weight,
22967                     cn : text
22968                 },
22969                 {
22970                     tag : 'button',
22971                     cls : 'dropdown-toggle btn btn-' + this.weight,
22972                     cn : [
22973                         {
22974                             tag : 'span',
22975                             cls : 'caret'
22976                         }
22977                     ]
22978                 },
22979                 {
22980                     tag : 'ul',
22981                     cls : 'dropdown-menu'
22982                 }
22983             ]
22984             
22985         };
22986         
22987         if(this.pos == 'top'){
22988             cfg.cls += ' dropup';
22989         }
22990         
22991         if(this.isSubMenu){
22992             cfg = {
22993                 tag : 'ul',
22994                 cls : 'dropdown-menu'
22995             }
22996         }
22997         
22998         return cfg;
22999     },
23000     
23001     onRender : function(ct, position)
23002     {
23003         this.isSubMenu = ct.hasClass('dropdown-submenu');
23004         
23005         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23006     },
23007     
23008     initEvents : function() 
23009     {
23010         if(this.isSubMenu){
23011             return;
23012         }
23013         
23014         this.hidden = true;
23015         
23016         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23017         this.triggerEl.on('click', this.onTriggerPress, this);
23018         
23019         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23020         this.buttonEl.on('click', this.onClick, this);
23021         
23022     },
23023     
23024     list : function()
23025     {
23026         if(this.isSubMenu){
23027             return this.el;
23028         }
23029         
23030         return this.el.select('ul.dropdown-menu', true).first();
23031     },
23032     
23033     onClick : function(e)
23034     {
23035         this.fireEvent("click", this, e);
23036     },
23037     
23038     onTriggerPress  : function(e)
23039     {   
23040         if (this.isVisible()) {
23041             this.hide();
23042         } else {
23043             this.show();
23044         }
23045     },
23046     
23047     isVisible : function(){
23048         return !this.hidden;
23049     },
23050     
23051     show : function()
23052     {
23053         this.fireEvent("beforeshow", this);
23054         
23055         this.hidden = false;
23056         this.el.addClass('open');
23057         
23058         Roo.get(document).on("mouseup", this.onMouseUp, this);
23059         
23060         this.fireEvent("show", this);
23061         
23062         
23063     },
23064     
23065     hide : function()
23066     {
23067         this.fireEvent("beforehide", this);
23068         
23069         this.hidden = true;
23070         this.el.removeClass('open');
23071         
23072         Roo.get(document).un("mouseup", this.onMouseUp);
23073         
23074         this.fireEvent("hide", this);
23075     },
23076     
23077     onMouseUp : function()
23078     {
23079         this.hide();
23080     }
23081     
23082 });
23083
23084  
23085  /*
23086  * - LGPL
23087  *
23088  * menu item
23089  * 
23090  */
23091 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23092
23093 /**
23094  * @class Roo.bootstrap.menu.Item
23095  * @extends Roo.bootstrap.Component
23096  * Bootstrap MenuItem class
23097  * @cfg {Boolean} submenu (true | false) default false
23098  * @cfg {String} html text of the item
23099  * @cfg {String} href the link
23100  * @cfg {Boolean} disable (true | false) default false
23101  * @cfg {Boolean} preventDefault (true | false) default true
23102  * @cfg {String} icon Font awesome icon
23103  * @cfg {String} pos Submenu align to (left | right) default right 
23104  * 
23105  * 
23106  * @constructor
23107  * Create a new Item
23108  * @param {Object} config The config object
23109  */
23110
23111
23112 Roo.bootstrap.menu.Item = function(config){
23113     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23114     this.addEvents({
23115         /**
23116          * @event mouseover
23117          * Fires when the mouse is hovering over this menu
23118          * @param {Roo.bootstrap.menu.Item} this
23119          * @param {Roo.EventObject} e
23120          */
23121         mouseover : true,
23122         /**
23123          * @event mouseout
23124          * Fires when the mouse exits this menu
23125          * @param {Roo.bootstrap.menu.Item} this
23126          * @param {Roo.EventObject} e
23127          */
23128         mouseout : true,
23129         // raw events
23130         /**
23131          * @event click
23132          * The raw click event for the entire grid.
23133          * @param {Roo.EventObject} e
23134          */
23135         click : true
23136     });
23137 };
23138
23139 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23140     
23141     submenu : false,
23142     href : '',
23143     html : '',
23144     preventDefault: true,
23145     disable : false,
23146     icon : false,
23147     pos : 'right',
23148     
23149     getAutoCreate : function()
23150     {
23151         var text = [
23152             {
23153                 tag : 'span',
23154                 cls : 'roo-menu-item-text',
23155                 html : this.html
23156             }
23157         ];
23158         
23159         if(this.icon){
23160             text.unshift({
23161                 tag : 'i',
23162                 cls : 'fa ' + this.icon
23163             })
23164         }
23165         
23166         var cfg = {
23167             tag : 'li',
23168             cn : [
23169                 {
23170                     tag : 'a',
23171                     href : this.href || '#',
23172                     cn : text
23173                 }
23174             ]
23175         };
23176         
23177         if(this.disable){
23178             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23179         }
23180         
23181         if(this.submenu){
23182             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23183             
23184             if(this.pos == 'left'){
23185                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23186             }
23187         }
23188         
23189         return cfg;
23190     },
23191     
23192     initEvents : function() 
23193     {
23194         this.el.on('mouseover', this.onMouseOver, this);
23195         this.el.on('mouseout', this.onMouseOut, this);
23196         
23197         this.el.select('a', true).first().on('click', this.onClick, this);
23198         
23199     },
23200     
23201     onClick : function(e)
23202     {
23203         if(this.preventDefault){
23204             e.preventDefault();
23205         }
23206         
23207         this.fireEvent("click", this, e);
23208     },
23209     
23210     onMouseOver : function(e)
23211     {
23212         if(this.submenu && this.pos == 'left'){
23213             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23214         }
23215         
23216         this.fireEvent("mouseover", this, e);
23217     },
23218     
23219     onMouseOut : function(e)
23220     {
23221         this.fireEvent("mouseout", this, e);
23222     }
23223 });
23224
23225  
23226
23227  /*
23228  * - LGPL
23229  *
23230  * menu separator
23231  * 
23232  */
23233 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23234
23235 /**
23236  * @class Roo.bootstrap.menu.Separator
23237  * @extends Roo.bootstrap.Component
23238  * Bootstrap Separator class
23239  * 
23240  * @constructor
23241  * Create a new Separator
23242  * @param {Object} config The config object
23243  */
23244
23245
23246 Roo.bootstrap.menu.Separator = function(config){
23247     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23248 };
23249
23250 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23251     
23252     getAutoCreate : function(){
23253         var cfg = {
23254             tag : 'li',
23255             cls: 'divider'
23256         };
23257         
23258         return cfg;
23259     }
23260    
23261 });
23262
23263  
23264
23265  /*
23266  * - LGPL
23267  *
23268  * Tooltip
23269  * 
23270  */
23271
23272 /**
23273  * @class Roo.bootstrap.Tooltip
23274  * Bootstrap Tooltip class
23275  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23276  * to determine which dom element triggers the tooltip.
23277  * 
23278  * It needs to add support for additional attributes like tooltip-position
23279  * 
23280  * @constructor
23281  * Create a new Toolti
23282  * @param {Object} config The config object
23283  */
23284
23285 Roo.bootstrap.Tooltip = function(config){
23286     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23287 };
23288
23289 Roo.apply(Roo.bootstrap.Tooltip, {
23290     /**
23291      * @function init initialize tooltip monitoring.
23292      * @static
23293      */
23294     currentEl : false,
23295     currentTip : false,
23296     currentRegion : false,
23297     
23298     //  init : delay?
23299     
23300     init : function()
23301     {
23302         Roo.get(document).on('mouseover', this.enter ,this);
23303         Roo.get(document).on('mouseout', this.leave, this);
23304          
23305         
23306         this.currentTip = new Roo.bootstrap.Tooltip();
23307     },
23308     
23309     enter : function(ev)
23310     {
23311         var dom = ev.getTarget();
23312         
23313         //Roo.log(['enter',dom]);
23314         var el = Roo.fly(dom);
23315         if (this.currentEl) {
23316             //Roo.log(dom);
23317             //Roo.log(this.currentEl);
23318             //Roo.log(this.currentEl.contains(dom));
23319             if (this.currentEl == el) {
23320                 return;
23321             }
23322             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23323                 return;
23324             }
23325
23326         }
23327         
23328         if (this.currentTip.el) {
23329             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23330         }    
23331         //Roo.log(ev);
23332         var bindEl = el;
23333         
23334         // you can not look for children, as if el is the body.. then everythign is the child..
23335         if (!el.attr('tooltip')) { //
23336             if (!el.select("[tooltip]").elements.length) {
23337                 return;
23338             }
23339             // is the mouse over this child...?
23340             bindEl = el.select("[tooltip]").first();
23341             var xy = ev.getXY();
23342             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23343                 //Roo.log("not in region.");
23344                 return;
23345             }
23346             //Roo.log("child element over..");
23347             
23348         }
23349         this.currentEl = bindEl;
23350         this.currentTip.bind(bindEl);
23351         this.currentRegion = Roo.lib.Region.getRegion(dom);
23352         this.currentTip.enter();
23353         
23354     },
23355     leave : function(ev)
23356     {
23357         var dom = ev.getTarget();
23358         //Roo.log(['leave',dom]);
23359         if (!this.currentEl) {
23360             return;
23361         }
23362         
23363         
23364         if (dom != this.currentEl.dom) {
23365             return;
23366         }
23367         var xy = ev.getXY();
23368         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23369             return;
23370         }
23371         // only activate leave if mouse cursor is outside... bounding box..
23372         
23373         
23374         
23375         
23376         if (this.currentTip) {
23377             this.currentTip.leave();
23378         }
23379         //Roo.log('clear currentEl');
23380         this.currentEl = false;
23381         
23382         
23383     },
23384     alignment : {
23385         'left' : ['r-l', [-2,0], 'right'],
23386         'right' : ['l-r', [2,0], 'left'],
23387         'bottom' : ['t-b', [0,2], 'top'],
23388         'top' : [ 'b-t', [0,-2], 'bottom']
23389     }
23390     
23391 });
23392
23393
23394 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23395     
23396     
23397     bindEl : false,
23398     
23399     delay : null, // can be { show : 300 , hide: 500}
23400     
23401     timeout : null,
23402     
23403     hoverState : null, //???
23404     
23405     placement : 'bottom', 
23406     
23407     getAutoCreate : function(){
23408     
23409         var cfg = {
23410            cls : 'tooltip',
23411            role : 'tooltip',
23412            cn : [
23413                 {
23414                     cls : 'tooltip-arrow'
23415                 },
23416                 {
23417                     cls : 'tooltip-inner'
23418                 }
23419            ]
23420         };
23421         
23422         return cfg;
23423     },
23424     bind : function(el)
23425     {
23426         this.bindEl = el;
23427     },
23428       
23429     
23430     enter : function () {
23431        
23432         if (this.timeout != null) {
23433             clearTimeout(this.timeout);
23434         }
23435         
23436         this.hoverState = 'in';
23437          //Roo.log("enter - show");
23438         if (!this.delay || !this.delay.show) {
23439             this.show();
23440             return;
23441         }
23442         var _t = this;
23443         this.timeout = setTimeout(function () {
23444             if (_t.hoverState == 'in') {
23445                 _t.show();
23446             }
23447         }, this.delay.show);
23448     },
23449     leave : function()
23450     {
23451         clearTimeout(this.timeout);
23452     
23453         this.hoverState = 'out';
23454          if (!this.delay || !this.delay.hide) {
23455             this.hide();
23456             return;
23457         }
23458        
23459         var _t = this;
23460         this.timeout = setTimeout(function () {
23461             //Roo.log("leave - timeout");
23462             
23463             if (_t.hoverState == 'out') {
23464                 _t.hide();
23465                 Roo.bootstrap.Tooltip.currentEl = false;
23466             }
23467         }, delay);
23468     },
23469     
23470     show : function ()
23471     {
23472         if (!this.el) {
23473             this.render(document.body);
23474         }
23475         // set content.
23476         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23477         
23478         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23479         
23480         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23481         
23482         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23483         
23484         var placement = typeof this.placement == 'function' ?
23485             this.placement.call(this, this.el, on_el) :
23486             this.placement;
23487             
23488         var autoToken = /\s?auto?\s?/i;
23489         var autoPlace = autoToken.test(placement);
23490         if (autoPlace) {
23491             placement = placement.replace(autoToken, '') || 'top';
23492         }
23493         
23494         //this.el.detach()
23495         //this.el.setXY([0,0]);
23496         this.el.show();
23497         //this.el.dom.style.display='block';
23498         
23499         //this.el.appendTo(on_el);
23500         
23501         var p = this.getPosition();
23502         var box = this.el.getBox();
23503         
23504         if (autoPlace) {
23505             // fixme..
23506         }
23507         
23508         var align = Roo.bootstrap.Tooltip.alignment[placement];
23509         
23510         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23511         
23512         if(placement == 'top' || placement == 'bottom'){
23513             if(xy[0] < 0){
23514                 placement = 'right';
23515             }
23516             
23517             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23518                 placement = 'left';
23519             }
23520         }
23521         
23522         align = Roo.bootstrap.Tooltip.alignment[placement];
23523         
23524         this.el.alignTo(this.bindEl, align[0],align[1]);
23525         //var arrow = this.el.select('.arrow',true).first();
23526         //arrow.set(align[2], 
23527         
23528         this.el.addClass(placement);
23529         
23530         this.el.addClass('in fade');
23531         
23532         this.hoverState = null;
23533         
23534         if (this.el.hasClass('fade')) {
23535             // fade it?
23536         }
23537         
23538     },
23539     hide : function()
23540     {
23541          
23542         if (!this.el) {
23543             return;
23544         }
23545         //this.el.setXY([0,0]);
23546         this.el.removeClass('in');
23547         //this.el.hide();
23548         
23549     }
23550     
23551 });
23552  
23553
23554  /*
23555  * - LGPL
23556  *
23557  * Location Picker
23558  * 
23559  */
23560
23561 /**
23562  * @class Roo.bootstrap.LocationPicker
23563  * @extends Roo.bootstrap.Component
23564  * Bootstrap LocationPicker class
23565  * @cfg {Number} latitude Position when init default 0
23566  * @cfg {Number} longitude Position when init default 0
23567  * @cfg {Number} zoom default 15
23568  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23569  * @cfg {Boolean} mapTypeControl default false
23570  * @cfg {Boolean} disableDoubleClickZoom default false
23571  * @cfg {Boolean} scrollwheel default true
23572  * @cfg {Boolean} streetViewControl default false
23573  * @cfg {Number} radius default 0
23574  * @cfg {String} locationName
23575  * @cfg {Boolean} draggable default true
23576  * @cfg {Boolean} enableAutocomplete default false
23577  * @cfg {Boolean} enableReverseGeocode default true
23578  * @cfg {String} markerTitle
23579  * 
23580  * @constructor
23581  * Create a new LocationPicker
23582  * @param {Object} config The config object
23583  */
23584
23585
23586 Roo.bootstrap.LocationPicker = function(config){
23587     
23588     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23589     
23590     this.addEvents({
23591         /**
23592          * @event initial
23593          * Fires when the picker initialized.
23594          * @param {Roo.bootstrap.LocationPicker} this
23595          * @param {Google Location} location
23596          */
23597         initial : true,
23598         /**
23599          * @event positionchanged
23600          * Fires when the picker position changed.
23601          * @param {Roo.bootstrap.LocationPicker} this
23602          * @param {Google Location} location
23603          */
23604         positionchanged : true,
23605         /**
23606          * @event resize
23607          * Fires when the map resize.
23608          * @param {Roo.bootstrap.LocationPicker} this
23609          */
23610         resize : true,
23611         /**
23612          * @event show
23613          * Fires when the map show.
23614          * @param {Roo.bootstrap.LocationPicker} this
23615          */
23616         show : true,
23617         /**
23618          * @event hide
23619          * Fires when the map hide.
23620          * @param {Roo.bootstrap.LocationPicker} this
23621          */
23622         hide : true,
23623         /**
23624          * @event mapClick
23625          * Fires when click the map.
23626          * @param {Roo.bootstrap.LocationPicker} this
23627          * @param {Map event} e
23628          */
23629         mapClick : true,
23630         /**
23631          * @event mapRightClick
23632          * Fires when right click the map.
23633          * @param {Roo.bootstrap.LocationPicker} this
23634          * @param {Map event} e
23635          */
23636         mapRightClick : true,
23637         /**
23638          * @event markerClick
23639          * Fires when click the marker.
23640          * @param {Roo.bootstrap.LocationPicker} this
23641          * @param {Map event} e
23642          */
23643         markerClick : true,
23644         /**
23645          * @event markerRightClick
23646          * Fires when right click the marker.
23647          * @param {Roo.bootstrap.LocationPicker} this
23648          * @param {Map event} e
23649          */
23650         markerRightClick : true,
23651         /**
23652          * @event OverlayViewDraw
23653          * Fires when OverlayView Draw
23654          * @param {Roo.bootstrap.LocationPicker} this
23655          */
23656         OverlayViewDraw : true,
23657         /**
23658          * @event OverlayViewOnAdd
23659          * Fires when OverlayView Draw
23660          * @param {Roo.bootstrap.LocationPicker} this
23661          */
23662         OverlayViewOnAdd : true,
23663         /**
23664          * @event OverlayViewOnRemove
23665          * Fires when OverlayView Draw
23666          * @param {Roo.bootstrap.LocationPicker} this
23667          */
23668         OverlayViewOnRemove : true,
23669         /**
23670          * @event OverlayViewShow
23671          * Fires when OverlayView Draw
23672          * @param {Roo.bootstrap.LocationPicker} this
23673          * @param {Pixel} cpx
23674          */
23675         OverlayViewShow : true,
23676         /**
23677          * @event OverlayViewHide
23678          * Fires when OverlayView Draw
23679          * @param {Roo.bootstrap.LocationPicker} this
23680          */
23681         OverlayViewHide : true
23682     });
23683         
23684 };
23685
23686 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
23687     
23688     gMapContext: false,
23689     
23690     latitude: 0,
23691     longitude: 0,
23692     zoom: 15,
23693     mapTypeId: false,
23694     mapTypeControl: false,
23695     disableDoubleClickZoom: false,
23696     scrollwheel: true,
23697     streetViewControl: false,
23698     radius: 0,
23699     locationName: '',
23700     draggable: true,
23701     enableAutocomplete: false,
23702     enableReverseGeocode: true,
23703     markerTitle: '',
23704     
23705     getAutoCreate: function()
23706     {
23707
23708         var cfg = {
23709             tag: 'div',
23710             cls: 'roo-location-picker'
23711         };
23712         
23713         return cfg
23714     },
23715     
23716     initEvents: function(ct, position)
23717     {       
23718         if(!this.el.getWidth() || this.isApplied()){
23719             return;
23720         }
23721         
23722         this.el.setVisibilityMode(Roo.Element.DISPLAY);
23723         
23724         this.initial();
23725     },
23726     
23727     initial: function()
23728     {
23729         if(!this.mapTypeId){
23730             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
23731         }
23732         
23733         this.gMapContext = this.GMapContext();
23734         
23735         this.initOverlayView();
23736         
23737         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
23738         
23739         var _this = this;
23740                 
23741         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
23742             _this.setPosition(_this.gMapContext.marker.position);
23743         });
23744         
23745         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
23746             _this.fireEvent('mapClick', this, event);
23747             
23748         });
23749
23750         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
23751             _this.fireEvent('mapRightClick', this, event);
23752             
23753         });
23754         
23755         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
23756             _this.fireEvent('markerClick', this, event);
23757             
23758         });
23759
23760         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
23761             _this.fireEvent('markerRightClick', this, event);
23762             
23763         });
23764         
23765         this.setPosition(this.gMapContext.location);
23766         
23767         this.fireEvent('initial', this, this.gMapContext.location);
23768     },
23769     
23770     initOverlayView: function()
23771     {
23772         var _this = this;
23773         
23774         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
23775             
23776             draw: function()
23777             {
23778                 _this.fireEvent('OverlayViewDraw', _this);
23779             },
23780             
23781             onAdd: function()
23782             {
23783                 _this.fireEvent('OverlayViewOnAdd', _this);
23784             },
23785             
23786             onRemove: function()
23787             {
23788                 _this.fireEvent('OverlayViewOnRemove', _this);
23789             },
23790             
23791             show: function(cpx)
23792             {
23793                 _this.fireEvent('OverlayViewShow', _this, cpx);
23794             },
23795             
23796             hide: function()
23797             {
23798                 _this.fireEvent('OverlayViewHide', _this);
23799             }
23800             
23801         });
23802     },
23803     
23804     fromLatLngToContainerPixel: function(event)
23805     {
23806         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
23807     },
23808     
23809     isApplied: function() 
23810     {
23811         return this.getGmapContext() == false ? false : true;
23812     },
23813     
23814     getGmapContext: function() 
23815     {
23816         return this.gMapContext
23817     },
23818     
23819     GMapContext: function() 
23820     {
23821         var position = new google.maps.LatLng(this.latitude, this.longitude);
23822         
23823         var _map = new google.maps.Map(this.el.dom, {
23824             center: position,
23825             zoom: this.zoom,
23826             mapTypeId: this.mapTypeId,
23827             mapTypeControl: this.mapTypeControl,
23828             disableDoubleClickZoom: this.disableDoubleClickZoom,
23829             scrollwheel: this.scrollwheel,
23830             streetViewControl: this.streetViewControl,
23831             locationName: this.locationName,
23832             draggable: this.draggable,
23833             enableAutocomplete: this.enableAutocomplete,
23834             enableReverseGeocode: this.enableReverseGeocode
23835         });
23836         
23837         var _marker = new google.maps.Marker({
23838             position: position,
23839             map: _map,
23840             title: this.markerTitle,
23841             draggable: this.draggable
23842         });
23843         
23844         return {
23845             map: _map,
23846             marker: _marker,
23847             circle: null,
23848             location: position,
23849             radius: this.radius,
23850             locationName: this.locationName,
23851             addressComponents: {
23852                 formatted_address: null,
23853                 addressLine1: null,
23854                 addressLine2: null,
23855                 streetName: null,
23856                 streetNumber: null,
23857                 city: null,
23858                 district: null,
23859                 state: null,
23860                 stateOrProvince: null
23861             },
23862             settings: this,
23863             domContainer: this.el.dom,
23864             geodecoder: new google.maps.Geocoder()
23865         };
23866     },
23867     
23868     drawCircle: function(center, radius, options) 
23869     {
23870         if (this.gMapContext.circle != null) {
23871             this.gMapContext.circle.setMap(null);
23872         }
23873         if (radius > 0) {
23874             radius *= 1;
23875             options = Roo.apply({}, options, {
23876                 strokeColor: "#0000FF",
23877                 strokeOpacity: .35,
23878                 strokeWeight: 2,
23879                 fillColor: "#0000FF",
23880                 fillOpacity: .2
23881             });
23882             
23883             options.map = this.gMapContext.map;
23884             options.radius = radius;
23885             options.center = center;
23886             this.gMapContext.circle = new google.maps.Circle(options);
23887             return this.gMapContext.circle;
23888         }
23889         
23890         return null;
23891     },
23892     
23893     setPosition: function(location) 
23894     {
23895         this.gMapContext.location = location;
23896         this.gMapContext.marker.setPosition(location);
23897         this.gMapContext.map.panTo(location);
23898         this.drawCircle(location, this.gMapContext.radius, {});
23899         
23900         var _this = this;
23901         
23902         if (this.gMapContext.settings.enableReverseGeocode) {
23903             this.gMapContext.geodecoder.geocode({
23904                 latLng: this.gMapContext.location
23905             }, function(results, status) {
23906                 
23907                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
23908                     _this.gMapContext.locationName = results[0].formatted_address;
23909                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
23910                     
23911                     _this.fireEvent('positionchanged', this, location);
23912                 }
23913             });
23914             
23915             return;
23916         }
23917         
23918         this.fireEvent('positionchanged', this, location);
23919     },
23920     
23921     resize: function()
23922     {
23923         google.maps.event.trigger(this.gMapContext.map, "resize");
23924         
23925         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
23926         
23927         this.fireEvent('resize', this);
23928     },
23929     
23930     setPositionByLatLng: function(latitude, longitude)
23931     {
23932         this.setPosition(new google.maps.LatLng(latitude, longitude));
23933     },
23934     
23935     getCurrentPosition: function() 
23936     {
23937         return {
23938             latitude: this.gMapContext.location.lat(),
23939             longitude: this.gMapContext.location.lng()
23940         };
23941     },
23942     
23943     getAddressName: function() 
23944     {
23945         return this.gMapContext.locationName;
23946     },
23947     
23948     getAddressComponents: function() 
23949     {
23950         return this.gMapContext.addressComponents;
23951     },
23952     
23953     address_component_from_google_geocode: function(address_components) 
23954     {
23955         var result = {};
23956         
23957         for (var i = 0; i < address_components.length; i++) {
23958             var component = address_components[i];
23959             if (component.types.indexOf("postal_code") >= 0) {
23960                 result.postalCode = component.short_name;
23961             } else if (component.types.indexOf("street_number") >= 0) {
23962                 result.streetNumber = component.short_name;
23963             } else if (component.types.indexOf("route") >= 0) {
23964                 result.streetName = component.short_name;
23965             } else if (component.types.indexOf("neighborhood") >= 0) {
23966                 result.city = component.short_name;
23967             } else if (component.types.indexOf("locality") >= 0) {
23968                 result.city = component.short_name;
23969             } else if (component.types.indexOf("sublocality") >= 0) {
23970                 result.district = component.short_name;
23971             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
23972                 result.stateOrProvince = component.short_name;
23973             } else if (component.types.indexOf("country") >= 0) {
23974                 result.country = component.short_name;
23975             }
23976         }
23977         
23978         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
23979         result.addressLine2 = "";
23980         return result;
23981     },
23982     
23983     setZoomLevel: function(zoom)
23984     {
23985         this.gMapContext.map.setZoom(zoom);
23986     },
23987     
23988     show: function()
23989     {
23990         if(!this.el){
23991             return;
23992         }
23993         
23994         this.el.show();
23995         
23996         this.resize();
23997         
23998         this.fireEvent('show', this);
23999     },
24000     
24001     hide: function()
24002     {
24003         if(!this.el){
24004             return;
24005         }
24006         
24007         this.el.hide();
24008         
24009         this.fireEvent('hide', this);
24010     }
24011     
24012 });
24013
24014 Roo.apply(Roo.bootstrap.LocationPicker, {
24015     
24016     OverlayView : function(map, options)
24017     {
24018         options = options || {};
24019         
24020         this.setMap(map);
24021     }
24022     
24023     
24024 });/*
24025  * - LGPL
24026  *
24027  * Alert
24028  * 
24029  */
24030
24031 /**
24032  * @class Roo.bootstrap.Alert
24033  * @extends Roo.bootstrap.Component
24034  * Bootstrap Alert class
24035  * @cfg {String} title The title of alert
24036  * @cfg {String} html The content of alert
24037  * @cfg {String} weight (  success | info | warning | danger )
24038  * @cfg {String} faicon font-awesomeicon
24039  * 
24040  * @constructor
24041  * Create a new alert
24042  * @param {Object} config The config object
24043  */
24044
24045
24046 Roo.bootstrap.Alert = function(config){
24047     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24048     
24049 };
24050
24051 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24052     
24053     title: '',
24054     html: '',
24055     weight: false,
24056     faicon: false,
24057     
24058     getAutoCreate : function()
24059     {
24060         
24061         var cfg = {
24062             tag : 'div',
24063             cls : 'alert',
24064             cn : [
24065                 {
24066                     tag : 'i',
24067                     cls : 'roo-alert-icon'
24068                     
24069                 },
24070                 {
24071                     tag : 'b',
24072                     cls : 'roo-alert-title',
24073                     html : this.title
24074                 },
24075                 {
24076                     tag : 'span',
24077                     cls : 'roo-alert-text',
24078                     html : this.html
24079                 }
24080             ]
24081         };
24082         
24083         if(this.faicon){
24084             cfg.cn[0].cls += ' fa ' + this.faicon;
24085         }
24086         
24087         if(this.weight){
24088             cfg.cls += ' alert-' + this.weight;
24089         }
24090         
24091         return cfg;
24092     },
24093     
24094     initEvents: function() 
24095     {
24096         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24097     },
24098     
24099     setTitle : function(str)
24100     {
24101         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24102     },
24103     
24104     setText : function(str)
24105     {
24106         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24107     },
24108     
24109     setWeight : function(weight)
24110     {
24111         if(this.weight){
24112             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24113         }
24114         
24115         this.weight = weight;
24116         
24117         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24118     },
24119     
24120     setIcon : function(icon)
24121     {
24122         if(this.faicon){
24123             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24124         }
24125         
24126         this.faicon = icon
24127         
24128         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24129     },
24130     
24131     hide: function() 
24132     {
24133         this.el.hide();   
24134     },
24135     
24136     show: function() 
24137     {  
24138         this.el.show();   
24139     }
24140     
24141 });
24142
24143  
24144 /*
24145 * Licence: LGPL
24146 */
24147
24148 /**
24149  * @class Roo.bootstrap.UploadCropbox
24150  * @extends Roo.bootstrap.Component
24151  * Bootstrap UploadCropbox class
24152  * @cfg {String} emptyText show when image has been loaded
24153  * @cfg {String} rotateNotify show when image too small to rotate
24154  * @cfg {Number} errorTimeout default 3000
24155  * @cfg {Number} minWidth default 300
24156  * @cfg {Number} minHeight default 300
24157  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24158  * @cfg {Boolean} isDocument (true|false) default false
24159  * @cfg {String} url action url
24160  * @cfg {String} paramName default 'imageUpload'
24161  * @cfg {String} method default POST
24162  * 
24163  * @constructor
24164  * Create a new UploadCropbox
24165  * @param {Object} config The config object
24166  */
24167
24168 Roo.bootstrap.UploadCropbox = function(config){
24169     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24170     
24171     this.addEvents({
24172         /**
24173          * @event beforeselectfile
24174          * Fire before select file
24175          * @param {Roo.bootstrap.UploadCropbox} this
24176          */
24177         "beforeselectfile" : true,
24178         /**
24179          * @event initial
24180          * Fire after initEvent
24181          * @param {Roo.bootstrap.UploadCropbox} this
24182          */
24183         "initial" : true,
24184         /**
24185          * @event crop
24186          * Fire after initEvent
24187          * @param {Roo.bootstrap.UploadCropbox} this
24188          * @param {String} data
24189          */
24190         "crop" : true,
24191         /**
24192          * @event prepare
24193          * Fire when preparing the file data
24194          * @param {Roo.bootstrap.UploadCropbox} this
24195          * @param {Object} file
24196          */
24197         "prepare" : true,
24198         /**
24199          * @event exception
24200          * Fire when get exception
24201          * @param {Roo.bootstrap.UploadCropbox} this
24202          * @param {XMLHttpRequest} xhr
24203          */
24204         "exception" : true,
24205         /**
24206          * @event beforeloadcanvas
24207          * Fire before load the canvas
24208          * @param {Roo.bootstrap.UploadCropbox} this
24209          * @param {String} src
24210          */
24211         "beforeloadcanvas" : true,
24212         /**
24213          * @event trash
24214          * Fire when trash image
24215          * @param {Roo.bootstrap.UploadCropbox} this
24216          */
24217         "trash" : true,
24218         /**
24219          * @event download
24220          * Fire when download the image
24221          * @param {Roo.bootstrap.UploadCropbox} this
24222          */
24223         "download" : true,
24224         /**
24225          * @event footerbuttonclick
24226          * Fire when footerbuttonclick
24227          * @param {Roo.bootstrap.UploadCropbox} this
24228          * @param {String} type
24229          */
24230         "footerbuttonclick" : true,
24231         /**
24232          * @event resize
24233          * Fire when resize
24234          * @param {Roo.bootstrap.UploadCropbox} this
24235          */
24236         "resize" : true,
24237         /**
24238          * @event rotate
24239          * Fire when rotate the image
24240          * @param {Roo.bootstrap.UploadCropbox} this
24241          * @param {String} pos
24242          */
24243         "rotate" : true,
24244         /**
24245          * @event inspect
24246          * Fire when inspect the file
24247          * @param {Roo.bootstrap.UploadCropbox} this
24248          * @param {Object} file
24249          */
24250         "inspect" : true,
24251         /**
24252          * @event upload
24253          * Fire when xhr upload the file
24254          * @param {Roo.bootstrap.UploadCropbox} this
24255          * @param {Object} data
24256          */
24257         "upload" : true,
24258         /**
24259          * @event arrange
24260          * Fire when arrange the file data
24261          * @param {Roo.bootstrap.UploadCropbox} this
24262          * @param {Object} formData
24263          */
24264         "arrange" : true
24265     });
24266     
24267     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24268 };
24269
24270 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24271     
24272     emptyText : 'Click to upload image',
24273     rotateNotify : 'Image is too small to rotate',
24274     errorTimeout : 3000,
24275     scale : 0,
24276     baseScale : 1,
24277     rotate : 0,
24278     dragable : false,
24279     pinching : false,
24280     mouseX : 0,
24281     mouseY : 0,
24282     cropData : false,
24283     minWidth : 300,
24284     minHeight : 300,
24285     file : false,
24286     exif : {},
24287     baseRotate : 1,
24288     cropType : 'image/jpeg',
24289     buttons : false,
24290     canvasLoaded : false,
24291     isDocument : false,
24292     method : 'POST',
24293     paramName : 'imageUpload',
24294     
24295     getAutoCreate : function()
24296     {
24297         var cfg = {
24298             tag : 'div',
24299             cls : 'roo-upload-cropbox',
24300             cn : [
24301                 {
24302                     tag : 'input',
24303                     cls : 'roo-upload-cropbox-selector',
24304                     type : 'file'
24305                 },
24306                 {
24307                     tag : 'div',
24308                     cls : 'roo-upload-cropbox-body',
24309                     style : 'cursor:pointer',
24310                     cn : [
24311                         {
24312                             tag : 'div',
24313                             cls : 'roo-upload-cropbox-preview'
24314                         },
24315                         {
24316                             tag : 'div',
24317                             cls : 'roo-upload-cropbox-thumb'
24318                         },
24319                         {
24320                             tag : 'div',
24321                             cls : 'roo-upload-cropbox-empty-notify',
24322                             html : this.emptyText
24323                         },
24324                         {
24325                             tag : 'div',
24326                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24327                             html : this.rotateNotify
24328                         }
24329                     ]
24330                 },
24331                 {
24332                     tag : 'div',
24333                     cls : 'roo-upload-cropbox-footer',
24334                     cn : {
24335                         tag : 'div',
24336                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24337                         cn : []
24338                     }
24339                 }
24340             ]
24341         };
24342         
24343         return cfg;
24344     },
24345     
24346     onRender : function(ct, position)
24347     {
24348         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24349         
24350         if (this.buttons.length) {
24351             
24352             Roo.each(this.buttons, function(bb) {
24353                 
24354                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24355                 
24356                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24357                 
24358             }, this);
24359         }
24360     },
24361     
24362     initEvents : function()
24363     {
24364         this.urlAPI = (window.createObjectURL && window) || 
24365                                 (window.URL && URL.revokeObjectURL && URL) || 
24366                                 (window.webkitURL && webkitURL);
24367                         
24368         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24369         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24370         
24371         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24372         this.selectorEl.hide();
24373         
24374         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24375         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24376         
24377         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24378         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24379         this.thumbEl.hide();
24380         
24381         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24382         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24383         
24384         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24385         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24386         this.errorEl.hide();
24387         
24388         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24389         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24390         this.footerEl.hide();
24391         
24392         this.setThumbBoxSize();
24393         
24394         this.bind();
24395         
24396         this.resize();
24397         
24398         this.fireEvent('initial', this);
24399     },
24400
24401     bind : function()
24402     {
24403         var _this = this;
24404         
24405         window.addEventListener("resize", function() { _this.resize(); } );
24406         
24407         this.bodyEl.on('click', this.beforeSelectFile, this);
24408         
24409         if(Roo.isTouch){
24410             this.bodyEl.on('touchstart', this.onTouchStart, this);
24411             this.bodyEl.on('touchmove', this.onTouchMove, this);
24412             this.bodyEl.on('touchend', this.onTouchEnd, this);
24413         }
24414         
24415         if(!Roo.isTouch){
24416             this.bodyEl.on('mousedown', this.onMouseDown, this);
24417             this.bodyEl.on('mousemove', this.onMouseMove, this);
24418             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24419             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24420             Roo.get(document).on('mouseup', this.onMouseUp, this);
24421         }
24422         
24423         this.selectorEl.on('change', this.onFileSelected, this);
24424     },
24425     
24426     reset : function()
24427     {    
24428         this.scale = 0;
24429         this.baseScale = 1;
24430         this.rotate = 0;
24431         this.baseRotate = 1;
24432         this.dragable = false;
24433         this.pinching = false;
24434         this.mouseX = 0;
24435         this.mouseY = 0;
24436         this.cropData = false;
24437         this.notifyEl.dom.innerHTML = this.emptyText;
24438         
24439         this.selectorEl.dom.value = '';
24440         
24441     },
24442     
24443     resize : function()
24444     {
24445         if(this.fireEvent('resize', this) != false){
24446             this.setThumbBoxPosition();
24447             this.setCanvasPosition();
24448         }
24449     },
24450     
24451     onFooterButtonClick : function(e, el, o, type)
24452     {
24453         switch (type) {
24454             case 'rotate-left' :
24455                 this.onRotateLeft(e);
24456                 break;
24457             case 'rotate-right' :
24458                 this.onRotateRight(e);
24459                 break;
24460             case 'picture' :
24461                 this.beforeSelectFile(e);
24462                 break;
24463             case 'trash' :
24464                 this.trash(e);
24465                 break;
24466             case 'crop' :
24467                 this.crop(e);
24468                 break;
24469             case 'download' :
24470                 this.download(e);
24471                 break;
24472             default :
24473                 break;
24474         }
24475         
24476         this.fireEvent('footerbuttonclick', this, type);
24477     },
24478     
24479     beforeSelectFile : function(e)
24480     {
24481         e.preventDefault();
24482         
24483         if(this.fireEvent('beforeselectfile', this) != false){
24484             this.selectorEl.dom.click();
24485         }
24486     },
24487     
24488     onFileSelected : function(e)
24489     {
24490         e.preventDefault();
24491         
24492         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24493             return;
24494         }
24495         
24496         var file = this.selectorEl.dom.files[0];
24497         
24498         if(this.fireEvent('inspect', this, file) != false){
24499             this.prepare(file);
24500         }
24501         
24502     },
24503     
24504     trash : function(e)
24505     {
24506         this.fireEvent('trash', this);
24507     },
24508     
24509     download : function(e)
24510     {
24511         this.fireEvent('download', this);
24512     },
24513     
24514     loadCanvas : function(src)
24515     {   
24516         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24517             
24518             this.reset();
24519             
24520             this.imageEl = document.createElement('img');
24521             
24522             var _this = this;
24523             
24524             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24525             
24526             this.imageEl.src = src;
24527         }
24528     },
24529     
24530     onLoadCanvas : function()
24531     {   
24532         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24533         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24534         
24535         this.bodyEl.un('click', this.beforeSelectFile, this);
24536         
24537         this.notifyEl.hide();
24538         this.thumbEl.show();
24539         this.footerEl.show();
24540         
24541         this.baseRotateLevel();
24542         
24543         if(this.isDocument){
24544             this.setThumbBoxSize();
24545         }
24546         
24547         this.setThumbBoxPosition();
24548         
24549         this.baseScaleLevel();
24550         
24551         this.draw();
24552         
24553         this.resize();
24554         
24555         this.canvasLoaded = true;
24556         
24557     },
24558     
24559     setCanvasPosition : function()
24560     {   
24561         if(!this.canvasEl){
24562             return;
24563         }
24564         
24565         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24566         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24567         
24568         this.previewEl.setLeft(pw);
24569         this.previewEl.setTop(ph);
24570         
24571     },
24572     
24573     onMouseDown : function(e)
24574     {   
24575         e.stopEvent();
24576         
24577         this.dragable = true;
24578         this.pinching = false;
24579         
24580         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24581             this.dragable = false;
24582             return;
24583         }
24584         
24585         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24586         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24587         
24588     },
24589     
24590     onMouseMove : function(e)
24591     {   
24592         e.stopEvent();
24593         
24594         if(!this.canvasLoaded){
24595             return;
24596         }
24597         
24598         if (!this.dragable){
24599             return;
24600         }
24601         
24602         var minX = Math.ceil(this.thumbEl.getLeft(true));
24603         var minY = Math.ceil(this.thumbEl.getTop(true));
24604         
24605         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24606         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24607         
24608         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24609         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24610         
24611         x = x - this.mouseX;
24612         y = y - this.mouseY;
24613         
24614         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
24615         var bgY = Math.ceil(y + this.previewEl.getTop(true));
24616         
24617         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
24618         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
24619         
24620         this.previewEl.setLeft(bgX);
24621         this.previewEl.setTop(bgY);
24622         
24623         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24624         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24625     },
24626     
24627     onMouseUp : function(e)
24628     {   
24629         e.stopEvent();
24630         
24631         this.dragable = false;
24632     },
24633     
24634     onMouseWheel : function(e)
24635     {   
24636         e.stopEvent();
24637         
24638         this.startScale = this.scale;
24639         
24640         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
24641         
24642         if(!this.zoomable()){
24643             this.scale = this.startScale;
24644             return;
24645         }
24646         
24647         this.draw();
24648         
24649         return;
24650     },
24651     
24652     zoomable : function()
24653     {
24654         var minScale = this.thumbEl.getWidth() / this.minWidth;
24655         
24656         if(this.minWidth < this.minHeight){
24657             minScale = this.thumbEl.getHeight() / this.minHeight;
24658         }
24659         
24660         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
24661         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
24662         
24663         if(
24664                 this.isDocument &&
24665                 (this.rotate == 0 || this.rotate == 180) && 
24666                 (
24667                     width > this.imageEl.OriginWidth || 
24668                     height > this.imageEl.OriginHeight ||
24669                     (width < this.minWidth && height < this.minHeight)
24670                 )
24671         ){
24672             return false;
24673         }
24674         
24675         if(
24676                 this.isDocument &&
24677                 (this.rotate == 90 || this.rotate == 270) && 
24678                 (
24679                     width > this.imageEl.OriginWidth || 
24680                     height > this.imageEl.OriginHeight ||
24681                     (width < this.minHeight && height < this.minWidth)
24682                 )
24683         ){
24684             return false;
24685         }
24686         
24687         if(
24688                 !this.isDocument &&
24689                 (this.rotate == 0 || this.rotate == 180) && 
24690                 (
24691                     width < this.minWidth || 
24692                     width > this.imageEl.OriginWidth || 
24693                     height < this.minHeight || 
24694                     height > this.imageEl.OriginHeight
24695                 )
24696         ){
24697             return false;
24698         }
24699         
24700         if(
24701                 !this.isDocument &&
24702                 (this.rotate == 90 || this.rotate == 270) && 
24703                 (
24704                     width < this.minHeight || 
24705                     width > this.imageEl.OriginWidth || 
24706                     height < this.minWidth || 
24707                     height > this.imageEl.OriginHeight
24708                 )
24709         ){
24710             return false;
24711         }
24712         
24713         return true;
24714         
24715     },
24716     
24717     onRotateLeft : function(e)
24718     {   
24719         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24720             
24721             var minScale = this.thumbEl.getWidth() / this.minWidth;
24722             
24723             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24724             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24725             
24726             this.startScale = this.scale;
24727             
24728             while (this.getScaleLevel() < minScale){
24729             
24730                 this.scale = this.scale + 1;
24731                 
24732                 if(!this.zoomable()){
24733                     break;
24734                 }
24735                 
24736                 if(
24737                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24738                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24739                 ){
24740                     continue;
24741                 }
24742                 
24743                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24744
24745                 this.draw();
24746                 
24747                 return;
24748             }
24749             
24750             this.scale = this.startScale;
24751             
24752             this.onRotateFail();
24753             
24754             return false;
24755         }
24756         
24757         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
24758
24759         if(this.isDocument){
24760             this.setThumbBoxSize();
24761             this.setThumbBoxPosition();
24762             this.setCanvasPosition();
24763         }
24764         
24765         this.draw();
24766         
24767         this.fireEvent('rotate', this, 'left');
24768         
24769     },
24770     
24771     onRotateRight : function(e)
24772     {
24773         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
24774             
24775             var minScale = this.thumbEl.getWidth() / this.minWidth;
24776         
24777             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
24778             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
24779             
24780             this.startScale = this.scale;
24781             
24782             while (this.getScaleLevel() < minScale){
24783             
24784                 this.scale = this.scale + 1;
24785                 
24786                 if(!this.zoomable()){
24787                     break;
24788                 }
24789                 
24790                 if(
24791                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
24792                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
24793                 ){
24794                     continue;
24795                 }
24796                 
24797                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24798
24799                 this.draw();
24800                 
24801                 return;
24802             }
24803             
24804             this.scale = this.startScale;
24805             
24806             this.onRotateFail();
24807             
24808             return false;
24809         }
24810         
24811         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
24812
24813         if(this.isDocument){
24814             this.setThumbBoxSize();
24815             this.setThumbBoxPosition();
24816             this.setCanvasPosition();
24817         }
24818         
24819         this.draw();
24820         
24821         this.fireEvent('rotate', this, 'right');
24822     },
24823     
24824     onRotateFail : function()
24825     {
24826         this.errorEl.show(true);
24827         
24828         var _this = this;
24829         
24830         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
24831     },
24832     
24833     draw : function()
24834     {
24835         this.previewEl.dom.innerHTML = '';
24836         
24837         var canvasEl = document.createElement("canvas");
24838         
24839         var contextEl = canvasEl.getContext("2d");
24840         
24841         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24842         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24843         var center = this.imageEl.OriginWidth / 2;
24844         
24845         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
24846             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24847             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24848             center = this.imageEl.OriginHeight / 2;
24849         }
24850         
24851         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
24852         
24853         contextEl.translate(center, center);
24854         contextEl.rotate(this.rotate * Math.PI / 180);
24855
24856         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24857         
24858         this.canvasEl = document.createElement("canvas");
24859         
24860         this.contextEl = this.canvasEl.getContext("2d");
24861         
24862         switch (this.rotate) {
24863             case 0 :
24864                 
24865                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24866                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24867                 
24868                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24869                 
24870                 break;
24871             case 90 : 
24872                 
24873                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24874                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24875                 
24876                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24877                     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);
24878                     break;
24879                 }
24880                 
24881                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24882                 
24883                 break;
24884             case 180 :
24885                 
24886                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
24887                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
24888                 
24889                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24890                     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);
24891                     break;
24892                 }
24893                 
24894                 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);
24895                 
24896                 break;
24897             case 270 :
24898                 
24899                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
24900                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
24901         
24902                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
24903                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
24904                     break;
24905                 }
24906                 
24907                 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);
24908                 
24909                 break;
24910             default : 
24911                 break;
24912         }
24913         
24914         this.previewEl.appendChild(this.canvasEl);
24915         
24916         this.setCanvasPosition();
24917     },
24918     
24919     crop : function()
24920     {
24921         if(!this.canvasLoaded){
24922             return;
24923         }
24924         
24925         var imageCanvas = document.createElement("canvas");
24926         
24927         var imageContext = imageCanvas.getContext("2d");
24928         
24929         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24930         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
24931         
24932         var center = imageCanvas.width / 2;
24933         
24934         imageContext.translate(center, center);
24935         
24936         imageContext.rotate(this.rotate * Math.PI / 180);
24937         
24938         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
24939         
24940         var canvas = document.createElement("canvas");
24941         
24942         var context = canvas.getContext("2d");
24943                 
24944         canvas.width = this.minWidth;
24945         canvas.height = this.minHeight;
24946
24947         switch (this.rotate) {
24948             case 0 :
24949                 
24950                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
24951                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
24952                 
24953                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24954                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24955                 
24956                 var targetWidth = this.minWidth - 2 * x;
24957                 var targetHeight = this.minHeight - 2 * y;
24958                 
24959                 var scale = 1;
24960                 
24961                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
24962                     scale = targetWidth / width;
24963                 }
24964                 
24965                 if(x > 0 && y == 0){
24966                     scale = targetHeight / height;
24967                 }
24968                 
24969                 if(x > 0 && y > 0){
24970                     scale = targetWidth / width;
24971                     
24972                     if(width < height){
24973                         scale = targetHeight / height;
24974                     }
24975                 }
24976                 
24977                 context.scale(scale, scale);
24978                 
24979                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
24980                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
24981
24982                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
24983                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
24984
24985                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
24986                 
24987                 break;
24988             case 90 : 
24989                 
24990                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
24991                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
24992                 
24993                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
24994                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
24995                 
24996                 var targetWidth = this.minWidth - 2 * x;
24997                 var targetHeight = this.minHeight - 2 * y;
24998                 
24999                 var scale = 1;
25000                 
25001                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25002                     scale = targetWidth / width;
25003                 }
25004                 
25005                 if(x > 0 && y == 0){
25006                     scale = targetHeight / height;
25007                 }
25008                 
25009                 if(x > 0 && y > 0){
25010                     scale = targetWidth / width;
25011                     
25012                     if(width < height){
25013                         scale = targetHeight / height;
25014                     }
25015                 }
25016                 
25017                 context.scale(scale, scale);
25018                 
25019                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25020                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25021
25022                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25023                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25024                 
25025                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25026                 
25027                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25028                 
25029                 break;
25030             case 180 :
25031                 
25032                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25033                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25034                 
25035                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25036                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25037                 
25038                 var targetWidth = this.minWidth - 2 * x;
25039                 var targetHeight = this.minHeight - 2 * y;
25040                 
25041                 var scale = 1;
25042                 
25043                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25044                     scale = targetWidth / width;
25045                 }
25046                 
25047                 if(x > 0 && y == 0){
25048                     scale = targetHeight / height;
25049                 }
25050                 
25051                 if(x > 0 && y > 0){
25052                     scale = targetWidth / width;
25053                     
25054                     if(width < height){
25055                         scale = targetHeight / height;
25056                     }
25057                 }
25058                 
25059                 context.scale(scale, scale);
25060                 
25061                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25062                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25063
25064                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25065                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25066
25067                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25068                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25069                 
25070                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25071                 
25072                 break;
25073             case 270 :
25074                 
25075                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25076                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25077                 
25078                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25079                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25080                 
25081                 var targetWidth = this.minWidth - 2 * x;
25082                 var targetHeight = this.minHeight - 2 * y;
25083                 
25084                 var scale = 1;
25085                 
25086                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25087                     scale = targetWidth / width;
25088                 }
25089                 
25090                 if(x > 0 && y == 0){
25091                     scale = targetHeight / height;
25092                 }
25093                 
25094                 if(x > 0 && y > 0){
25095                     scale = targetWidth / width;
25096                     
25097                     if(width < height){
25098                         scale = targetHeight / height;
25099                     }
25100                 }
25101                 
25102                 context.scale(scale, scale);
25103                 
25104                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25105                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25106
25107                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25108                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25109                 
25110                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25111                 
25112                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25113                 
25114                 break;
25115             default : 
25116                 break;
25117         }
25118         
25119         this.cropData = canvas.toDataURL(this.cropType);
25120         
25121         if(this.fireEvent('crop', this, this.cropData) !== false){
25122             this.process(this.file, this.cropData);
25123         }
25124         
25125         return;
25126         
25127     },
25128     
25129     setThumbBoxSize : function()
25130     {
25131         var width, height;
25132         
25133         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25134             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25135             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25136             
25137             this.minWidth = width;
25138             this.minHeight = height;
25139             
25140             if(this.rotate == 90 || this.rotate == 270){
25141                 this.minWidth = height;
25142                 this.minHeight = width;
25143             }
25144         }
25145         
25146         height = 300;
25147         width = Math.ceil(this.minWidth * height / this.minHeight);
25148         
25149         if(this.minWidth > this.minHeight){
25150             width = 300;
25151             height = Math.ceil(this.minHeight * width / this.minWidth);
25152         }
25153         
25154         this.thumbEl.setStyle({
25155             width : width + 'px',
25156             height : height + 'px'
25157         });
25158
25159         return;
25160             
25161     },
25162     
25163     setThumbBoxPosition : function()
25164     {
25165         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25166         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25167         
25168         this.thumbEl.setLeft(x);
25169         this.thumbEl.setTop(y);
25170         
25171     },
25172     
25173     baseRotateLevel : function()
25174     {
25175         this.baseRotate = 1;
25176         
25177         if(
25178                 typeof(this.exif) != 'undefined' &&
25179                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25180                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25181         ){
25182             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25183         }
25184         
25185         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25186         
25187     },
25188     
25189     baseScaleLevel : function()
25190     {
25191         var width, height;
25192         
25193         if(this.isDocument){
25194             
25195             if(this.baseRotate == 6 || this.baseRotate == 8){
25196             
25197                 height = this.thumbEl.getHeight();
25198                 this.baseScale = height / this.imageEl.OriginWidth;
25199
25200                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25201                     width = this.thumbEl.getWidth();
25202                     this.baseScale = width / this.imageEl.OriginHeight;
25203                 }
25204
25205                 return;
25206             }
25207
25208             height = this.thumbEl.getHeight();
25209             this.baseScale = height / this.imageEl.OriginHeight;
25210
25211             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25212                 width = this.thumbEl.getWidth();
25213                 this.baseScale = width / this.imageEl.OriginWidth;
25214             }
25215
25216             return;
25217         }
25218         
25219         if(this.baseRotate == 6 || this.baseRotate == 8){
25220             
25221             width = this.thumbEl.getHeight();
25222             this.baseScale = width / this.imageEl.OriginHeight;
25223             
25224             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25225                 height = this.thumbEl.getWidth();
25226                 this.baseScale = height / this.imageEl.OriginHeight;
25227             }
25228             
25229             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25230                 height = this.thumbEl.getWidth();
25231                 this.baseScale = height / this.imageEl.OriginHeight;
25232                 
25233                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25234                     width = this.thumbEl.getHeight();
25235                     this.baseScale = width / this.imageEl.OriginWidth;
25236                 }
25237             }
25238             
25239             return;
25240         }
25241         
25242         width = this.thumbEl.getWidth();
25243         this.baseScale = width / this.imageEl.OriginWidth;
25244         
25245         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25246             height = this.thumbEl.getHeight();
25247             this.baseScale = height / this.imageEl.OriginHeight;
25248         }
25249         
25250         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25251             
25252             height = this.thumbEl.getHeight();
25253             this.baseScale = height / this.imageEl.OriginHeight;
25254             
25255             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25256                 width = this.thumbEl.getWidth();
25257                 this.baseScale = width / this.imageEl.OriginWidth;
25258             }
25259             
25260         }
25261         
25262         return;
25263     },
25264     
25265     getScaleLevel : function()
25266     {
25267         return this.baseScale * Math.pow(1.1, this.scale);
25268     },
25269     
25270     onTouchStart : function(e)
25271     {
25272         if(!this.canvasLoaded){
25273             this.beforeSelectFile(e);
25274             return;
25275         }
25276         
25277         var touches = e.browserEvent.touches;
25278         
25279         if(!touches){
25280             return;
25281         }
25282         
25283         if(touches.length == 1){
25284             this.onMouseDown(e);
25285             return;
25286         }
25287         
25288         if(touches.length != 2){
25289             return;
25290         }
25291         
25292         var coords = [];
25293         
25294         for(var i = 0, finger; finger = touches[i]; i++){
25295             coords.push(finger.pageX, finger.pageY);
25296         }
25297         
25298         var x = Math.pow(coords[0] - coords[2], 2);
25299         var y = Math.pow(coords[1] - coords[3], 2);
25300         
25301         this.startDistance = Math.sqrt(x + y);
25302         
25303         this.startScale = this.scale;
25304         
25305         this.pinching = true;
25306         this.dragable = false;
25307         
25308     },
25309     
25310     onTouchMove : function(e)
25311     {
25312         if(!this.pinching && !this.dragable){
25313             return;
25314         }
25315         
25316         var touches = e.browserEvent.touches;
25317         
25318         if(!touches){
25319             return;
25320         }
25321         
25322         if(this.dragable){
25323             this.onMouseMove(e);
25324             return;
25325         }
25326         
25327         var coords = [];
25328         
25329         for(var i = 0, finger; finger = touches[i]; i++){
25330             coords.push(finger.pageX, finger.pageY);
25331         }
25332         
25333         var x = Math.pow(coords[0] - coords[2], 2);
25334         var y = Math.pow(coords[1] - coords[3], 2);
25335         
25336         this.endDistance = Math.sqrt(x + y);
25337         
25338         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25339         
25340         if(!this.zoomable()){
25341             this.scale = this.startScale;
25342             return;
25343         }
25344         
25345         this.draw();
25346         
25347     },
25348     
25349     onTouchEnd : function(e)
25350     {
25351         this.pinching = false;
25352         this.dragable = false;
25353         
25354     },
25355     
25356     process : function(file, crop)
25357     {
25358         this.xhr = new XMLHttpRequest();
25359         
25360         file.xhr = this.xhr;
25361
25362         this.xhr.open(this.method, this.url, true);
25363         
25364         var headers = {
25365             "Accept": "application/json",
25366             "Cache-Control": "no-cache",
25367             "X-Requested-With": "XMLHttpRequest"
25368         };
25369         
25370         for (var headerName in headers) {
25371             var headerValue = headers[headerName];
25372             if (headerValue) {
25373                 this.xhr.setRequestHeader(headerName, headerValue);
25374             }
25375         }
25376         
25377         var _this = this;
25378         
25379         this.xhr.onload = function()
25380         {
25381             _this.xhrOnLoad(_this.xhr);
25382         }
25383         
25384         this.xhr.onerror = function()
25385         {
25386             _this.xhrOnError(_this.xhr);
25387         }
25388         
25389         var formData = new FormData();
25390
25391         formData.append('returnHTML', 'NO');
25392         
25393         if(crop){
25394             formData.append('crop', crop);
25395         }
25396         
25397         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25398             formData.append(this.paramName, file, file.name);
25399         }
25400         
25401         if(typeof(file.filename) != 'undefined'){
25402             formData.append('filename', file.filename);
25403         }
25404         
25405         if(typeof(file.mimetype) != 'undefined'){
25406             formData.append('mimetype', file.mimetype);
25407         }
25408         
25409         if(this.fireEvent('arrange', this, formData) != false){
25410             this.xhr.send(formData);
25411         };
25412     },
25413     
25414     xhrOnLoad : function(xhr)
25415     {
25416         if (xhr.readyState !== 4) {
25417             this.fireEvent('exception', this, xhr);
25418             return;
25419         }
25420
25421         var response = Roo.decode(xhr.responseText);
25422         
25423         if(!response.success){
25424             this.fireEvent('exception', this, xhr);
25425             return;
25426         }
25427         
25428         var response = Roo.decode(xhr.responseText);
25429         
25430         this.fireEvent('upload', this, response);
25431         
25432     },
25433     
25434     xhrOnError : function()
25435     {
25436         Roo.log('xhr on error');
25437         
25438         var response = Roo.decode(xhr.responseText);
25439           
25440         Roo.log(response);
25441         
25442     },
25443     
25444     prepare : function(file)
25445     {   
25446         this.file = false;
25447         this.exif = {};
25448         
25449         if(typeof(file) === 'string'){
25450             this.loadCanvas(file);
25451             return;
25452         }
25453         
25454         if(!file || !this.urlAPI){
25455             return;
25456         }
25457         
25458         this.file = file;
25459         this.cropType = file.type;
25460         
25461         var _this = this;
25462         
25463         if(this.fireEvent('prepare', this, this.file) != false){
25464             
25465             var reader = new FileReader();
25466             
25467             reader.onload = function (e) {
25468                 if (e.target.error) {
25469                     Roo.log(e.target.error);
25470                     return;
25471                 }
25472                 
25473                 var buffer = e.target.result,
25474                     dataView = new DataView(buffer),
25475                     offset = 2,
25476                     maxOffset = dataView.byteLength - 4,
25477                     markerBytes,
25478                     markerLength;
25479                 
25480                 if (dataView.getUint16(0) === 0xffd8) {
25481                     while (offset < maxOffset) {
25482                         markerBytes = dataView.getUint16(offset);
25483                         
25484                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25485                             markerLength = dataView.getUint16(offset + 2) + 2;
25486                             if (offset + markerLength > dataView.byteLength) {
25487                                 Roo.log('Invalid meta data: Invalid segment size.');
25488                                 break;
25489                             }
25490                             
25491                             if(markerBytes == 0xffe1){
25492                                 _this.parseExifData(
25493                                     dataView,
25494                                     offset,
25495                                     markerLength
25496                                 );
25497                             }
25498                             
25499                             offset += markerLength;
25500                             
25501                             continue;
25502                         }
25503                         
25504                         break;
25505                     }
25506                     
25507                 }
25508                 
25509                 var url = _this.urlAPI.createObjectURL(_this.file);
25510                 
25511                 _this.loadCanvas(url);
25512                 
25513                 return;
25514             }
25515             
25516             reader.readAsArrayBuffer(this.file);
25517             
25518         }
25519         
25520     },
25521     
25522     parseExifData : function(dataView, offset, length)
25523     {
25524         var tiffOffset = offset + 10,
25525             littleEndian,
25526             dirOffset;
25527     
25528         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25529             // No Exif data, might be XMP data instead
25530             return;
25531         }
25532         
25533         // Check for the ASCII code for "Exif" (0x45786966):
25534         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25535             // No Exif data, might be XMP data instead
25536             return;
25537         }
25538         if (tiffOffset + 8 > dataView.byteLength) {
25539             Roo.log('Invalid Exif data: Invalid segment size.');
25540             return;
25541         }
25542         // Check for the two null bytes:
25543         if (dataView.getUint16(offset + 8) !== 0x0000) {
25544             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25545             return;
25546         }
25547         // Check the byte alignment:
25548         switch (dataView.getUint16(tiffOffset)) {
25549         case 0x4949:
25550             littleEndian = true;
25551             break;
25552         case 0x4D4D:
25553             littleEndian = false;
25554             break;
25555         default:
25556             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25557             return;
25558         }
25559         // Check for the TIFF tag marker (0x002A):
25560         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25561             Roo.log('Invalid Exif data: Missing TIFF marker.');
25562             return;
25563         }
25564         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25565         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25566         
25567         this.parseExifTags(
25568             dataView,
25569             tiffOffset,
25570             tiffOffset + dirOffset,
25571             littleEndian
25572         );
25573     },
25574     
25575     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25576     {
25577         var tagsNumber,
25578             dirEndOffset,
25579             i;
25580         if (dirOffset + 6 > dataView.byteLength) {
25581             Roo.log('Invalid Exif data: Invalid directory offset.');
25582             return;
25583         }
25584         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25585         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25586         if (dirEndOffset + 4 > dataView.byteLength) {
25587             Roo.log('Invalid Exif data: Invalid directory size.');
25588             return;
25589         }
25590         for (i = 0; i < tagsNumber; i += 1) {
25591             this.parseExifTag(
25592                 dataView,
25593                 tiffOffset,
25594                 dirOffset + 2 + 12 * i, // tag offset
25595                 littleEndian
25596             );
25597         }
25598         // Return the offset to the next directory:
25599         return dataView.getUint32(dirEndOffset, littleEndian);
25600     },
25601     
25602     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
25603     {
25604         var tag = dataView.getUint16(offset, littleEndian);
25605         
25606         this.exif[tag] = this.getExifValue(
25607             dataView,
25608             tiffOffset,
25609             offset,
25610             dataView.getUint16(offset + 2, littleEndian), // tag type
25611             dataView.getUint32(offset + 4, littleEndian), // tag length
25612             littleEndian
25613         );
25614     },
25615     
25616     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
25617     {
25618         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
25619             tagSize,
25620             dataOffset,
25621             values,
25622             i,
25623             str,
25624             c;
25625     
25626         if (!tagType) {
25627             Roo.log('Invalid Exif data: Invalid tag type.');
25628             return;
25629         }
25630         
25631         tagSize = tagType.size * length;
25632         // Determine if the value is contained in the dataOffset bytes,
25633         // or if the value at the dataOffset is a pointer to the actual data:
25634         dataOffset = tagSize > 4 ?
25635                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
25636         if (dataOffset + tagSize > dataView.byteLength) {
25637             Roo.log('Invalid Exif data: Invalid data offset.');
25638             return;
25639         }
25640         if (length === 1) {
25641             return tagType.getValue(dataView, dataOffset, littleEndian);
25642         }
25643         values = [];
25644         for (i = 0; i < length; i += 1) {
25645             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
25646         }
25647         
25648         if (tagType.ascii) {
25649             str = '';
25650             // Concatenate the chars:
25651             for (i = 0; i < values.length; i += 1) {
25652                 c = values[i];
25653                 // Ignore the terminating NULL byte(s):
25654                 if (c === '\u0000') {
25655                     break;
25656                 }
25657                 str += c;
25658             }
25659             return str;
25660         }
25661         return values;
25662     }
25663     
25664 });
25665
25666 Roo.apply(Roo.bootstrap.UploadCropbox, {
25667     tags : {
25668         'Orientation': 0x0112
25669     },
25670     
25671     Orientation: {
25672             1: 0, //'top-left',
25673 //            2: 'top-right',
25674             3: 180, //'bottom-right',
25675 //            4: 'bottom-left',
25676 //            5: 'left-top',
25677             6: 90, //'right-top',
25678 //            7: 'right-bottom',
25679             8: 270 //'left-bottom'
25680     },
25681     
25682     exifTagTypes : {
25683         // byte, 8-bit unsigned int:
25684         1: {
25685             getValue: function (dataView, dataOffset) {
25686                 return dataView.getUint8(dataOffset);
25687             },
25688             size: 1
25689         },
25690         // ascii, 8-bit byte:
25691         2: {
25692             getValue: function (dataView, dataOffset) {
25693                 return String.fromCharCode(dataView.getUint8(dataOffset));
25694             },
25695             size: 1,
25696             ascii: true
25697         },
25698         // short, 16 bit int:
25699         3: {
25700             getValue: function (dataView, dataOffset, littleEndian) {
25701                 return dataView.getUint16(dataOffset, littleEndian);
25702             },
25703             size: 2
25704         },
25705         // long, 32 bit int:
25706         4: {
25707             getValue: function (dataView, dataOffset, littleEndian) {
25708                 return dataView.getUint32(dataOffset, littleEndian);
25709             },
25710             size: 4
25711         },
25712         // rational = two long values, first is numerator, second is denominator:
25713         5: {
25714             getValue: function (dataView, dataOffset, littleEndian) {
25715                 return dataView.getUint32(dataOffset, littleEndian) /
25716                     dataView.getUint32(dataOffset + 4, littleEndian);
25717             },
25718             size: 8
25719         },
25720         // slong, 32 bit signed int:
25721         9: {
25722             getValue: function (dataView, dataOffset, littleEndian) {
25723                 return dataView.getInt32(dataOffset, littleEndian);
25724             },
25725             size: 4
25726         },
25727         // srational, two slongs, first is numerator, second is denominator:
25728         10: {
25729             getValue: function (dataView, dataOffset, littleEndian) {
25730                 return dataView.getInt32(dataOffset, littleEndian) /
25731                     dataView.getInt32(dataOffset + 4, littleEndian);
25732             },
25733             size: 8
25734         }
25735     },
25736     
25737     footer : {
25738         STANDARD : [
25739             {
25740                 tag : 'div',
25741                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25742                 action : 'rotate-left',
25743                 cn : [
25744                     {
25745                         tag : 'button',
25746                         cls : 'btn btn-default',
25747                         html : '<i class="fa fa-undo"></i>'
25748                     }
25749                 ]
25750             },
25751             {
25752                 tag : 'div',
25753                 cls : 'btn-group roo-upload-cropbox-picture',
25754                 action : 'picture',
25755                 cn : [
25756                     {
25757                         tag : 'button',
25758                         cls : 'btn btn-default',
25759                         html : '<i class="fa fa-picture-o"></i>'
25760                     }
25761                 ]
25762             },
25763             {
25764                 tag : 'div',
25765                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25766                 action : 'rotate-right',
25767                 cn : [
25768                     {
25769                         tag : 'button',
25770                         cls : 'btn btn-default',
25771                         html : '<i class="fa fa-repeat"></i>'
25772                     }
25773                 ]
25774             }
25775         ],
25776         DOCUMENT : [
25777             {
25778                 tag : 'div',
25779                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25780                 action : 'rotate-left',
25781                 cn : [
25782                     {
25783                         tag : 'button',
25784                         cls : 'btn btn-default',
25785                         html : '<i class="fa fa-undo"></i>'
25786                     }
25787                 ]
25788             },
25789             {
25790                 tag : 'div',
25791                 cls : 'btn-group roo-upload-cropbox-download',
25792                 action : 'download',
25793                 cn : [
25794                     {
25795                         tag : 'button',
25796                         cls : 'btn btn-default',
25797                         html : '<i class="fa fa-download"></i>'
25798                     }
25799                 ]
25800             },
25801             {
25802                 tag : 'div',
25803                 cls : 'btn-group roo-upload-cropbox-crop',
25804                 action : 'crop',
25805                 cn : [
25806                     {
25807                         tag : 'button',
25808                         cls : 'btn btn-default',
25809                         html : '<i class="fa fa-crop"></i>'
25810                     }
25811                 ]
25812             },
25813             {
25814                 tag : 'div',
25815                 cls : 'btn-group roo-upload-cropbox-trash',
25816                 action : 'trash',
25817                 cn : [
25818                     {
25819                         tag : 'button',
25820                         cls : 'btn btn-default',
25821                         html : '<i class="fa fa-trash"></i>'
25822                     }
25823                 ]
25824             },
25825             {
25826                 tag : 'div',
25827                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25828                 action : 'rotate-right',
25829                 cn : [
25830                     {
25831                         tag : 'button',
25832                         cls : 'btn btn-default',
25833                         html : '<i class="fa fa-repeat"></i>'
25834                     }
25835                 ]
25836             }
25837         ],
25838         ROTATOR : [
25839             {
25840                 tag : 'div',
25841                 cls : 'btn-group roo-upload-cropbox-rotate-left',
25842                 action : 'rotate-left',
25843                 cn : [
25844                     {
25845                         tag : 'button',
25846                         cls : 'btn btn-default',
25847                         html : '<i class="fa fa-undo"></i>'
25848                     }
25849                 ]
25850             },
25851             {
25852                 tag : 'div',
25853                 cls : 'btn-group roo-upload-cropbox-rotate-right',
25854                 action : 'rotate-right',
25855                 cn : [
25856                     {
25857                         tag : 'button',
25858                         cls : 'btn btn-default',
25859                         html : '<i class="fa fa-repeat"></i>'
25860                     }
25861                 ]
25862             }
25863         ]
25864     }
25865 });
25866
25867 /*
25868 * Licence: LGPL
25869 */
25870
25871 /**
25872  * @class Roo.bootstrap.DocumentManager
25873  * @extends Roo.bootstrap.Component
25874  * Bootstrap DocumentManager class
25875  * @cfg {String} paramName default 'imageUpload'
25876  * @cfg {String} method default POST
25877  * @cfg {String} url action url
25878  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
25879  * @cfg {Boolean} multiple multiple upload default true
25880  * @cfg {Number} thumbSize default 300
25881  * @cfg {String} fieldLabel
25882  * @cfg {Number} labelWidth default 4
25883  * @cfg {String} labelAlign (left|top) default left
25884  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
25885  * 
25886  * @constructor
25887  * Create a new DocumentManager
25888  * @param {Object} config The config object
25889  */
25890
25891 Roo.bootstrap.DocumentManager = function(config){
25892     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
25893     
25894     this.addEvents({
25895         /**
25896          * @event initial
25897          * Fire when initial the DocumentManager
25898          * @param {Roo.bootstrap.DocumentManager} this
25899          */
25900         "initial" : true,
25901         /**
25902          * @event inspect
25903          * inspect selected file
25904          * @param {Roo.bootstrap.DocumentManager} this
25905          * @param {File} file
25906          */
25907         "inspect" : true,
25908         /**
25909          * @event exception
25910          * Fire when xhr load exception
25911          * @param {Roo.bootstrap.DocumentManager} this
25912          * @param {XMLHttpRequest} xhr
25913          */
25914         "exception" : true,
25915         /**
25916          * @event prepare
25917          * prepare the form data
25918          * @param {Roo.bootstrap.DocumentManager} this
25919          * @param {Object} formData
25920          */
25921         "prepare" : true,
25922         /**
25923          * @event remove
25924          * Fire when remove the file
25925          * @param {Roo.bootstrap.DocumentManager} this
25926          * @param {Object} file
25927          */
25928         "remove" : true,
25929         /**
25930          * @event refresh
25931          * Fire after refresh the file
25932          * @param {Roo.bootstrap.DocumentManager} this
25933          */
25934         "refresh" : true,
25935         /**
25936          * @event click
25937          * Fire after click the image
25938          * @param {Roo.bootstrap.DocumentManager} this
25939          * @param {Object} file
25940          */
25941         "click" : true,
25942         /**
25943          * @event edit
25944          * Fire when upload a image and editable set to true
25945          * @param {Roo.bootstrap.DocumentManager} this
25946          * @param {Object} file
25947          */
25948         "edit" : true,
25949         /**
25950          * @event beforeselectfile
25951          * Fire before select file
25952          * @param {Roo.bootstrap.DocumentManager} this
25953          */
25954         "beforeselectfile" : true,
25955         /**
25956          * @event process
25957          * Fire before process file
25958          * @param {Roo.bootstrap.DocumentManager} this
25959          * @param {Object} file
25960          */
25961         "process" : true
25962         
25963     });
25964 };
25965
25966 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
25967     
25968     boxes : 0,
25969     inputName : '',
25970     thumbSize : 300,
25971     multiple : true,
25972     files : [],
25973     method : 'POST',
25974     url : '',
25975     paramName : 'imageUpload',
25976     fieldLabel : '',
25977     labelWidth : 4,
25978     labelAlign : 'left',
25979     editable : true,
25980     delegates : [],
25981     
25982     getAutoCreate : function()
25983     {   
25984         var managerWidget = {
25985             tag : 'div',
25986             cls : 'roo-document-manager',
25987             cn : [
25988                 {
25989                     tag : 'input',
25990                     cls : 'roo-document-manager-selector',
25991                     type : 'file'
25992                 },
25993                 {
25994                     tag : 'div',
25995                     cls : 'roo-document-manager-uploader',
25996                     cn : [
25997                         {
25998                             tag : 'div',
25999                             cls : 'roo-document-manager-upload-btn',
26000                             html : '<i class="fa fa-plus"></i>'
26001                         }
26002                     ]
26003                     
26004                 }
26005             ]
26006         };
26007         
26008         var content = [
26009             {
26010                 tag : 'div',
26011                 cls : 'column col-md-12',
26012                 cn : managerWidget
26013             }
26014         ];
26015         
26016         if(this.fieldLabel.length){
26017             
26018             content = [
26019                 {
26020                     tag : 'div',
26021                     cls : 'column col-md-12',
26022                     html : this.fieldLabel
26023                 },
26024                 {
26025                     tag : 'div',
26026                     cls : 'column col-md-12',
26027                     cn : managerWidget
26028                 }
26029             ];
26030
26031             if(this.labelAlign == 'left'){
26032                 content = [
26033                     {
26034                         tag : 'div',
26035                         cls : 'column col-md-' + this.labelWidth,
26036                         html : this.fieldLabel
26037                     },
26038                     {
26039                         tag : 'div',
26040                         cls : 'column col-md-' + (12 - this.labelWidth),
26041                         cn : managerWidget
26042                     }
26043                 ];
26044                 
26045             }
26046         }
26047         
26048         var cfg = {
26049             tag : 'div',
26050             cls : 'row clearfix',
26051             cn : content
26052         };
26053         
26054         return cfg;
26055         
26056     },
26057     
26058     initEvents : function()
26059     {
26060         this.managerEl = this.el.select('.roo-document-manager', true).first();
26061         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26062         
26063         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26064         this.selectorEl.hide();
26065         
26066         if(this.multiple){
26067             this.selectorEl.attr('multiple', 'multiple');
26068         }
26069         
26070         this.selectorEl.on('change', this.onFileSelected, this);
26071         
26072         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26073         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26074         
26075         this.uploader.on('click', this.onUploaderClick, this);
26076         
26077         this.renderProgressDialog();
26078         
26079         var _this = this;
26080         
26081         window.addEventListener("resize", function() { _this.refresh(); } );
26082         
26083         this.fireEvent('initial', this);
26084     },
26085     
26086     renderProgressDialog : function()
26087     {
26088         var _this = this;
26089         
26090         this.progressDialog = new Roo.bootstrap.Modal({
26091             cls : 'roo-document-manager-progress-dialog',
26092             allow_close : false,
26093             title : '',
26094             buttons : [
26095                 {
26096                     name  :'cancel',
26097                     weight : 'danger',
26098                     html : 'Cancel'
26099                 }
26100             ], 
26101             listeners : { 
26102                 btnclick : function() {
26103                     _this.uploadCancel();
26104                     this.hide();
26105                 }
26106             }
26107         });
26108          
26109         this.progressDialog.render(Roo.get(document.body));
26110          
26111         this.progress = new Roo.bootstrap.Progress({
26112             cls : 'roo-document-manager-progress',
26113             active : true,
26114             striped : true
26115         });
26116         
26117         this.progress.render(this.progressDialog.getChildContainer());
26118         
26119         this.progressBar = new Roo.bootstrap.ProgressBar({
26120             cls : 'roo-document-manager-progress-bar',
26121             aria_valuenow : 0,
26122             aria_valuemin : 0,
26123             aria_valuemax : 12,
26124             panel : 'success'
26125         });
26126         
26127         this.progressBar.render(this.progress.getChildContainer());
26128     },
26129     
26130     onUploaderClick : function(e)
26131     {
26132         e.preventDefault();
26133      
26134         if(this.fireEvent('beforeselectfile', this) != false){
26135             this.selectorEl.dom.click();
26136         }
26137         
26138     },
26139     
26140     onFileSelected : function(e)
26141     {
26142         e.preventDefault();
26143         
26144         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26145             return;
26146         }
26147         
26148         Roo.each(this.selectorEl.dom.files, function(file){
26149             if(this.fireEvent('inspect', this, file) != false){
26150                 this.files.push(file);
26151             }
26152         }, this);
26153         
26154         this.queue();
26155         
26156     },
26157     
26158     queue : function()
26159     {
26160         this.selectorEl.dom.value = '';
26161         
26162         if(!this.files.length){
26163             return;
26164         }
26165         
26166         if(this.boxes > 0 && this.files.length > this.boxes){
26167             this.files = this.files.slice(0, this.boxes);
26168         }
26169         
26170         this.uploader.show();
26171         
26172         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26173             this.uploader.hide();
26174         }
26175         
26176         var _this = this;
26177         
26178         var files = [];
26179         
26180         var docs = [];
26181         
26182         Roo.each(this.files, function(file){
26183             
26184             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26185                 var f = this.renderPreview(file);
26186                 files.push(f);
26187                 return;
26188             }
26189             
26190             if(file.type.indexOf('image') != -1){
26191                 this.delegates.push(
26192                     (function(){
26193                         _this.process(file);
26194                     }).createDelegate(this)
26195                 );
26196         
26197                 return;
26198             }
26199             
26200             docs.push(
26201                 (function(){
26202                     _this.process(file);
26203                 }).createDelegate(this)
26204             );
26205             
26206         }, this);
26207         
26208         this.files = files;
26209         
26210         this.delegates = this.delegates.concat(docs);
26211         
26212         if(!this.delegates.length){
26213             this.refresh();
26214             return;
26215         }
26216         
26217         this.progressBar.aria_valuemax = this.delegates.length;
26218         
26219         this.arrange();
26220         
26221         return;
26222     },
26223     
26224     arrange : function()
26225     {
26226         if(!this.delegates.length){
26227             this.progressDialog.hide();
26228             this.refresh();
26229             return;
26230         }
26231         
26232         var delegate = this.delegates.shift();
26233         
26234         this.progressDialog.show();
26235         
26236         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26237         
26238         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26239         
26240         delegate();
26241     },
26242     
26243     refresh : function()
26244     {
26245         this.uploader.show();
26246         
26247         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26248             this.uploader.hide();
26249         }
26250         
26251         Roo.isTouch ? this.closable(false) : this.closable(true);
26252         
26253         this.fireEvent('refresh', this);
26254     },
26255     
26256     onRemove : function(e, el, o)
26257     {
26258         e.preventDefault();
26259         
26260         this.fireEvent('remove', this, o);
26261         
26262     },
26263     
26264     remove : function(o)
26265     {
26266         var files = [];
26267         
26268         Roo.each(this.files, function(file){
26269             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26270                 files.push(file);
26271                 return;
26272             }
26273
26274             o.target.remove();
26275
26276         }, this);
26277         
26278         this.files = files;
26279         
26280         this.refresh();
26281     },
26282     
26283     clear : function()
26284     {
26285         Roo.each(this.files, function(file){
26286             if(!file.target){
26287                 return;
26288             }
26289             
26290             file.target.remove();
26291
26292         }, this);
26293         
26294         this.files = [];
26295         
26296         this.refresh();
26297     },
26298     
26299     onClick : function(e, el, o)
26300     {
26301         e.preventDefault();
26302         
26303         this.fireEvent('click', this, o);
26304         
26305     },
26306     
26307     closable : function(closable)
26308     {
26309         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26310             
26311             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26312             
26313             if(closable){
26314                 el.show();
26315                 return;
26316             }
26317             
26318             el.hide();
26319             
26320         }, this);
26321     },
26322     
26323     xhrOnLoad : function(xhr)
26324     {
26325         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26326             el.remove();
26327         }, this);
26328         
26329         if (xhr.readyState !== 4) {
26330             this.arrange();
26331             this.fireEvent('exception', this, xhr);
26332             return;
26333         }
26334
26335         var response = Roo.decode(xhr.responseText);
26336         
26337         if(!response.success){
26338             this.arrange();
26339             this.fireEvent('exception', this, xhr);
26340             return;
26341         }
26342         
26343         var file = this.renderPreview(response.data);
26344         
26345         this.files.push(file);
26346         
26347         this.arrange();
26348         
26349     },
26350     
26351     xhrOnError : function()
26352     {
26353         Roo.log('xhr on error');
26354         
26355         var response = Roo.decode(xhr.responseText);
26356           
26357         Roo.log(response);
26358         
26359         this.arrange();
26360     },
26361     
26362     process : function(file)
26363     {
26364         if(this.fireEvent('process', this, file) !== false){
26365             if(this.editable && file.type.indexOf('image') != -1){
26366                 this.fireEvent('edit', this, file);
26367                 return;
26368             }
26369
26370             this.uploadStart(file, false);
26371
26372             return;
26373         }
26374         
26375     },
26376     
26377     uploadStart : function(file, crop)
26378     {
26379         this.xhr = new XMLHttpRequest();
26380         
26381         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26382             this.arrange();
26383             return;
26384         }
26385         
26386         file.xhr = this.xhr;
26387             
26388         this.managerEl.createChild({
26389             tag : 'div',
26390             cls : 'roo-document-manager-loading',
26391             cn : [
26392                 {
26393                     tag : 'div',
26394                     tooltip : file.name,
26395                     cls : 'roo-document-manager-thumb',
26396                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26397                 }
26398             ]
26399
26400         });
26401
26402         this.xhr.open(this.method, this.url, true);
26403         
26404         var headers = {
26405             "Accept": "application/json",
26406             "Cache-Control": "no-cache",
26407             "X-Requested-With": "XMLHttpRequest"
26408         };
26409         
26410         for (var headerName in headers) {
26411             var headerValue = headers[headerName];
26412             if (headerValue) {
26413                 this.xhr.setRequestHeader(headerName, headerValue);
26414             }
26415         }
26416         
26417         var _this = this;
26418         
26419         this.xhr.onload = function()
26420         {
26421             _this.xhrOnLoad(_this.xhr);
26422         }
26423         
26424         this.xhr.onerror = function()
26425         {
26426             _this.xhrOnError(_this.xhr);
26427         }
26428         
26429         var formData = new FormData();
26430
26431         formData.append('returnHTML', 'NO');
26432         
26433         if(crop){
26434             formData.append('crop', crop);
26435         }
26436         
26437         formData.append(this.paramName, file, file.name);
26438         
26439         if(this.fireEvent('prepare', this, formData) != false){
26440             this.xhr.send(formData);
26441         };
26442     },
26443     
26444     uploadCancel : function()
26445     {
26446         this.xhr.abort();
26447         
26448         this.delegates = [];
26449         
26450         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26451             el.remove();
26452         }, this);
26453         
26454         this.arrange();
26455     },
26456     
26457     renderPreview : function(file)
26458     {
26459         if(typeof(file.target) != 'undefined' && file.target){
26460             return file;
26461         }
26462         
26463         var previewEl = this.managerEl.createChild({
26464             tag : 'div',
26465             cls : 'roo-document-manager-preview',
26466             cn : [
26467                 {
26468                     tag : 'div',
26469                     tooltip : file.filename,
26470                     cls : 'roo-document-manager-thumb',
26471                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26472                 },
26473                 {
26474                     tag : 'button',
26475                     cls : 'close',
26476                     html : '<i class="fa fa-times-circle"></i>'
26477                 }
26478             ]
26479         });
26480
26481         var close = previewEl.select('button.close', true).first();
26482
26483         close.on('click', this.onRemove, this, file);
26484
26485         file.target = previewEl;
26486
26487         var image = previewEl.select('img', true).first();
26488         
26489         var _this = this;
26490         
26491         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26492         
26493         image.on('click', this.onClick, this, file);
26494         
26495         return file;
26496         
26497     },
26498     
26499     onPreviewLoad : function(file, image)
26500     {
26501         if(typeof(file.target) == 'undefined' || !file.target){
26502             return;
26503         }
26504         
26505         var width = image.dom.naturalWidth || image.dom.width;
26506         var height = image.dom.naturalHeight || image.dom.height;
26507         
26508         if(width > height){
26509             file.target.addClass('wide');
26510             return;
26511         }
26512         
26513         file.target.addClass('tall');
26514         return;
26515         
26516     },
26517     
26518     uploadFromSource : function(file, crop)
26519     {
26520         this.xhr = new XMLHttpRequest();
26521         
26522         this.managerEl.createChild({
26523             tag : 'div',
26524             cls : 'roo-document-manager-loading',
26525             cn : [
26526                 {
26527                     tag : 'div',
26528                     tooltip : file.name,
26529                     cls : 'roo-document-manager-thumb',
26530                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26531                 }
26532             ]
26533
26534         });
26535
26536         this.xhr.open(this.method, this.url, true);
26537         
26538         var headers = {
26539             "Accept": "application/json",
26540             "Cache-Control": "no-cache",
26541             "X-Requested-With": "XMLHttpRequest"
26542         };
26543         
26544         for (var headerName in headers) {
26545             var headerValue = headers[headerName];
26546             if (headerValue) {
26547                 this.xhr.setRequestHeader(headerName, headerValue);
26548             }
26549         }
26550         
26551         var _this = this;
26552         
26553         this.xhr.onload = function()
26554         {
26555             _this.xhrOnLoad(_this.xhr);
26556         }
26557         
26558         this.xhr.onerror = function()
26559         {
26560             _this.xhrOnError(_this.xhr);
26561         }
26562         
26563         var formData = new FormData();
26564
26565         formData.append('returnHTML', 'NO');
26566         
26567         formData.append('crop', crop);
26568         
26569         if(typeof(file.filename) != 'undefined'){
26570             formData.append('filename', file.filename);
26571         }
26572         
26573         if(typeof(file.mimetype) != 'undefined'){
26574             formData.append('mimetype', file.mimetype);
26575         }
26576         
26577         if(this.fireEvent('prepare', this, formData) != false){
26578             this.xhr.send(formData);
26579         };
26580     }
26581 });
26582
26583 /*
26584 * Licence: LGPL
26585 */
26586
26587 /**
26588  * @class Roo.bootstrap.DocumentViewer
26589  * @extends Roo.bootstrap.Component
26590  * Bootstrap DocumentViewer class
26591  * 
26592  * @constructor
26593  * Create a new DocumentViewer
26594  * @param {Object} config The config object
26595  */
26596
26597 Roo.bootstrap.DocumentViewer = function(config){
26598     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
26599     
26600     this.addEvents({
26601         /**
26602          * @event initial
26603          * Fire after initEvent
26604          * @param {Roo.bootstrap.DocumentViewer} this
26605          */
26606         "initial" : true,
26607         /**
26608          * @event click
26609          * Fire after click
26610          * @param {Roo.bootstrap.DocumentViewer} this
26611          */
26612         "click" : true,
26613         /**
26614          * @event trash
26615          * Fire after trash button
26616          * @param {Roo.bootstrap.DocumentViewer} this
26617          */
26618         "trash" : true
26619         
26620     });
26621 };
26622
26623 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
26624     
26625     getAutoCreate : function()
26626     {
26627         var cfg = {
26628             tag : 'div',
26629             cls : 'roo-document-viewer',
26630             cn : [
26631                 {
26632                     tag : 'div',
26633                     cls : 'roo-document-viewer-body',
26634                     cn : [
26635                         {
26636                             tag : 'div',
26637                             cls : 'roo-document-viewer-thumb',
26638                             cn : [
26639                                 {
26640                                     tag : 'img',
26641                                     cls : 'roo-document-viewer-image'
26642                                 }
26643                             ]
26644                         }
26645                     ]
26646                 },
26647                 {
26648                     tag : 'div',
26649                     cls : 'roo-document-viewer-footer',
26650                     cn : {
26651                         tag : 'div',
26652                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
26653                         cn : [
26654                             {
26655                                 tag : 'div',
26656                                 cls : 'btn-group',
26657                                 cn : [
26658                                     {
26659                                         tag : 'button',
26660                                         cls : 'btn btn-default roo-document-viewer-trash',
26661                                         html : '<i class="fa fa-trash"></i>'
26662                                     }
26663                                 ]
26664                             }
26665                         ]
26666                     }
26667                 }
26668             ]
26669         };
26670         
26671         return cfg;
26672     },
26673     
26674     initEvents : function()
26675     {
26676         
26677         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
26678         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26679         
26680         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
26681         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26682         
26683         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
26684         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26685         
26686         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
26687         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26688         
26689         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
26690         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26691         
26692         this.bodyEl.on('click', this.onClick, this);
26693         
26694         this.trashBtn.on('click', this.onTrash, this);
26695         
26696     },
26697     
26698     initial : function()
26699     {
26700 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
26701         
26702         
26703         this.fireEvent('initial', this);
26704         
26705     },
26706     
26707     onClick : function(e)
26708     {
26709         e.preventDefault();
26710         
26711         this.fireEvent('click', this);
26712     },
26713     
26714     onTrash : function(e)
26715     {
26716         e.preventDefault();
26717         
26718         this.fireEvent('trash', this);
26719     }
26720     
26721 });
26722 /*
26723  * - LGPL
26724  *
26725  * nav progress bar
26726  * 
26727  */
26728
26729 /**
26730  * @class Roo.bootstrap.NavProgressBar
26731  * @extends Roo.bootstrap.Component
26732  * Bootstrap NavProgressBar class
26733  * 
26734  * @constructor
26735  * Create a new nav progress bar
26736  * @param {Object} config The config object
26737  */
26738
26739 Roo.bootstrap.NavProgressBar = function(config){
26740     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
26741
26742     this.bullets = this.bullets || [];
26743    
26744 //    Roo.bootstrap.NavProgressBar.register(this);
26745      this.addEvents({
26746         /**
26747              * @event changed
26748              * Fires when the active item changes
26749              * @param {Roo.bootstrap.NavProgressBar} this
26750              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
26751              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
26752          */
26753         'changed': true
26754      });
26755     
26756 };
26757
26758 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
26759     
26760     bullets : [],
26761     barItems : [],
26762     
26763     getAutoCreate : function()
26764     {
26765         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
26766         
26767         cfg = {
26768             tag : 'div',
26769             cls : 'roo-navigation-bar-group',
26770             cn : [
26771                 {
26772                     tag : 'div',
26773                     cls : 'roo-navigation-top-bar'
26774                 },
26775                 {
26776                     tag : 'div',
26777                     cls : 'roo-navigation-bullets-bar',
26778                     cn : [
26779                         {
26780                             tag : 'ul',
26781                             cls : 'roo-navigation-bar'
26782                         }
26783                     ]
26784                 },
26785                 
26786                 {
26787                     tag : 'div',
26788                     cls : 'roo-navigation-bottom-bar'
26789                 }
26790             ]
26791             
26792         };
26793         
26794         return cfg;
26795         
26796     },
26797     
26798     initEvents: function() 
26799     {
26800         
26801     },
26802     
26803     onRender : function(ct, position) 
26804     {
26805         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
26806         
26807         if(this.bullets.length){
26808             Roo.each(this.bullets, function(b){
26809                this.addItem(b);
26810             }, this);
26811         }
26812         
26813         this.format();
26814         
26815     },
26816     
26817     addItem : function(cfg)
26818     {
26819         var item = new Roo.bootstrap.NavProgressItem(cfg);
26820         
26821         item.parentId = this.id;
26822         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
26823         
26824         if(cfg.html){
26825             var top = new Roo.bootstrap.Element({
26826                 tag : 'div',
26827                 cls : 'roo-navigation-bar-text'
26828             });
26829             
26830             var bottom = new Roo.bootstrap.Element({
26831                 tag : 'div',
26832                 cls : 'roo-navigation-bar-text'
26833             });
26834             
26835             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
26836             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
26837             
26838             var topText = new Roo.bootstrap.Element({
26839                 tag : 'span',
26840                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
26841             });
26842             
26843             var bottomText = new Roo.bootstrap.Element({
26844                 tag : 'span',
26845                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
26846             });
26847             
26848             topText.onRender(top.el, null);
26849             bottomText.onRender(bottom.el, null);
26850             
26851             item.topEl = top;
26852             item.bottomEl = bottom;
26853         }
26854         
26855         this.barItems.push(item);
26856         
26857         return item;
26858     },
26859     
26860     getActive : function()
26861     {
26862         var active = false;
26863         
26864         Roo.each(this.barItems, function(v){
26865             
26866             if (!v.isActive()) {
26867                 return;
26868             }
26869             
26870             active = v;
26871             return false;
26872             
26873         });
26874         
26875         return active;
26876     },
26877     
26878     setActiveItem : function(item)
26879     {
26880         var prev = false;
26881         
26882         Roo.each(this.barItems, function(v){
26883             if (v.rid == item.rid) {
26884                 return ;
26885             }
26886             
26887             if (v.isActive()) {
26888                 v.setActive(false);
26889                 prev = v;
26890             }
26891         });
26892
26893         item.setActive(true);
26894         
26895         this.fireEvent('changed', this, item, prev);
26896     },
26897     
26898     getBarItem: function(rid)
26899     {
26900         var ret = false;
26901         
26902         Roo.each(this.barItems, function(e) {
26903             if (e.rid != rid) {
26904                 return;
26905             }
26906             
26907             ret =  e;
26908             return false;
26909         });
26910         
26911         return ret;
26912     },
26913     
26914     indexOfItem : function(item)
26915     {
26916         var index = false;
26917         
26918         Roo.each(this.barItems, function(v, i){
26919             
26920             if (v.rid != item.rid) {
26921                 return;
26922             }
26923             
26924             index = i;
26925             return false
26926         });
26927         
26928         return index;
26929     },
26930     
26931     setActiveNext : function()
26932     {
26933         var i = this.indexOfItem(this.getActive());
26934         
26935         if (i > this.barItems.length) {
26936             return;
26937         }
26938         
26939         this.setActiveItem(this.barItems[i+1]);
26940     },
26941     
26942     setActivePrev : function()
26943     {
26944         var i = this.indexOfItem(this.getActive());
26945         
26946         if (i  < 1) {
26947             return;
26948         }
26949         
26950         this.setActiveItem(this.barItems[i-1]);
26951     },
26952     
26953     format : function()
26954     {
26955         if(!this.barItems.length){
26956             return;
26957         }
26958      
26959         var width = 100 / this.barItems.length;
26960         
26961         Roo.each(this.barItems, function(i){
26962             i.el.setStyle('width', width + '%');
26963             i.topEl.el.setStyle('width', width + '%');
26964             i.bottomEl.el.setStyle('width', width + '%');
26965         }, this);
26966         
26967     }
26968     
26969 });
26970 /*
26971  * - LGPL
26972  *
26973  * Nav Progress Item
26974  * 
26975  */
26976
26977 /**
26978  * @class Roo.bootstrap.NavProgressItem
26979  * @extends Roo.bootstrap.Component
26980  * Bootstrap NavProgressItem class
26981  * @cfg {String} rid the reference id
26982  * @cfg {Boolean} active (true|false) Is item active default false
26983  * @cfg {Boolean} disabled (true|false) Is item active default false
26984  * @cfg {String} html
26985  * @cfg {String} position (top|bottom) text position default bottom
26986  * @cfg {String} icon show icon instead of number
26987  * 
26988  * @constructor
26989  * Create a new NavProgressItem
26990  * @param {Object} config The config object
26991  */
26992 Roo.bootstrap.NavProgressItem = function(config){
26993     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
26994     this.addEvents({
26995         // raw events
26996         /**
26997          * @event click
26998          * The raw click event for the entire grid.
26999          * @param {Roo.bootstrap.NavProgressItem} this
27000          * @param {Roo.EventObject} e
27001          */
27002         "click" : true
27003     });
27004    
27005 };
27006
27007 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27008     
27009     rid : '',
27010     active : false,
27011     disabled : false,
27012     html : '',
27013     position : 'bottom',
27014     icon : false,
27015     
27016     getAutoCreate : function()
27017     {
27018         var iconCls = 'roo-navigation-bar-item-icon';
27019         
27020         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27021         
27022         var cfg = {
27023             tag: 'li',
27024             cls: 'roo-navigation-bar-item',
27025             cn : [
27026                 {
27027                     tag : 'i',
27028                     cls : iconCls
27029                 }
27030             ]
27031         }
27032         
27033         if(this.active){
27034             cfg.cls += ' active';
27035         }
27036         if(this.disabled){
27037             cfg.cls += ' disabled';
27038         }
27039         
27040         return cfg;
27041     },
27042     
27043     disable : function()
27044     {
27045         this.setDisabled(true);
27046     },
27047     
27048     enable : function()
27049     {
27050         this.setDisabled(false);
27051     },
27052     
27053     initEvents: function() 
27054     {
27055         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27056         
27057         this.iconEl.on('click', this.onClick, this);
27058     },
27059     
27060     onClick : function(e)
27061     {
27062         e.preventDefault();
27063         
27064         if(this.disabled){
27065             return;
27066         }
27067         
27068         if(this.fireEvent('click', this, e) === false){
27069             return;
27070         };
27071         
27072         this.parent().setActiveItem(this);
27073     },
27074     
27075     isActive: function () 
27076     {
27077         return this.active;
27078     },
27079     
27080     setActive : function(state)
27081     {
27082         if(this.active == state){
27083             return;
27084         }
27085         
27086         this.active = state;
27087         
27088         if (state) {
27089             this.el.addClass('active');
27090             return;
27091         }
27092         
27093         this.el.removeClass('active');
27094         
27095         return;
27096     },
27097     
27098     setDisabled : function(state)
27099     {
27100         if(this.disabled == state){
27101             return;
27102         }
27103         
27104         this.disabled = state;
27105         
27106         if (state) {
27107             this.el.addClass('disabled');
27108             return;
27109         }
27110         
27111         this.el.removeClass('disabled');
27112     },
27113     
27114     tooltipEl : function()
27115     {
27116         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27117     }
27118 });
27119  
27120
27121