Roo/bootstrap/Table.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * 
20  * @constructor
21  * Do not use directly - it does not do anything..
22  * @param {Object} config The config object
23  */
24
25
26
27 Roo.bootstrap.Component = function(config){
28     Roo.bootstrap.Component.superclass.constructor.call(this, config);
29 };
30
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
32     
33     
34     allowDomMove : false, // to stop relocations in parent onRender...
35     
36     cls : false,
37     
38     style : false,
39     
40     autoCreate : false,
41     
42     initEvents : function() {  },
43     
44     xattr : false,
45     
46     parentId : false,
47     
48     can_build_overlaid : true,
49     
50     dataId : false,
51     
52     name : false,
53     
54     parent: function() {
55         // returns the parent component..
56         return Roo.ComponentMgr.get(this.parentId)
57         
58         
59     },
60     
61     // private
62     onRender : function(ct, position)
63     {
64        // Roo.log("Call onRender: " + this.xtype);
65         
66         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
67         
68         if(this.el){
69             if (this.el.attr('xtype')) {
70                 this.el.attr('xtypex', this.el.attr('xtype'));
71                 this.el.dom.removeAttribute('xtype');
72                 
73                 this.initEvents();
74             }
75             
76             return;
77         }
78         
79          
80         
81         var cfg = Roo.apply({},  this.getAutoCreate());
82         cfg.id = Roo.id();
83         
84         // fill in the extra attributes 
85         if (this.xattr && typeof(this.xattr) =='object') {
86             for (var i in this.xattr) {
87                 cfg[i] = this.xattr[i];
88             }
89         }
90         
91         if(this.dataId){
92             cfg.dataId = this.dataId;
93         }
94         
95         if (this.cls) {
96             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
97         }
98         
99         if (this.style) { // fixme needs to support more complex style data.
100             cfg.style = this.style;
101         }
102         
103         if(this.name){
104             cfg.name = this.name;
105         }
106         
107         this.el = ct.createChild(cfg, position);
108         
109         if(this.tabIndex !== undefined){
110             this.el.dom.setAttribute('tabIndex', this.tabIndex);
111         }
112         this.initEvents();
113         
114         
115     },
116     
117     getChildContainer : function()
118     {
119         return this.el;
120     },
121     
122     
123     addxtype  : function(tree,cntr)
124     {
125         var cn = this;
126         
127         cn = Roo.factory(tree);
128            
129         cn.parentType = this.xtype; //??
130         cn.parentId = this.id;
131         
132         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
133         
134         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
135         
136         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
137         
138         var build_from_html =  Roo.XComponent.build_from_html;
139           
140         var is_body  = (tree.xtype == 'Body') ;
141           
142         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
143           
144         var self_cntr_el = Roo.get(this[cntr](false));
145         
146         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148                 return this.addxtypeChild(tree,cntr);
149             }
150             
151             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
152                 
153             if(echild){
154                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
155             }
156             
157             Roo.log('skipping render');
158             return cn;
159             
160         }
161         
162         var ret = false;
163         
164         while (true) {
165             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
166             
167             if (!echild) {
168                 break;
169             }
170             
171             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
172                 break;
173             }
174             
175             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
176         }
177         return ret;
178     },
179     
180     addxtypeChild : function (tree, cntr)
181     {
182         Roo.log('addxtypeChild:' + cntr);
183         var cn = this;
184         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
185         
186         
187         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188                     (typeof(tree['flexy:foreach']) != 'undefined');
189           
190         
191         
192         
193         // render the element if it's not BODY.
194         if (tree.xtype != 'Body') {
195            
196             cn = Roo.factory(tree);
197            
198             cn.parentType = this.xtype; //??
199             cn.parentId = this.id;
200             
201             var build_from_html =  Roo.XComponent.build_from_html;
202             
203             
204             // does the container contain child eleemnts with 'xtype' attributes.
205             // that match this xtype..
206             // note - when we render we create these as well..
207             // so we should check to see if body has xtype set.
208             if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
209                
210                 var self_cntr_el = Roo.get(this[cntr](false));
211                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
212                 
213                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
215                   
216                   
217                   
218                     cn.el = echild;
219                   //  Roo.log("GOT");
220                     //echild.dom.removeAttribute('xtype');
221                 } else {
222                     Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
223                    
224                 }
225             }
226            
227             
228                
229             // if object has flexy:if - then it may or may not be rendered.
230             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
231                 // skip a flexy if element.
232                 Roo.log('skipping render');
233              } else {
234                  
235                 // actually if flexy:foreach is found, we really want to create 
236                 // multiple copies here...
237                 //Roo.log('render');
238                 //Roo.log(this[cntr]());
239                 cn.render(this[cntr](true));
240              }
241             // then add the element..
242         }
243         
244         
245         // handle the kids..
246         
247         var nitems = [];
248         /*
249         if (typeof (tree.menu) != 'undefined') {
250             tree.menu.parentType = cn.xtype;
251             tree.menu.triggerEl = cn.el;
252             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
253             
254         }
255         */
256         if (!tree.items || !tree.items.length) {
257             cn.items = nitems;
258             return cn;
259         }
260         var items = tree.items;
261         delete tree.items;
262         
263         //Roo.log(items.length);
264             // add the items..
265         for(var i =0;i < items.length;i++) {
266             nitems.push(cn.addxtype(Roo.apply({}, items[i])));
267         }
268         
269         cn.items = nitems;
270         
271         return cn;
272     }
273     
274     
275     
276     
277 });
278
279  /*
280  * - LGPL
281  *
282  * Body
283  * 
284  */
285
286 /**
287  * @class Roo.bootstrap.Body
288  * @extends Roo.bootstrap.Component
289  * Bootstrap Body class
290  * 
291  * @constructor
292  * Create a new body
293  * @param {Object} config The config object
294  */
295
296 Roo.bootstrap.Body = function(config){
297     Roo.bootstrap.Body.superclass.constructor.call(this, config);
298     this.el = Roo.get(document.body);
299     if (this.cls && this.cls.length) {
300         Roo.get(document.body).addClass(this.cls);
301     }
302 };
303
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
305       
306         autoCreate : {
307         cls: 'container'
308     },
309     onRender : function(ct, position)
310     {
311        /* Roo.log("Roo.bootstrap.Body - onRender");
312         if (this.cls && this.cls.length) {
313             Roo.get(document.body).addClass(this.cls);
314         }
315         // style??? xttr???
316         */
317     }
318     
319     
320  
321    
322 });
323
324  /*
325  * - LGPL
326  *
327  * button group
328  * 
329  */
330
331
332 /**
333  * @class Roo.bootstrap.ButtonGroup
334  * @extends Roo.bootstrap.Component
335  * Bootstrap ButtonGroup class
336  * @cfg {String} size lg | sm | xs (default empty normal)
337  * @cfg {String} align vertical | justified  (default none)
338  * @cfg {String} direction up | down (default down)
339  * @cfg {Boolean} toolbar false | true
340  * @cfg {Boolean} btn true | false
341  * 
342  * 
343  * @constructor
344  * Create a new Input
345  * @param {Object} config The config object
346  */
347
348 Roo.bootstrap.ButtonGroup = function(config){
349     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
350 };
351
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
353     
354     size: '',
355     align: '',
356     direction: '',
357     toolbar: false,
358     btn: true,
359
360     getAutoCreate : function(){
361         var cfg = {
362             cls: 'btn-group',
363             html : null
364         }
365         
366         cfg.html = this.html || cfg.html;
367         
368         if (this.toolbar) {
369             cfg = {
370                 cls: 'btn-toolbar',
371                 html: null
372             }
373             
374             return cfg;
375         }
376         
377         if (['vertical','justified'].indexOf(this.align)!==-1) {
378             cfg.cls = 'btn-group-' + this.align;
379             
380             if (this.align == 'justified') {
381                 console.log(this.items);
382             }
383         }
384         
385         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386             cfg.cls += ' btn-group-' + this.size;
387         }
388         
389         if (this.direction == 'up') {
390             cfg.cls += ' dropup' ;
391         }
392         
393         return cfg;
394     }
395    
396 });
397
398  /*
399  * - LGPL
400  *
401  * button
402  * 
403  */
404
405 /**
406  * @class Roo.bootstrap.Button
407  * @extends Roo.bootstrap.Component
408  * Bootstrap Button class
409  * @cfg {String} html The button content
410  * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411  * @cfg {String} size empty | lg | sm | xs
412  * @cfg {String} tag empty | a | input | submit
413  * @cfg {String} href empty or href
414  * @cfg {Boolean} disabled false | true
415  * @cfg {Boolean} isClose false | true
416  * @cfg {String} glyphicon empty | 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
417  * @cfg {String} badge text for badge
418  * @cfg {String} theme default (or empty) | glow
419  * @cfg {Boolean} inverse false | true
420  * @cfg {Boolean} toggle false | true
421  * @cfg {String} ontext text for on toggle state
422  * @cfg {String} offtext text for off toggle state
423  * @cfg {Boolean} defaulton true | false
424  * @cfg {Boolean} preventDefault (true | false) default true
425  * @cfg {Boolean} removeClass true | false remove the standard class..
426  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
427  * 
428  * @constructor
429  * Create a new button
430  * @param {Object} config The config object
431  */
432
433
434 Roo.bootstrap.Button = function(config){
435     Roo.bootstrap.Button.superclass.constructor.call(this, config);
436     this.addEvents({
437         // raw events
438         /**
439          * @event click
440          * When a butotn is pressed
441          * @param {Roo.EventObject} e
442          */
443         "click" : true,
444          /**
445          * @event toggle
446          * After the button has been toggles
447          * @param {Roo.EventObject} e
448          * @param {boolean} pressed (also available as button.pressed)
449          */
450         "toggle" : true
451     });
452 };
453
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
455     html: false,
456     active: false,
457     weight: '',
458     size: '',
459     tag: 'button',
460     href: '',
461     disabled: false,
462     isClose: false,
463     glyphicon: '',
464     badge: '',
465     theme: 'default',
466     inverse: false,
467     
468     toggle: false,
469     ontext: 'ON',
470     offtext: 'OFF',
471     defaulton: true,
472     preventDefault: true,
473     removeClass: false,
474     name: false,
475     target: false,
476     
477     
478     pressed : null,
479      
480     
481     getAutoCreate : function(){
482         
483         var cfg = {
484             tag : 'button',
485             cls : 'roo-button',
486             html: ''
487         };
488         
489         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
491             this.tag = 'button';
492         } else {
493             cfg.tag = this.tag;
494         }
495         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
496         
497         if (this.toggle == true) {
498             cfg={
499                 tag: 'div',
500                 cls: 'slider-frame roo-button',
501                 cn: [
502                     {
503                         tag: 'span',
504                         'data-on-text':'ON',
505                         'data-off-text':'OFF',
506                         cls: 'slider-button',
507                         html: this.offtext
508                     }
509                 ]
510             };
511             
512             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513                 cfg.cls += ' '+this.weight;
514             }
515             
516             return cfg;
517         }
518         
519         if (this.isClose) {
520             cfg.cls += ' close';
521             
522             cfg["aria-hidden"] = true;
523             
524             cfg.html = "&times;";
525             
526             return cfg;
527         }
528         
529          
530         if (this.theme==='default') {
531             cfg.cls = 'btn roo-button';
532             
533             //if (this.parentType != 'Navbar') {
534             this.weight = this.weight.length ?  this.weight : 'default';
535             //}
536             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
537                 
538                 cfg.cls += ' btn-' + this.weight;
539             }
540         } else if (this.theme==='glow') {
541             
542             cfg.tag = 'a';
543             cfg.cls = 'btn-glow roo-button';
544             
545             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
546                 
547                 cfg.cls += ' ' + this.weight;
548             }
549         }
550    
551         
552         if (this.inverse) {
553             this.cls += ' inverse';
554         }
555         
556         
557         if (this.active) {
558             cfg.cls += ' active';
559         }
560         
561         if (this.disabled) {
562             cfg.disabled = 'disabled';
563         }
564         
565         if (this.items) {
566             Roo.log('changing to ul' );
567             cfg.tag = 'ul';
568             this.glyphicon = 'caret';
569         }
570         
571         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
572          
573         //gsRoo.log(this.parentType);
574         if (this.parentType === 'Navbar' && !this.parent().bar) {
575             Roo.log('changing to li?');
576             
577             cfg.tag = 'li';
578             
579             cfg.cls = '';
580             cfg.cn =  [{
581                 tag : 'a',
582                 cls : 'roo-button',
583                 html : this.html,
584                 href : this.href || '#'
585             }];
586             if (this.menu) {
587                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
588                 cfg.cls += ' dropdown';
589             }   
590             
591             delete cfg.html;
592             
593         }
594         
595        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
596         
597         if (this.glyphicon) {
598             cfg.html = ' ' + cfg.html;
599             
600             cfg.cn = [
601                 {
602                     tag: 'span',
603                     cls: 'glyphicon glyphicon-' + this.glyphicon
604                 }
605             ];
606         }
607         
608         if (this.badge) {
609             cfg.html += ' ';
610             
611             cfg.tag = 'a';
612             
613 //            cfg.cls='btn roo-button';
614             
615             cfg.href=this.href;
616             
617             var value = cfg.html;
618             
619             if(this.glyphicon){
620                 value = {
621                             tag: 'span',
622                             cls: 'glyphicon glyphicon-' + this.glyphicon,
623                             html: this.html
624                         };
625                 
626             }
627             
628             cfg.cn = [
629                 value,
630                 {
631                     tag: 'span',
632                     cls: 'badge',
633                     html: this.badge
634                 }
635             ];
636             
637             cfg.html='';
638         }
639         
640         if (this.menu) {
641             cfg.cls += ' dropdown';
642             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
643         }
644         
645         if (cfg.tag !== 'a' && this.href !== '') {
646             throw "Tag must be a to set href.";
647         } else if (this.href.length > 0) {
648             cfg.href = this.href;
649         }
650         
651         if(this.removeClass){
652             cfg.cls = '';
653         }
654         
655         if(this.target){
656             cfg.target = this.target;
657         }
658         
659         return cfg;
660     },
661     initEvents: function() {
662        // Roo.log('init events?');
663 //        Roo.log(this.el.dom);
664         // add the menu...
665         
666         if (typeof (this.menu) != 'undefined') {
667             this.menu.parentType = this.xtype;
668             this.menu.triggerEl = this.el;
669             this.addxtype(Roo.apply({}, this.menu));
670         }
671
672
673        if (this.el.hasClass('roo-button')) {
674             this.el.on('click', this.onClick, this);
675        } else {
676             this.el.select('.roo-button').on('click', this.onClick, this);
677        }
678        
679        if(this.removeClass){
680            this.el.on('click', this.onClick, this);
681        }
682        
683        this.el.enableDisplayMode();
684         
685     },
686     onClick : function(e)
687     {
688         if (this.disabled) {
689             return;
690         }
691         
692         Roo.log('button on click ');
693         if(this.preventDefault){
694             e.preventDefault();
695         }
696         if (this.pressed === true || this.pressed === false) {
697             this.pressed = !this.pressed;
698             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699             this.fireEvent('toggle', this, e, this.pressed);
700         }
701         
702         
703         this.fireEvent('click', this, e);
704     },
705     
706     /**
707      * Enables this button
708      */
709     enable : function()
710     {
711         this.disabled = false;
712         this.el.removeClass('disabled');
713     },
714     
715     /**
716      * Disable this button
717      */
718     disable : function()
719     {
720         this.disabled = true;
721         this.el.addClass('disabled');
722     },
723      /**
724      * sets the active state on/off, 
725      * @param {Boolean} state (optional) Force a particular state
726      */
727     setActive : function(v) {
728         
729         this.el[v ? 'addClass' : 'removeClass']('active');
730     },
731      /**
732      * toggles the current active state 
733      */
734     toggleActive : function()
735     {
736        var active = this.el.hasClass('active');
737        this.setActive(!active);
738        
739         
740     },
741     setText : function(str)
742     {
743         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
744     },
745     getText : function()
746     {
747         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
748     },
749     hide: function() {
750        
751      
752         this.el.hide();   
753     },
754     show: function() {
755        
756         this.el.show();   
757     }
758     
759     
760 });
761
762  /*
763  * - LGPL
764  *
765  * column
766  * 
767  */
768
769 /**
770  * @class Roo.bootstrap.Column
771  * @extends Roo.bootstrap.Component
772  * Bootstrap Column class
773  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
774  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
775  * @cfg {Number} md colspan out of 12 for computer-sized screens
776  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
777  * @cfg {String} html content of column.
778  * 
779  * @constructor
780  * Create a new Column
781  * @param {Object} config The config object
782  */
783
784 Roo.bootstrap.Column = function(config){
785     Roo.bootstrap.Column.superclass.constructor.call(this, config);
786 };
787
788 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
789     
790     xs: null,
791     sm: null,
792     md: null,
793     lg: null,
794     html: '',
795     offset: 0,
796     
797     getAutoCreate : function(){
798         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
799         
800         cfg = {
801             tag: 'div',
802             cls: 'column'
803         };
804         
805         var settings=this;
806         ['xs','sm','md','lg'].map(function(size){
807             if (settings[size]) {
808                 cfg.cls += ' col-' + size + '-' + settings[size];
809             }
810         });
811         if (this.html.length) {
812             cfg.html = this.html;
813         }
814         
815         return cfg;
816     }
817    
818 });
819
820  
821
822  /*
823  * - LGPL
824  *
825  * page container.
826  * 
827  */
828
829
830 /**
831  * @class Roo.bootstrap.Container
832  * @extends Roo.bootstrap.Component
833  * Bootstrap Container class
834  * @cfg {Boolean} jumbotron is it a jumbotron element
835  * @cfg {String} html content of element
836  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
837  * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
838  * @cfg {String} header content of header (for panel)
839  * @cfg {String} footer content of footer (for panel)
840  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
841  * @cfg {String} tag (header|aside|section) type of HTML tag.
842
843  *     
844  * @constructor
845  * Create a new Container
846  * @param {Object} config The config object
847  */
848
849 Roo.bootstrap.Container = function(config){
850     Roo.bootstrap.Container.superclass.constructor.call(this, config);
851 };
852
853 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
854     
855     jumbotron : false,
856     well: '',
857     panel : '',
858     header: '',
859     footer : '',
860     sticky: '',
861     tag : false,
862   
863      
864     getChildContainer : function() {
865         
866         if(!this.el){
867             return false;
868         }
869         
870         if (this.panel.length) {
871             return this.el.select('.panel-body',true).first();
872         }
873         
874         return this.el;
875     },
876     
877     
878     getAutoCreate : function(){
879         
880         var cfg = {
881             tag : this.tag || 'div',
882             html : '',
883             cls : ''
884         };
885         if (this.jumbotron) {
886             cfg.cls = 'jumbotron';
887         }
888         // - this is applied by the parent..
889         //if (this.cls) {
890         //    cfg.cls = this.cls + '';
891         //}
892         
893         if (this.sticky.length) {
894             
895             var bd = Roo.get(document.body);
896             if (!bd.hasClass('bootstrap-sticky')) {
897                 bd.addClass('bootstrap-sticky');
898                 Roo.select('html',true).setStyle('height', '100%');
899             }
900              
901             cfg.cls += 'bootstrap-sticky-' + this.sticky;
902         }
903         
904         
905         if (this.well.length) {
906             switch (this.well) {
907                 case 'lg':
908                 case 'sm':
909                     cfg.cls +=' well well-' +this.well;
910                     break;
911                 default:
912                     cfg.cls +=' well';
913                     break;
914             }
915         }
916         
917         var body = cfg;
918         
919         if (this.panel.length) {
920             cfg.cls += ' panel panel-' + this.panel;
921             cfg.cn = [];
922             if (this.header.length) {
923                 cfg.cn.push({
924                     
925                     cls : 'panel-heading',
926                     cn : [{
927                         tag: 'h3',
928                         cls : 'panel-title',
929                         html : this.header
930                     }]
931                     
932                 });
933             }
934             body = false;
935             cfg.cn.push({
936                 cls : 'panel-body',
937                 html : this.html
938             });
939             
940             
941             if (this.footer.length) {
942                 cfg.cn.push({
943                     cls : 'panel-footer',
944                     html : this.footer
945                     
946                 });
947             }
948             
949         }
950         
951         if (body) {
952             body.html = this.html || cfg.html;
953         }
954         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
955             cfg.cls =  'container';
956         }
957         
958         return cfg;
959     },
960     
961     titleEl : function()
962     {
963         if(!this.el || !this.panel.length || !this.header.length){
964             return;
965         }
966         
967         return this.el.select('.panel-title',true).first();
968     },
969     
970     setTitle : function(v)
971     {
972         var titleEl = this.titleEl();
973         
974         if(!titleEl){
975             return;
976         }
977         
978         titleEl.dom.innerHTML = v;
979     },
980     
981     getTitle : function()
982     {
983         
984         var titleEl = this.titleEl();
985         
986         if(!titleEl){
987             return '';
988         }
989         
990         return titleEl.dom.innerHTML;
991     }
992    
993 });
994
995  /*
996  * - LGPL
997  *
998  * image
999  * 
1000  */
1001
1002
1003 /**
1004  * @class Roo.bootstrap.Img
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Img class
1007  * @cfg {Boolean} imgResponsive false | true
1008  * @cfg {String} border rounded | circle | thumbnail
1009  * @cfg {String} src image source
1010  * @cfg {String} alt image alternative text
1011  * @cfg {String} href a tag href
1012  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1013  * 
1014  * @constructor
1015  * Create a new Input
1016  * @param {Object} config The config object
1017  */
1018
1019 Roo.bootstrap.Img = function(config){
1020     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1021     
1022     this.addEvents({
1023         // img events
1024         /**
1025          * @event click
1026          * The img click event for the img.
1027          * @param {Roo.EventObject} e
1028          */
1029         "click" : true
1030     });
1031 };
1032
1033 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1034     
1035     imgResponsive: true,
1036     border: '',
1037     src: '',
1038     href: false,
1039     target: false,
1040
1041     getAutoCreate : function(){
1042         
1043         var cfg = {
1044             tag: 'img',
1045             cls: (this.imgResponsive) ? 'img-responsive' : '',
1046             html : null
1047         }
1048         
1049         cfg.html = this.html || cfg.html;
1050         
1051         cfg.src = this.src || cfg.src;
1052         
1053         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1054             cfg.cls += ' img-' + this.border;
1055         }
1056         
1057         if(this.alt){
1058             cfg.alt = this.alt;
1059         }
1060         
1061         if(this.href){
1062             var a = {
1063                 tag: 'a',
1064                 href: this.href,
1065                 cn: [
1066                     cfg
1067                 ]
1068             }
1069             
1070             if(this.target){
1071                 a.target = this.target;
1072             }
1073             
1074         }
1075         
1076         
1077         return (this.href) ? a : cfg;
1078     },
1079     
1080     initEvents: function() {
1081         
1082         if(!this.href){
1083             this.el.on('click', this.onClick, this);
1084         }
1085     },
1086     
1087     onClick : function(e)
1088     {
1089         Roo.log('img onclick');
1090         this.fireEvent('click', this, e);
1091     }
1092    
1093 });
1094
1095  /*
1096  * - LGPL
1097  *
1098  * image
1099  * 
1100  */
1101
1102
1103 /**
1104  * @class Roo.bootstrap.Link
1105  * @extends Roo.bootstrap.Component
1106  * Bootstrap Link Class
1107  * @cfg {String} alt image alternative text
1108  * @cfg {String} href a tag href
1109  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1110  * @cfg {String} html the content of the link.
1111  * @cfg {Boolean} preventDefault (true | false) default false
1112
1113  * 
1114  * @constructor
1115  * Create a new Input
1116  * @param {Object} config The config object
1117  */
1118
1119 Roo.bootstrap.Link = function(config){
1120     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1121     
1122     this.addEvents({
1123         // img events
1124         /**
1125          * @event click
1126          * The img click event for the img.
1127          * @param {Roo.EventObject} e
1128          */
1129         "click" : true
1130     });
1131 };
1132
1133 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1134     
1135     href: false,
1136     target: false,
1137     preventDefault: false,
1138
1139     getAutoCreate : function(){
1140         
1141         var cfg = {
1142             tag: 'a',
1143             html : this.html || 'html-missing'
1144         }
1145         
1146         
1147         if(this.alt){
1148             cfg.alt = this.alt;
1149         }
1150         cfg.href = this.href || '#';
1151         if(this.target){
1152             cfg.target = this.target;
1153         }
1154         
1155         return cfg;
1156     },
1157     
1158     initEvents: function() {
1159         
1160         if(!this.href || this.preventDefault){
1161             this.el.on('click', this.onClick, this);
1162         }
1163     },
1164     
1165     onClick : function(e)
1166     {
1167         if(this.preventDefault){
1168             e.preventDefault();
1169         }
1170         //Roo.log('img onclick');
1171         this.fireEvent('click', this, e);
1172     }
1173    
1174 });
1175
1176  /*
1177  * - LGPL
1178  *
1179  * header
1180  * 
1181  */
1182
1183 /**
1184  * @class Roo.bootstrap.Header
1185  * @extends Roo.bootstrap.Component
1186  * Bootstrap Header class
1187  * @cfg {String} html content of header
1188  * @cfg {Number} level (1|2|3|4|5|6) default 1
1189  * 
1190  * @constructor
1191  * Create a new Header
1192  * @param {Object} config The config object
1193  */
1194
1195
1196 Roo.bootstrap.Header  = function(config){
1197     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1198 };
1199
1200 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1201     
1202     //href : false,
1203     html : false,
1204     level : 1,
1205     
1206     
1207     
1208     getAutoCreate : function(){
1209         
1210         var cfg = {
1211             tag: 'h' + (1 *this.level),
1212             html: this.html || 'fill in html'
1213         } ;
1214         
1215         return cfg;
1216     }
1217    
1218 });
1219
1220  
1221
1222  /*
1223  * Based on:
1224  * Ext JS Library 1.1.1
1225  * Copyright(c) 2006-2007, Ext JS, LLC.
1226  *
1227  * Originally Released Under LGPL - original licence link has changed is not relivant.
1228  *
1229  * Fork - LGPL
1230  * <script type="text/javascript">
1231  */
1232  
1233 /**
1234  * @class Roo.bootstrap.MenuMgr
1235  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1236  * @singleton
1237  */
1238 Roo.bootstrap.MenuMgr = function(){
1239    var menus, active, groups = {}, attached = false, lastShow = new Date();
1240
1241    // private - called when first menu is created
1242    function init(){
1243        menus = {};
1244        active = new Roo.util.MixedCollection();
1245        Roo.get(document).addKeyListener(27, function(){
1246            if(active.length > 0){
1247                hideAll();
1248            }
1249        });
1250    }
1251
1252    // private
1253    function hideAll(){
1254        if(active && active.length > 0){
1255            var c = active.clone();
1256            c.each(function(m){
1257                m.hide();
1258            });
1259        }
1260    }
1261
1262    // private
1263    function onHide(m){
1264        active.remove(m);
1265        if(active.length < 1){
1266            Roo.get(document).un("mouseup", onMouseDown);
1267             
1268            attached = false;
1269        }
1270    }
1271
1272    // private
1273    function onShow(m){
1274        var last = active.last();
1275        lastShow = new Date();
1276        active.add(m);
1277        if(!attached){
1278           Roo.get(document).on("mouseup", onMouseDown);
1279            
1280            attached = true;
1281        }
1282        if(m.parentMenu){
1283           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1284           m.parentMenu.activeChild = m;
1285        }else if(last && last.isVisible()){
1286           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1287        }
1288    }
1289
1290    // private
1291    function onBeforeHide(m){
1292        if(m.activeChild){
1293            m.activeChild.hide();
1294        }
1295        if(m.autoHideTimer){
1296            clearTimeout(m.autoHideTimer);
1297            delete m.autoHideTimer;
1298        }
1299    }
1300
1301    // private
1302    function onBeforeShow(m){
1303        var pm = m.parentMenu;
1304        if(!pm && !m.allowOtherMenus){
1305            hideAll();
1306        }else if(pm && pm.activeChild && active != m){
1307            pm.activeChild.hide();
1308        }
1309    }
1310
1311    // private
1312    function onMouseDown(e){
1313         Roo.log("on MouseDown");
1314         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1315            hideAll();
1316         }
1317         
1318         
1319    }
1320
1321    // private
1322    function onBeforeCheck(mi, state){
1323        if(state){
1324            var g = groups[mi.group];
1325            for(var i = 0, l = g.length; i < l; i++){
1326                if(g[i] != mi){
1327                    g[i].setChecked(false);
1328                }
1329            }
1330        }
1331    }
1332
1333    return {
1334
1335        /**
1336         * Hides all menus that are currently visible
1337         */
1338        hideAll : function(){
1339             hideAll();  
1340        },
1341
1342        // private
1343        register : function(menu){
1344            if(!menus){
1345                init();
1346            }
1347            menus[menu.id] = menu;
1348            menu.on("beforehide", onBeforeHide);
1349            menu.on("hide", onHide);
1350            menu.on("beforeshow", onBeforeShow);
1351            menu.on("show", onShow);
1352            var g = menu.group;
1353            if(g && menu.events["checkchange"]){
1354                if(!groups[g]){
1355                    groups[g] = [];
1356                }
1357                groups[g].push(menu);
1358                menu.on("checkchange", onCheck);
1359            }
1360        },
1361
1362         /**
1363          * Returns a {@link Roo.menu.Menu} object
1364          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1365          * be used to generate and return a new Menu instance.
1366          */
1367        get : function(menu){
1368            if(typeof menu == "string"){ // menu id
1369                return menus[menu];
1370            }else if(menu.events){  // menu instance
1371                return menu;
1372            }
1373            /*else if(typeof menu.length == 'number'){ // array of menu items?
1374                return new Roo.bootstrap.Menu({items:menu});
1375            }else{ // otherwise, must be a config
1376                return new Roo.bootstrap.Menu(menu);
1377            }
1378            */
1379            return false;
1380        },
1381
1382        // private
1383        unregister : function(menu){
1384            delete menus[menu.id];
1385            menu.un("beforehide", onBeforeHide);
1386            menu.un("hide", onHide);
1387            menu.un("beforeshow", onBeforeShow);
1388            menu.un("show", onShow);
1389            var g = menu.group;
1390            if(g && menu.events["checkchange"]){
1391                groups[g].remove(menu);
1392                menu.un("checkchange", onCheck);
1393            }
1394        },
1395
1396        // private
1397        registerCheckable : function(menuItem){
1398            var g = menuItem.group;
1399            if(g){
1400                if(!groups[g]){
1401                    groups[g] = [];
1402                }
1403                groups[g].push(menuItem);
1404                menuItem.on("beforecheckchange", onBeforeCheck);
1405            }
1406        },
1407
1408        // private
1409        unregisterCheckable : function(menuItem){
1410            var g = menuItem.group;
1411            if(g){
1412                groups[g].remove(menuItem);
1413                menuItem.un("beforecheckchange", onBeforeCheck);
1414            }
1415        }
1416    };
1417 }();/*
1418  * - LGPL
1419  *
1420  * menu
1421  * 
1422  */
1423
1424 /**
1425  * @class Roo.bootstrap.Menu
1426  * @extends Roo.bootstrap.Component
1427  * Bootstrap Menu class - container for MenuItems
1428  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1429  * 
1430  * @constructor
1431  * Create a new Menu
1432  * @param {Object} config The config object
1433  */
1434
1435
1436 Roo.bootstrap.Menu = function(config){
1437     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1438     if (this.registerMenu) {
1439         Roo.bootstrap.MenuMgr.register(this);
1440     }
1441     this.addEvents({
1442         /**
1443          * @event beforeshow
1444          * Fires before this menu is displayed
1445          * @param {Roo.menu.Menu} this
1446          */
1447         beforeshow : true,
1448         /**
1449          * @event beforehide
1450          * Fires before this menu is hidden
1451          * @param {Roo.menu.Menu} this
1452          */
1453         beforehide : true,
1454         /**
1455          * @event show
1456          * Fires after this menu is displayed
1457          * @param {Roo.menu.Menu} this
1458          */
1459         show : true,
1460         /**
1461          * @event hide
1462          * Fires after this menu is hidden
1463          * @param {Roo.menu.Menu} this
1464          */
1465         hide : true,
1466         /**
1467          * @event click
1468          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1469          * @param {Roo.menu.Menu} this
1470          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1471          * @param {Roo.EventObject} e
1472          */
1473         click : true,
1474         /**
1475          * @event mouseover
1476          * Fires when the mouse is hovering over this menu
1477          * @param {Roo.menu.Menu} this
1478          * @param {Roo.EventObject} e
1479          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1480          */
1481         mouseover : true,
1482         /**
1483          * @event mouseout
1484          * Fires when the mouse exits this menu
1485          * @param {Roo.menu.Menu} this
1486          * @param {Roo.EventObject} e
1487          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1488          */
1489         mouseout : true,
1490         /**
1491          * @event itemclick
1492          * Fires when a menu item contained in this menu is clicked
1493          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1494          * @param {Roo.EventObject} e
1495          */
1496         itemclick: true
1497     });
1498     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1499 };
1500
1501 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1502     
1503    /// html : false,
1504     //align : '',
1505     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1506     type: false,
1507     /**
1508      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1509      */
1510     registerMenu : true,
1511     
1512     menuItems :false, // stores the menu items..
1513     
1514     hidden:true,
1515     
1516     parentMenu : false,
1517     
1518     getChildContainer : function() {
1519         return this.el;  
1520     },
1521     
1522     getAutoCreate : function(){
1523          
1524         //if (['right'].indexOf(this.align)!==-1) {
1525         //    cfg.cn[1].cls += ' pull-right'
1526         //}
1527         
1528         
1529         var cfg = {
1530             tag : 'ul',
1531             cls : 'dropdown-menu' ,
1532             style : 'z-index:1000'
1533             
1534         }
1535         
1536         if (this.type === 'submenu') {
1537             cfg.cls = 'submenu active';
1538         }
1539         if (this.type === 'treeview') {
1540             cfg.cls = 'treeview-menu';
1541         }
1542         
1543         return cfg;
1544     },
1545     initEvents : function() {
1546         
1547        // Roo.log("ADD event");
1548        // Roo.log(this.triggerEl.dom);
1549         this.triggerEl.on('click', this.onTriggerPress, this);
1550         this.triggerEl.addClass('dropdown-toggle');
1551         this.el.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
1552
1553         this.el.on("mouseover", this.onMouseOver, this);
1554         this.el.on("mouseout", this.onMouseOut, this);
1555         
1556         
1557     },
1558     findTargetItem : function(e){
1559         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
1560         if(!t){
1561             return false;
1562         }
1563         //Roo.log(t);         Roo.log(t.id);
1564         if(t && t.id){
1565             //Roo.log(this.menuitems);
1566             return this.menuitems.get(t.id);
1567             
1568             //return this.items.get(t.menuItemId);
1569         }
1570         
1571         return false;
1572     },
1573     onClick : function(e){
1574         Roo.log("menu.onClick");
1575         var t = this.findTargetItem(e);
1576         if(!t){
1577             return;
1578         }
1579         Roo.log(e);
1580         /*
1581         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
1582             if(t == this.activeItem && t.shouldDeactivate(e)){
1583                 this.activeItem.deactivate();
1584                 delete this.activeItem;
1585                 return;
1586             }
1587             if(t.canActivate){
1588                 this.setActiveItem(t, true);
1589             }
1590             return;
1591             
1592             
1593         }
1594         */
1595         Roo.log('pass click event');
1596         
1597         t.onClick(e);
1598         
1599         this.fireEvent("click", this, t, e);
1600         
1601         this.hide();
1602     },
1603      onMouseOver : function(e){
1604         var t  = this.findTargetItem(e);
1605         //Roo.log(t);
1606         //if(t){
1607         //    if(t.canActivate && !t.disabled){
1608         //        this.setActiveItem(t, true);
1609         //    }
1610         //}
1611         
1612         this.fireEvent("mouseover", this, e, t);
1613     },
1614     isVisible : function(){
1615         return !this.hidden;
1616     },
1617      onMouseOut : function(e){
1618         var t  = this.findTargetItem(e);
1619         
1620         //if(t ){
1621         //    if(t == this.activeItem && t.shouldDeactivate(e)){
1622         //        this.activeItem.deactivate();
1623         //        delete this.activeItem;
1624         //    }
1625         //}
1626         this.fireEvent("mouseout", this, e, t);
1627     },
1628     
1629     
1630     /**
1631      * Displays this menu relative to another element
1632      * @param {String/HTMLElement/Roo.Element} element The element to align to
1633      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1634      * the element (defaults to this.defaultAlign)
1635      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1636      */
1637     show : function(el, pos, parentMenu){
1638         this.parentMenu = parentMenu;
1639         if(!this.el){
1640             this.render();
1641         }
1642         this.fireEvent("beforeshow", this);
1643         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1644     },
1645      /**
1646      * Displays this menu at a specific xy position
1647      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1648      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1649      */
1650     showAt : function(xy, parentMenu, /* private: */_e){
1651         this.parentMenu = parentMenu;
1652         if(!this.el){
1653             this.render();
1654         }
1655         if(_e !== false){
1656             this.fireEvent("beforeshow", this);
1657             
1658             //xy = this.el.adjustForConstraints(xy);
1659         }
1660         //this.el.setXY(xy);
1661         //this.el.show();
1662         this.hideMenuItems();
1663         this.hidden = false;
1664         this.triggerEl.addClass('open');
1665         this.focus();
1666         this.fireEvent("show", this);
1667     },
1668     
1669     focus : function(){
1670         return;
1671         if(!this.hidden){
1672             this.doFocus.defer(50, this);
1673         }
1674     },
1675
1676     doFocus : function(){
1677         if(!this.hidden){
1678             this.focusEl.focus();
1679         }
1680     },
1681
1682     /**
1683      * Hides this menu and optionally all parent menus
1684      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1685      */
1686     hide : function(deep){
1687         
1688         this.hideMenuItems();
1689         if(this.el && this.isVisible()){
1690             this.fireEvent("beforehide", this);
1691             if(this.activeItem){
1692                 this.activeItem.deactivate();
1693                 this.activeItem = null;
1694             }
1695             this.triggerEl.removeClass('open');;
1696             this.hidden = true;
1697             this.fireEvent("hide", this);
1698         }
1699         if(deep === true && this.parentMenu){
1700             this.parentMenu.hide(true);
1701         }
1702     },
1703     
1704     onTriggerPress  : function(e)
1705     {
1706         
1707         Roo.log('trigger press');
1708         //Roo.log(e.getTarget());
1709        // Roo.log(this.triggerEl.dom);
1710         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1711             return;
1712         }
1713         if (this.isVisible()) {
1714             Roo.log('hide');
1715             this.hide();
1716         } else {
1717             this.show(this.triggerEl, false, false);
1718         }
1719         
1720         
1721     },
1722     
1723          
1724        
1725     
1726     hideMenuItems : function()
1727     {
1728         //$(backdrop).remove()
1729         Roo.select('.open',true).each(function(aa) {
1730             
1731             aa.removeClass('open');
1732           //var parent = getParent($(this))
1733           //var relatedTarget = { relatedTarget: this }
1734           
1735            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1736           //if (e.isDefaultPrevented()) return
1737            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1738         })
1739     },
1740     addxtypeChild : function (tree, cntr) {
1741         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1742           
1743         this.menuitems.add(comp);
1744         return comp;
1745
1746     },
1747     getEl : function()
1748     {
1749         Roo.log(this.el);
1750         return this.el;
1751     }
1752 });
1753
1754  
1755  /*
1756  * - LGPL
1757  *
1758  * menu item
1759  * 
1760  */
1761
1762
1763 /**
1764  * @class Roo.bootstrap.MenuItem
1765  * @extends Roo.bootstrap.Component
1766  * Bootstrap MenuItem class
1767  * @cfg {String} html the menu label
1768  * @cfg {String} href the link
1769  * @cfg {Boolean} preventDefault (true | false) default true
1770  * 
1771  * 
1772  * @constructor
1773  * Create a new MenuItem
1774  * @param {Object} config The config object
1775  */
1776
1777
1778 Roo.bootstrap.MenuItem = function(config){
1779     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1780     this.addEvents({
1781         // raw events
1782         /**
1783          * @event click
1784          * The raw click event for the entire grid.
1785          * @param {Roo.EventObject} e
1786          */
1787         "click" : true
1788     });
1789 };
1790
1791 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
1792     
1793     href : false,
1794     html : false,
1795     preventDefault: true,
1796     
1797     getAutoCreate : function(){
1798         var cfg= {
1799             tag: 'li',
1800             cls: 'dropdown-menu-item',
1801             cn: [
1802                     {
1803                         tag : 'a',
1804                         href : '#',
1805                         html : 'Link'
1806                     }
1807                 ]
1808         };
1809         if (this.parent().type == 'treeview') {
1810             cfg.cls = 'treeview-menu';
1811         }
1812         
1813         cfg.cn[0].href = this.href || cfg.cn[0].href ;
1814         cfg.cn[0].html = this.html || cfg.cn[0].html ;
1815         return cfg;
1816     },
1817     
1818     initEvents: function() {
1819         
1820         //this.el.select('a').on('click', this.onClick, this);
1821         
1822     },
1823     onClick : function(e)
1824     {
1825         Roo.log('item on click ');
1826         //if(this.preventDefault){
1827         //    e.preventDefault();
1828         //}
1829         //this.parent().hideMenuItems();
1830         
1831         this.fireEvent('click', this, e);
1832     },
1833     getEl : function()
1834     {
1835         return this.el;
1836     }
1837 });
1838
1839  
1840
1841  /*
1842  * - LGPL
1843  *
1844  * menu separator
1845  * 
1846  */
1847
1848
1849 /**
1850  * @class Roo.bootstrap.MenuSeparator
1851  * @extends Roo.bootstrap.Component
1852  * Bootstrap MenuSeparator class
1853  * 
1854  * @constructor
1855  * Create a new MenuItem
1856  * @param {Object} config The config object
1857  */
1858
1859
1860 Roo.bootstrap.MenuSeparator = function(config){
1861     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1862 };
1863
1864 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
1865     
1866     getAutoCreate : function(){
1867         var cfg = {
1868             cls: 'divider',
1869             tag : 'li'
1870         };
1871         
1872         return cfg;
1873     }
1874    
1875 });
1876
1877  
1878
1879  
1880 /*
1881 <div class="modal fade">
1882   <div class="modal-dialog">
1883     <div class="modal-content">
1884       <div class="modal-header">
1885         <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1886         <h4 class="modal-title">Modal title</h4>
1887       </div>
1888       <div class="modal-body">
1889         <p>One fine body&hellip;</p>
1890       </div>
1891       <div class="modal-footer">
1892         <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1893         <button type="button" class="btn btn-primary">Save changes</button>
1894       </div>
1895     </div><!-- /.modal-content -->
1896   </div><!-- /.modal-dialog -->
1897 </div><!-- /.modal -->
1898 */
1899 /*
1900  * - LGPL
1901  *
1902  * page contgainer.
1903  * 
1904  */
1905
1906 /**
1907  * @class Roo.bootstrap.Modal
1908  * @extends Roo.bootstrap.Component
1909  * Bootstrap Modal class
1910  * @cfg {String} title Title of dialog
1911  * @cfg {Boolean} specificTitle (true|false) default false
1912  * @cfg {Array} buttons Array of buttons or standard button set..
1913  * @cfg {String} buttonPosition (left|right|center) default right
1914  * 
1915  * @constructor
1916  * Create a new Modal Dialog
1917  * @param {Object} config The config object
1918  */
1919
1920 Roo.bootstrap.Modal = function(config){
1921     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1922     this.addEvents({
1923         // raw events
1924         /**
1925          * @event btnclick
1926          * The raw btnclick event for the button
1927          * @param {Roo.EventObject} e
1928          */
1929         "btnclick" : true
1930     });
1931     this.buttons = this.buttons || [];
1932 };
1933
1934 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
1935     
1936     title : 'test dialog',
1937    
1938     buttons : false,
1939     
1940     // set on load...
1941     body:  false,
1942     
1943     specificTitle: false,
1944     
1945     buttonPosition: 'right',
1946     
1947     onRender : function(ct, position)
1948     {
1949         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1950      
1951         if(!this.el){
1952             var cfg = Roo.apply({},  this.getAutoCreate());
1953             cfg.id = Roo.id();
1954             //if(!cfg.name){
1955             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1956             //}
1957             //if (!cfg.name.length) {
1958             //    delete cfg.name;
1959            // }
1960             if (this.cls) {
1961                 cfg.cls += ' ' + this.cls;
1962             }
1963             if (this.style) {
1964                 cfg.style = this.style;
1965             }
1966             this.el = Roo.get(document.body).createChild(cfg, position);
1967         }
1968         //var type = this.el.dom.type;
1969         
1970         if(this.tabIndex !== undefined){
1971             this.el.dom.setAttribute('tabIndex', this.tabIndex);
1972         }
1973         
1974         
1975         
1976         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1977         this.maskEl.enableDisplayMode("block");
1978         this.maskEl.hide();
1979         //this.el.addClass("x-dlg-modal");
1980     
1981         if (this.buttons.length) {
1982             Roo.each(this.buttons, function(bb) {
1983                 b = Roo.apply({}, bb);
1984                 b.xns = b.xns || Roo.bootstrap;
1985                 b.xtype = b.xtype || 'Button';
1986                 if (typeof(b.listeners) == 'undefined') {
1987                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
1988                 }
1989                 
1990                 var btn = Roo.factory(b);
1991                 
1992                 btn.onRender(this.el.select('.modal-footer div').first());
1993                 
1994             },this);
1995         }
1996         // render the children.
1997         var nitems = [];
1998         
1999         if(typeof(this.items) != 'undefined'){
2000             var items = this.items;
2001             delete this.items;
2002
2003             for(var i =0;i < items.length;i++) {
2004                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2005             }
2006         }
2007         
2008         this.items = nitems;
2009         
2010         this.body = this.el.select('.modal-body',true).first();
2011         this.close = this.el.select('.modal-header .close', true).first();
2012         this.footer = this.el.select('.modal-footer',true).first();
2013         this.initEvents();
2014         //this.el.addClass([this.fieldClass, this.cls]);
2015         
2016     },
2017     getAutoCreate : function(){
2018         
2019         
2020         var bdy = {
2021                 cls : 'modal-body',
2022                 html : this.html || ''
2023         };
2024         
2025         var title = {
2026             tag: 'h4',
2027             cls : 'modal-title',
2028             html : this.title
2029         };
2030         
2031         if(this.specificTitle){
2032             title = this.title;
2033         };
2034         
2035         return modal = {
2036             cls: "modal fade",
2037             style : 'display: none',
2038             cn : [
2039                 {
2040                     cls: "modal-dialog",
2041                     cn : [
2042                         {
2043                             cls : "modal-content",
2044                             cn : [
2045                                 {
2046                                     cls : 'modal-header',
2047                                     cn : [
2048                                         {
2049                                             tag: 'button',
2050                                             cls : 'close',
2051                                             html : '&times'
2052                                         },
2053                                         title
2054                                     ]
2055                                 },
2056                                 bdy,
2057                                 {
2058                                     cls : 'modal-footer',
2059                                     cn : [
2060                                         {
2061                                             tag: 'div',
2062                                             cls: 'btn-' + this.buttonPosition
2063                                         }
2064                                     ]
2065                                     
2066                                 }
2067                                 
2068                                 
2069                             ]
2070                             
2071                         }
2072                     ]
2073                         
2074                 }
2075             ]
2076             
2077             
2078         };
2079           
2080     },
2081     getChildContainer : function() {
2082          
2083          return this.el.select('.modal-body',true).first();
2084         
2085     },
2086     getButtonContainer : function() {
2087          return this.el.select('.modal-footer div',true).first();
2088         
2089     },
2090     initEvents : function()
2091     {
2092         this.el.select('.modal-header .close').on('click', this.hide, this);
2093 //        
2094 //        this.addxtype(this);
2095     },
2096     show : function() {
2097         
2098         if (!this.rendered) {
2099             this.render();
2100         }
2101        
2102         this.el.addClass('on');
2103         this.el.removeClass('fade');
2104         this.el.setStyle('display', 'block');
2105         Roo.get(document.body).addClass("x-body-masked");
2106         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2107         this.maskEl.show();
2108         this.el.setStyle('zIndex', '10001');
2109         this.fireEvent('show', this);
2110         
2111         
2112     },
2113     hide : function()
2114     {
2115         Roo.log('Modal hide?!');
2116         this.maskEl.hide();
2117         Roo.get(document.body).removeClass("x-body-masked");
2118         this.el.removeClass('on');
2119         this.el.addClass('fade');
2120         this.el.setStyle('display', 'none');
2121         this.fireEvent('hide', this);
2122     },
2123     
2124     addButton : function(str, cb)
2125     {
2126          
2127         
2128         var b = Roo.apply({}, { html : str } );
2129         b.xns = b.xns || Roo.bootstrap;
2130         b.xtype = b.xtype || 'Button';
2131         if (typeof(b.listeners) == 'undefined') {
2132             b.listeners = { click : cb.createDelegate(this)  };
2133         }
2134         
2135         var btn = Roo.factory(b);
2136            
2137         btn.onRender(this.el.select('.modal-footer div').first());
2138         
2139         return btn;   
2140        
2141     },
2142     
2143     setDefaultButton : function(btn)
2144     {
2145         //this.el.select('.modal-footer').()
2146     },
2147     resizeTo: function(w,h)
2148     {
2149         // skip..
2150     },
2151     setContentSize  : function(w, h)
2152     {
2153         
2154     },
2155     onButtonClick: function(btn,e)
2156     {
2157         //Roo.log([a,b,c]);
2158         this.fireEvent('btnclick', btn.name, e);
2159     },
2160     setTitle: function(str) {
2161         this.el.select('.modal-title',true).first().dom.innerHTML = str;
2162         
2163     }
2164 });
2165
2166
2167 Roo.apply(Roo.bootstrap.Modal,  {
2168     /**
2169          * Button config that displays a single OK button
2170          * @type Object
2171          */
2172         OK :  [{
2173             name : 'ok',
2174             weight : 'primary',
2175             html : 'OK'
2176         }], 
2177         /**
2178          * Button config that displays Yes and No buttons
2179          * @type Object
2180          */
2181         YESNO : [
2182             {
2183                 name  : 'no',
2184                 html : 'No'
2185             },
2186             {
2187                 name  :'yes',
2188                 weight : 'primary',
2189                 html : 'Yes'
2190             }
2191         ],
2192         
2193         /**
2194          * Button config that displays OK and Cancel buttons
2195          * @type Object
2196          */
2197         OKCANCEL : [
2198             {
2199                name : 'cancel',
2200                 html : 'Cancel'
2201             },
2202             {
2203                 name : 'ok',
2204                 weight : 'primary',
2205                 html : 'OK'
2206             }
2207         ],
2208         /**
2209          * Button config that displays Yes, No and Cancel buttons
2210          * @type Object
2211          */
2212         YESNOCANCEL : [
2213             {
2214                 name : 'yes',
2215                 weight : 'primary',
2216                 html : 'Yes'
2217             },
2218             {
2219                 name : 'no',
2220                 html : 'No'
2221             },
2222             {
2223                 name : 'cancel',
2224                 html : 'Cancel'
2225             }
2226         ]
2227 });
2228  /*
2229  * - LGPL
2230  *
2231  * messagebox - can be used as a replace
2232  * 
2233  */
2234 /**
2235  * @class Roo.MessageBox
2236  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2237  * Example usage:
2238  *<pre><code>
2239 // Basic alert:
2240 Roo.Msg.alert('Status', 'Changes saved successfully.');
2241
2242 // Prompt for user data:
2243 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2244     if (btn == 'ok'){
2245         // process text value...
2246     }
2247 });
2248
2249 // Show a dialog using config options:
2250 Roo.Msg.show({
2251    title:'Save Changes?',
2252    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2253    buttons: Roo.Msg.YESNOCANCEL,
2254    fn: processResult,
2255    animEl: 'elId'
2256 });
2257 </code></pre>
2258  * @singleton
2259  */
2260 Roo.bootstrap.MessageBox = function(){
2261     var dlg, opt, mask, waitTimer;
2262     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2263     var buttons, activeTextEl, bwidth;
2264
2265     
2266     // private
2267     var handleButton = function(button){
2268         dlg.hide();
2269         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2270     };
2271
2272     // private
2273     var handleHide = function(){
2274         if(opt && opt.cls){
2275             dlg.el.removeClass(opt.cls);
2276         }
2277         //if(waitTimer){
2278         //    Roo.TaskMgr.stop(waitTimer);
2279         //    waitTimer = null;
2280         //}
2281     };
2282
2283     // private
2284     var updateButtons = function(b){
2285         var width = 0;
2286         if(!b){
2287             buttons["ok"].hide();
2288             buttons["cancel"].hide();
2289             buttons["yes"].hide();
2290             buttons["no"].hide();
2291             //dlg.footer.dom.style.display = 'none';
2292             return width;
2293         }
2294         dlg.footer.dom.style.display = '';
2295         for(var k in buttons){
2296             if(typeof buttons[k] != "function"){
2297                 if(b[k]){
2298                     buttons[k].show();
2299                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2300                     width += buttons[k].el.getWidth()+15;
2301                 }else{
2302                     buttons[k].hide();
2303                 }
2304             }
2305         }
2306         return width;
2307     };
2308
2309     // private
2310     var handleEsc = function(d, k, e){
2311         if(opt && opt.closable !== false){
2312             dlg.hide();
2313         }
2314         if(e){
2315             e.stopEvent();
2316         }
2317     };
2318
2319     return {
2320         /**
2321          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2322          * @return {Roo.BasicDialog} The BasicDialog element
2323          */
2324         getDialog : function(){
2325            if(!dlg){
2326                 dlg = new Roo.bootstrap.Modal( {
2327                     //draggable: true,
2328                     //resizable:false,
2329                     //constraintoviewport:false,
2330                     //fixedcenter:true,
2331                     //collapsible : false,
2332                     //shim:true,
2333                     //modal: true,
2334                   //  width:400,
2335                   //  height:100,
2336                     //buttonAlign:"center",
2337                     closeClick : function(){
2338                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2339                             handleButton("no");
2340                         }else{
2341                             handleButton("cancel");
2342                         }
2343                     }
2344                 });
2345                 dlg.render();
2346                 dlg.on("hide", handleHide);
2347                 mask = dlg.mask;
2348                 //dlg.addKeyListener(27, handleEsc);
2349                 buttons = {};
2350                 this.buttons = buttons;
2351                 var bt = this.buttonText;
2352                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2353                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2354                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2355                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2356                 Roo.log(buttons)
2357                 bodyEl = dlg.body.createChild({
2358
2359                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2360                         '<textarea class="roo-mb-textarea"></textarea>' +
2361                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2362                 });
2363                 msgEl = bodyEl.dom.firstChild;
2364                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2365                 textboxEl.enableDisplayMode();
2366                 textboxEl.addKeyListener([10,13], function(){
2367                     if(dlg.isVisible() && opt && opt.buttons){
2368                         if(opt.buttons.ok){
2369                             handleButton("ok");
2370                         }else if(opt.buttons.yes){
2371                             handleButton("yes");
2372                         }
2373                     }
2374                 });
2375                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2376                 textareaEl.enableDisplayMode();
2377                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2378                 progressEl.enableDisplayMode();
2379                 var pf = progressEl.dom.firstChild;
2380                 if (pf) {
2381                     pp = Roo.get(pf.firstChild);
2382                     pp.setHeight(pf.offsetHeight);
2383                 }
2384                 
2385             }
2386             return dlg;
2387         },
2388
2389         /**
2390          * Updates the message box body text
2391          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2392          * the XHTML-compliant non-breaking space character '&amp;#160;')
2393          * @return {Roo.MessageBox} This message box
2394          */
2395         updateText : function(text){
2396             if(!dlg.isVisible() && !opt.width){
2397                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2398             }
2399             msgEl.innerHTML = text || '&#160;';
2400       
2401             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2402             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2403             var w = Math.max(
2404                     Math.min(opt.width || cw , this.maxWidth), 
2405                     Math.max(opt.minWidth || this.minWidth, bwidth)
2406             );
2407             if(opt.prompt){
2408                 activeTextEl.setWidth(w);
2409             }
2410             if(dlg.isVisible()){
2411                 dlg.fixedcenter = false;
2412             }
2413             // to big, make it scroll. = But as usual stupid IE does not support
2414             // !important..
2415             
2416             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2417                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2418                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2419             } else {
2420                 bodyEl.dom.style.height = '';
2421                 bodyEl.dom.style.overflowY = '';
2422             }
2423             if (cw > w) {
2424                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2425             } else {
2426                 bodyEl.dom.style.overflowX = '';
2427             }
2428             
2429             dlg.setContentSize(w, bodyEl.getHeight());
2430             if(dlg.isVisible()){
2431                 dlg.fixedcenter = true;
2432             }
2433             return this;
2434         },
2435
2436         /**
2437          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2438          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2439          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2440          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2441          * @return {Roo.MessageBox} This message box
2442          */
2443         updateProgress : function(value, text){
2444             if(text){
2445                 this.updateText(text);
2446             }
2447             if (pp) { // weird bug on my firefox - for some reason this is not defined
2448                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2449             }
2450             return this;
2451         },        
2452
2453         /**
2454          * Returns true if the message box is currently displayed
2455          * @return {Boolean} True if the message box is visible, else false
2456          */
2457         isVisible : function(){
2458             return dlg && dlg.isVisible();  
2459         },
2460
2461         /**
2462          * Hides the message box if it is displayed
2463          */
2464         hide : function(){
2465             if(this.isVisible()){
2466                 dlg.hide();
2467             }  
2468         },
2469
2470         /**
2471          * Displays a new message box, or reinitializes an existing message box, based on the config options
2472          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2473          * The following config object properties are supported:
2474          * <pre>
2475 Property    Type             Description
2476 ----------  ---------------  ------------------------------------------------------------------------------------
2477 animEl            String/Element   An id or Element from which the message box should animate as it opens and
2478                                    closes (defaults to undefined)
2479 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2480                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
2481 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
2482                                    progress and wait dialogs will ignore this property and always hide the
2483                                    close button as they can only be closed programmatically.
2484 cls               String           A custom CSS class to apply to the message box element
2485 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
2486                                    displayed (defaults to 75)
2487 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
2488                                    function will be btn (the name of the button that was clicked, if applicable,
2489                                    e.g. "ok"), and text (the value of the active text field, if applicable).
2490                                    Progress and wait dialogs will ignore this option since they do not respond to
2491                                    user actions and can only be closed programmatically, so any required function
2492                                    should be called by the same code after it closes the dialog.
2493 icon              String           A CSS class that provides a background image to be used as an icon for
2494                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2495 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
2496 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
2497 modal             Boolean          False to allow user interaction with the page while the message box is
2498                                    displayed (defaults to true)
2499 msg               String           A string that will replace the existing message box body text (defaults
2500                                    to the XHTML-compliant non-breaking space character '&#160;')
2501 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
2502 progress          Boolean          True to display a progress bar (defaults to false)
2503 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
2504 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
2505 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
2506 title             String           The title text
2507 value             String           The string value to set into the active textbox element if displayed
2508 wait              Boolean          True to display a progress bar (defaults to false)
2509 width             Number           The width of the dialog in pixels
2510 </pre>
2511          *
2512          * Example usage:
2513          * <pre><code>
2514 Roo.Msg.show({
2515    title: 'Address',
2516    msg: 'Please enter your address:',
2517    width: 300,
2518    buttons: Roo.MessageBox.OKCANCEL,
2519    multiline: true,
2520    fn: saveAddress,
2521    animEl: 'addAddressBtn'
2522 });
2523 </code></pre>
2524          * @param {Object} config Configuration options
2525          * @return {Roo.MessageBox} This message box
2526          */
2527         show : function(options)
2528         {
2529             
2530             // this causes nightmares if you show one dialog after another
2531             // especially on callbacks..
2532              
2533             if(this.isVisible()){
2534                 
2535                 this.hide();
2536                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2537                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
2538                 Roo.log("New Dialog Message:" +  options.msg )
2539                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2540                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2541                 
2542             }
2543             var d = this.getDialog();
2544             opt = options;
2545             d.setTitle(opt.title || "&#160;");
2546             d.close.setDisplayed(opt.closable !== false);
2547             activeTextEl = textboxEl;
2548             opt.prompt = opt.prompt || (opt.multiline ? true : false);
2549             if(opt.prompt){
2550                 if(opt.multiline){
2551                     textboxEl.hide();
2552                     textareaEl.show();
2553                     textareaEl.setHeight(typeof opt.multiline == "number" ?
2554                         opt.multiline : this.defaultTextHeight);
2555                     activeTextEl = textareaEl;
2556                 }else{
2557                     textboxEl.show();
2558                     textareaEl.hide();
2559                 }
2560             }else{
2561                 textboxEl.hide();
2562                 textareaEl.hide();
2563             }
2564             progressEl.setDisplayed(opt.progress === true);
2565             this.updateProgress(0);
2566             activeTextEl.dom.value = opt.value || "";
2567             if(opt.prompt){
2568                 dlg.setDefaultButton(activeTextEl);
2569             }else{
2570                 var bs = opt.buttons;
2571                 var db = null;
2572                 if(bs && bs.ok){
2573                     db = buttons["ok"];
2574                 }else if(bs && bs.yes){
2575                     db = buttons["yes"];
2576                 }
2577                 dlg.setDefaultButton(db);
2578             }
2579             bwidth = updateButtons(opt.buttons);
2580             this.updateText(opt.msg);
2581             if(opt.cls){
2582                 d.el.addClass(opt.cls);
2583             }
2584             d.proxyDrag = opt.proxyDrag === true;
2585             d.modal = opt.modal !== false;
2586             d.mask = opt.modal !== false ? mask : false;
2587             if(!d.isVisible()){
2588                 // force it to the end of the z-index stack so it gets a cursor in FF
2589                 document.body.appendChild(dlg.el.dom);
2590                 d.animateTarget = null;
2591                 d.show(options.animEl);
2592             }
2593             return this;
2594         },
2595
2596         /**
2597          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
2598          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2599          * and closing the message box when the process is complete.
2600          * @param {String} title The title bar text
2601          * @param {String} msg The message box body text
2602          * @return {Roo.MessageBox} This message box
2603          */
2604         progress : function(title, msg){
2605             this.show({
2606                 title : title,
2607                 msg : msg,
2608                 buttons: false,
2609                 progress:true,
2610                 closable:false,
2611                 minWidth: this.minProgressWidth,
2612                 modal : true
2613             });
2614             return this;
2615         },
2616
2617         /**
2618          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2619          * If a callback function is passed it will be called after the user clicks the button, and the
2620          * id of the button that was clicked will be passed as the only parameter to the callback
2621          * (could also be the top-right close button).
2622          * @param {String} title The title bar text
2623          * @param {String} msg The message box body text
2624          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2625          * @param {Object} scope (optional) The scope of the callback function
2626          * @return {Roo.MessageBox} This message box
2627          */
2628         alert : function(title, msg, fn, scope){
2629             this.show({
2630                 title : title,
2631                 msg : msg,
2632                 buttons: this.OK,
2633                 fn: fn,
2634                 scope : scope,
2635                 modal : true
2636             });
2637             return this;
2638         },
2639
2640         /**
2641          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
2642          * interaction while waiting for a long-running process to complete that does not have defined intervals.
2643          * You are responsible for closing the message box when the process is complete.
2644          * @param {String} msg The message box body text
2645          * @param {String} title (optional) The title bar text
2646          * @return {Roo.MessageBox} This message box
2647          */
2648         wait : function(msg, title){
2649             this.show({
2650                 title : title,
2651                 msg : msg,
2652                 buttons: false,
2653                 closable:false,
2654                 progress:true,
2655                 modal:true,
2656                 width:300,
2657                 wait:true
2658             });
2659             waitTimer = Roo.TaskMgr.start({
2660                 run: function(i){
2661                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2662                 },
2663                 interval: 1000
2664             });
2665             return this;
2666         },
2667
2668         /**
2669          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2670          * If a callback function is passed it will be called after the user clicks either button, and the id of the
2671          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2672          * @param {String} title The title bar text
2673          * @param {String} msg The message box body text
2674          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2675          * @param {Object} scope (optional) The scope of the callback function
2676          * @return {Roo.MessageBox} This message box
2677          */
2678         confirm : function(title, msg, fn, scope){
2679             this.show({
2680                 title : title,
2681                 msg : msg,
2682                 buttons: this.YESNO,
2683                 fn: fn,
2684                 scope : scope,
2685                 modal : true
2686             });
2687             return this;
2688         },
2689
2690         /**
2691          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2692          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
2693          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2694          * (could also be the top-right close button) and the text that was entered will be passed as the two
2695          * parameters to the callback.
2696          * @param {String} title The title bar text
2697          * @param {String} msg The message box body text
2698          * @param {Function} fn (optional) The callback function invoked after the message box is closed
2699          * @param {Object} scope (optional) The scope of the callback function
2700          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2701          * property, or the height in pixels to create the textbox (defaults to false / single-line)
2702          * @return {Roo.MessageBox} This message box
2703          */
2704         prompt : function(title, msg, fn, scope, multiline){
2705             this.show({
2706                 title : title,
2707                 msg : msg,
2708                 buttons: this.OKCANCEL,
2709                 fn: fn,
2710                 minWidth:250,
2711                 scope : scope,
2712                 prompt:true,
2713                 multiline: multiline,
2714                 modal : true
2715             });
2716             return this;
2717         },
2718
2719         /**
2720          * Button config that displays a single OK button
2721          * @type Object
2722          */
2723         OK : {ok:true},
2724         /**
2725          * Button config that displays Yes and No buttons
2726          * @type Object
2727          */
2728         YESNO : {yes:true, no:true},
2729         /**
2730          * Button config that displays OK and Cancel buttons
2731          * @type Object
2732          */
2733         OKCANCEL : {ok:true, cancel:true},
2734         /**
2735          * Button config that displays Yes, No and Cancel buttons
2736          * @type Object
2737          */
2738         YESNOCANCEL : {yes:true, no:true, cancel:true},
2739
2740         /**
2741          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2742          * @type Number
2743          */
2744         defaultTextHeight : 75,
2745         /**
2746          * The maximum width in pixels of the message box (defaults to 600)
2747          * @type Number
2748          */
2749         maxWidth : 600,
2750         /**
2751          * The minimum width in pixels of the message box (defaults to 100)
2752          * @type Number
2753          */
2754         minWidth : 100,
2755         /**
2756          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
2757          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2758          * @type Number
2759          */
2760         minProgressWidth : 250,
2761         /**
2762          * An object containing the default button text strings that can be overriden for localized language support.
2763          * Supported properties are: ok, cancel, yes and no.
2764          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2765          * @type Object
2766          */
2767         buttonText : {
2768             ok : "OK",
2769             cancel : "Cancel",
2770             yes : "Yes",
2771             no : "No"
2772         }
2773     };
2774 }();
2775
2776 /**
2777  * Shorthand for {@link Roo.MessageBox}
2778  */
2779 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox 
2780 Roo.Msg = Roo.Msg || Roo.MessageBox;
2781 /*
2782  * - LGPL
2783  *
2784  * navbar
2785  * 
2786  */
2787
2788 /**
2789  * @class Roo.bootstrap.Navbar
2790  * @extends Roo.bootstrap.Component
2791  * Bootstrap Navbar class
2792
2793  * @constructor
2794  * Create a new Navbar
2795  * @param {Object} config The config object
2796  */
2797
2798
2799 Roo.bootstrap.Navbar = function(config){
2800     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2801     
2802 };
2803
2804 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
2805     
2806     
2807    
2808     // private
2809     navItems : false,
2810     loadMask : false,
2811     
2812     
2813     getAutoCreate : function(){
2814         
2815         
2816         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2817         
2818     },
2819     
2820     initEvents :function ()
2821     {
2822         //Roo.log(this.el.select('.navbar-toggle',true));
2823         this.el.select('.navbar-toggle',true).on('click', function() {
2824            // Roo.log('click');
2825             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
2826         }, this);
2827         
2828         var mark = {
2829             tag: "div",
2830             cls:"x-dlg-mask"
2831         }
2832         
2833         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2834         
2835         var size = this.el.getSize();
2836         this.maskEl.setSize(size.width, size.height);
2837         this.maskEl.enableDisplayMode("block");
2838         this.maskEl.hide();
2839         
2840         if(this.loadMask){
2841             this.maskEl.show();
2842         }
2843     },
2844     
2845     
2846     getChildContainer : function()
2847     {
2848         if (this.el.select('.collapse').getCount()) {
2849             return this.el.select('.collapse',true).first();
2850         }
2851         
2852         return this.el;
2853     },
2854     
2855     mask : function()
2856     {
2857         this.maskEl.show();
2858     },
2859     
2860     unmask : function()
2861     {
2862         this.maskEl.hide();
2863     } 
2864     
2865     
2866     
2867     
2868 });
2869
2870
2871
2872  
2873
2874  /*
2875  * - LGPL
2876  *
2877  * navbar
2878  * 
2879  */
2880
2881 /**
2882  * @class Roo.bootstrap.NavSimplebar
2883  * @extends Roo.bootstrap.Navbar
2884  * Bootstrap Sidebar class
2885  *
2886  * @cfg {Boolean} inverse is inverted color
2887  * 
2888  * @cfg {String} type (nav | pills | tabs)
2889  * @cfg {Boolean} arrangement stacked | justified
2890  * @cfg {String} align (left | right) alignment
2891  * 
2892  * @cfg {Boolean} main (true|false) main nav bar? default false
2893  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2894  * 
2895  * @cfg {String} tag (header|footer|nav|div) default is nav 
2896
2897  * 
2898  * 
2899  * 
2900  * @constructor
2901  * Create a new Sidebar
2902  * @param {Object} config The config object
2903  */
2904
2905
2906 Roo.bootstrap.NavSimplebar = function(config){
2907     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2908 };
2909
2910 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
2911     
2912     inverse: false,
2913     
2914     type: false,
2915     arrangement: '',
2916     align : false,
2917     
2918     
2919     
2920     main : false,
2921     
2922     
2923     tag : false,
2924     
2925     
2926     getAutoCreate : function(){
2927         
2928         
2929         var cfg = {
2930             tag : this.tag || 'div',
2931             cls : 'navbar'
2932         };
2933           
2934         
2935         cfg.cn = [
2936             {
2937                 cls: 'nav',
2938                 tag : 'ul'
2939             }
2940         ];
2941         
2942          
2943         this.type = this.type || 'nav';
2944         if (['tabs','pills'].indexOf(this.type)!==-1) {
2945             cfg.cn[0].cls += ' nav-' + this.type
2946         
2947         
2948         } else {
2949             if (this.type!=='nav') {
2950                 Roo.log('nav type must be nav/tabs/pills')
2951             }
2952             cfg.cn[0].cls += ' navbar-nav'
2953         }
2954         
2955         
2956         
2957         
2958         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2959             cfg.cn[0].cls += ' nav-' + this.arrangement;
2960         }
2961         
2962         
2963         if (this.align === 'right') {
2964             cfg.cn[0].cls += ' navbar-right';
2965         }
2966         
2967         if (this.inverse) {
2968             cfg.cls += ' navbar-inverse';
2969             
2970         }
2971         
2972         
2973         return cfg;
2974     
2975         
2976     }
2977     
2978     
2979     
2980 });
2981
2982
2983
2984  
2985
2986  
2987        /*
2988  * - LGPL
2989  *
2990  * navbar
2991  * 
2992  */
2993
2994 /**
2995  * @class Roo.bootstrap.NavHeaderbar
2996  * @extends Roo.bootstrap.NavSimplebar
2997  * Bootstrap Sidebar class
2998  *
2999  * @cfg {String} brand what is brand
3000  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3001  * @cfg {String} brand_href href of the brand
3002  * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3003  * 
3004  * @constructor
3005  * Create a new Sidebar
3006  * @param {Object} config The config object
3007  */
3008
3009
3010 Roo.bootstrap.NavHeaderbar = function(config){
3011     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3012 };
3013
3014 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3015     
3016     position: '',
3017     brand: '',
3018     brand_href: false,
3019     srButton : true,
3020     
3021     
3022     getAutoCreate : function(){
3023         
3024         var   cfg = {
3025             tag: this.nav || 'nav',
3026             cls: 'navbar',
3027             role: 'navigation',
3028             cn: []
3029         };
3030         
3031         if(this.srButton){
3032             cfg.cn.push({
3033                 tag: 'div',
3034                 cls: 'navbar-header',
3035                 cn: [
3036                     {
3037                         tag: 'button',
3038                         type: 'button',
3039                         cls: 'navbar-toggle',
3040                         'data-toggle': 'collapse',
3041                         cn: [
3042                             {
3043                                 tag: 'span',
3044                                 cls: 'sr-only',
3045                                 html: 'Toggle navigation'
3046                             },
3047                             {
3048                                 tag: 'span',
3049                                 cls: 'icon-bar'
3050                             },
3051                             {
3052                                 tag: 'span',
3053                                 cls: 'icon-bar'
3054                             },
3055                             {
3056                                 tag: 'span',
3057                                 cls: 'icon-bar'
3058                             }
3059                         ]
3060                     }
3061                 ]
3062             });
3063         }
3064         
3065         cfg.cn.push({
3066             tag: 'div',
3067             cls: 'collapse navbar-collapse',
3068             cn : []
3069         });
3070         
3071         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3072         
3073         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3074             cfg.cls += ' navbar-' + this.position;
3075             
3076             // tag can override this..
3077             
3078             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3079         }
3080         
3081         if (this.brand !== '') {
3082             cfg.cn[0].cn.push({
3083                 tag: 'a',
3084                 href: this.brand_href ? this.brand_href : '#',
3085                 cls: 'navbar-brand',
3086                 cn: [
3087                 this.brand
3088                 ]
3089             });
3090         }
3091         
3092         if(this.main){
3093             cfg.cls += ' main-nav';
3094         }
3095         
3096         
3097         return cfg;
3098
3099         
3100     }
3101     
3102     
3103     
3104 });
3105
3106
3107
3108  
3109
3110  /*
3111  * - LGPL
3112  *
3113  * navbar
3114  * 
3115  */
3116
3117 /**
3118  * @class Roo.bootstrap.NavSidebar
3119  * @extends Roo.bootstrap.Navbar
3120  * Bootstrap Sidebar class
3121  * 
3122  * @constructor
3123  * Create a new Sidebar
3124  * @param {Object} config The config object
3125  */
3126
3127
3128 Roo.bootstrap.NavSidebar = function(config){
3129     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3130 };
3131
3132 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3133     
3134     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3135     
3136     getAutoCreate : function(){
3137         
3138         
3139         return  {
3140             tag: 'div',
3141             cls: 'sidebar sidebar-nav'
3142         };
3143     
3144         
3145     }
3146     
3147     
3148     
3149 });
3150
3151
3152
3153  
3154
3155  /*
3156  * - LGPL
3157  *
3158  * nav group
3159  * 
3160  */
3161
3162 /**
3163  * @class Roo.bootstrap.NavGroup
3164  * @extends Roo.bootstrap.Component
3165  * Bootstrap NavGroup class
3166  * @cfg {String} align left | right
3167  * @cfg {Boolean} inverse false | true
3168  * @cfg {String} type (nav|pills|tab) default nav
3169  * @cfg {String} navId - reference Id for navbar.
3170
3171  * 
3172  * @constructor
3173  * Create a new nav group
3174  * @param {Object} config The config object
3175  */
3176
3177 Roo.bootstrap.NavGroup = function(config){
3178     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3179     this.navItems = [];
3180     Roo.bootstrap.NavGroup.register(this);
3181      this.addEvents({
3182         /**
3183              * @event changed
3184              * Fires when the active item changes
3185              * @param {Roo.bootstrap.NavGroup} this
3186              * @param {Roo.bootstrap.Navbar.Item} item The item selected
3187              * @param {Roo.bootstrap.Navbar.Item} item The previously selected item 
3188          */
3189         'changed': true
3190      });
3191     
3192 };
3193
3194 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3195     
3196     align: '',
3197     inverse: false,
3198     form: false,
3199     type: 'nav',
3200     navId : '',
3201     // private
3202     
3203     navItems : false,
3204     
3205     getAutoCreate : function()
3206     {
3207         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3208         
3209         cfg = {
3210             tag : 'ul',
3211             cls: 'nav' 
3212         }
3213         
3214         if (['tabs','pills'].indexOf(this.type)!==-1) {
3215             cfg.cls += ' nav-' + this.type
3216         } else {
3217             if (this.type!=='nav') {
3218                 Roo.log('nav type must be nav/tabs/pills')
3219             }
3220             cfg.cls += ' navbar-nav'
3221         }
3222         
3223         if (this.parent().sidebar) {
3224             cfg = {
3225                 tag: 'ul',
3226                 cls: 'dashboard-menu sidebar-menu'
3227             }
3228             
3229             return cfg;
3230         }
3231         
3232         if (this.form === true) {
3233             cfg = {
3234                 tag: 'form',
3235                 cls: 'navbar-form'
3236             }
3237             
3238             if (this.align === 'right') {
3239                 cfg.cls += ' navbar-right';
3240             } else {
3241                 cfg.cls += ' navbar-left';
3242             }
3243         }
3244         
3245         if (this.align === 'right') {
3246             cfg.cls += ' navbar-right';
3247         }
3248         
3249         if (this.inverse) {
3250             cfg.cls += ' navbar-inverse';
3251             
3252         }
3253         
3254         
3255         return cfg;
3256     },
3257     
3258     setActiveItem : function(item)
3259     {
3260         var prev = false;
3261         Roo.each(this.navItems, function(v){
3262             if (v == item) {
3263                 return ;
3264             }
3265             if (v.isActive()) {
3266                 v.setActive(false, true);
3267                 prev = v;
3268                 
3269             }
3270             
3271         });
3272
3273         item.setActive(true, true);
3274         this.fireEvent('changed', this, item, prev);
3275         
3276         
3277     },
3278     
3279     addItem : function(cfg)
3280     {
3281         var cn = new Roo.bootstrap.NavItem(cfg);
3282         this.register(cn);
3283         cn.parentId = this.id;
3284         cn.onRender(this.el, null);
3285         return cn;
3286     },
3287     
3288     register : function(item)
3289     {
3290         this.navItems.push( item);
3291         item.navId = this.navId;
3292     
3293     },
3294     getNavItem: function(tabId)
3295     {
3296         var ret = false;
3297         Roo.each(this.navItems, function(e) {
3298             if (e.tabId == tabId) {
3299                ret =  e;
3300                return false;
3301             }
3302             return true;
3303             
3304         });
3305         return ret;
3306     }
3307     
3308     
3309     
3310     
3311 });
3312
3313  
3314 Roo.apply(Roo.bootstrap.NavGroup, {
3315     
3316     groups: {},
3317     
3318     register : function(navgrp)
3319     {
3320         this.groups[navgrp.navId] = navgrp;
3321         
3322     },
3323     get: function(navId) {
3324         return this.groups[navId];
3325     }
3326     
3327     
3328     
3329 });
3330
3331  /*
3332  * - LGPL
3333  *
3334  * row
3335  * 
3336  */
3337
3338 /**
3339  * @class Roo.bootstrap.NavItem
3340  * @extends Roo.bootstrap.Component
3341  * Bootstrap Navbar.NavItem class
3342  * @cfg {String} href  link to
3343  * @cfg {String} html content of button
3344  * @cfg {String} badge text inside badge
3345  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3346  * @cfg {String} glyphicon name of glyphicon
3347  * @cfg {String} icon name of font awesome icon
3348  * @cfg {Boolean} active Is item active
3349  * @cfg {Boolean} disabled Is item disabled
3350  
3351  * @cfg {Boolean} preventDefault (true | false) default false
3352  * @cfg {String} tabId the tab that this item activates.
3353  * @cfg {String} tagtype (a|span) render as a href or span?
3354   
3355  * @constructor
3356  * Create a new Navbar Item
3357  * @param {Object} config The config object
3358  */
3359 Roo.bootstrap.NavItem = function(config){
3360     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3361     this.addEvents({
3362         // raw events
3363         /**
3364          * @event click
3365          * The raw click event for the entire grid.
3366          * @param {Roo.EventObject} e
3367          */
3368         "click" : true,
3369          /**
3370             * @event changed
3371             * Fires when the active item active state changes
3372             * @param {Roo.bootstrap.NavItem} this
3373             * @param {boolean} state the new state
3374              
3375          */
3376         'changed': true
3377     });
3378    
3379 };
3380
3381 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
3382     
3383     href: false,
3384     html: '',
3385     badge: '',
3386     icon: false,
3387     glyphicon: false,
3388     active: false,
3389     preventDefault : false,
3390     tabId : false,
3391     tagtype : 'a',
3392     disabled : false,
3393     
3394     getAutoCreate : function(){
3395          
3396         var cfg = {
3397             tag: 'li',
3398             cls: 'nav-item'
3399             
3400         }
3401         if (this.active) {
3402             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3403         }
3404         if (this.disabled) {
3405             cfg.cls += ' disabled';
3406         }
3407         
3408         if (this.href || this.html || this.glyphicon || this.icon) {
3409             cfg.cn = [
3410                 {
3411                     tag: this.tagtype,
3412                     href : this.href || "#",
3413                     html: this.html || ''
3414                 }
3415             ];
3416             
3417             if (this.icon) {
3418                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3419             }
3420
3421             if(this.glyphicon) {
3422                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
3423             }
3424             
3425             if (this.menu) {
3426                 
3427                 cfg.cn[0].html += " <span class='caret'></span>";
3428              
3429             }
3430             
3431             if (this.badge !== '') {
3432                  
3433                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3434             }
3435         }
3436         
3437         
3438         
3439         return cfg;
3440     },
3441     initEvents: function() {
3442        // Roo.log('init events?');
3443        // Roo.log(this.el.dom);
3444         if (typeof (this.menu) != 'undefined') {
3445             this.menu.parentType = this.xtype;
3446             this.menu.triggerEl = this.el;
3447             this.addxtype(Roo.apply({}, this.menu));
3448         }
3449
3450        
3451         this.el.select('a',true).on('click', this.onClick, this);
3452         // at this point parent should be available..
3453         this.parent().register(this);
3454     },
3455     
3456     onClick : function(e)
3457     {
3458          
3459         if(this.preventDefault){
3460             e.preventDefault();
3461         }
3462         if (this.disabled) {
3463             return;
3464         }
3465         Roo.log("fire event clicked");
3466         if(this.fireEvent('click', this, e) === false){
3467             return;
3468         };
3469         
3470         if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3471             if (typeof(this.parent().setActiveItem) !== 'undefined') {
3472                 this.parent().setActiveItem(this);
3473             }
3474             
3475             
3476             
3477         } 
3478     },
3479     
3480     isActive: function () {
3481         return this.active
3482     },
3483     setActive : function(state, fire)
3484     {
3485         this.active = state;
3486         if (!state ) {
3487             this.el.removeClass('active');
3488         } else if (!this.el.hasClass('active')) {
3489             this.el.addClass('active');
3490         }
3491         if (fire) {
3492             this.fireEvent('changed', this, state);
3493         }
3494         
3495         
3496     },
3497      // this should not be here...
3498     setDisabled : function(state)
3499     {
3500         this.disabled = state;
3501         if (!state ) {
3502             this.el.removeClass('disabled');
3503         } else if (!this.el.hasClass('disabled')) {
3504             this.el.addClass('disabled');
3505         }
3506         
3507     }
3508 });
3509  
3510
3511  /*
3512  * - LGPL
3513  *
3514  * sidebar item
3515  *
3516  *  li
3517  *    <span> icon </span>
3518  *    <span> text </span>
3519  *    <span>badge </span>
3520  */
3521
3522 /**
3523  * @class Roo.bootstrap.NavSidebarItem
3524  * @extends Roo.bootstrap.NavItem
3525  * Bootstrap Navbar.NavSidebarItem class
3526  * @constructor
3527  * Create a new Navbar Button
3528  * @param {Object} config The config object
3529  */
3530 Roo.bootstrap.NavSidebarItem = function(config){
3531     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3532     this.addEvents({
3533         // raw events
3534         /**
3535          * @event click
3536          * The raw click event for the entire grid.
3537          * @param {Roo.EventObject} e
3538          */
3539         "click" : true,
3540          /**
3541             * @event changed
3542             * Fires when the active item active state changes
3543             * @param {Roo.bootstrap.NavSidebarItem} this
3544             * @param {boolean} state the new state
3545              
3546          */
3547         'changed': true
3548     });
3549    
3550 };
3551
3552 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
3553     
3554     
3555     getAutoCreate : function(){
3556         
3557         
3558         var a = {
3559                 tag: 'a',
3560                 href : this.href || '#',
3561                 cls: '',
3562                 html : '',
3563                 cn : []
3564         };
3565         var cfg = {
3566             tag: 'li',
3567             cls: '',
3568             cn: [ a ]
3569         }
3570         var span = {
3571             tag: 'span',
3572             html : this.html || ''
3573         }
3574         
3575         
3576         if (this.active) {
3577             cfg.cls += ' active';
3578         }
3579         
3580         // left icon..
3581         if (this.glyphicon || this.icon) {
3582             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
3583             a.cn.push({ tag : 'i', cls : c }) ;
3584         }
3585         // html..
3586         a.cn.push(span);
3587         // then badge..
3588         if (this.badge !== '') {
3589             a.cn.push({ tag: 'span',  cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge }); 
3590         }
3591         // fi
3592         if (this.menu) {
3593             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3594             a.cls += 'dropdown-toggle treeview' ;
3595             
3596         }
3597         
3598         
3599         
3600         return cfg;
3601          
3602            
3603     }
3604    
3605      
3606  
3607 });
3608  
3609
3610  /*
3611  * - LGPL
3612  *
3613  * row
3614  * 
3615  */
3616
3617 /**
3618  * @class Roo.bootstrap.Row
3619  * @extends Roo.bootstrap.Component
3620  * Bootstrap Row class (contains columns...)
3621  * 
3622  * @constructor
3623  * Create a new Row
3624  * @param {Object} config The config object
3625  */
3626
3627 Roo.bootstrap.Row = function(config){
3628     Roo.bootstrap.Row.superclass.constructor.call(this, config);
3629 };
3630
3631 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
3632     
3633     getAutoCreate : function(){
3634        return {
3635             cls: 'row clearfix'
3636        };
3637     }
3638     
3639     
3640 });
3641
3642  
3643
3644  /*
3645  * - LGPL
3646  *
3647  * element
3648  * 
3649  */
3650
3651 /**
3652  * @class Roo.bootstrap.Element
3653  * @extends Roo.bootstrap.Component
3654  * Bootstrap Element class
3655  * @cfg {String} html contents of the element
3656  * @cfg {String} tag tag of the element
3657  * @cfg {String} cls class of the element
3658  * 
3659  * @constructor
3660  * Create a new Element
3661  * @param {Object} config The config object
3662  */
3663
3664 Roo.bootstrap.Element = function(config){
3665     Roo.bootstrap.Element.superclass.constructor.call(this, config);
3666 };
3667
3668 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
3669     
3670     tag: 'div',
3671     cls: '',
3672     html: '',
3673      
3674     
3675     getAutoCreate : function(){
3676         
3677         var cfg = {
3678             tag: this.tag,
3679             cls: this.cls,
3680             html: this.html
3681         }
3682         
3683         
3684         
3685         return cfg;
3686     }
3687    
3688 });
3689
3690  
3691
3692  /*
3693  * - LGPL
3694  *
3695  * pagination
3696  * 
3697  */
3698
3699 /**
3700  * @class Roo.bootstrap.Pagination
3701  * @extends Roo.bootstrap.Component
3702  * Bootstrap Pagination class
3703  * @cfg {String} size xs | sm | md | lg
3704  * @cfg {Boolean} inverse false | true
3705  * 
3706  * @constructor
3707  * Create a new Pagination
3708  * @param {Object} config The config object
3709  */
3710
3711 Roo.bootstrap.Pagination = function(config){
3712     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3713 };
3714
3715 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
3716     
3717     cls: false,
3718     size: false,
3719     inverse: false,
3720     
3721     getAutoCreate : function(){
3722         var cfg = {
3723             tag: 'ul',
3724                 cls: 'pagination'
3725         };
3726         if (this.inverse) {
3727             cfg.cls += ' inverse';
3728         }
3729         if (this.html) {
3730             cfg.html=this.html;
3731         }
3732         if (this.cls) {
3733             cfg.cls += " " + this.cls;
3734         }
3735         return cfg;
3736     }
3737    
3738 });
3739
3740  
3741
3742  /*
3743  * - LGPL
3744  *
3745  * Pagination item
3746  * 
3747  */
3748
3749
3750 /**
3751  * @class Roo.bootstrap.PaginationItem
3752  * @extends Roo.bootstrap.Component
3753  * Bootstrap PaginationItem class
3754  * @cfg {String} html text
3755  * @cfg {String} href the link
3756  * @cfg {Boolean} preventDefault (true | false) default true
3757  * @cfg {Boolean} active (true | false) default false
3758  * 
3759  * 
3760  * @constructor
3761  * Create a new PaginationItem
3762  * @param {Object} config The config object
3763  */
3764
3765
3766 Roo.bootstrap.PaginationItem = function(config){
3767     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3768     this.addEvents({
3769         // raw events
3770         /**
3771          * @event click
3772          * The raw click event for the entire grid.
3773          * @param {Roo.EventObject} e
3774          */
3775         "click" : true
3776     });
3777 };
3778
3779 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
3780     
3781     href : false,
3782     html : false,
3783     preventDefault: true,
3784     active : false,
3785     cls : false,
3786     
3787     getAutoCreate : function(){
3788         var cfg= {
3789             tag: 'li',
3790             cn: [
3791                 {
3792                     tag : 'a',
3793                     href : this.href ? this.href : '#',
3794                     html : this.html ? this.html : ''
3795                 }
3796             ]
3797         };
3798         
3799         if(this.cls){
3800             cfg.cls = this.cls;
3801         }
3802         
3803         if(this.active){
3804             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3805         }
3806         
3807         return cfg;
3808     },
3809     
3810     initEvents: function() {
3811         
3812         this.el.on('click', this.onClick, this);
3813         
3814     },
3815     onClick : function(e)
3816     {
3817         Roo.log('PaginationItem on click ');
3818         if(this.preventDefault){
3819             e.preventDefault();
3820         }
3821         
3822         this.fireEvent('click', this, e);
3823     }
3824    
3825 });
3826
3827  
3828
3829  /*
3830  * - LGPL
3831  *
3832  * slider
3833  * 
3834  */
3835
3836
3837 /**
3838  * @class Roo.bootstrap.Slider
3839  * @extends Roo.bootstrap.Component
3840  * Bootstrap Slider class
3841  *    
3842  * @constructor
3843  * Create a new Slider
3844  * @param {Object} config The config object
3845  */
3846
3847 Roo.bootstrap.Slider = function(config){
3848     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3849 };
3850
3851 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
3852     
3853     getAutoCreate : function(){
3854         
3855         var cfg = {
3856             tag: 'div',
3857             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3858             cn: [
3859                 {
3860                     tag: 'a',
3861                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
3862                 }
3863             ]
3864         }
3865         
3866         return cfg;
3867     }
3868    
3869 });
3870
3871  /*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881  
3882
3883 /**
3884  * @class Roo.grid.ColumnModel
3885  * @extends Roo.util.Observable
3886  * This is the default implementation of a ColumnModel used by the Grid. It defines
3887  * the columns in the grid.
3888  * <br>Usage:<br>
3889  <pre><code>
3890  var colModel = new Roo.grid.ColumnModel([
3891         {header: "Ticker", width: 60, sortable: true, locked: true},
3892         {header: "Company Name", width: 150, sortable: true},
3893         {header: "Market Cap.", width: 100, sortable: true},
3894         {header: "$ Sales", width: 100, sortable: true, renderer: money},
3895         {header: "Employees", width: 100, sortable: true, resizable: false}
3896  ]);
3897  </code></pre>
3898  * <p>
3899  
3900  * The config options listed for this class are options which may appear in each
3901  * individual column definition.
3902  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
3903  * @constructor
3904  * @param {Object} config An Array of column config objects. See this class's
3905  * config objects for details.
3906 */
3907 Roo.grid.ColumnModel = function(config){
3908         /**
3909      * The config passed into the constructor
3910      */
3911     this.config = config;
3912     this.lookup = {};
3913
3914     // if no id, create one
3915     // if the column does not have a dataIndex mapping,
3916     // map it to the order it is in the config
3917     for(var i = 0, len = config.length; i < len; i++){
3918         var c = config[i];
3919         if(typeof c.dataIndex == "undefined"){
3920             c.dataIndex = i;
3921         }
3922         if(typeof c.renderer == "string"){
3923             c.renderer = Roo.util.Format[c.renderer];
3924         }
3925         if(typeof c.id == "undefined"){
3926             c.id = Roo.id();
3927         }
3928         if(c.editor && c.editor.xtype){
3929             c.editor  = Roo.factory(c.editor, Roo.grid);
3930         }
3931         if(c.editor && c.editor.isFormField){
3932             c.editor = new Roo.grid.GridEditor(c.editor);
3933         }
3934         this.lookup[c.id] = c;
3935     }
3936
3937     /**
3938      * The width of columns which have no width specified (defaults to 100)
3939      * @type Number
3940      */
3941     this.defaultWidth = 100;
3942
3943     /**
3944      * Default sortable of columns which have no sortable specified (defaults to false)
3945      * @type Boolean
3946      */
3947     this.defaultSortable = false;
3948
3949     this.addEvents({
3950         /**
3951              * @event widthchange
3952              * Fires when the width of a column changes.
3953              * @param {ColumnModel} this
3954              * @param {Number} columnIndex The column index
3955              * @param {Number} newWidth The new width
3956              */
3957             "widthchange": true,
3958         /**
3959              * @event headerchange
3960              * Fires when the text of a header changes.
3961              * @param {ColumnModel} this
3962              * @param {Number} columnIndex The column index
3963              * @param {Number} newText The new header text
3964              */
3965             "headerchange": true,
3966         /**
3967              * @event hiddenchange
3968              * Fires when a column is hidden or "unhidden".
3969              * @param {ColumnModel} this
3970              * @param {Number} columnIndex The column index
3971              * @param {Boolean} hidden true if hidden, false otherwise
3972              */
3973             "hiddenchange": true,
3974             /**
3975          * @event columnmoved
3976          * Fires when a column is moved.
3977          * @param {ColumnModel} this
3978          * @param {Number} oldIndex
3979          * @param {Number} newIndex
3980          */
3981         "columnmoved" : true,
3982         /**
3983          * @event columlockchange
3984          * Fires when a column's locked state is changed
3985          * @param {ColumnModel} this
3986          * @param {Number} colIndex
3987          * @param {Boolean} locked true if locked
3988          */
3989         "columnlockchange" : true
3990     });
3991     Roo.grid.ColumnModel.superclass.constructor.call(this);
3992 };
3993 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
3994     /**
3995      * @cfg {String} header The header text to display in the Grid view.
3996      */
3997     /**
3998      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
3999      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4000      * specified, the column's index is used as an index into the Record's data Array.
4001      */
4002     /**
4003      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4004      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4005      */
4006     /**
4007      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4008      * Defaults to the value of the {@link #defaultSortable} property.
4009      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4010      */
4011     /**
4012      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4013      */
4014     /**
4015      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4016      */
4017     /**
4018      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4019      */
4020     /**
4021      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4022      */
4023     /**
4024      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4025      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4026      * default renderer uses the raw data value.
4027      */
4028        /**
4029      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4030      */
4031     /**
4032      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
4033      */
4034
4035     /**
4036      * Returns the id of the column at the specified index.
4037      * @param {Number} index The column index
4038      * @return {String} the id
4039      */
4040     getColumnId : function(index){
4041         return this.config[index].id;
4042     },
4043
4044     /**
4045      * Returns the column for a specified id.
4046      * @param {String} id The column id
4047      * @return {Object} the column
4048      */
4049     getColumnById : function(id){
4050         return this.lookup[id];
4051     },
4052
4053     
4054     /**
4055      * Returns the column for a specified dataIndex.
4056      * @param {String} dataIndex The column dataIndex
4057      * @return {Object|Boolean} the column or false if not found
4058      */
4059     getColumnByDataIndex: function(dataIndex){
4060         var index = this.findColumnIndex(dataIndex);
4061         return index > -1 ? this.config[index] : false;
4062     },
4063     
4064     /**
4065      * Returns the index for a specified column id.
4066      * @param {String} id The column id
4067      * @return {Number} the index, or -1 if not found
4068      */
4069     getIndexById : function(id){
4070         for(var i = 0, len = this.config.length; i < len; i++){
4071             if(this.config[i].id == id){
4072                 return i;
4073             }
4074         }
4075         return -1;
4076     },
4077     
4078     /**
4079      * Returns the index for a specified column dataIndex.
4080      * @param {String} dataIndex The column dataIndex
4081      * @return {Number} the index, or -1 if not found
4082      */
4083     
4084     findColumnIndex : function(dataIndex){
4085         for(var i = 0, len = this.config.length; i < len; i++){
4086             if(this.config[i].dataIndex == dataIndex){
4087                 return i;
4088             }
4089         }
4090         return -1;
4091     },
4092     
4093     
4094     moveColumn : function(oldIndex, newIndex){
4095         var c = this.config[oldIndex];
4096         this.config.splice(oldIndex, 1);
4097         this.config.splice(newIndex, 0, c);
4098         this.dataMap = null;
4099         this.fireEvent("columnmoved", this, oldIndex, newIndex);
4100     },
4101
4102     isLocked : function(colIndex){
4103         return this.config[colIndex].locked === true;
4104     },
4105
4106     setLocked : function(colIndex, value, suppressEvent){
4107         if(this.isLocked(colIndex) == value){
4108             return;
4109         }
4110         this.config[colIndex].locked = value;
4111         if(!suppressEvent){
4112             this.fireEvent("columnlockchange", this, colIndex, value);
4113         }
4114     },
4115
4116     getTotalLockedWidth : function(){
4117         var totalWidth = 0;
4118         for(var i = 0; i < this.config.length; i++){
4119             if(this.isLocked(i) && !this.isHidden(i)){
4120                 this.totalWidth += this.getColumnWidth(i);
4121             }
4122         }
4123         return totalWidth;
4124     },
4125
4126     getLockedCount : function(){
4127         for(var i = 0, len = this.config.length; i < len; i++){
4128             if(!this.isLocked(i)){
4129                 return i;
4130             }
4131         }
4132     },
4133
4134     /**
4135      * Returns the number of columns.
4136      * @return {Number}
4137      */
4138     getColumnCount : function(visibleOnly){
4139         if(visibleOnly === true){
4140             var c = 0;
4141             for(var i = 0, len = this.config.length; i < len; i++){
4142                 if(!this.isHidden(i)){
4143                     c++;
4144                 }
4145             }
4146             return c;
4147         }
4148         return this.config.length;
4149     },
4150
4151     /**
4152      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4153      * @param {Function} fn
4154      * @param {Object} scope (optional)
4155      * @return {Array} result
4156      */
4157     getColumnsBy : function(fn, scope){
4158         var r = [];
4159         for(var i = 0, len = this.config.length; i < len; i++){
4160             var c = this.config[i];
4161             if(fn.call(scope||this, c, i) === true){
4162                 r[r.length] = c;
4163             }
4164         }
4165         return r;
4166     },
4167
4168     /**
4169      * Returns true if the specified column is sortable.
4170      * @param {Number} col The column index
4171      * @return {Boolean}
4172      */
4173     isSortable : function(col){
4174         if(typeof this.config[col].sortable == "undefined"){
4175             return this.defaultSortable;
4176         }
4177         return this.config[col].sortable;
4178     },
4179
4180     /**
4181      * Returns the rendering (formatting) function defined for the column.
4182      * @param {Number} col The column index.
4183      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4184      */
4185     getRenderer : function(col){
4186         if(!this.config[col].renderer){
4187             return Roo.grid.ColumnModel.defaultRenderer;
4188         }
4189         return this.config[col].renderer;
4190     },
4191
4192     /**
4193      * Sets the rendering (formatting) function for a column.
4194      * @param {Number} col The column index
4195      * @param {Function} fn The function to use to process the cell's raw data
4196      * to return HTML markup for the grid view. The render function is called with
4197      * the following parameters:<ul>
4198      * <li>Data value.</li>
4199      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4200      * <li>css A CSS style string to apply to the table cell.</li>
4201      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4202      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4203      * <li>Row index</li>
4204      * <li>Column index</li>
4205      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4206      */
4207     setRenderer : function(col, fn){
4208         this.config[col].renderer = fn;
4209     },
4210
4211     /**
4212      * Returns the width for the specified column.
4213      * @param {Number} col The column index
4214      * @return {Number}
4215      */
4216     getColumnWidth : function(col){
4217         return this.config[col].width * 1 || this.defaultWidth;
4218     },
4219
4220     /**
4221      * Sets the width for a column.
4222      * @param {Number} col The column index
4223      * @param {Number} width The new width
4224      */
4225     setColumnWidth : function(col, width, suppressEvent){
4226         this.config[col].width = width;
4227         this.totalWidth = null;
4228         if(!suppressEvent){
4229              this.fireEvent("widthchange", this, col, width);
4230         }
4231     },
4232
4233     /**
4234      * Returns the total width of all columns.
4235      * @param {Boolean} includeHidden True to include hidden column widths
4236      * @return {Number}
4237      */
4238     getTotalWidth : function(includeHidden){
4239         if(!this.totalWidth){
4240             this.totalWidth = 0;
4241             for(var i = 0, len = this.config.length; i < len; i++){
4242                 if(includeHidden || !this.isHidden(i)){
4243                     this.totalWidth += this.getColumnWidth(i);
4244                 }
4245             }
4246         }
4247         return this.totalWidth;
4248     },
4249
4250     /**
4251      * Returns the header for the specified column.
4252      * @param {Number} col The column index
4253      * @return {String}
4254      */
4255     getColumnHeader : function(col){
4256         return this.config[col].header;
4257     },
4258
4259     /**
4260      * Sets the header for a column.
4261      * @param {Number} col The column index
4262      * @param {String} header The new header
4263      */
4264     setColumnHeader : function(col, header){
4265         this.config[col].header = header;
4266         this.fireEvent("headerchange", this, col, header);
4267     },
4268
4269     /**
4270      * Returns the tooltip for the specified column.
4271      * @param {Number} col The column index
4272      * @return {String}
4273      */
4274     getColumnTooltip : function(col){
4275             return this.config[col].tooltip;
4276     },
4277     /**
4278      * Sets the tooltip for a column.
4279      * @param {Number} col The column index
4280      * @param {String} tooltip The new tooltip
4281      */
4282     setColumnTooltip : function(col, tooltip){
4283             this.config[col].tooltip = tooltip;
4284     },
4285
4286     /**
4287      * Returns the dataIndex for the specified column.
4288      * @param {Number} col The column index
4289      * @return {Number}
4290      */
4291     getDataIndex : function(col){
4292         return this.config[col].dataIndex;
4293     },
4294
4295     /**
4296      * Sets the dataIndex for a column.
4297      * @param {Number} col The column index
4298      * @param {Number} dataIndex The new dataIndex
4299      */
4300     setDataIndex : function(col, dataIndex){
4301         this.config[col].dataIndex = dataIndex;
4302     },
4303
4304     
4305     
4306     /**
4307      * Returns true if the cell is editable.
4308      * @param {Number} colIndex The column index
4309      * @param {Number} rowIndex The row index
4310      * @return {Boolean}
4311      */
4312     isCellEditable : function(colIndex, rowIndex){
4313         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4314     },
4315
4316     /**
4317      * Returns the editor defined for the cell/column.
4318      * return false or null to disable editing.
4319      * @param {Number} colIndex The column index
4320      * @param {Number} rowIndex The row index
4321      * @return {Object}
4322      */
4323     getCellEditor : function(colIndex, rowIndex){
4324         return this.config[colIndex].editor;
4325     },
4326
4327     /**
4328      * Sets if a column is editable.
4329      * @param {Number} col The column index
4330      * @param {Boolean} editable True if the column is editable
4331      */
4332     setEditable : function(col, editable){
4333         this.config[col].editable = editable;
4334     },
4335
4336
4337     /**
4338      * Returns true if the column is hidden.
4339      * @param {Number} colIndex The column index
4340      * @return {Boolean}
4341      */
4342     isHidden : function(colIndex){
4343         return this.config[colIndex].hidden;
4344     },
4345
4346
4347     /**
4348      * Returns true if the column width cannot be changed
4349      */
4350     isFixed : function(colIndex){
4351         return this.config[colIndex].fixed;
4352     },
4353
4354     /**
4355      * Returns true if the column can be resized
4356      * @return {Boolean}
4357      */
4358     isResizable : function(colIndex){
4359         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4360     },
4361     /**
4362      * Sets if a column is hidden.
4363      * @param {Number} colIndex The column index
4364      * @param {Boolean} hidden True if the column is hidden
4365      */
4366     setHidden : function(colIndex, hidden){
4367         this.config[colIndex].hidden = hidden;
4368         this.totalWidth = null;
4369         this.fireEvent("hiddenchange", this, colIndex, hidden);
4370     },
4371
4372     /**
4373      * Sets the editor for a column.
4374      * @param {Number} col The column index
4375      * @param {Object} editor The editor object
4376      */
4377     setEditor : function(col, editor){
4378         this.config[col].editor = editor;
4379     }
4380 });
4381
4382 Roo.grid.ColumnModel.defaultRenderer = function(value){
4383         if(typeof value == "string" && value.length < 1){
4384             return "&#160;";
4385         }
4386         return value;
4387 };
4388
4389 // Alias for backwards compatibility
4390 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4391 /*
4392  * Based on:
4393  * Ext JS Library 1.1.1
4394  * Copyright(c) 2006-2007, Ext JS, LLC.
4395  *
4396  * Originally Released Under LGPL - original licence link has changed is not relivant.
4397  *
4398  * Fork - LGPL
4399  * <script type="text/javascript">
4400  */
4401  
4402 /**
4403  * @class Roo.LoadMask
4404  * A simple utility class for generically masking elements while loading data.  If the element being masked has
4405  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4406  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
4407  * element's UpdateManager load indicator and will be destroyed after the initial load.
4408  * @constructor
4409  * Create a new LoadMask
4410  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4411  * @param {Object} config The config object
4412  */
4413 Roo.LoadMask = function(el, config){
4414     this.el = Roo.get(el);
4415     Roo.apply(this, config);
4416     if(this.store){
4417         this.store.on('beforeload', this.onBeforeLoad, this);
4418         this.store.on('load', this.onLoad, this);
4419         this.store.on('loadexception', this.onLoadException, this);
4420         this.removeMask = false;
4421     }else{
4422         var um = this.el.getUpdateManager();
4423         um.showLoadIndicator = false; // disable the default indicator
4424         um.on('beforeupdate', this.onBeforeLoad, this);
4425         um.on('update', this.onLoad, this);
4426         um.on('failure', this.onLoad, this);
4427         this.removeMask = true;
4428     }
4429 };
4430
4431 Roo.LoadMask.prototype = {
4432     /**
4433      * @cfg {Boolean} removeMask
4434      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4435      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
4436      */
4437     /**
4438      * @cfg {String} msg
4439      * The text to display in a centered loading message box (defaults to 'Loading...')
4440      */
4441     msg : 'Loading...',
4442     /**
4443      * @cfg {String} msgCls
4444      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4445      */
4446     msgCls : 'x-mask-loading',
4447
4448     /**
4449      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4450      * @type Boolean
4451      */
4452     disabled: false,
4453
4454     /**
4455      * Disables the mask to prevent it from being displayed
4456      */
4457     disable : function(){
4458        this.disabled = true;
4459     },
4460
4461     /**
4462      * Enables the mask so that it can be displayed
4463      */
4464     enable : function(){
4465         this.disabled = false;
4466     },
4467     
4468     onLoadException : function()
4469     {
4470         Roo.log(arguments);
4471         
4472         if (typeof(arguments[3]) != 'undefined') {
4473             Roo.MessageBox.alert("Error loading",arguments[3]);
4474         } 
4475         /*
4476         try {
4477             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4478                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4479             }   
4480         } catch(e) {
4481             
4482         }
4483         */
4484     
4485         
4486         
4487         this.el.unmask(this.removeMask);
4488     },
4489     // private
4490     onLoad : function()
4491     {
4492         this.el.unmask(this.removeMask);
4493     },
4494
4495     // private
4496     onBeforeLoad : function(){
4497         if(!this.disabled){
4498             this.el.mask(this.msg, this.msgCls);
4499         }
4500     },
4501
4502     // private
4503     destroy : function(){
4504         if(this.store){
4505             this.store.un('beforeload', this.onBeforeLoad, this);
4506             this.store.un('load', this.onLoad, this);
4507             this.store.un('loadexception', this.onLoadException, this);
4508         }else{
4509             var um = this.el.getUpdateManager();
4510             um.un('beforeupdate', this.onBeforeLoad, this);
4511             um.un('update', this.onLoad, this);
4512             um.un('failure', this.onLoad, this);
4513         }
4514     }
4515 };/*
4516  * - LGPL
4517  *
4518  * table
4519  * 
4520  */
4521
4522 /**
4523  * @class Roo.bootstrap.Table
4524  * @extends Roo.bootstrap.Component
4525  * Bootstrap Table class
4526  * @cfg {String} cls table class
4527  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4528  * @cfg {String} bgcolor Specifies the background color for a table
4529  * @cfg {Number} border Specifies whether the table cells should have borders or not
4530  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4531  * @cfg {Number} cellspacing Specifies the space between cells
4532  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4533  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4534  * @cfg {String} sortable Specifies that the table should be sortable
4535  * @cfg {String} summary Specifies a summary of the content of a table
4536  * @cfg {Number} width Specifies the width of a table
4537  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4538  * 
4539  * @cfg {boolean} striped Should the rows be alternative striped
4540  * @cfg {boolean} bordered Add borders to the table
4541  * @cfg {boolean} hover Add hover highlighting
4542  * @cfg {boolean} condensed Format condensed
4543  * @cfg {boolean} responsive Format condensed
4544  * @cfg {Boolean} loadMask (true|false) default false
4545  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4546  * @cfg {Boolean} thead (true|false) generate thead, default true
4547  * @cfg {Boolean} RowSelection (true|false) default false
4548  * @cfg {Boolean} CellSelection (true|false) default false
4549  *
4550  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
4551  
4552  * 
4553  * @constructor
4554  * Create a new Table
4555  * @param {Object} config The config object
4556  */
4557
4558 Roo.bootstrap.Table = function(config){
4559     Roo.bootstrap.Table.superclass.constructor.call(this, config);
4560     
4561     if (this.sm) {
4562         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4563         this.sm = this.selModel;
4564         this.sm.xmodule = this.xmodule || false;
4565     }
4566     if (this.cm && typeof(this.cm.config) == 'undefined') {
4567         this.colModel = new Roo.grid.ColumnModel(this.cm);
4568         this.cm = this.colModel;
4569         this.cm.xmodule = this.xmodule || false;
4570     }
4571     if (this.store) {
4572         this.store= Roo.factory(this.store, Roo.data);
4573         this.ds = this.store;
4574         this.ds.xmodule = this.xmodule || false;
4575          
4576     }
4577     if (this.footer && this.store) {
4578         this.footer.dataSource = this.ds;
4579         this.footer = Roo.factory(this.footer);
4580     }
4581     
4582     /** @private */
4583     this.addEvents({
4584         /**
4585          * @event cellclick
4586          * Fires when a cell is clicked
4587          * @param {Roo.bootstrap.Table} this
4588          * @param {Roo.Element} el
4589          * @param {Number} rowIndex
4590          * @param {Number} columnIndex
4591          * @param {Roo.EventObject} e
4592          */
4593         "cellclick" : true,
4594         /**
4595          * @event celldblclick
4596          * Fires when a cell is double clicked
4597          * @param {Roo.bootstrap.Table} this
4598          * @param {Roo.Element} el
4599          * @param {Number} rowIndex
4600          * @param {Number} columnIndex
4601          * @param {Roo.EventObject} e
4602          */
4603         "celldblclick" : true,
4604         /**
4605          * @event rowclick
4606          * Fires when a row is clicked
4607          * @param {Roo.bootstrap.Table} this
4608          * @param {Roo.Element} el
4609          * @param {Number} rowIndex
4610          * @param {Roo.EventObject} e
4611          */
4612         "rowclick" : true,
4613         /**
4614          * @event rowdblclick
4615          * Fires when a row is double clicked
4616          * @param {Roo.bootstrap.Table} this
4617          * @param {Roo.Element} el
4618          * @param {Number} rowIndex
4619          * @param {Roo.EventObject} e
4620          */
4621         "rowdblclick" : true,
4622         /**
4623          * @event mouseover
4624          * Fires when a mouseover occur
4625          * @param {Roo.bootstrap.Table} this
4626          * @param {Roo.Element} el
4627          * @param {Number} rowIndex
4628          * @param {Number} columnIndex
4629          * @param {Roo.EventObject} e
4630          */
4631         "mouseover" : true,
4632         /**
4633          * @event mouseout
4634          * Fires when a mouseout occur
4635          * @param {Roo.bootstrap.Table} this
4636          * @param {Roo.Element} el
4637          * @param {Number} rowIndex
4638          * @param {Number} columnIndex
4639          * @param {Roo.EventObject} e
4640          */
4641         "mouseout" : true,
4642         /**
4643          * @event rowclass
4644          * Fires when a row is rendered, so you can change add a style to it.
4645          * @param {Roo.bootstrap.Table} this
4646          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
4647          */
4648         'rowclass' : true
4649         
4650     });
4651 };
4652
4653 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
4654     
4655     cls: false,
4656     align: false,
4657     bgcolor: false,
4658     border: false,
4659     cellpadding: false,
4660     cellspacing: false,
4661     frame: false,
4662     rules: false,
4663     sortable: false,
4664     summary: false,
4665     width: false,
4666     striped : false,
4667     bordered: false,
4668     hover:  false,
4669     condensed : false,
4670     responsive : false,
4671     sm : false,
4672     cm : false,
4673     store : false,
4674     loadMask : false,
4675     tfoot : true,
4676     thead : true,
4677     RowSelection : false,
4678     CellSelection : false,
4679     layout : false,
4680     
4681     // Roo.Element - the tbody
4682     mainBody: false, 
4683     
4684     getAutoCreate : function(){
4685         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4686         
4687         cfg = {
4688             tag: 'table',
4689             cls : 'table',
4690             cn : []
4691         }
4692             
4693         if (this.striped) {
4694             cfg.cls += ' table-striped';
4695         }
4696         
4697         if (this.hover) {
4698             cfg.cls += ' table-hover';
4699         }
4700         if (this.bordered) {
4701             cfg.cls += ' table-bordered';
4702         }
4703         if (this.condensed) {
4704             cfg.cls += ' table-condensed';
4705         }
4706         if (this.responsive) {
4707             cfg.cls += ' table-responsive';
4708         }
4709         
4710         if (this.cls) {
4711             cfg.cls+=  ' ' +this.cls;
4712         }
4713         
4714         // this lot should be simplifed...
4715         
4716         if (this.align) {
4717             cfg.align=this.align;
4718         }
4719         if (this.bgcolor) {
4720             cfg.bgcolor=this.bgcolor;
4721         }
4722         if (this.border) {
4723             cfg.border=this.border;
4724         }
4725         if (this.cellpadding) {
4726             cfg.cellpadding=this.cellpadding;
4727         }
4728         if (this.cellspacing) {
4729             cfg.cellspacing=this.cellspacing;
4730         }
4731         if (this.frame) {
4732             cfg.frame=this.frame;
4733         }
4734         if (this.rules) {
4735             cfg.rules=this.rules;
4736         }
4737         if (this.sortable) {
4738             cfg.sortable=this.sortable;
4739         }
4740         if (this.summary) {
4741             cfg.summary=this.summary;
4742         }
4743         if (this.width) {
4744             cfg.width=this.width;
4745         }
4746         if (this.layout) {
4747             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
4748         }
4749         
4750         if(this.store || this.cm){
4751             if(this.thead){
4752                 cfg.cn.push(this.renderHeader());
4753             }
4754             
4755             cfg.cn.push(this.renderBody());
4756             
4757             if(this.tfoot){
4758                 cfg.cn.push(this.renderFooter());
4759             }
4760             
4761             cfg.cls+=  ' TableGrid';
4762         }
4763         
4764         return { cn : [ cfg ] };
4765     },
4766     
4767     initEvents : function()
4768     {   
4769         if(!this.store || !this.cm){
4770             return;
4771         }
4772         
4773         //Roo.log('initEvents with ds!!!!');
4774         
4775         this.mainBody = this.el.select('tbody', true).first();
4776         
4777         
4778         var _this = this;
4779         
4780         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4781             e.on('click', _this.sort, _this);
4782         });
4783         
4784         this.el.on("click", this.onClick, this);
4785         this.el.on("dblclick", this.onDblClick, this);
4786         
4787         this.parent().el.setStyle('position', 'relative');
4788         if (this.footer) {
4789             this.footer.parentId = this.id;
4790             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
4791         }
4792         
4793         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
4794         
4795         this.store.on('load', this.onLoad, this);
4796         this.store.on('beforeload', this.onBeforeLoad, this);
4797         this.store.on('update', this.onUpdate, this);
4798         
4799     },
4800     
4801     onMouseover : function(e, el)
4802     {
4803         var cell = Roo.get(el);
4804         
4805         if(!cell){
4806             return;
4807         }
4808         
4809         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4810             cell = cell.findParent('td', false, true);
4811         }
4812         
4813         var row = cell.findParent('tr', false, true);
4814         var cellIndex = cell.dom.cellIndex;
4815         var rowIndex = row.dom.rowIndex - 1; // start from 0
4816         
4817         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
4818         
4819     },
4820     
4821     onMouseout : function(e, el)
4822     {
4823         var cell = Roo.get(el);
4824         
4825         if(!cell){
4826             return;
4827         }
4828         
4829         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4830             cell = cell.findParent('td', false, true);
4831         }
4832         
4833         var row = cell.findParent('tr', false, true);
4834         var cellIndex = cell.dom.cellIndex;
4835         var rowIndex = row.dom.rowIndex - 1; // start from 0
4836         
4837         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
4838         
4839     },
4840     
4841     onClick : function(e, el)
4842     {
4843         var cell = Roo.get(el);
4844         
4845         if(!cell || (!this.CellSelection && !this.RowSelection)){
4846             return;
4847         }
4848         
4849         
4850         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4851             cell = cell.findParent('td', false, true);
4852         }
4853         
4854         var row = cell.findParent('tr', false, true);
4855         var cellIndex = cell.dom.cellIndex;
4856         var rowIndex = row.dom.rowIndex - 1;
4857         
4858         if(this.CellSelection){
4859             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
4860         }
4861         
4862         if(this.RowSelection){
4863             this.fireEvent('rowclick', this, row, rowIndex, e);
4864         }
4865         
4866         
4867     },
4868     
4869     onDblClick : function(e,el)
4870     {
4871         var cell = Roo.get(el);
4872         
4873         if(!cell || (!this.CellSelection && !this.RowSelection)){
4874             return;
4875         }
4876         
4877         if(e.getTarget().nodeName.toLowerCase() != 'td'){
4878             cell = cell.findParent('td', false, true);
4879         }
4880         
4881         var row = cell.findParent('tr', false, true);
4882         var cellIndex = cell.dom.cellIndex;
4883         var rowIndex = row.dom.rowIndex - 1;
4884         
4885         if(this.CellSelection){
4886             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
4887         }
4888         
4889         if(this.RowSelection){
4890             this.fireEvent('rowdblclick', this, row, rowIndex, e);
4891         }
4892     },
4893     
4894     sort : function(e,el)
4895     {
4896         var col = Roo.get(el)
4897         
4898         if(!col.hasClass('sortable')){
4899             return;
4900         }
4901         
4902         var sort = col.attr('sort');
4903         var dir = 'ASC';
4904         
4905         if(col.hasClass('glyphicon-arrow-up')){
4906             dir = 'DESC';
4907         }
4908         
4909         this.store.sortInfo = {field : sort, direction : dir};
4910         
4911         if (this.footer) {
4912             Roo.log("calling footer first");
4913             this.footer.onClick('first');
4914         } else {
4915         
4916             this.store.load({ params : { start : 0 } });
4917         }
4918     },
4919     
4920     renderHeader : function()
4921     {
4922         var header = {
4923             tag: 'thead',
4924             cn : []
4925         };
4926         
4927         var cm = this.cm;
4928         
4929         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4930             
4931             var config = cm.config[i];
4932                     
4933             var c = {
4934                 tag: 'th',
4935                 style : '',
4936                 html: cm.getColumnHeader(i)
4937             };
4938             
4939             if(typeof(config.hidden) != 'undefined' && config.hidden){
4940                 c.style += ' display:none;';
4941             }
4942             
4943             if(typeof(config.dataIndex) != 'undefined'){
4944                 c.sort = config.dataIndex;
4945             }
4946             
4947             if(typeof(config.sortable) != 'undefined' && config.sortable){
4948                 c.cls = 'sortable';
4949             }
4950             
4951 //            if(typeof(config.align) != 'undefined' && config.align.length){
4952 //                c.style += ' text-align:' + config.align + ';';
4953 //            }
4954             
4955             if(typeof(config.width) != 'undefined'){
4956                 c.style += ' width:' + config.width + 'px;';
4957             }
4958             
4959             header.cn.push(c)
4960         }
4961         
4962         return header;
4963     },
4964     
4965     renderBody : function()
4966     {
4967         var body = {
4968             tag: 'tbody',
4969             cn : [
4970                 {
4971                     tag: 'tr',
4972                     cn : [
4973                         {
4974                             tag : 'td',
4975                             colspan :  this.cm.getColumnCount()
4976                         }
4977                     ]
4978                 }
4979             ]
4980         };
4981         
4982         return body;
4983     },
4984     
4985     renderFooter : function()
4986     {
4987         var footer = {
4988             tag: 'tfoot',
4989             cn : [
4990                 {
4991                     tag: 'tr',
4992                     cn : [
4993                         {
4994                             tag : 'td',
4995                             colspan :  this.cm.getColumnCount()
4996                         }
4997                     ]
4998                 }
4999             ]
5000         };
5001         
5002         return footer;
5003     },
5004     
5005     
5006     
5007     onLoad : function()
5008     {
5009         Roo.log('ds onload');
5010         this.clear();
5011         
5012         var _this = this;
5013         var cm = this.cm;
5014         var ds = this.store;
5015         
5016         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5017             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5018             
5019             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5020                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5021             }
5022             
5023             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5024                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5025             }
5026         });
5027         
5028         var tbody = this.mainBody;
5029         
5030         var renders = [];
5031                     
5032         if(ds.getCount() > 0){
5033             ds.data.each(function(d,rowIndex){
5034                 var row =  this.renderRow(cm, ds, rowIndex);
5035                 
5036                 tbody.createChild(row);
5037                 
5038             }, this);
5039         }
5040         
5041         
5042         if(renders.length){
5043             var _this = this;
5044             Roo.each(renders, function(r){
5045                 _this.renderColumn(r);
5046             })
5047         }
5048         
5049         Roo.each(this.el.select('tbody td', true).elements, function(e){
5050             e.on('mouseover', _this.onMouseover, _this);
5051         });
5052         
5053         Roo.each(this.el.select('tbody td', true).elements, function(e){
5054             e.on('mouseout', _this.onMouseout, _this);
5055         });
5056
5057         //if(this.loadMask){
5058         //    this.maskEl.hide();
5059         //}
5060     },
5061     
5062     
5063     onUpdate : function(ds,record)
5064     {
5065         this.refreshRow(record);
5066     },
5067     onRemove : function(ds, record, index, isUpdate){
5068         if(isUpdate !== true){
5069             this.fireEvent("beforerowremoved", this, index, record);
5070         }
5071         var bt = this.mainBody.dom;
5072         if(bt.rows[index]){
5073             bt.removeChild(bt.rows[index]);
5074         }
5075         
5076         if(isUpdate !== true){
5077             //this.stripeRows(index);
5078             //this.syncRowHeights(index, index);
5079             //this.layout();
5080             this.fireEvent("rowremoved", this, index, record);
5081         }
5082     },
5083     
5084     
5085     refreshRow : function(record){
5086         var ds = this.store, index;
5087         if(typeof record == 'number'){
5088             index = record;
5089             record = ds.getAt(index);
5090         }else{
5091             index = ds.indexOf(record);
5092         }
5093         this.insertRow(ds, index, true);
5094         this.onRemove(ds, record, index+1, true);
5095         //this.syncRowHeights(index, index);
5096         //this.layout();
5097         this.fireEvent("rowupdated", this, index, record);
5098     },
5099     
5100     insertRow : function(dm, rowIndex, isUpdate){
5101         
5102         if(!isUpdate){
5103             this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
5104         }
5105             //var s = this.getScrollState();
5106         var row = this.renderRow(this.cm, this.store, rowIndex);
5107         // insert before rowIndex..
5108         this.mainBody.createChild(row,this.getRowDom(rowIndex));
5109             
5110         if(!isUpdate){
5111             this.fireEvent("rowsinserted", this, firstRow, lastRow);
5112             //this.syncRowHeights(firstRow, lastRow);
5113             //this.stripeRows(firstRow);
5114             //this.layout();
5115         }
5116         
5117     },
5118     
5119     
5120     getRowDom : function(rowIndex)
5121     {
5122         // not sure if I need to check this.. but let's do it anyway..
5123         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5124                 this.mainBody.dom.rows[rowIndex] : false
5125     },
5126     // returns the object tree for a tr..
5127   
5128     
5129     renderRow : function(cm, ds, rowIndex) {
5130         
5131         var d = ds.getAt(rowIndex);
5132         
5133         var row = {
5134             tag : 'tr',
5135             cn : []
5136         };
5137             
5138         
5139         
5140         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5141             var config = cm.config[i];
5142             
5143             var renderer = cm.getRenderer(i);
5144             var value = '';
5145             var id = Roo.id();
5146             
5147             if(typeof(renderer) !== 'undefined'){
5148                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5149             }
5150             
5151             if(typeof(value) === 'object'){
5152                 renders.push({
5153                     container : id,
5154                     cfg : value 
5155                 })
5156             }
5157             
5158             var rowcfg = {
5159                 record: d,
5160                 rowIndex : rowIndex,
5161                 colIndex : i,
5162                 rowClass : ''
5163             }
5164
5165             this.fireEvent('rowclass', this, rowcfg);
5166             
5167             var td = {
5168                 tag: 'td',
5169                 id: id,
5170                 cls : rowcfg.rowClass,
5171                 style: '',
5172                 html: (typeof(value) === 'object') ? '' : value
5173             };
5174             
5175             if(typeof(config.hidden) != 'undefined' && config.hidden){
5176                 td.style += ' display:none;';
5177             }
5178             
5179             if(typeof(config.align) != 'undefined' && config.align.length){
5180                 td.style += ' text-align:' + config.align + ';';
5181             }
5182             
5183             if(typeof(config.width) != 'undefined'){
5184                 td.style += ' width:' +  config.width + 'px;';
5185             }
5186              
5187             row.cn.push(td);
5188            
5189         }
5190         return row;
5191           
5192     },
5193     
5194     
5195     
5196     onBeforeLoad : function()
5197     {
5198         //Roo.log('ds onBeforeLoad');
5199         
5200         //this.clear();
5201         
5202         //if(this.loadMask){
5203         //    this.maskEl.show();
5204         //}
5205     },
5206     
5207     clear : function()
5208     {
5209         this.el.select('tbody', true).first().dom.innerHTML = '';
5210     },
5211     
5212     getSelectionModel : function(){
5213         if(!this.selModel){
5214             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5215         }
5216         return this.selModel;
5217     },
5218     
5219     renderColumn : function(r)
5220     {
5221         var _this = this;
5222         
5223         var t = r.cfg.render(r.container);
5224         
5225         if(r.cfg.cn){
5226             Roo.each(r.cfg.cn, function(c){
5227                 var child = {
5228                     container: t.getChildContainer(),
5229                     cfg: c
5230                 }
5231                 _this.renderColumn(child);
5232             })
5233         }
5234     }
5235    
5236 });
5237
5238  
5239
5240  /*
5241  * - LGPL
5242  *
5243  * table cell
5244  * 
5245  */
5246
5247 /**
5248  * @class Roo.bootstrap.TableCell
5249  * @extends Roo.bootstrap.Component
5250  * Bootstrap TableCell class
5251  * @cfg {String} html cell contain text
5252  * @cfg {String} cls cell class
5253  * @cfg {String} tag cell tag (td|th) default td
5254  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5255  * @cfg {String} align Aligns the content in a cell
5256  * @cfg {String} axis Categorizes cells
5257  * @cfg {String} bgcolor Specifies the background color of a cell
5258  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5259  * @cfg {Number} colspan Specifies the number of columns a cell should span
5260  * @cfg {String} headers Specifies one or more header cells a cell is related to
5261  * @cfg {Number} height Sets the height of a cell
5262  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5263  * @cfg {Number} rowspan Sets the number of rows a cell should span
5264  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5265  * @cfg {String} valign Vertical aligns the content in a cell
5266  * @cfg {Number} width Specifies the width of a cell
5267  * 
5268  * @constructor
5269  * Create a new TableCell
5270  * @param {Object} config The config object
5271  */
5272
5273 Roo.bootstrap.TableCell = function(config){
5274     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5275 };
5276
5277 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
5278     
5279     html: false,
5280     cls: false,
5281     tag: false,
5282     abbr: false,
5283     align: false,
5284     axis: false,
5285     bgcolor: false,
5286     charoff: false,
5287     colspan: false,
5288     headers: false,
5289     height: false,
5290     nowrap: false,
5291     rowspan: false,
5292     scope: false,
5293     valign: false,
5294     width: false,
5295     
5296     
5297     getAutoCreate : function(){
5298         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5299         
5300         cfg = {
5301             tag: 'td'
5302         }
5303         
5304         if(this.tag){
5305             cfg.tag = this.tag;
5306         }
5307         
5308         if (this.html) {
5309             cfg.html=this.html
5310         }
5311         if (this.cls) {
5312             cfg.cls=this.cls
5313         }
5314         if (this.abbr) {
5315             cfg.abbr=this.abbr
5316         }
5317         if (this.align) {
5318             cfg.align=this.align
5319         }
5320         if (this.axis) {
5321             cfg.axis=this.axis
5322         }
5323         if (this.bgcolor) {
5324             cfg.bgcolor=this.bgcolor
5325         }
5326         if (this.charoff) {
5327             cfg.charoff=this.charoff
5328         }
5329         if (this.colspan) {
5330             cfg.colspan=this.colspan
5331         }
5332         if (this.headers) {
5333             cfg.headers=this.headers
5334         }
5335         if (this.height) {
5336             cfg.height=this.height
5337         }
5338         if (this.nowrap) {
5339             cfg.nowrap=this.nowrap
5340         }
5341         if (this.rowspan) {
5342             cfg.rowspan=this.rowspan
5343         }
5344         if (this.scope) {
5345             cfg.scope=this.scope
5346         }
5347         if (this.valign) {
5348             cfg.valign=this.valign
5349         }
5350         if (this.width) {
5351             cfg.width=this.width
5352         }
5353         
5354         
5355         return cfg;
5356     }
5357    
5358 });
5359
5360  
5361
5362  /*
5363  * - LGPL
5364  *
5365  * table row
5366  * 
5367  */
5368
5369 /**
5370  * @class Roo.bootstrap.TableRow
5371  * @extends Roo.bootstrap.Component
5372  * Bootstrap TableRow class
5373  * @cfg {String} cls row class
5374  * @cfg {String} align Aligns the content in a table row
5375  * @cfg {String} bgcolor Specifies a background color for a table row
5376  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5377  * @cfg {String} valign Vertical aligns the content in a table row
5378  * 
5379  * @constructor
5380  * Create a new TableRow
5381  * @param {Object} config The config object
5382  */
5383
5384 Roo.bootstrap.TableRow = function(config){
5385     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5386 };
5387
5388 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
5389     
5390     cls: false,
5391     align: false,
5392     bgcolor: false,
5393     charoff: false,
5394     valign: false,
5395     
5396     getAutoCreate : function(){
5397         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5398         
5399         cfg = {
5400             tag: 'tr'
5401         }
5402             
5403         if(this.cls){
5404             cfg.cls = this.cls;
5405         }
5406         if(this.align){
5407             cfg.align = this.align;
5408         }
5409         if(this.bgcolor){
5410             cfg.bgcolor = this.bgcolor;
5411         }
5412         if(this.charoff){
5413             cfg.charoff = this.charoff;
5414         }
5415         if(this.valign){
5416             cfg.valign = this.valign;
5417         }
5418         
5419         return cfg;
5420     }
5421    
5422 });
5423
5424  
5425
5426  /*
5427  * - LGPL
5428  *
5429  * table body
5430  * 
5431  */
5432
5433 /**
5434  * @class Roo.bootstrap.TableBody
5435  * @extends Roo.bootstrap.Component
5436  * Bootstrap TableBody class
5437  * @cfg {String} cls element class
5438  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5439  * @cfg {String} align Aligns the content inside the element
5440  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5441  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5442  * 
5443  * @constructor
5444  * Create a new TableBody
5445  * @param {Object} config The config object
5446  */
5447
5448 Roo.bootstrap.TableBody = function(config){
5449     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5450 };
5451
5452 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
5453     
5454     cls: false,
5455     tag: false,
5456     align: false,
5457     charoff: false,
5458     valign: false,
5459     
5460     getAutoCreate : function(){
5461         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5462         
5463         cfg = {
5464             tag: 'tbody'
5465         }
5466             
5467         if (this.cls) {
5468             cfg.cls=this.cls
5469         }
5470         if(this.tag){
5471             cfg.tag = this.tag;
5472         }
5473         
5474         if(this.align){
5475             cfg.align = this.align;
5476         }
5477         if(this.charoff){
5478             cfg.charoff = this.charoff;
5479         }
5480         if(this.valign){
5481             cfg.valign = this.valign;
5482         }
5483         
5484         return cfg;
5485     }
5486     
5487     
5488 //    initEvents : function()
5489 //    {
5490 //        
5491 //        if(!this.store){
5492 //            return;
5493 //        }
5494 //        
5495 //        this.store = Roo.factory(this.store, Roo.data);
5496 //        this.store.on('load', this.onLoad, this);
5497 //        
5498 //        this.store.load();
5499 //        
5500 //    },
5501 //    
5502 //    onLoad: function () 
5503 //    {   
5504 //        this.fireEvent('load', this);
5505 //    }
5506 //    
5507 //   
5508 });
5509
5510  
5511
5512  /*
5513  * Based on:
5514  * Ext JS Library 1.1.1
5515  * Copyright(c) 2006-2007, Ext JS, LLC.
5516  *
5517  * Originally Released Under LGPL - original licence link has changed is not relivant.
5518  *
5519  * Fork - LGPL
5520  * <script type="text/javascript">
5521  */
5522
5523 // as we use this in bootstrap.
5524 Roo.namespace('Roo.form');
5525  /**
5526  * @class Roo.form.Action
5527  * Internal Class used to handle form actions
5528  * @constructor
5529  * @param {Roo.form.BasicForm} el The form element or its id
5530  * @param {Object} config Configuration options
5531  */
5532
5533  
5534  
5535 // define the action interface
5536 Roo.form.Action = function(form, options){
5537     this.form = form;
5538     this.options = options || {};
5539 };
5540 /**
5541  * Client Validation Failed
5542  * @const 
5543  */
5544 Roo.form.Action.CLIENT_INVALID = 'client';
5545 /**
5546  * Server Validation Failed
5547  * @const 
5548  */
5549 Roo.form.Action.SERVER_INVALID = 'server';
5550  /**
5551  * Connect to Server Failed
5552  * @const 
5553  */
5554 Roo.form.Action.CONNECT_FAILURE = 'connect';
5555 /**
5556  * Reading Data from Server Failed
5557  * @const 
5558  */
5559 Roo.form.Action.LOAD_FAILURE = 'load';
5560
5561 Roo.form.Action.prototype = {
5562     type : 'default',
5563     failureType : undefined,
5564     response : undefined,
5565     result : undefined,
5566
5567     // interface method
5568     run : function(options){
5569
5570     },
5571
5572     // interface method
5573     success : function(response){
5574
5575     },
5576
5577     // interface method
5578     handleResponse : function(response){
5579
5580     },
5581
5582     // default connection failure
5583     failure : function(response){
5584         
5585         this.response = response;
5586         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5587         this.form.afterAction(this, false);
5588     },
5589
5590     processResponse : function(response){
5591         this.response = response;
5592         if(!response.responseText){
5593             return true;
5594         }
5595         this.result = this.handleResponse(response);
5596         return this.result;
5597     },
5598
5599     // utility functions used internally
5600     getUrl : function(appendParams){
5601         var url = this.options.url || this.form.url || this.form.el.dom.action;
5602         if(appendParams){
5603             var p = this.getParams();
5604             if(p){
5605                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5606             }
5607         }
5608         return url;
5609     },
5610
5611     getMethod : function(){
5612         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5613     },
5614
5615     getParams : function(){
5616         var bp = this.form.baseParams;
5617         var p = this.options.params;
5618         if(p){
5619             if(typeof p == "object"){
5620                 p = Roo.urlEncode(Roo.applyIf(p, bp));
5621             }else if(typeof p == 'string' && bp){
5622                 p += '&' + Roo.urlEncode(bp);
5623             }
5624         }else if(bp){
5625             p = Roo.urlEncode(bp);
5626         }
5627         return p;
5628     },
5629
5630     createCallback : function(){
5631         return {
5632             success: this.success,
5633             failure: this.failure,
5634             scope: this,
5635             timeout: (this.form.timeout*1000),
5636             upload: this.form.fileUpload ? this.success : undefined
5637         };
5638     }
5639 };
5640
5641 Roo.form.Action.Submit = function(form, options){
5642     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
5643 };
5644
5645 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
5646     type : 'submit',
5647
5648     haveProgress : false,
5649     uploadComplete : false,
5650     
5651     // uploadProgress indicator.
5652     uploadProgress : function()
5653     {
5654         if (!this.form.progressUrl) {
5655             return;
5656         }
5657         
5658         if (!this.haveProgress) {
5659             Roo.MessageBox.progress("Uploading", "Uploading");
5660         }
5661         if (this.uploadComplete) {
5662            Roo.MessageBox.hide();
5663            return;
5664         }
5665         
5666         this.haveProgress = true;
5667    
5668         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
5669         
5670         var c = new Roo.data.Connection();
5671         c.request({
5672             url : this.form.progressUrl,
5673             params: {
5674                 id : uid
5675             },
5676             method: 'GET',
5677             success : function(req){
5678                //console.log(data);
5679                 var rdata = false;
5680                 var edata;
5681                 try  {
5682                    rdata = Roo.decode(req.responseText)
5683                 } catch (e) {
5684                     Roo.log("Invalid data from server..");
5685                     Roo.log(edata);
5686                     return;
5687                 }
5688                 if (!rdata || !rdata.success) {
5689                     Roo.log(rdata);
5690                     Roo.MessageBox.alert(Roo.encode(rdata));
5691                     return;
5692                 }
5693                 var data = rdata.data;
5694                 
5695                 if (this.uploadComplete) {
5696                    Roo.MessageBox.hide();
5697                    return;
5698                 }
5699                    
5700                 if (data){
5701                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
5702                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
5703                     );
5704                 }
5705                 this.uploadProgress.defer(2000,this);
5706             },
5707        
5708             failure: function(data) {
5709                 Roo.log('progress url failed ');
5710                 Roo.log(data);
5711             },
5712             scope : this
5713         });
5714            
5715     },
5716     
5717     
5718     run : function()
5719     {
5720         // run get Values on the form, so it syncs any secondary forms.
5721         this.form.getValues();
5722         
5723         var o = this.options;
5724         var method = this.getMethod();
5725         var isPost = method == 'POST';
5726         if(o.clientValidation === false || this.form.isValid()){
5727             
5728             if (this.form.progressUrl) {
5729                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
5730                     (new Date() * 1) + '' + Math.random());
5731                     
5732             } 
5733             
5734             
5735             Roo.Ajax.request(Roo.apply(this.createCallback(), {
5736                 form:this.form.el.dom,
5737                 url:this.getUrl(!isPost),
5738                 method: method,
5739                 params:isPost ? this.getParams() : null,
5740                 isUpload: this.form.fileUpload
5741             }));
5742             
5743             this.uploadProgress();
5744
5745         }else if (o.clientValidation !== false){ // client validation failed
5746             this.failureType = Roo.form.Action.CLIENT_INVALID;
5747             this.form.afterAction(this, false);
5748         }
5749     },
5750
5751     success : function(response)
5752     {
5753         this.uploadComplete= true;
5754         if (this.haveProgress) {
5755             Roo.MessageBox.hide();
5756         }
5757         
5758         
5759         var result = this.processResponse(response);
5760         if(result === true || result.success){
5761             this.form.afterAction(this, true);
5762             return;
5763         }
5764         if(result.errors){
5765             this.form.markInvalid(result.errors);
5766             this.failureType = Roo.form.Action.SERVER_INVALID;
5767         }
5768         this.form.afterAction(this, false);
5769     },
5770     failure : function(response)
5771     {
5772         this.uploadComplete= true;
5773         if (this.haveProgress) {
5774             Roo.MessageBox.hide();
5775         }
5776         
5777         this.response = response;
5778         this.failureType = Roo.form.Action.CONNECT_FAILURE;
5779         this.form.afterAction(this, false);
5780     },
5781     
5782     handleResponse : function(response){
5783         if(this.form.errorReader){
5784             var rs = this.form.errorReader.read(response);
5785             var errors = [];
5786             if(rs.records){
5787                 for(var i = 0, len = rs.records.length; i < len; i++) {
5788                     var r = rs.records[i];
5789                     errors[i] = r.data;
5790                 }
5791             }
5792             if(errors.length < 1){
5793                 errors = null;
5794             }
5795             return {
5796                 success : rs.success,
5797                 errors : errors
5798             };
5799         }
5800         var ret = false;
5801         try {
5802             ret = Roo.decode(response.responseText);
5803         } catch (e) {
5804             ret = {
5805                 success: false,
5806                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
5807                 errors : []
5808             };
5809         }
5810         return ret;
5811         
5812     }
5813 });
5814
5815
5816 Roo.form.Action.Load = function(form, options){
5817     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
5818     this.reader = this.form.reader;
5819 };
5820
5821 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
5822     type : 'load',
5823
5824     run : function(){
5825         
5826         Roo.Ajax.request(Roo.apply(
5827                 this.createCallback(), {
5828                     method:this.getMethod(),
5829                     url:this.getUrl(false),
5830                     params:this.getParams()
5831         }));
5832     },
5833
5834     success : function(response){
5835         
5836         var result = this.processResponse(response);
5837         if(result === true || !result.success || !result.data){
5838             this.failureType = Roo.form.Action.LOAD_FAILURE;
5839             this.form.afterAction(this, false);
5840             return;
5841         }
5842         this.form.clearInvalid();
5843         this.form.setValues(result.data);
5844         this.form.afterAction(this, true);
5845     },
5846
5847     handleResponse : function(response){
5848         if(this.form.reader){
5849             var rs = this.form.reader.read(response);
5850             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5851             return {
5852                 success : rs.success,
5853                 data : data
5854             };
5855         }
5856         return Roo.decode(response.responseText);
5857     }
5858 });
5859
5860 Roo.form.Action.ACTION_TYPES = {
5861     'load' : Roo.form.Action.Load,
5862     'submit' : Roo.form.Action.Submit
5863 };/*
5864  * - LGPL
5865  *
5866  * form
5867  * 
5868  */
5869
5870 /**
5871  * @class Roo.bootstrap.Form
5872  * @extends Roo.bootstrap.Component
5873  * Bootstrap Form class
5874  * @cfg {String} method  GET | POST (default POST)
5875  * @cfg {String} labelAlign top | left (default top)
5876   * @cfg {String} align left  | right - for navbars
5877
5878  * 
5879  * @constructor
5880  * Create a new Form
5881  * @param {Object} config The config object
5882  */
5883
5884
5885 Roo.bootstrap.Form = function(config){
5886     Roo.bootstrap.Form.superclass.constructor.call(this, config);
5887     this.addEvents({
5888         /**
5889          * @event clientvalidation
5890          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5891          * @param {Form} this
5892          * @param {Boolean} valid true if the form has passed client-side validation
5893          */
5894         clientvalidation: true,
5895         /**
5896          * @event beforeaction
5897          * Fires before any action is performed. Return false to cancel the action.
5898          * @param {Form} this
5899          * @param {Action} action The action to be performed
5900          */
5901         beforeaction: true,
5902         /**
5903          * @event actionfailed
5904          * Fires when an action fails.
5905          * @param {Form} this
5906          * @param {Action} action The action that failed
5907          */
5908         actionfailed : true,
5909         /**
5910          * @event actioncomplete
5911          * Fires when an action is completed.
5912          * @param {Form} this
5913          * @param {Action} action The action that completed
5914          */
5915         actioncomplete : true
5916     });
5917     
5918 };
5919
5920 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
5921       
5922      /**
5923      * @cfg {String} method
5924      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5925      */
5926     method : 'POST',
5927     /**
5928      * @cfg {String} url
5929      * The URL to use for form actions if one isn't supplied in the action options.
5930      */
5931     /**
5932      * @cfg {Boolean} fileUpload
5933      * Set to true if this form is a file upload.
5934      */
5935      
5936     /**
5937      * @cfg {Object} baseParams
5938      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
5939      */
5940       
5941     /**
5942      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5943      */
5944     timeout: 30,
5945     /**
5946      * @cfg {Sting} align (left|right) for navbar forms
5947      */
5948     align : 'left',
5949
5950     // private
5951     activeAction : null,
5952  
5953     /**
5954      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5955      * element by passing it or its id or mask the form itself by passing in true.
5956      * @type Mixed
5957      */
5958     waitMsgTarget : false,
5959     
5960      
5961     
5962     /**
5963      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5964      * element by passing it or its id or mask the form itself by passing in true.
5965      * @type Mixed
5966      */
5967     
5968     getAutoCreate : function(){
5969         
5970         var cfg = {
5971             tag: 'form',
5972             method : this.method || 'POST',
5973             id : this.id || Roo.id(),
5974             cls : ''
5975         }
5976         if (this.parent().xtype.match(/^Nav/)) {
5977             cfg.cls = 'navbar-form navbar-' + this.align;
5978             
5979         }
5980         
5981         if (this.labelAlign == 'left' ) {
5982             cfg.cls += ' form-horizontal';
5983         }
5984         
5985         
5986         return cfg;
5987     },
5988     initEvents : function()
5989     {
5990         this.el.on('submit', this.onSubmit, this);
5991         // this was added as random key presses on the form where triggering form submit.
5992         this.el.on('keypress', function(e) {
5993             if (e.getCharCode() != 13) {
5994                 return true;
5995             }
5996             // we might need to allow it for textareas.. and some other items.
5997             // check e.getTarget().
5998             
5999             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6000                 return true;
6001             }
6002         
6003             Roo.log("keypress blocked");
6004             
6005             e.preventDefault();
6006             return false;
6007         });
6008         
6009     },
6010     // private
6011     onSubmit : function(e){
6012         e.stopEvent();
6013     },
6014     
6015      /**
6016      * Returns true if client-side validation on the form is successful.
6017      * @return Boolean
6018      */
6019     isValid : function(){
6020         var items = this.getItems();
6021         var valid = true;
6022         items.each(function(f){
6023            if(!f.validate()){
6024                valid = false;
6025                
6026            }
6027         });
6028         return valid;
6029     },
6030     /**
6031      * Returns true if any fields in this form have changed since their original load.
6032      * @return Boolean
6033      */
6034     isDirty : function(){
6035         var dirty = false;
6036         var items = this.getItems();
6037         items.each(function(f){
6038            if(f.isDirty()){
6039                dirty = true;
6040                return false;
6041            }
6042            return true;
6043         });
6044         return dirty;
6045     },
6046      /**
6047      * Performs a predefined action (submit or load) or custom actions you define on this form.
6048      * @param {String} actionName The name of the action type
6049      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
6050      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6051      * accept other config options):
6052      * <pre>
6053 Property          Type             Description
6054 ----------------  ---------------  ----------------------------------------------------------------------------------
6055 url               String           The url for the action (defaults to the form's url)
6056 method            String           The form method to use (defaults to the form's method, or POST if not defined)
6057 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
6058 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
6059                                    validate the form on the client (defaults to false)
6060      * </pre>
6061      * @return {BasicForm} this
6062      */
6063     doAction : function(action, options){
6064         if(typeof action == 'string'){
6065             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6066         }
6067         if(this.fireEvent('beforeaction', this, action) !== false){
6068             this.beforeAction(action);
6069             action.run.defer(100, action);
6070         }
6071         return this;
6072     },
6073     
6074     // private
6075     beforeAction : function(action){
6076         var o = action.options;
6077         
6078         // not really supported yet.. ??
6079         
6080         //if(this.waitMsgTarget === true){
6081             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6082         //}else if(this.waitMsgTarget){
6083         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6084         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6085         //}else {
6086         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6087        // }
6088          
6089     },
6090
6091     // private
6092     afterAction : function(action, success){
6093         this.activeAction = null;
6094         var o = action.options;
6095         
6096         //if(this.waitMsgTarget === true){
6097             this.el.unmask();
6098         //}else if(this.waitMsgTarget){
6099         //    this.waitMsgTarget.unmask();
6100         //}else{
6101         //    Roo.MessageBox.updateProgress(1);
6102         //    Roo.MessageBox.hide();
6103        // }
6104         // 
6105         if(success){
6106             if(o.reset){
6107                 this.reset();
6108             }
6109             Roo.callback(o.success, o.scope, [this, action]);
6110             this.fireEvent('actioncomplete', this, action);
6111             
6112         }else{
6113             
6114             // failure condition..
6115             // we have a scenario where updates need confirming.
6116             // eg. if a locking scenario exists..
6117             // we look for { errors : { needs_confirm : true }} in the response.
6118             if (
6119                 (typeof(action.result) != 'undefined')  &&
6120                 (typeof(action.result.errors) != 'undefined')  &&
6121                 (typeof(action.result.errors.needs_confirm) != 'undefined')
6122            ){
6123                 var _t = this;
6124                 Roo.log("not supported yet");
6125                  /*
6126                 
6127                 Roo.MessageBox.confirm(
6128                     "Change requires confirmation",
6129                     action.result.errorMsg,
6130                     function(r) {
6131                         if (r != 'yes') {
6132                             return;
6133                         }
6134                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
6135                     }
6136                     
6137                 );
6138                 */
6139                 
6140                 
6141                 return;
6142             }
6143             
6144             Roo.callback(o.failure, o.scope, [this, action]);
6145             // show an error message if no failed handler is set..
6146             if (!this.hasListener('actionfailed')) {
6147                 Roo.log("need to add dialog support");
6148                 /*
6149                 Roo.MessageBox.alert("Error",
6150                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6151                         action.result.errorMsg :
6152                         "Saving Failed, please check your entries or try again"
6153                 );
6154                 */
6155             }
6156             
6157             this.fireEvent('actionfailed', this, action);
6158         }
6159         
6160     },
6161     /**
6162      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6163      * @param {String} id The value to search for
6164      * @return Field
6165      */
6166     findField : function(id){
6167         var items = this.getItems();
6168         var field = items.get(id);
6169         if(!field){
6170              items.each(function(f){
6171                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6172                     field = f;
6173                     return false;
6174                 }
6175                 return true;
6176             });
6177         }
6178         return field || null;
6179     },
6180      /**
6181      * Mark fields in this form invalid in bulk.
6182      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6183      * @return {BasicForm} this
6184      */
6185     markInvalid : function(errors){
6186         if(errors instanceof Array){
6187             for(var i = 0, len = errors.length; i < len; i++){
6188                 var fieldError = errors[i];
6189                 var f = this.findField(fieldError.id);
6190                 if(f){
6191                     f.markInvalid(fieldError.msg);
6192                 }
6193             }
6194         }else{
6195             var field, id;
6196             for(id in errors){
6197                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6198                     field.markInvalid(errors[id]);
6199                 }
6200             }
6201         }
6202         //Roo.each(this.childForms || [], function (f) {
6203         //    f.markInvalid(errors);
6204         //});
6205         
6206         return this;
6207     },
6208
6209     /**
6210      * Set values for fields in this form in bulk.
6211      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6212      * @return {BasicForm} this
6213      */
6214     setValues : function(values){
6215         if(values instanceof Array){ // array of objects
6216             for(var i = 0, len = values.length; i < len; i++){
6217                 var v = values[i];
6218                 var f = this.findField(v.id);
6219                 if(f){
6220                     f.setValue(v.value);
6221                     if(this.trackResetOnLoad){
6222                         f.originalValue = f.getValue();
6223                     }
6224                 }
6225             }
6226         }else{ // object hash
6227             var field, id;
6228             for(id in values){
6229                 if(typeof values[id] != 'function' && (field = this.findField(id))){
6230                     
6231                     if (field.setFromData && 
6232                         field.valueField && 
6233                         field.displayField &&
6234                         // combos' with local stores can 
6235                         // be queried via setValue()
6236                         // to set their value..
6237                         (field.store && !field.store.isLocal)
6238                         ) {
6239                         // it's a combo
6240                         var sd = { };
6241                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6242                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6243                         field.setFromData(sd);
6244                         
6245                     } else {
6246                         field.setValue(values[id]);
6247                     }
6248                     
6249                     
6250                     if(this.trackResetOnLoad){
6251                         field.originalValue = field.getValue();
6252                     }
6253                 }
6254             }
6255         }
6256          
6257         //Roo.each(this.childForms || [], function (f) {
6258         //    f.setValues(values);
6259         //});
6260                 
6261         return this;
6262     },
6263
6264     /**
6265      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6266      * they are returned as an array.
6267      * @param {Boolean} asString
6268      * @return {Object}
6269      */
6270     getValues : function(asString){
6271         //if (this.childForms) {
6272             // copy values from the child forms
6273         //    Roo.each(this.childForms, function (f) {
6274         //        this.setValues(f.getValues());
6275         //    }, this);
6276         //}
6277         
6278         
6279         
6280         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6281         if(asString === true){
6282             return fs;
6283         }
6284         return Roo.urlDecode(fs);
6285     },
6286     
6287     /**
6288      * Returns the fields in this form as an object with key/value pairs. 
6289      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6290      * @return {Object}
6291      */
6292     getFieldValues : function(with_hidden)
6293     {
6294         var items = this.getItems();
6295         var ret = {};
6296         items.each(function(f){
6297             if (!f.getName()) {
6298                 return;
6299             }
6300             var v = f.getValue();
6301             if (f.inputType =='radio') {
6302                 if (typeof(ret[f.getName()]) == 'undefined') {
6303                     ret[f.getName()] = ''; // empty..
6304                 }
6305                 
6306                 if (!f.el.dom.checked) {
6307                     return;
6308                     
6309                 }
6310                 v = f.el.dom.value;
6311                 
6312             }
6313             
6314             // not sure if this supported any more..
6315             if ((typeof(v) == 'object') && f.getRawValue) {
6316                 v = f.getRawValue() ; // dates..
6317             }
6318             // combo boxes where name != hiddenName...
6319             if (f.name != f.getName()) {
6320                 ret[f.name] = f.getRawValue();
6321             }
6322             ret[f.getName()] = v;
6323         });
6324         
6325         return ret;
6326     },
6327
6328     /**
6329      * Clears all invalid messages in this form.
6330      * @return {BasicForm} this
6331      */
6332     clearInvalid : function(){
6333         var items = this.getItems();
6334         
6335         items.each(function(f){
6336            f.clearInvalid();
6337         });
6338         
6339         
6340         
6341         return this;
6342     },
6343
6344     /**
6345      * Resets this form.
6346      * @return {BasicForm} this
6347      */
6348     reset : function(){
6349         var items = this.getItems();
6350         items.each(function(f){
6351             f.reset();
6352         });
6353         
6354         Roo.each(this.childForms || [], function (f) {
6355             f.reset();
6356         });
6357        
6358         
6359         return this;
6360     },
6361     getItems : function()
6362     {
6363         var r=new Roo.util.MixedCollection(false, function(o){
6364             return o.id || (o.id = Roo.id());
6365         });
6366         var iter = function(el) {
6367             if (el.inputEl) {
6368                 r.add(el);
6369             }
6370             if (!el.items) {
6371                 return;
6372             }
6373             Roo.each(el.items,function(e) {
6374                 iter(e);
6375             });
6376             
6377             
6378         };
6379         iter(this);
6380         return r;
6381         
6382         
6383         
6384         
6385     }
6386     
6387 });
6388
6389  
6390 /*
6391  * Based on:
6392  * Ext JS Library 1.1.1
6393  * Copyright(c) 2006-2007, Ext JS, LLC.
6394  *
6395  * Originally Released Under LGPL - original licence link has changed is not relivant.
6396  *
6397  * Fork - LGPL
6398  * <script type="text/javascript">
6399  */
6400 /**
6401  * @class Roo.form.VTypes
6402  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6403  * @singleton
6404  */
6405 Roo.form.VTypes = function(){
6406     // closure these in so they are only created once.
6407     var alpha = /^[a-zA-Z_]+$/;
6408     var alphanum = /^[a-zA-Z0-9_]+$/;
6409     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6410     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6411
6412     // All these messages and functions are configurable
6413     return {
6414         /**
6415          * The function used to validate email addresses
6416          * @param {String} value The email address
6417          */
6418         'email' : function(v){
6419             return email.test(v);
6420         },
6421         /**
6422          * The error text to display when the email validation function returns false
6423          * @type String
6424          */
6425         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6426         /**
6427          * The keystroke filter mask to be applied on email input
6428          * @type RegExp
6429          */
6430         'emailMask' : /[a-z0-9_\.\-@]/i,
6431
6432         /**
6433          * The function used to validate URLs
6434          * @param {String} value The URL
6435          */
6436         'url' : function(v){
6437             return url.test(v);
6438         },
6439         /**
6440          * The error text to display when the url validation function returns false
6441          * @type String
6442          */
6443         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6444         
6445         /**
6446          * The function used to validate alpha values
6447          * @param {String} value The value
6448          */
6449         'alpha' : function(v){
6450             return alpha.test(v);
6451         },
6452         /**
6453          * The error text to display when the alpha validation function returns false
6454          * @type String
6455          */
6456         'alphaText' : 'This field should only contain letters and _',
6457         /**
6458          * The keystroke filter mask to be applied on alpha input
6459          * @type RegExp
6460          */
6461         'alphaMask' : /[a-z_]/i,
6462
6463         /**
6464          * The function used to validate alphanumeric values
6465          * @param {String} value The value
6466          */
6467         'alphanum' : function(v){
6468             return alphanum.test(v);
6469         },
6470         /**
6471          * The error text to display when the alphanumeric validation function returns false
6472          * @type String
6473          */
6474         'alphanumText' : 'This field should only contain letters, numbers and _',
6475         /**
6476          * The keystroke filter mask to be applied on alphanumeric input
6477          * @type RegExp
6478          */
6479         'alphanumMask' : /[a-z0-9_]/i
6480     };
6481 }();/*
6482  * - LGPL
6483  *
6484  * Input
6485  * 
6486  */
6487
6488 /**
6489  * @class Roo.bootstrap.Input
6490  * @extends Roo.bootstrap.Component
6491  * Bootstrap Input class
6492  * @cfg {Boolean} disabled is it disabled
6493  * @cfg {String} fieldLabel - the label associated
6494  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6495  * @cfg {String} name name of the input
6496  * @cfg {string} fieldLabel - the label associated
6497  * @cfg {string}  inputType - input / file submit ...
6498  * @cfg {string} placeholder - placeholder to put in text.
6499  * @cfg {string}  before - input group add on before
6500  * @cfg {string} after - input group add on after
6501  * @cfg {string} size - (lg|sm) or leave empty..
6502  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6503  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6504  * @cfg {Number} md colspan out of 12 for computer-sized screens
6505  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6506  * @cfg {string} value default value of the input
6507  * @cfg {Number} labelWidth set the width of label (0-12)
6508  * @cfg {String} labelAlign (top|left)
6509  * @cfg {Boolean} readOnly Specifies that the field should be read-only
6510  * @cfg {String} align (left|center|right) Default left
6511  * @cfg {Boolean} formatedValue (true | false) Default false
6512  * 
6513  * 
6514  * @constructor
6515  * Create a new Input
6516  * @param {Object} config The config object
6517  */
6518
6519 Roo.bootstrap.Input = function(config){
6520     Roo.bootstrap.Input.superclass.constructor.call(this, config);
6521    
6522         this.addEvents({
6523             /**
6524              * @event focus
6525              * Fires when this field receives input focus.
6526              * @param {Roo.form.Field} this
6527              */
6528             focus : true,
6529             /**
6530              * @event blur
6531              * Fires when this field loses input focus.
6532              * @param {Roo.form.Field} this
6533              */
6534             blur : true,
6535             /**
6536              * @event specialkey
6537              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
6538              * {@link Roo.EventObject#getKey} to determine which key was pressed.
6539              * @param {Roo.form.Field} this
6540              * @param {Roo.EventObject} e The event object
6541              */
6542             specialkey : true,
6543             /**
6544              * @event change
6545              * Fires just before the field blurs if the field value has changed.
6546              * @param {Roo.form.Field} this
6547              * @param {Mixed} newValue The new value
6548              * @param {Mixed} oldValue The original value
6549              */
6550             change : true,
6551             /**
6552              * @event invalid
6553              * Fires after the field has been marked as invalid.
6554              * @param {Roo.form.Field} this
6555              * @param {String} msg The validation message
6556              */
6557             invalid : true,
6558             /**
6559              * @event valid
6560              * Fires after the field has been validated with no errors.
6561              * @param {Roo.form.Field} this
6562              */
6563             valid : true,
6564              /**
6565              * @event keyup
6566              * Fires after the key up
6567              * @param {Roo.form.Field} this
6568              * @param {Roo.EventObject}  e The event Object
6569              */
6570             keyup : true,
6571             /**
6572              * @event formatedValue
6573              * Fires when get the value of the formated input
6574              * @param {Roo.bootstrap.Input} this
6575              * @param {String} value
6576              */
6577             formatedValue : true
6578         });
6579 };
6580
6581 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
6582      /**
6583      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6584       automatic validation (defaults to "keyup").
6585      */
6586     validationEvent : "keyup",
6587      /**
6588      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6589      */
6590     validateOnBlur : true,
6591     /**
6592      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6593      */
6594     validationDelay : 250,
6595      /**
6596      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6597      */
6598     focusClass : "x-form-focus",  // not needed???
6599     
6600        
6601     /**
6602      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6603      */
6604     invalidClass : "has-error",
6605     
6606     /**
6607      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6608      */
6609     selectOnFocus : false,
6610     
6611      /**
6612      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6613      */
6614     maskRe : null,
6615        /**
6616      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6617      */
6618     vtype : null,
6619     
6620       /**
6621      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6622      */
6623     disableKeyFilter : false,
6624     
6625        /**
6626      * @cfg {Boolean} disabled True to disable the field (defaults to false).
6627      */
6628     disabled : false,
6629      /**
6630      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6631      */
6632     allowBlank : true,
6633     /**
6634      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
6635      */
6636     blankText : "This field is required",
6637     
6638      /**
6639      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
6640      */
6641     minLength : 0,
6642     /**
6643      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
6644      */
6645     maxLength : Number.MAX_VALUE,
6646     /**
6647      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
6648      */
6649     minLengthText : "The minimum length for this field is {0}",
6650     /**
6651      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
6652      */
6653     maxLengthText : "The maximum length for this field is {0}",
6654   
6655     
6656     /**
6657      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
6658      * If available, this function will be called only after the basic validators all return true, and will be passed the
6659      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
6660      */
6661     validator : null,
6662     /**
6663      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
6664      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
6665      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
6666      */
6667     regex : null,
6668     /**
6669      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
6670      */
6671     regexText : "",
6672     
6673     
6674     
6675     fieldLabel : '',
6676     inputType : 'text',
6677     
6678     name : false,
6679     placeholder: false,
6680     before : false,
6681     after : false,
6682     size : false,
6683     // private
6684     hasFocus : false,
6685     preventMark: false,
6686     isFormField : true,
6687     value : '',
6688     labelWidth : 2,
6689     labelAlign : false,
6690     readOnly : false,
6691     align : false,
6692     formatedValue : false,
6693     
6694     parentLabelAlign : function()
6695     {
6696         var parent = this;
6697         while (parent.parent()) {
6698             parent = parent.parent();
6699             if (typeof(parent.labelAlign) !='undefined') {
6700                 return parent.labelAlign;
6701             }
6702         }
6703         return 'left';
6704         
6705     },
6706     
6707     getAutoCreate : function(){
6708         
6709         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6710         
6711         var id = Roo.id();
6712         
6713         var cfg = {};
6714         
6715         if(this.inputType != 'hidden'){
6716             cfg.cls = 'form-group' //input-group
6717         }
6718         
6719         var input =  {
6720             tag: 'input',
6721             id : id,
6722             type : this.inputType,
6723             value : this.value,
6724             cls : 'form-control',
6725             placeholder : this.placeholder || ''
6726             
6727         };
6728         
6729         if(this.align){
6730             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
6731         }
6732         
6733         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6734             input.maxLength = this.maxLength;
6735         }
6736         
6737         if (this.disabled) {
6738             input.disabled=true;
6739         }
6740         
6741         if (this.readOnly) {
6742             input.readonly=true;
6743         }
6744         
6745         if (this.name) {
6746             input.name = this.name;
6747         }
6748         if (this.size) {
6749             input.cls += ' input-' + this.size;
6750         }
6751         var settings=this;
6752         ['xs','sm','md','lg'].map(function(size){
6753             if (settings[size]) {
6754                 cfg.cls += ' col-' + size + '-' + settings[size];
6755             }
6756         });
6757         
6758         var inputblock = input;
6759         
6760         if (this.before || this.after) {
6761             
6762             inputblock = {
6763                 cls : 'input-group',
6764                 cn :  [] 
6765             };
6766             if (this.before && typeof(this.before) == 'string') {
6767                 
6768                 inputblock.cn.push({
6769                     tag :'span',
6770                     cls : 'roo-input-before input-group-addon',
6771                     html : this.before
6772                 });
6773             }
6774             if (this.before && typeof(this.before) == 'object') {
6775                 this.before = Roo.factory(this.before);
6776                 Roo.log(this.before);
6777                 inputblock.cn.push({
6778                     tag :'span',
6779                     cls : 'roo-input-before input-group-' +
6780                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6781                 });
6782             }
6783             
6784             inputblock.cn.push(input);
6785             
6786             if (this.after && typeof(this.after) == 'string') {
6787                 inputblock.cn.push({
6788                     tag :'span',
6789                     cls : 'roo-input-after input-group-addon',
6790                     html : this.after
6791                 });
6792             }
6793             if (this.after && typeof(this.after) == 'object') {
6794                 this.after = Roo.factory(this.after);
6795                 Roo.log(this.after);
6796                 inputblock.cn.push({
6797                     tag :'span',
6798                     cls : 'roo-input-after input-group-' +
6799                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
6800                 });
6801             }
6802         };
6803         
6804         if (align ==='left' && this.fieldLabel.length) {
6805                 Roo.log("left and has label");
6806                 cfg.cn = [
6807                     
6808                     {
6809                         tag: 'label',
6810                         'for' :  id,
6811                         cls : 'control-label col-sm-' + this.labelWidth,
6812                         html : this.fieldLabel
6813                         
6814                     },
6815                     {
6816                         cls : "col-sm-" + (12 - this.labelWidth), 
6817                         cn: [
6818                             inputblock
6819                         ]
6820                     }
6821                     
6822                 ];
6823         } else if ( this.fieldLabel.length) {
6824                 Roo.log(" label");
6825                  cfg.cn = [
6826                    
6827                     {
6828                         tag: 'label',
6829                         //cls : 'input-group-addon',
6830                         html : this.fieldLabel
6831                         
6832                     },
6833                     
6834                     inputblock
6835                     
6836                 ];
6837
6838         } else {
6839             
6840                 Roo.log(" no label && no align");
6841                 cfg.cn = [
6842                     
6843                         inputblock
6844                     
6845                 ];
6846                 
6847                 
6848         };
6849         Roo.log('input-parentType: ' + this.parentType);
6850         
6851         if (this.parentType === 'Navbar' &&  this.parent().bar) {
6852            cfg.cls += ' navbar-form';
6853            Roo.log(cfg);
6854         }
6855         
6856         return cfg;
6857         
6858     },
6859     /**
6860      * return the real input element.
6861      */
6862     inputEl: function ()
6863     {
6864         return this.el.select('input.form-control',true).first();
6865     },
6866     setDisabled : function(v)
6867     {
6868         var i  = this.inputEl().dom;
6869         if (!v) {
6870             i.removeAttribute('disabled');
6871             return;
6872             
6873         }
6874         i.setAttribute('disabled','true');
6875     },
6876     initEvents : function()
6877     {
6878         
6879         this.inputEl().on("keydown" , this.fireKey,  this);
6880         this.inputEl().on("focus", this.onFocus,  this);
6881         this.inputEl().on("blur", this.onBlur,  this);
6882         
6883         this.inputEl().relayEvent('keyup', this);
6884
6885         // reference to original value for reset
6886         this.originalValue = this.getValue();
6887         //Roo.form.TextField.superclass.initEvents.call(this);
6888         if(this.validationEvent == 'keyup'){
6889             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6890             this.inputEl().on('keyup', this.filterValidation, this);
6891         }
6892         else if(this.validationEvent !== false){
6893             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6894         }
6895         
6896         if(this.selectOnFocus){
6897             this.on("focus", this.preFocus, this);
6898             
6899         }
6900         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6901             this.inputEl().on("keypress", this.filterKeys, this);
6902         }
6903        /* if(this.grow){
6904             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
6905             this.el.on("click", this.autoSize,  this);
6906         }
6907         */
6908         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6909             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6910         }
6911         
6912         if (typeof(this.before) == 'object') {
6913             this.before.render(this.el.select('.roo-input-before',true).first());
6914         }
6915         if (typeof(this.after) == 'object') {
6916             this.after.render(this.el.select('.roo-input-after',true).first());
6917         }
6918         
6919         
6920     },
6921     filterValidation : function(e){
6922         if(!e.isNavKeyPress()){
6923             this.validationTask.delay(this.validationDelay);
6924         }
6925     },
6926      /**
6927      * Validates the field value
6928      * @return {Boolean} True if the value is valid, else false
6929      */
6930     validate : function(){
6931         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6932         if(this.disabled || this.validateValue(this.getRawValue())){
6933             this.clearInvalid();
6934             return true;
6935         }
6936         return false;
6937     },
6938     
6939     
6940     /**
6941      * Validates a value according to the field's validation rules and marks the field as invalid
6942      * if the validation fails
6943      * @param {Mixed} value The value to validate
6944      * @return {Boolean} True if the value is valid, else false
6945      */
6946     validateValue : function(value){
6947         if(value.length < 1)  { // if it's blank
6948              if(this.allowBlank){
6949                 this.clearInvalid();
6950                 return true;
6951              }else{
6952                 this.markInvalid(this.blankText);
6953                 return false;
6954              }
6955         }
6956         if(value.length < this.minLength){
6957             this.markInvalid(String.format(this.minLengthText, this.minLength));
6958             return false;
6959         }
6960         if(value.length > this.maxLength){
6961             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
6962             return false;
6963         }
6964         if(this.vtype){
6965             var vt = Roo.form.VTypes;
6966             if(!vt[this.vtype](value, this)){
6967                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
6968                 return false;
6969             }
6970         }
6971         if(typeof this.validator == "function"){
6972             var msg = this.validator(value);
6973             if(msg !== true){
6974                 this.markInvalid(msg);
6975                 return false;
6976             }
6977         }
6978         if(this.regex && !this.regex.test(value)){
6979             this.markInvalid(this.regexText);
6980             return false;
6981         }
6982         return true;
6983     },
6984
6985     
6986     
6987      // private
6988     fireKey : function(e){
6989         //Roo.log('field ' + e.getKey());
6990         if(e.isNavKeyPress()){
6991             this.fireEvent("specialkey", this, e);
6992         }
6993     },
6994     focus : function (selectText){
6995         if(this.rendered){
6996             this.inputEl().focus();
6997             if(selectText === true){
6998                 this.inputEl().dom.select();
6999             }
7000         }
7001         return this;
7002     } ,
7003     
7004     onFocus : function(){
7005         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7006            // this.el.addClass(this.focusClass);
7007         }
7008         if(!this.hasFocus){
7009             this.hasFocus = true;
7010             this.startValue = this.getValue();
7011             this.fireEvent("focus", this);
7012         }
7013     },
7014     
7015     beforeBlur : Roo.emptyFn,
7016
7017     
7018     // private
7019     onBlur : function(){
7020         this.beforeBlur();
7021         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7022             //this.el.removeClass(this.focusClass);
7023         }
7024         this.hasFocus = false;
7025         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7026             this.validate();
7027         }
7028         var v = this.getValue();
7029         if(String(v) !== String(this.startValue)){
7030             this.fireEvent('change', this, v, this.startValue);
7031         }
7032         this.fireEvent("blur", this);
7033     },
7034     
7035     /**
7036      * Resets the current field value to the originally loaded value and clears any validation messages
7037      */
7038     reset : function(){
7039         this.setValue(this.originalValue);
7040         this.clearInvalid();
7041     },
7042      /**
7043      * Returns the name of the field
7044      * @return {Mixed} name The name field
7045      */
7046     getName: function(){
7047         return this.name;
7048     },
7049      /**
7050      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
7051      * @return {Mixed} value The field value
7052      */
7053     getValue : function(){
7054         
7055         var v = this.inputEl().getValue();
7056         
7057         if(this.formatedValue){
7058             
7059             this.fireEvent("formatedValue", this, v);
7060             
7061             return this.value;
7062         }
7063         
7064         return v;
7065     },
7066     /**
7067      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
7068      * @return {Mixed} value The field value
7069      */
7070     getRawValue : function(){
7071         var v = this.inputEl().getValue();
7072         
7073         return v;
7074     },
7075     
7076     /**
7077      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
7078      * @param {Mixed} value The value to set
7079      */
7080     setRawValue : function(v){
7081         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7082     },
7083     
7084     selectText : function(start, end){
7085         var v = this.getRawValue();
7086         if(v.length > 0){
7087             start = start === undefined ? 0 : start;
7088             end = end === undefined ? v.length : end;
7089             var d = this.inputEl().dom;
7090             if(d.setSelectionRange){
7091                 d.setSelectionRange(start, end);
7092             }else if(d.createTextRange){
7093                 var range = d.createTextRange();
7094                 range.moveStart("character", start);
7095                 range.moveEnd("character", v.length-end);
7096                 range.select();
7097             }
7098         }
7099     },
7100     
7101     /**
7102      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
7103      * @param {Mixed} value The value to set
7104      */
7105     setValue : function(v){
7106         this.value = v;
7107         if(this.rendered){
7108             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7109             this.validate();
7110         }
7111     },
7112     
7113     /*
7114     processValue : function(value){
7115         if(this.stripCharsRe){
7116             var newValue = value.replace(this.stripCharsRe, '');
7117             if(newValue !== value){
7118                 this.setRawValue(newValue);
7119                 return newValue;
7120             }
7121         }
7122         return value;
7123     },
7124   */
7125     preFocus : function(){
7126         
7127         if(this.selectOnFocus){
7128             this.inputEl().dom.select();
7129         }
7130     },
7131     filterKeys : function(e){
7132         var k = e.getKey();
7133         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7134             return;
7135         }
7136         var c = e.getCharCode(), cc = String.fromCharCode(c);
7137         if(Roo.isIE && (e.isSpecialKey() || !cc)){
7138             return;
7139         }
7140         if(!this.maskRe.test(cc)){
7141             e.stopEvent();
7142         }
7143     },
7144      /**
7145      * Clear any invalid styles/messages for this field
7146      */
7147     clearInvalid : function(){
7148         
7149         if(!this.el || this.preventMark){ // not rendered
7150             return;
7151         }
7152         this.el.removeClass(this.invalidClass);
7153         /*
7154         switch(this.msgTarget){
7155             case 'qtip':
7156                 this.el.dom.qtip = '';
7157                 break;
7158             case 'title':
7159                 this.el.dom.title = '';
7160                 break;
7161             case 'under':
7162                 if(this.errorEl){
7163                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7164                 }
7165                 break;
7166             case 'side':
7167                 if(this.errorIcon){
7168                     this.errorIcon.dom.qtip = '';
7169                     this.errorIcon.hide();
7170                     this.un('resize', this.alignErrorIcon, this);
7171                 }
7172                 break;
7173             default:
7174                 var t = Roo.getDom(this.msgTarget);
7175                 t.innerHTML = '';
7176                 t.style.display = 'none';
7177                 break;
7178         }
7179         */
7180         this.fireEvent('valid', this);
7181     },
7182      /**
7183      * Mark this field as invalid
7184      * @param {String} msg The validation message
7185      */
7186     markInvalid : function(msg){
7187         if(!this.el  || this.preventMark){ // not rendered
7188             return;
7189         }
7190         this.el.addClass(this.invalidClass);
7191         /*
7192         msg = msg || this.invalidText;
7193         switch(this.msgTarget){
7194             case 'qtip':
7195                 this.el.dom.qtip = msg;
7196                 this.el.dom.qclass = 'x-form-invalid-tip';
7197                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7198                     Roo.QuickTips.enable();
7199                 }
7200                 break;
7201             case 'title':
7202                 this.el.dom.title = msg;
7203                 break;
7204             case 'under':
7205                 if(!this.errorEl){
7206                     var elp = this.el.findParent('.x-form-element', 5, true);
7207                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7208                     this.errorEl.setWidth(elp.getWidth(true)-20);
7209                 }
7210                 this.errorEl.update(msg);
7211                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7212                 break;
7213             case 'side':
7214                 if(!this.errorIcon){
7215                     var elp = this.el.findParent('.x-form-element', 5, true);
7216                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7217                 }
7218                 this.alignErrorIcon();
7219                 this.errorIcon.dom.qtip = msg;
7220                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7221                 this.errorIcon.show();
7222                 this.on('resize', this.alignErrorIcon, this);
7223                 break;
7224             default:
7225                 var t = Roo.getDom(this.msgTarget);
7226                 t.innerHTML = msg;
7227                 t.style.display = this.msgDisplay;
7228                 break;
7229         }
7230         */
7231         this.fireEvent('invalid', this, msg);
7232     },
7233     // private
7234     SafariOnKeyDown : function(event)
7235     {
7236         // this is a workaround for a password hang bug on chrome/ webkit.
7237         
7238         var isSelectAll = false;
7239         
7240         if(this.inputEl().dom.selectionEnd > 0){
7241             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7242         }
7243         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7244             event.preventDefault();
7245             this.setValue('');
7246             return;
7247         }
7248         
7249         if(isSelectAll){ // backspace and delete key
7250             
7251             event.preventDefault();
7252             // this is very hacky as keydown always get's upper case.
7253             //
7254             var cc = String.fromCharCode(event.getCharCode());
7255             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
7256             
7257         }
7258     },
7259     adjustWidth : function(tag, w){
7260         tag = tag.toLowerCase();
7261         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7262             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7263                 if(tag == 'input'){
7264                     return w + 2;
7265                 }
7266                 if(tag == 'textarea'){
7267                     return w-2;
7268                 }
7269             }else if(Roo.isOpera){
7270                 if(tag == 'input'){
7271                     return w + 2;
7272                 }
7273                 if(tag == 'textarea'){
7274                     return w-2;
7275                 }
7276             }
7277         }
7278         return w;
7279     }
7280     
7281 });
7282
7283  
7284 /*
7285  * - LGPL
7286  *
7287  * Input
7288  * 
7289  */
7290
7291 /**
7292  * @class Roo.bootstrap.TextArea
7293  * @extends Roo.bootstrap.Input
7294  * Bootstrap TextArea class
7295  * @cfg {Number} cols Specifies the visible width of a text area
7296  * @cfg {Number} rows Specifies the visible number of lines in a text area
7297  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7298  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7299  * @cfg {string} html text
7300  * 
7301  * @constructor
7302  * Create a new TextArea
7303  * @param {Object} config The config object
7304  */
7305
7306 Roo.bootstrap.TextArea = function(config){
7307     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7308    
7309 };
7310
7311 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
7312      
7313     cols : false,
7314     rows : 5,
7315     readOnly : false,
7316     warp : 'soft',
7317     resize : false,
7318     value: false,
7319     html: false,
7320     
7321     getAutoCreate : function(){
7322         
7323         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7324         
7325         var id = Roo.id();
7326         
7327         var cfg = {};
7328         
7329         var input =  {
7330             tag: 'textarea',
7331             id : id,
7332             warp : this.warp,
7333             rows : this.rows,
7334             value : this.value || '',
7335             html: this.html || '',
7336             cls : 'form-control',
7337             placeholder : this.placeholder || '' 
7338             
7339         };
7340         
7341         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7342             input.maxLength = this.maxLength;
7343         }
7344         
7345         if(this.resize){
7346             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7347         }
7348         
7349         if(this.cols){
7350             input.cols = this.cols;
7351         }
7352         
7353         if (this.readOnly) {
7354             input.readonly = true;
7355         }
7356         
7357         if (this.name) {
7358             input.name = this.name;
7359         }
7360         
7361         if (this.size) {
7362             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7363         }
7364         
7365         var settings=this;
7366         ['xs','sm','md','lg'].map(function(size){
7367             if (settings[size]) {
7368                 cfg.cls += ' col-' + size + '-' + settings[size];
7369             }
7370         });
7371         
7372         var inputblock = input;
7373         
7374         if (this.before || this.after) {
7375             
7376             inputblock = {
7377                 cls : 'input-group',
7378                 cn :  [] 
7379             };
7380             if (this.before) {
7381                 inputblock.cn.push({
7382                     tag :'span',
7383                     cls : 'input-group-addon',
7384                     html : this.before
7385                 });
7386             }
7387             inputblock.cn.push(input);
7388             if (this.after) {
7389                 inputblock.cn.push({
7390                     tag :'span',
7391                     cls : 'input-group-addon',
7392                     html : this.after
7393                 });
7394             }
7395             
7396         }
7397         
7398         if (align ==='left' && this.fieldLabel.length) {
7399                 Roo.log("left and has label");
7400                 cfg.cn = [
7401                     
7402                     {
7403                         tag: 'label',
7404                         'for' :  id,
7405                         cls : 'control-label col-sm-' + this.labelWidth,
7406                         html : this.fieldLabel
7407                         
7408                     },
7409                     {
7410                         cls : "col-sm-" + (12 - this.labelWidth), 
7411                         cn: [
7412                             inputblock
7413                         ]
7414                     }
7415                     
7416                 ];
7417         } else if ( this.fieldLabel.length) {
7418                 Roo.log(" label");
7419                  cfg.cn = [
7420                    
7421                     {
7422                         tag: 'label',
7423                         //cls : 'input-group-addon',
7424                         html : this.fieldLabel
7425                         
7426                     },
7427                     
7428                     inputblock
7429                     
7430                 ];
7431
7432         } else {
7433             
7434                    Roo.log(" no label && no align");
7435                 cfg.cn = [
7436                     
7437                         inputblock
7438                     
7439                 ];
7440                 
7441                 
7442         }
7443         
7444         if (this.disabled) {
7445             input.disabled=true;
7446         }
7447         
7448         return cfg;
7449         
7450     },
7451     /**
7452      * return the real textarea element.
7453      */
7454     inputEl: function ()
7455     {
7456         return this.el.select('textarea.form-control',true).first();
7457     }
7458 });
7459
7460  
7461 /*
7462  * - LGPL
7463  *
7464  * trigger field - base class for combo..
7465  * 
7466  */
7467  
7468 /**
7469  * @class Roo.bootstrap.TriggerField
7470  * @extends Roo.bootstrap.Input
7471  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7472  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7473  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7474  * for which you can provide a custom implementation.  For example:
7475  * <pre><code>
7476 var trigger = new Roo.bootstrap.TriggerField();
7477 trigger.onTriggerClick = myTriggerFn;
7478 trigger.applyTo('my-field');
7479 </code></pre>
7480  *
7481  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7482  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7483  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
7484  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7485  * @constructor
7486  * Create a new TriggerField.
7487  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7488  * to the base TextField)
7489  */
7490 Roo.bootstrap.TriggerField = function(config){
7491     this.mimicing = false;
7492     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7493 };
7494
7495 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
7496     /**
7497      * @cfg {String} triggerClass A CSS class to apply to the trigger
7498      */
7499      /**
7500      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7501      */
7502     hideTrigger:false,
7503
7504     /** @cfg {Boolean} grow @hide */
7505     /** @cfg {Number} growMin @hide */
7506     /** @cfg {Number} growMax @hide */
7507
7508     /**
7509      * @hide 
7510      * @method
7511      */
7512     autoSize: Roo.emptyFn,
7513     // private
7514     monitorTab : true,
7515     // private
7516     deferHeight : true,
7517
7518     
7519     actionMode : 'wrap',
7520     
7521     
7522     
7523     getAutoCreate : function(){
7524        
7525         var parent = this.parent();
7526         
7527         var align = this.labelAlign || this.parentLabelAlign();
7528         
7529         var id = Roo.id();
7530         
7531         var cfg = {
7532             cls: 'form-group' //input-group
7533         };
7534         
7535         
7536         var input =  {
7537             tag: 'input',
7538             id : id,
7539             type : this.inputType,
7540             cls : 'form-control',
7541             autocomplete: 'off',
7542             placeholder : this.placeholder || '' 
7543             
7544         };
7545         if (this.name) {
7546             input.name = this.name;
7547         }
7548         if (this.size) {
7549             input.cls += ' input-' + this.size;
7550         }
7551         
7552         if (this.disabled) {
7553             input.disabled=true;
7554         }
7555         
7556         var inputblock = input;
7557         
7558         if (this.before || this.after) {
7559             
7560             inputblock = {
7561                 cls : 'input-group',
7562                 cn :  [] 
7563             };
7564             if (this.before) {
7565                 inputblock.cn.push({
7566                     tag :'span',
7567                     cls : 'input-group-addon',
7568                     html : this.before
7569                 });
7570             }
7571             inputblock.cn.push(input);
7572             if (this.after) {
7573                 inputblock.cn.push({
7574                     tag :'span',
7575                     cls : 'input-group-addon',
7576                     html : this.after
7577                 });
7578             }
7579             
7580         };
7581         
7582         var box = {
7583             tag: 'div',
7584             cn: [
7585                 {
7586                     tag: 'input',
7587                     type : 'hidden',
7588                     cls: 'form-hidden-field'
7589                 },
7590                 inputblock
7591             ]
7592             
7593         };
7594         
7595         if(this.multiple){
7596             Roo.log('multiple');
7597             
7598             box = {
7599                 tag: 'div',
7600                 cn: [
7601                     {
7602                         tag: 'input',
7603                         type : 'hidden',
7604                         cls: 'form-hidden-field'
7605                     },
7606                     {
7607                         tag: 'ul',
7608                         cls: 'select2-choices',
7609                         cn:[
7610                             {
7611                                 tag: 'li',
7612                                 cls: 'select2-search-field',
7613                                 cn: [
7614
7615                                     inputblock
7616                                 ]
7617                             }
7618                         ]
7619                     }
7620                 ]
7621             }
7622         };
7623         
7624         var combobox = {
7625             cls: 'select2-container input-group',
7626             cn: [
7627                 box,
7628                 {
7629                     tag: 'ul',
7630                     cls: 'typeahead typeahead-long dropdown-menu',
7631                     style: 'display:none'
7632                 }
7633             ]
7634         };
7635         
7636         if(!this.multiple){
7637             combobox.cn.push({
7638                 tag :'span',
7639                 cls : 'input-group-addon btn dropdown-toggle',
7640                 cn : [
7641                     {
7642                         tag: 'span',
7643                         cls: 'caret'
7644                     },
7645                     {
7646                         tag: 'span',
7647                         cls: 'combobox-clear',
7648                         cn  : [
7649                             {
7650                                 tag : 'i',
7651                                 cls: 'icon-remove'
7652                             }
7653                         ]
7654                     }
7655                 ]
7656
7657             })
7658         }
7659         
7660         if(this.multiple){
7661             combobox.cls += ' select2-container-multi';
7662         }
7663         
7664         if (align ==='left' && this.fieldLabel.length) {
7665             
7666                 Roo.log("left and has label");
7667                 cfg.cn = [
7668                     
7669                     {
7670                         tag: 'label',
7671                         'for' :  id,
7672                         cls : 'control-label col-sm-' + this.labelWidth,
7673                         html : this.fieldLabel
7674                         
7675                     },
7676                     {
7677                         cls : "col-sm-" + (12 - this.labelWidth), 
7678                         cn: [
7679                             combobox
7680                         ]
7681                     }
7682                     
7683                 ];
7684         } else if ( this.fieldLabel.length) {
7685                 Roo.log(" label");
7686                  cfg.cn = [
7687                    
7688                     {
7689                         tag: 'label',
7690                         //cls : 'input-group-addon',
7691                         html : this.fieldLabel
7692                         
7693                     },
7694                     
7695                     combobox
7696                     
7697                 ];
7698
7699         } else {
7700             
7701                 Roo.log(" no label && no align");
7702                 cfg = combobox
7703                      
7704                 
7705         }
7706          
7707         var settings=this;
7708         ['xs','sm','md','lg'].map(function(size){
7709             if (settings[size]) {
7710                 cfg.cls += ' col-' + size + '-' + settings[size];
7711             }
7712         });
7713         
7714         return cfg;
7715         
7716     },
7717     
7718     
7719     
7720     // private
7721     onResize : function(w, h){
7722 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
7723 //        if(typeof w == 'number'){
7724 //            var x = w - this.trigger.getWidth();
7725 //            this.inputEl().setWidth(this.adjustWidth('input', x));
7726 //            this.trigger.setStyle('left', x+'px');
7727 //        }
7728     },
7729
7730     // private
7731     adjustSize : Roo.BoxComponent.prototype.adjustSize,
7732
7733     // private
7734     getResizeEl : function(){
7735         return this.inputEl();
7736     },
7737
7738     // private
7739     getPositionEl : function(){
7740         return this.inputEl();
7741     },
7742
7743     // private
7744     alignErrorIcon : function(){
7745         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
7746     },
7747
7748     // private
7749     initEvents : function(){
7750         
7751         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
7752         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
7753         if(!this.multiple){
7754             this.trigger = this.el.select('span.dropdown-toggle',true).first();
7755             if(this.hideTrigger){
7756                 this.trigger.setDisplayed(false);
7757             }
7758             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
7759         }
7760         
7761         if(this.multiple){
7762             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
7763         }
7764         
7765         //this.trigger.addClassOnOver('x-form-trigger-over');
7766         //this.trigger.addClassOnClick('x-form-trigger-click');
7767         
7768         //if(!this.width){
7769         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
7770         //}
7771     },
7772
7773     // private
7774     initTrigger : function(){
7775        
7776     },
7777
7778     // private
7779     onDestroy : function(){
7780         if(this.trigger){
7781             this.trigger.removeAllListeners();
7782           //  this.trigger.remove();
7783         }
7784         //if(this.wrap){
7785         //    this.wrap.remove();
7786         //}
7787         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
7788     },
7789
7790     // private
7791     onFocus : function(){
7792         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
7793         /*
7794         if(!this.mimicing){
7795             this.wrap.addClass('x-trigger-wrap-focus');
7796             this.mimicing = true;
7797             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
7798             if(this.monitorTab){
7799                 this.el.on("keydown", this.checkTab, this);
7800             }
7801         }
7802         */
7803     },
7804
7805     // private
7806     checkTab : function(e){
7807         if(e.getKey() == e.TAB){
7808             this.triggerBlur();
7809         }
7810     },
7811
7812     // private
7813     onBlur : function(){
7814         // do nothing
7815     },
7816
7817     // private
7818     mimicBlur : function(e, t){
7819         /*
7820         if(!this.wrap.contains(t) && this.validateBlur()){
7821             this.triggerBlur();
7822         }
7823         */
7824     },
7825
7826     // private
7827     triggerBlur : function(){
7828         this.mimicing = false;
7829         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
7830         if(this.monitorTab){
7831             this.el.un("keydown", this.checkTab, this);
7832         }
7833         //this.wrap.removeClass('x-trigger-wrap-focus');
7834         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
7835     },
7836
7837     // private
7838     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
7839     validateBlur : function(e, t){
7840         return true;
7841     },
7842
7843     // private
7844     onDisable : function(){
7845         this.inputEl().dom.disabled = true;
7846         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
7847         //if(this.wrap){
7848         //    this.wrap.addClass('x-item-disabled');
7849         //}
7850     },
7851
7852     // private
7853     onEnable : function(){
7854         this.inputEl().dom.disabled = false;
7855         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
7856         //if(this.wrap){
7857         //    this.el.removeClass('x-item-disabled');
7858         //}
7859     },
7860
7861     // private
7862     onShow : function(){
7863         var ae = this.getActionEl();
7864         
7865         if(ae){
7866             ae.dom.style.display = '';
7867             ae.dom.style.visibility = 'visible';
7868         }
7869     },
7870
7871     // private
7872     
7873     onHide : function(){
7874         var ae = this.getActionEl();
7875         ae.dom.style.display = 'none';
7876     },
7877
7878     /**
7879      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
7880      * by an implementing function.
7881      * @method
7882      * @param {EventObject} e
7883      */
7884     onTriggerClick : Roo.emptyFn
7885 });
7886  /*
7887  * Based on:
7888  * Ext JS Library 1.1.1
7889  * Copyright(c) 2006-2007, Ext JS, LLC.
7890  *
7891  * Originally Released Under LGPL - original licence link has changed is not relivant.
7892  *
7893  * Fork - LGPL
7894  * <script type="text/javascript">
7895  */
7896
7897
7898 /**
7899  * @class Roo.data.SortTypes
7900  * @singleton
7901  * Defines the default sorting (casting?) comparison functions used when sorting data.
7902  */
7903 Roo.data.SortTypes = {
7904     /**
7905      * Default sort that does nothing
7906      * @param {Mixed} s The value being converted
7907      * @return {Mixed} The comparison value
7908      */
7909     none : function(s){
7910         return s;
7911     },
7912     
7913     /**
7914      * The regular expression used to strip tags
7915      * @type {RegExp}
7916      * @property
7917      */
7918     stripTagsRE : /<\/?[^>]+>/gi,
7919     
7920     /**
7921      * Strips all HTML tags to sort on text only
7922      * @param {Mixed} s The value being converted
7923      * @return {String} The comparison value
7924      */
7925     asText : function(s){
7926         return String(s).replace(this.stripTagsRE, "");
7927     },
7928     
7929     /**
7930      * Strips all HTML tags to sort on text only - Case insensitive
7931      * @param {Mixed} s The value being converted
7932      * @return {String} The comparison value
7933      */
7934     asUCText : function(s){
7935         return String(s).toUpperCase().replace(this.stripTagsRE, "");
7936     },
7937     
7938     /**
7939      * Case insensitive string
7940      * @param {Mixed} s The value being converted
7941      * @return {String} The comparison value
7942      */
7943     asUCString : function(s) {
7944         return String(s).toUpperCase();
7945     },
7946     
7947     /**
7948      * Date sorting
7949      * @param {Mixed} s The value being converted
7950      * @return {Number} The comparison value
7951      */
7952     asDate : function(s) {
7953         if(!s){
7954             return 0;
7955         }
7956         if(s instanceof Date){
7957             return s.getTime();
7958         }
7959         return Date.parse(String(s));
7960     },
7961     
7962     /**
7963      * Float sorting
7964      * @param {Mixed} s The value being converted
7965      * @return {Float} The comparison value
7966      */
7967     asFloat : function(s) {
7968         var val = parseFloat(String(s).replace(/,/g, ""));
7969         if(isNaN(val)) val = 0;
7970         return val;
7971     },
7972     
7973     /**
7974      * Integer sorting
7975      * @param {Mixed} s The value being converted
7976      * @return {Number} The comparison value
7977      */
7978     asInt : function(s) {
7979         var val = parseInt(String(s).replace(/,/g, ""));
7980         if(isNaN(val)) val = 0;
7981         return val;
7982     }
7983 };/*
7984  * Based on:
7985  * Ext JS Library 1.1.1
7986  * Copyright(c) 2006-2007, Ext JS, LLC.
7987  *
7988  * Originally Released Under LGPL - original licence link has changed is not relivant.
7989  *
7990  * Fork - LGPL
7991  * <script type="text/javascript">
7992  */
7993
7994 /**
7995 * @class Roo.data.Record
7996  * Instances of this class encapsulate both record <em>definition</em> information, and record
7997  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7998  * to access Records cached in an {@link Roo.data.Store} object.<br>
7999  * <p>
8000  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8001  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8002  * objects.<br>
8003  * <p>
8004  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8005  * @constructor
8006  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8007  * {@link #create}. The parameters are the same.
8008  * @param {Array} data An associative Array of data values keyed by the field name.
8009  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8010  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8011  * not specified an integer id is generated.
8012  */
8013 Roo.data.Record = function(data, id){
8014     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8015     this.data = data;
8016 };
8017
8018 /**
8019  * Generate a constructor for a specific record layout.
8020  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8021  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8022  * Each field definition object may contain the following properties: <ul>
8023  * <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,
8024  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8025  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8026  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8027  * is being used, then this is a string containing the javascript expression to reference the data relative to 
8028  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8029  * to the data item relative to the record element. If the mapping expression is the same as the field name,
8030  * this may be omitted.</p></li>
8031  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8032  * <ul><li>auto (Default, implies no conversion)</li>
8033  * <li>string</li>
8034  * <li>int</li>
8035  * <li>float</li>
8036  * <li>boolean</li>
8037  * <li>date</li></ul></p></li>
8038  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8039  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8040  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8041  * by the Reader into an object that will be stored in the Record. It is passed the
8042  * following parameters:<ul>
8043  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8044  * </ul></p></li>
8045  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8046  * </ul>
8047  * <br>usage:<br><pre><code>
8048 var TopicRecord = Roo.data.Record.create(
8049     {name: 'title', mapping: 'topic_title'},
8050     {name: 'author', mapping: 'username'},
8051     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8052     {name: 'lastPost', mapping: 'post_time', type: 'date'},
8053     {name: 'lastPoster', mapping: 'user2'},
8054     {name: 'excerpt', mapping: 'post_text'}
8055 );
8056
8057 var myNewRecord = new TopicRecord({
8058     title: 'Do my job please',
8059     author: 'noobie',
8060     totalPosts: 1,
8061     lastPost: new Date(),
8062     lastPoster: 'Animal',
8063     excerpt: 'No way dude!'
8064 });
8065 myStore.add(myNewRecord);
8066 </code></pre>
8067  * @method create
8068  * @static
8069  */
8070 Roo.data.Record.create = function(o){
8071     var f = function(){
8072         f.superclass.constructor.apply(this, arguments);
8073     };
8074     Roo.extend(f, Roo.data.Record);
8075     var p = f.prototype;
8076     p.fields = new Roo.util.MixedCollection(false, function(field){
8077         return field.name;
8078     });
8079     for(var i = 0, len = o.length; i < len; i++){
8080         p.fields.add(new Roo.data.Field(o[i]));
8081     }
8082     f.getField = function(name){
8083         return p.fields.get(name);  
8084     };
8085     return f;
8086 };
8087
8088 Roo.data.Record.AUTO_ID = 1000;
8089 Roo.data.Record.EDIT = 'edit';
8090 Roo.data.Record.REJECT = 'reject';
8091 Roo.data.Record.COMMIT = 'commit';
8092
8093 Roo.data.Record.prototype = {
8094     /**
8095      * Readonly flag - true if this record has been modified.
8096      * @type Boolean
8097      */
8098     dirty : false,
8099     editing : false,
8100     error: null,
8101     modified: null,
8102
8103     // private
8104     join : function(store){
8105         this.store = store;
8106     },
8107
8108     /**
8109      * Set the named field to the specified value.
8110      * @param {String} name The name of the field to set.
8111      * @param {Object} value The value to set the field to.
8112      */
8113     set : function(name, value){
8114         if(this.data[name] == value){
8115             return;
8116         }
8117         this.dirty = true;
8118         if(!this.modified){
8119             this.modified = {};
8120         }
8121         if(typeof this.modified[name] == 'undefined'){
8122             this.modified[name] = this.data[name];
8123         }
8124         this.data[name] = value;
8125         if(!this.editing && this.store){
8126             this.store.afterEdit(this);
8127         }       
8128     },
8129
8130     /**
8131      * Get the value of the named field.
8132      * @param {String} name The name of the field to get the value of.
8133      * @return {Object} The value of the field.
8134      */
8135     get : function(name){
8136         return this.data[name]; 
8137     },
8138
8139     // private
8140     beginEdit : function(){
8141         this.editing = true;
8142         this.modified = {}; 
8143     },
8144
8145     // private
8146     cancelEdit : function(){
8147         this.editing = false;
8148         delete this.modified;
8149     },
8150
8151     // private
8152     endEdit : function(){
8153         this.editing = false;
8154         if(this.dirty && this.store){
8155             this.store.afterEdit(this);
8156         }
8157     },
8158
8159     /**
8160      * Usually called by the {@link Roo.data.Store} which owns the Record.
8161      * Rejects all changes made to the Record since either creation, or the last commit operation.
8162      * Modified fields are reverted to their original values.
8163      * <p>
8164      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8165      * of reject operations.
8166      */
8167     reject : function(){
8168         var m = this.modified;
8169         for(var n in m){
8170             if(typeof m[n] != "function"){
8171                 this.data[n] = m[n];
8172             }
8173         }
8174         this.dirty = false;
8175         delete this.modified;
8176         this.editing = false;
8177         if(this.store){
8178             this.store.afterReject(this);
8179         }
8180     },
8181
8182     /**
8183      * Usually called by the {@link Roo.data.Store} which owns the Record.
8184      * Commits all changes made to the Record since either creation, or the last commit operation.
8185      * <p>
8186      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8187      * of commit operations.
8188      */
8189     commit : function(){
8190         this.dirty = false;
8191         delete this.modified;
8192         this.editing = false;
8193         if(this.store){
8194             this.store.afterCommit(this);
8195         }
8196     },
8197
8198     // private
8199     hasError : function(){
8200         return this.error != null;
8201     },
8202
8203     // private
8204     clearError : function(){
8205         this.error = null;
8206     },
8207
8208     /**
8209      * Creates a copy of this record.
8210      * @param {String} id (optional) A new record id if you don't want to use this record's id
8211      * @return {Record}
8212      */
8213     copy : function(newId) {
8214         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8215     }
8216 };/*
8217  * Based on:
8218  * Ext JS Library 1.1.1
8219  * Copyright(c) 2006-2007, Ext JS, LLC.
8220  *
8221  * Originally Released Under LGPL - original licence link has changed is not relivant.
8222  *
8223  * Fork - LGPL
8224  * <script type="text/javascript">
8225  */
8226
8227
8228
8229 /**
8230  * @class Roo.data.Store
8231  * @extends Roo.util.Observable
8232  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8233  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8234  * <p>
8235  * 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
8236  * has no knowledge of the format of the data returned by the Proxy.<br>
8237  * <p>
8238  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8239  * instances from the data object. These records are cached and made available through accessor functions.
8240  * @constructor
8241  * Creates a new Store.
8242  * @param {Object} config A config object containing the objects needed for the Store to access data,
8243  * and read the data into Records.
8244  */
8245 Roo.data.Store = function(config){
8246     this.data = new Roo.util.MixedCollection(false);
8247     this.data.getKey = function(o){
8248         return o.id;
8249     };
8250     this.baseParams = {};
8251     // private
8252     this.paramNames = {
8253         "start" : "start",
8254         "limit" : "limit",
8255         "sort" : "sort",
8256         "dir" : "dir",
8257         "multisort" : "_multisort"
8258     };
8259
8260     if(config && config.data){
8261         this.inlineData = config.data;
8262         delete config.data;
8263     }
8264
8265     Roo.apply(this, config);
8266     
8267     if(this.reader){ // reader passed
8268         this.reader = Roo.factory(this.reader, Roo.data);
8269         this.reader.xmodule = this.xmodule || false;
8270         if(!this.recordType){
8271             this.recordType = this.reader.recordType;
8272         }
8273         if(this.reader.onMetaChange){
8274             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8275         }
8276     }
8277
8278     if(this.recordType){
8279         this.fields = this.recordType.prototype.fields;
8280     }
8281     this.modified = [];
8282
8283     this.addEvents({
8284         /**
8285          * @event datachanged
8286          * Fires when the data cache has changed, and a widget which is using this Store
8287          * as a Record cache should refresh its view.
8288          * @param {Store} this
8289          */
8290         datachanged : true,
8291         /**
8292          * @event metachange
8293          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8294          * @param {Store} this
8295          * @param {Object} meta The JSON metadata
8296          */
8297         metachange : true,
8298         /**
8299          * @event add
8300          * Fires when Records have been added to the Store
8301          * @param {Store} this
8302          * @param {Roo.data.Record[]} records The array of Records added
8303          * @param {Number} index The index at which the record(s) were added
8304          */
8305         add : true,
8306         /**
8307          * @event remove
8308          * Fires when a Record has been removed from the Store
8309          * @param {Store} this
8310          * @param {Roo.data.Record} record The Record that was removed
8311          * @param {Number} index The index at which the record was removed
8312          */
8313         remove : true,
8314         /**
8315          * @event update
8316          * Fires when a Record has been updated
8317          * @param {Store} this
8318          * @param {Roo.data.Record} record The Record that was updated
8319          * @param {String} operation The update operation being performed.  Value may be one of:
8320          * <pre><code>
8321  Roo.data.Record.EDIT
8322  Roo.data.Record.REJECT
8323  Roo.data.Record.COMMIT
8324          * </code></pre>
8325          */
8326         update : true,
8327         /**
8328          * @event clear
8329          * Fires when the data cache has been cleared.
8330          * @param {Store} this
8331          */
8332         clear : true,
8333         /**
8334          * @event beforeload
8335          * Fires before a request is made for a new data object.  If the beforeload handler returns false
8336          * the load action will be canceled.
8337          * @param {Store} this
8338          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8339          */
8340         beforeload : true,
8341         /**
8342          * @event beforeloadadd
8343          * Fires after a new set of Records has been loaded.
8344          * @param {Store} this
8345          * @param {Roo.data.Record[]} records The Records that were loaded
8346          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8347          */
8348         beforeloadadd : true,
8349         /**
8350          * @event load
8351          * Fires after a new set of Records has been loaded, before they are added to the store.
8352          * @param {Store} this
8353          * @param {Roo.data.Record[]} records The Records that were loaded
8354          * @param {Object} options The loading options that were specified (see {@link #load} for details)
8355          * @params {Object} return from reader
8356          */
8357         load : true,
8358         /**
8359          * @event loadexception
8360          * Fires if an exception occurs in the Proxy during loading.
8361          * Called with the signature of the Proxy's "loadexception" event.
8362          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8363          * 
8364          * @param {Proxy} 
8365          * @param {Object} return from JsonData.reader() - success, totalRecords, records
8366          * @param {Object} load options 
8367          * @param {Object} jsonData from your request (normally this contains the Exception)
8368          */
8369         loadexception : true
8370     });
8371     
8372     if(this.proxy){
8373         this.proxy = Roo.factory(this.proxy, Roo.data);
8374         this.proxy.xmodule = this.xmodule || false;
8375         this.relayEvents(this.proxy,  ["loadexception"]);
8376     }
8377     this.sortToggle = {};
8378     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8379
8380     Roo.data.Store.superclass.constructor.call(this);
8381
8382     if(this.inlineData){
8383         this.loadData(this.inlineData);
8384         delete this.inlineData;
8385     }
8386 };
8387
8388 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8389      /**
8390     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
8391     * without a remote query - used by combo/forms at present.
8392     */
8393     
8394     /**
8395     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8396     */
8397     /**
8398     * @cfg {Array} data Inline data to be loaded when the store is initialized.
8399     */
8400     /**
8401     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8402     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8403     */
8404     /**
8405     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8406     * on any HTTP request
8407     */
8408     /**
8409     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8410     */
8411     /**
8412     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8413     */
8414     multiSort: false,
8415     /**
8416     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8417     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8418     */
8419     remoteSort : false,
8420
8421     /**
8422     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8423      * loaded or when a record is removed. (defaults to false).
8424     */
8425     pruneModifiedRecords : false,
8426
8427     // private
8428     lastOptions : null,
8429
8430     /**
8431      * Add Records to the Store and fires the add event.
8432      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8433      */
8434     add : function(records){
8435         records = [].concat(records);
8436         for(var i = 0, len = records.length; i < len; i++){
8437             records[i].join(this);
8438         }
8439         var index = this.data.length;
8440         this.data.addAll(records);
8441         this.fireEvent("add", this, records, index);
8442     },
8443
8444     /**
8445      * Remove a Record from the Store and fires the remove event.
8446      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8447      */
8448     remove : function(record){
8449         var index = this.data.indexOf(record);
8450         this.data.removeAt(index);
8451         if(this.pruneModifiedRecords){
8452             this.modified.remove(record);
8453         }
8454         this.fireEvent("remove", this, record, index);
8455     },
8456
8457     /**
8458      * Remove all Records from the Store and fires the clear event.
8459      */
8460     removeAll : function(){
8461         this.data.clear();
8462         if(this.pruneModifiedRecords){
8463             this.modified = [];
8464         }
8465         this.fireEvent("clear", this);
8466     },
8467
8468     /**
8469      * Inserts Records to the Store at the given index and fires the add event.
8470      * @param {Number} index The start index at which to insert the passed Records.
8471      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8472      */
8473     insert : function(index, records){
8474         records = [].concat(records);
8475         for(var i = 0, len = records.length; i < len; i++){
8476             this.data.insert(index, records[i]);
8477             records[i].join(this);
8478         }
8479         this.fireEvent("add", this, records, index);
8480     },
8481
8482     /**
8483      * Get the index within the cache of the passed Record.
8484      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8485      * @return {Number} The index of the passed Record. Returns -1 if not found.
8486      */
8487     indexOf : function(record){
8488         return this.data.indexOf(record);
8489     },
8490
8491     /**
8492      * Get the index within the cache of the Record with the passed id.
8493      * @param {String} id The id of the Record to find.
8494      * @return {Number} The index of the Record. Returns -1 if not found.
8495      */
8496     indexOfId : function(id){
8497         return this.data.indexOfKey(id);
8498     },
8499
8500     /**
8501      * Get the Record with the specified id.
8502      * @param {String} id The id of the Record to find.
8503      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8504      */
8505     getById : function(id){
8506         return this.data.key(id);
8507     },
8508
8509     /**
8510      * Get the Record at the specified index.
8511      * @param {Number} index The index of the Record to find.
8512      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8513      */
8514     getAt : function(index){
8515         return this.data.itemAt(index);
8516     },
8517
8518     /**
8519      * Returns a range of Records between specified indices.
8520      * @param {Number} startIndex (optional) The starting index (defaults to 0)
8521      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8522      * @return {Roo.data.Record[]} An array of Records
8523      */
8524     getRange : function(start, end){
8525         return this.data.getRange(start, end);
8526     },
8527
8528     // private
8529     storeOptions : function(o){
8530         o = Roo.apply({}, o);
8531         delete o.callback;
8532         delete o.scope;
8533         this.lastOptions = o;
8534     },
8535
8536     /**
8537      * Loads the Record cache from the configured Proxy using the configured Reader.
8538      * <p>
8539      * If using remote paging, then the first load call must specify the <em>start</em>
8540      * and <em>limit</em> properties in the options.params property to establish the initial
8541      * position within the dataset, and the number of Records to cache on each read from the Proxy.
8542      * <p>
8543      * <strong>It is important to note that for remote data sources, loading is asynchronous,
8544      * and this call will return before the new data has been loaded. Perform any post-processing
8545      * in a callback function, or in a "load" event handler.</strong>
8546      * <p>
8547      * @param {Object} options An object containing properties which control loading options:<ul>
8548      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8549      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8550      * passed the following arguments:<ul>
8551      * <li>r : Roo.data.Record[]</li>
8552      * <li>options: Options object from the load call</li>
8553      * <li>success: Boolean success indicator</li></ul></li>
8554      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8555      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8556      * </ul>
8557      */
8558     load : function(options){
8559         options = options || {};
8560         if(this.fireEvent("beforeload", this, options) !== false){
8561             this.storeOptions(options);
8562             var p = Roo.apply(options.params || {}, this.baseParams);
8563             // if meta was not loaded from remote source.. try requesting it.
8564             if (!this.reader.metaFromRemote) {
8565                 p._requestMeta = 1;
8566             }
8567             if(this.sortInfo && this.remoteSort){
8568                 var pn = this.paramNames;
8569                 p[pn["sort"]] = this.sortInfo.field;
8570                 p[pn["dir"]] = this.sortInfo.direction;
8571             }
8572             if (this.multiSort) {
8573                 var pn = this.paramNames;
8574                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8575             }
8576             
8577             this.proxy.load(p, this.reader, this.loadRecords, this, options);
8578         }
8579     },
8580
8581     /**
8582      * Reloads the Record cache from the configured Proxy using the configured Reader and
8583      * the options from the last load operation performed.
8584      * @param {Object} options (optional) An object containing properties which may override the options
8585      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8586      * the most recently used options are reused).
8587      */
8588     reload : function(options){
8589         this.load(Roo.applyIf(options||{}, this.lastOptions));
8590     },
8591
8592     // private
8593     // Called as a callback by the Reader during a load operation.
8594     loadRecords : function(o, options, success){
8595         if(!o || success === false){
8596             if(success !== false){
8597                 this.fireEvent("load", this, [], options, o);
8598             }
8599             if(options.callback){
8600                 options.callback.call(options.scope || this, [], options, false);
8601             }
8602             return;
8603         }
8604         // if data returned failure - throw an exception.
8605         if (o.success === false) {
8606             // show a message if no listener is registered.
8607             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8608                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8609             }
8610             // loadmask wil be hooked into this..
8611             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8612             return;
8613         }
8614         var r = o.records, t = o.totalRecords || r.length;
8615         
8616         this.fireEvent("beforeloadadd", this, r, options, o);
8617         
8618         if(!options || options.add !== true){
8619             if(this.pruneModifiedRecords){
8620                 this.modified = [];
8621             }
8622             for(var i = 0, len = r.length; i < len; i++){
8623                 r[i].join(this);
8624             }
8625             if(this.snapshot){
8626                 this.data = this.snapshot;
8627                 delete this.snapshot;
8628             }
8629             this.data.clear();
8630             this.data.addAll(r);
8631             this.totalLength = t;
8632             this.applySort();
8633             this.fireEvent("datachanged", this);
8634         }else{
8635             this.totalLength = Math.max(t, this.data.length+r.length);
8636             this.add(r);
8637         }
8638         this.fireEvent("load", this, r, options, o);
8639         if(options.callback){
8640             options.callback.call(options.scope || this, r, options, true);
8641         }
8642     },
8643
8644
8645     /**
8646      * Loads data from a passed data block. A Reader which understands the format of the data
8647      * must have been configured in the constructor.
8648      * @param {Object} data The data block from which to read the Records.  The format of the data expected
8649      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
8650      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
8651      */
8652     loadData : function(o, append){
8653         var r = this.reader.readRecords(o);
8654         this.loadRecords(r, {add: append}, true);
8655     },
8656
8657     /**
8658      * Gets the number of cached records.
8659      * <p>
8660      * <em>If using paging, this may not be the total size of the dataset. If the data object
8661      * used by the Reader contains the dataset size, then the getTotalCount() function returns
8662      * the data set size</em>
8663      */
8664     getCount : function(){
8665         return this.data.length || 0;
8666     },
8667
8668     /**
8669      * Gets the total number of records in the dataset as returned by the server.
8670      * <p>
8671      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
8672      * the dataset size</em>
8673      */
8674     getTotalCount : function(){
8675         return this.totalLength || 0;
8676     },
8677
8678     /**
8679      * Returns the sort state of the Store as an object with two properties:
8680      * <pre><code>
8681  field {String} The name of the field by which the Records are sorted
8682  direction {String} The sort order, "ASC" or "DESC"
8683      * </code></pre>
8684      */
8685     getSortState : function(){
8686         return this.sortInfo;
8687     },
8688
8689     // private
8690     applySort : function(){
8691         if(this.sortInfo && !this.remoteSort){
8692             var s = this.sortInfo, f = s.field;
8693             var st = this.fields.get(f).sortType;
8694             var fn = function(r1, r2){
8695                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
8696                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
8697             };
8698             this.data.sort(s.direction, fn);
8699             if(this.snapshot && this.snapshot != this.data){
8700                 this.snapshot.sort(s.direction, fn);
8701             }
8702         }
8703     },
8704
8705     /**
8706      * Sets the default sort column and order to be used by the next load operation.
8707      * @param {String} fieldName The name of the field to sort by.
8708      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8709      */
8710     setDefaultSort : function(field, dir){
8711         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
8712     },
8713
8714     /**
8715      * Sort the Records.
8716      * If remote sorting is used, the sort is performed on the server, and the cache is
8717      * reloaded. If local sorting is used, the cache is sorted internally.
8718      * @param {String} fieldName The name of the field to sort by.
8719      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
8720      */
8721     sort : function(fieldName, dir){
8722         var f = this.fields.get(fieldName);
8723         if(!dir){
8724             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
8725             
8726             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
8727                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
8728             }else{
8729                 dir = f.sortDir;
8730             }
8731         }
8732         this.sortToggle[f.name] = dir;
8733         this.sortInfo = {field: f.name, direction: dir};
8734         if(!this.remoteSort){
8735             this.applySort();
8736             this.fireEvent("datachanged", this);
8737         }else{
8738             this.load(this.lastOptions);
8739         }
8740     },
8741
8742     /**
8743      * Calls the specified function for each of the Records in the cache.
8744      * @param {Function} fn The function to call. The Record is passed as the first parameter.
8745      * Returning <em>false</em> aborts and exits the iteration.
8746      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
8747      */
8748     each : function(fn, scope){
8749         this.data.each(fn, scope);
8750     },
8751
8752     /**
8753      * Gets all records modified since the last commit.  Modified records are persisted across load operations
8754      * (e.g., during paging).
8755      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
8756      */
8757     getModifiedRecords : function(){
8758         return this.modified;
8759     },
8760
8761     // private
8762     createFilterFn : function(property, value, anyMatch){
8763         if(!value.exec){ // not a regex
8764             value = String(value);
8765             if(value.length == 0){
8766                 return false;
8767             }
8768             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
8769         }
8770         return function(r){
8771             return value.test(r.data[property]);
8772         };
8773     },
8774
8775     /**
8776      * Sums the value of <i>property</i> for each record between start and end and returns the result.
8777      * @param {String} property A field on your records
8778      * @param {Number} start The record index to start at (defaults to 0)
8779      * @param {Number} end The last record index to include (defaults to length - 1)
8780      * @return {Number} The sum
8781      */
8782     sum : function(property, start, end){
8783         var rs = this.data.items, v = 0;
8784         start = start || 0;
8785         end = (end || end === 0) ? end : rs.length-1;
8786
8787         for(var i = start; i <= end; i++){
8788             v += (rs[i].data[property] || 0);
8789         }
8790         return v;
8791     },
8792
8793     /**
8794      * Filter the records by a specified property.
8795      * @param {String} field A field on your records
8796      * @param {String/RegExp} value Either a string that the field
8797      * should start with or a RegExp to test against the field
8798      * @param {Boolean} anyMatch True to match any part not just the beginning
8799      */
8800     filter : function(property, value, anyMatch){
8801         var fn = this.createFilterFn(property, value, anyMatch);
8802         return fn ? this.filterBy(fn) : this.clearFilter();
8803     },
8804
8805     /**
8806      * Filter by a function. The specified function will be called with each
8807      * record in this data source. If the function returns true the record is included,
8808      * otherwise it is filtered.
8809      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8810      * @param {Object} scope (optional) The scope of the function (defaults to this)
8811      */
8812     filterBy : function(fn, scope){
8813         this.snapshot = this.snapshot || this.data;
8814         this.data = this.queryBy(fn, scope||this);
8815         this.fireEvent("datachanged", this);
8816     },
8817
8818     /**
8819      * Query the records by a specified property.
8820      * @param {String} field A field on your records
8821      * @param {String/RegExp} value Either a string that the field
8822      * should start with or a RegExp to test against the field
8823      * @param {Boolean} anyMatch True to match any part not just the beginning
8824      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8825      */
8826     query : function(property, value, anyMatch){
8827         var fn = this.createFilterFn(property, value, anyMatch);
8828         return fn ? this.queryBy(fn) : this.data.clone();
8829     },
8830
8831     /**
8832      * Query by a function. The specified function will be called with each
8833      * record in this data source. If the function returns true the record is included
8834      * in the results.
8835      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
8836      * @param {Object} scope (optional) The scope of the function (defaults to this)
8837       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
8838      **/
8839     queryBy : function(fn, scope){
8840         var data = this.snapshot || this.data;
8841         return data.filterBy(fn, scope||this);
8842     },
8843
8844     /**
8845      * Collects unique values for a particular dataIndex from this store.
8846      * @param {String} dataIndex The property to collect
8847      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
8848      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
8849      * @return {Array} An array of the unique values
8850      **/
8851     collect : function(dataIndex, allowNull, bypassFilter){
8852         var d = (bypassFilter === true && this.snapshot) ?
8853                 this.snapshot.items : this.data.items;
8854         var v, sv, r = [], l = {};
8855         for(var i = 0, len = d.length; i < len; i++){
8856             v = d[i].data[dataIndex];
8857             sv = String(v);
8858             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
8859                 l[sv] = true;
8860                 r[r.length] = v;
8861             }
8862         }
8863         return r;
8864     },
8865
8866     /**
8867      * Revert to a view of the Record cache with no filtering applied.
8868      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
8869      */
8870     clearFilter : function(suppressEvent){
8871         if(this.snapshot && this.snapshot != this.data){
8872             this.data = this.snapshot;
8873             delete this.snapshot;
8874             if(suppressEvent !== true){
8875                 this.fireEvent("datachanged", this);
8876             }
8877         }
8878     },
8879
8880     // private
8881     afterEdit : function(record){
8882         if(this.modified.indexOf(record) == -1){
8883             this.modified.push(record);
8884         }
8885         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8886     },
8887     
8888     // private
8889     afterReject : function(record){
8890         this.modified.remove(record);
8891         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8892     },
8893
8894     // private
8895     afterCommit : function(record){
8896         this.modified.remove(record);
8897         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8898     },
8899
8900     /**
8901      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8902      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8903      */
8904     commitChanges : function(){
8905         var m = this.modified.slice(0);
8906         this.modified = [];
8907         for(var i = 0, len = m.length; i < len; i++){
8908             m[i].commit();
8909         }
8910     },
8911
8912     /**
8913      * Cancel outstanding changes on all changed records.
8914      */
8915     rejectChanges : function(){
8916         var m = this.modified.slice(0);
8917         this.modified = [];
8918         for(var i = 0, len = m.length; i < len; i++){
8919             m[i].reject();
8920         }
8921     },
8922
8923     onMetaChange : function(meta, rtype, o){
8924         this.recordType = rtype;
8925         this.fields = rtype.prototype.fields;
8926         delete this.snapshot;
8927         this.sortInfo = meta.sortInfo || this.sortInfo;
8928         this.modified = [];
8929         this.fireEvent('metachange', this, this.reader.meta);
8930     },
8931     
8932     moveIndex : function(data, type)
8933     {
8934         var index = this.indexOf(data);
8935         
8936         var newIndex = index + type;
8937         
8938         this.remove(data);
8939         
8940         this.insert(newIndex, data);
8941         
8942     }
8943 });/*
8944  * Based on:
8945  * Ext JS Library 1.1.1
8946  * Copyright(c) 2006-2007, Ext JS, LLC.
8947  *
8948  * Originally Released Under LGPL - original licence link has changed is not relivant.
8949  *
8950  * Fork - LGPL
8951  * <script type="text/javascript">
8952  */
8953
8954 /**
8955  * @class Roo.data.SimpleStore
8956  * @extends Roo.data.Store
8957  * Small helper class to make creating Stores from Array data easier.
8958  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
8959  * @cfg {Array} fields An array of field definition objects, or field name strings.
8960  * @cfg {Array} data The multi-dimensional array of data
8961  * @constructor
8962  * @param {Object} config
8963  */
8964 Roo.data.SimpleStore = function(config){
8965     Roo.data.SimpleStore.superclass.constructor.call(this, {
8966         isLocal : true,
8967         reader: new Roo.data.ArrayReader({
8968                 id: config.id
8969             },
8970             Roo.data.Record.create(config.fields)
8971         ),
8972         proxy : new Roo.data.MemoryProxy(config.data)
8973     });
8974     this.load();
8975 };
8976 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
8977  * Based on:
8978  * Ext JS Library 1.1.1
8979  * Copyright(c) 2006-2007, Ext JS, LLC.
8980  *
8981  * Originally Released Under LGPL - original licence link has changed is not relivant.
8982  *
8983  * Fork - LGPL
8984  * <script type="text/javascript">
8985  */
8986
8987 /**
8988 /**
8989  * @extends Roo.data.Store
8990  * @class Roo.data.JsonStore
8991  * Small helper class to make creating Stores for JSON data easier. <br/>
8992 <pre><code>
8993 var store = new Roo.data.JsonStore({
8994     url: 'get-images.php',
8995     root: 'images',
8996     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8997 });
8998 </code></pre>
8999  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9000  * JsonReader and HttpProxy (unless inline data is provided).</b>
9001  * @cfg {Array} fields An array of field definition objects, or field name strings.
9002  * @constructor
9003  * @param {Object} config
9004  */
9005 Roo.data.JsonStore = function(c){
9006     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9007         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9008         reader: new Roo.data.JsonReader(c, c.fields)
9009     }));
9010 };
9011 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9012  * Based on:
9013  * Ext JS Library 1.1.1
9014  * Copyright(c) 2006-2007, Ext JS, LLC.
9015  *
9016  * Originally Released Under LGPL - original licence link has changed is not relivant.
9017  *
9018  * Fork - LGPL
9019  * <script type="text/javascript">
9020  */
9021
9022  
9023 Roo.data.Field = function(config){
9024     if(typeof config == "string"){
9025         config = {name: config};
9026     }
9027     Roo.apply(this, config);
9028     
9029     if(!this.type){
9030         this.type = "auto";
9031     }
9032     
9033     var st = Roo.data.SortTypes;
9034     // named sortTypes are supported, here we look them up
9035     if(typeof this.sortType == "string"){
9036         this.sortType = st[this.sortType];
9037     }
9038     
9039     // set default sortType for strings and dates
9040     if(!this.sortType){
9041         switch(this.type){
9042             case "string":
9043                 this.sortType = st.asUCString;
9044                 break;
9045             case "date":
9046                 this.sortType = st.asDate;
9047                 break;
9048             default:
9049                 this.sortType = st.none;
9050         }
9051     }
9052
9053     // define once
9054     var stripRe = /[\$,%]/g;
9055
9056     // prebuilt conversion function for this field, instead of
9057     // switching every time we're reading a value
9058     if(!this.convert){
9059         var cv, dateFormat = this.dateFormat;
9060         switch(this.type){
9061             case "":
9062             case "auto":
9063             case undefined:
9064                 cv = function(v){ return v; };
9065                 break;
9066             case "string":
9067                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9068                 break;
9069             case "int":
9070                 cv = function(v){
9071                     return v !== undefined && v !== null && v !== '' ?
9072                            parseInt(String(v).replace(stripRe, ""), 10) : '';
9073                     };
9074                 break;
9075             case "float":
9076                 cv = function(v){
9077                     return v !== undefined && v !== null && v !== '' ?
9078                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
9079                     };
9080                 break;
9081             case "bool":
9082             case "boolean":
9083                 cv = function(v){ return v === true || v === "true" || v == 1; };
9084                 break;
9085             case "date":
9086                 cv = function(v){
9087                     if(!v){
9088                         return '';
9089                     }
9090                     if(v instanceof Date){
9091                         return v;
9092                     }
9093                     if(dateFormat){
9094                         if(dateFormat == "timestamp"){
9095                             return new Date(v*1000);
9096                         }
9097                         return Date.parseDate(v, dateFormat);
9098                     }
9099                     var parsed = Date.parse(v);
9100                     return parsed ? new Date(parsed) : null;
9101                 };
9102              break;
9103             
9104         }
9105         this.convert = cv;
9106     }
9107 };
9108
9109 Roo.data.Field.prototype = {
9110     dateFormat: null,
9111     defaultValue: "",
9112     mapping: null,
9113     sortType : null,
9114     sortDir : "ASC"
9115 };/*
9116  * Based on:
9117  * Ext JS Library 1.1.1
9118  * Copyright(c) 2006-2007, Ext JS, LLC.
9119  *
9120  * Originally Released Under LGPL - original licence link has changed is not relivant.
9121  *
9122  * Fork - LGPL
9123  * <script type="text/javascript">
9124  */
9125  
9126 // Base class for reading structured data from a data source.  This class is intended to be
9127 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9128
9129 /**
9130  * @class Roo.data.DataReader
9131  * Base class for reading structured data from a data source.  This class is intended to be
9132  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9133  */
9134
9135 Roo.data.DataReader = function(meta, recordType){
9136     
9137     this.meta = meta;
9138     
9139     this.recordType = recordType instanceof Array ? 
9140         Roo.data.Record.create(recordType) : recordType;
9141 };
9142
9143 Roo.data.DataReader.prototype = {
9144      /**
9145      * Create an empty record
9146      * @param {Object} data (optional) - overlay some values
9147      * @return {Roo.data.Record} record created.
9148      */
9149     newRow :  function(d) {
9150         var da =  {};
9151         this.recordType.prototype.fields.each(function(c) {
9152             switch( c.type) {
9153                 case 'int' : da[c.name] = 0; break;
9154                 case 'date' : da[c.name] = new Date(); break;
9155                 case 'float' : da[c.name] = 0.0; break;
9156                 case 'boolean' : da[c.name] = false; break;
9157                 default : da[c.name] = ""; break;
9158             }
9159             
9160         });
9161         return new this.recordType(Roo.apply(da, d));
9162     }
9163     
9164 };/*
9165  * Based on:
9166  * Ext JS Library 1.1.1
9167  * Copyright(c) 2006-2007, Ext JS, LLC.
9168  *
9169  * Originally Released Under LGPL - original licence link has changed is not relivant.
9170  *
9171  * Fork - LGPL
9172  * <script type="text/javascript">
9173  */
9174
9175 /**
9176  * @class Roo.data.DataProxy
9177  * @extends Roo.data.Observable
9178  * This class is an abstract base class for implementations which provide retrieval of
9179  * unformatted data objects.<br>
9180  * <p>
9181  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9182  * (of the appropriate type which knows how to parse the data object) to provide a block of
9183  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9184  * <p>
9185  * Custom implementations must implement the load method as described in
9186  * {@link Roo.data.HttpProxy#load}.
9187  */
9188 Roo.data.DataProxy = function(){
9189     this.addEvents({
9190         /**
9191          * @event beforeload
9192          * Fires before a network request is made to retrieve a data object.
9193          * @param {Object} This DataProxy object.
9194          * @param {Object} params The params parameter to the load function.
9195          */
9196         beforeload : true,
9197         /**
9198          * @event load
9199          * Fires before the load method's callback is called.
9200          * @param {Object} This DataProxy object.
9201          * @param {Object} o The data object.
9202          * @param {Object} arg The callback argument object passed to the load function.
9203          */
9204         load : true,
9205         /**
9206          * @event loadexception
9207          * Fires if an Exception occurs during data retrieval.
9208          * @param {Object} This DataProxy object.
9209          * @param {Object} o The data object.
9210          * @param {Object} arg The callback argument object passed to the load function.
9211          * @param {Object} e The Exception.
9212          */
9213         loadexception : true
9214     });
9215     Roo.data.DataProxy.superclass.constructor.call(this);
9216 };
9217
9218 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9219
9220     /**
9221      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9222      */
9223 /*
9224  * Based on:
9225  * Ext JS Library 1.1.1
9226  * Copyright(c) 2006-2007, Ext JS, LLC.
9227  *
9228  * Originally Released Under LGPL - original licence link has changed is not relivant.
9229  *
9230  * Fork - LGPL
9231  * <script type="text/javascript">
9232  */
9233 /**
9234  * @class Roo.data.MemoryProxy
9235  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9236  * to the Reader when its load method is called.
9237  * @constructor
9238  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9239  */
9240 Roo.data.MemoryProxy = function(data){
9241     if (data.data) {
9242         data = data.data;
9243     }
9244     Roo.data.MemoryProxy.superclass.constructor.call(this);
9245     this.data = data;
9246 };
9247
9248 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9249     /**
9250      * Load data from the requested source (in this case an in-memory
9251      * data object passed to the constructor), read the data object into
9252      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9253      * process that block using the passed callback.
9254      * @param {Object} params This parameter is not used by the MemoryProxy class.
9255      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9256      * object into a block of Roo.data.Records.
9257      * @param {Function} callback The function into which to pass the block of Roo.data.records.
9258      * The function must be passed <ul>
9259      * <li>The Record block object</li>
9260      * <li>The "arg" argument from the load function</li>
9261      * <li>A boolean success indicator</li>
9262      * </ul>
9263      * @param {Object} scope The scope in which to call the callback
9264      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9265      */
9266     load : function(params, reader, callback, scope, arg){
9267         params = params || {};
9268         var result;
9269         try {
9270             result = reader.readRecords(this.data);
9271         }catch(e){
9272             this.fireEvent("loadexception", this, arg, null, e);
9273             callback.call(scope, null, arg, false);
9274             return;
9275         }
9276         callback.call(scope, result, arg, true);
9277     },
9278     
9279     // private
9280     update : function(params, records){
9281         
9282     }
9283 });/*
9284  * Based on:
9285  * Ext JS Library 1.1.1
9286  * Copyright(c) 2006-2007, Ext JS, LLC.
9287  *
9288  * Originally Released Under LGPL - original licence link has changed is not relivant.
9289  *
9290  * Fork - LGPL
9291  * <script type="text/javascript">
9292  */
9293 /**
9294  * @class Roo.data.HttpProxy
9295  * @extends Roo.data.DataProxy
9296  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9297  * configured to reference a certain URL.<br><br>
9298  * <p>
9299  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9300  * from which the running page was served.<br><br>
9301  * <p>
9302  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9303  * <p>
9304  * Be aware that to enable the browser to parse an XML document, the server must set
9305  * the Content-Type header in the HTTP response to "text/xml".
9306  * @constructor
9307  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9308  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
9309  * will be used to make the request.
9310  */
9311 Roo.data.HttpProxy = function(conn){
9312     Roo.data.HttpProxy.superclass.constructor.call(this);
9313     // is conn a conn config or a real conn?
9314     this.conn = conn;
9315     this.useAjax = !conn || !conn.events;
9316   
9317 };
9318
9319 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9320     // thse are take from connection...
9321     
9322     /**
9323      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9324      */
9325     /**
9326      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9327      * extra parameters to each request made by this object. (defaults to undefined)
9328      */
9329     /**
9330      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9331      *  to each request made by this object. (defaults to undefined)
9332      */
9333     /**
9334      * @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)
9335      */
9336     /**
9337      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9338      */
9339      /**
9340      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9341      * @type Boolean
9342      */
9343   
9344
9345     /**
9346      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9347      * @type Boolean
9348      */
9349     /**
9350      * Return the {@link Roo.data.Connection} object being used by this Proxy.
9351      * @return {Connection} The Connection object. This object may be used to subscribe to events on
9352      * a finer-grained basis than the DataProxy events.
9353      */
9354     getConnection : function(){
9355         return this.useAjax ? Roo.Ajax : this.conn;
9356     },
9357
9358     /**
9359      * Load data from the configured {@link Roo.data.Connection}, read the data object into
9360      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9361      * process that block using the passed callback.
9362      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9363      * for the request to the remote server.
9364      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9365      * object into a block of Roo.data.Records.
9366      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9367      * The function must be passed <ul>
9368      * <li>The Record block object</li>
9369      * <li>The "arg" argument from the load function</li>
9370      * <li>A boolean success indicator</li>
9371      * </ul>
9372      * @param {Object} scope The scope in which to call the callback
9373      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9374      */
9375     load : function(params, reader, callback, scope, arg){
9376         if(this.fireEvent("beforeload", this, params) !== false){
9377             var  o = {
9378                 params : params || {},
9379                 request: {
9380                     callback : callback,
9381                     scope : scope,
9382                     arg : arg
9383                 },
9384                 reader: reader,
9385                 callback : this.loadResponse,
9386                 scope: this
9387             };
9388             if(this.useAjax){
9389                 Roo.applyIf(o, this.conn);
9390                 if(this.activeRequest){
9391                     Roo.Ajax.abort(this.activeRequest);
9392                 }
9393                 this.activeRequest = Roo.Ajax.request(o);
9394             }else{
9395                 this.conn.request(o);
9396             }
9397         }else{
9398             callback.call(scope||this, null, arg, false);
9399         }
9400     },
9401
9402     // private
9403     loadResponse : function(o, success, response){
9404         delete this.activeRequest;
9405         if(!success){
9406             this.fireEvent("loadexception", this, o, response);
9407             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9408             return;
9409         }
9410         var result;
9411         try {
9412             result = o.reader.read(response);
9413         }catch(e){
9414             this.fireEvent("loadexception", this, o, response, e);
9415             o.request.callback.call(o.request.scope, null, o.request.arg, false);
9416             return;
9417         }
9418         
9419         this.fireEvent("load", this, o, o.request.arg);
9420         o.request.callback.call(o.request.scope, result, o.request.arg, true);
9421     },
9422
9423     // private
9424     update : function(dataSet){
9425
9426     },
9427
9428     // private
9429     updateResponse : function(dataSet){
9430
9431     }
9432 });/*
9433  * Based on:
9434  * Ext JS Library 1.1.1
9435  * Copyright(c) 2006-2007, Ext JS, LLC.
9436  *
9437  * Originally Released Under LGPL - original licence link has changed is not relivant.
9438  *
9439  * Fork - LGPL
9440  * <script type="text/javascript">
9441  */
9442
9443 /**
9444  * @class Roo.data.ScriptTagProxy
9445  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9446  * other than the originating domain of the running page.<br><br>
9447  * <p>
9448  * <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
9449  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9450  * <p>
9451  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9452  * source code that is used as the source inside a &lt;script> tag.<br><br>
9453  * <p>
9454  * In order for the browser to process the returned data, the server must wrap the data object
9455  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9456  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9457  * depending on whether the callback name was passed:
9458  * <p>
9459  * <pre><code>
9460 boolean scriptTag = false;
9461 String cb = request.getParameter("callback");
9462 if (cb != null) {
9463     scriptTag = true;
9464     response.setContentType("text/javascript");
9465 } else {
9466     response.setContentType("application/x-json");
9467 }
9468 Writer out = response.getWriter();
9469 if (scriptTag) {
9470     out.write(cb + "(");
9471 }
9472 out.print(dataBlock.toJsonString());
9473 if (scriptTag) {
9474     out.write(");");
9475 }
9476 </pre></code>
9477  *
9478  * @constructor
9479  * @param {Object} config A configuration object.
9480  */
9481 Roo.data.ScriptTagProxy = function(config){
9482     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9483     Roo.apply(this, config);
9484     this.head = document.getElementsByTagName("head")[0];
9485 };
9486
9487 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9488
9489 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9490     /**
9491      * @cfg {String} url The URL from which to request the data object.
9492      */
9493     /**
9494      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9495      */
9496     timeout : 30000,
9497     /**
9498      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9499      * the server the name of the callback function set up by the load call to process the returned data object.
9500      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9501      * javascript output which calls this named function passing the data object as its only parameter.
9502      */
9503     callbackParam : "callback",
9504     /**
9505      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9506      * name to the request.
9507      */
9508     nocache : true,
9509
9510     /**
9511      * Load data from the configured URL, read the data object into
9512      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9513      * process that block using the passed callback.
9514      * @param {Object} params An object containing properties which are to be used as HTTP parameters
9515      * for the request to the remote server.
9516      * @param {Roo.data.DataReader} reader The Reader object which converts the data
9517      * object into a block of Roo.data.Records.
9518      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9519      * The function must be passed <ul>
9520      * <li>The Record block object</li>
9521      * <li>The "arg" argument from the load function</li>
9522      * <li>A boolean success indicator</li>
9523      * </ul>
9524      * @param {Object} scope The scope in which to call the callback
9525      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9526      */
9527     load : function(params, reader, callback, scope, arg){
9528         if(this.fireEvent("beforeload", this, params) !== false){
9529
9530             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9531
9532             var url = this.url;
9533             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9534             if(this.nocache){
9535                 url += "&_dc=" + (new Date().getTime());
9536             }
9537             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9538             var trans = {
9539                 id : transId,
9540                 cb : "stcCallback"+transId,
9541                 scriptId : "stcScript"+transId,
9542                 params : params,
9543                 arg : arg,
9544                 url : url,
9545                 callback : callback,
9546                 scope : scope,
9547                 reader : reader
9548             };
9549             var conn = this;
9550
9551             window[trans.cb] = function(o){
9552                 conn.handleResponse(o, trans);
9553             };
9554
9555             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9556
9557             if(this.autoAbort !== false){
9558                 this.abort();
9559             }
9560
9561             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9562
9563             var script = document.createElement("script");
9564             script.setAttribute("src", url);
9565             script.setAttribute("type", "text/javascript");
9566             script.setAttribute("id", trans.scriptId);
9567             this.head.appendChild(script);
9568
9569             this.trans = trans;
9570         }else{
9571             callback.call(scope||this, null, arg, false);
9572         }
9573     },
9574
9575     // private
9576     isLoading : function(){
9577         return this.trans ? true : false;
9578     },
9579
9580     /**
9581      * Abort the current server request.
9582      */
9583     abort : function(){
9584         if(this.isLoading()){
9585             this.destroyTrans(this.trans);
9586         }
9587     },
9588
9589     // private
9590     destroyTrans : function(trans, isLoaded){
9591         this.head.removeChild(document.getElementById(trans.scriptId));
9592         clearTimeout(trans.timeoutId);
9593         if(isLoaded){
9594             window[trans.cb] = undefined;
9595             try{
9596                 delete window[trans.cb];
9597             }catch(e){}
9598         }else{
9599             // if hasn't been loaded, wait for load to remove it to prevent script error
9600             window[trans.cb] = function(){
9601                 window[trans.cb] = undefined;
9602                 try{
9603                     delete window[trans.cb];
9604                 }catch(e){}
9605             };
9606         }
9607     },
9608
9609     // private
9610     handleResponse : function(o, trans){
9611         this.trans = false;
9612         this.destroyTrans(trans, true);
9613         var result;
9614         try {
9615             result = trans.reader.readRecords(o);
9616         }catch(e){
9617             this.fireEvent("loadexception", this, o, trans.arg, e);
9618             trans.callback.call(trans.scope||window, null, trans.arg, false);
9619             return;
9620         }
9621         this.fireEvent("load", this, o, trans.arg);
9622         trans.callback.call(trans.scope||window, result, trans.arg, true);
9623     },
9624
9625     // private
9626     handleFailure : function(trans){
9627         this.trans = false;
9628         this.destroyTrans(trans, false);
9629         this.fireEvent("loadexception", this, null, trans.arg);
9630         trans.callback.call(trans.scope||window, null, trans.arg, false);
9631     }
9632 });/*
9633  * Based on:
9634  * Ext JS Library 1.1.1
9635  * Copyright(c) 2006-2007, Ext JS, LLC.
9636  *
9637  * Originally Released Under LGPL - original licence link has changed is not relivant.
9638  *
9639  * Fork - LGPL
9640  * <script type="text/javascript">
9641  */
9642
9643 /**
9644  * @class Roo.data.JsonReader
9645  * @extends Roo.data.DataReader
9646  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
9647  * based on mappings in a provided Roo.data.Record constructor.
9648  * 
9649  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
9650  * in the reply previously. 
9651  * 
9652  * <p>
9653  * Example code:
9654  * <pre><code>
9655 var RecordDef = Roo.data.Record.create([
9656     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
9657     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
9658 ]);
9659 var myReader = new Roo.data.JsonReader({
9660     totalProperty: "results",    // The property which contains the total dataset size (optional)
9661     root: "rows",                // The property which contains an Array of row objects
9662     id: "id"                     // The property within each row object that provides an ID for the record (optional)
9663 }, RecordDef);
9664 </code></pre>
9665  * <p>
9666  * This would consume a JSON file like this:
9667  * <pre><code>
9668 { 'results': 2, 'rows': [
9669     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
9670     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
9671 }
9672 </code></pre>
9673  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
9674  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
9675  * paged from the remote server.
9676  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
9677  * @cfg {String} root name of the property which contains the Array of row objects.
9678  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
9679  * @constructor
9680  * Create a new JsonReader
9681  * @param {Object} meta Metadata configuration options
9682  * @param {Object} recordType Either an Array of field definition objects,
9683  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
9684  */
9685 Roo.data.JsonReader = function(meta, recordType){
9686     
9687     meta = meta || {};
9688     // set some defaults:
9689     Roo.applyIf(meta, {
9690         totalProperty: 'total',
9691         successProperty : 'success',
9692         root : 'data',
9693         id : 'id'
9694     });
9695     
9696     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
9697 };
9698 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
9699     
9700     /**
9701      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
9702      * Used by Store query builder to append _requestMeta to params.
9703      * 
9704      */
9705     metaFromRemote : false,
9706     /**
9707      * This method is only used by a DataProxy which has retrieved data from a remote server.
9708      * @param {Object} response The XHR object which contains the JSON data in its responseText.
9709      * @return {Object} data A data block which is used by an Roo.data.Store object as
9710      * a cache of Roo.data.Records.
9711      */
9712     read : function(response){
9713         var json = response.responseText;
9714        
9715         var o = /* eval:var:o */ eval("("+json+")");
9716         if(!o) {
9717             throw {message: "JsonReader.read: Json object not found"};
9718         }
9719         
9720         if(o.metaData){
9721             
9722             delete this.ef;
9723             this.metaFromRemote = true;
9724             this.meta = o.metaData;
9725             this.recordType = Roo.data.Record.create(o.metaData.fields);
9726             this.onMetaChange(this.meta, this.recordType, o);
9727         }
9728         return this.readRecords(o);
9729     },
9730
9731     // private function a store will implement
9732     onMetaChange : function(meta, recordType, o){
9733
9734     },
9735
9736     /**
9737          * @ignore
9738          */
9739     simpleAccess: function(obj, subsc) {
9740         return obj[subsc];
9741     },
9742
9743         /**
9744          * @ignore
9745          */
9746     getJsonAccessor: function(){
9747         var re = /[\[\.]/;
9748         return function(expr) {
9749             try {
9750                 return(re.test(expr))
9751                     ? new Function("obj", "return obj." + expr)
9752                     : function(obj){
9753                         return obj[expr];
9754                     };
9755             } catch(e){}
9756             return Roo.emptyFn;
9757         };
9758     }(),
9759
9760     /**
9761      * Create a data block containing Roo.data.Records from an XML document.
9762      * @param {Object} o An object which contains an Array of row objects in the property specified
9763      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
9764      * which contains the total size of the dataset.
9765      * @return {Object} data A data block which is used by an Roo.data.Store object as
9766      * a cache of Roo.data.Records.
9767      */
9768     readRecords : function(o){
9769         /**
9770          * After any data loads, the raw JSON data is available for further custom processing.
9771          * @type Object
9772          */
9773         this.o = o;
9774         var s = this.meta, Record = this.recordType,
9775             f = Record.prototype.fields, fi = f.items, fl = f.length;
9776
9777 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
9778         if (!this.ef) {
9779             if(s.totalProperty) {
9780                     this.getTotal = this.getJsonAccessor(s.totalProperty);
9781                 }
9782                 if(s.successProperty) {
9783                     this.getSuccess = this.getJsonAccessor(s.successProperty);
9784                 }
9785                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
9786                 if (s.id) {
9787                         var g = this.getJsonAccessor(s.id);
9788                         this.getId = function(rec) {
9789                                 var r = g(rec);
9790                                 return (r === undefined || r === "") ? null : r;
9791                         };
9792                 } else {
9793                         this.getId = function(){return null;};
9794                 }
9795             this.ef = [];
9796             for(var jj = 0; jj < fl; jj++){
9797                 f = fi[jj];
9798                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
9799                 this.ef[jj] = this.getJsonAccessor(map);
9800             }
9801         }
9802
9803         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
9804         if(s.totalProperty){
9805             var vt = parseInt(this.getTotal(o), 10);
9806             if(!isNaN(vt)){
9807                 totalRecords = vt;
9808             }
9809         }
9810         if(s.successProperty){
9811             var vs = this.getSuccess(o);
9812             if(vs === false || vs === 'false'){
9813                 success = false;
9814             }
9815         }
9816         var records = [];
9817             for(var i = 0; i < c; i++){
9818                     var n = root[i];
9819                 var values = {};
9820                 var id = this.getId(n);
9821                 for(var j = 0; j < fl; j++){
9822                     f = fi[j];
9823                 var v = this.ef[j](n);
9824                 if (!f.convert) {
9825                     Roo.log('missing convert for ' + f.name);
9826                     Roo.log(f);
9827                     continue;
9828                 }
9829                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
9830                 }
9831                 var record = new Record(values, id);
9832                 record.json = n;
9833                 records[i] = record;
9834             }
9835             return {
9836             raw : o,
9837                 success : success,
9838                 records : records,
9839                 totalRecords : totalRecords
9840             };
9841     }
9842 });/*
9843  * Based on:
9844  * Ext JS Library 1.1.1
9845  * Copyright(c) 2006-2007, Ext JS, LLC.
9846  *
9847  * Originally Released Under LGPL - original licence link has changed is not relivant.
9848  *
9849  * Fork - LGPL
9850  * <script type="text/javascript">
9851  */
9852
9853 /**
9854  * @class Roo.data.ArrayReader
9855  * @extends Roo.data.DataReader
9856  * Data reader class to create an Array of Roo.data.Record objects from an Array.
9857  * Each element of that Array represents a row of data fields. The
9858  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
9859  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
9860  * <p>
9861  * Example code:.
9862  * <pre><code>
9863 var RecordDef = Roo.data.Record.create([
9864     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
9865     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
9866 ]);
9867 var myReader = new Roo.data.ArrayReader({
9868     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
9869 }, RecordDef);
9870 </code></pre>
9871  * <p>
9872  * This would consume an Array like this:
9873  * <pre><code>
9874 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9875   </code></pre>
9876  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9877  * @constructor
9878  * Create a new JsonReader
9879  * @param {Object} meta Metadata configuration options.
9880  * @param {Object} recordType Either an Array of field definition objects
9881  * as specified to {@link Roo.data.Record#create},
9882  * or an {@link Roo.data.Record} object
9883  * created using {@link Roo.data.Record#create}.
9884  */
9885 Roo.data.ArrayReader = function(meta, recordType){
9886     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9887 };
9888
9889 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9890     /**
9891      * Create a data block containing Roo.data.Records from an XML document.
9892      * @param {Object} o An Array of row objects which represents the dataset.
9893      * @return {Object} data A data block which is used by an Roo.data.Store object as
9894      * a cache of Roo.data.Records.
9895      */
9896     readRecords : function(o){
9897         var sid = this.meta ? this.meta.id : null;
9898         var recordType = this.recordType, fields = recordType.prototype.fields;
9899         var records = [];
9900         var root = o;
9901             for(var i = 0; i < root.length; i++){
9902                     var n = root[i];
9903                 var values = {};
9904                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
9905                 for(var j = 0, jlen = fields.length; j < jlen; j++){
9906                 var f = fields.items[j];
9907                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
9908                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
9909                 v = f.convert(v);
9910                 values[f.name] = v;
9911             }
9912                 var record = new recordType(values, id);
9913                 record.json = n;
9914                 records[records.length] = record;
9915             }
9916             return {
9917                 records : records,
9918                 totalRecords : records.length
9919             };
9920     }
9921 });/*
9922  * - LGPL
9923  * * 
9924  */
9925
9926 /**
9927  * @class Roo.bootstrap.ComboBox
9928  * @extends Roo.bootstrap.TriggerField
9929  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9930  * @cfg {Boolean} append (true|false) default false
9931  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
9932  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
9933  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
9934  * @constructor
9935  * Create a new ComboBox.
9936  * @param {Object} config Configuration options
9937  */
9938 Roo.bootstrap.ComboBox = function(config){
9939     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9940     this.addEvents({
9941         /**
9942          * @event expand
9943          * Fires when the dropdown list is expanded
9944              * @param {Roo.bootstrap.ComboBox} combo This combo box
9945              */
9946         'expand' : true,
9947         /**
9948          * @event collapse
9949          * Fires when the dropdown list is collapsed
9950              * @param {Roo.bootstrap.ComboBox} combo This combo box
9951              */
9952         'collapse' : true,
9953         /**
9954          * @event beforeselect
9955          * Fires before a list item is selected. Return false to cancel the selection.
9956              * @param {Roo.bootstrap.ComboBox} combo This combo box
9957              * @param {Roo.data.Record} record The data record returned from the underlying store
9958              * @param {Number} index The index of the selected item in the dropdown list
9959              */
9960         'beforeselect' : true,
9961         /**
9962          * @event select
9963          * Fires when a list item is selected
9964              * @param {Roo.bootstrap.ComboBox} combo This combo box
9965              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
9966              * @param {Number} index The index of the selected item in the dropdown list
9967              */
9968         'select' : true,
9969         /**
9970          * @event beforequery
9971          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
9972          * The event object passed has these properties:
9973              * @param {Roo.bootstrap.ComboBox} combo This combo box
9974              * @param {String} query The query
9975              * @param {Boolean} forceAll true to force "all" query
9976              * @param {Boolean} cancel true to cancel the query
9977              * @param {Object} e The query event object
9978              */
9979         'beforequery': true,
9980          /**
9981          * @event add
9982          * Fires when the 'add' icon is pressed (add a listener to enable add button)
9983              * @param {Roo.bootstrap.ComboBox} combo This combo box
9984              */
9985         'add' : true,
9986         /**
9987          * @event edit
9988          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9989              * @param {Roo.bootstrap.ComboBox} combo This combo box
9990              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9991              */
9992         'edit' : true,
9993         /**
9994          * @event remove
9995          * Fires when the remove value from the combobox array
9996              * @param {Roo.bootstrap.ComboBox} combo This combo box
9997              */
9998         'remove' : true
9999         
10000     });
10001     
10002     this.item = [];
10003     this.tickItems = [];
10004     
10005     this.selectedIndex = -1;
10006     if(this.mode == 'local'){
10007         if(config.queryDelay === undefined){
10008             this.queryDelay = 10;
10009         }
10010         if(config.minChars === undefined){
10011             this.minChars = 0;
10012         }
10013     }
10014 };
10015
10016 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10017      
10018     /**
10019      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10020      * rendering into an Roo.Editor, defaults to false)
10021      */
10022     /**
10023      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10024      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10025      */
10026     /**
10027      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10028      */
10029     /**
10030      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10031      * the dropdown list (defaults to undefined, with no header element)
10032      */
10033
10034      /**
10035      * @cfg {String/Roo.Template} tpl The template to use to render the output
10036      */
10037      
10038      /**
10039      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10040      */
10041     listWidth: undefined,
10042     /**
10043      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10044      * mode = 'remote' or 'text' if mode = 'local')
10045      */
10046     displayField: undefined,
10047     /**
10048      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10049      * mode = 'remote' or 'value' if mode = 'local'). 
10050      * Note: use of a valueField requires the user make a selection
10051      * in order for a value to be mapped.
10052      */
10053     valueField: undefined,
10054     
10055     
10056     /**
10057      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10058      * field's data value (defaults to the underlying DOM element's name)
10059      */
10060     hiddenName: undefined,
10061     /**
10062      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10063      */
10064     listClass: '',
10065     /**
10066      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10067      */
10068     selectedClass: 'active',
10069     
10070     /**
10071      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10072      */
10073     shadow:'sides',
10074     /**
10075      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10076      * anchor positions (defaults to 'tl-bl')
10077      */
10078     listAlign: 'tl-bl?',
10079     /**
10080      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10081      */
10082     maxHeight: 300,
10083     /**
10084      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
10085      * query specified by the allQuery config option (defaults to 'query')
10086      */
10087     triggerAction: 'query',
10088     /**
10089      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10090      * (defaults to 4, does not apply if editable = false)
10091      */
10092     minChars : 4,
10093     /**
10094      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10095      * delay (typeAheadDelay) if it matches a known value (defaults to false)
10096      */
10097     typeAhead: false,
10098     /**
10099      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10100      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10101      */
10102     queryDelay: 500,
10103     /**
10104      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10105      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
10106      */
10107     pageSize: 0,
10108     /**
10109      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
10110      * when editable = true (defaults to false)
10111      */
10112     selectOnFocus:false,
10113     /**
10114      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10115      */
10116     queryParam: 'query',
10117     /**
10118      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
10119      * when mode = 'remote' (defaults to 'Loading...')
10120      */
10121     loadingText: 'Loading...',
10122     /**
10123      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10124      */
10125     resizable: false,
10126     /**
10127      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10128      */
10129     handleHeight : 8,
10130     /**
10131      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10132      * traditional select (defaults to true)
10133      */
10134     editable: true,
10135     /**
10136      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10137      */
10138     allQuery: '',
10139     /**
10140      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10141      */
10142     mode: 'remote',
10143     /**
10144      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10145      * listWidth has a higher value)
10146      */
10147     minListWidth : 70,
10148     /**
10149      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10150      * allow the user to set arbitrary text into the field (defaults to false)
10151      */
10152     forceSelection:false,
10153     /**
10154      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10155      * if typeAhead = true (defaults to 250)
10156      */
10157     typeAheadDelay : 250,
10158     /**
10159      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10160      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10161      */
10162     valueNotFoundText : undefined,
10163     /**
10164      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10165      */
10166     blockFocus : false,
10167     
10168     /**
10169      * @cfg {Boolean} disableClear Disable showing of clear button.
10170      */
10171     disableClear : false,
10172     /**
10173      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
10174      */
10175     alwaysQuery : false,
10176     
10177     /**
10178      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
10179      */
10180     multiple : false,
10181     
10182     //private
10183     addicon : false,
10184     editicon: false,
10185     
10186     page: 0,
10187     hasQuery: false,
10188     append: false,
10189     loadNext: false,
10190     autoFocus : true,
10191     tickable : false,
10192     btnPosition : 'right',
10193     
10194     // element that contains real text value.. (when hidden is used..)
10195     
10196     getAutoCreate : function()
10197     {
10198         var cfg = false;
10199         
10200         /*
10201          *  Normal ComboBox
10202          */
10203         if(!this.tickable){
10204             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10205             return cfg;
10206         }
10207         
10208         /*
10209          *  ComboBox with tickable selections
10210          */
10211              
10212         var align = this.labelAlign || this.parentLabelAlign();
10213         
10214         cfg = {
10215             cls : 'form-group roo-combobox-tickable' //input-group
10216         };
10217         
10218         
10219         var buttons = {
10220             tag : 'div',
10221             cls : 'tickable-buttons',
10222             cn : [
10223                 {
10224                     tag : 'button',
10225                     type : 'button',
10226                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10227                     html : 'Edit'
10228                 },
10229                 {
10230                     tag : 'button',
10231                     type : 'button',
10232                     name : 'ok',
10233                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10234                     html : 'Done'
10235                 },
10236                 {
10237                     tag : 'button',
10238                     type : 'button',
10239                     name : 'cancel',
10240                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10241                     html : 'Cancel'
10242                 }
10243             ]
10244         };
10245         
10246         var _this = this;
10247         Roo.each(buttons.cn, function(c){
10248             if (_this.size) {
10249                 c.cls += ' btn-' + _this.size;
10250             }
10251
10252             if (_this.disabled) {
10253                 c.disabled = true;
10254             }
10255         });
10256         
10257         var box = {
10258             tag: 'div',
10259             cn: [
10260                 {
10261                     tag: 'input',
10262                     type : 'hidden',
10263                     cls: 'form-hidden-field'
10264                 },
10265                 {
10266                     tag: 'ul',
10267                     cls: 'select2-choices',
10268                     cn:[
10269                         {
10270                             tag: 'li',
10271                             cls: 'select2-search-field',
10272                             cn: [
10273
10274                                 buttons
10275                             ]
10276                         }
10277                     ]
10278                 }
10279             ]
10280         }
10281         
10282         var combobox = {
10283             cls: 'select2-container input-group select2-container-multi',
10284             cn: [
10285                 box,
10286                 {
10287                     tag: 'ul',
10288                     cls: 'typeahead typeahead-long dropdown-menu',
10289                     style: 'display:none; max-height:' + this.maxHeight + 'px;'
10290                 }
10291             ]
10292         };
10293         
10294         if (align ==='left' && this.fieldLabel.length) {
10295             
10296                 Roo.log("left and has label");
10297                 cfg.cn = [
10298                     
10299                     {
10300                         tag: 'label',
10301                         'for' :  id,
10302                         cls : 'control-label col-sm-' + this.labelWidth,
10303                         html : this.fieldLabel
10304                         
10305                     },
10306                     {
10307                         cls : "col-sm-" + (12 - this.labelWidth), 
10308                         cn: [
10309                             combobox
10310                         ]
10311                     }
10312                     
10313                 ];
10314         } else if ( this.fieldLabel.length) {
10315                 Roo.log(" label");
10316                  cfg.cn = [
10317                    
10318                     {
10319                         tag: 'label',
10320                         //cls : 'input-group-addon',
10321                         html : this.fieldLabel
10322                         
10323                     },
10324                     
10325                     combobox
10326                     
10327                 ];
10328
10329         } else {
10330             
10331                 Roo.log(" no label && no align");
10332                 cfg = combobox
10333                      
10334                 
10335         }
10336          
10337         var settings=this;
10338         ['xs','sm','md','lg'].map(function(size){
10339             if (settings[size]) {
10340                 cfg.cls += ' col-' + size + '-' + settings[size];
10341             }
10342         });
10343         
10344         return cfg;
10345         
10346     },
10347     
10348     // private
10349     initEvents: function()
10350     {
10351         
10352         if (!this.store) {
10353             throw "can not find store for combo";
10354         }
10355         this.store = Roo.factory(this.store, Roo.data);
10356         
10357         if(this.tickable){
10358             this.initTickableEvnets();
10359             return;
10360         }
10361         
10362         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10363         
10364         
10365         if(this.hiddenName){
10366             
10367             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10368             
10369             this.hiddenField.dom.value =
10370                 this.hiddenValue !== undefined ? this.hiddenValue :
10371                 this.value !== undefined ? this.value : '';
10372
10373             // prevent input submission
10374             this.el.dom.removeAttribute('name');
10375             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10376              
10377              
10378         }
10379         //if(Roo.isGecko){
10380         //    this.el.dom.setAttribute('autocomplete', 'off');
10381         //}
10382
10383         var cls = 'x-combo-list';
10384         this.list = this.el.select('ul.dropdown-menu',true).first();
10385
10386         //this.list = new Roo.Layer({
10387         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10388         //});
10389         
10390         var _this = this;
10391         
10392         (function(){
10393             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10394             _this.list.setWidth(lw);
10395         }).defer(100);
10396         
10397         this.list.on('mouseover', this.onViewOver, this);
10398         this.list.on('mousemove', this.onViewMove, this);
10399         
10400         this.list.on('scroll', this.onViewScroll, this);
10401         
10402         /*
10403         this.list.swallowEvent('mousewheel');
10404         this.assetHeight = 0;
10405
10406         if(this.title){
10407             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10408             this.assetHeight += this.header.getHeight();
10409         }
10410
10411         this.innerList = this.list.createChild({cls:cls+'-inner'});
10412         this.innerList.on('mouseover', this.onViewOver, this);
10413         this.innerList.on('mousemove', this.onViewMove, this);
10414         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10415         
10416         if(this.allowBlank && !this.pageSize && !this.disableClear){
10417             this.footer = this.list.createChild({cls:cls+'-ft'});
10418             this.pageTb = new Roo.Toolbar(this.footer);
10419            
10420         }
10421         if(this.pageSize){
10422             this.footer = this.list.createChild({cls:cls+'-ft'});
10423             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10424                     {pageSize: this.pageSize});
10425             
10426         }
10427         
10428         if (this.pageTb && this.allowBlank && !this.disableClear) {
10429             var _this = this;
10430             this.pageTb.add(new Roo.Toolbar.Fill(), {
10431                 cls: 'x-btn-icon x-btn-clear',
10432                 text: '&#160;',
10433                 handler: function()
10434                 {
10435                     _this.collapse();
10436                     _this.clearValue();
10437                     _this.onSelect(false, -1);
10438                 }
10439             });
10440         }
10441         if (this.footer) {
10442             this.assetHeight += this.footer.getHeight();
10443         }
10444         */
10445             
10446         if(!this.tpl){
10447             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10448         }
10449
10450         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10451             singleSelect:true, store: this.store, selectedClass: this.selectedClass
10452         });
10453         //this.view.wrapEl.setDisplayed(false);
10454         this.view.on('click', this.onViewClick, this);
10455         
10456         
10457         
10458         this.store.on('beforeload', this.onBeforeLoad, this);
10459         this.store.on('load', this.onLoad, this);
10460         this.store.on('loadexception', this.onLoadException, this);
10461         /*
10462         if(this.resizable){
10463             this.resizer = new Roo.Resizable(this.list,  {
10464                pinned:true, handles:'se'
10465             });
10466             this.resizer.on('resize', function(r, w, h){
10467                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10468                 this.listWidth = w;
10469                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10470                 this.restrictHeight();
10471             }, this);
10472             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10473         }
10474         */
10475         if(!this.editable){
10476             this.editable = true;
10477             this.setEditable(false);
10478         }
10479         
10480         /*
10481         
10482         if (typeof(this.events.add.listeners) != 'undefined') {
10483             
10484             this.addicon = this.wrap.createChild(
10485                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
10486        
10487             this.addicon.on('click', function(e) {
10488                 this.fireEvent('add', this);
10489             }, this);
10490         }
10491         if (typeof(this.events.edit.listeners) != 'undefined') {
10492             
10493             this.editicon = this.wrap.createChild(
10494                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
10495             if (this.addicon) {
10496                 this.editicon.setStyle('margin-left', '40px');
10497             }
10498             this.editicon.on('click', function(e) {
10499                 
10500                 // we fire even  if inothing is selected..
10501                 this.fireEvent('edit', this, this.lastData );
10502                 
10503             }, this);
10504         }
10505         */
10506         
10507         this.keyNav = new Roo.KeyNav(this.inputEl(), {
10508             "up" : function(e){
10509                 this.inKeyMode = true;
10510                 this.selectPrev();
10511             },
10512
10513             "down" : function(e){
10514                 if(!this.isExpanded()){
10515                     this.onTriggerClick();
10516                 }else{
10517                     this.inKeyMode = true;
10518                     this.selectNext();
10519                 }
10520             },
10521
10522             "enter" : function(e){
10523 //                this.onViewClick();
10524                 //return true;
10525                 this.collapse();
10526                 
10527                 if(this.fireEvent("specialkey", this, e)){
10528                     this.onViewClick(false);
10529                 }
10530                 
10531                 return true;
10532             },
10533
10534             "esc" : function(e){
10535                 this.collapse();
10536             },
10537
10538             "tab" : function(e){
10539                 this.collapse();
10540                 
10541                 if(this.fireEvent("specialkey", this, e)){
10542                     this.onViewClick(false);
10543                 }
10544                 
10545                 return true;
10546             },
10547
10548             scope : this,
10549
10550             doRelay : function(foo, bar, hname){
10551                 if(hname == 'down' || this.scope.isExpanded()){
10552                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10553                 }
10554                 return true;
10555             },
10556
10557             forceKeyDown: true
10558         });
10559         
10560         
10561         this.queryDelay = Math.max(this.queryDelay || 10,
10562                 this.mode == 'local' ? 10 : 250);
10563         
10564         
10565         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10566         
10567         if(this.typeAhead){
10568             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10569         }
10570         if(this.editable !== false){
10571             this.inputEl().on("keyup", this.onKeyUp, this);
10572         }
10573         if(this.forceSelection){
10574             this.inputEl().on('blur', this.doForce, this);
10575         }
10576         
10577         if(this.multiple){
10578             this.choices = this.el.select('ul.select2-choices', true).first();
10579             this.searchField = this.el.select('ul li.select2-search-field', true).first();
10580         }
10581     },
10582     
10583     initTickableEvnets: function()
10584     {   
10585         if(this.hiddenName){
10586             
10587             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10588             
10589             this.hiddenField.dom.value =
10590                 this.hiddenValue !== undefined ? this.hiddenValue :
10591                 this.value !== undefined ? this.value : '';
10592
10593             // prevent input submission
10594             this.el.dom.removeAttribute('name');
10595             this.hiddenField.dom.setAttribute('name', this.hiddenName);
10596              
10597              
10598         }
10599         
10600         this.list = this.el.select('ul.dropdown-menu',true).first();
10601         
10602         this.choices = this.el.select('ul.select2-choices', true).first();
10603         this.searchField = this.el.select('ul li.select2-search-field', true).first();
10604         this.searchField.on("click", this.onTriggerClick, this, {preventDefault:true});
10605          
10606         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10607         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10608         
10609         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10610         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10611         
10612         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10613         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10614         
10615         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10616         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10617         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
10618         
10619         this.okBtn.hide();
10620         this.cancelBtn.hide();
10621         
10622         var _this = this;
10623         
10624         (function(){
10625             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10626             _this.list.setWidth(lw);
10627         }).defer(100);
10628         
10629         this.list.on('mouseover', this.onViewOver, this);
10630         this.list.on('mousemove', this.onViewMove, this);
10631         
10632         this.list.on('scroll', this.onViewScroll, this);
10633         
10634         if(!this.tpl){
10635             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>';
10636         }
10637
10638         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
10639             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
10640         });
10641         
10642         //this.view.wrapEl.setDisplayed(false);
10643         this.view.on('click', this.onViewClick, this);
10644         
10645         
10646         
10647         this.store.on('beforeload', this.onBeforeLoad, this);
10648         this.store.on('load', this.onLoad, this);
10649         this.store.on('loadexception', this.onLoadException, this);
10650         
10651 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
10652 //            "up" : function(e){
10653 //                this.inKeyMode = true;
10654 //                this.selectPrev();
10655 //            },
10656 //
10657 //            "down" : function(e){
10658 //                if(!this.isExpanded()){
10659 //                    this.onTriggerClick();
10660 //                }else{
10661 //                    this.inKeyMode = true;
10662 //                    this.selectNext();
10663 //                }
10664 //            },
10665 //
10666 //            "enter" : function(e){
10667 ////                this.onViewClick();
10668 //                //return true;
10669 //                this.collapse();
10670 //                
10671 //                if(this.fireEvent("specialkey", this, e)){
10672 //                    this.onViewClick(false);
10673 //                }
10674 //                
10675 //                return true;
10676 //            },
10677 //
10678 //            "esc" : function(e){
10679 //                this.collapse();
10680 //            },
10681 //
10682 //            "tab" : function(e){
10683 //                this.collapse();
10684 //                
10685 //                if(this.fireEvent("specialkey", this, e)){
10686 //                    this.onViewClick(false);
10687 //                }
10688 //                
10689 //                return true;
10690 //            },
10691 //
10692 //            scope : this,
10693 //
10694 //            doRelay : function(foo, bar, hname){
10695 //                if(hname == 'down' || this.scope.isExpanded()){
10696 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10697 //                }
10698 //                return true;
10699 //            },
10700 //
10701 //            forceKeyDown: true
10702 //        });
10703         
10704         
10705         this.queryDelay = Math.max(this.queryDelay || 10,
10706                 this.mode == 'local' ? 10 : 250);
10707         
10708         
10709         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10710         
10711         if(this.typeAhead){
10712             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10713         }
10714     },
10715
10716     onDestroy : function(){
10717         if(this.view){
10718             this.view.setStore(null);
10719             this.view.el.removeAllListeners();
10720             this.view.el.remove();
10721             this.view.purgeListeners();
10722         }
10723         if(this.list){
10724             this.list.dom.innerHTML  = '';
10725         }
10726         
10727         if(this.store){
10728             this.store.un('beforeload', this.onBeforeLoad, this);
10729             this.store.un('load', this.onLoad, this);
10730             this.store.un('loadexception', this.onLoadException, this);
10731         }
10732         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
10733     },
10734
10735     // private
10736     fireKey : function(e){
10737         if(e.isNavKeyPress() && !this.list.isVisible()){
10738             this.fireEvent("specialkey", this, e);
10739         }
10740     },
10741
10742     // private
10743     onResize: function(w, h){
10744 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
10745 //        
10746 //        if(typeof w != 'number'){
10747 //            // we do not handle it!?!?
10748 //            return;
10749 //        }
10750 //        var tw = this.trigger.getWidth();
10751 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
10752 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
10753 //        var x = w - tw;
10754 //        this.inputEl().setWidth( this.adjustWidth('input', x));
10755 //            
10756 //        //this.trigger.setStyle('left', x+'px');
10757 //        
10758 //        if(this.list && this.listWidth === undefined){
10759 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
10760 //            this.list.setWidth(lw);
10761 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10762 //        }
10763         
10764     
10765         
10766     },
10767
10768     /**
10769      * Allow or prevent the user from directly editing the field text.  If false is passed,
10770      * the user will only be able to select from the items defined in the dropdown list.  This method
10771      * is the runtime equivalent of setting the 'editable' config option at config time.
10772      * @param {Boolean} value True to allow the user to directly edit the field text
10773      */
10774     setEditable : function(value){
10775         if(value == this.editable){
10776             return;
10777         }
10778         this.editable = value;
10779         if(!value){
10780             this.inputEl().dom.setAttribute('readOnly', true);
10781             this.inputEl().on('mousedown', this.onTriggerClick,  this);
10782             this.inputEl().addClass('x-combo-noedit');
10783         }else{
10784             this.inputEl().dom.setAttribute('readOnly', false);
10785             this.inputEl().un('mousedown', this.onTriggerClick,  this);
10786             this.inputEl().removeClass('x-combo-noedit');
10787         }
10788     },
10789
10790     // private
10791     
10792     onBeforeLoad : function(combo,opts){
10793         if(!this.hasFocus){
10794             return;
10795         }
10796          if (!opts.add) {
10797             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
10798          }
10799         this.restrictHeight();
10800         this.selectedIndex = -1;
10801     },
10802
10803     // private
10804     onLoad : function(){
10805         
10806         this.hasQuery = false;
10807         
10808         if(!this.hasFocus){
10809             return;
10810         }
10811         
10812         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10813             this.loading.hide();
10814         }
10815         
10816         if(this.store.getCount() > 0){
10817             this.expand();
10818             this.restrictHeight();
10819             if(this.lastQuery == this.allQuery){
10820                 if(this.editable && !this.tickable){
10821                     this.inputEl().dom.select();
10822                 }
10823                 if(!this.selectByValue(this.value, true) && this.autoFocus){
10824                     this.select(0, true);
10825                 }
10826             }else{
10827                 if(this.autoFocus){
10828                     this.selectNext();
10829                 }
10830                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
10831                     this.taTask.delay(this.typeAheadDelay);
10832                 }
10833             }
10834         }else{
10835             this.onEmptyResults();
10836         }
10837         
10838         //this.el.focus();
10839     },
10840     // private
10841     onLoadException : function()
10842     {
10843         this.hasQuery = false;
10844         
10845         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
10846             this.loading.hide();
10847         }
10848         
10849         this.collapse();
10850         Roo.log(this.store.reader.jsonData);
10851         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
10852             // fixme
10853             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
10854         }
10855         
10856         
10857     },
10858     // private
10859     onTypeAhead : function(){
10860         if(this.store.getCount() > 0){
10861             var r = this.store.getAt(0);
10862             var newValue = r.data[this.displayField];
10863             var len = newValue.length;
10864             var selStart = this.getRawValue().length;
10865             
10866             if(selStart != len){
10867                 this.setRawValue(newValue);
10868                 this.selectText(selStart, newValue.length);
10869             }
10870         }
10871     },
10872
10873     // private
10874     onSelect : function(record, index){
10875         
10876         if(this.fireEvent('beforeselect', this, record, index) !== false){
10877         
10878             this.setFromData(index > -1 ? record.data : false);
10879             
10880             this.collapse();
10881             this.fireEvent('select', this, record, index);
10882         }
10883     },
10884
10885     /**
10886      * Returns the currently selected field value or empty string if no value is set.
10887      * @return {String} value The selected value
10888      */
10889     getValue : function(){
10890         
10891         if(this.multiple){
10892             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
10893         }
10894         
10895         if(this.valueField){
10896             return typeof this.value != 'undefined' ? this.value : '';
10897         }else{
10898             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
10899         }
10900     },
10901
10902     /**
10903      * Clears any text/value currently set in the field
10904      */
10905     clearValue : function(){
10906         if(this.hiddenField){
10907             this.hiddenField.dom.value = '';
10908         }
10909         this.value = '';
10910         this.setRawValue('');
10911         this.lastSelectionText = '';
10912         
10913     },
10914
10915     /**
10916      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
10917      * will be displayed in the field.  If the value does not match the data value of an existing item,
10918      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
10919      * Otherwise the field will be blank (although the value will still be set).
10920      * @param {String} value The value to match
10921      */
10922     setValue : function(v){
10923         if(this.multiple){
10924             this.syncValue();
10925             return;
10926         }
10927         
10928         var text = v;
10929         if(this.valueField){
10930             var r = this.findRecord(this.valueField, v);
10931             if(r){
10932                 text = r.data[this.displayField];
10933             }else if(this.valueNotFoundText !== undefined){
10934                 text = this.valueNotFoundText;
10935             }
10936         }
10937         this.lastSelectionText = text;
10938         if(this.hiddenField){
10939             this.hiddenField.dom.value = v;
10940         }
10941         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
10942         this.value = v;
10943     },
10944     /**
10945      * @property {Object} the last set data for the element
10946      */
10947     
10948     lastData : false,
10949     /**
10950      * Sets the value of the field based on a object which is related to the record format for the store.
10951      * @param {Object} value the value to set as. or false on reset?
10952      */
10953     setFromData : function(o){
10954         
10955         if(this.multiple){
10956             this.addItem(o);
10957             return;
10958         }
10959             
10960         var dv = ''; // display value
10961         var vv = ''; // value value..
10962         this.lastData = o;
10963         if (this.displayField) {
10964             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10965         } else {
10966             // this is an error condition!!!
10967             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
10968         }
10969         
10970         if(this.valueField){
10971             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
10972         }
10973         
10974         if(this.hiddenField){
10975             this.hiddenField.dom.value = vv;
10976             
10977             this.lastSelectionText = dv;
10978             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
10979             this.value = vv;
10980             return;
10981         }
10982         // no hidden field.. - we store the value in 'value', but still display
10983         // display field!!!!
10984         this.lastSelectionText = dv;
10985         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
10986         this.value = vv;
10987         
10988         
10989     },
10990     // private
10991     reset : function(){
10992         // overridden so that last data is reset..
10993         this.setValue(this.originalValue);
10994         this.clearInvalid();
10995         this.lastData = false;
10996         if (this.view) {
10997             this.view.clearSelections();
10998         }
10999     },
11000     // private
11001     findRecord : function(prop, value){
11002         var record;
11003         if(this.store.getCount() > 0){
11004             this.store.each(function(r){
11005                 if(r.data[prop] == value){
11006                     record = r;
11007                     return false;
11008                 }
11009                 return true;
11010             });
11011         }
11012         return record;
11013     },
11014     
11015     getName: function()
11016     {
11017         // returns hidden if it's set..
11018         if (!this.rendered) {return ''};
11019         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
11020         
11021     },
11022     // private
11023     onViewMove : function(e, t){
11024         this.inKeyMode = false;
11025     },
11026
11027     // private
11028     onViewOver : function(e, t){
11029         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11030             return;
11031         }
11032         var item = this.view.findItemFromChild(t);
11033         
11034         if(item){
11035             var index = this.view.indexOf(item);
11036             this.select(index, false);
11037         }
11038     },
11039
11040     // private
11041     onViewClick : function(view, doFocus, el, e)
11042     {
11043         var index = this.view.getSelectedIndexes()[0];
11044         
11045         var r = this.store.getAt(index);
11046         
11047         if(this.tickable){
11048             
11049             if(e.getTarget().nodeName.toLowerCase() != 'input'){
11050                 return;
11051             }
11052             
11053             var rm = false;
11054             var _this = this;
11055             
11056             Roo.each(this.tickItems, function(v,k){
11057                 
11058                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11059                     _this.tickItems.splice(k, 1);
11060                     rm = true;
11061                     return;
11062                 }
11063             })
11064             
11065             if(rm){
11066                 return;
11067             }
11068             
11069             this.tickItems.push(r.data);
11070             return;
11071         }
11072         
11073         if(r){
11074             this.onSelect(r, index);
11075         }
11076         if(doFocus !== false && !this.blockFocus){
11077             this.inputEl().focus();
11078         }
11079     },
11080
11081     // private
11082     restrictHeight : function(){
11083         //this.innerList.dom.style.height = '';
11084         //var inner = this.innerList.dom;
11085         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11086         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11087         //this.list.beginUpdate();
11088         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11089         this.list.alignTo(this.inputEl(), this.listAlign);
11090         //this.list.endUpdate();
11091     },
11092
11093     // private
11094     onEmptyResults : function(){
11095         this.collapse();
11096     },
11097
11098     /**
11099      * Returns true if the dropdown list is expanded, else false.
11100      */
11101     isExpanded : function(){
11102         return this.list.isVisible();
11103     },
11104
11105     /**
11106      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11107      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11108      * @param {String} value The data value of the item to select
11109      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11110      * selected item if it is not currently in view (defaults to true)
11111      * @return {Boolean} True if the value matched an item in the list, else false
11112      */
11113     selectByValue : function(v, scrollIntoView){
11114         if(v !== undefined && v !== null){
11115             var r = this.findRecord(this.valueField || this.displayField, v);
11116             if(r){
11117                 this.select(this.store.indexOf(r), scrollIntoView);
11118                 return true;
11119             }
11120         }
11121         return false;
11122     },
11123
11124     /**
11125      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11126      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11127      * @param {Number} index The zero-based index of the list item to select
11128      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11129      * selected item if it is not currently in view (defaults to true)
11130      */
11131     select : function(index, scrollIntoView){
11132         this.selectedIndex = index;
11133         this.view.select(index);
11134         if(scrollIntoView !== false){
11135             var el = this.view.getNode(index);
11136             if(el){
11137                 //this.innerList.scrollChildIntoView(el, false);
11138                 
11139             }
11140         }
11141     },
11142
11143     // private
11144     selectNext : function(){
11145         var ct = this.store.getCount();
11146         if(ct > 0){
11147             if(this.selectedIndex == -1){
11148                 this.select(0);
11149             }else if(this.selectedIndex < ct-1){
11150                 this.select(this.selectedIndex+1);
11151             }
11152         }
11153     },
11154
11155     // private
11156     selectPrev : function(){
11157         var ct = this.store.getCount();
11158         if(ct > 0){
11159             if(this.selectedIndex == -1){
11160                 this.select(0);
11161             }else if(this.selectedIndex != 0){
11162                 this.select(this.selectedIndex-1);
11163             }
11164         }
11165     },
11166
11167     // private
11168     onKeyUp : function(e){
11169         if(this.editable !== false && !e.isSpecialKey()){
11170             this.lastKey = e.getKey();
11171             this.dqTask.delay(this.queryDelay);
11172         }
11173     },
11174
11175     // private
11176     validateBlur : function(){
11177         return !this.list || !this.list.isVisible();   
11178     },
11179
11180     // private
11181     initQuery : function(){
11182         this.doQuery(this.getRawValue());
11183     },
11184
11185     // private
11186     doForce : function(){
11187         if(this.inputEl().dom.value.length > 0){
11188             this.inputEl().dom.value =
11189                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11190              
11191         }
11192     },
11193
11194     /**
11195      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
11196      * query allowing the query action to be canceled if needed.
11197      * @param {String} query The SQL query to execute
11198      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11199      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
11200      * saved in the current store (defaults to false)
11201      */
11202     doQuery : function(q, forceAll){
11203         
11204         if(q === undefined || q === null){
11205             q = '';
11206         }
11207         var qe = {
11208             query: q,
11209             forceAll: forceAll,
11210             combo: this,
11211             cancel:false
11212         };
11213         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11214             return false;
11215         }
11216         q = qe.query;
11217         
11218         forceAll = qe.forceAll;
11219         if(forceAll === true || (q.length >= this.minChars)){
11220             
11221             this.hasQuery = true;
11222             
11223             if(this.lastQuery != q || this.alwaysQuery){
11224                 this.lastQuery = q;
11225                 if(this.mode == 'local'){
11226                     this.selectedIndex = -1;
11227                     if(forceAll){
11228                         this.store.clearFilter();
11229                     }else{
11230                         this.store.filter(this.displayField, q);
11231                     }
11232                     this.onLoad();
11233                 }else{
11234                     this.store.baseParams[this.queryParam] = q;
11235                     
11236                     var options = {params : this.getParams(q)};
11237                     
11238                     if(this.loadNext){
11239                         options.add = true;
11240                         options.params.start = this.page * this.pageSize;
11241                     }
11242                     
11243                     this.store.load(options);
11244                     /*
11245                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
11246                      *  we should expand the list on onLoad
11247                      *  so command out it
11248                      */
11249 //                    this.expand();
11250                 }
11251             }else{
11252                 this.selectedIndex = -1;
11253                 this.onLoad();   
11254             }
11255         }
11256         
11257         this.loadNext = false;
11258     },
11259
11260     // private
11261     getParams : function(q){
11262         var p = {};
11263         //p[this.queryParam] = q;
11264         
11265         if(this.pageSize){
11266             p.start = 0;
11267             p.limit = this.pageSize;
11268         }
11269         return p;
11270     },
11271
11272     /**
11273      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11274      */
11275     collapse : function(){
11276         if(!this.isExpanded()){
11277             return;
11278         }
11279         
11280         this.list.hide();
11281         
11282         if(this.tickable){
11283             this.okBtn.hide();
11284             this.cancelBtn.hide();
11285             this.trigger.show();
11286         }
11287         
11288         Roo.get(document).un('mousedown', this.collapseIf, this);
11289         Roo.get(document).un('mousewheel', this.collapseIf, this);
11290         if (!this.editable) {
11291             Roo.get(document).un('keydown', this.listKeyPress, this);
11292         }
11293         this.fireEvent('collapse', this);
11294     },
11295
11296     // private
11297     collapseIf : function(e){
11298         var in_combo  = e.within(this.el);
11299         var in_list =  e.within(this.list);
11300         
11301         if (in_combo || in_list) {
11302             //e.stopPropagation();
11303             return;
11304         }
11305         
11306         if(this.tickable){
11307             this.onTickableFooterButtonClick(e, false, false);
11308         }
11309
11310         this.collapse();
11311         
11312     },
11313
11314     /**
11315      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11316      */
11317     expand : function(){
11318        
11319         if(this.isExpanded() || !this.hasFocus){
11320             return;
11321         }
11322          Roo.log('expand');
11323         this.list.alignTo(this.inputEl(), this.listAlign);
11324         this.list.show();
11325         
11326         if(this.tickable){
11327             
11328             this.tickItems = Roo.apply([], this.item);
11329             
11330             this.okBtn.show();
11331             this.cancelBtn.show();
11332             this.trigger.hide();
11333             
11334         }
11335         
11336         Roo.get(document).on('mousedown', this.collapseIf, this);
11337         Roo.get(document).on('mousewheel', this.collapseIf, this);
11338         if (!this.editable) {
11339             Roo.get(document).on('keydown', this.listKeyPress, this);
11340         }
11341         
11342         this.fireEvent('expand', this);
11343     },
11344
11345     // private
11346     // Implements the default empty TriggerField.onTriggerClick function
11347     onTriggerClick : function()
11348     {
11349         Roo.log('trigger click');
11350         
11351         if(this.disabled){
11352             return;
11353         }
11354         
11355         if(this.tickable){
11356             this.onTickableTriggerClick();
11357             return;
11358         }
11359         
11360         this.page = 0;
11361         this.loadNext = false;
11362         
11363         if(this.isExpanded()){
11364             this.collapse();
11365             if (!this.blockFocus) {
11366                 this.inputEl().focus();
11367             }
11368             
11369         }else {
11370             this.hasFocus = true;
11371             if(this.triggerAction == 'all') {
11372                 this.doQuery(this.allQuery, true);
11373             } else {
11374                 this.doQuery(this.getRawValue());
11375             }
11376             if (!this.blockFocus) {
11377                 this.inputEl().focus();
11378             }
11379         }
11380     },
11381     
11382     onTickableTriggerClick : function()
11383     {
11384         this.page = 0;
11385         this.loadNext = false;
11386         this.hasFocus = true;
11387         
11388         if(this.triggerAction == 'all') {
11389             this.doQuery(this.allQuery, true);
11390         } else {
11391             this.doQuery(this.getRawValue());
11392         }
11393     },
11394     
11395     listKeyPress : function(e)
11396     {
11397         //Roo.log('listkeypress');
11398         // scroll to first matching element based on key pres..
11399         if (e.isSpecialKey()) {
11400             return false;
11401         }
11402         var k = String.fromCharCode(e.getKey()).toUpperCase();
11403         //Roo.log(k);
11404         var match  = false;
11405         var csel = this.view.getSelectedNodes();
11406         var cselitem = false;
11407         if (csel.length) {
11408             var ix = this.view.indexOf(csel[0]);
11409             cselitem  = this.store.getAt(ix);
11410             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11411                 cselitem = false;
11412             }
11413             
11414         }
11415         
11416         this.store.each(function(v) { 
11417             if (cselitem) {
11418                 // start at existing selection.
11419                 if (cselitem.id == v.id) {
11420                     cselitem = false;
11421                 }
11422                 return true;
11423             }
11424                 
11425             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11426                 match = this.store.indexOf(v);
11427                 return false;
11428             }
11429             return true;
11430         }, this);
11431         
11432         if (match === false) {
11433             return true; // no more action?
11434         }
11435         // scroll to?
11436         this.view.select(match);
11437         var sn = Roo.get(this.view.getSelectedNodes()[0])
11438         //sn.scrollIntoView(sn.dom.parentNode, false);
11439     },
11440     
11441     onViewScroll : function(e, t){
11442         
11443         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
11444             return;
11445         }
11446         
11447         this.hasQuery = true;
11448         
11449         this.loading = this.list.select('.loading', true).first();
11450         
11451         if(this.loading === null){
11452             this.list.createChild({
11453                 tag: 'div',
11454                 cls: 'loading select2-more-results select2-active',
11455                 html: 'Loading more results...'
11456             })
11457             
11458             this.loading = this.list.select('.loading', true).first();
11459             
11460             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11461             
11462             this.loading.hide();
11463         }
11464         
11465         this.loading.show();
11466         
11467         var _combo = this;
11468         
11469         this.page++;
11470         this.loadNext = true;
11471         
11472         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11473         
11474         return;
11475     },
11476     
11477     addItem : function(o)
11478     {   
11479         var dv = ''; // display value
11480         
11481         if (this.displayField) {
11482             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11483         } else {
11484             // this is an error condition!!!
11485             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
11486         }
11487         
11488         if(!dv.length){
11489             return;
11490         }
11491         
11492         var choice = this.choices.createChild({
11493             tag: 'li',
11494             cls: 'select2-search-choice',
11495             cn: [
11496                 {
11497                     tag: 'div',
11498                     html: dv
11499                 },
11500                 {
11501                     tag: 'a',
11502                     href: '#',
11503                     cls: 'select2-search-choice-close',
11504                     tabindex: '-1'
11505                 }
11506             ]
11507             
11508         }, this.searchField);
11509         
11510         var close = choice.select('a.select2-search-choice-close', true).first()
11511         
11512         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11513         
11514         this.item.push(o);
11515         
11516         this.lastData = o;
11517         
11518         this.syncValue();
11519         
11520         this.inputEl().dom.value = '';
11521         
11522     },
11523     
11524     onRemoveItem : function(e, _self, o)
11525     {
11526         e.preventDefault();
11527         var index = this.item.indexOf(o.data) * 1;
11528         
11529         if( index < 0){
11530             Roo.log('not this item?!');
11531             return;
11532         }
11533         
11534         this.item.splice(index, 1);
11535         o.item.remove();
11536         
11537         this.syncValue();
11538         
11539         this.fireEvent('remove', this, e);
11540         
11541     },
11542     
11543     syncValue : function()
11544     {
11545         if(!this.item.length){
11546             this.clearValue();
11547             return;
11548         }
11549             
11550         var value = [];
11551         var _this = this;
11552         Roo.each(this.item, function(i){
11553             if(_this.valueField){
11554                 value.push(i[_this.valueField]);
11555                 return;
11556             }
11557
11558             value.push(i);
11559         });
11560
11561         this.value = value.join(',');
11562
11563         if(this.hiddenField){
11564             this.hiddenField.dom.value = this.value;
11565         }
11566     },
11567     
11568     clearItem : function()
11569     {
11570         if(!this.multiple){
11571             return;
11572         }
11573         
11574         this.item = [];
11575         
11576         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11577            c.remove();
11578         });
11579         
11580         this.syncValue();
11581     },
11582     
11583     inputEl: function ()
11584     {
11585         if(this.tickable){
11586             return this.searchField;
11587         }
11588         return this.el.select('input.form-control',true).first();
11589     },
11590     
11591     
11592     onTickableFooterButtonClick : function(e, btn, el)
11593     {
11594         e.preventDefault();
11595         
11596         if(btn && btn.name == 'cancel'){
11597             this.tickItems = Roo.apply([], this.item);
11598             this.collapse();
11599             return;
11600         }
11601         
11602         this.clearItem();
11603         
11604         var _this = this;
11605         
11606         Roo.each(this.tickItems, function(o){
11607             _this.addItem(o);
11608         });
11609         
11610         this.collapse();
11611         
11612     }
11613     
11614     
11615
11616     /** 
11617     * @cfg {Boolean} grow 
11618     * @hide 
11619     */
11620     /** 
11621     * @cfg {Number} growMin 
11622     * @hide 
11623     */
11624     /** 
11625     * @cfg {Number} growMax 
11626     * @hide 
11627     */
11628     /**
11629      * @hide
11630      * @method autoSize
11631      */
11632 });
11633 /*
11634  * Based on:
11635  * Ext JS Library 1.1.1
11636  * Copyright(c) 2006-2007, Ext JS, LLC.
11637  *
11638  * Originally Released Under LGPL - original licence link has changed is not relivant.
11639  *
11640  * Fork - LGPL
11641  * <script type="text/javascript">
11642  */
11643
11644 /**
11645  * @class Roo.View
11646  * @extends Roo.util.Observable
11647  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
11648  * This class also supports single and multi selection modes. <br>
11649  * Create a data model bound view:
11650  <pre><code>
11651  var store = new Roo.data.Store(...);
11652
11653  var view = new Roo.View({
11654     el : "my-element",
11655     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
11656  
11657     singleSelect: true,
11658     selectedClass: "ydataview-selected",
11659     store: store
11660  });
11661
11662  // listen for node click?
11663  view.on("click", function(vw, index, node, e){
11664  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
11665  });
11666
11667  // load XML data
11668  dataModel.load("foobar.xml");
11669  </code></pre>
11670  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
11671  * <br><br>
11672  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
11673  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
11674  * 
11675  * Note: old style constructor is still suported (container, template, config)
11676  * 
11677  * @constructor
11678  * Create a new View
11679  * @param {Object} config The config object
11680  * 
11681  */
11682 Roo.View = function(config, depreciated_tpl, depreciated_config){
11683     
11684     this.parent = false;
11685     
11686     if (typeof(depreciated_tpl) == 'undefined') {
11687         // new way.. - universal constructor.
11688         Roo.apply(this, config);
11689         this.el  = Roo.get(this.el);
11690     } else {
11691         // old format..
11692         this.el  = Roo.get(config);
11693         this.tpl = depreciated_tpl;
11694         Roo.apply(this, depreciated_config);
11695     }
11696     this.wrapEl  = this.el.wrap().wrap();
11697     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
11698     
11699     
11700     if(typeof(this.tpl) == "string"){
11701         this.tpl = new Roo.Template(this.tpl);
11702     } else {
11703         // support xtype ctors..
11704         this.tpl = new Roo.factory(this.tpl, Roo);
11705     }
11706     
11707     
11708     this.tpl.compile();
11709     
11710     /** @private */
11711     this.addEvents({
11712         /**
11713          * @event beforeclick
11714          * Fires before a click is processed. Returns false to cancel the default action.
11715          * @param {Roo.View} this
11716          * @param {Number} index The index of the target node
11717          * @param {HTMLElement} node The target node
11718          * @param {Roo.EventObject} e The raw event object
11719          */
11720             "beforeclick" : true,
11721         /**
11722          * @event click
11723          * Fires when a template node is clicked.
11724          * @param {Roo.View} this
11725          * @param {Number} index The index of the target node
11726          * @param {HTMLElement} node The target node
11727          * @param {Roo.EventObject} e The raw event object
11728          */
11729             "click" : true,
11730         /**
11731          * @event dblclick
11732          * Fires when a template node is double clicked.
11733          * @param {Roo.View} this
11734          * @param {Number} index The index of the target node
11735          * @param {HTMLElement} node The target node
11736          * @param {Roo.EventObject} e The raw event object
11737          */
11738             "dblclick" : true,
11739         /**
11740          * @event contextmenu
11741          * Fires when a template node is right clicked.
11742          * @param {Roo.View} this
11743          * @param {Number} index The index of the target node
11744          * @param {HTMLElement} node The target node
11745          * @param {Roo.EventObject} e The raw event object
11746          */
11747             "contextmenu" : true,
11748         /**
11749          * @event selectionchange
11750          * Fires when the selected nodes change.
11751          * @param {Roo.View} this
11752          * @param {Array} selections Array of the selected nodes
11753          */
11754             "selectionchange" : true,
11755     
11756         /**
11757          * @event beforeselect
11758          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
11759          * @param {Roo.View} this
11760          * @param {HTMLElement} node The node to be selected
11761          * @param {Array} selections Array of currently selected nodes
11762          */
11763             "beforeselect" : true,
11764         /**
11765          * @event preparedata
11766          * Fires on every row to render, to allow you to change the data.
11767          * @param {Roo.View} this
11768          * @param {Object} data to be rendered (change this)
11769          */
11770           "preparedata" : true
11771           
11772           
11773         });
11774
11775
11776
11777     this.el.on({
11778         "click": this.onClick,
11779         "dblclick": this.onDblClick,
11780         "contextmenu": this.onContextMenu,
11781         scope:this
11782     });
11783
11784     this.selections = [];
11785     this.nodes = [];
11786     this.cmp = new Roo.CompositeElementLite([]);
11787     if(this.store){
11788         this.store = Roo.factory(this.store, Roo.data);
11789         this.setStore(this.store, true);
11790     }
11791     
11792     if ( this.footer && this.footer.xtype) {
11793            
11794          var fctr = this.wrapEl.appendChild(document.createElement("div"));
11795         
11796         this.footer.dataSource = this.store
11797         this.footer.container = fctr;
11798         this.footer = Roo.factory(this.footer, Roo);
11799         fctr.insertFirst(this.el);
11800         
11801         // this is a bit insane - as the paging toolbar seems to detach the el..
11802 //        dom.parentNode.parentNode.parentNode
11803          // they get detached?
11804     }
11805     
11806     
11807     Roo.View.superclass.constructor.call(this);
11808     
11809     
11810 };
11811
11812 Roo.extend(Roo.View, Roo.util.Observable, {
11813     
11814      /**
11815      * @cfg {Roo.data.Store} store Data store to load data from.
11816      */
11817     store : false,
11818     
11819     /**
11820      * @cfg {String|Roo.Element} el The container element.
11821      */
11822     el : '',
11823     
11824     /**
11825      * @cfg {String|Roo.Template} tpl The template used by this View 
11826      */
11827     tpl : false,
11828     /**
11829      * @cfg {String} dataName the named area of the template to use as the data area
11830      *                          Works with domtemplates roo-name="name"
11831      */
11832     dataName: false,
11833     /**
11834      * @cfg {String} selectedClass The css class to add to selected nodes
11835      */
11836     selectedClass : "x-view-selected",
11837      /**
11838      * @cfg {String} emptyText The empty text to show when nothing is loaded.
11839      */
11840     emptyText : "",
11841     
11842     /**
11843      * @cfg {String} text to display on mask (default Loading)
11844      */
11845     mask : false,
11846     /**
11847      * @cfg {Boolean} multiSelect Allow multiple selection
11848      */
11849     multiSelect : false,
11850     /**
11851      * @cfg {Boolean} singleSelect Allow single selection
11852      */
11853     singleSelect:  false,
11854     
11855     /**
11856      * @cfg {Boolean} toggleSelect - selecting 
11857      */
11858     toggleSelect : false,
11859     
11860     /**
11861      * @cfg {Boolean} tickable - selecting 
11862      */
11863     tickable : false,
11864     
11865     /**
11866      * Returns the element this view is bound to.
11867      * @return {Roo.Element}
11868      */
11869     getEl : function(){
11870         return this.wrapEl;
11871     },
11872     
11873     
11874
11875     /**
11876      * Refreshes the view. - called by datachanged on the store. - do not call directly.
11877      */
11878     refresh : function(){
11879         Roo.log('refresh');
11880         var t = this.tpl;
11881         
11882         // if we are using something like 'domtemplate', then
11883         // the what gets used is:
11884         // t.applySubtemplate(NAME, data, wrapping data..)
11885         // the outer template then get' applied with
11886         //     the store 'extra data'
11887         // and the body get's added to the
11888         //      roo-name="data" node?
11889         //      <span class='roo-tpl-{name}'></span> ?????
11890         
11891         
11892         
11893         this.clearSelections();
11894         this.el.update("");
11895         var html = [];
11896         var records = this.store.getRange();
11897         if(records.length < 1) {
11898             
11899             // is this valid??  = should it render a template??
11900             
11901             this.el.update(this.emptyText);
11902             return;
11903         }
11904         var el = this.el;
11905         if (this.dataName) {
11906             this.el.update(t.apply(this.store.meta)); //????
11907             el = this.el.child('.roo-tpl-' + this.dataName);
11908         }
11909         
11910         for(var i = 0, len = records.length; i < len; i++){
11911             var data = this.prepareData(records[i].data, i, records[i]);
11912             this.fireEvent("preparedata", this, data, i, records[i]);
11913             
11914             var d = Roo.apply({}, data);
11915             
11916             if(this.tickable){
11917                 Roo.apply(d, {'roo-id' : Roo.id()});
11918                 
11919                 var _this = this;
11920             
11921                 Roo.each(this.parent.item, function(item){
11922                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
11923                         return;
11924                     }
11925                     Roo.apply(d, {'roo-data-checked' : 'checked'});
11926                 });
11927             }
11928             
11929             html[html.length] = Roo.util.Format.trim(
11930                 this.dataName ?
11931                     t.applySubtemplate(this.dataName, d, this.store.meta) :
11932                     t.apply(d)
11933             );
11934         }
11935         
11936         
11937         
11938         el.update(html.join(""));
11939         this.nodes = el.dom.childNodes;
11940         this.updateIndexes(0);
11941     },
11942     
11943
11944     /**
11945      * Function to override to reformat the data that is sent to
11946      * the template for each node.
11947      * DEPRICATED - use the preparedata event handler.
11948      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
11949      * a JSON object for an UpdateManager bound view).
11950      */
11951     prepareData : function(data, index, record)
11952     {
11953         this.fireEvent("preparedata", this, data, index, record);
11954         return data;
11955     },
11956
11957     onUpdate : function(ds, record){
11958          Roo.log('on update');   
11959         this.clearSelections();
11960         var index = this.store.indexOf(record);
11961         var n = this.nodes[index];
11962         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
11963         n.parentNode.removeChild(n);
11964         this.updateIndexes(index, index);
11965     },
11966
11967     
11968     
11969 // --------- FIXME     
11970     onAdd : function(ds, records, index)
11971     {
11972         Roo.log(['on Add', ds, records, index] );        
11973         this.clearSelections();
11974         if(this.nodes.length == 0){
11975             this.refresh();
11976             return;
11977         }
11978         var n = this.nodes[index];
11979         for(var i = 0, len = records.length; i < len; i++){
11980             var d = this.prepareData(records[i].data, i, records[i]);
11981             if(n){
11982                 this.tpl.insertBefore(n, d);
11983             }else{
11984                 
11985                 this.tpl.append(this.el, d);
11986             }
11987         }
11988         this.updateIndexes(index);
11989     },
11990
11991     onRemove : function(ds, record, index){
11992         Roo.log('onRemove');
11993         this.clearSelections();
11994         var el = this.dataName  ?
11995             this.el.child('.roo-tpl-' + this.dataName) :
11996             this.el; 
11997         
11998         el.dom.removeChild(this.nodes[index]);
11999         this.updateIndexes(index);
12000     },
12001
12002     /**
12003      * Refresh an individual node.
12004      * @param {Number} index
12005      */
12006     refreshNode : function(index){
12007         this.onUpdate(this.store, this.store.getAt(index));
12008     },
12009
12010     updateIndexes : function(startIndex, endIndex){
12011         var ns = this.nodes;
12012         startIndex = startIndex || 0;
12013         endIndex = endIndex || ns.length - 1;
12014         for(var i = startIndex; i <= endIndex; i++){
12015             ns[i].nodeIndex = i;
12016         }
12017     },
12018
12019     /**
12020      * Changes the data store this view uses and refresh the view.
12021      * @param {Store} store
12022      */
12023     setStore : function(store, initial){
12024         if(!initial && this.store){
12025             this.store.un("datachanged", this.refresh);
12026             this.store.un("add", this.onAdd);
12027             this.store.un("remove", this.onRemove);
12028             this.store.un("update", this.onUpdate);
12029             this.store.un("clear", this.refresh);
12030             this.store.un("beforeload", this.onBeforeLoad);
12031             this.store.un("load", this.onLoad);
12032             this.store.un("loadexception", this.onLoad);
12033         }
12034         if(store){
12035           
12036             store.on("datachanged", this.refresh, this);
12037             store.on("add", this.onAdd, this);
12038             store.on("remove", this.onRemove, this);
12039             store.on("update", this.onUpdate, this);
12040             store.on("clear", this.refresh, this);
12041             store.on("beforeload", this.onBeforeLoad, this);
12042             store.on("load", this.onLoad, this);
12043             store.on("loadexception", this.onLoad, this);
12044         }
12045         
12046         if(store){
12047             this.refresh();
12048         }
12049     },
12050     /**
12051      * onbeforeLoad - masks the loading area.
12052      *
12053      */
12054     onBeforeLoad : function(store,opts)
12055     {
12056          Roo.log('onBeforeLoad');   
12057         if (!opts.add) {
12058             this.el.update("");
12059         }
12060         this.el.mask(this.mask ? this.mask : "Loading" ); 
12061     },
12062     onLoad : function ()
12063     {
12064         this.el.unmask();
12065     },
12066     
12067
12068     /**
12069      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12070      * @param {HTMLElement} node
12071      * @return {HTMLElement} The template node
12072      */
12073     findItemFromChild : function(node){
12074         var el = this.dataName  ?
12075             this.el.child('.roo-tpl-' + this.dataName,true) :
12076             this.el.dom; 
12077         
12078         if(!node || node.parentNode == el){
12079                     return node;
12080             }
12081             var p = node.parentNode;
12082             while(p && p != el){
12083             if(p.parentNode == el){
12084                 return p;
12085             }
12086             p = p.parentNode;
12087         }
12088             return null;
12089     },
12090
12091     /** @ignore */
12092     onClick : function(e){
12093         var item = this.findItemFromChild(e.getTarget());
12094         if(item){
12095             var index = this.indexOf(item);
12096             if(this.onItemClick(item, index, e) !== false){
12097                 this.fireEvent("click", this, index, item, e);
12098             }
12099         }else{
12100             this.clearSelections();
12101         }
12102     },
12103
12104     /** @ignore */
12105     onContextMenu : function(e){
12106         var item = this.findItemFromChild(e.getTarget());
12107         if(item){
12108             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12109         }
12110     },
12111
12112     /** @ignore */
12113     onDblClick : function(e){
12114         var item = this.findItemFromChild(e.getTarget());
12115         if(item){
12116             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12117         }
12118     },
12119
12120     onItemClick : function(item, index, e)
12121     {
12122         if(this.fireEvent("beforeclick", this, index, item, e) === false){
12123             return false;
12124         }
12125         if (this.toggleSelect) {
12126             var m = this.isSelected(item) ? 'unselect' : 'select';
12127             Roo.log(m);
12128             var _t = this;
12129             _t[m](item, true, false);
12130             return true;
12131         }
12132         if(this.multiSelect || this.singleSelect){
12133             if(this.multiSelect && e.shiftKey && this.lastSelection){
12134                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12135             }else{
12136                 this.select(item, this.multiSelect && e.ctrlKey);
12137                 this.lastSelection = item;
12138             }
12139             
12140             if(!this.tickable){
12141                 e.preventDefault();
12142             }
12143             
12144         }
12145         return true;
12146     },
12147
12148     /**
12149      * Get the number of selected nodes.
12150      * @return {Number}
12151      */
12152     getSelectionCount : function(){
12153         return this.selections.length;
12154     },
12155
12156     /**
12157      * Get the currently selected nodes.
12158      * @return {Array} An array of HTMLElements
12159      */
12160     getSelectedNodes : function(){
12161         return this.selections;
12162     },
12163
12164     /**
12165      * Get the indexes of the selected nodes.
12166      * @return {Array}
12167      */
12168     getSelectedIndexes : function(){
12169         var indexes = [], s = this.selections;
12170         for(var i = 0, len = s.length; i < len; i++){
12171             indexes.push(s[i].nodeIndex);
12172         }
12173         return indexes;
12174     },
12175
12176     /**
12177      * Clear all selections
12178      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12179      */
12180     clearSelections : function(suppressEvent){
12181         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12182             this.cmp.elements = this.selections;
12183             this.cmp.removeClass(this.selectedClass);
12184             this.selections = [];
12185             if(!suppressEvent){
12186                 this.fireEvent("selectionchange", this, this.selections);
12187             }
12188         }
12189     },
12190
12191     /**
12192      * Returns true if the passed node is selected
12193      * @param {HTMLElement/Number} node The node or node index
12194      * @return {Boolean}
12195      */
12196     isSelected : function(node){
12197         var s = this.selections;
12198         if(s.length < 1){
12199             return false;
12200         }
12201         node = this.getNode(node);
12202         return s.indexOf(node) !== -1;
12203     },
12204
12205     /**
12206      * Selects nodes.
12207      * @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
12208      * @param {Boolean} keepExisting (optional) true to keep existing selections
12209      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12210      */
12211     select : function(nodeInfo, keepExisting, suppressEvent){
12212         if(nodeInfo instanceof Array){
12213             if(!keepExisting){
12214                 this.clearSelections(true);
12215             }
12216             for(var i = 0, len = nodeInfo.length; i < len; i++){
12217                 this.select(nodeInfo[i], true, true);
12218             }
12219             return;
12220         } 
12221         var node = this.getNode(nodeInfo);
12222         if(!node || this.isSelected(node)){
12223             return; // already selected.
12224         }
12225         if(!keepExisting){
12226             this.clearSelections(true);
12227         }
12228         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12229             Roo.fly(node).addClass(this.selectedClass);
12230             this.selections.push(node);
12231             if(!suppressEvent){
12232                 this.fireEvent("selectionchange", this, this.selections);
12233             }
12234         }
12235         
12236         
12237     },
12238       /**
12239      * Unselects nodes.
12240      * @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
12241      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12242      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12243      */
12244     unselect : function(nodeInfo, keepExisting, suppressEvent)
12245     {
12246         if(nodeInfo instanceof Array){
12247             Roo.each(this.selections, function(s) {
12248                 this.unselect(s, nodeInfo);
12249             }, this);
12250             return;
12251         }
12252         var node = this.getNode(nodeInfo);
12253         if(!node || !this.isSelected(node)){
12254             Roo.log("not selected");
12255             return; // not selected.
12256         }
12257         // fireevent???
12258         var ns = [];
12259         Roo.each(this.selections, function(s) {
12260             if (s == node ) {
12261                 Roo.fly(node).removeClass(this.selectedClass);
12262
12263                 return;
12264             }
12265             ns.push(s);
12266         },this);
12267         
12268         this.selections= ns;
12269         this.fireEvent("selectionchange", this, this.selections);
12270     },
12271
12272     /**
12273      * Gets a template node.
12274      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12275      * @return {HTMLElement} The node or null if it wasn't found
12276      */
12277     getNode : function(nodeInfo){
12278         if(typeof nodeInfo == "string"){
12279             return document.getElementById(nodeInfo);
12280         }else if(typeof nodeInfo == "number"){
12281             return this.nodes[nodeInfo];
12282         }
12283         return nodeInfo;
12284     },
12285
12286     /**
12287      * Gets a range template nodes.
12288      * @param {Number} startIndex
12289      * @param {Number} endIndex
12290      * @return {Array} An array of nodes
12291      */
12292     getNodes : function(start, end){
12293         var ns = this.nodes;
12294         start = start || 0;
12295         end = typeof end == "undefined" ? ns.length - 1 : end;
12296         var nodes = [];
12297         if(start <= end){
12298             for(var i = start; i <= end; i++){
12299                 nodes.push(ns[i]);
12300             }
12301         } else{
12302             for(var i = start; i >= end; i--){
12303                 nodes.push(ns[i]);
12304             }
12305         }
12306         return nodes;
12307     },
12308
12309     /**
12310      * Finds the index of the passed node
12311      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12312      * @return {Number} The index of the node or -1
12313      */
12314     indexOf : function(node){
12315         node = this.getNode(node);
12316         if(typeof node.nodeIndex == "number"){
12317             return node.nodeIndex;
12318         }
12319         var ns = this.nodes;
12320         for(var i = 0, len = ns.length; i < len; i++){
12321             if(ns[i] == node){
12322                 return i;
12323             }
12324         }
12325         return -1;
12326     }
12327 });
12328 /*
12329  * - LGPL
12330  *
12331  * based on jquery fullcalendar
12332  * 
12333  */
12334
12335 Roo.bootstrap = Roo.bootstrap || {};
12336 /**
12337  * @class Roo.bootstrap.Calendar
12338  * @extends Roo.bootstrap.Component
12339  * Bootstrap Calendar class
12340  * @cfg {Boolean} loadMask (true|false) default false
12341  * @cfg {Object} header generate the user specific header of the calendar, default false
12342
12343  * @constructor
12344  * Create a new Container
12345  * @param {Object} config The config object
12346  */
12347
12348
12349
12350 Roo.bootstrap.Calendar = function(config){
12351     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12352      this.addEvents({
12353         /**
12354              * @event select
12355              * Fires when a date is selected
12356              * @param {DatePicker} this
12357              * @param {Date} date The selected date
12358              */
12359         'select': true,
12360         /**
12361              * @event monthchange
12362              * Fires when the displayed month changes 
12363              * @param {DatePicker} this
12364              * @param {Date} date The selected month
12365              */
12366         'monthchange': true,
12367         /**
12368              * @event evententer
12369              * Fires when mouse over an event
12370              * @param {Calendar} this
12371              * @param {event} Event
12372              */
12373         'evententer': true,
12374         /**
12375              * @event eventleave
12376              * Fires when the mouse leaves an
12377              * @param {Calendar} this
12378              * @param {event}
12379              */
12380         'eventleave': true,
12381         /**
12382              * @event eventclick
12383              * Fires when the mouse click an
12384              * @param {Calendar} this
12385              * @param {event}
12386              */
12387         'eventclick': true
12388         
12389     });
12390
12391 };
12392
12393 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
12394     
12395      /**
12396      * @cfg {Number} startDay
12397      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12398      */
12399     startDay : 0,
12400     
12401     loadMask : false,
12402     
12403     header : false,
12404       
12405     getAutoCreate : function(){
12406         
12407         
12408         var fc_button = function(name, corner, style, content ) {
12409             return Roo.apply({},{
12410                 tag : 'span',
12411                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
12412                          (corner.length ?
12413                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12414                             ''
12415                         ),
12416                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12417                 unselectable: 'on'
12418             });
12419         };
12420         
12421         var header = {};
12422         
12423         if(!this.header){
12424             header = {
12425                 tag : 'table',
12426                 cls : 'fc-header',
12427                 style : 'width:100%',
12428                 cn : [
12429                     {
12430                         tag: 'tr',
12431                         cn : [
12432                             {
12433                                 tag : 'td',
12434                                 cls : 'fc-header-left',
12435                                 cn : [
12436                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
12437                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
12438                                     { tag: 'span', cls: 'fc-header-space' },
12439                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
12440
12441
12442                                 ]
12443                             },
12444
12445                             {
12446                                 tag : 'td',
12447                                 cls : 'fc-header-center',
12448                                 cn : [
12449                                     {
12450                                         tag: 'span',
12451                                         cls: 'fc-header-title',
12452                                         cn : {
12453                                             tag: 'H2',
12454                                             html : 'month / year'
12455                                         }
12456                                     }
12457
12458                                 ]
12459                             },
12460                             {
12461                                 tag : 'td',
12462                                 cls : 'fc-header-right',
12463                                 cn : [
12464                               /*      fc_button('month', 'left', '', 'month' ),
12465                                     fc_button('week', '', '', 'week' ),
12466                                     fc_button('day', 'right', '', 'day' )
12467                                 */    
12468
12469                                 ]
12470                             }
12471
12472                         ]
12473                     }
12474                 ]
12475             };
12476         }
12477         
12478         header = this.header;
12479         
12480        
12481         var cal_heads = function() {
12482             var ret = [];
12483             // fixme - handle this.
12484             
12485             for (var i =0; i < Date.dayNames.length; i++) {
12486                 var d = Date.dayNames[i];
12487                 ret.push({
12488                     tag: 'th',
12489                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12490                     html : d.substring(0,3)
12491                 });
12492                 
12493             }
12494             ret[0].cls += ' fc-first';
12495             ret[6].cls += ' fc-last';
12496             return ret;
12497         };
12498         var cal_cell = function(n) {
12499             return  {
12500                 tag: 'td',
12501                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12502                 cn : [
12503                     {
12504                         cn : [
12505                             {
12506                                 cls: 'fc-day-number',
12507                                 html: 'D'
12508                             },
12509                             {
12510                                 cls: 'fc-day-content',
12511                              
12512                                 cn : [
12513                                      {
12514                                         style: 'position: relative;' // height: 17px;
12515                                     }
12516                                 ]
12517                             }
12518                             
12519                             
12520                         ]
12521                     }
12522                 ]
12523                 
12524             }
12525         };
12526         var cal_rows = function() {
12527             
12528             var ret = []
12529             for (var r = 0; r < 6; r++) {
12530                 var row= {
12531                     tag : 'tr',
12532                     cls : 'fc-week',
12533                     cn : []
12534                 };
12535                 
12536                 for (var i =0; i < Date.dayNames.length; i++) {
12537                     var d = Date.dayNames[i];
12538                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12539
12540                 }
12541                 row.cn[0].cls+=' fc-first';
12542                 row.cn[0].cn[0].style = 'min-height:90px';
12543                 row.cn[6].cls+=' fc-last';
12544                 ret.push(row);
12545                 
12546             }
12547             ret[0].cls += ' fc-first';
12548             ret[4].cls += ' fc-prev-last';
12549             ret[5].cls += ' fc-last';
12550             return ret;
12551             
12552         };
12553         
12554         var cal_table = {
12555             tag: 'table',
12556             cls: 'fc-border-separate',
12557             style : 'width:100%',
12558             cellspacing  : 0,
12559             cn : [
12560                 { 
12561                     tag: 'thead',
12562                     cn : [
12563                         { 
12564                             tag: 'tr',
12565                             cls : 'fc-first fc-last',
12566                             cn : cal_heads()
12567                         }
12568                     ]
12569                 },
12570                 { 
12571                     tag: 'tbody',
12572                     cn : cal_rows()
12573                 }
12574                   
12575             ]
12576         };
12577          
12578          var cfg = {
12579             cls : 'fc fc-ltr',
12580             cn : [
12581                 header,
12582                 {
12583                     cls : 'fc-content',
12584                     style : "position: relative;",
12585                     cn : [
12586                         {
12587                             cls : 'fc-view fc-view-month fc-grid',
12588                             style : 'position: relative',
12589                             unselectable : 'on',
12590                             cn : [
12591                                 {
12592                                     cls : 'fc-event-container',
12593                                     style : 'position:absolute;z-index:8;top:0;left:0;'
12594                                 },
12595                                 cal_table
12596                             ]
12597                         }
12598                     ]
12599     
12600                 }
12601            ] 
12602             
12603         };
12604         
12605          
12606         
12607         return cfg;
12608     },
12609     
12610     
12611     initEvents : function()
12612     {
12613         if(!this.store){
12614             throw "can not find store for calendar";
12615         }
12616         
12617         var mark = {
12618             tag: "div",
12619             cls:"x-dlg-mask",
12620             style: "text-align:center",
12621             cn: [
12622                 {
12623                     tag: "div",
12624                     style: "background-color:white;width:50%;margin:250 auto",
12625                     cn: [
12626                         {
12627                             tag: "img",
12628                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
12629                         },
12630                         {
12631                             tag: "span",
12632                             html: "Loading"
12633                         }
12634                         
12635                     ]
12636                 }
12637             ]
12638         }
12639         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
12640         
12641         var size = this.el.select('.fc-content', true).first().getSize();
12642         this.maskEl.setSize(size.width, size.height);
12643         this.maskEl.enableDisplayMode("block");
12644         if(!this.loadMask){
12645             this.maskEl.hide();
12646         }
12647         
12648         this.store = Roo.factory(this.store, Roo.data);
12649         this.store.on('load', this.onLoad, this);
12650         this.store.on('beforeload', this.onBeforeLoad, this);
12651         
12652         this.resize();
12653         
12654         this.cells = this.el.select('.fc-day',true);
12655         //Roo.log(this.cells);
12656         this.textNodes = this.el.query('.fc-day-number');
12657         this.cells.addClassOnOver('fc-state-hover');
12658         
12659         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
12660         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
12661         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
12662         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
12663         
12664         this.on('monthchange', this.onMonthChange, this);
12665         
12666         this.update(new Date().clearTime());
12667     },
12668     
12669     resize : function() {
12670         var sz  = this.el.getSize();
12671         
12672         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
12673         this.el.select('.fc-day-content div',true).setHeight(34);
12674     },
12675     
12676     
12677     // private
12678     showPrevMonth : function(e){
12679         this.update(this.activeDate.add("mo", -1));
12680     },
12681     showToday : function(e){
12682         this.update(new Date().clearTime());
12683     },
12684     // private
12685     showNextMonth : function(e){
12686         this.update(this.activeDate.add("mo", 1));
12687     },
12688
12689     // private
12690     showPrevYear : function(){
12691         this.update(this.activeDate.add("y", -1));
12692     },
12693
12694     // private
12695     showNextYear : function(){
12696         this.update(this.activeDate.add("y", 1));
12697     },
12698
12699     
12700    // private
12701     update : function(date)
12702     {
12703         var vd = this.activeDate;
12704         this.activeDate = date;
12705 //        if(vd && this.el){
12706 //            var t = date.getTime();
12707 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
12708 //                Roo.log('using add remove');
12709 //                
12710 //                this.fireEvent('monthchange', this, date);
12711 //                
12712 //                this.cells.removeClass("fc-state-highlight");
12713 //                this.cells.each(function(c){
12714 //                   if(c.dateValue == t){
12715 //                       c.addClass("fc-state-highlight");
12716 //                       setTimeout(function(){
12717 //                            try{c.dom.firstChild.focus();}catch(e){}
12718 //                       }, 50);
12719 //                       return false;
12720 //                   }
12721 //                   return true;
12722 //                });
12723 //                return;
12724 //            }
12725 //        }
12726         
12727         var days = date.getDaysInMonth();
12728         
12729         var firstOfMonth = date.getFirstDateOfMonth();
12730         var startingPos = firstOfMonth.getDay()-this.startDay;
12731         
12732         if(startingPos < this.startDay){
12733             startingPos += 7;
12734         }
12735         
12736         var pm = date.add(Date.MONTH, -1);
12737         var prevStart = pm.getDaysInMonth()-startingPos;
12738 //        
12739         this.cells = this.el.select('.fc-day',true);
12740         this.textNodes = this.el.query('.fc-day-number');
12741         this.cells.addClassOnOver('fc-state-hover');
12742         
12743         var cells = this.cells.elements;
12744         var textEls = this.textNodes;
12745         
12746         Roo.each(cells, function(cell){
12747             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
12748         });
12749         
12750         days += startingPos;
12751
12752         // convert everything to numbers so it's fast
12753         var day = 86400000;
12754         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
12755         //Roo.log(d);
12756         //Roo.log(pm);
12757         //Roo.log(prevStart);
12758         
12759         var today = new Date().clearTime().getTime();
12760         var sel = date.clearTime().getTime();
12761         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
12762         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
12763         var ddMatch = this.disabledDatesRE;
12764         var ddText = this.disabledDatesText;
12765         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
12766         var ddaysText = this.disabledDaysText;
12767         var format = this.format;
12768         
12769         var setCellClass = function(cal, cell){
12770             cell.row = 0;
12771             cell.events = [];
12772             cell.more = [];
12773             //Roo.log('set Cell Class');
12774             cell.title = "";
12775             var t = d.getTime();
12776             
12777             //Roo.log(d);
12778             
12779             cell.dateValue = t;
12780             if(t == today){
12781                 cell.className += " fc-today";
12782                 cell.className += " fc-state-highlight";
12783                 cell.title = cal.todayText;
12784             }
12785             if(t == sel){
12786                 // disable highlight in other month..
12787                 //cell.className += " fc-state-highlight";
12788                 
12789             }
12790             // disabling
12791             if(t < min) {
12792                 cell.className = " fc-state-disabled";
12793                 cell.title = cal.minText;
12794                 return;
12795             }
12796             if(t > max) {
12797                 cell.className = " fc-state-disabled";
12798                 cell.title = cal.maxText;
12799                 return;
12800             }
12801             if(ddays){
12802                 if(ddays.indexOf(d.getDay()) != -1){
12803                     cell.title = ddaysText;
12804                     cell.className = " fc-state-disabled";
12805                 }
12806             }
12807             if(ddMatch && format){
12808                 var fvalue = d.dateFormat(format);
12809                 if(ddMatch.test(fvalue)){
12810                     cell.title = ddText.replace("%0", fvalue);
12811                     cell.className = " fc-state-disabled";
12812                 }
12813             }
12814             
12815             if (!cell.initialClassName) {
12816                 cell.initialClassName = cell.dom.className;
12817             }
12818             
12819             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
12820         };
12821
12822         var i = 0;
12823         
12824         for(; i < startingPos; i++) {
12825             textEls[i].innerHTML = (++prevStart);
12826             d.setDate(d.getDate()+1);
12827             
12828             cells[i].className = "fc-past fc-other-month";
12829             setCellClass(this, cells[i]);
12830         }
12831         
12832         var intDay = 0;
12833         
12834         for(; i < days; i++){
12835             intDay = i - startingPos + 1;
12836             textEls[i].innerHTML = (intDay);
12837             d.setDate(d.getDate()+1);
12838             
12839             cells[i].className = ''; // "x-date-active";
12840             setCellClass(this, cells[i]);
12841         }
12842         var extraDays = 0;
12843         
12844         for(; i < 42; i++) {
12845             textEls[i].innerHTML = (++extraDays);
12846             d.setDate(d.getDate()+1);
12847             
12848             cells[i].className = "fc-future fc-other-month";
12849             setCellClass(this, cells[i]);
12850         }
12851         
12852         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
12853         
12854         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
12855         
12856         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
12857         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
12858         
12859         if(totalRows != 6){
12860             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
12861             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
12862         }
12863         
12864         this.fireEvent('monthchange', this, date);
12865         
12866         
12867         /*
12868         if(!this.internalRender){
12869             var main = this.el.dom.firstChild;
12870             var w = main.offsetWidth;
12871             this.el.setWidth(w + this.el.getBorderWidth("lr"));
12872             Roo.fly(main).setWidth(w);
12873             this.internalRender = true;
12874             // opera does not respect the auto grow header center column
12875             // then, after it gets a width opera refuses to recalculate
12876             // without a second pass
12877             if(Roo.isOpera && !this.secondPass){
12878                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
12879                 this.secondPass = true;
12880                 this.update.defer(10, this, [date]);
12881             }
12882         }
12883         */
12884         
12885     },
12886     
12887     findCell : function(dt) {
12888         dt = dt.clearTime().getTime();
12889         var ret = false;
12890         this.cells.each(function(c){
12891             //Roo.log("check " +c.dateValue + '?=' + dt);
12892             if(c.dateValue == dt){
12893                 ret = c;
12894                 return false;
12895             }
12896             return true;
12897         });
12898         
12899         return ret;
12900     },
12901     
12902     findCells : function(ev) {
12903         var s = ev.start.clone().clearTime().getTime();
12904        // Roo.log(s);
12905         var e= ev.end.clone().clearTime().getTime();
12906        // Roo.log(e);
12907         var ret = [];
12908         this.cells.each(function(c){
12909              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
12910             
12911             if(c.dateValue > e){
12912                 return ;
12913             }
12914             if(c.dateValue < s){
12915                 return ;
12916             }
12917             ret.push(c);
12918         });
12919         
12920         return ret;    
12921     },
12922     
12923 //    findBestRow: function(cells)
12924 //    {
12925 //        var ret = 0;
12926 //        
12927 //        for (var i =0 ; i < cells.length;i++) {
12928 //            ret  = Math.max(cells[i].rows || 0,ret);
12929 //        }
12930 //        return ret;
12931 //        
12932 //    },
12933     
12934     
12935     addItem : function(ev)
12936     {
12937         // look for vertical location slot in
12938         var cells = this.findCells(ev);
12939         
12940 //        ev.row = this.findBestRow(cells);
12941         
12942         // work out the location.
12943         
12944         var crow = false;
12945         var rows = [];
12946         for(var i =0; i < cells.length; i++) {
12947             
12948             cells[i].row = cells[0].row;
12949             
12950             if(i == 0){
12951                 cells[i].row = cells[i].row + 1;
12952             }
12953             
12954             if (!crow) {
12955                 crow = {
12956                     start : cells[i],
12957                     end :  cells[i]
12958                 };
12959                 continue;
12960             }
12961             if (crow.start.getY() == cells[i].getY()) {
12962                 // on same row.
12963                 crow.end = cells[i];
12964                 continue;
12965             }
12966             // different row.
12967             rows.push(crow);
12968             crow = {
12969                 start: cells[i],
12970                 end : cells[i]
12971             };
12972             
12973         }
12974         
12975         rows.push(crow);
12976         ev.els = [];
12977         ev.rows = rows;
12978         ev.cells = cells;
12979         
12980         cells[0].events.push(ev);
12981         
12982         this.calevents.push(ev);
12983     },
12984     
12985     clearEvents: function() {
12986         
12987         if(!this.calevents){
12988             return;
12989         }
12990         
12991         Roo.each(this.cells.elements, function(c){
12992             c.row = 0;
12993             c.events = [];
12994             c.more = [];
12995         });
12996         
12997         Roo.each(this.calevents, function(e) {
12998             Roo.each(e.els, function(el) {
12999                 el.un('mouseenter' ,this.onEventEnter, this);
13000                 el.un('mouseleave' ,this.onEventLeave, this);
13001                 el.remove();
13002             },this);
13003         },this);
13004         
13005         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13006             e.remove();
13007         });
13008         
13009     },
13010     
13011     renderEvents: function()
13012     {   
13013         var _this = this;
13014         
13015         this.cells.each(function(c) {
13016             
13017             if(c.row < 5){
13018                 return;
13019             }
13020             
13021             var ev = c.events;
13022             
13023             var r = 4;
13024             if(c.row != c.events.length){
13025                 r = 4 - (4 - (c.row - c.events.length));
13026             }
13027             
13028             c.events = ev.slice(0, r);
13029             c.more = ev.slice(r);
13030             
13031             if(c.more.length && c.more.length == 1){
13032                 c.events.push(c.more.pop());
13033             }
13034             
13035             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13036             
13037         });
13038             
13039         this.cells.each(function(c) {
13040             
13041             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13042             
13043             
13044             for (var e = 0; e < c.events.length; e++){
13045                 var ev = c.events[e];
13046                 var rows = ev.rows;
13047                 
13048                 for(var i = 0; i < rows.length; i++) {
13049                 
13050                     // how many rows should it span..
13051
13052                     var  cfg = {
13053                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13054                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13055
13056                         unselectable : "on",
13057                         cn : [
13058                             {
13059                                 cls: 'fc-event-inner',
13060                                 cn : [
13061     //                                {
13062     //                                  tag:'span',
13063     //                                  cls: 'fc-event-time',
13064     //                                  html : cells.length > 1 ? '' : ev.time
13065     //                                },
13066                                     {
13067                                       tag:'span',
13068                                       cls: 'fc-event-title',
13069                                       html : String.format('{0}', ev.title)
13070                                     }
13071
13072
13073                                 ]
13074                             },
13075                             {
13076                                 cls: 'ui-resizable-handle ui-resizable-e',
13077                                 html : '&nbsp;&nbsp;&nbsp'
13078                             }
13079
13080                         ]
13081                     };
13082
13083                     if (i == 0) {
13084                         cfg.cls += ' fc-event-start';
13085                     }
13086                     if ((i+1) == rows.length) {
13087                         cfg.cls += ' fc-event-end';
13088                     }
13089
13090                     var ctr = _this.el.select('.fc-event-container',true).first();
13091                     var cg = ctr.createChild(cfg);
13092
13093                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13094                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13095
13096                     var r = (c.more.length) ? 1 : 0;
13097                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
13098                     cg.setWidth(ebox.right - sbox.x -2);
13099
13100                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13101                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13102                     cg.on('click', _this.onEventClick, _this, ev);
13103
13104                     ev.els.push(cg);
13105                     
13106                 }
13107                 
13108             }
13109             
13110             
13111             if(c.more.length){
13112                 var  cfg = {
13113                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13114                     style : 'position: absolute',
13115                     unselectable : "on",
13116                     cn : [
13117                         {
13118                             cls: 'fc-event-inner',
13119                             cn : [
13120                                 {
13121                                   tag:'span',
13122                                   cls: 'fc-event-title',
13123                                   html : 'More'
13124                                 }
13125
13126
13127                             ]
13128                         },
13129                         {
13130                             cls: 'ui-resizable-handle ui-resizable-e',
13131                             html : '&nbsp;&nbsp;&nbsp'
13132                         }
13133
13134                     ]
13135                 };
13136
13137                 var ctr = _this.el.select('.fc-event-container',true).first();
13138                 var cg = ctr.createChild(cfg);
13139
13140                 var sbox = c.select('.fc-day-content',true).first().getBox();
13141                 var ebox = c.select('.fc-day-content',true).first().getBox();
13142                 //Roo.log(cg);
13143                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
13144                 cg.setWidth(ebox.right - sbox.x -2);
13145
13146                 cg.on('click', _this.onMoreEventClick, _this, c.more);
13147                 
13148             }
13149             
13150         });
13151         
13152         
13153         
13154     },
13155     
13156     onEventEnter: function (e, el,event,d) {
13157         this.fireEvent('evententer', this, el, event);
13158     },
13159     
13160     onEventLeave: function (e, el,event,d) {
13161         this.fireEvent('eventleave', this, el, event);
13162     },
13163     
13164     onEventClick: function (e, el,event,d) {
13165         this.fireEvent('eventclick', this, el, event);
13166     },
13167     
13168     onMonthChange: function () {
13169         this.store.load();
13170     },
13171     
13172     onMoreEventClick: function(e, el, more)
13173     {
13174         var _this = this;
13175         
13176         this.calpopover.placement = 'right';
13177         this.calpopover.setTitle('More');
13178         
13179         this.calpopover.setContent('');
13180         
13181         var ctr = this.calpopover.el.select('.popover-content', true).first();
13182         
13183         Roo.each(more, function(m){
13184             var cfg = {
13185                 cls : 'fc-event-hori fc-event-draggable',
13186                 html : m.title
13187             }
13188             var cg = ctr.createChild(cfg);
13189             
13190             cg.on('click', _this.onEventClick, _this, m);
13191         });
13192         
13193         this.calpopover.show(el);
13194         
13195         
13196     },
13197     
13198     onLoad: function () 
13199     {   
13200         this.calevents = [];
13201         var cal = this;
13202         
13203         if(this.store.getCount() > 0){
13204             this.store.data.each(function(d){
13205                cal.addItem({
13206                     id : d.data.id,
13207                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13208                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13209                     time : d.data.start_time,
13210                     title : d.data.title,
13211                     description : d.data.description,
13212                     venue : d.data.venue
13213                 });
13214             });
13215         }
13216         
13217         this.renderEvents();
13218         
13219         if(this.calevents.length && this.loadMask){
13220             this.maskEl.hide();
13221         }
13222     },
13223     
13224     onBeforeLoad: function()
13225     {
13226         this.clearEvents();
13227         if(this.loadMask){
13228             this.maskEl.show();
13229         }
13230     }
13231 });
13232
13233  
13234  /*
13235  * - LGPL
13236  *
13237  * element
13238  * 
13239  */
13240
13241 /**
13242  * @class Roo.bootstrap.Popover
13243  * @extends Roo.bootstrap.Component
13244  * Bootstrap Popover class
13245  * @cfg {String} html contents of the popover   (or false to use children..)
13246  * @cfg {String} title of popover (or false to hide)
13247  * @cfg {String} placement how it is placed
13248  * @cfg {String} trigger click || hover (or false to trigger manually)
13249  * @cfg {String} over what (parent or false to trigger manually.)
13250  * 
13251  * @constructor
13252  * Create a new Popover
13253  * @param {Object} config The config object
13254  */
13255
13256 Roo.bootstrap.Popover = function(config){
13257     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13258 };
13259
13260 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
13261     
13262     title: 'Fill in a title',
13263     html: false,
13264     
13265     placement : 'right',
13266     trigger : 'hover', // hover
13267     
13268     over: 'parent',
13269     
13270     can_build_overlaid : false,
13271     
13272     getChildContainer : function()
13273     {
13274         return this.el.select('.popover-content',true).first();
13275     },
13276     
13277     getAutoCreate : function(){
13278          Roo.log('make popover?');
13279         var cfg = {
13280            cls : 'popover roo-dynamic',
13281            style: 'display:block',
13282            cn : [
13283                 {
13284                     cls : 'arrow'
13285                 },
13286                 {
13287                     cls : 'popover-inner',
13288                     cn : [
13289                         {
13290                             tag: 'h3',
13291                             cls: 'popover-title',
13292                             html : this.title
13293                         },
13294                         {
13295                             cls : 'popover-content',
13296                             html : this.html
13297                         }
13298                     ]
13299                     
13300                 }
13301            ]
13302         };
13303         
13304         return cfg;
13305     },
13306     setTitle: function(str)
13307     {
13308         this.el.select('.popover-title',true).first().dom.innerHTML = str;
13309     },
13310     setContent: function(str)
13311     {
13312         this.el.select('.popover-content',true).first().dom.innerHTML = str;
13313     },
13314     // as it get's added to the bottom of the page.
13315     onRender : function(ct, position)
13316     {
13317         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13318         if(!this.el){
13319             var cfg = Roo.apply({},  this.getAutoCreate());
13320             cfg.id = Roo.id();
13321             
13322             if (this.cls) {
13323                 cfg.cls += ' ' + this.cls;
13324             }
13325             if (this.style) {
13326                 cfg.style = this.style;
13327             }
13328             Roo.log("adding to ")
13329             this.el = Roo.get(document.body).createChild(cfg, position);
13330             Roo.log(this.el);
13331         }
13332         this.initEvents();
13333     },
13334     
13335     initEvents : function()
13336     {
13337         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13338         this.el.enableDisplayMode('block');
13339         this.el.hide();
13340         if (this.over === false) {
13341             return; 
13342         }
13343         if (this.triggers === false) {
13344             return;
13345         }
13346         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13347         var triggers = this.trigger ? this.trigger.split(' ') : [];
13348         Roo.each(triggers, function(trigger) {
13349         
13350             if (trigger == 'click') {
13351                 on_el.on('click', this.toggle, this);
13352             } else if (trigger != 'manual') {
13353                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
13354                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13355       
13356                 on_el.on(eventIn  ,this.enter, this);
13357                 on_el.on(eventOut, this.leave, this);
13358             }
13359         }, this);
13360         
13361     },
13362     
13363     
13364     // private
13365     timeout : null,
13366     hoverState : null,
13367     
13368     toggle : function () {
13369         this.hoverState == 'in' ? this.leave() : this.enter();
13370     },
13371     
13372     enter : function () {
13373        
13374     
13375         clearTimeout(this.timeout);
13376     
13377         this.hoverState = 'in'
13378     
13379         if (!this.delay || !this.delay.show) {
13380             this.show();
13381             return 
13382         }
13383         var _t = this;
13384         this.timeout = setTimeout(function () {
13385             if (_t.hoverState == 'in') {
13386                 _t.show();
13387             }
13388         }, this.delay.show)
13389     },
13390     leave : function() {
13391         clearTimeout(this.timeout);
13392     
13393         this.hoverState = 'out'
13394     
13395         if (!this.delay || !this.delay.hide) {
13396             this.hide();
13397             return 
13398         }
13399         var _t = this;
13400         this.timeout = setTimeout(function () {
13401             if (_t.hoverState == 'out') {
13402                 _t.hide();
13403             }
13404         }, this.delay.hide)
13405     },
13406     
13407     show : function (on_el)
13408     {
13409         if (!on_el) {
13410             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13411         }
13412         // set content.
13413         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13414         if (this.html !== false) {
13415             this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13416         }
13417         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13418         if (!this.title.length) {
13419             this.el.select('.popover-title',true).hide();
13420         }
13421         
13422         var placement = typeof this.placement == 'function' ?
13423             this.placement.call(this, this.el, on_el) :
13424             this.placement;
13425             
13426         var autoToken = /\s?auto?\s?/i;
13427         var autoPlace = autoToken.test(placement);
13428         if (autoPlace) {
13429             placement = placement.replace(autoToken, '') || 'top';
13430         }
13431         
13432         //this.el.detach()
13433         //this.el.setXY([0,0]);
13434         this.el.show();
13435         this.el.dom.style.display='block';
13436         this.el.addClass(placement);
13437         
13438         //this.el.appendTo(on_el);
13439         
13440         var p = this.getPosition();
13441         var box = this.el.getBox();
13442         
13443         if (autoPlace) {
13444             // fixme..
13445         }
13446         var align = Roo.bootstrap.Popover.alignment[placement]
13447         this.el.alignTo(on_el, align[0],align[1]);
13448         //var arrow = this.el.select('.arrow',true).first();
13449         //arrow.set(align[2], 
13450         
13451         this.el.addClass('in');
13452         this.hoverState = null;
13453         
13454         if (this.el.hasClass('fade')) {
13455             // fade it?
13456         }
13457         
13458     },
13459     hide : function()
13460     {
13461         this.el.setXY([0,0]);
13462         this.el.removeClass('in');
13463         this.el.hide();
13464         
13465     }
13466     
13467 });
13468
13469 Roo.bootstrap.Popover.alignment = {
13470     'left' : ['r-l', [-10,0], 'right'],
13471     'right' : ['l-r', [10,0], 'left'],
13472     'bottom' : ['t-b', [0,10], 'top'],
13473     'top' : [ 'b-t', [0,-10], 'bottom']
13474 };
13475
13476  /*
13477  * - LGPL
13478  *
13479  * Progress
13480  * 
13481  */
13482
13483 /**
13484  * @class Roo.bootstrap.Progress
13485  * @extends Roo.bootstrap.Component
13486  * Bootstrap Progress class
13487  * @cfg {Boolean} striped striped of the progress bar
13488  * @cfg {Boolean} active animated of the progress bar
13489  * 
13490  * 
13491  * @constructor
13492  * Create a new Progress
13493  * @param {Object} config The config object
13494  */
13495
13496 Roo.bootstrap.Progress = function(config){
13497     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13498 };
13499
13500 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
13501     
13502     striped : false,
13503     active: false,
13504     
13505     getAutoCreate : function(){
13506         var cfg = {
13507             tag: 'div',
13508             cls: 'progress'
13509         };
13510         
13511         
13512         if(this.striped){
13513             cfg.cls += ' progress-striped';
13514         }
13515       
13516         if(this.active){
13517             cfg.cls += ' active';
13518         }
13519         
13520         
13521         return cfg;
13522     }
13523    
13524 });
13525
13526  
13527
13528  /*
13529  * - LGPL
13530  *
13531  * ProgressBar
13532  * 
13533  */
13534
13535 /**
13536  * @class Roo.bootstrap.ProgressBar
13537  * @extends Roo.bootstrap.Component
13538  * Bootstrap ProgressBar class
13539  * @cfg {Number} aria_valuenow aria-value now
13540  * @cfg {Number} aria_valuemin aria-value min
13541  * @cfg {Number} aria_valuemax aria-value max
13542  * @cfg {String} label label for the progress bar
13543  * @cfg {String} panel (success | info | warning | danger )
13544  * @cfg {String} role role of the progress bar
13545  * @cfg {String} sr_only text
13546  * 
13547  * 
13548  * @constructor
13549  * Create a new ProgressBar
13550  * @param {Object} config The config object
13551  */
13552
13553 Roo.bootstrap.ProgressBar = function(config){
13554     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13555 };
13556
13557 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
13558     
13559     aria_valuenow : 0,
13560     aria_valuemin : 0,
13561     aria_valuemax : 100,
13562     label : false,
13563     panel : false,
13564     role : false,
13565     sr_only: false,
13566     
13567     getAutoCreate : function()
13568     {
13569         
13570         var cfg = {
13571             tag: 'div',
13572             cls: 'progress-bar',
13573             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
13574         };
13575         
13576         if(this.sr_only){
13577             cfg.cn = {
13578                 tag: 'span',
13579                 cls: 'sr-only',
13580                 html: this.sr_only
13581             }
13582         }
13583         
13584         if(this.role){
13585             cfg.role = this.role;
13586         }
13587         
13588         if(this.aria_valuenow){
13589             cfg['aria-valuenow'] = this.aria_valuenow;
13590         }
13591         
13592         if(this.aria_valuemin){
13593             cfg['aria-valuemin'] = this.aria_valuemin;
13594         }
13595         
13596         if(this.aria_valuemax){
13597             cfg['aria-valuemax'] = this.aria_valuemax;
13598         }
13599         
13600         if(this.label && !this.sr_only){
13601             cfg.html = this.label;
13602         }
13603         
13604         if(this.panel){
13605             cfg.cls += ' progress-bar-' + this.panel;
13606         }
13607         
13608         return cfg;
13609     },
13610     
13611     update : function(aria_valuenow)
13612     {
13613         this.aria_valuenow = aria_valuenow;
13614         
13615         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
13616     }
13617    
13618 });
13619
13620  
13621
13622  /*
13623  * - LGPL
13624  *
13625  * TabPanel
13626  * 
13627  */
13628
13629 /**
13630  * @class Roo.bootstrap.TabPanel
13631  * @extends Roo.bootstrap.Component
13632  * Bootstrap TabPanel class
13633  * @cfg {Boolean} active panel active
13634  * @cfg {String} html panel content
13635  * @cfg {String} tabId tab relate id
13636  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
13637  * 
13638  * 
13639  * @constructor
13640  * Create a new TabPanel
13641  * @param {Object} config The config object
13642  */
13643
13644 Roo.bootstrap.TabPanel = function(config){
13645     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
13646      this.addEvents({
13647         /**
13648              * @event changed
13649              * Fires when the active status changes
13650              * @param {Roo.bootstrap.TabPanel} this
13651              * @param {Boolean} state the new state
13652             
13653          */
13654         'changed': true
13655      });
13656 };
13657
13658 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
13659     
13660     active: false,
13661     html: false,
13662     tabId: false,
13663     navId : false,
13664     
13665     getAutoCreate : function(){
13666         var cfg = {
13667             tag: 'div',
13668             cls: 'tab-pane',
13669             html: this.html || ''
13670         };
13671         
13672         if(this.active){
13673             cfg.cls += ' active';
13674         }
13675         
13676         if(this.tabId){
13677             cfg.tabId = this.tabId;
13678         }
13679         
13680         return cfg;
13681     },
13682     onRender : function(ct, position)
13683     {
13684        // Roo.log("Call onRender: " + this.xtype);
13685         
13686         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
13687         
13688         if (this.navId && this.tabId) {
13689             var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
13690             if (!item) {
13691                 Roo.log("could not find navID:"  + this.navId + ", tabId: " + this.tabId);
13692             } else {
13693                 item.on('changed', function(item, state) {
13694                     this.setActive(state);
13695                 }, this);
13696             }
13697         }
13698         
13699     },
13700     setActive: function(state)
13701     {
13702         Roo.log("panel - set active " + this.tabId + "=" + state);
13703         
13704         this.active = state;
13705         if (!state) {
13706             this.el.removeClass('active');
13707             
13708         } else  if (!this.el.hasClass('active')) {
13709             this.el.addClass('active');
13710         }
13711         this.fireEvent('changed', this, state);
13712     }
13713     
13714     
13715 });
13716  
13717
13718  
13719
13720  /*
13721  * - LGPL
13722  *
13723  * DateField
13724  * 
13725  */
13726
13727 /**
13728  * @class Roo.bootstrap.DateField
13729  * @extends Roo.bootstrap.Input
13730  * Bootstrap DateField class
13731  * @cfg {Number} weekStart default 0
13732  * @cfg {Number} weekStart default 0
13733  * @cfg {Number} viewMode default empty, (months|years)
13734  * @cfg {Number} minViewMode default empty, (months|years)
13735  * @cfg {Number} startDate default -Infinity
13736  * @cfg {Number} endDate default Infinity
13737  * @cfg {Boolean} todayHighlight default false
13738  * @cfg {Boolean} todayBtn default false
13739  * @cfg {Boolean} calendarWeeks default false
13740  * @cfg {Object} daysOfWeekDisabled default empty
13741  * 
13742  * @cfg {Boolean} keyboardNavigation default true
13743  * @cfg {String} language default en
13744  * 
13745  * @constructor
13746  * Create a new DateField
13747  * @param {Object} config The config object
13748  */
13749
13750 Roo.bootstrap.DateField = function(config){
13751     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
13752      this.addEvents({
13753             /**
13754              * @event show
13755              * Fires when this field show.
13756              * @param {Roo.bootstrap.DateField} this
13757              * @param {Mixed} date The date value
13758              */
13759             show : true,
13760             /**
13761              * @event show
13762              * Fires when this field hide.
13763              * @param {Roo.bootstrap.DateField} this
13764              * @param {Mixed} date The date value
13765              */
13766             hide : true,
13767             /**
13768              * @event select
13769              * Fires when select a date.
13770              * @param {Roo.bootstrap.DateField} this
13771              * @param {Mixed} date The date value
13772              */
13773             select : true
13774         });
13775 };
13776
13777 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
13778     
13779     /**
13780      * @cfg {String} format
13781      * The default date format string which can be overriden for localization support.  The format must be
13782      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
13783      */
13784     format : "m/d/y",
13785     /**
13786      * @cfg {String} altFormats
13787      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
13788      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
13789      */
13790     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
13791     
13792     weekStart : 0,
13793     
13794     viewMode : '',
13795     
13796     minViewMode : '',
13797     
13798     todayHighlight : false,
13799     
13800     todayBtn: false,
13801     
13802     language: 'en',
13803     
13804     keyboardNavigation: true,
13805     
13806     calendarWeeks: false,
13807     
13808     startDate: -Infinity,
13809     
13810     endDate: Infinity,
13811     
13812     daysOfWeekDisabled: [],
13813     
13814     _events: [],
13815     
13816     UTCDate: function()
13817     {
13818         return new Date(Date.UTC.apply(Date, arguments));
13819     },
13820     
13821     UTCToday: function()
13822     {
13823         var today = new Date();
13824         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
13825     },
13826     
13827     getDate: function() {
13828             var d = this.getUTCDate();
13829             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
13830     },
13831     
13832     getUTCDate: function() {
13833             return this.date;
13834     },
13835     
13836     setDate: function(d) {
13837             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
13838     },
13839     
13840     setUTCDate: function(d) {
13841             this.date = d;
13842             this.setValue(this.formatDate(this.date));
13843     },
13844         
13845     onRender: function(ct, position)
13846     {
13847         
13848         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
13849         
13850         this.language = this.language || 'en';
13851         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
13852         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
13853         
13854         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
13855         this.format = this.format || 'm/d/y';
13856         this.isInline = false;
13857         this.isInput = true;
13858         this.component = this.el.select('.add-on', true).first() || false;
13859         this.component = (this.component && this.component.length === 0) ? false : this.component;
13860         this.hasInput = this.component && this.inputEL().length;
13861         
13862         if (typeof(this.minViewMode === 'string')) {
13863             switch (this.minViewMode) {
13864                 case 'months':
13865                     this.minViewMode = 1;
13866                     break;
13867                 case 'years':
13868                     this.minViewMode = 2;
13869                     break;
13870                 default:
13871                     this.minViewMode = 0;
13872                     break;
13873             }
13874         }
13875         
13876         if (typeof(this.viewMode === 'string')) {
13877             switch (this.viewMode) {
13878                 case 'months':
13879                     this.viewMode = 1;
13880                     break;
13881                 case 'years':
13882                     this.viewMode = 2;
13883                     break;
13884                 default:
13885                     this.viewMode = 0;
13886                     break;
13887             }
13888         }
13889                 
13890         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
13891         
13892         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13893         
13894         this.picker().on('mousedown', this.onMousedown, this);
13895         this.picker().on('click', this.onClick, this);
13896         
13897         this.picker().addClass('datepicker-dropdown');
13898         
13899         this.startViewMode = this.viewMode;
13900         
13901         
13902         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
13903             if(!this.calendarWeeks){
13904                 v.remove();
13905                 return;
13906             };
13907             
13908             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
13909             v.attr('colspan', function(i, val){
13910                 return parseInt(val) + 1;
13911             });
13912         })
13913                         
13914         
13915         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
13916         
13917         this.setStartDate(this.startDate);
13918         this.setEndDate(this.endDate);
13919         
13920         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
13921         
13922         this.fillDow();
13923         this.fillMonths();
13924         this.update();
13925         this.showMode();
13926         
13927         if(this.isInline) {
13928             this.show();
13929         }
13930     },
13931     
13932     picker : function()
13933     {
13934         return this.el.select('.datepicker', true).first();
13935     },
13936     
13937     fillDow: function()
13938     {
13939         var dowCnt = this.weekStart;
13940         
13941         var dow = {
13942             tag: 'tr',
13943             cn: [
13944                 
13945             ]
13946         };
13947         
13948         if(this.calendarWeeks){
13949             dow.cn.push({
13950                 tag: 'th',
13951                 cls: 'cw',
13952                 html: '&nbsp;'
13953             })
13954         }
13955         
13956         while (dowCnt < this.weekStart + 7) {
13957             dow.cn.push({
13958                 tag: 'th',
13959                 cls: 'dow',
13960                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
13961             });
13962         }
13963         
13964         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
13965     },
13966     
13967     fillMonths: function()
13968     {    
13969         var i = 0
13970         var months = this.picker().select('>.datepicker-months td', true).first();
13971         
13972         months.dom.innerHTML = '';
13973         
13974         while (i < 12) {
13975             var month = {
13976                 tag: 'span',
13977                 cls: 'month',
13978                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
13979             }
13980             
13981             months.createChild(month);
13982         }
13983         
13984     },
13985     
13986     update: function()
13987     {
13988         
13989         this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
13990         
13991         if (this.date < this.startDate) {
13992             this.viewDate = new Date(this.startDate);
13993         } else if (this.date > this.endDate) {
13994             this.viewDate = new Date(this.endDate);
13995         } else {
13996             this.viewDate = new Date(this.date);
13997         }
13998         
13999         this.fill();
14000     },
14001     
14002     fill: function() 
14003     {
14004         var d = new Date(this.viewDate),
14005                 year = d.getUTCFullYear(),
14006                 month = d.getUTCMonth(),
14007                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14008                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14009                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14010                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14011                 currentDate = this.date && this.date.valueOf(),
14012                 today = this.UTCToday();
14013         
14014         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14015         
14016 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14017         
14018 //        this.picker.select('>tfoot th.today').
14019 //                                              .text(dates[this.language].today)
14020 //                                              .toggle(this.todayBtn !== false);
14021     
14022         this.updateNavArrows();
14023         this.fillMonths();
14024                                                 
14025         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14026         
14027         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14028          
14029         prevMonth.setUTCDate(day);
14030         
14031         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14032         
14033         var nextMonth = new Date(prevMonth);
14034         
14035         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14036         
14037         nextMonth = nextMonth.valueOf();
14038         
14039         var fillMonths = false;
14040         
14041         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14042         
14043         while(prevMonth.valueOf() < nextMonth) {
14044             var clsName = '';
14045             
14046             if (prevMonth.getUTCDay() === this.weekStart) {
14047                 if(fillMonths){
14048                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14049                 }
14050                     
14051                 fillMonths = {
14052                     tag: 'tr',
14053                     cn: []
14054                 };
14055                 
14056                 if(this.calendarWeeks){
14057                     // ISO 8601: First week contains first thursday.
14058                     // ISO also states week starts on Monday, but we can be more abstract here.
14059                     var
14060                     // Start of current week: based on weekstart/current date
14061                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14062                     // Thursday of this week
14063                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14064                     // First Thursday of year, year from thursday
14065                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14066                     // Calendar week: ms between thursdays, div ms per day, div 7 days
14067                     calWeek =  (th - yth) / 864e5 / 7 + 1;
14068                     
14069                     fillMonths.cn.push({
14070                         tag: 'td',
14071                         cls: 'cw',
14072                         html: calWeek
14073                     });
14074                 }
14075             }
14076             
14077             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14078                 clsName += ' old';
14079             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14080                 clsName += ' new';
14081             }
14082             if (this.todayHighlight &&
14083                 prevMonth.getUTCFullYear() == today.getFullYear() &&
14084                 prevMonth.getUTCMonth() == today.getMonth() &&
14085                 prevMonth.getUTCDate() == today.getDate()) {
14086                 clsName += ' today';
14087             }
14088             
14089             if (currentDate && prevMonth.valueOf() === currentDate) {
14090                 clsName += ' active';
14091             }
14092             
14093             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14094                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14095                     clsName += ' disabled';
14096             }
14097             
14098             fillMonths.cn.push({
14099                 tag: 'td',
14100                 cls: 'day ' + clsName,
14101                 html: prevMonth.getDate()
14102             })
14103             
14104             prevMonth.setDate(prevMonth.getDate()+1);
14105         }
14106           
14107         var currentYear = this.date && this.date.getUTCFullYear();
14108         var currentMonth = this.date && this.date.getUTCMonth();
14109         
14110         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14111         
14112         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14113             v.removeClass('active');
14114             
14115             if(currentYear === year && k === currentMonth){
14116                 v.addClass('active');
14117             }
14118             
14119             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14120                 v.addClass('disabled');
14121             }
14122             
14123         });
14124         
14125         
14126         year = parseInt(year/10, 10) * 10;
14127         
14128         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14129         
14130         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14131         
14132         year -= 1;
14133         for (var i = -1; i < 11; i++) {
14134             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14135                 tag: 'span',
14136                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14137                 html: year
14138             })
14139             
14140             year += 1;
14141         }
14142     },
14143     
14144     showMode: function(dir) 
14145     {
14146         if (dir) {
14147             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14148         }
14149         Roo.each(this.picker().select('>div',true).elements, function(v){
14150             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14151             v.hide();
14152         });
14153         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14154     },
14155     
14156     place: function()
14157     {
14158         if(this.isInline) return;
14159         
14160         this.picker().removeClass(['bottom', 'top']);
14161         
14162         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14163             /*
14164              * place to the top of element!
14165              *
14166              */
14167             
14168             this.picker().addClass('top');
14169             this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14170             
14171             return;
14172         }
14173         
14174         this.picker().addClass('bottom');
14175         
14176         this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
14177     },
14178     
14179     parseDate : function(value)
14180     {
14181         if(!value || value instanceof Date){
14182             return value;
14183         }
14184         var v = Date.parseDate(value, this.format);
14185         if (!v && this.useIso) {
14186             v = Date.parseDate(value, 'Y-m-d');
14187         }
14188         if(!v && this.altFormats){
14189             if(!this.altFormatsArray){
14190                 this.altFormatsArray = this.altFormats.split("|");
14191             }
14192             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14193                 v = Date.parseDate(value, this.altFormatsArray[i]);
14194             }
14195         }
14196         return v;
14197     },
14198     
14199     formatDate : function(date, fmt)
14200     {
14201         return (!date || !(date instanceof Date)) ?
14202         date : date.dateFormat(fmt || this.format);
14203     },
14204     
14205     onFocus : function()
14206     {
14207         Roo.bootstrap.DateField.superclass.onFocus.call(this);
14208         this.show();
14209     },
14210     
14211     onBlur : function()
14212     {
14213         Roo.bootstrap.DateField.superclass.onBlur.call(this);
14214         
14215         var d = this.inputEl().getValue();
14216         
14217         if(d && d.length){
14218             this.setValue(d);
14219         }
14220                 
14221         this.hide();
14222     },
14223     
14224     show : function()
14225     {
14226         this.picker().show();
14227         this.update();
14228         this.place();
14229         
14230         this.fireEvent('show', this, this.date);
14231     },
14232     
14233     hide : function()
14234     {
14235         if(this.isInline) return;
14236         this.picker().hide();
14237         this.viewMode = this.startViewMode;
14238         this.showMode();
14239         
14240         this.fireEvent('hide', this, this.date);
14241         
14242     },
14243     
14244     onMousedown: function(e)
14245     {
14246         e.stopPropagation();
14247         e.preventDefault();
14248     },
14249     
14250     keyup: function(e)
14251     {
14252         Roo.bootstrap.DateField.superclass.keyup.call(this);
14253         this.update();
14254     },
14255
14256     setValue: function(v)
14257     {
14258         Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14259         
14260         var d = new Date(v);
14261         
14262         if(isNaN(d.getTime())){
14263             return;
14264         }
14265         
14266         this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14267
14268         this.update();
14269
14270         this.fireEvent('select', this, this.date);
14271         
14272     },
14273     
14274     getValue: function()
14275     {
14276         return this.formatDate(this.date);
14277     },
14278     
14279     fireKey: function(e)
14280     {
14281         if (!this.picker().isVisible()){
14282             if (e.keyCode == 27) // allow escape to hide and re-show picker
14283                 this.show();
14284             return;
14285         }
14286         var dateChanged = false,
14287         dir, day, month,
14288         newDate, newViewDate;
14289         
14290         switch(e.keyCode){
14291             case 27: // escape
14292                 this.hide();
14293                 e.preventDefault();
14294                 break;
14295             case 37: // left
14296             case 39: // right
14297                 if (!this.keyboardNavigation) break;
14298                 dir = e.keyCode == 37 ? -1 : 1;
14299                 
14300                 if (e.ctrlKey){
14301                     newDate = this.moveYear(this.date, dir);
14302                     newViewDate = this.moveYear(this.viewDate, dir);
14303                 } else if (e.shiftKey){
14304                     newDate = this.moveMonth(this.date, dir);
14305                     newViewDate = this.moveMonth(this.viewDate, dir);
14306                 } else {
14307                     newDate = new Date(this.date);
14308                     newDate.setUTCDate(this.date.getUTCDate() + dir);
14309                     newViewDate = new Date(this.viewDate);
14310                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14311                 }
14312                 if (this.dateWithinRange(newDate)){
14313                     this.date = newDate;
14314                     this.viewDate = newViewDate;
14315                     this.setValue(this.formatDate(this.date));
14316 //                    this.update();
14317                     e.preventDefault();
14318                     dateChanged = true;
14319                 }
14320                 break;
14321             case 38: // up
14322             case 40: // down
14323                 if (!this.keyboardNavigation) break;
14324                 dir = e.keyCode == 38 ? -1 : 1;
14325                 if (e.ctrlKey){
14326                     newDate = this.moveYear(this.date, dir);
14327                     newViewDate = this.moveYear(this.viewDate, dir);
14328                 } else if (e.shiftKey){
14329                     newDate = this.moveMonth(this.date, dir);
14330                     newViewDate = this.moveMonth(this.viewDate, dir);
14331                 } else {
14332                     newDate = new Date(this.date);
14333                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
14334                     newViewDate = new Date(this.viewDate);
14335                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
14336                 }
14337                 if (this.dateWithinRange(newDate)){
14338                     this.date = newDate;
14339                     this.viewDate = newViewDate;
14340                     this.setValue(this.formatDate(this.date));
14341 //                    this.update();
14342                     e.preventDefault();
14343                     dateChanged = true;
14344                 }
14345                 break;
14346             case 13: // enter
14347                 this.setValue(this.formatDate(this.date));
14348                 this.hide();
14349                 e.preventDefault();
14350                 break;
14351             case 9: // tab
14352                 this.setValue(this.formatDate(this.date));
14353                 this.hide();
14354                 break;
14355                 
14356         }
14357     },
14358     
14359     
14360     onClick: function(e) 
14361     {
14362         e.stopPropagation();
14363         e.preventDefault();
14364         
14365         var target = e.getTarget();
14366         
14367         if(target.nodeName.toLowerCase() === 'i'){
14368             target = Roo.get(target).dom.parentNode;
14369         }
14370         
14371         var nodeName = target.nodeName;
14372         var className = target.className;
14373         var html = target.innerHTML;
14374         
14375         switch(nodeName.toLowerCase()) {
14376             case 'th':
14377                 switch(className) {
14378                     case 'switch':
14379                         this.showMode(1);
14380                         break;
14381                     case 'prev':
14382                     case 'next':
14383                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
14384                         switch(this.viewMode){
14385                                 case 0:
14386                                         this.viewDate = this.moveMonth(this.viewDate, dir);
14387                                         break;
14388                                 case 1:
14389                                 case 2:
14390                                         this.viewDate = this.moveYear(this.viewDate, dir);
14391                                         break;
14392                         }
14393                         this.fill();
14394                         break;
14395                     case 'today':
14396                         var date = new Date();
14397                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
14398 //                        this.fill()
14399                         this.setValue(this.formatDate(this.date));
14400                         
14401                         this.hide();
14402                         break;
14403                 }
14404                 break;
14405             case 'span':
14406                 if (className.indexOf('disabled') === -1) {
14407                     this.viewDate.setUTCDate(1);
14408                     if (className.indexOf('month') !== -1) {
14409                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
14410                     } else {
14411                         var year = parseInt(html, 10) || 0;
14412                         this.viewDate.setUTCFullYear(year);
14413                         
14414                     }
14415                     this.showMode(-1);
14416                     this.fill();
14417                 }
14418                 break;
14419                 
14420             case 'td':
14421                 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
14422                     var day = parseInt(html, 10) || 1;
14423                     var year = this.viewDate.getUTCFullYear(),
14424                         month = this.viewDate.getUTCMonth();
14425
14426                     if (className.indexOf('old') !== -1) {
14427                         if(month === 0 ){
14428                             month = 11;
14429                             year -= 1;
14430                         }else{
14431                             month -= 1;
14432                         }
14433                     } else if (className.indexOf('new') !== -1) {
14434                         if (month == 11) {
14435                             month = 0;
14436                             year += 1;
14437                         } else {
14438                             month += 1;
14439                         }
14440                     }
14441                     this.date = this.UTCDate(year, month, day,0,0,0,0);
14442                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
14443 //                    this.fill();
14444                     this.setValue(this.formatDate(this.date));
14445                     this.hide();
14446                 }
14447                 break;
14448         }
14449     },
14450     
14451     setStartDate: function(startDate)
14452     {
14453         this.startDate = startDate || -Infinity;
14454         if (this.startDate !== -Infinity) {
14455             this.startDate = this.parseDate(this.startDate);
14456         }
14457         this.update();
14458         this.updateNavArrows();
14459     },
14460
14461     setEndDate: function(endDate)
14462     {
14463         this.endDate = endDate || Infinity;
14464         if (this.endDate !== Infinity) {
14465             this.endDate = this.parseDate(this.endDate);
14466         }
14467         this.update();
14468         this.updateNavArrows();
14469     },
14470     
14471     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
14472     {
14473         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
14474         if (typeof(this.daysOfWeekDisabled) !== 'object') {
14475             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
14476         }
14477         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
14478             return parseInt(d, 10);
14479         });
14480         this.update();
14481         this.updateNavArrows();
14482     },
14483     
14484     updateNavArrows: function() 
14485     {
14486         var d = new Date(this.viewDate),
14487         year = d.getUTCFullYear(),
14488         month = d.getUTCMonth();
14489         
14490         Roo.each(this.picker().select('.prev', true).elements, function(v){
14491             v.show();
14492             switch (this.viewMode) {
14493                 case 0:
14494
14495                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
14496                         v.hide();
14497                     }
14498                     break;
14499                 case 1:
14500                 case 2:
14501                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
14502                         v.hide();
14503                     }
14504                     break;
14505             }
14506         });
14507         
14508         Roo.each(this.picker().select('.next', true).elements, function(v){
14509             v.show();
14510             switch (this.viewMode) {
14511                 case 0:
14512
14513                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
14514                         v.hide();
14515                     }
14516                     break;
14517                 case 1:
14518                 case 2:
14519                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
14520                         v.hide();
14521                     }
14522                     break;
14523             }
14524         })
14525     },
14526     
14527     moveMonth: function(date, dir)
14528     {
14529         if (!dir) return date;
14530         var new_date = new Date(date.valueOf()),
14531         day = new_date.getUTCDate(),
14532         month = new_date.getUTCMonth(),
14533         mag = Math.abs(dir),
14534         new_month, test;
14535         dir = dir > 0 ? 1 : -1;
14536         if (mag == 1){
14537             test = dir == -1
14538             // If going back one month, make sure month is not current month
14539             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
14540             ? function(){
14541                 return new_date.getUTCMonth() == month;
14542             }
14543             // If going forward one month, make sure month is as expected
14544             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
14545             : function(){
14546                 return new_date.getUTCMonth() != new_month;
14547             };
14548             new_month = month + dir;
14549             new_date.setUTCMonth(new_month);
14550             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
14551             if (new_month < 0 || new_month > 11)
14552                 new_month = (new_month + 12) % 12;
14553         } else {
14554             // For magnitudes >1, move one month at a time...
14555             for (var i=0; i<mag; i++)
14556                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
14557                 new_date = this.moveMonth(new_date, dir);
14558             // ...then reset the day, keeping it in the new month
14559             new_month = new_date.getUTCMonth();
14560             new_date.setUTCDate(day);
14561             test = function(){
14562                 return new_month != new_date.getUTCMonth();
14563             };
14564         }
14565         // Common date-resetting loop -- if date is beyond end of month, make it
14566         // end of month
14567         while (test()){
14568             new_date.setUTCDate(--day);
14569             new_date.setUTCMonth(new_month);
14570         }
14571         return new_date;
14572     },
14573
14574     moveYear: function(date, dir)
14575     {
14576         return this.moveMonth(date, dir*12);
14577     },
14578
14579     dateWithinRange: function(date)
14580     {
14581         return date >= this.startDate && date <= this.endDate;
14582     },
14583
14584     
14585     remove: function() 
14586     {
14587         this.picker().remove();
14588     }
14589    
14590 });
14591
14592 Roo.apply(Roo.bootstrap.DateField,  {
14593     
14594     head : {
14595         tag: 'thead',
14596         cn: [
14597         {
14598             tag: 'tr',
14599             cn: [
14600             {
14601                 tag: 'th',
14602                 cls: 'prev',
14603                 html: '<i class="fa fa-arrow-left"/>'
14604             },
14605             {
14606                 tag: 'th',
14607                 cls: 'switch',
14608                 colspan: '5'
14609             },
14610             {
14611                 tag: 'th',
14612                 cls: 'next',
14613                 html: '<i class="fa fa-arrow-right"/>'
14614             }
14615
14616             ]
14617         }
14618         ]
14619     },
14620     
14621     content : {
14622         tag: 'tbody',
14623         cn: [
14624         {
14625             tag: 'tr',
14626             cn: [
14627             {
14628                 tag: 'td',
14629                 colspan: '7'
14630             }
14631             ]
14632         }
14633         ]
14634     },
14635     
14636     footer : {
14637         tag: 'tfoot',
14638         cn: [
14639         {
14640             tag: 'tr',
14641             cn: [
14642             {
14643                 tag: 'th',
14644                 colspan: '7',
14645                 cls: 'today'
14646             }
14647                     
14648             ]
14649         }
14650         ]
14651     },
14652     
14653     dates:{
14654         en: {
14655             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
14656             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
14657             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
14658             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
14659             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
14660             today: "Today"
14661         }
14662     },
14663     
14664     modes: [
14665     {
14666         clsName: 'days',
14667         navFnc: 'Month',
14668         navStep: 1
14669     },
14670     {
14671         clsName: 'months',
14672         navFnc: 'FullYear',
14673         navStep: 1
14674     },
14675     {
14676         clsName: 'years',
14677         navFnc: 'FullYear',
14678         navStep: 10
14679     }]
14680 });
14681
14682 Roo.apply(Roo.bootstrap.DateField,  {
14683   
14684     template : {
14685         tag: 'div',
14686         cls: 'datepicker dropdown-menu',
14687         cn: [
14688         {
14689             tag: 'div',
14690             cls: 'datepicker-days',
14691             cn: [
14692             {
14693                 tag: 'table',
14694                 cls: 'table-condensed',
14695                 cn:[
14696                 Roo.bootstrap.DateField.head,
14697                 {
14698                     tag: 'tbody'
14699                 },
14700                 Roo.bootstrap.DateField.footer
14701                 ]
14702             }
14703             ]
14704         },
14705         {
14706             tag: 'div',
14707             cls: 'datepicker-months',
14708             cn: [
14709             {
14710                 tag: 'table',
14711                 cls: 'table-condensed',
14712                 cn:[
14713                 Roo.bootstrap.DateField.head,
14714                 Roo.bootstrap.DateField.content,
14715                 Roo.bootstrap.DateField.footer
14716                 ]
14717             }
14718             ]
14719         },
14720         {
14721             tag: 'div',
14722             cls: 'datepicker-years',
14723             cn: [
14724             {
14725                 tag: 'table',
14726                 cls: 'table-condensed',
14727                 cn:[
14728                 Roo.bootstrap.DateField.head,
14729                 Roo.bootstrap.DateField.content,
14730                 Roo.bootstrap.DateField.footer
14731                 ]
14732             }
14733             ]
14734         }
14735         ]
14736     }
14737 });
14738
14739  
14740
14741  /*
14742  * - LGPL
14743  *
14744  * TimeField
14745  * 
14746  */
14747
14748 /**
14749  * @class Roo.bootstrap.TimeField
14750  * @extends Roo.bootstrap.Input
14751  * Bootstrap DateField class
14752  * 
14753  * 
14754  * @constructor
14755  * Create a new TimeField
14756  * @param {Object} config The config object
14757  */
14758
14759 Roo.bootstrap.TimeField = function(config){
14760     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
14761     this.addEvents({
14762             /**
14763              * @event show
14764              * Fires when this field show.
14765              * @param {Roo.bootstrap.DateField} this
14766              * @param {Mixed} date The date value
14767              */
14768             show : true,
14769             /**
14770              * @event show
14771              * Fires when this field hide.
14772              * @param {Roo.bootstrap.DateField} this
14773              * @param {Mixed} date The date value
14774              */
14775             hide : true,
14776             /**
14777              * @event select
14778              * Fires when select a date.
14779              * @param {Roo.bootstrap.DateField} this
14780              * @param {Mixed} date The date value
14781              */
14782             select : true
14783         });
14784 };
14785
14786 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
14787     
14788     /**
14789      * @cfg {String} format
14790      * The default time format string which can be overriden for localization support.  The format must be
14791      * valid according to {@link Date#parseDate} (defaults to 'H:i').
14792      */
14793     format : "H:i",
14794        
14795     onRender: function(ct, position)
14796     {
14797         
14798         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
14799                 
14800         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
14801         
14802         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14803         
14804         this.pop = this.picker().select('>.datepicker-time',true).first();
14805         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block' 
14806         
14807         this.picker().on('mousedown', this.onMousedown, this);
14808         this.picker().on('click', this.onClick, this);
14809         
14810         this.picker().addClass('datepicker-dropdown');
14811     
14812         this.fillTime();
14813         this.update();
14814             
14815         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
14816         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
14817         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
14818         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
14819         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
14820         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
14821
14822     },
14823     
14824     fireKey: function(e){
14825         if (!this.picker().isVisible()){
14826             if (e.keyCode == 27) // allow escape to hide and re-show picker
14827                 this.show();
14828             return;
14829         }
14830
14831         e.preventDefault();
14832         
14833         switch(e.keyCode){
14834             case 27: // escape
14835                 this.hide();
14836                 break;
14837             case 37: // left
14838             case 39: // right
14839                 this.onTogglePeriod();
14840                 break;
14841             case 38: // up
14842                 this.onIncrementMinutes();
14843                 break;
14844             case 40: // down
14845                 this.onDecrementMinutes();
14846                 break;
14847             case 13: // enter
14848             case 9: // tab
14849                 this.setTime();
14850                 break;
14851         }
14852     },
14853     
14854     onClick: function(e) {
14855         e.stopPropagation();
14856         e.preventDefault();
14857     },
14858     
14859     picker : function()
14860     {
14861         return this.el.select('.datepicker', true).first();
14862     },
14863     
14864     fillTime: function()
14865     {    
14866         var time = this.pop.select('tbody', true).first();
14867         
14868         time.dom.innerHTML = '';
14869         
14870         time.createChild({
14871             tag: 'tr',
14872             cn: [
14873                 {
14874                     tag: 'td',
14875                     cn: [
14876                         {
14877                             tag: 'a',
14878                             href: '#',
14879                             cls: 'btn',
14880                             cn: [
14881                                 {
14882                                     tag: 'span',
14883                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
14884                                 }
14885                             ]
14886                         } 
14887                     ]
14888                 },
14889                 {
14890                     tag: 'td',
14891                     cls: 'separator'
14892                 },
14893                 {
14894                     tag: 'td',
14895                     cn: [
14896                         {
14897                             tag: 'a',
14898                             href: '#',
14899                             cls: 'btn',
14900                             cn: [
14901                                 {
14902                                     tag: 'span',
14903                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
14904                                 }
14905                             ]
14906                         }
14907                     ]
14908                 },
14909                 {
14910                     tag: 'td',
14911                     cls: 'separator'
14912                 }
14913             ]
14914         });
14915         
14916         time.createChild({
14917             tag: 'tr',
14918             cn: [
14919                 {
14920                     tag: 'td',
14921                     cn: [
14922                         {
14923                             tag: 'span',
14924                             cls: 'timepicker-hour',
14925                             html: '00'
14926                         }  
14927                     ]
14928                 },
14929                 {
14930                     tag: 'td',
14931                     cls: 'separator',
14932                     html: ':'
14933                 },
14934                 {
14935                     tag: 'td',
14936                     cn: [
14937                         {
14938                             tag: 'span',
14939                             cls: 'timepicker-minute',
14940                             html: '00'
14941                         }  
14942                     ]
14943                 },
14944                 {
14945                     tag: 'td',
14946                     cls: 'separator'
14947                 },
14948                 {
14949                     tag: 'td',
14950                     cn: [
14951                         {
14952                             tag: 'button',
14953                             type: 'button',
14954                             cls: 'btn btn-primary period',
14955                             html: 'AM'
14956                             
14957                         }
14958                     ]
14959                 }
14960             ]
14961         });
14962         
14963         time.createChild({
14964             tag: 'tr',
14965             cn: [
14966                 {
14967                     tag: 'td',
14968                     cn: [
14969                         {
14970                             tag: 'a',
14971                             href: '#',
14972                             cls: 'btn',
14973                             cn: [
14974                                 {
14975                                     tag: 'span',
14976                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
14977                                 }
14978                             ]
14979                         }
14980                     ]
14981                 },
14982                 {
14983                     tag: 'td',
14984                     cls: 'separator'
14985                 },
14986                 {
14987                     tag: 'td',
14988                     cn: [
14989                         {
14990                             tag: 'a',
14991                             href: '#',
14992                             cls: 'btn',
14993                             cn: [
14994                                 {
14995                                     tag: 'span',
14996                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
14997                                 }
14998                             ]
14999                         }
15000                     ]
15001                 },
15002                 {
15003                     tag: 'td',
15004                     cls: 'separator'
15005                 }
15006             ]
15007         });
15008         
15009     },
15010     
15011     update: function()
15012     {
15013         
15014         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15015         
15016         this.fill();
15017     },
15018     
15019     fill: function() 
15020     {
15021         var hours = this.time.getHours();
15022         var minutes = this.time.getMinutes();
15023         var period = 'AM';
15024         
15025         if(hours > 11){
15026             period = 'PM';
15027         }
15028         
15029         if(hours == 0){
15030             hours = 12;
15031         }
15032         
15033         
15034         if(hours > 12){
15035             hours = hours - 12;
15036         }
15037         
15038         if(hours < 10){
15039             hours = '0' + hours;
15040         }
15041         
15042         if(minutes < 10){
15043             minutes = '0' + minutes;
15044         }
15045         
15046         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15047         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15048         this.pop.select('button', true).first().dom.innerHTML = period;
15049         
15050     },
15051     
15052     place: function()
15053     {   
15054         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15055         
15056         var cls = ['bottom'];
15057         
15058         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15059             cls.pop();
15060             cls.push('top');
15061         }
15062         
15063         cls.push('right');
15064         
15065         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15066             cls.pop();
15067             cls.push('left');
15068         }
15069         
15070         this.picker().addClass(cls.join('-'));
15071         
15072         var _this = this;
15073         
15074         Roo.each(cls, function(c){
15075             if(c == 'bottom'){
15076                 _this.picker().setTop(_this.inputEl().getHeight());
15077                 return;
15078             }
15079             if(c == 'top'){
15080                 _this.picker().setTop(0 - _this.picker().getHeight());
15081                 return;
15082             }
15083             
15084             if(c == 'left'){
15085                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15086                 return;
15087             }
15088             if(c == 'right'){
15089                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15090                 return;
15091             }
15092         });
15093         
15094     },
15095   
15096     onFocus : function()
15097     {
15098         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15099         this.show();
15100     },
15101     
15102     onBlur : function()
15103     {
15104         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15105         this.hide();
15106     },
15107     
15108     show : function()
15109     {
15110         this.picker().show();
15111         this.pop.show();
15112         this.update();
15113         this.place();
15114         
15115         this.fireEvent('show', this, this.date);
15116     },
15117     
15118     hide : function()
15119     {
15120         this.picker().hide();
15121         this.pop.hide();
15122         
15123         this.fireEvent('hide', this, this.date);
15124     },
15125     
15126     setTime : function()
15127     {
15128         this.hide();
15129         this.setValue(this.time.format(this.format));
15130         
15131         this.fireEvent('select', this, this.date);
15132         
15133         
15134     },
15135     
15136     onMousedown: function(e){
15137         e.stopPropagation();
15138         e.preventDefault();
15139     },
15140     
15141     onIncrementHours: function()
15142     {
15143         Roo.log('onIncrementHours');
15144         this.time = this.time.add(Date.HOUR, 1);
15145         this.update();
15146         
15147     },
15148     
15149     onDecrementHours: function()
15150     {
15151         Roo.log('onDecrementHours');
15152         this.time = this.time.add(Date.HOUR, -1);
15153         this.update();
15154     },
15155     
15156     onIncrementMinutes: function()
15157     {
15158         Roo.log('onIncrementMinutes');
15159         this.time = this.time.add(Date.MINUTE, 1);
15160         this.update();
15161     },
15162     
15163     onDecrementMinutes: function()
15164     {
15165         Roo.log('onDecrementMinutes');
15166         this.time = this.time.add(Date.MINUTE, -1);
15167         this.update();
15168     },
15169     
15170     onTogglePeriod: function()
15171     {
15172         Roo.log('onTogglePeriod');
15173         this.time = this.time.add(Date.HOUR, 12);
15174         this.update();
15175     }
15176     
15177    
15178 });
15179
15180 Roo.apply(Roo.bootstrap.TimeField,  {
15181     
15182     content : {
15183         tag: 'tbody',
15184         cn: [
15185             {
15186                 tag: 'tr',
15187                 cn: [
15188                 {
15189                     tag: 'td',
15190                     colspan: '7'
15191                 }
15192                 ]
15193             }
15194         ]
15195     },
15196     
15197     footer : {
15198         tag: 'tfoot',
15199         cn: [
15200             {
15201                 tag: 'tr',
15202                 cn: [
15203                 {
15204                     tag: 'th',
15205                     colspan: '7',
15206                     cls: '',
15207                     cn: [
15208                         {
15209                             tag: 'button',
15210                             cls: 'btn btn-info ok',
15211                             html: 'OK'
15212                         }
15213                     ]
15214                 }
15215
15216                 ]
15217             }
15218         ]
15219     }
15220 });
15221
15222 Roo.apply(Roo.bootstrap.TimeField,  {
15223   
15224     template : {
15225         tag: 'div',
15226         cls: 'datepicker dropdown-menu',
15227         cn: [
15228             {
15229                 tag: 'div',
15230                 cls: 'datepicker-time',
15231                 cn: [
15232                 {
15233                     tag: 'table',
15234                     cls: 'table-condensed',
15235                     cn:[
15236                     Roo.bootstrap.TimeField.content,
15237                     Roo.bootstrap.TimeField.footer
15238                     ]
15239                 }
15240                 ]
15241             }
15242         ]
15243     }
15244 });
15245
15246  
15247
15248  /*
15249  * - LGPL
15250  *
15251  * CheckBox
15252  * 
15253  */
15254
15255 /**
15256  * @class Roo.bootstrap.CheckBox
15257  * @extends Roo.bootstrap.Input
15258  * Bootstrap CheckBox class
15259  * 
15260  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15261  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15262  * @cfg {String} boxLabel The text that appears beside the checkbox
15263  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15264  * @cfg {Boolean} checked initnal the element
15265  * 
15266  * 
15267  * @constructor
15268  * Create a new CheckBox
15269  * @param {Object} config The config object
15270  */
15271
15272 Roo.bootstrap.CheckBox = function(config){
15273     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15274    
15275         this.addEvents({
15276             /**
15277             * @event check
15278             * Fires when the element is checked or unchecked.
15279             * @param {Roo.bootstrap.CheckBox} this This input
15280             * @param {Boolean} checked The new checked value
15281             */
15282            check : true
15283         });
15284 };
15285
15286 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
15287     
15288     inputType: 'checkbox',
15289     inputValue: 1,
15290     valueOff: 0,
15291     boxLabel: false,
15292     checked: false,
15293     weight : false,
15294     
15295     getAutoCreate : function()
15296     {
15297         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15298         
15299         var id = Roo.id();
15300         
15301         var cfg = {};
15302         
15303         cfg.cls = 'form-group checkbox' //input-group
15304         
15305         
15306         
15307         
15308         var input =  {
15309             tag: 'input',
15310             id : id,
15311             type : this.inputType,
15312             value : (!this.checked) ? this.valueOff : this.inputValue,
15313             cls : 'roo-checkbox', //'form-box',
15314             placeholder : this.placeholder || ''
15315             
15316         };
15317         
15318         if (this.weight) { // Validity check?
15319             cfg.cls += " checkbox-" + this.weight;
15320         }
15321         
15322         if (this.disabled) {
15323             input.disabled=true;
15324         }
15325         
15326         if(this.checked){
15327             input.checked = this.checked;
15328         }
15329         
15330         if (this.name) {
15331             input.name = this.name;
15332         }
15333         
15334         if (this.size) {
15335             input.cls += ' input-' + this.size;
15336         }
15337         
15338         var settings=this;
15339         ['xs','sm','md','lg'].map(function(size){
15340             if (settings[size]) {
15341                 cfg.cls += ' col-' + size + '-' + settings[size];
15342             }
15343         });
15344         
15345        
15346         
15347         var inputblock = input;
15348         
15349         
15350         
15351         
15352         if (this.before || this.after) {
15353             
15354             inputblock = {
15355                 cls : 'input-group',
15356                 cn :  [] 
15357             };
15358             if (this.before) {
15359                 inputblock.cn.push({
15360                     tag :'span',
15361                     cls : 'input-group-addon',
15362                     html : this.before
15363                 });
15364             }
15365             inputblock.cn.push(input);
15366             if (this.after) {
15367                 inputblock.cn.push({
15368                     tag :'span',
15369                     cls : 'input-group-addon',
15370                     html : this.after
15371                 });
15372             }
15373             
15374         };
15375         
15376         if (align ==='left' && this.fieldLabel.length) {
15377                 Roo.log("left and has label");
15378                 cfg.cn = [
15379                     
15380                     {
15381                         tag: 'label',
15382                         'for' :  id,
15383                         cls : 'control-label col-md-' + this.labelWidth,
15384                         html : this.fieldLabel
15385                         
15386                     },
15387                     {
15388                         cls : "col-md-" + (12 - this.labelWidth), 
15389                         cn: [
15390                             inputblock
15391                         ]
15392                     }
15393                     
15394                 ];
15395         } else if ( this.fieldLabel.length) {
15396                 Roo.log(" label");
15397                 cfg.cn = [
15398                    
15399                     {
15400                         tag: this.boxLabel ? 'span' : 'label',
15401                         'for': id,
15402                         cls: 'control-label box-input-label',
15403                         //cls : 'input-group-addon',
15404                         html : this.fieldLabel
15405                         
15406                     },
15407                     
15408                     inputblock
15409                     
15410                 ];
15411
15412         } else {
15413             
15414                 Roo.log(" no label && no align");
15415                 cfg.cn = [  inputblock ] ;
15416                 
15417                 
15418         };
15419          if(this.boxLabel){
15420             cfg.cn.push( {
15421                 tag: 'label',
15422                 'for': id,
15423                 cls: 'box-label',
15424                 html: this.boxLabel
15425                 
15426             });
15427         }
15428         
15429         
15430        
15431         return cfg;
15432         
15433     },
15434     
15435     /**
15436      * return the real input element.
15437      */
15438     inputEl: function ()
15439     {
15440         return this.el.select('input.roo-checkbox',true).first();
15441     },
15442     
15443     label: function()
15444     {
15445         return this.el.select('label.control-label',true).first();
15446     },
15447     
15448     initEvents : function()
15449     {
15450 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
15451         
15452         this.inputEl().on('click', this.onClick,  this);
15453         
15454     },
15455     
15456     onClick : function()
15457     {   
15458         this.setChecked(!this.checked);
15459     },
15460     
15461     setChecked : function(state,suppressEvent)
15462     {
15463         this.checked = state;
15464         
15465         this.inputEl().dom.checked = state;
15466         
15467         if(suppressEvent !== true){
15468             this.fireEvent('check', this, state);
15469         }
15470         
15471         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15472         
15473     },
15474     
15475     setValue : function(v,suppressEvent)
15476     {
15477         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
15478     }
15479     
15480 });
15481
15482  
15483 /*
15484  * - LGPL
15485  *
15486  * Radio
15487  * 
15488  */
15489
15490 /**
15491  * @class Roo.bootstrap.Radio
15492  * @extends Roo.bootstrap.CheckBox
15493  * Bootstrap Radio class
15494
15495  * @constructor
15496  * Create a new Radio
15497  * @param {Object} config The config object
15498  */
15499
15500 Roo.bootstrap.Radio = function(config){
15501     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
15502    
15503 };
15504
15505 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
15506     
15507     inputType: 'radio',
15508     inputValue: '',
15509     valueOff: '',
15510     
15511     getAutoCreate : function()
15512     {
15513         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15514         
15515         var id = Roo.id();
15516         
15517         var cfg = {};
15518         
15519         cfg.cls = 'form-group radio' //input-group
15520         
15521         var input =  {
15522             tag: 'input',
15523             id : id,
15524             type : this.inputType,
15525             value : (!this.checked) ? this.valueOff : this.inputValue,
15526             cls : 'roo-radio',
15527             placeholder : this.placeholder || ''
15528             
15529         };
15530           if (this.weight) { // Validity check?
15531             cfg.cls += " radio-" + this.weight;
15532         }
15533         if (this.disabled) {
15534             input.disabled=true;
15535         }
15536         
15537         if(this.checked){
15538             input.checked = this.checked;
15539         }
15540         
15541         if (this.name) {
15542             input.name = this.name;
15543         }
15544         
15545         if (this.size) {
15546             input.cls += ' input-' + this.size;
15547         }
15548         
15549         var settings=this;
15550         ['xs','sm','md','lg'].map(function(size){
15551             if (settings[size]) {
15552                 cfg.cls += ' col-' + size + '-' + settings[size];
15553             }
15554         });
15555         
15556         var inputblock = input;
15557         
15558         if (this.before || this.after) {
15559             
15560             inputblock = {
15561                 cls : 'input-group',
15562                 cn :  [] 
15563             };
15564             if (this.before) {
15565                 inputblock.cn.push({
15566                     tag :'span',
15567                     cls : 'input-group-addon',
15568                     html : this.before
15569                 });
15570             }
15571             inputblock.cn.push(input);
15572             if (this.after) {
15573                 inputblock.cn.push({
15574                     tag :'span',
15575                     cls : 'input-group-addon',
15576                     html : this.after
15577                 });
15578             }
15579             
15580         };
15581         
15582         if (align ==='left' && this.fieldLabel.length) {
15583                 Roo.log("left and has label");
15584                 cfg.cn = [
15585                     
15586                     {
15587                         tag: 'label',
15588                         'for' :  id,
15589                         cls : 'control-label col-md-' + this.labelWidth,
15590                         html : this.fieldLabel
15591                         
15592                     },
15593                     {
15594                         cls : "col-md-" + (12 - this.labelWidth), 
15595                         cn: [
15596                             inputblock
15597                         ]
15598                     }
15599                     
15600                 ];
15601         } else if ( this.fieldLabel.length) {
15602                 Roo.log(" label");
15603                  cfg.cn = [
15604                    
15605                     {
15606                         tag: 'label',
15607                         'for': id,
15608                         cls: 'control-label box-input-label',
15609                         //cls : 'input-group-addon',
15610                         html : this.fieldLabel
15611                         
15612                     },
15613                     
15614                     inputblock
15615                     
15616                 ];
15617
15618         } else {
15619             
15620                    Roo.log(" no label && no align");
15621                 cfg.cn = [
15622                     
15623                         inputblock
15624                     
15625                 ];
15626                 
15627                 
15628         };
15629         
15630         if(this.boxLabel){
15631             cfg.cn.push({
15632                 tag: 'label',
15633                 'for': id,
15634                 cls: 'box-label',
15635                 html: this.boxLabel
15636             })
15637         }
15638         
15639         return cfg;
15640         
15641     },
15642     inputEl: function ()
15643     {
15644         return this.el.select('input.roo-radio',true).first();
15645     },
15646     onClick : function()
15647     {   
15648         this.setChecked(true);
15649     },
15650     
15651     setChecked : function(state,suppressEvent)
15652     {
15653         if(state){
15654             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
15655                 v.dom.checked = false;
15656             });
15657         }
15658         
15659         this.checked = state;
15660         this.inputEl().dom.checked = state;
15661         
15662         if(suppressEvent !== true){
15663             this.fireEvent('check', this, state);
15664         }
15665         
15666         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
15667         
15668     },
15669     
15670     getGroupValue : function()
15671     {
15672         var value = ''
15673         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
15674             if(v.dom.checked == true){
15675                 value = v.dom.value;
15676             }
15677         });
15678         
15679         return value;
15680     },
15681     
15682     /**
15683      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
15684      * @return {Mixed} value The field value
15685      */
15686     getValue : function(){
15687         return this.getGroupValue();
15688     }
15689     
15690 });
15691
15692  
15693 //<script type="text/javascript">
15694
15695 /*
15696  * Based  Ext JS Library 1.1.1
15697  * Copyright(c) 2006-2007, Ext JS, LLC.
15698  * LGPL
15699  *
15700  */
15701  
15702 /**
15703  * @class Roo.HtmlEditorCore
15704  * @extends Roo.Component
15705  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
15706  *
15707  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
15708  */
15709
15710 Roo.HtmlEditorCore = function(config){
15711     
15712     
15713     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
15714     this.addEvents({
15715         /**
15716          * @event initialize
15717          * Fires when the editor is fully initialized (including the iframe)
15718          * @param {Roo.HtmlEditorCore} this
15719          */
15720         initialize: true,
15721         /**
15722          * @event activate
15723          * Fires when the editor is first receives the focus. Any insertion must wait
15724          * until after this event.
15725          * @param {Roo.HtmlEditorCore} this
15726          */
15727         activate: true,
15728          /**
15729          * @event beforesync
15730          * Fires before the textarea is updated with content from the editor iframe. Return false
15731          * to cancel the sync.
15732          * @param {Roo.HtmlEditorCore} this
15733          * @param {String} html
15734          */
15735         beforesync: true,
15736          /**
15737          * @event beforepush
15738          * Fires before the iframe editor is updated with content from the textarea. Return false
15739          * to cancel the push.
15740          * @param {Roo.HtmlEditorCore} this
15741          * @param {String} html
15742          */
15743         beforepush: true,
15744          /**
15745          * @event sync
15746          * Fires when the textarea is updated with content from the editor iframe.
15747          * @param {Roo.HtmlEditorCore} this
15748          * @param {String} html
15749          */
15750         sync: true,
15751          /**
15752          * @event push
15753          * Fires when the iframe editor is updated with content from the textarea.
15754          * @param {Roo.HtmlEditorCore} this
15755          * @param {String} html
15756          */
15757         push: true,
15758         
15759         /**
15760          * @event editorevent
15761          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15762          * @param {Roo.HtmlEditorCore} this
15763          */
15764         editorevent: true
15765     });
15766      
15767 };
15768
15769
15770 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
15771
15772
15773      /**
15774      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
15775      */
15776     
15777     owner : false,
15778     
15779      /**
15780      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
15781      *                        Roo.resizable.
15782      */
15783     resizable : false,
15784      /**
15785      * @cfg {Number} height (in pixels)
15786      */   
15787     height: 300,
15788    /**
15789      * @cfg {Number} width (in pixels)
15790      */   
15791     width: 500,
15792     
15793     /**
15794      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15795      * 
15796      */
15797     stylesheets: false,
15798     
15799     // id of frame..
15800     frameId: false,
15801     
15802     // private properties
15803     validationEvent : false,
15804     deferHeight: true,
15805     initialized : false,
15806     activated : false,
15807     sourceEditMode : false,
15808     onFocus : Roo.emptyFn,
15809     iframePad:3,
15810     hideMode:'offsets',
15811     
15812     clearUp: true,
15813     
15814      
15815     
15816
15817     /**
15818      * Protected method that will not generally be called directly. It
15819      * is called when the editor initializes the iframe with HTML contents. Override this method if you
15820      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
15821      */
15822     getDocMarkup : function(){
15823         // body styles..
15824         var st = '';
15825         Roo.log(this.stylesheets);
15826         
15827         // inherit styels from page...?? 
15828         if (this.stylesheets === false) {
15829             
15830             Roo.get(document.head).select('style').each(function(node) {
15831                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
15832             });
15833             
15834             Roo.get(document.head).select('link').each(function(node) { 
15835                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
15836             });
15837             
15838         } else if (!this.stylesheets.length) {
15839                 // simple..
15840                 st = '<style type="text/css">' +
15841                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
15842                    '</style>';
15843         } else {
15844             Roo.each(this.stylesheets, function(s) {
15845                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
15846             });
15847             
15848         }
15849         
15850         st +=  '<style type="text/css">' +
15851             'IMG { cursor: pointer } ' +
15852         '</style>';
15853
15854         
15855         return '<html><head>' + st  +
15856             //<style type="text/css">' +
15857             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
15858             //'</style>' +
15859             ' </head><body class="roo-htmleditor-body"></body></html>';
15860     },
15861
15862     // private
15863     onRender : function(ct, position)
15864     {
15865         var _t = this;
15866         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
15867         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
15868         
15869         
15870         this.el.dom.style.border = '0 none';
15871         this.el.dom.setAttribute('tabIndex', -1);
15872         this.el.addClass('x-hidden hide');
15873         
15874         
15875         
15876         if(Roo.isIE){ // fix IE 1px bogus margin
15877             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
15878         }
15879        
15880         
15881         this.frameId = Roo.id();
15882         
15883          
15884         
15885         var iframe = this.owner.wrap.createChild({
15886             tag: 'iframe',
15887             cls: 'form-control', // bootstrap..
15888             id: this.frameId,
15889             name: this.frameId,
15890             frameBorder : 'no',
15891             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
15892         }, this.el
15893         );
15894         
15895         
15896         this.iframe = iframe.dom;
15897
15898          this.assignDocWin();
15899         
15900         this.doc.designMode = 'on';
15901        
15902         this.doc.open();
15903         this.doc.write(this.getDocMarkup());
15904         this.doc.close();
15905
15906         
15907         var task = { // must defer to wait for browser to be ready
15908             run : function(){
15909                 //console.log("run task?" + this.doc.readyState);
15910                 this.assignDocWin();
15911                 if(this.doc.body || this.doc.readyState == 'complete'){
15912                     try {
15913                         this.doc.designMode="on";
15914                     } catch (e) {
15915                         return;
15916                     }
15917                     Roo.TaskMgr.stop(task);
15918                     this.initEditor.defer(10, this);
15919                 }
15920             },
15921             interval : 10,
15922             duration: 10000,
15923             scope: this
15924         };
15925         Roo.TaskMgr.start(task);
15926
15927         
15928          
15929     },
15930
15931     // private
15932     onResize : function(w, h)
15933     {
15934          Roo.log('resize: ' +w + ',' + h );
15935         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
15936         if(!this.iframe){
15937             return;
15938         }
15939         if(typeof w == 'number'){
15940             
15941             this.iframe.style.width = w + 'px';
15942         }
15943         if(typeof h == 'number'){
15944             
15945             this.iframe.style.height = h + 'px';
15946             if(this.doc){
15947                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
15948             }
15949         }
15950         
15951     },
15952
15953     /**
15954      * Toggles the editor between standard and source edit mode.
15955      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15956      */
15957     toggleSourceEdit : function(sourceEditMode){
15958         
15959         this.sourceEditMode = sourceEditMode === true;
15960         
15961         if(this.sourceEditMode){
15962  
15963             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
15964             
15965         }else{
15966             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
15967             //this.iframe.className = '';
15968             this.deferFocus();
15969         }
15970         //this.setSize(this.owner.wrap.getSize());
15971         //this.fireEvent('editmodechange', this, this.sourceEditMode);
15972     },
15973
15974     
15975   
15976
15977     /**
15978      * Protected method that will not generally be called directly. If you need/want
15979      * custom HTML cleanup, this is the method you should override.
15980      * @param {String} html The HTML to be cleaned
15981      * return {String} The cleaned HTML
15982      */
15983     cleanHtml : function(html){
15984         html = String(html);
15985         if(html.length > 5){
15986             if(Roo.isSafari){ // strip safari nonsense
15987                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
15988             }
15989         }
15990         if(html == '&nbsp;'){
15991             html = '';
15992         }
15993         return html;
15994     },
15995
15996     /**
15997      * HTML Editor -> Textarea
15998      * Protected method that will not generally be called directly. Syncs the contents
15999      * of the editor iframe with the textarea.
16000      */
16001     syncValue : function(){
16002         if(this.initialized){
16003             var bd = (this.doc.body || this.doc.documentElement);
16004             //this.cleanUpPaste(); -- this is done else where and causes havoc..
16005             var html = bd.innerHTML;
16006             if(Roo.isSafari){
16007                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16008                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16009                 if(m && m[1]){
16010                     html = '<div style="'+m[0]+'">' + html + '</div>';
16011                 }
16012             }
16013             html = this.cleanHtml(html);
16014             // fix up the special chars.. normaly like back quotes in word...
16015             // however we do not want to do this with chinese..
16016             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16017                 var cc = b.charCodeAt();
16018                 if (
16019                     (cc >= 0x4E00 && cc < 0xA000 ) ||
16020                     (cc >= 0x3400 && cc < 0x4E00 ) ||
16021                     (cc >= 0xf900 && cc < 0xfb00 )
16022                 ) {
16023                         return b;
16024                 }
16025                 return "&#"+cc+";" 
16026             });
16027             if(this.owner.fireEvent('beforesync', this, html) !== false){
16028                 this.el.dom.value = html;
16029                 this.owner.fireEvent('sync', this, html);
16030             }
16031         }
16032     },
16033
16034     /**
16035      * Protected method that will not generally be called directly. Pushes the value of the textarea
16036      * into the iframe editor.
16037      */
16038     pushValue : function(){
16039         if(this.initialized){
16040             var v = this.el.dom.value.trim();
16041             
16042 //            if(v.length < 1){
16043 //                v = '&#160;';
16044 //            }
16045             
16046             if(this.owner.fireEvent('beforepush', this, v) !== false){
16047                 var d = (this.doc.body || this.doc.documentElement);
16048                 d.innerHTML = v;
16049                 this.cleanUpPaste();
16050                 this.el.dom.value = d.innerHTML;
16051                 this.owner.fireEvent('push', this, v);
16052             }
16053         }
16054     },
16055
16056     // private
16057     deferFocus : function(){
16058         this.focus.defer(10, this);
16059     },
16060
16061     // doc'ed in Field
16062     focus : function(){
16063         if(this.win && !this.sourceEditMode){
16064             this.win.focus();
16065         }else{
16066             this.el.focus();
16067         }
16068     },
16069     
16070     assignDocWin: function()
16071     {
16072         var iframe = this.iframe;
16073         
16074          if(Roo.isIE){
16075             this.doc = iframe.contentWindow.document;
16076             this.win = iframe.contentWindow;
16077         } else {
16078             if (!Roo.get(this.frameId)) {
16079                 return;
16080             }
16081             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16082             this.win = Roo.get(this.frameId).dom.contentWindow;
16083         }
16084     },
16085     
16086     // private
16087     initEditor : function(){
16088         //console.log("INIT EDITOR");
16089         this.assignDocWin();
16090         
16091         
16092         
16093         this.doc.designMode="on";
16094         this.doc.open();
16095         this.doc.write(this.getDocMarkup());
16096         this.doc.close();
16097         
16098         var dbody = (this.doc.body || this.doc.documentElement);
16099         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16100         // this copies styles from the containing element into thsi one..
16101         // not sure why we need all of this..
16102         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16103         
16104         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16105         //ss['background-attachment'] = 'fixed'; // w3c
16106         dbody.bgProperties = 'fixed'; // ie
16107         //Roo.DomHelper.applyStyles(dbody, ss);
16108         Roo.EventManager.on(this.doc, {
16109             //'mousedown': this.onEditorEvent,
16110             'mouseup': this.onEditorEvent,
16111             'dblclick': this.onEditorEvent,
16112             'click': this.onEditorEvent,
16113             'keyup': this.onEditorEvent,
16114             buffer:100,
16115             scope: this
16116         });
16117         if(Roo.isGecko){
16118             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16119         }
16120         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16121             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16122         }
16123         this.initialized = true;
16124
16125         this.owner.fireEvent('initialize', this);
16126         this.pushValue();
16127     },
16128
16129     // private
16130     onDestroy : function(){
16131         
16132         
16133         
16134         if(this.rendered){
16135             
16136             //for (var i =0; i < this.toolbars.length;i++) {
16137             //    // fixme - ask toolbars for heights?
16138             //    this.toolbars[i].onDestroy();
16139            // }
16140             
16141             //this.wrap.dom.innerHTML = '';
16142             //this.wrap.remove();
16143         }
16144     },
16145
16146     // private
16147     onFirstFocus : function(){
16148         
16149         this.assignDocWin();
16150         
16151         
16152         this.activated = true;
16153          
16154     
16155         if(Roo.isGecko){ // prevent silly gecko errors
16156             this.win.focus();
16157             var s = this.win.getSelection();
16158             if(!s.focusNode || s.focusNode.nodeType != 3){
16159                 var r = s.getRangeAt(0);
16160                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16161                 r.collapse(true);
16162                 this.deferFocus();
16163             }
16164             try{
16165                 this.execCmd('useCSS', true);
16166                 this.execCmd('styleWithCSS', false);
16167             }catch(e){}
16168         }
16169         this.owner.fireEvent('activate', this);
16170     },
16171
16172     // private
16173     adjustFont: function(btn){
16174         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16175         //if(Roo.isSafari){ // safari
16176         //    adjust *= 2;
16177        // }
16178         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16179         if(Roo.isSafari){ // safari
16180             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16181             v =  (v < 10) ? 10 : v;
16182             v =  (v > 48) ? 48 : v;
16183             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16184             
16185         }
16186         
16187         
16188         v = Math.max(1, v+adjust);
16189         
16190         this.execCmd('FontSize', v  );
16191     },
16192
16193     onEditorEvent : function(e){
16194         this.owner.fireEvent('editorevent', this, e);
16195       //  this.updateToolbar();
16196         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16197     },
16198
16199     insertTag : function(tg)
16200     {
16201         // could be a bit smarter... -> wrap the current selected tRoo..
16202         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16203             
16204             range = this.createRange(this.getSelection());
16205             var wrappingNode = this.doc.createElement(tg.toLowerCase());
16206             wrappingNode.appendChild(range.extractContents());
16207             range.insertNode(wrappingNode);
16208
16209             return;
16210             
16211             
16212             
16213         }
16214         this.execCmd("formatblock",   tg);
16215         
16216     },
16217     
16218     insertText : function(txt)
16219     {
16220         
16221         
16222         var range = this.createRange();
16223         range.deleteContents();
16224                //alert(Sender.getAttribute('label'));
16225                
16226         range.insertNode(this.doc.createTextNode(txt));
16227     } ,
16228     
16229      
16230
16231     /**
16232      * Executes a Midas editor command on the editor document and performs necessary focus and
16233      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16234      * @param {String} cmd The Midas command
16235      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16236      */
16237     relayCmd : function(cmd, value){
16238         this.win.focus();
16239         this.execCmd(cmd, value);
16240         this.owner.fireEvent('editorevent', this);
16241         //this.updateToolbar();
16242         this.owner.deferFocus();
16243     },
16244
16245     /**
16246      * Executes a Midas editor command directly on the editor document.
16247      * For visual commands, you should use {@link #relayCmd} instead.
16248      * <b>This should only be called after the editor is initialized.</b>
16249      * @param {String} cmd The Midas command
16250      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16251      */
16252     execCmd : function(cmd, value){
16253         this.doc.execCommand(cmd, false, value === undefined ? null : value);
16254         this.syncValue();
16255     },
16256  
16257  
16258    
16259     /**
16260      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16261      * to insert tRoo.
16262      * @param {String} text | dom node.. 
16263      */
16264     insertAtCursor : function(text)
16265     {
16266         
16267         
16268         
16269         if(!this.activated){
16270             return;
16271         }
16272         /*
16273         if(Roo.isIE){
16274             this.win.focus();
16275             var r = this.doc.selection.createRange();
16276             if(r){
16277                 r.collapse(true);
16278                 r.pasteHTML(text);
16279                 this.syncValue();
16280                 this.deferFocus();
16281             
16282             }
16283             return;
16284         }
16285         */
16286         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16287             this.win.focus();
16288             
16289             
16290             // from jquery ui (MIT licenced)
16291             var range, node;
16292             var win = this.win;
16293             
16294             if (win.getSelection && win.getSelection().getRangeAt) {
16295                 range = win.getSelection().getRangeAt(0);
16296                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16297                 range.insertNode(node);
16298             } else if (win.document.selection && win.document.selection.createRange) {
16299                 // no firefox support
16300                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16301                 win.document.selection.createRange().pasteHTML(txt);
16302             } else {
16303                 // no firefox support
16304                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16305                 this.execCmd('InsertHTML', txt);
16306             } 
16307             
16308             this.syncValue();
16309             
16310             this.deferFocus();
16311         }
16312     },
16313  // private
16314     mozKeyPress : function(e){
16315         if(e.ctrlKey){
16316             var c = e.getCharCode(), cmd;
16317           
16318             if(c > 0){
16319                 c = String.fromCharCode(c).toLowerCase();
16320                 switch(c){
16321                     case 'b':
16322                         cmd = 'bold';
16323                         break;
16324                     case 'i':
16325                         cmd = 'italic';
16326                         break;
16327                     
16328                     case 'u':
16329                         cmd = 'underline';
16330                         break;
16331                     
16332                     case 'v':
16333                         this.cleanUpPaste.defer(100, this);
16334                         return;
16335                         
16336                 }
16337                 if(cmd){
16338                     this.win.focus();
16339                     this.execCmd(cmd);
16340                     this.deferFocus();
16341                     e.preventDefault();
16342                 }
16343                 
16344             }
16345         }
16346     },
16347
16348     // private
16349     fixKeys : function(){ // load time branching for fastest keydown performance
16350         if(Roo.isIE){
16351             return function(e){
16352                 var k = e.getKey(), r;
16353                 if(k == e.TAB){
16354                     e.stopEvent();
16355                     r = this.doc.selection.createRange();
16356                     if(r){
16357                         r.collapse(true);
16358                         r.pasteHTML('&#160;&#160;&#160;&#160;');
16359                         this.deferFocus();
16360                     }
16361                     return;
16362                 }
16363                 
16364                 if(k == e.ENTER){
16365                     r = this.doc.selection.createRange();
16366                     if(r){
16367                         var target = r.parentElement();
16368                         if(!target || target.tagName.toLowerCase() != 'li'){
16369                             e.stopEvent();
16370                             r.pasteHTML('<br />');
16371                             r.collapse(false);
16372                             r.select();
16373                         }
16374                     }
16375                 }
16376                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16377                     this.cleanUpPaste.defer(100, this);
16378                     return;
16379                 }
16380                 
16381                 
16382             };
16383         }else if(Roo.isOpera){
16384             return function(e){
16385                 var k = e.getKey();
16386                 if(k == e.TAB){
16387                     e.stopEvent();
16388                     this.win.focus();
16389                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
16390                     this.deferFocus();
16391                 }
16392                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16393                     this.cleanUpPaste.defer(100, this);
16394                     return;
16395                 }
16396                 
16397             };
16398         }else if(Roo.isSafari){
16399             return function(e){
16400                 var k = e.getKey();
16401                 
16402                 if(k == e.TAB){
16403                     e.stopEvent();
16404                     this.execCmd('InsertText','\t');
16405                     this.deferFocus();
16406                     return;
16407                 }
16408                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
16409                     this.cleanUpPaste.defer(100, this);
16410                     return;
16411                 }
16412                 
16413              };
16414         }
16415     }(),
16416     
16417     getAllAncestors: function()
16418     {
16419         var p = this.getSelectedNode();
16420         var a = [];
16421         if (!p) {
16422             a.push(p); // push blank onto stack..
16423             p = this.getParentElement();
16424         }
16425         
16426         
16427         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
16428             a.push(p);
16429             p = p.parentNode;
16430         }
16431         a.push(this.doc.body);
16432         return a;
16433     },
16434     lastSel : false,
16435     lastSelNode : false,
16436     
16437     
16438     getSelection : function() 
16439     {
16440         this.assignDocWin();
16441         return Roo.isIE ? this.doc.selection : this.win.getSelection();
16442     },
16443     
16444     getSelectedNode: function() 
16445     {
16446         // this may only work on Gecko!!!
16447         
16448         // should we cache this!!!!
16449         
16450         
16451         
16452          
16453         var range = this.createRange(this.getSelection()).cloneRange();
16454         
16455         if (Roo.isIE) {
16456             var parent = range.parentElement();
16457             while (true) {
16458                 var testRange = range.duplicate();
16459                 testRange.moveToElementText(parent);
16460                 if (testRange.inRange(range)) {
16461                     break;
16462                 }
16463                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
16464                     break;
16465                 }
16466                 parent = parent.parentElement;
16467             }
16468             return parent;
16469         }
16470         
16471         // is ancestor a text element.
16472         var ac =  range.commonAncestorContainer;
16473         if (ac.nodeType == 3) {
16474             ac = ac.parentNode;
16475         }
16476         
16477         var ar = ac.childNodes;
16478          
16479         var nodes = [];
16480         var other_nodes = [];
16481         var has_other_nodes = false;
16482         for (var i=0;i<ar.length;i++) {
16483             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
16484                 continue;
16485             }
16486             // fullly contained node.
16487             
16488             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
16489                 nodes.push(ar[i]);
16490                 continue;
16491             }
16492             
16493             // probably selected..
16494             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
16495                 other_nodes.push(ar[i]);
16496                 continue;
16497             }
16498             // outer..
16499             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
16500                 continue;
16501             }
16502             
16503             
16504             has_other_nodes = true;
16505         }
16506         if (!nodes.length && other_nodes.length) {
16507             nodes= other_nodes;
16508         }
16509         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
16510             return false;
16511         }
16512         
16513         return nodes[0];
16514     },
16515     createRange: function(sel)
16516     {
16517         // this has strange effects when using with 
16518         // top toolbar - not sure if it's a great idea.
16519         //this.editor.contentWindow.focus();
16520         if (typeof sel != "undefined") {
16521             try {
16522                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
16523             } catch(e) {
16524                 return this.doc.createRange();
16525             }
16526         } else {
16527             return this.doc.createRange();
16528         }
16529     },
16530     getParentElement: function()
16531     {
16532         
16533         this.assignDocWin();
16534         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
16535         
16536         var range = this.createRange(sel);
16537          
16538         try {
16539             var p = range.commonAncestorContainer;
16540             while (p.nodeType == 3) { // text node
16541                 p = p.parentNode;
16542             }
16543             return p;
16544         } catch (e) {
16545             return null;
16546         }
16547     
16548     },
16549     /***
16550      *
16551      * Range intersection.. the hard stuff...
16552      *  '-1' = before
16553      *  '0' = hits..
16554      *  '1' = after.
16555      *         [ -- selected range --- ]
16556      *   [fail]                        [fail]
16557      *
16558      *    basically..
16559      *      if end is before start or  hits it. fail.
16560      *      if start is after end or hits it fail.
16561      *
16562      *   if either hits (but other is outside. - then it's not 
16563      *   
16564      *    
16565      **/
16566     
16567     
16568     // @see http://www.thismuchiknow.co.uk/?p=64.
16569     rangeIntersectsNode : function(range, node)
16570     {
16571         var nodeRange = node.ownerDocument.createRange();
16572         try {
16573             nodeRange.selectNode(node);
16574         } catch (e) {
16575             nodeRange.selectNodeContents(node);
16576         }
16577     
16578         var rangeStartRange = range.cloneRange();
16579         rangeStartRange.collapse(true);
16580     
16581         var rangeEndRange = range.cloneRange();
16582         rangeEndRange.collapse(false);
16583     
16584         var nodeStartRange = nodeRange.cloneRange();
16585         nodeStartRange.collapse(true);
16586     
16587         var nodeEndRange = nodeRange.cloneRange();
16588         nodeEndRange.collapse(false);
16589     
16590         return rangeStartRange.compareBoundaryPoints(
16591                  Range.START_TO_START, nodeEndRange) == -1 &&
16592                rangeEndRange.compareBoundaryPoints(
16593                  Range.START_TO_START, nodeStartRange) == 1;
16594         
16595          
16596     },
16597     rangeCompareNode : function(range, node)
16598     {
16599         var nodeRange = node.ownerDocument.createRange();
16600         try {
16601             nodeRange.selectNode(node);
16602         } catch (e) {
16603             nodeRange.selectNodeContents(node);
16604         }
16605         
16606         
16607         range.collapse(true);
16608     
16609         nodeRange.collapse(true);
16610      
16611         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
16612         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
16613          
16614         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
16615         
16616         var nodeIsBefore   =  ss == 1;
16617         var nodeIsAfter    = ee == -1;
16618         
16619         if (nodeIsBefore && nodeIsAfter)
16620             return 0; // outer
16621         if (!nodeIsBefore && nodeIsAfter)
16622             return 1; //right trailed.
16623         
16624         if (nodeIsBefore && !nodeIsAfter)
16625             return 2;  // left trailed.
16626         // fully contined.
16627         return 3;
16628     },
16629
16630     // private? - in a new class?
16631     cleanUpPaste :  function()
16632     {
16633         // cleans up the whole document..
16634         Roo.log('cleanuppaste');
16635         
16636         this.cleanUpChildren(this.doc.body);
16637         var clean = this.cleanWordChars(this.doc.body.innerHTML);
16638         if (clean != this.doc.body.innerHTML) {
16639             this.doc.body.innerHTML = clean;
16640         }
16641         
16642     },
16643     
16644     cleanWordChars : function(input) {// change the chars to hex code
16645         var he = Roo.HtmlEditorCore;
16646         
16647         var output = input;
16648         Roo.each(he.swapCodes, function(sw) { 
16649             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
16650             
16651             output = output.replace(swapper, sw[1]);
16652         });
16653         
16654         return output;
16655     },
16656     
16657     
16658     cleanUpChildren : function (n)
16659     {
16660         if (!n.childNodes.length) {
16661             return;
16662         }
16663         for (var i = n.childNodes.length-1; i > -1 ; i--) {
16664            this.cleanUpChild(n.childNodes[i]);
16665         }
16666     },
16667     
16668     
16669         
16670     
16671     cleanUpChild : function (node)
16672     {
16673         var ed = this;
16674         //console.log(node);
16675         if (node.nodeName == "#text") {
16676             // clean up silly Windows -- stuff?
16677             return; 
16678         }
16679         if (node.nodeName == "#comment") {
16680             node.parentNode.removeChild(node);
16681             // clean up silly Windows -- stuff?
16682             return; 
16683         }
16684         
16685         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
16686             // remove node.
16687             node.parentNode.removeChild(node);
16688             return;
16689             
16690         }
16691         
16692         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
16693         
16694         // remove <a name=....> as rendering on yahoo mailer is borked with this.
16695         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
16696         
16697         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
16698         //    remove_keep_children = true;
16699         //}
16700         
16701         if (remove_keep_children) {
16702             this.cleanUpChildren(node);
16703             // inserts everything just before this node...
16704             while (node.childNodes.length) {
16705                 var cn = node.childNodes[0];
16706                 node.removeChild(cn);
16707                 node.parentNode.insertBefore(cn, node);
16708             }
16709             node.parentNode.removeChild(node);
16710             return;
16711         }
16712         
16713         if (!node.attributes || !node.attributes.length) {
16714             this.cleanUpChildren(node);
16715             return;
16716         }
16717         
16718         function cleanAttr(n,v)
16719         {
16720             
16721             if (v.match(/^\./) || v.match(/^\//)) {
16722                 return;
16723             }
16724             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
16725                 return;
16726             }
16727             if (v.match(/^#/)) {
16728                 return;
16729             }
16730 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
16731             node.removeAttribute(n);
16732             
16733         }
16734         
16735         function cleanStyle(n,v)
16736         {
16737             if (v.match(/expression/)) { //XSS?? should we even bother..
16738                 node.removeAttribute(n);
16739                 return;
16740             }
16741             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
16742             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
16743             
16744             
16745             var parts = v.split(/;/);
16746             var clean = [];
16747             
16748             Roo.each(parts, function(p) {
16749                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
16750                 if (!p.length) {
16751                     return true;
16752                 }
16753                 var l = p.split(':').shift().replace(/\s+/g,'');
16754                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
16755                 
16756                 if ( cblack.indexOf(l) > -1) {
16757 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
16758                     //node.removeAttribute(n);
16759                     return true;
16760                 }
16761                 //Roo.log()
16762                 // only allow 'c whitelisted system attributes'
16763                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
16764 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
16765                     //node.removeAttribute(n);
16766                     return true;
16767                 }
16768                 
16769                 
16770                  
16771                 
16772                 clean.push(p);
16773                 return true;
16774             });
16775             if (clean.length) { 
16776                 node.setAttribute(n, clean.join(';'));
16777             } else {
16778                 node.removeAttribute(n);
16779             }
16780             
16781         }
16782         
16783         
16784         for (var i = node.attributes.length-1; i > -1 ; i--) {
16785             var a = node.attributes[i];
16786             //console.log(a);
16787             
16788             if (a.name.toLowerCase().substr(0,2)=='on')  {
16789                 node.removeAttribute(a.name);
16790                 continue;
16791             }
16792             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
16793                 node.removeAttribute(a.name);
16794                 continue;
16795             }
16796             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
16797                 cleanAttr(a.name,a.value); // fixme..
16798                 continue;
16799             }
16800             if (a.name == 'style') {
16801                 cleanStyle(a.name,a.value);
16802                 continue;
16803             }
16804             /// clean up MS crap..
16805             // tecnically this should be a list of valid class'es..
16806             
16807             
16808             if (a.name == 'class') {
16809                 if (a.value.match(/^Mso/)) {
16810                     node.className = '';
16811                 }
16812                 
16813                 if (a.value.match(/body/)) {
16814                     node.className = '';
16815                 }
16816                 continue;
16817             }
16818             
16819             // style cleanup!?
16820             // class cleanup?
16821             
16822         }
16823         
16824         
16825         this.cleanUpChildren(node);
16826         
16827         
16828     },
16829     /**
16830      * Clean up MS wordisms...
16831      */
16832     cleanWord : function(node)
16833     {
16834         var _t = this;
16835         var cleanWordChildren = function()
16836         {
16837             if (!node.childNodes.length) {
16838                 return;
16839             }
16840             for (var i = node.childNodes.length-1; i > -1 ; i--) {
16841                _t.cleanWord(node.childNodes[i]);
16842             }
16843         }
16844         
16845         
16846         if (!node) {
16847             this.cleanWord(this.doc.body);
16848             return;
16849         }
16850         if (node.nodeName == "#text") {
16851             // clean up silly Windows -- stuff?
16852             return; 
16853         }
16854         if (node.nodeName == "#comment") {
16855             node.parentNode.removeChild(node);
16856             // clean up silly Windows -- stuff?
16857             return; 
16858         }
16859         
16860         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
16861             node.parentNode.removeChild(node);
16862             return;
16863         }
16864         
16865         // remove - but keep children..
16866         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
16867             while (node.childNodes.length) {
16868                 var cn = node.childNodes[0];
16869                 node.removeChild(cn);
16870                 node.parentNode.insertBefore(cn, node);
16871             }
16872             node.parentNode.removeChild(node);
16873             cleanWordChildren();
16874             return;
16875         }
16876         // clean styles
16877         if (node.className.length) {
16878             
16879             var cn = node.className.split(/\W+/);
16880             var cna = [];
16881             Roo.each(cn, function(cls) {
16882                 if (cls.match(/Mso[a-zA-Z]+/)) {
16883                     return;
16884                 }
16885                 cna.push(cls);
16886             });
16887             node.className = cna.length ? cna.join(' ') : '';
16888             if (!cna.length) {
16889                 node.removeAttribute("class");
16890             }
16891         }
16892         
16893         if (node.hasAttribute("lang")) {
16894             node.removeAttribute("lang");
16895         }
16896         
16897         if (node.hasAttribute("style")) {
16898             
16899             var styles = node.getAttribute("style").split(";");
16900             var nstyle = [];
16901             Roo.each(styles, function(s) {
16902                 if (!s.match(/:/)) {
16903                     return;
16904                 }
16905                 var kv = s.split(":");
16906                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
16907                     return;
16908                 }
16909                 // what ever is left... we allow.
16910                 nstyle.push(s);
16911             });
16912             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
16913             if (!nstyle.length) {
16914                 node.removeAttribute('style');
16915             }
16916         }
16917         
16918         cleanWordChildren();
16919         
16920         
16921     },
16922     domToHTML : function(currentElement, depth, nopadtext) {
16923         
16924             depth = depth || 0;
16925             nopadtext = nopadtext || false;
16926         
16927             if (!currentElement) {
16928                 return this.domToHTML(this.doc.body);
16929             }
16930             
16931             //Roo.log(currentElement);
16932             var j;
16933             var allText = false;
16934             var nodeName = currentElement.nodeName;
16935             var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
16936             
16937             if  (nodeName == '#text') {
16938                 return currentElement.nodeValue;
16939             }
16940             
16941             
16942             var ret = '';
16943             if (nodeName != 'BODY') {
16944                  
16945                 var i = 0;
16946                 // Prints the node tagName, such as <A>, <IMG>, etc
16947                 if (tagName) {
16948                     var attr = [];
16949                     for(i = 0; i < currentElement.attributes.length;i++) {
16950                         // quoting?
16951                         var aname = currentElement.attributes.item(i).name;
16952                         if (!currentElement.attributes.item(i).value.length) {
16953                             continue;
16954                         }
16955                         attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
16956                     }
16957                     
16958                     ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
16959                 } 
16960                 else {
16961                     
16962                     // eack
16963                 }
16964             } else {
16965                 tagName = false;
16966             }
16967             if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
16968                 return ret;
16969             }
16970             if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
16971                 nopadtext = true;
16972             }
16973             
16974             
16975             // Traverse the tree
16976             i = 0;
16977             var currentElementChild = currentElement.childNodes.item(i);
16978             var allText = true;
16979             var innerHTML  = '';
16980             lastnode = '';
16981             while (currentElementChild) {
16982                 // Formatting code (indent the tree so it looks nice on the screen)
16983                 var nopad = nopadtext;
16984                 if (lastnode == 'SPAN') {
16985                     nopad  = true;
16986                 }
16987                 // text
16988                 if  (currentElementChild.nodeName == '#text') {
16989                     var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
16990                     if (!nopad && toadd.length > 80) {
16991                         innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
16992                     }
16993                     innerHTML  += toadd;
16994                     
16995                     i++;
16996                     currentElementChild = currentElement.childNodes.item(i);
16997                     lastNode = '';
16998                     continue;
16999                 }
17000                 allText = false;
17001                 
17002                 innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
17003                     
17004                 // Recursively traverse the tree structure of the child node
17005                 innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
17006                 lastnode = currentElementChild.nodeName;
17007                 i++;
17008                 currentElementChild=currentElement.childNodes.item(i);
17009             }
17010             
17011             ret += innerHTML;
17012             
17013             if (!allText) {
17014                     // The remaining code is mostly for formatting the tree
17015                 ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
17016             }
17017             
17018             
17019             if (tagName) {
17020                 ret+= "</"+tagName+">";
17021             }
17022             return ret;
17023             
17024         }
17025     
17026     // hide stuff that is not compatible
17027     /**
17028      * @event blur
17029      * @hide
17030      */
17031     /**
17032      * @event change
17033      * @hide
17034      */
17035     /**
17036      * @event focus
17037      * @hide
17038      */
17039     /**
17040      * @event specialkey
17041      * @hide
17042      */
17043     /**
17044      * @cfg {String} fieldClass @hide
17045      */
17046     /**
17047      * @cfg {String} focusClass @hide
17048      */
17049     /**
17050      * @cfg {String} autoCreate @hide
17051      */
17052     /**
17053      * @cfg {String} inputType @hide
17054      */
17055     /**
17056      * @cfg {String} invalidClass @hide
17057      */
17058     /**
17059      * @cfg {String} invalidText @hide
17060      */
17061     /**
17062      * @cfg {String} msgFx @hide
17063      */
17064     /**
17065      * @cfg {String} validateOnBlur @hide
17066      */
17067 });
17068
17069 Roo.HtmlEditorCore.white = [
17070         'area', 'br', 'img', 'input', 'hr', 'wbr',
17071         
17072        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
17073        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
17074        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
17075        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
17076        'table',   'ul',         'xmp', 
17077        
17078        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
17079       'thead',   'tr', 
17080      
17081       'dir', 'menu', 'ol', 'ul', 'dl',
17082        
17083       'embed',  'object'
17084 ];
17085
17086
17087 Roo.HtmlEditorCore.black = [
17088     //    'embed',  'object', // enable - backend responsiblity to clean thiese
17089         'applet', // 
17090         'base',   'basefont', 'bgsound', 'blink',  'body', 
17091         'frame',  'frameset', 'head',    'html',   'ilayer', 
17092         'iframe', 'layer',  'link',     'meta',    'object',   
17093         'script', 'style' ,'title',  'xml' // clean later..
17094 ];
17095 Roo.HtmlEditorCore.clean = [
17096     'script', 'style', 'title', 'xml'
17097 ];
17098 Roo.HtmlEditorCore.remove = [
17099     'font'
17100 ];
17101 // attributes..
17102
17103 Roo.HtmlEditorCore.ablack = [
17104     'on'
17105 ];
17106     
17107 Roo.HtmlEditorCore.aclean = [ 
17108     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
17109 ];
17110
17111 // protocols..
17112 Roo.HtmlEditorCore.pwhite= [
17113         'http',  'https',  'mailto'
17114 ];
17115
17116 // white listed style attributes.
17117 Roo.HtmlEditorCore.cwhite= [
17118       //  'text-align', /// default is to allow most things..
17119       
17120          
17121 //        'font-size'//??
17122 ];
17123
17124 // black listed style attributes.
17125 Roo.HtmlEditorCore.cblack= [
17126       //  'font-size' -- this can be set by the project 
17127 ];
17128
17129
17130 Roo.HtmlEditorCore.swapCodes   =[ 
17131     [    8211, "--" ], 
17132     [    8212, "--" ], 
17133     [    8216,  "'" ],  
17134     [    8217, "'" ],  
17135     [    8220, '"' ],  
17136     [    8221, '"' ],  
17137     [    8226, "*" ],  
17138     [    8230, "..." ]
17139 ]; 
17140
17141     /*
17142  * - LGPL
17143  *
17144  * HtmlEditor
17145  * 
17146  */
17147
17148 /**
17149  * @class Roo.bootstrap.HtmlEditor
17150  * @extends Roo.bootstrap.TextArea
17151  * Bootstrap HtmlEditor class
17152
17153  * @constructor
17154  * Create a new HtmlEditor
17155  * @param {Object} config The config object
17156  */
17157
17158 Roo.bootstrap.HtmlEditor = function(config){
17159     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17160     if (!this.toolbars) {
17161         this.toolbars = [];
17162     }
17163     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17164     this.addEvents({
17165             /**
17166              * @event initialize
17167              * Fires when the editor is fully initialized (including the iframe)
17168              * @param {HtmlEditor} this
17169              */
17170             initialize: true,
17171             /**
17172              * @event activate
17173              * Fires when the editor is first receives the focus. Any insertion must wait
17174              * until after this event.
17175              * @param {HtmlEditor} this
17176              */
17177             activate: true,
17178              /**
17179              * @event beforesync
17180              * Fires before the textarea is updated with content from the editor iframe. Return false
17181              * to cancel the sync.
17182              * @param {HtmlEditor} this
17183              * @param {String} html
17184              */
17185             beforesync: true,
17186              /**
17187              * @event beforepush
17188              * Fires before the iframe editor is updated with content from the textarea. Return false
17189              * to cancel the push.
17190              * @param {HtmlEditor} this
17191              * @param {String} html
17192              */
17193             beforepush: true,
17194              /**
17195              * @event sync
17196              * Fires when the textarea is updated with content from the editor iframe.
17197              * @param {HtmlEditor} this
17198              * @param {String} html
17199              */
17200             sync: true,
17201              /**
17202              * @event push
17203              * Fires when the iframe editor is updated with content from the textarea.
17204              * @param {HtmlEditor} this
17205              * @param {String} html
17206              */
17207             push: true,
17208              /**
17209              * @event editmodechange
17210              * Fires when the editor switches edit modes
17211              * @param {HtmlEditor} this
17212              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
17213              */
17214             editmodechange: true,
17215             /**
17216              * @event editorevent
17217              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
17218              * @param {HtmlEditor} this
17219              */
17220             editorevent: true,
17221             /**
17222              * @event firstfocus
17223              * Fires when on first focus - needed by toolbars..
17224              * @param {HtmlEditor} this
17225              */
17226             firstfocus: true,
17227             /**
17228              * @event autosave
17229              * Auto save the htmlEditor value as a file into Events
17230              * @param {HtmlEditor} this
17231              */
17232             autosave: true,
17233             /**
17234              * @event savedpreview
17235              * preview the saved version of htmlEditor
17236              * @param {HtmlEditor} this
17237              */
17238             savedpreview: true
17239         });
17240 };
17241
17242
17243 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
17244     
17245     
17246       /**
17247      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
17248      */
17249     toolbars : false,
17250    
17251      /**
17252      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
17253      *                        Roo.resizable.
17254      */
17255     resizable : false,
17256      /**
17257      * @cfg {Number} height (in pixels)
17258      */   
17259     height: 300,
17260    /**
17261      * @cfg {Number} width (in pixels)
17262      */   
17263     width: false,
17264     
17265     /**
17266      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
17267      * 
17268      */
17269     stylesheets: false,
17270     
17271     // id of frame..
17272     frameId: false,
17273     
17274     // private properties
17275     validationEvent : false,
17276     deferHeight: true,
17277     initialized : false,
17278     activated : false,
17279     
17280     onFocus : Roo.emptyFn,
17281     iframePad:3,
17282     hideMode:'offsets',
17283     
17284     
17285     tbContainer : false,
17286     
17287     toolbarContainer :function() {
17288         return this.wrap.select('.x-html-editor-tb',true).first();
17289     },
17290
17291     /**
17292      * Protected method that will not generally be called directly. It
17293      * is called when the editor creates its toolbar. Override this method if you need to
17294      * add custom toolbar buttons.
17295      * @param {HtmlEditor} editor
17296      */
17297     createToolbar : function(){
17298         
17299         Roo.log("create toolbars");
17300         
17301         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
17302         this.toolbars[0].render(this.toolbarContainer());
17303         
17304         return;
17305         
17306 //        if (!editor.toolbars || !editor.toolbars.length) {
17307 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
17308 //        }
17309 //        
17310 //        for (var i =0 ; i < editor.toolbars.length;i++) {
17311 //            editor.toolbars[i] = Roo.factory(
17312 //                    typeof(editor.toolbars[i]) == 'string' ?
17313 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
17314 //                Roo.bootstrap.HtmlEditor);
17315 //            editor.toolbars[i].init(editor);
17316 //        }
17317     },
17318
17319      
17320     // private
17321     onRender : function(ct, position)
17322     {
17323        // Roo.log("Call onRender: " + this.xtype);
17324         var _t = this;
17325         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
17326       
17327         this.wrap = this.inputEl().wrap({
17328             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
17329         });
17330         
17331         this.editorcore.onRender(ct, position);
17332          
17333         if (this.resizable) {
17334             this.resizeEl = new Roo.Resizable(this.wrap, {
17335                 pinned : true,
17336                 wrap: true,
17337                 dynamic : true,
17338                 minHeight : this.height,
17339                 height: this.height,
17340                 handles : this.resizable,
17341                 width: this.width,
17342                 listeners : {
17343                     resize : function(r, w, h) {
17344                         _t.onResize(w,h); // -something
17345                     }
17346                 }
17347             });
17348             
17349         }
17350         this.createToolbar(this);
17351        
17352         
17353         if(!this.width && this.resizable){
17354             this.setSize(this.wrap.getSize());
17355         }
17356         if (this.resizeEl) {
17357             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
17358             // should trigger onReize..
17359         }
17360         
17361     },
17362
17363     // private
17364     onResize : function(w, h)
17365     {
17366         Roo.log('resize: ' +w + ',' + h );
17367         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
17368         var ew = false;
17369         var eh = false;
17370         
17371         if(this.inputEl() ){
17372             if(typeof w == 'number'){
17373                 var aw = w - this.wrap.getFrameWidth('lr');
17374                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
17375                 ew = aw;
17376             }
17377             if(typeof h == 'number'){
17378                  var tbh = -11;  // fixme it needs to tool bar size!
17379                 for (var i =0; i < this.toolbars.length;i++) {
17380                     // fixme - ask toolbars for heights?
17381                     tbh += this.toolbars[i].el.getHeight();
17382                     //if (this.toolbars[i].footer) {
17383                     //    tbh += this.toolbars[i].footer.el.getHeight();
17384                     //}
17385                 }
17386               
17387                 
17388                 
17389                 
17390                 
17391                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
17392                 ah -= 5; // knock a few pixes off for look..
17393                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
17394                 var eh = ah;
17395             }
17396         }
17397         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
17398         this.editorcore.onResize(ew,eh);
17399         
17400     },
17401
17402     /**
17403      * Toggles the editor between standard and source edit mode.
17404      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
17405      */
17406     toggleSourceEdit : function(sourceEditMode)
17407     {
17408         this.editorcore.toggleSourceEdit(sourceEditMode);
17409         
17410         if(this.editorcore.sourceEditMode){
17411             Roo.log('editor - showing textarea');
17412             
17413 //            Roo.log('in');
17414 //            Roo.log(this.syncValue());
17415             this.syncValue();
17416             this.inputEl().removeClass(['hide', 'x-hidden']);
17417             this.inputEl().dom.removeAttribute('tabIndex');
17418             this.inputEl().focus();
17419         }else{
17420             Roo.log('editor - hiding textarea');
17421 //            Roo.log('out')
17422 //            Roo.log(this.pushValue()); 
17423             this.pushValue();
17424             
17425             this.inputEl().addClass(['hide', 'x-hidden']);
17426             this.inputEl().dom.setAttribute('tabIndex', -1);
17427             //this.deferFocus();
17428         }
17429          
17430         if(this.resizable){
17431             this.setSize(this.wrap.getSize());
17432         }
17433         
17434         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
17435     },
17436  
17437     // private (for BoxComponent)
17438     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17439
17440     // private (for BoxComponent)
17441     getResizeEl : function(){
17442         return this.wrap;
17443     },
17444
17445     // private (for BoxComponent)
17446     getPositionEl : function(){
17447         return this.wrap;
17448     },
17449
17450     // private
17451     initEvents : function(){
17452         this.originalValue = this.getValue();
17453     },
17454
17455 //    /**
17456 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17457 //     * @method
17458 //     */
17459 //    markInvalid : Roo.emptyFn,
17460 //    /**
17461 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
17462 //     * @method
17463 //     */
17464 //    clearInvalid : Roo.emptyFn,
17465
17466     setValue : function(v){
17467         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
17468         this.editorcore.pushValue();
17469     },
17470
17471      
17472     // private
17473     deferFocus : function(){
17474         this.focus.defer(10, this);
17475     },
17476
17477     // doc'ed in Field
17478     focus : function(){
17479         this.editorcore.focus();
17480         
17481     },
17482       
17483
17484     // private
17485     onDestroy : function(){
17486         
17487         
17488         
17489         if(this.rendered){
17490             
17491             for (var i =0; i < this.toolbars.length;i++) {
17492                 // fixme - ask toolbars for heights?
17493                 this.toolbars[i].onDestroy();
17494             }
17495             
17496             this.wrap.dom.innerHTML = '';
17497             this.wrap.remove();
17498         }
17499     },
17500
17501     // private
17502     onFirstFocus : function(){
17503         //Roo.log("onFirstFocus");
17504         this.editorcore.onFirstFocus();
17505          for (var i =0; i < this.toolbars.length;i++) {
17506             this.toolbars[i].onFirstFocus();
17507         }
17508         
17509     },
17510     
17511     // private
17512     syncValue : function()
17513     {   
17514         this.editorcore.syncValue();
17515     },
17516     
17517     pushValue : function()
17518     {   
17519         this.editorcore.pushValue();
17520     }
17521      
17522     
17523     // hide stuff that is not compatible
17524     /**
17525      * @event blur
17526      * @hide
17527      */
17528     /**
17529      * @event change
17530      * @hide
17531      */
17532     /**
17533      * @event focus
17534      * @hide
17535      */
17536     /**
17537      * @event specialkey
17538      * @hide
17539      */
17540     /**
17541      * @cfg {String} fieldClass @hide
17542      */
17543     /**
17544      * @cfg {String} focusClass @hide
17545      */
17546     /**
17547      * @cfg {String} autoCreate @hide
17548      */
17549     /**
17550      * @cfg {String} inputType @hide
17551      */
17552     /**
17553      * @cfg {String} invalidClass @hide
17554      */
17555     /**
17556      * @cfg {String} invalidText @hide
17557      */
17558     /**
17559      * @cfg {String} msgFx @hide
17560      */
17561     /**
17562      * @cfg {String} validateOnBlur @hide
17563      */
17564 });
17565  
17566     
17567    
17568    
17569    
17570       
17571 Roo.namespace('Roo.bootstrap.htmleditor');
17572 /**
17573  * @class Roo.bootstrap.HtmlEditorToolbar1
17574  * Basic Toolbar
17575  * 
17576  * Usage:
17577  *
17578  new Roo.bootstrap.HtmlEditor({
17579     ....
17580     toolbars : [
17581         new Roo.bootstrap.HtmlEditorToolbar1({
17582             disable : { fonts: 1 , format: 1, ..., ... , ...],
17583             btns : [ .... ]
17584         })
17585     }
17586      
17587  * 
17588  * @cfg {Object} disable List of elements to disable..
17589  * @cfg {Array} btns List of additional buttons.
17590  * 
17591  * 
17592  * NEEDS Extra CSS? 
17593  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
17594  */
17595  
17596 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
17597 {
17598     
17599     Roo.apply(this, config);
17600     
17601     // default disabled, based on 'good practice'..
17602     this.disable = this.disable || {};
17603     Roo.applyIf(this.disable, {
17604         fontSize : true,
17605         colors : true,
17606         specialElements : true
17607     });
17608     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
17609     
17610     this.editor = config.editor;
17611     this.editorcore = config.editor.editorcore;
17612     
17613     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
17614     
17615     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
17616     // dont call parent... till later.
17617 }
17618 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
17619      
17620     bar : true,
17621     
17622     editor : false,
17623     editorcore : false,
17624     
17625     
17626     formats : [
17627         "p" ,  
17628         "h1","h2","h3","h4","h5","h6", 
17629         "pre", "code", 
17630         "abbr", "acronym", "address", "cite", "samp", "var",
17631         'div','span'
17632     ],
17633     
17634     onRender : function(ct, position)
17635     {
17636        // Roo.log("Call onRender: " + this.xtype);
17637         
17638        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
17639        Roo.log(this.el);
17640        this.el.dom.style.marginBottom = '0';
17641        var _this = this;
17642        var editorcore = this.editorcore;
17643        var editor= this.editor;
17644        
17645        var children = [];
17646        var btn = function(id,cmd , toggle, handler){
17647        
17648             var  event = toggle ? 'toggle' : 'click';
17649        
17650             var a = {
17651                 size : 'sm',
17652                 xtype: 'Button',
17653                 xns: Roo.bootstrap,
17654                 glyphicon : id,
17655                 cmd : id || cmd,
17656                 enableToggle:toggle !== false,
17657                 //html : 'submit'
17658                 pressed : toggle ? false : null,
17659                 listeners : {}
17660             }
17661             a.listeners[toggle ? 'toggle' : 'click'] = function() {
17662                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
17663             }
17664             children.push(a);
17665             return a;
17666        }
17667         
17668         var style = {
17669                 xtype: 'Button',
17670                 size : 'sm',
17671                 xns: Roo.bootstrap,
17672                 glyphicon : 'font',
17673                 //html : 'submit'
17674                 menu : {
17675                     xtype: 'Menu',
17676                     xns: Roo.bootstrap,
17677                     items:  []
17678                 }
17679         };
17680         Roo.each(this.formats, function(f) {
17681             style.menu.items.push({
17682                 xtype :'MenuItem',
17683                 xns: Roo.bootstrap,
17684                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
17685                 tagname : f,
17686                 listeners : {
17687                     click : function()
17688                     {
17689                         editorcore.insertTag(this.tagname);
17690                         editor.focus();
17691                     }
17692                 }
17693                 
17694             });
17695         });
17696          children.push(style);   
17697             
17698             
17699         btn('bold',false,true);
17700         btn('italic',false,true);
17701         btn('align-left', 'justifyleft',true);
17702         btn('align-center', 'justifycenter',true);
17703         btn('align-right' , 'justifyright',true);
17704         btn('link', false, false, function(btn) {
17705             //Roo.log("create link?");
17706             var url = prompt(this.createLinkText, this.defaultLinkValue);
17707             if(url && url != 'http:/'+'/'){
17708                 this.editorcore.relayCmd('createlink', url);
17709             }
17710         }),
17711         btn('list','insertunorderedlist',true);
17712         btn('pencil', false,true, function(btn){
17713                 Roo.log(this);
17714                 
17715                 this.toggleSourceEdit(btn.pressed);
17716         });
17717         /*
17718         var cog = {
17719                 xtype: 'Button',
17720                 size : 'sm',
17721                 xns: Roo.bootstrap,
17722                 glyphicon : 'cog',
17723                 //html : 'submit'
17724                 menu : {
17725                     xtype: 'Menu',
17726                     xns: Roo.bootstrap,
17727                     items:  []
17728                 }
17729         };
17730         
17731         cog.menu.items.push({
17732             xtype :'MenuItem',
17733             xns: Roo.bootstrap,
17734             html : Clean styles,
17735             tagname : f,
17736             listeners : {
17737                 click : function()
17738                 {
17739                     editorcore.insertTag(this.tagname);
17740                     editor.focus();
17741                 }
17742             }
17743             
17744         });
17745        */
17746         
17747          
17748        this.xtype = 'NavSimplebar';
17749         
17750         for(var i=0;i< children.length;i++) {
17751             
17752             this.buttons.add(this.addxtypeChild(children[i]));
17753             
17754         }
17755         
17756         editor.on('editorevent', this.updateToolbar, this);
17757     },
17758     onBtnClick : function(id)
17759     {
17760        this.editorcore.relayCmd(id);
17761        this.editorcore.focus();
17762     },
17763     
17764     /**
17765      * Protected method that will not generally be called directly. It triggers
17766      * a toolbar update by reading the markup state of the current selection in the editor.
17767      */
17768     updateToolbar: function(){
17769
17770         if(!this.editorcore.activated){
17771             this.editor.onFirstFocus(); // is this neeed?
17772             return;
17773         }
17774
17775         var btns = this.buttons; 
17776         var doc = this.editorcore.doc;
17777         btns.get('bold').setActive(doc.queryCommandState('bold'));
17778         btns.get('italic').setActive(doc.queryCommandState('italic'));
17779         //btns.get('underline').setActive(doc.queryCommandState('underline'));
17780         
17781         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
17782         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
17783         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
17784         
17785         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
17786         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
17787          /*
17788         
17789         var ans = this.editorcore.getAllAncestors();
17790         if (this.formatCombo) {
17791             
17792             
17793             var store = this.formatCombo.store;
17794             this.formatCombo.setValue("");
17795             for (var i =0; i < ans.length;i++) {
17796                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
17797                     // select it..
17798                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
17799                     break;
17800                 }
17801             }
17802         }
17803         
17804         
17805         
17806         // hides menus... - so this cant be on a menu...
17807         Roo.bootstrap.MenuMgr.hideAll();
17808         */
17809         Roo.bootstrap.MenuMgr.hideAll();
17810         //this.editorsyncValue();
17811     },
17812     onFirstFocus: function() {
17813         this.buttons.each(function(item){
17814            item.enable();
17815         });
17816     },
17817     toggleSourceEdit : function(sourceEditMode){
17818         
17819           
17820         if(sourceEditMode){
17821             Roo.log("disabling buttons");
17822            this.buttons.each( function(item){
17823                 if(item.cmd != 'pencil'){
17824                     item.disable();
17825                 }
17826             });
17827           
17828         }else{
17829             Roo.log("enabling buttons");
17830             if(this.editorcore.initialized){
17831                 this.buttons.each( function(item){
17832                     item.enable();
17833                 });
17834             }
17835             
17836         }
17837         Roo.log("calling toggole on editor");
17838         // tell the editor that it's been pressed..
17839         this.editor.toggleSourceEdit(sourceEditMode);
17840        
17841     }
17842 });
17843
17844
17845
17846
17847
17848 /**
17849  * @class Roo.bootstrap.Table.AbstractSelectionModel
17850  * @extends Roo.util.Observable
17851  * Abstract base class for grid SelectionModels.  It provides the interface that should be
17852  * implemented by descendant classes.  This class should not be directly instantiated.
17853  * @constructor
17854  */
17855 Roo.bootstrap.Table.AbstractSelectionModel = function(){
17856     this.locked = false;
17857     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
17858 };
17859
17860
17861 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
17862     /** @ignore Called by the grid automatically. Do not call directly. */
17863     init : function(grid){
17864         this.grid = grid;
17865         this.initEvents();
17866     },
17867
17868     /**
17869      * Locks the selections.
17870      */
17871     lock : function(){
17872         this.locked = true;
17873     },
17874
17875     /**
17876      * Unlocks the selections.
17877      */
17878     unlock : function(){
17879         this.locked = false;
17880     },
17881
17882     /**
17883      * Returns true if the selections are locked.
17884      * @return {Boolean}
17885      */
17886     isLocked : function(){
17887         return this.locked;
17888     }
17889 });
17890 /**
17891  * @extends Roo.bootstrap.Table.AbstractSelectionModel
17892  * @class Roo.bootstrap.Table.RowSelectionModel
17893  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
17894  * It supports multiple selections and keyboard selection/navigation. 
17895  * @constructor
17896  * @param {Object} config
17897  */
17898
17899 Roo.bootstrap.Table.RowSelectionModel = function(config){
17900     Roo.apply(this, config);
17901     this.selections = new Roo.util.MixedCollection(false, function(o){
17902         return o.id;
17903     });
17904
17905     this.last = false;
17906     this.lastActive = false;
17907
17908     this.addEvents({
17909         /**
17910              * @event selectionchange
17911              * Fires when the selection changes
17912              * @param {SelectionModel} this
17913              */
17914             "selectionchange" : true,
17915         /**
17916              * @event afterselectionchange
17917              * Fires after the selection changes (eg. by key press or clicking)
17918              * @param {SelectionModel} this
17919              */
17920             "afterselectionchange" : true,
17921         /**
17922              * @event beforerowselect
17923              * Fires when a row is selected being selected, return false to cancel.
17924              * @param {SelectionModel} this
17925              * @param {Number} rowIndex The selected index
17926              * @param {Boolean} keepExisting False if other selections will be cleared
17927              */
17928             "beforerowselect" : true,
17929         /**
17930              * @event rowselect
17931              * Fires when a row is selected.
17932              * @param {SelectionModel} this
17933              * @param {Number} rowIndex The selected index
17934              * @param {Roo.data.Record} r The record
17935              */
17936             "rowselect" : true,
17937         /**
17938              * @event rowdeselect
17939              * Fires when a row is deselected.
17940              * @param {SelectionModel} this
17941              * @param {Number} rowIndex The selected index
17942              */
17943         "rowdeselect" : true
17944     });
17945     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
17946     this.locked = false;
17947 };
17948
17949 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
17950     /**
17951      * @cfg {Boolean} singleSelect
17952      * True to allow selection of only one row at a time (defaults to false)
17953      */
17954     singleSelect : false,
17955
17956     // private
17957     initEvents : function(){
17958
17959         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
17960             this.grid.on("mousedown", this.handleMouseDown, this);
17961         }else{ // allow click to work like normal
17962             this.grid.on("rowclick", this.handleDragableRowClick, this);
17963         }
17964
17965         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
17966             "up" : function(e){
17967                 if(!e.shiftKey){
17968                     this.selectPrevious(e.shiftKey);
17969                 }else if(this.last !== false && this.lastActive !== false){
17970                     var last = this.last;
17971                     this.selectRange(this.last,  this.lastActive-1);
17972                     this.grid.getView().focusRow(this.lastActive);
17973                     if(last !== false){
17974                         this.last = last;
17975                     }
17976                 }else{
17977                     this.selectFirstRow();
17978                 }
17979                 this.fireEvent("afterselectionchange", this);
17980             },
17981             "down" : function(e){
17982                 if(!e.shiftKey){
17983                     this.selectNext(e.shiftKey);
17984                 }else if(this.last !== false && this.lastActive !== false){
17985                     var last = this.last;
17986                     this.selectRange(this.last,  this.lastActive+1);
17987                     this.grid.getView().focusRow(this.lastActive);
17988                     if(last !== false){
17989                         this.last = last;
17990                     }
17991                 }else{
17992                     this.selectFirstRow();
17993                 }
17994                 this.fireEvent("afterselectionchange", this);
17995             },
17996             scope: this
17997         });
17998
17999         var view = this.grid.view;
18000         view.on("refresh", this.onRefresh, this);
18001         view.on("rowupdated", this.onRowUpdated, this);
18002         view.on("rowremoved", this.onRemove, this);
18003     },
18004
18005     // private
18006     onRefresh : function(){
18007         var ds = this.grid.dataSource, i, v = this.grid.view;
18008         var s = this.selections;
18009         s.each(function(r){
18010             if((i = ds.indexOfId(r.id)) != -1){
18011                 v.onRowSelect(i);
18012             }else{
18013                 s.remove(r);
18014             }
18015         });
18016     },
18017
18018     // private
18019     onRemove : function(v, index, r){
18020         this.selections.remove(r);
18021     },
18022
18023     // private
18024     onRowUpdated : function(v, index, r){
18025         if(this.isSelected(r)){
18026             v.onRowSelect(index);
18027         }
18028     },
18029
18030     /**
18031      * Select records.
18032      * @param {Array} records The records to select
18033      * @param {Boolean} keepExisting (optional) True to keep existing selections
18034      */
18035     selectRecords : function(records, keepExisting){
18036         if(!keepExisting){
18037             this.clearSelections();
18038         }
18039         var ds = this.grid.dataSource;
18040         for(var i = 0, len = records.length; i < len; i++){
18041             this.selectRow(ds.indexOf(records[i]), true);
18042         }
18043     },
18044
18045     /**
18046      * Gets the number of selected rows.
18047      * @return {Number}
18048      */
18049     getCount : function(){
18050         return this.selections.length;
18051     },
18052
18053     /**
18054      * Selects the first row in the grid.
18055      */
18056     selectFirstRow : function(){
18057         this.selectRow(0);
18058     },
18059
18060     /**
18061      * Select the last row.
18062      * @param {Boolean} keepExisting (optional) True to keep existing selections
18063      */
18064     selectLastRow : function(keepExisting){
18065         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18066     },
18067
18068     /**
18069      * Selects the row immediately following the last selected row.
18070      * @param {Boolean} keepExisting (optional) True to keep existing selections
18071      */
18072     selectNext : function(keepExisting){
18073         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18074             this.selectRow(this.last+1, keepExisting);
18075             this.grid.getView().focusRow(this.last);
18076         }
18077     },
18078
18079     /**
18080      * Selects the row that precedes the last selected row.
18081      * @param {Boolean} keepExisting (optional) True to keep existing selections
18082      */
18083     selectPrevious : function(keepExisting){
18084         if(this.last){
18085             this.selectRow(this.last-1, keepExisting);
18086             this.grid.getView().focusRow(this.last);
18087         }
18088     },
18089
18090     /**
18091      * Returns the selected records
18092      * @return {Array} Array of selected records
18093      */
18094     getSelections : function(){
18095         return [].concat(this.selections.items);
18096     },
18097
18098     /**
18099      * Returns the first selected record.
18100      * @return {Record}
18101      */
18102     getSelected : function(){
18103         return this.selections.itemAt(0);
18104     },
18105
18106
18107     /**
18108      * Clears all selections.
18109      */
18110     clearSelections : function(fast){
18111         if(this.locked) return;
18112         if(fast !== true){
18113             var ds = this.grid.dataSource;
18114             var s = this.selections;
18115             s.each(function(r){
18116                 this.deselectRow(ds.indexOfId(r.id));
18117             }, this);
18118             s.clear();
18119         }else{
18120             this.selections.clear();
18121         }
18122         this.last = false;
18123     },
18124
18125
18126     /**
18127      * Selects all rows.
18128      */
18129     selectAll : function(){
18130         if(this.locked) return;
18131         this.selections.clear();
18132         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18133             this.selectRow(i, true);
18134         }
18135     },
18136
18137     /**
18138      * Returns True if there is a selection.
18139      * @return {Boolean}
18140      */
18141     hasSelection : function(){
18142         return this.selections.length > 0;
18143     },
18144
18145     /**
18146      * Returns True if the specified row is selected.
18147      * @param {Number/Record} record The record or index of the record to check
18148      * @return {Boolean}
18149      */
18150     isSelected : function(index){
18151         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18152         return (r && this.selections.key(r.id) ? true : false);
18153     },
18154
18155     /**
18156      * Returns True if the specified record id is selected.
18157      * @param {String} id The id of record to check
18158      * @return {Boolean}
18159      */
18160     isIdSelected : function(id){
18161         return (this.selections.key(id) ? true : false);
18162     },
18163
18164     // private
18165     handleMouseDown : function(e, t){
18166         var view = this.grid.getView(), rowIndex;
18167         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18168             return;
18169         };
18170         if(e.shiftKey && this.last !== false){
18171             var last = this.last;
18172             this.selectRange(last, rowIndex, e.ctrlKey);
18173             this.last = last; // reset the last
18174             view.focusRow(rowIndex);
18175         }else{
18176             var isSelected = this.isSelected(rowIndex);
18177             if(e.button !== 0 && isSelected){
18178                 view.focusRow(rowIndex);
18179             }else if(e.ctrlKey && isSelected){
18180                 this.deselectRow(rowIndex);
18181             }else if(!isSelected){
18182                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18183                 view.focusRow(rowIndex);
18184             }
18185         }
18186         this.fireEvent("afterselectionchange", this);
18187     },
18188     // private
18189     handleDragableRowClick :  function(grid, rowIndex, e) 
18190     {
18191         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18192             this.selectRow(rowIndex, false);
18193             grid.view.focusRow(rowIndex);
18194              this.fireEvent("afterselectionchange", this);
18195         }
18196     },
18197     
18198     /**
18199      * Selects multiple rows.
18200      * @param {Array} rows Array of the indexes of the row to select
18201      * @param {Boolean} keepExisting (optional) True to keep existing selections
18202      */
18203     selectRows : function(rows, keepExisting){
18204         if(!keepExisting){
18205             this.clearSelections();
18206         }
18207         for(var i = 0, len = rows.length; i < len; i++){
18208             this.selectRow(rows[i], true);
18209         }
18210     },
18211
18212     /**
18213      * Selects a range of rows. All rows in between startRow and endRow are also selected.
18214      * @param {Number} startRow The index of the first row in the range
18215      * @param {Number} endRow The index of the last row in the range
18216      * @param {Boolean} keepExisting (optional) True to retain existing selections
18217      */
18218     selectRange : function(startRow, endRow, keepExisting){
18219         if(this.locked) return;
18220         if(!keepExisting){
18221             this.clearSelections();
18222         }
18223         if(startRow <= endRow){
18224             for(var i = startRow; i <= endRow; i++){
18225                 this.selectRow(i, true);
18226             }
18227         }else{
18228             for(var i = startRow; i >= endRow; i--){
18229                 this.selectRow(i, true);
18230             }
18231         }
18232     },
18233
18234     /**
18235      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
18236      * @param {Number} startRow The index of the first row in the range
18237      * @param {Number} endRow The index of the last row in the range
18238      */
18239     deselectRange : function(startRow, endRow, preventViewNotify){
18240         if(this.locked) return;
18241         for(var i = startRow; i <= endRow; i++){
18242             this.deselectRow(i, preventViewNotify);
18243         }
18244     },
18245
18246     /**
18247      * Selects a row.
18248      * @param {Number} row The index of the row to select
18249      * @param {Boolean} keepExisting (optional) True to keep existing selections
18250      */
18251     selectRow : function(index, keepExisting, preventViewNotify){
18252         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
18253         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
18254             if(!keepExisting || this.singleSelect){
18255                 this.clearSelections();
18256             }
18257             var r = this.grid.dataSource.getAt(index);
18258             this.selections.add(r);
18259             this.last = this.lastActive = index;
18260             if(!preventViewNotify){
18261                 this.grid.getView().onRowSelect(index);
18262             }
18263             this.fireEvent("rowselect", this, index, r);
18264             this.fireEvent("selectionchange", this);
18265         }
18266     },
18267
18268     /**
18269      * Deselects a row.
18270      * @param {Number} row The index of the row to deselect
18271      */
18272     deselectRow : function(index, preventViewNotify){
18273         if(this.locked) return;
18274         if(this.last == index){
18275             this.last = false;
18276         }
18277         if(this.lastActive == index){
18278             this.lastActive = false;
18279         }
18280         var r = this.grid.dataSource.getAt(index);
18281         this.selections.remove(r);
18282         if(!preventViewNotify){
18283             this.grid.getView().onRowDeselect(index);
18284         }
18285         this.fireEvent("rowdeselect", this, index);
18286         this.fireEvent("selectionchange", this);
18287     },
18288
18289     // private
18290     restoreLast : function(){
18291         if(this._last){
18292             this.last = this._last;
18293         }
18294     },
18295
18296     // private
18297     acceptsNav : function(row, col, cm){
18298         return !cm.isHidden(col) && cm.isCellEditable(col, row);
18299     },
18300
18301     // private
18302     onEditorKey : function(field, e){
18303         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
18304         if(k == e.TAB){
18305             e.stopEvent();
18306             ed.completeEdit();
18307             if(e.shiftKey){
18308                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
18309             }else{
18310                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
18311             }
18312         }else if(k == e.ENTER && !e.ctrlKey){
18313             e.stopEvent();
18314             ed.completeEdit();
18315             if(e.shiftKey){
18316                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
18317             }else{
18318                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
18319             }
18320         }else if(k == e.ESC){
18321             ed.cancelEdit();
18322         }
18323         if(newCell){
18324             g.startEditing(newCell[0], newCell[1]);
18325         }
18326     }
18327 });/*
18328  * Based on:
18329  * Ext JS Library 1.1.1
18330  * Copyright(c) 2006-2007, Ext JS, LLC.
18331  *
18332  * Originally Released Under LGPL - original licence link has changed is not relivant.
18333  *
18334  * Fork - LGPL
18335  * <script type="text/javascript">
18336  */
18337  
18338 /**
18339  * @class Roo.bootstrap.PagingToolbar
18340  * @extends Roo.Row
18341  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
18342  * @constructor
18343  * Create a new PagingToolbar
18344  * @param {Object} config The config object
18345  */
18346 Roo.bootstrap.PagingToolbar = function(config)
18347 {
18348     // old args format still supported... - xtype is prefered..
18349         // created from xtype...
18350     var ds = config.dataSource;
18351     this.toolbarItems = [];
18352     if (config.items) {
18353         this.toolbarItems = config.items;
18354 //        config.items = [];
18355     }
18356     
18357     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
18358     this.ds = ds;
18359     this.cursor = 0;
18360     if (ds) { 
18361         this.bind(ds);
18362     }
18363     
18364     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
18365     
18366 };
18367
18368 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
18369     /**
18370      * @cfg {Roo.data.Store} dataSource
18371      * The underlying data store providing the paged data
18372      */
18373     /**
18374      * @cfg {String/HTMLElement/Element} container
18375      * container The id or element that will contain the toolbar
18376      */
18377     /**
18378      * @cfg {Boolean} displayInfo
18379      * True to display the displayMsg (defaults to false)
18380      */
18381     /**
18382      * @cfg {Number} pageSize
18383      * The number of records to display per page (defaults to 20)
18384      */
18385     pageSize: 20,
18386     /**
18387      * @cfg {String} displayMsg
18388      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
18389      */
18390     displayMsg : 'Displaying {0} - {1} of {2}',
18391     /**
18392      * @cfg {String} emptyMsg
18393      * The message to display when no records are found (defaults to "No data to display")
18394      */
18395     emptyMsg : 'No data to display',
18396     /**
18397      * Customizable piece of the default paging text (defaults to "Page")
18398      * @type String
18399      */
18400     beforePageText : "Page",
18401     /**
18402      * Customizable piece of the default paging text (defaults to "of %0")
18403      * @type String
18404      */
18405     afterPageText : "of {0}",
18406     /**
18407      * Customizable piece of the default paging text (defaults to "First Page")
18408      * @type String
18409      */
18410     firstText : "First Page",
18411     /**
18412      * Customizable piece of the default paging text (defaults to "Previous Page")
18413      * @type String
18414      */
18415     prevText : "Previous Page",
18416     /**
18417      * Customizable piece of the default paging text (defaults to "Next Page")
18418      * @type String
18419      */
18420     nextText : "Next Page",
18421     /**
18422      * Customizable piece of the default paging text (defaults to "Last Page")
18423      * @type String
18424      */
18425     lastText : "Last Page",
18426     /**
18427      * Customizable piece of the default paging text (defaults to "Refresh")
18428      * @type String
18429      */
18430     refreshText : "Refresh",
18431
18432     buttons : false,
18433     // private
18434     onRender : function(ct, position) 
18435     {
18436         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
18437         this.navgroup.parentId = this.id;
18438         this.navgroup.onRender(this.el, null);
18439         // add the buttons to the navgroup
18440         
18441         if(this.displayInfo){
18442             Roo.log(this.el.select('ul.navbar-nav',true).first());
18443             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
18444             this.displayEl = this.el.select('.x-paging-info', true).first();
18445 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
18446 //            this.displayEl = navel.el.select('span',true).first();
18447         }
18448         
18449         var _this = this;
18450         
18451         if(this.buttons){
18452             Roo.each(_this.buttons, function(e){
18453                Roo.factory(e).onRender(_this.el, null);
18454             });
18455         }
18456             
18457         Roo.each(_this.toolbarItems, function(e) {
18458             _this.navgroup.addItem(e);
18459         });
18460         
18461         this.first = this.navgroup.addItem({
18462             tooltip: this.firstText,
18463             cls: "prev",
18464             icon : 'fa fa-backward',
18465             disabled: true,
18466             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
18467         });
18468         
18469         this.prev =  this.navgroup.addItem({
18470             tooltip: this.prevText,
18471             cls: "prev",
18472             icon : 'fa fa-step-backward',
18473             disabled: true,
18474             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
18475         });
18476     //this.addSeparator();
18477         
18478         
18479         var field = this.navgroup.addItem( {
18480             tagtype : 'span',
18481             cls : 'x-paging-position',
18482             
18483             html : this.beforePageText  +
18484                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
18485                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
18486          } ); //?? escaped?
18487         
18488         this.field = field.el.select('input', true).first();
18489         this.field.on("keydown", this.onPagingKeydown, this);
18490         this.field.on("focus", function(){this.dom.select();});
18491     
18492     
18493         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
18494         //this.field.setHeight(18);
18495         //this.addSeparator();
18496         this.next = this.navgroup.addItem({
18497             tooltip: this.nextText,
18498             cls: "next",
18499             html : ' <i class="fa fa-step-forward">',
18500             disabled: true,
18501             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
18502         });
18503         this.last = this.navgroup.addItem({
18504             tooltip: this.lastText,
18505             icon : 'fa fa-forward',
18506             cls: "next",
18507             disabled: true,
18508             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
18509         });
18510     //this.addSeparator();
18511         this.loading = this.navgroup.addItem({
18512             tooltip: this.refreshText,
18513             icon: 'fa fa-refresh',
18514             
18515             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
18516         });
18517
18518     },
18519
18520     // private
18521     updateInfo : function(){
18522         if(this.displayEl){
18523             var count = this.ds.getCount();
18524             var msg = count == 0 ?
18525                 this.emptyMsg :
18526                 String.format(
18527                     this.displayMsg,
18528                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
18529                 );
18530             this.displayEl.update(msg);
18531         }
18532     },
18533
18534     // private
18535     onLoad : function(ds, r, o){
18536        this.cursor = o.params ? o.params.start : 0;
18537        var d = this.getPageData(),
18538             ap = d.activePage,
18539             ps = d.pages;
18540         
18541        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
18542        this.field.dom.value = ap;
18543        this.first.setDisabled(ap == 1);
18544        this.prev.setDisabled(ap == 1);
18545        this.next.setDisabled(ap == ps);
18546        this.last.setDisabled(ap == ps);
18547        this.loading.enable();
18548        this.updateInfo();
18549     },
18550
18551     // private
18552     getPageData : function(){
18553         var total = this.ds.getTotalCount();
18554         return {
18555             total : total,
18556             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
18557             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
18558         };
18559     },
18560
18561     // private
18562     onLoadError : function(){
18563         this.loading.enable();
18564     },
18565
18566     // private
18567     onPagingKeydown : function(e){
18568         var k = e.getKey();
18569         var d = this.getPageData();
18570         if(k == e.RETURN){
18571             var v = this.field.dom.value, pageNum;
18572             if(!v || isNaN(pageNum = parseInt(v, 10))){
18573                 this.field.dom.value = d.activePage;
18574                 return;
18575             }
18576             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
18577             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18578             e.stopEvent();
18579         }
18580         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))
18581         {
18582           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
18583           this.field.dom.value = pageNum;
18584           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
18585           e.stopEvent();
18586         }
18587         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18588         {
18589           var v = this.field.dom.value, pageNum; 
18590           var increment = (e.shiftKey) ? 10 : 1;
18591           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
18592             increment *= -1;
18593           if(!v || isNaN(pageNum = parseInt(v, 10))) {
18594             this.field.dom.value = d.activePage;
18595             return;
18596           }
18597           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
18598           {
18599             this.field.dom.value = parseInt(v, 10) + increment;
18600             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
18601             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
18602           }
18603           e.stopEvent();
18604         }
18605     },
18606
18607     // private
18608     beforeLoad : function(){
18609         if(this.loading){
18610             this.loading.disable();
18611         }
18612     },
18613
18614     // private
18615     onClick : function(which){
18616         var ds = this.ds;
18617         if (!ds) {
18618             return;
18619         }
18620         switch(which){
18621             case "first":
18622                 ds.load({params:{start: 0, limit: this.pageSize}});
18623             break;
18624             case "prev":
18625                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
18626             break;
18627             case "next":
18628                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
18629             break;
18630             case "last":
18631                 var total = ds.getTotalCount();
18632                 var extra = total % this.pageSize;
18633                 var lastStart = extra ? (total - extra) : total-this.pageSize;
18634                 ds.load({params:{start: lastStart, limit: this.pageSize}});
18635             break;
18636             case "refresh":
18637                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
18638             break;
18639         }
18640     },
18641
18642     /**
18643      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
18644      * @param {Roo.data.Store} store The data store to unbind
18645      */
18646     unbind : function(ds){
18647         ds.un("beforeload", this.beforeLoad, this);
18648         ds.un("load", this.onLoad, this);
18649         ds.un("loadexception", this.onLoadError, this);
18650         ds.un("remove", this.updateInfo, this);
18651         ds.un("add", this.updateInfo, this);
18652         this.ds = undefined;
18653     },
18654
18655     /**
18656      * Binds the paging toolbar to the specified {@link Roo.data.Store}
18657      * @param {Roo.data.Store} store The data store to bind
18658      */
18659     bind : function(ds){
18660         ds.on("beforeload", this.beforeLoad, this);
18661         ds.on("load", this.onLoad, this);
18662         ds.on("loadexception", this.onLoadError, this);
18663         ds.on("remove", this.updateInfo, this);
18664         ds.on("add", this.updateInfo, this);
18665         this.ds = ds;
18666     }
18667 });/*
18668  * - LGPL
18669  *
18670  * element
18671  * 
18672  */
18673
18674 /**
18675  * @class Roo.bootstrap.MessageBar
18676  * @extends Roo.bootstrap.Component
18677  * Bootstrap MessageBar class
18678  * @cfg {String} html contents of the MessageBar
18679  * @cfg {String} weight (info | success | warning | danger) default info
18680  * @cfg {String} beforeClass insert the bar before the given class
18681  * @cfg {Boolean} closable (true | false) default false
18682  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
18683  * 
18684  * @constructor
18685  * Create a new Element
18686  * @param {Object} config The config object
18687  */
18688
18689 Roo.bootstrap.MessageBar = function(config){
18690     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
18691 };
18692
18693 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
18694     
18695     html: '',
18696     weight: 'info',
18697     closable: false,
18698     fixed: false,
18699     beforeClass: 'bootstrap-sticky-wrap',
18700     
18701     getAutoCreate : function(){
18702         
18703         var cfg = {
18704             tag: 'div',
18705             cls: 'alert alert-dismissable alert-' + this.weight,
18706             cn: [
18707                 {
18708                     tag: 'span',
18709                     cls: 'message',
18710                     html: this.html || ''
18711                 }
18712             ]
18713         }
18714         
18715         if(this.fixed){
18716             cfg.cls += ' alert-messages-fixed';
18717         }
18718         
18719         if(this.closable){
18720             cfg.cn.push({
18721                 tag: 'button',
18722                 cls: 'close',
18723                 html: 'x'
18724             });
18725         }
18726         
18727         return cfg;
18728     },
18729     
18730     onRender : function(ct, position)
18731     {
18732         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
18733         
18734         if(!this.el){
18735             var cfg = Roo.apply({},  this.getAutoCreate());
18736             cfg.id = Roo.id();
18737             
18738             if (this.cls) {
18739                 cfg.cls += ' ' + this.cls;
18740             }
18741             if (this.style) {
18742                 cfg.style = this.style;
18743             }
18744             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
18745             
18746             this.el.setVisibilityMode(Roo.Element.DISPLAY);
18747         }
18748         
18749         this.el.select('>button.close').on('click', this.hide, this);
18750         
18751     },
18752     
18753     show : function()
18754     {
18755         if (!this.rendered) {
18756             this.render();
18757         }
18758         
18759         this.el.show();
18760         
18761         this.fireEvent('show', this);
18762         
18763     },
18764     
18765     hide : function()
18766     {
18767         if (!this.rendered) {
18768             this.render();
18769         }
18770         
18771         this.el.hide();
18772         
18773         this.fireEvent('hide', this);
18774     },
18775     
18776     update : function()
18777     {
18778 //        var e = this.el.dom.firstChild;
18779 //        
18780 //        if(this.closable){
18781 //            e = e.nextSibling;
18782 //        }
18783 //        
18784 //        e.data = this.html || '';
18785
18786         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
18787     }
18788    
18789 });
18790
18791  
18792
18793      /*
18794  * - LGPL
18795  *
18796  * Graph
18797  * 
18798  */
18799
18800
18801 /**
18802  * @class Roo.bootstrap.Graph
18803  * @extends Roo.bootstrap.Component
18804  * Bootstrap Graph class
18805 > Prameters
18806  -sm {number} sm 4
18807  -md {number} md 5
18808  @cfg {String} graphtype  bar | vbar | pie
18809  @cfg {number} g_x coodinator | centre x (pie)
18810  @cfg {number} g_y coodinator | centre y (pie)
18811  @cfg {number} g_r radius (pie)
18812  @cfg {number} g_height height of the chart (respected by all elements in the set)
18813  @cfg {number} g_width width of the chart (respected by all elements in the set)
18814  @cfg {Object} title The title of the chart
18815     
18816  -{Array}  values
18817  -opts (object) options for the chart 
18818      o {
18819      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
18820      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
18821      o vgutter (number)
18822      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.
18823      o stacked (boolean) whether or not to tread values as in a stacked bar chart
18824      o to
18825      o stretch (boolean)
18826      o }
18827  -opts (object) options for the pie
18828      o{
18829      o cut
18830      o startAngle (number)
18831      o endAngle (number)
18832      } 
18833  *
18834  * @constructor
18835  * Create a new Input
18836  * @param {Object} config The config object
18837  */
18838
18839 Roo.bootstrap.Graph = function(config){
18840     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
18841     
18842     this.addEvents({
18843         // img events
18844         /**
18845          * @event click
18846          * The img click event for the img.
18847          * @param {Roo.EventObject} e
18848          */
18849         "click" : true
18850     });
18851 };
18852
18853 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
18854     
18855     sm: 4,
18856     md: 5,
18857     graphtype: 'bar',
18858     g_height: 250,
18859     g_width: 400,
18860     g_x: 50,
18861     g_y: 50,
18862     g_r: 30,
18863     opts:{
18864         //g_colors: this.colors,
18865         g_type: 'soft',
18866         g_gutter: '20%'
18867
18868     },
18869     title : false,
18870
18871     getAutoCreate : function(){
18872         
18873         var cfg = {
18874             tag: 'div',
18875             html : null
18876         }
18877         
18878         
18879         return  cfg;
18880     },
18881
18882     onRender : function(ct,position){
18883         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
18884         this.raphael = Raphael(this.el.dom);
18885         
18886                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18887                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18888                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
18889                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
18890                 /*
18891                 r.text(160, 10, "Single Series Chart").attr(txtattr);
18892                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
18893                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
18894                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
18895                 
18896                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
18897                 r.barchart(330, 10, 300, 220, data1);
18898                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
18899                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
18900                 */
18901                 
18902                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
18903                 // r.barchart(30, 30, 560, 250,  xdata, {
18904                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
18905                 //     axis : "0 0 1 1",
18906                 //     axisxlabels :  xdata
18907                 //     //yvalues : cols,
18908                    
18909                 // });
18910 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
18911 //        
18912 //        this.load(null,xdata,{
18913 //                axis : "0 0 1 1",
18914 //                axisxlabels :  xdata
18915 //                });
18916
18917     },
18918
18919     load : function(graphtype,xdata,opts){
18920         this.raphael.clear();
18921         if(!graphtype) {
18922             graphtype = this.graphtype;
18923         }
18924         if(!opts){
18925             opts = this.opts;
18926         }
18927         var r = this.raphael,
18928             fin = function () {
18929                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
18930             },
18931             fout = function () {
18932                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
18933             },
18934             pfin = function() {
18935                 this.sector.stop();
18936                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
18937
18938                 if (this.label) {
18939                     this.label[0].stop();
18940                     this.label[0].attr({ r: 7.5 });
18941                     this.label[1].attr({ "font-weight": 800 });
18942                 }
18943             },
18944             pfout = function() {
18945                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
18946
18947                 if (this.label) {
18948                     this.label[0].animate({ r: 5 }, 500, "bounce");
18949                     this.label[1].attr({ "font-weight": 400 });
18950                 }
18951             };
18952
18953         switch(graphtype){
18954             case 'bar':
18955                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
18956                 break;
18957             case 'hbar':
18958                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
18959                 break;
18960             case 'pie':
18961 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
18962 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
18963 //            
18964                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
18965                 
18966                 break;
18967
18968         }
18969         
18970         if(this.title){
18971             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
18972         }
18973         
18974     },
18975     
18976     setTitle: function(o)
18977     {
18978         this.title = o;
18979     },
18980     
18981     initEvents: function() {
18982         
18983         if(!this.href){
18984             this.el.on('click', this.onClick, this);
18985         }
18986     },
18987     
18988     onClick : function(e)
18989     {
18990         Roo.log('img onclick');
18991         this.fireEvent('click', this, e);
18992     }
18993    
18994 });
18995
18996  
18997 /*
18998  * - LGPL
18999  *
19000  * numberBox
19001  * 
19002  */
19003 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19004
19005 /**
19006  * @class Roo.bootstrap.dash.NumberBox
19007  * @extends Roo.bootstrap.Component
19008  * Bootstrap NumberBox class
19009  * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19010  * @cfg {String} headline Box headline
19011  * @cfg {String} content Box content
19012  * @cfg {String} icon Box icon
19013  * @cfg {String} footer Footer text
19014  * @cfg {String} fhref Footer href
19015  * 
19016  * @constructor
19017  * Create a new NumberBox
19018  * @param {Object} config The config object
19019  */
19020
19021
19022 Roo.bootstrap.dash.NumberBox = function(config){
19023     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19024     
19025 };
19026
19027 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
19028     
19029     bgcolor : 'aqua',
19030     headline : '',
19031     content : '',
19032     icon : '',
19033     footer : '',
19034     fhref : '',
19035     ficon : '',
19036     
19037     getAutoCreate : function(){
19038         
19039         var cfg = {
19040             tag : 'div',
19041             cls : 'small-box bg-' + this.bgcolor,
19042             cn : [
19043                 {
19044                     tag : 'div',
19045                     cls : 'inner',
19046                     cn :[
19047                         {
19048                             tag : 'h3',
19049                             cls : 'roo-headline',
19050                             html : this.headline
19051                         },
19052                         {
19053                             tag : 'p',
19054                             cls : 'roo-content',
19055                             html : this.content
19056                         }
19057                     ]
19058                 }
19059             ]
19060         }
19061         
19062         if(this.icon){
19063             cfg.cn.push({
19064                 tag : 'div',
19065                 cls : 'icon',
19066                 cn :[
19067                     {
19068                         tag : 'i',
19069                         cls : 'ion ' + this.icon
19070                     }
19071                 ]
19072             });
19073         }
19074         
19075         if(this.footer){
19076             var footer = {
19077                 tag : 'a',
19078                 cls : 'small-box-footer',
19079                 href : this.fhref || '#',
19080                 html : this.footer
19081             };
19082             
19083             cfg.cn.push(footer);
19084             
19085         }
19086         
19087         return  cfg;
19088     },
19089
19090     onRender : function(ct,position){
19091         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19092
19093
19094        
19095                 
19096     },
19097
19098     setHeadline: function (value)
19099     {
19100         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19101     },
19102     
19103     setFooter: function (value, href)
19104     {
19105         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19106         
19107         if(href){
19108             this.el.select('a.small-box-footer',true).first().attr('href', href);
19109         }
19110         
19111     },
19112
19113     setContent: function (value)
19114     {
19115         this.el.select('.roo-content',true).first().dom.innerHTML = value;
19116     },
19117
19118     initEvents: function() 
19119     {   
19120         
19121     }
19122     
19123 });
19124
19125  
19126 /*
19127  * - LGPL
19128  *
19129  * TabBox
19130  * 
19131  */
19132 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19133
19134 /**
19135  * @class Roo.bootstrap.dash.TabBox
19136  * @extends Roo.bootstrap.Component
19137  * Bootstrap TabBox class
19138  * @cfg {String} title Title of the TabBox
19139  * @cfg {String} icon Icon of the TabBox
19140  * 
19141  * @constructor
19142  * Create a new TabBox
19143  * @param {Object} config The config object
19144  */
19145
19146
19147 Roo.bootstrap.dash.TabBox = function(config){
19148     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19149     this.addEvents({
19150         // raw events
19151         /**
19152          * @event addpane
19153          * When a pane is added
19154          * @param {Roo.bootstrap.dash.TabPane} pane
19155          */
19156         "addpane" : true
19157          
19158     });
19159 };
19160
19161 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
19162
19163     title : '',
19164     icon : false,
19165     
19166     getChildContainer : function()
19167     {
19168         return this.el.select('.tab-content', true).first();
19169     },
19170     
19171     getAutoCreate : function(){
19172         
19173         var header = {
19174             tag: 'li',
19175             cls: 'pull-left header',
19176             html: this.title,
19177             cn : []
19178         };
19179         
19180         if(this.icon){
19181             header.cn.push({
19182                 tag: 'i',
19183                 cls: 'fa ' + this.icon
19184             });
19185         }
19186         
19187         
19188         var cfg = {
19189             tag: 'div',
19190             cls: 'nav-tabs-custom',
19191             cn: [
19192                 {
19193                     tag: 'ul',
19194                     cls: 'nav nav-tabs pull-right',
19195                     cn: [
19196                         header
19197                     ]
19198                 },
19199                 {
19200                     tag: 'div',
19201                     cls: 'tab-content no-padding',
19202                     cn: []
19203                 }
19204             ]
19205         }
19206
19207         return  cfg;
19208     },
19209     initEvents : function()
19210     {
19211         //Roo.log('add add pane handler');
19212         this.on('addpane', this.onAddPane, this);
19213     },
19214      /**
19215      * Updates the box title
19216      * @param {String} html to set the title to.
19217      */
19218     setTitle : function(value)
19219     {
19220         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
19221     },
19222     onAddPane : function(pane)
19223     {
19224         //Roo.log('addpane');
19225         //Roo.log(pane);
19226         // tabs are rendere left to right..
19227         var ctr = this.el.select('.nav-tabs', true).first();
19228          
19229          
19230         var existing = ctr.select('.nav-tab',true);
19231         var qty = existing.getCount();;
19232         
19233         
19234         var tab = ctr.createChild({
19235             tag : 'li',
19236             cls : 'nav-tab' + (qty ? '' : ' active'),
19237             cn : [
19238                 {
19239                     tag : 'a',
19240                     href:'#',
19241                     html : pane.title
19242                 }
19243             ]
19244         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
19245         pane.tab = tab;
19246         
19247         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
19248         if (!qty) {
19249             pane.el.addClass('active');
19250         }
19251         
19252                 
19253     },
19254     onTabClick : function(ev,un,ob,pane)
19255     {
19256         //Roo.log('tab - prev default');
19257         ev.preventDefault();
19258         
19259         
19260         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
19261         pane.tab.addClass('active');
19262         //Roo.log(pane.title);
19263         this.getChildContainer().select('.tab-pane',true).removeClass('active');
19264         // technically we should have a deactivate event.. but maybe add later.
19265         // and it should not de-activate the selected tab...
19266         
19267         pane.el.addClass('active');
19268         pane.fireEvent('activate');
19269         
19270         
19271     }
19272     
19273     
19274 });
19275
19276  
19277 /*
19278  * - LGPL
19279  *
19280  * Tab pane
19281  * 
19282  */
19283 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19284 /**
19285  * @class Roo.bootstrap.TabPane
19286  * @extends Roo.bootstrap.Component
19287  * Bootstrap TabPane class
19288  * @cfg {Boolean} active (false | true) Default false
19289  * @cfg {String} title title of panel
19290
19291  * 
19292  * @constructor
19293  * Create a new TabPane
19294  * @param {Object} config The config object
19295  */
19296
19297 Roo.bootstrap.dash.TabPane = function(config){
19298     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
19299     
19300 };
19301
19302 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
19303     
19304     active : false,
19305     title : '',
19306     
19307     // the tabBox that this is attached to.
19308     tab : false,
19309      
19310     getAutoCreate : function() 
19311     {
19312         var cfg = {
19313             tag: 'div',
19314             cls: 'tab-pane'
19315         }
19316         
19317         if(this.active){
19318             cfg.cls += ' active';
19319         }
19320         
19321         return cfg;
19322     },
19323     initEvents  : function()
19324     {
19325         //Roo.log('trigger add pane handler');
19326         this.parent().fireEvent('addpane', this)
19327     },
19328     
19329      /**
19330      * Updates the tab title 
19331      * @param {String} html to set the title to.
19332      */
19333     setTitle: function(str)
19334     {
19335         if (!this.tab) {
19336             return;
19337         }
19338         this.title = str;
19339         this.tab.select('a'.true).first().dom.innerHTML = str;
19340         
19341     }
19342     
19343     
19344     
19345 });
19346
19347  
19348
19349
19350  /*
19351  * - LGPL
19352  *
19353  * menu
19354  * 
19355  */
19356 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19357
19358 /**
19359  * @class Roo.bootstrap.menu.Menu
19360  * @extends Roo.bootstrap.Component
19361  * Bootstrap Menu class - container for Menu
19362  * @cfg {String} html Text of the menu
19363  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
19364  * @cfg {String} icon Font awesome icon
19365  * @cfg {String} pos Menu align to (top | bottom) default bottom
19366  * 
19367  * 
19368  * @constructor
19369  * Create a new Menu
19370  * @param {Object} config The config object
19371  */
19372
19373
19374 Roo.bootstrap.menu.Menu = function(config){
19375     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
19376     
19377     this.addEvents({
19378         /**
19379          * @event beforeshow
19380          * Fires before this menu is displayed
19381          * @param {Roo.bootstrap.menu.Menu} this
19382          */
19383         beforeshow : true,
19384         /**
19385          * @event beforehide
19386          * Fires before this menu is hidden
19387          * @param {Roo.bootstrap.menu.Menu} this
19388          */
19389         beforehide : true,
19390         /**
19391          * @event show
19392          * Fires after this menu is displayed
19393          * @param {Roo.bootstrap.menu.Menu} this
19394          */
19395         show : true,
19396         /**
19397          * @event hide
19398          * Fires after this menu is hidden
19399          * @param {Roo.bootstrap.menu.Menu} this
19400          */
19401         hide : true,
19402         /**
19403          * @event click
19404          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19405          * @param {Roo.bootstrap.menu.Menu} this
19406          * @param {Roo.EventObject} e
19407          */
19408         click : true
19409     });
19410     
19411 };
19412
19413 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
19414     
19415     submenu : false,
19416     html : '',
19417     weight : 'default',
19418     icon : false,
19419     pos : 'bottom',
19420     
19421     
19422     getChildContainer : function() {
19423         if(this.isSubMenu){
19424             return this.el;
19425         }
19426         
19427         return this.el.select('ul.dropdown-menu', true).first();  
19428     },
19429     
19430     getAutoCreate : function()
19431     {
19432         var text = [
19433             {
19434                 tag : 'span',
19435                 cls : 'roo-menu-text',
19436                 html : this.html
19437             }
19438         ];
19439         
19440         if(this.icon){
19441             text.unshift({
19442                 tag : 'i',
19443                 cls : 'fa ' + this.icon
19444             })
19445         }
19446         
19447         
19448         var cfg = {
19449             tag : 'div',
19450             cls : 'btn-group',
19451             cn : [
19452                 {
19453                     tag : 'button',
19454                     cls : 'dropdown-button btn btn-' + this.weight,
19455                     cn : text
19456                 },
19457                 {
19458                     tag : 'button',
19459                     cls : 'dropdown-toggle btn btn-' + this.weight,
19460                     cn : [
19461                         {
19462                             tag : 'span',
19463                             cls : 'caret'
19464                         }
19465                     ]
19466                 },
19467                 {
19468                     tag : 'ul',
19469                     cls : 'dropdown-menu'
19470                 }
19471             ]
19472             
19473         };
19474         
19475         if(this.pos == 'top'){
19476             cfg.cls += ' dropup';
19477         }
19478         
19479         if(this.isSubMenu){
19480             cfg = {
19481                 tag : 'ul',
19482                 cls : 'dropdown-menu'
19483             }
19484         }
19485         
19486         return cfg;
19487     },
19488     
19489     onRender : function(ct, position)
19490     {
19491         this.isSubMenu = ct.hasClass('dropdown-submenu');
19492         
19493         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
19494     },
19495     
19496     initEvents : function() 
19497     {
19498         if(this.isSubMenu){
19499             return;
19500         }
19501         
19502         this.hidden = true;
19503         
19504         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
19505         this.triggerEl.on('click', this.onTriggerPress, this);
19506         
19507         this.buttonEl = this.el.select('button.dropdown-button', true).first();
19508         this.buttonEl.on('click', this.onClick, this);
19509         
19510     },
19511     
19512     list : function()
19513     {
19514         if(this.isSubMenu){
19515             return this.el;
19516         }
19517         
19518         return this.el.select('ul.dropdown-menu', true).first();
19519     },
19520     
19521     onClick : function(e)
19522     {
19523         this.fireEvent("click", this, e);
19524     },
19525     
19526     onTriggerPress  : function(e)
19527     {   
19528         if (this.isVisible()) {
19529             this.hide();
19530         } else {
19531             this.show();
19532         }
19533     },
19534     
19535     isVisible : function(){
19536         return !this.hidden;
19537     },
19538     
19539     show : function()
19540     {
19541         this.fireEvent("beforeshow", this);
19542         
19543         this.hidden = false;
19544         this.el.addClass('open');
19545         
19546         Roo.get(document).on("mouseup", this.onMouseUp, this);
19547         
19548         this.fireEvent("show", this);
19549         
19550         
19551     },
19552     
19553     hide : function()
19554     {
19555         this.fireEvent("beforehide", this);
19556         
19557         this.hidden = true;
19558         this.el.removeClass('open');
19559         
19560         Roo.get(document).un("mouseup", this.onMouseUp);
19561         
19562         this.fireEvent("hide", this);
19563     },
19564     
19565     onMouseUp : function()
19566     {
19567         this.hide();
19568     }
19569     
19570 });
19571
19572  
19573  /*
19574  * - LGPL
19575  *
19576  * menu item
19577  * 
19578  */
19579 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19580
19581 /**
19582  * @class Roo.bootstrap.menu.Item
19583  * @extends Roo.bootstrap.Component
19584  * Bootstrap MenuItem class
19585  * @cfg {Boolean} submenu (true | false) default false
19586  * @cfg {String} html text of the item
19587  * @cfg {String} href the link
19588  * @cfg {Boolean} disable (true | false) default false
19589  * @cfg {Boolean} preventDefault (true | false) default true
19590  * @cfg {String} icon Font awesome icon
19591  * @cfg {String} pos Submenu align to (left | right) default right 
19592  * 
19593  * 
19594  * @constructor
19595  * Create a new Item
19596  * @param {Object} config The config object
19597  */
19598
19599
19600 Roo.bootstrap.menu.Item = function(config){
19601     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
19602     this.addEvents({
19603         /**
19604          * @event mouseover
19605          * Fires when the mouse is hovering over this menu
19606          * @param {Roo.bootstrap.menu.Item} this
19607          * @param {Roo.EventObject} e
19608          */
19609         mouseover : true,
19610         /**
19611          * @event mouseout
19612          * Fires when the mouse exits this menu
19613          * @param {Roo.bootstrap.menu.Item} this
19614          * @param {Roo.EventObject} e
19615          */
19616         mouseout : true,
19617         // raw events
19618         /**
19619          * @event click
19620          * The raw click event for the entire grid.
19621          * @param {Roo.EventObject} e
19622          */
19623         click : true
19624     });
19625 };
19626
19627 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
19628     
19629     submenu : false,
19630     href : '',
19631     html : '',
19632     preventDefault: true,
19633     disable : false,
19634     icon : false,
19635     pos : 'right',
19636     
19637     getAutoCreate : function()
19638     {
19639         var text = [
19640             {
19641                 tag : 'span',
19642                 cls : 'roo-menu-item-text',
19643                 html : this.html
19644             }
19645         ];
19646         
19647         if(this.icon){
19648             text.unshift({
19649                 tag : 'i',
19650                 cls : 'fa ' + this.icon
19651             })
19652         }
19653         
19654         var cfg = {
19655             tag : 'li',
19656             cn : [
19657                 {
19658                     tag : 'a',
19659                     href : this.href || '#',
19660                     cn : text
19661                 }
19662             ]
19663         };
19664         
19665         if(this.disable){
19666             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
19667         }
19668         
19669         if(this.submenu){
19670             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
19671             
19672             if(this.pos == 'left'){
19673                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
19674             }
19675         }
19676         
19677         return cfg;
19678     },
19679     
19680     initEvents : function() 
19681     {
19682         this.el.on('mouseover', this.onMouseOver, this);
19683         this.el.on('mouseout', this.onMouseOut, this);
19684         
19685         this.el.select('a', true).first().on('click', this.onClick, this);
19686         
19687     },
19688     
19689     onClick : function(e)
19690     {
19691         if(this.preventDefault){
19692             e.preventDefault();
19693         }
19694         
19695         this.fireEvent("click", this, e);
19696     },
19697     
19698     onMouseOver : function(e)
19699     {
19700         if(this.submenu && this.pos == 'left'){
19701             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
19702         }
19703         
19704         this.fireEvent("mouseover", this, e);
19705     },
19706     
19707     onMouseOut : function(e)
19708     {
19709         this.fireEvent("mouseout", this, e);
19710     }
19711 });
19712
19713  
19714
19715  /*
19716  * - LGPL
19717  *
19718  * menu separator
19719  * 
19720  */
19721 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
19722
19723 /**
19724  * @class Roo.bootstrap.menu.Separator
19725  * @extends Roo.bootstrap.Component
19726  * Bootstrap Separator class
19727  * 
19728  * @constructor
19729  * Create a new Separator
19730  * @param {Object} config The config object
19731  */
19732
19733
19734 Roo.bootstrap.menu.Separator = function(config){
19735     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
19736 };
19737
19738 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
19739     
19740     getAutoCreate : function(){
19741         var cfg = {
19742             tag : 'li',
19743             cls: 'divider'
19744         };
19745         
19746         return cfg;
19747     }
19748    
19749 });
19750
19751  
19752
19753